isthisreallife
isthisreallife

Reputation: 157

understanding a python descriptors example (TypedProperty)

Here is a slightly modified version of some code found in a python book:

class TypedProperty(object):
    def __init__(self,name,type,default=None):
        self.name = "_" + name
        self.type = type
        self.default = default if default else type()
    def __get__(self,instance,cls):
        return getattr(instance,self.name,self.default)
    def __set__(self,instance,value):
        if not isinstance(value,self.type):
            raise TypeError("Must be a %s" % self.type)
        setattr(instance,self.name,value)

class Foo(object):
    name = TypedProperty("name",str)
    num = TypedProperty("num",int,42)

f = Foo()
f.name = 'blah'

My question: why are we creating attributes in f? In the code above, TypedProperty is written such that f.name = 'blah' creates the attribute "_name" in the instance f.

Why not save the values as attributes of the class TypedProperty? Here is what I had in mind:

class TypedProperty2(object):
    def __init__(self, val, typ):
        if not isinstance(val, typ):
            raise TypeError()
        self.value = val
        self.typ = typ

    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, val):
        if not isinstance(val, self.typ):
            raise TypeError()
        self.value = val

Is this an arbitrary design decision?

Upvotes: 2

Views: 171

Answers (1)

mgilson
mgilson

Reputation: 309891

All instances of the class will share the same instance of the descriptor (e.g. TypedProperty). So, if you store the value on the TypedProperty, then all instances of Foo will have the same value for the name and num values. This is usually not desirable (or expected) for descriptors.

e.g. if you run the following script:

class TypedProperty2(object):
    def __init__(self, val, typ):
        if not isinstance(val, typ):
            raise TypeError()
        self.value = val
        self.typ = typ

    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, val):
        if not isinstance(val, self.typ):
            raise TypeError()
        self.value = val


class Foo(object):
    name = TypedProperty2("name", str)

f1 = Foo()
f1.name = 'blah'

f2 = Foo()
print(f2.name)
f2.name = 'bar'

print(f1.name)

You'll see the following output:

blah
bar

so we can see that initially f2 had f1's name and then, after changing the name of f2, f1 picked up f2's name.

Upvotes: 2

Related Questions