diff -r 4f84fe5a997b Doc/howto/descriptor.rst --- a/Doc/howto/descriptor.rst Mon Feb 11 02:23:13 2013 -0500 +++ b/Doc/howto/descriptor.rst Mon Feb 11 17:43:44 2013 +0100 @@ -40,19 +40,20 @@ Descriptors are a powerful, general purpose protocol. They are the mechanism behind properties, methods, static methods, class methods, and :func:`super()`. -They are used throughout Python itself to implement the new style classes -introduced in version 2.2. Descriptors simplify the underlying C-code and offer -a flexible set of new tools for everyday Python programs. +Descriptors simplify the underlying C-code and offer a flexible set of new +tools for everyday Python programs. Descriptor Protocol ------------------- -``descr.__get__(self, obj, type=None) --> value`` +.. code-block:: python -``descr.__set__(self, obj, value) --> None`` + descr.__get__(self, instance, owner=None) # --> Value -``descr.__delete__(self, obj) --> None`` + descr.__set__(self, instance, value) # --> None + + descr.__delete__(self, instance) # --> None That is all there is to it. Define any of these methods and an object is considered a descriptor and can override default behavior upon being looked up @@ -78,13 +79,15 @@ Invoking Descriptors -------------------- -A descriptor can be called directly by its method name. For example, +A common practice is to return the descriptor (`self`) if the instance is +`None` in :meth:`__get__`, so that accesses through the class work. A +descriptor can be called directly by its method name. For example, ``d.__get__(obj)``. Alternatively, it is more common for a descriptor to be invoked automatically -upon attribute access. For example, ``obj.d`` looks up ``d`` in the dictionary -of ``obj``. If ``d`` defines the method :meth:`__get__`, then ``d.__get__(obj)`` -is invoked according to the precedence rules listed below. +upon attribute access on instances. For example, ``obj.d`` looks up ``d`` in +the dictionary of ``obj``. If ``d`` defines the method :meth:`__get__`, then +``d.__get__(obj)`` is invoked according to the precedence rules listed below. The details of invocation depend on whether ``obj`` is an object or a class. @@ -98,10 +101,12 @@ For classes, the machinery is in :meth:`type.__getattribute__` which transforms ``B.x`` into ``B.__dict__['x'].__get__(None, B)``. In pure Python, it looks -like:: +like: + +.. code-block:: python def __getattribute__(self, key): - "Emulate type_getattro() in Objects/typeobject.c" + '''Emulate type_getattro() in Objects/typeobject.c''' v = object.__getattribute__(self, key) if hasattr(v, '__get__'): return v.__get__(None, self) @@ -116,7 +121,7 @@ * data descriptors always override instance dictionaries. * non-data descriptors may be overridden by instance dictionaries. -The object returned by ``super()`` also has a custom :meth:`__getattribute__` +The object returned by :meth:`super` also has a custom :meth:`__getattribute__` method for invoking descriptors. The call ``super(B, obj).m()`` searches ``obj.__class__.__mro__`` for the base class ``A`` immediately following ``B`` and then returns ``A.__dict__['m'].__get__(obj, A)``. If not a descriptor, @@ -131,10 +136,8 @@ The details above show that the mechanism for descriptors is embedded in the :meth:`__getattribute__()` methods for :class:`object`, :class:`type`, and -:func:`super`. Classes inherit this machinery when they derive from -:class:`object` or if they have a meta-class providing similar functionality. -Likewise, classes can turn-off descriptor invocation by overriding -:meth:`__getattribute__()`. +:func:`super`. Likewise, classes can turn-off descriptor invocation by +overriding :meth:`__getattribute__()`. Descriptor Example @@ -143,29 +146,28 @@ The following code creates a class whose objects are data descriptors which print a message for each get or set. Overriding :meth:`__getattribute__` is alternate approach that could do this for every attribute. However, this -descriptor is useful for monitoring just a few chosen attributes:: +descriptor is useful for monitoring just a few chosen attributes: - class RevealAccess(object): - """A data descriptor that sets and returns values - normally and prints a message logging their access. - """ +.. code-block:: python - def __init__(self, initval=None, name='var'): - self.val = initval - self.name = name - - def __get__(self, obj, objtype): - print('Retrieving', self.name) - return self.val - - def __set__(self, obj, val): - print('Updating', self.name) - self.val = val - - >>> class MyClass(object): - x = RevealAccess(10, 'var "x"') - y = 5 - + >>> class RevealAccess: + ... """A data descriptor that sets and returns values + ... normally and prints a message logging their access. + ... """ + ... def __init__(self, initval=None, name='var'): + ... self.val = initval + ... self.name = name + ... def __get__(self, instance, type=None): + ... print('Retrieving', self.name) + ... return self.val + ... def __set__(self, obj, val): + ... print('Updating', self.name) + ... self.val = val + ... + >>> class MyClass: + ... x = RevealAccess(10, 'var "x"') + ... y = 5 + ... >>> m = MyClass() >>> m.x Retrieving var "x" @@ -183,51 +185,69 @@ Properties, bound and unbound methods, static methods, and class methods are all based on the descriptor protocol. +.. note:: + In this how-to, :meth:`property`, :meth:`classmethod` and + :meth:`staticmethod` are used like functions, but they can also be used as + decortators. See also their respective documentations. + Properties ---------- Calling :func:`property` is a succinct way of building a data descriptor that -triggers function calls upon access to an attribute. Its signature is:: +triggers function calls upon access to an attribute. Its signature is: - property(fget=None, fset=None, fdel=None, doc=None) -> property attribute +.. code-block:: python -The documentation shows a typical use to define a managed attribute ``x``:: + property(fget=None, fset=None, fdel=None, doc=None) # -> property attribute - class C(object): - def getx(self): return self.__x - def setx(self, value): self.__x = value - def delx(self): del self.__x +The documentation shows a typical use to define a managed attribute ``x``: + +.. code-block:: python + + class C: + def getx(self): + return self.__x + + def setx(self, value): + self.__x = value + + def delx(self): + del self.__x + x = property(getx, setx, delx, "I'm the 'x' property.") To see how :func:`property` is implemented in terms of the descriptor protocol, -here is a pure Python equivalent:: +here is a pure Python equivalent: - class Property(object): - "Emulate PyProperty_Type() in Objects/descrobject.c" +.. code-block:: python + + class Property: + '''Emulate PyProperty_Type() in Objects/descrobject.c''' def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel - self.__doc__ = doc + if doc is not None: + self.__doc__ = doc - def __get__(self, obj, objtype=None): - if obj is None: + def __get__(self, instance, owner=None): + if instance is None: return self if self.fget is None: - raise AttributeError, "unreadable attribute" - return self.fget(obj) + raise AttributeError("unreadable attribute") + return self.fget(instance) - def __set__(self, obj, value): + def __set__(self, instance, value): if self.fset is None: - raise AttributeError, "can't set attribute" - self.fset(obj, value) + raise AttributeError("can't set attribute") + self.fset(instance, value) - def __delete__(self, obj): + def __delete__(self, instance): if self.fdel is None: - raise AttributeError, "can't delete attribute" - self.fdel(obj) + raise AttributeError("can't delete attribute") + self.fdel(instance) The :func:`property` builtin helps whenever a user interface has granted attribute access and then subsequent changes require the intervention of a @@ -237,16 +257,17 @@ ``Cell('b10').value``. Subsequent improvements to the program require the cell to be recalculated on every access; however, the programmer does not want to affect existing client code accessing the attribute directly. The solution is -to wrap access to the value attribute in a property data descriptor:: +to wrap access to the value attribute in a property data descriptor: - class Cell(object): - . . . - def getvalue(self, obj): - "Recalculate cell before returning value" +.. code-block:: python + + class Cell: + # [...] + def value(self, obj): + '''Recalculate cell before returning value.''' self.recalc() return obj._value - value = property(getvalue) - + value = property(value) Functions and Methods --------------------- @@ -265,27 +286,31 @@ 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:: +this: - class Function(object): - . . . - def __get__(self, obj, objtype=None): - "Simulate func_descr_get() in Objects/funcobject.c" - return types.MethodType(self, obj, objtype) +.. code-block:: python -Running the interpreter shows how the function descriptor works in practice:: + class Function: - >>> class D(object): - def f(self, x): - return x + # [...] + def __get__(self, instance, owner=None): + '''Simulate func_descr_get() in Objects/funcobject.c''' + return types.MethodType(self, instance, owner) + +Running the interpreter shows how the function descriptor works in practice: + +.. code-block:: python + + >>> class D: + ... def f(self, x): + ... 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 - > + >>> D.f # Get from a class, is a function. + + >>> d.f # Get from an instance, becomes a bound method. + > 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 @@ -345,71 +370,85 @@ ``Sample.erf(1.5) --> .9332``. Since staticmethods return the underlying function with no changes, the example -calls are unexciting:: +calls are unexciting: - >>> class E(object): - def f(x): - print(x) - f = staticmethod(f) +.. code-block:: python - >>> print(E.f(3)) + >>> class E: + ... def f(x): + ... print(x) + ... f = staticmethod(f) + ... + >>> E.f(3) 3 - >>> print(E().f(3)) + >>> E().f(3) 3 Using the non-data descriptor protocol, a pure Python version of -:func:`staticmethod` would look like this:: +:func:`staticmethod` would look like this: - class StaticMethod(object): - "Emulate PyStaticMethod_Type() in Objects/funcobject.c" +.. code-block:: python - def __init__(self, f): - self.f = f + class StaticMethod: + '''Emulate PyStaticMethod_Type() in Objects/funcobject.c''' - def __get__(self, obj, objtype=None): - return self.f + def __init__(self, f): + self.f = f + + def __get__(self, obj, objtype=None): + return self.f Unlike static methods, class methods prepend the class reference to the argument list before calling the function. This format is the same -for whether the caller is an object or a class:: +for whether the caller is an object or a class: - >>> class E(object): - def f(klass, x): - return klass.__name__, x - f = classmethod(f) +.. code-block:: python + >>> class E: + ... def f(klass, x): + ... return klass.__name__, x + ... f = classmethod(f) + ... >>> print(E.f(3)) ('E', 3) >>> print(E().f(3)) ('E', 3) - This behavior is useful whenever the function only needs to have a class reference and does not care about any underlying data. One use for classmethods is to create alternate class constructors. In Python 2.3, the classmethod :func:`dict.fromkeys` creates a new dictionary from a list of keys. The pure -Python equivalent is:: +Python equivalent is: + +.. code-block:: python class Dict: - . . . + + # [...] + def fromkeys(klass, iterable, value=None): - "Emulate dict_fromkeys() in Objects/dictobject.c" + '''Emulate dict_fromkeys() in Objects/dictobject.c''' d = klass() for key in iterable: d[key] = value return d + fromkeys = classmethod(fromkeys) -Now a new dictionary of unique keys can be constructed like this:: +Now a new dictionary of unique keys can be constructed like this: + +.. code-block:: python >>> Dict.fromkeys('abracadabra') {'a': None, 'r': None, 'b': None, 'c': None, 'd': None} Using the non-data descriptor protocol, a pure Python version of -:func:`classmethod` would look like this:: +:func:`classmethod` would look like this: - class ClassMethod(object): - "Emulate PyClassMethod_Type() in Objects/funcobject.c" +.. code-block:: python + + class ClassMethod: + '''Emulate PyClassMethod_Type() in Objects/funcobject.c''' def __init__(self, f): self.f = f @@ -420,4 +459,3 @@ def newfunc(*args): return self.f(klass, *args) return newfunc -