Not so long ago on Habré there was already a translation of the article by Raymond Hettinger
Guide to descriptors . In this article I will try to consider the questions that have arisen after reading me. There will be some code examples, actually questions and answers to them. To understand what this is about, you need to know what descriptors are and why they are.
When are handles called?
Consider the following code:
>>> class M(type):
... def __new__(cls, name, bases, dct):
... dct['test'] = lambda self, x: x*2
... return type.__new__(cls, name, bases, dct)
...
>>> class A(object):
... def __init__(self):
... self.__dict__['test2'] = lambda self, x: x*4
... __metaclass__ = M
...
>>> A().test(2)
4
>>> A().test2(2)
Traceback (most recent call last):
File " < stdin > ", line 1, in < module >
TypeError: < lambda > () takes exactly 2 arguments (1 given)
* This source code was highlighted with Source Code Highlighter .
What's wrong? It seems to add the function in the same way, using the object dictionary. Why is the handle for the function "test2" not called? The point is that the function “test” is defined for the class dictionary, and the function “test2” is defined for the object dictionary:
')
>>> 'test' in A.__dict__
True
>>> 'test2' in A.__dict__
False
>>> 'test' in A().__dict__
False
>>> 'test2' in A().__dict__
True
* This source code was highlighted with Source Code Highlighter .
Hence the first answer: the function "__get__" is called only for descriptors that are properties of a class, and not properties of objects of this class.
What is a handle?
The previous answer immediately raises the question - what does “only for descriptors” mean? I did not create any descriptors, I created only a function!
The answer is expected - functions in Python are descriptors (to be more precise, data descriptors are not):
>>> def foo(x):
... return x * 2
...
>>> '__get__' in dir(foo)
True
* This source code was highlighted with Source Code Highlighter .
Bound / Unbound methods
And finally, the most delicious.
Task:There is an object of a certain class to which you need to dynamically add a method to which, of course, the “self” parameter must be passed. It is not possible to add this method to the class dictionary (we do not want to affect other objects).
To decide "in the forehead" does not go:
>>> class A(object):
... def __init__(self):
... self.x = 3
...
>>> def add_x(self, add):
... self.x += add
... print 'Modified value: %s' % (self.x,)
...
>>> a = A()
>>> a.add_x = add_x
>>> ax
3
>>> a.add_x(3)
Traceback (most recent call last):
File " < stdin > ", line 1, in < module >
TypeError: add_x() takes exactly 2 arguments (1 given)
* This source code was highlighted with Source Code Highlighter .
We get the expected error, which confirms the first answer - when accessing the property descriptor of the object, __get__ is not used. What to do?
Let's play a little with the "__get__" method of the function we just created:
>>> add_x.__get__
< method-wrapper '__get__' of function object at 0x800f2caa0 >
>>> add_x.__get__(a)
< bound method ?. add_x of 60 ; __main__ . A object at 0x800f2dc50 >>
>>> add_x.__get__(a, A)
< bound method A . add_x of 60 ; __main__ . A object at 0x800f2dc50 >>
>>> add_x.__get__(None, A)
< unbound method A . add_x >
* This source code was highlighted with Source Code Highlighter .
Oh, that seems to be what we need! In the penultimate line, we received the “bound” method - that would be the way the “A (). Add_x” would look if the “add_x” method were defined in the “A” class. And in the last line we see the “expected” result of the “A.add_x” call. Now we know how to add a method to an object:
>>> a.add_x = add_x.__get__(a, A)
>>> a.add_x(3)
Modified value: 6
* This source code was highlighted with Source Code Highlighter .
Bingo!
And so, it is the "__get__" method for descriptor functions that creates the "bound / unbound" methods of classes and objects. And there is no magic here :)
Literature
What else to read? In fact - a bit. There are several chapters about descriptors in the Python Data Model (
implementing-descriptors ), and you can again recall a really good article by Hettinger (
original ).
PS Russian is not a native language, I ask for comments in a personal.