5

I have a descriptor class X here. I try to use the descriptor X in another class Y

class X:
    def __init__(self, value):
        self.value = value

    def __get__(self, instance, owner):
        print('#### X.__get__ ####')
        return self.value

    def __set__(self, instance, value):
        print('#### X.__set__ ####')
        self.value = value

class Y:
    def __init__(self):
        self.x = X(10)

y = Y()
print(y.x)
y.x = 20

I was hoping that the statement print(y.x) would invoke x.__get__ and the statement y.x = 20 would invoke x.__set__, but it doesn't happen. When I run the above program, I just get this output.

<__main__.X object at 0x7fc65f947950>

Why were the descriptor methods not invoked?

Lone Learner
  • 18,088
  • 20
  • 102
  • 200
  • @jonrsharpe I don't think with `__getattr__`, `y.x` would invoke `x.__getattr__`. Instead, `y.x` would invoke `y.__getattr__`. However, I am trying to invoke `x.__getattr__` with `y.x` which is what descriptors are supposed to do. – Lone Learner Jan 26 '15 at 18:25
  • possible duplicate of [Dynamically adding @property in python](http://stackoverflow.com/questions/2954331/dynamically-adding-property-in-python) – jonrsharpe Jan 26 '15 at 18:29

1 Answers1

3

To get the descriptor protocol methods invoked, you need to put the descriptor instance (X() in your case) on the Y class, not on the instance. This is described in some detail in the Descriptor HowTo Guide by Raymond Hettinger:

For objects, the machinery is in object.__getattribute__() which transforms b.x into type(b).__dict__['x'].__get__(b, type(b)).

Note how b.x is transformed into the invocation of the descriptor obtained by dict access of type(b). The Y class should look like this:

class Y:
    x = X(10)

However, since a single instance of X will be shared by all instances of Y, the X descriptor class will need to be modified to get and set the values from instance rather than from self.

user4815162342
  • 141,790
  • 18
  • 296
  • 355