Any object with a __get__() method, and optionally
__set__() and __delete__()
methods, accepting specific parameters is said to follow the
descriptor protocol. Such an object qualifies as
a descriptor and can be placed inside a class's
__dict__ to do something special when an attribute
is retrieved, set or deleted. An empty descriptor is shown below.
| Called when attribute is read (eg. print
objectname.attrname). Here obj is the
object on which the attribute is accessed (may be
None if the attribute is accessed directly on the
class, eg. print classname.attrname). Also
cls is the class of obj (or the
class, if the access was on the class itself. In this
case, obj is None). |
| Called when attribute is set on an instance
(eg. objectname.attrname = 12). Here
obj is the object on which the attribute is being
set and val is the object provided as the
value. |
| Called when attribute is deleted from an instance
(eg. del objectname.attrname). Here
obj is the object on which the attribute is being
deleted. |
What we defined above is a class that can be instantiated to create a
descriptor. Let's see how we can create a descriptor, attach it to a
class and put it to work.
|
Now the attribute called d is a descriptor. (This
uses Desc from previous example.) |
| Calls
d.__get__(cobj, C). The value returned is bound to
x. Here d means the instance of
Desc defined in . It can be found in
C.__dict__['d']. |
|
Calls d.__set__(cobj, "setting a value"). |
| Sticking a value
directly in the instance's __dict__ works,
but... |
|
is futile. This still calls d.__get__(cobj, C). |
|
Calls d.__delete__(cobj). |
|
Calls d.__get__(None, C). |
| Doesn't call
anything. This replaces the descriptor with a new string object. After
this, accessing cobj.d or C.d
will just return the string "setting a value on
class". The descriptor has been kicked out of
C's __dict__. |
Note that when accessed from the class itself, only the
__get__() method comes in the picture, setting or
deleting the attribute will actually replace or remove the
descriptor.
Descriptors work only when attached to classes. Sticking a
descriptor in an object that is not a class gives us nothing.