Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Descriptors : Precendence of Attribute access through __getattribute()__

I can't understand what does the below precedence means in context of __getattribute__() special method and Descriptors

I read this under the topic("Precedence") - under topic ("Desriptors") from book Core Python Programming 3 times, still can't get through it.. Can any one explain what are these precedence, and where they are used for??

  • Class attributes
  • Data descriptors
  • Instance attributes
  • Non-data descriptors
  • Defaulting to __getattr__()

I also read the python documentation, where I found the below statement: -

For instance bindings, the precedence of descriptor invocation depends on the which descriptor methods are defined. A descriptor can define any combination of __get__(), __set__() and __delete__(). If it does not define __get__(), then accessing the attribute will return the descriptor object itself unless there is a value in the object’s instance dictionary. If the descriptor defines __set__() and/or __delete__(), it is a data descriptor; if it defines neither, it is a non-data descriptor. Normally, data descriptors define both __get__() and __set__(), while non-data descriptors have just the __get__() method.

Data descriptors with **__set__()** and **__get__()** defined always override a redefinition in an instance dictionary. In contrast, non-data descriptors can be overridden by instances.

Python methods (including staticmethod() and classmethod()) are implemented as non-data descriptors. Accordingly, instances can redefine and override methods. This allows individual instances to acquire behaviors that differ from other instances of the same class.

Can anyone give a small example to explain what the first paragraph is all about? Also what does it mean by saying - override a redefinition in an instance dictionary??

like image 279
Rohit Jain Avatar asked Sep 27 '12 10:09

Rohit Jain


Video Answer


1 Answers

Suppose you have a class:

class C(object):
    dd = MyDataDescriptor()
    ndd = MyNonDataDescriptor()
    def __init__(self):
        self.__value = 1

Let's look first at data descriptors. If in your code you do:

cobj = C()
cobj.dd

accordingly to the above paragraph, the cobj.__dict__ object will be always overriden when the dd attribute is accessed, i.e.__get__/__set__/__del__ methods of the descriptor object will always be used instead of the dictionary. The only exception occurs when the descriptor object doesn't define a __get__ method. Then if there is a dd key in the cobj.__dict__ object its value will be read, if not the descriptor object itself will be returned.

Now for the non-data descriptors. If in your code you call:

cobj.ndd = 2

then the cobj.__dict__ hides the non-data descriptor and the ndd attribute is always read from the cobj.__dict__ object. So if you do:

cobj.ndd

the __get__ method of the descriptor will not be called. But if you delete the attribute from the dictionary:

del cobj.ndd

then the descriptor is back, so calling

cobj.ndd

will call the __get__ method on the descriptor.

like image 61
Vicent Avatar answered Oct 13 '22 12:10

Vicent