[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
|
The protocol is simple and offers exciting possibilities. Several use cases are
|
||||||
so common that they have been packaged into individual function calls.
|
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.
|
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.
|
Using non-data descriptors, the two are merged seamlessly.
|
||||||
|
|
||||||
Class dictionaries store methods as functions. In a class definition, methods
|
Class dictionaries store methods as functions. In a class definition, methods
|
||||||
are written using :keyword:`def` and :keyword:`lambda`, the usual tools for
|
are written using :keyword:`def` or :keyword:`lambda`, the usual tools for
|
||||||
creating functions. The only difference from regular functions is that the
|
creating functions. Methods only differ from regular functions in that the
|
||||||
first argument is reserved for the object instance. By Python convention, 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
|
instance reference is called *self* but may be called *this* or any other
|
||||||
variable name.
|
variable name.
|
||||||
|
|
||||||
To support method calls, functions include the :meth:`__get__` method for
|
To support method calls, functions include the :meth:`__get__` method for
|
||||||
binding methods during attribute access. This means that all functions are
|
binding methods during attribute access. This means that all functions are
|
||||||
non-data descriptors which return bound or unbound methods depending whether
|
non-data descriptors which return bound methods when they are invoked from an
|
||||||
they are invoked from an object or a class. In pure python, it works like
|
object. In pure python, it works like this::
|
||||||
this::
|
|
||||||
|
|
||||||
class Function(object):
|
class Function(object):
|
||||||
. . .
|
. . .
|
||||||
def __get__(self, obj, objtype=None):
|
def __get__(self, obj, objtype=None):
|
||||||
"Simulate func_descr_get() in Objects/funcobject.c"
|
"Simulate func_descr_get() in Objects/funcobject.c"
|
||||||
|
if obj is None:
|
||||||
|
return self
|
||||||
return types.MethodType(self, obj)
|
return types.MethodType(self, obj)
|
||||||
|
|
||||||
Running the interpreter shows how the function descriptor works in practice::
|
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
|
... return x
|
||||||
...
|
...
|
||||||
>>> d = D()
|
>>> d = D()
|
||||||
>>> D.__dict__['f'] # Stored internally as a function
|
|
||||||
<function f at 0x00C45070>
|
# Access through the class dictionary does not invoke __get__.
|
||||||
>>> D.f # Get from a class becomes an unbound method
|
# It just returns the underlying function object.
|
||||||
<unbound method D.f>
|
>>> D.__dict__['f']
|
||||||
>>> d.f # Get from an instance becomes a bound method
|
<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>>
|
<bound method D.f of <__main__.D object at 0x00B18C90>>
|
||||||
|
|
||||||
The output suggests that bound and unbound methods are two different types.
|
# Internally, the bound method stores the underlying function,
|
||||||
While they could have been implemented that way, the actual C implementation of
|
# the bound instance, and the class of the bound instance.
|
||||||
:c:type:`PyMethod_Type` in :source:`Objects/classobject.c` is a single object
|
>>> d.f.__func__
|
||||||
with two different representations depending on whether the :attr:`im_self`
|
<function D.f at 0x1012e5ae8>
|
||||||
field is set or is *NULL* (the C equivalent of ``None``).
|
>>> d.f.__self__
|
||||||
|
<__main__.D object at 0x1012e1f98>
|
||||||
Likewise, the effects of calling a method object depend on the :attr:`im_self`
|
>>> d.f.__class__
|
||||||
field. If set (meaning bound), the original function (stored in the
|
<class 'method'>
|
||||||
: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.
|
|
||||||
|
|
||||||
|
|
||||||
Static Methods and Class Methods
|
Static Methods and Class Methods
|
||||||
|
|
Loading…
Reference in New Issue