pythonpython-3.6propertydescriptor

descriptor behavior difference on class and instance


I am trying to understand descriptors in python, and one thing I noticed is the following:

In [1]: class descriptor:                  
   ...:     def __init__(self):            
   ...:         pass                       
   ...:     def __get__(self, instance, owner):                                        
   ...:         print ("__get__ called")   
   ...:         return 0                   
   ...:     def __set__(self, obj, value): 
   ...:         print ("__set__ called")   

In [2]: class Foo:                         
   ...:     y = descriptor()               
   ...:     def __init__(self):            
   ...:         self.x = descriptor()      

In [3]: Foo.y                              
__get__ called                             
Out[3]: 0                                  

In [4]: f = Foo()                          

In [5]: f.x                                
Out[5]: <__main__.descriptor at 0x1099dd588>

As you can see, on the class attribute, descriptor's __get__ is being correctly called, but on instance attribute it is not calling the required method. I tried reading this, but it is not immediately obvious what part of that page applies over here.


Solution

  • Descriptors are only supported on the class, never on an instance.

    See the Invoking Descriptors section of the reference datamodel documentation:

    The following methods only apply when an instance of the class containing the method (a so-called descriptor class) appears in an owner class (the descriptor must be in either the owner’s class dictionary or in the class dictionary for one of its parents).

    Bold emphasis mine. Here, owner class is the class of the instance you accessed the attribute on, or in case of accessing the attribute on the class, the class itself is the owner class.