[3.6] bpo-23702: Update Descriptor-HOWTO to reflect the removal of unbound methods (GH-3739) (#3742)
(cherry picked from commit 0d4497b9ca
)
This commit is contained in:
parent
01438ed4c2
commit
73c915a5cd
|
@ -180,7 +180,7 @@ descriptor is useful for monitoring just a few chosen attributes::
|
|||
|
||||
The protocol is simple and offers exciting possibilities. Several use cases are
|
||||
so common that they have been packaged into individual function calls.
|
||||
Properties, bound and unbound methods, static methods, and class methods are all
|
||||
Properties, bound methods, static methods, and class methods are all
|
||||
based on the descriptor protocol.
|
||||
|
||||
|
||||
|
@ -266,22 +266,23 @@ Python's object oriented features are built upon a function based environment.
|
|||
Using non-data descriptors, the two are merged seamlessly.
|
||||
|
||||
Class dictionaries store methods as functions. In a class definition, methods
|
||||
are written using :keyword:`def` and :keyword:`lambda`, the usual tools for
|
||||
creating functions. The only difference from regular functions is that the
|
||||
are written using :keyword:`def` or :keyword:`lambda`, the usual tools for
|
||||
creating functions. Methods only differ from regular functions in that the
|
||||
first argument is reserved for the object instance. By Python convention, the
|
||||
instance reference is called *self* but may be called *this* or any other
|
||||
variable name.
|
||||
|
||||
To support method calls, functions include the :meth:`__get__` method for
|
||||
binding methods during attribute access. This means that all functions are
|
||||
non-data descriptors which return bound or unbound methods depending whether
|
||||
they are invoked from an object or a class. In pure python, it works like
|
||||
this::
|
||||
non-data descriptors which return bound methods when they are invoked from an
|
||||
object. In pure python, it works like this::
|
||||
|
||||
class Function(object):
|
||||
. . .
|
||||
def __get__(self, obj, objtype=None):
|
||||
"Simulate func_descr_get() in Objects/funcobject.c"
|
||||
if obj is None:
|
||||
return self
|
||||
return types.MethodType(self, obj)
|
||||
|
||||
Running the interpreter shows how the function descriptor works in practice::
|
||||
|
@ -291,25 +292,34 @@ Running the interpreter shows how the function descriptor works in practice::
|
|||
... return x
|
||||
...
|
||||
>>> d = D()
|
||||
>>> D.__dict__['f'] # Stored internally as a function
|
||||
<function f at 0x00C45070>
|
||||
>>> D.f # Get from a class becomes an unbound method
|
||||
<unbound method D.f>
|
||||
>>> d.f # Get from an instance becomes a bound method
|
||||
|
||||
# Access through the class dictionary does not invoke __get__.
|
||||
# It just returns the underlying function object.
|
||||
>>> D.__dict__['f']
|
||||
<function D.f at 0x00C45070>
|
||||
|
||||
# Dotted access from a class calls __get__() which just returns
|
||||
# the underlying function unchanged.
|
||||
>>> D.f
|
||||
<function D.f at 0x00C45070>
|
||||
|
||||
# The function has a __qualname__ attribute to support introspection
|
||||
>>> D.f.__qualname__
|
||||
'D.f'
|
||||
|
||||
# Dotted access from an instance calls __get__() which returns the
|
||||
# function wrapped in a bound method object
|
||||
>>> d.f
|
||||
<bound method D.f of <__main__.D object at 0x00B18C90>>
|
||||
|
||||
The output suggests that bound and unbound methods are two different types.
|
||||
While they could have been implemented that way, the actual C implementation of
|
||||
:c:type:`PyMethod_Type` in :source:`Objects/classobject.c` is a single object
|
||||
with two different representations depending on whether the :attr:`im_self`
|
||||
field is set or is *NULL* (the C equivalent of ``None``).
|
||||
|
||||
Likewise, the effects of calling a method object depend on the :attr:`im_self`
|
||||
field. If set (meaning bound), the original function (stored in the
|
||||
:attr:`im_func` field) is called as expected with the first argument set to the
|
||||
instance. If unbound, all of the arguments are passed unchanged to the original
|
||||
function. The actual C implementation of :func:`instancemethod_call()` is only
|
||||
slightly more complex in that it includes some type checking.
|
||||
# Internally, the bound method stores the underlying function,
|
||||
# the bound instance, and the class of the bound instance.
|
||||
>>> d.f.__func__
|
||||
<function D.f at 0x1012e5ae8>
|
||||
>>> d.f.__self__
|
||||
<__main__.D object at 0x1012e1f98>
|
||||
>>> d.f.__class__
|
||||
<class 'method'>
|
||||
|
||||
|
||||
Static Methods and Class Methods
|
||||
|
|
Loading…
Reference in New Issue