From 0d4497b9cae7942b7f731a6f99a73985c3fb4630 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 25 Sep 2017 01:05:49 -0700 Subject: [PATCH] bpo-23702: Update Descriptor-HOWTO to reflect the removal of unbound methods (#3739) --- Doc/howto/descriptor.rst | 56 +++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index b34937585e..5e85a9aa65 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -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 - - >>> D.f # Get from a class becomes an unbound method - - >>> 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'] + + + # Dotted access from a class calls __get__() which just returns + # the underlying function unchanged. + >>> D.f + + + # 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 > -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__ + + >>> d.f.__self__ + <__main__.D object at 0x1012e1f98> + >>> d.f.__class__ + Static Methods and Class Methods -- 2.40.0