Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I am studying descriptor. They work with class varibles out of the box, but I want them to operate on instance variables too, so I did this;

class NumberDescriptor(object):
    def __init__(self,name):
        self.name = name

    def __set__(self, instance, value):
        if not isinstance(value, int):
            raise ValueError("%s is not a number." % value)
        else:
            setattr(instance, self.name, value)

    def __get__(self, instance, owner):
        return getattr(instance, self.name)

class Human(object):
    age = NumberDescriptor("_age")

    def __init__(self,age):
        self.age = age

a = Human(12)
print a.age
b = Human("osman")
print b.age

Is this right way to go about this?

share|improve this question

1 Answer

That looks reasonable; but you should handle the case of when the descriptor is invoked from the class: Human.age. In that case, __get__ is invoked with None as an argument to instance. If you've no better use for it; you can just return the descriptor itself:

def __get__(self, instance, owner):
    if instance is None:
        return self
    else:
        return getattr(instance, self.name)

Another thing you can do, which is sometimes preferred, is to make the descriptor figure out its own name by inspecting the class it's attached to; this is a little more complicated, of course, but has the advantage of allowing you to specify the "name" of the descriptor attribute just one time.

class NumberDescriptor(object):
    def snoop_name(self, owner):
        for attr in dir(owner):
            if getattr(owner, attr) is self:
                return attr
    def __get__(self, instance, owner):
        if instance is None:
            return self
        name = self.snoop_name(self, owner)
        return getattr(instance, '_'+name)
    def __set__(self, instance, value):
        name = self.snoop_name(self, type(instance))
        setattr(instance, '_' + name, int(value))

class Human(object):
    age = NumberDescriptor()

Other variations to get a similar effect (without using dir()) would be to use a metaclass that knows to look for your descriptor and sets its name there.

share|improve this answer
Doesn't this make a lookup for every set and get action. It looks like a little bit waste to me. – yasar11732 Aug 28 '12 at 14:33
yes, this version is a little inelegant; and meant mainly to give you some ideas about how you might use descriptors. A more reasonable implementation would probably use a metaclass or a weakref.WeakKeyDict to avoid the extra lookups, but I've decided to leave that as an exercise. – TokenMacGuy Aug 28 '12 at 14:47

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.