user634615
user634615

Reputation: 677

Particular implementation of Python Descriptors

class Cls():

    def __init__(self, start):
        self.value = start


    class Desc():
        def __get__(self, instance ,owner):
            print("In Descriptor's __get__method")
            return self.value
        def __set__(self, instance, start):
            print("In Descriptor's __set__ method")
            self.value = start

    value = Desc()

X = Cls('Hello')
X.value = "Hi"

Above implementation of descriptor is obscure for me. X.value and Cls.value are refering to same object and is of class str. but Cls.__dict__['value'] is descriptor object. There are two types assigned to a name 'value'.

Can somebody explain this?. What is the logic behind this particular implementation. Why Cls.value or X.value is not descriptor object. I am using python 3.3

Upvotes: 1

Views: 101

Answers (1)

BrenBarn
BrenBarn

Reputation: 251373

You are confusing things by using the name value for two things: one is an attribute of Cls, and its value is a descriptor object. The other is an attribute of that descriptor object, and that is the one whose value is the string.

The key thing to remember is that there is only one descriptor object, shared across all instances of the class. When you do self.value = start in your __set__ method, self refers to the descriptor object, so you are setting a "value" attribute on the descriptor object itself, not on the Cls instance. (If you change it to instance.value = start instead, you will get a recursion error, since that will try to call __set__ again.)

You will see what is going on if you create multiple instances of your class:

>>> x = Cls("oops")
In Descriptor's __set__ method
>>> y = Cls("dang")
In Descriptor's __set__ method
>>> x.value
In Descriptor's __get__method
'dang'
>>> Cls.__dict__['value'].value
'dang'

Notice that creating y changed x.value. This is because there is only one descriptor object, and it only has one "value" attribute, so that value is shared across all instances of Cls.

It's not clear what you're trying to achieve here, so it's hard to say how to "fix" this. Some general principles are:

  1. Don't use self in __get__ and __set__ unless you want to store class-level information. To store instance-specific data, you need to use instance.
  2. Even if you do the above, don't use the same name for the descriptor attribute itself and the hidden attribute where it stores its data, or you will step on your own toes.
  3. Unless you want to do something really fancy, you probably can just use property and not write your own descriptor at all. If you "fixed" the descriptor you wrote above, it would still be useless, because it wouldn't do anything that property doesn't already do.

Upvotes: 4

Related Questions