質問

And by best, I mean both Pythonically and with as few lines of code as possible.

I am used to defining classes with attributes in Python like so.

class MyClass(object):

    def __init__(self):
        self.value = 5

Often, however, I find myself needing to ensure that self.value can only take a specific type of value. In the past (coming from a PHP OOP background) I've done this with a getter-setter method similar to this:

    def value(self, new_value = -1)
        if new_value == -1:
            return self.value
        if isinstance(new_value, int) and new_value > -1:
            self.value = new_value
        else:
            raise TypeError

I then access self.value with self.value(), and set it with self.value(67). Usually I'll name-mangle self.value to self.__value to discourage its direct access.

I've recently discovered (or am in the process of discovering) descriptors. Following closely the excellent discussion here I've written something that looks like this:

class ValidatedAttribute(object):

    def __init__(self, key, kind):
        self.key = key
        self.kind = kind

    def __get__(self, instance, owner):
        if self.key not in instance.__dict__:
            raise AttributeError, self.key
        return instance.__dict__[self.key]

    def __set__(self, instance, value):
        if not isinstance(value, self.kind):
            raise TypeError, self.kind
        instance.__dict__[self.key] = value     


class MyUsefulClass(object):

    a = ValidatedAttribute("a", int)

But I have a few questions!

Firstly, what exactly is MyUsefulClass().a? Is it an instance of a ValidatedAttribute class somehow bound to an instance of a MyUsefulClass class?

And secondly, what I'd really like is something like,

class MyUsefulClass(object):

    def __init__(self):
        self.value = Checker(int, 99)

    def Checker(self, kind, default):
        # Code for verifying that associated attribute is of type kind.

and somehow bind an attribute access to some sort of intercept method, without having to use a whole other descriptor class.

I'm sorry if my understanding is lacking - I'm still getting to grips with new-style Python classes. Any help or pointers in the right direction would be much appreciated.

役に立ちましたか?

解決

Firstly, what exactly is MyUsefulClass().a? Is it an instance of a ValidatedAttribute class somehow bound to an instance of a MyUsefulClass class?

Yes, that is it exactly.

And secondly, what I'd really like is something like [...] and somehow bind an attribute access to some sort of intercept method, without having to use a whole other descriptor class.

Well, you could overwrite __getattribute__, as it does intecept every attribute lookup, but it is way more complicated than using descriptors.

Descriptors work for exactly what you are wanting to do (automatic validation). About the only improvement you could make is to also use a metaclass to automatically set the key for you, and then your class code look like this:

class MyUsefulClass(object):

    a = ValidatedAttribute(int)

But first get comfortable with descriptors, metaclasses are complex and can be tough to grok.

Oh, and one small change I would make to your ValidatedAttribute:

def __init__(self, key, default):

    self.key = key
    self.kind = type(default)
    self.default = default

def __get__(self, instance, owner):
    if self.key not in instance.__dict__:
        return self.default
    return instance.__dict__[self.key]

他のヒント

Please, please, please do not do isinstance(new_value, int).

You might possibly get away with

from numbers import Integral
isinstance(new_value, Integral)

but it's completely anathema to duck typing.

Firstly, what exactly is MyUsefulClass().a? Is it an instance of a ValidatedAttribute class somehow bound to an instance of a MyUsefulClass class?

In order to find MyUsefulClass().a, Python asks the class. The class tells it that it has a getter-setter for the attribute and tells Python to ask the attribute. The attribute says finds it and then that tells the class what the attribute is. The attribute isn't the getter-setter.

MyUsefulClass.a is the getter-setter (property), not MyUsefulClass().a.

And secondly, what I'd really like is something like [stuff]

AFAIK, you can only set properties on the class, not on instances, because how would you know whether you want to retrieve the property or the item the property shadows?

Instead of writing this stuff yourself, use the traits library: https://pypi.python.org/pypi/traits. Using libraries is the pythonic approach to most problems.

However, in general, you should write code that is sufficiently generic that this sort of checking is largely unnecessary. It's easy to do in python.

I think what you're looking for are python properties. Correct approach to validate attributes of an instance of class seems relevant to your qeustion. You might also look at Python descriptor vs property

It sounds like you want to use properties

class MyUsefulClass(object):

    def __init__(self):
        self._value = 0

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, value):
        self._value = self.Checker(value, int, 99)

    def Checker(self, value, kind, default):
        return value if isinstance(value, kind) else default
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top