Skip to content

Commit 4a9c637

Browse files
authored
Second round of updates to the descriptor howto guide (GH-22946)
1 parent 5df6c99 commit 4a9c637

File tree

1 file changed

+156
-96
lines changed

1 file changed

+156
-96
lines changed

Doc/howto/descriptor.rst

Lines changed: 156 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ This HowTo guide has three major sections:
2929
Primer
3030
^^^^^^
3131

32-
In this primer, we start with most basic possible example and then we'll add
33-
new capabilities one by one.
32+
In this primer, we start with the most basic possible example and then we'll
33+
add new capabilities one by one.
3434

3535

3636
Simple example: A descriptor that returns a constant
@@ -197,7 +197,7 @@ be recorded, giving each descriptor its own *public_name* and *private_name*::
197197

198198
import logging
199199

200-
logging.basicConfig(level=logging.INFO)
200+
logging.basicConfig(level=logging.INFO, force=True)
201201

202202
class LoggedAccess:
203203

@@ -258,6 +258,10 @@ Closing thoughts
258258
A :term:`descriptor` is what we call any object that defines :meth:`__get__`,
259259
:meth:`__set__`, or :meth:`__delete__`.
260260

261+
Optionally, descriptors can have a :meth:`__set_name__` method. This is only
262+
used in cases where a descriptor needs to know either the class where it is
263+
created or the name of class variable it was assigned to.
264+
261265
Descriptors get invoked by the dot operator during attribute lookup. If a
262266
descriptor is accessed indirectly with ``vars(some_class)[descriptor_name]``,
263267
the descriptor instance is returned without invoking it.
@@ -291,7 +295,7 @@ Validator class
291295
A validator is a descriptor for managed attribute access. Prior to storing
292296
any data, it verifies that the new value meets various type and range
293297
restrictions. If those restrictions aren't met, it raises an exception to
294-
prevents data corruption at its source.
298+
prevent data corruption at its source.
295299

296300
This :class:`Validator` class is both an :term:`abstract base class` and a
297301
managed attribute descriptor::
@@ -438,12 +442,12 @@ In general, a descriptor is an object attribute with "binding behavior", one
438442
whose attribute access has been overridden by methods in the descriptor
439443
protocol. Those methods are :meth:`__get__`, :meth:`__set__`, and
440444
:meth:`__delete__`. If any of those methods are defined for an object, it is
441-
said to be a descriptor.
445+
said to be a :term:`descriptor`.
442446

443447
The default behavior for attribute access is to get, set, or delete the
444448
attribute from an object's dictionary. For instance, ``a.x`` has a lookup chain
445449
starting with ``a.__dict__['x']``, then ``type(a).__dict__['x']``, and
446-
continuing through the base classes of ``type(a)`` excluding metaclasses. If the
450+
continuing through the base classes of ``type(a)``. If the
447451
looked-up value is an object defining one of the descriptor methods, then Python
448452
may override the default behavior and invoke the descriptor method instead.
449453
Where this occurs in the precedence chain depends on which descriptor methods
@@ -492,60 +496,76 @@ Invoking Descriptors
492496
A descriptor can be called directly by its method name. For example,
493497
``d.__get__(obj)``.
494498

495-
Alternatively, it is more common for a descriptor to be invoked automatically
496-
upon attribute access. For example, ``obj.d`` looks up ``d`` in the dictionary
497-
of ``obj``. If ``d`` defines the method :meth:`__get__`, then ``d.__get__(obj)``
499+
But it is more common for a descriptor to be invoked automatically from
500+
attribute access. The expression ``obj.d`` looks up ``d`` in the dictionary of
501+
``obj``. If ``d`` defines the method :meth:`__get__`, then ``d.__get__(obj)``
498502
is invoked according to the precedence rules listed below.
499503

500-
The details of invocation depend on whether ``obj`` is an object or a class.
504+
The details of invocation depend on whether ``obj`` is an object, class, or
505+
instance of super.
506+
507+
**Objects**: The machinery is in :meth:`object.__getattribute__`.
508+
509+
It transforms ``b.x`` into ``type(b).__dict__['x'].__get__(b, type(b))``.
501510

502-
For objects, the machinery is in :meth:`object.__getattribute__` which
503-
transforms ``b.x`` into ``type(b).__dict__['x'].__get__(b, type(b))``. The
504-
implementation works through a precedence chain that gives data descriptors
511+
The implementation works through a precedence chain that gives data descriptors
505512
priority over instance variables, instance variables priority over non-data
506513
descriptors, and assigns lowest priority to :meth:`__getattr__` if provided.
514+
507515
The full C implementation can be found in :c:func:`PyObject_GenericGetAttr()` in
508516
:source:`Objects/object.c`.
509517

510-
For classes, the machinery is in :meth:`type.__getattribute__` which transforms
511-
``B.x`` into ``B.__dict__['x'].__get__(None, B)``. In pure Python, it looks
512-
like::
518+
**Classes**: The machinery is in :meth:`type.__getattribute__`.
519+
520+
It transforms ``A.x`` into ``A.__dict__['x'].__get__(None, A)``.
521+
522+
In pure Python, it looks like this::
513523

514-
def __getattribute__(self, key):
524+
def __getattribute__(cls, key):
515525
"Emulate type_getattro() in Objects/typeobject.c"
516-
v = object.__getattribute__(self, key)
526+
v = object.__getattribute__(cls, key)
517527
if hasattr(v, '__get__'):
518-
return v.__get__(None, self)
528+
return v.__get__(None, cls)
519529
return v
520530

521-
The important points to remember are:
531+
**Super**: The machinery is in the custom :meth:`__getattribute__` method for
532+
object returned by :class:`super()`.
522533

523-
* descriptors are invoked by the :meth:`__getattribute__` method
524-
* overriding :meth:`__getattribute__` prevents automatic descriptor calls
525-
* :meth:`object.__getattribute__` and :meth:`type.__getattribute__` make
526-
different calls to :meth:`__get__`.
527-
* data descriptors always override instance dictionaries.
528-
* non-data descriptors may be overridden by instance dictionaries.
534+
The attribute lookup ``super(A, obj).m`` searches ``obj.__class__.__mro__`` for
535+
the base class ``B`` immediately following ``A`` and then returns
536+
``B.__dict__['m'].__get__(obj, A)``.
529537

530-
The object returned by ``super()`` also has a custom :meth:`__getattribute__`
531-
method for invoking descriptors. The attribute lookup ``super(B, obj).m`` searches
532-
``obj.__class__.__mro__`` for the base class ``A`` immediately following ``B``
533-
and then returns ``A.__dict__['m'].__get__(obj, B)``. If not a descriptor,
534-
``m`` is returned unchanged. If not in the dictionary, ``m`` reverts to a
535-
search using :meth:`object.__getattribute__`.
538+
If not a descriptor, ``m`` is returned unchanged. If not in the dictionary,
539+
``m`` reverts to a search using :meth:`object.__getattribute__`.
536540

537541
The implementation details are in :c:func:`super_getattro()` in
538-
:source:`Objects/typeobject.c`. and a pure Python equivalent can be found in
542+
:source:`Objects/typeobject.c`. A pure Python equivalent can be found in
539543
`Guido's Tutorial`_.
540544

541545
.. _`Guido's Tutorial`: https://www.python.org/download/releases/2.2.3/descrintro/#cooperation
542546

543-
The details above show that the mechanism for descriptors is embedded in the
544-
:meth:`__getattribute__()` methods for :class:`object`, :class:`type`, and
545-
:func:`super`. Classes inherit this machinery when they derive from
546-
:class:`object` or if they have a metaclass providing similar functionality.
547-
Likewise, classes can turn-off descriptor invocation by overriding
548-
:meth:`__getattribute__()`.
547+
**Summary**: The details listed above show that the mechanism for descriptors is
548+
embedded in the :meth:`__getattribute__()` methods for :class:`object`,
549+
:class:`type`, and :func:`super`.
550+
551+
The important points to remember are:
552+
553+
* Descriptors are invoked by the :meth:`__getattribute__` method.
554+
555+
* Classes inherit this machinery from :class:`object`, :class:`type`, or
556+
:func:`super`.
557+
558+
* Overriding :meth:`__getattribute__` prevents automatic descriptor calls
559+
because all the descriptor logic is in that method.
560+
561+
* :meth:`object.__getattribute__` and :meth:`type.__getattribute__` make
562+
different calls to :meth:`__get__`. The first includes the instance and may
563+
include the class. The second puts in ``None`` for the instance and always
564+
includes the class.
565+
566+
* Data descriptors always override instance dictionaries.
567+
568+
* Non-data descriptors may be overridden by instance dictionaries.
549569

550570

551571
Automatic Name Notification
@@ -569,47 +589,70 @@ afterwards, :meth:`__set_name__` will need to be called manually.
569589
Descriptor Example
570590
------------------
571591

572-
The following code creates a class whose objects are data descriptors which
573-
print a message for each get or set. Overriding :meth:`__getattribute__` is
574-
alternate approach that could do this for every attribute. However, this
575-
descriptor is useful for monitoring just a few chosen attributes::
592+
The following code is simplified skeleton showing how data descriptors could
593+
be used to implement an `object relational mapping
594+
<https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping>`_.
576595

577-
class RevealAccess:
578-
"""A data descriptor that sets and returns values
579-
normally and prints a message logging their access.
580-
"""
596+
The essential idea is that instances only hold keys to a database table. The
597+
actual data is stored in an external table that is being dynamically updated::
581598

582-
def __init__(self, initval=None, name='var'):
583-
self.val = initval
584-
self.name = name
599+
class Field:
600+
601+
def __set_name__(self, owner, name):
602+
self.fetch = f'SELECT {name} FROM {owner.table} WHERE {owner.key}=?;'
603+
self.store = f'UPDATE {owner.table} SET {name}=? WHERE {owner.key}=?;'
585604

586605
def __get__(self, obj, objtype=None):
587-
print('Retrieving', self.name)
588-
return self.val
606+
return conn.execute(self.fetch, [obj.key]).fetchone()[0]
589607

590-
def __set__(self, obj, val):
591-
print('Updating', self.name)
592-
self.val = val
608+
def __set__(self, obj, value):
609+
conn.execute(self.store, [value, obj.key])
610+
conn.commit()
593611

594-
class B:
595-
x = RevealAccess(10, 'var "x"')
596-
y = 5
612+
We can use the :class:`Field` to define "models" that describe the schema for
613+
each table in a database::
597614

598-
>>> m = B()
599-
>>> m.x
600-
Retrieving var "x"
601-
10
602-
>>> m.x = 20
603-
Updating var "x"
604-
>>> m.x
605-
Retrieving var "x"
606-
20
607-
>>> m.y
608-
5
615+
class Movie:
616+
table = 'Movies' # Table name
617+
key = 'title' # Primary key
618+
director = Field()
619+
year = Field()
609620

610-
The protocol is simple and offers exciting possibilities. Several use cases are
611-
so common that they have been packaged into individual function calls.
612-
Properties, bound methods, static methods, and class methods are all
621+
def __init__(self, key):
622+
self.key = key
623+
624+
class Song:
625+
table = 'Music'
626+
key = 'title'
627+
artist = Field()
628+
year = Field()
629+
genre = Field()
630+
631+
def __init__(self, key):
632+
self.key = key
633+
634+
An interactive session shows how data is retrieved from the database and how
635+
it can be updated::
636+
637+
>>> import sqlite3
638+
>>> conn = sqlite3.connect('entertainment.db')
639+
640+
>>> Movie('Star Wars').director
641+
'George Lucas'
642+
>>> jaws = Movie('Jaws')
643+
>>> f'Released in {jaws.year} by {jaws.director}'
644+
'Released in 1975 by Steven Spielberg'
645+
646+
>>> Song('Country Roads').artist
647+
'John Denver'
648+
649+
>>> Movie('Star Wars').director = 'J.J. Abrams'
650+
>>> Movie('Star Wars').director
651+
'J.J. Abrams'
652+
653+
The descriptor protocol is simple and offers exciting possibilities. Several
654+
use cases are so common that they have been packaged into individual function
655+
calls. Properties, bound methods, static methods, and class methods are all
613656
based on the descriptor protocol.
614657

615658

@@ -619,7 +662,7 @@ Properties
619662
Calling :func:`property` is a succinct way of building a data descriptor that
620663
triggers function calls upon access to an attribute. Its signature is::
621664

622-
property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
665+
property(fget=None, fset=None, fdel=None, doc=None) -> property
623666

624667
The documentation shows a typical use to define a managed attribute ``x``::
625668

@@ -695,17 +738,30 @@ Functions and Methods
695738
Python's object oriented features are built upon a function based environment.
696739
Using non-data descriptors, the two are merged seamlessly.
697740

698-
Class dictionaries store methods as functions. In a class definition, methods
699-
are written using :keyword:`def` or :keyword:`lambda`, the usual tools for
700-
creating functions. Methods only differ from regular functions in that the
701-
first argument is reserved for the object instance. By Python convention, the
702-
instance reference is called *self* but may be called *this* or any other
703-
variable name.
741+
Functions stored in class dictionaries get turned into methods when invoked.
742+
Methods only differ from regular functions in that the object instance is
743+
prepended to the other arguments. By convention, the instance is called
744+
*self* but could be called *this* or any other variable name.
745+
746+
Methods can be created manually with :class:`types.MethodType` which is
747+
roughly equivalent to::
748+
749+
class Method:
750+
"Emulate Py_MethodType in Objects/classobject.c"
751+
752+
def __init__(self, func, obj):
753+
self.__func__ = func
754+
self.__self__ = obj
755+
756+
def __call__(self, *args, **kwargs):
757+
func = self.__func__
758+
obj = self.__self__
759+
return func(obj, *args, **kwargs)
704760

705-
To support method calls, functions include the :meth:`__get__` method for
706-
binding methods during attribute access. This means that all functions are
707-
non-data descriptors which return bound methods when they are invoked from an
708-
object. In pure Python, it works like this::
761+
To support automatic creation of methods, functions include the
762+
:meth:`__get__` method for binding methods during attribute access. This
763+
means that functions are non-data descriptors which return bound methods
764+
during dotted lookup from an instance. Here's how it works::
709765

710766
class Function:
711767
...
@@ -716,15 +772,20 @@ object. In pure Python, it works like this::
716772
return self
717773
return types.MethodType(self, obj)
718774

719-
Running the following in class in the interpreter shows how the function
775+
Running the following class in the interpreter shows how the function
720776
descriptor works in practice::
721777

722778
class D:
723779
def f(self, x):
724780
return x
725781

726-
Access through the class dictionary does not invoke :meth:`__get__`. Instead,
727-
it just returns the underlying function object::
782+
The function has a :term:`qualified name` attribute to support introspection::
783+
784+
>>> D.f.__qualname__
785+
'D.f'
786+
787+
Accessing the function through the class dictionary does not invoke
788+
:meth:`__get__`. Instead, it just returns the underlying function object::
728789

729790
>>> D.__dict__['f']
730791
<function D.f at 0x00C45070>
@@ -735,13 +796,8 @@ underlying function unchanged::
735796
>>> D.f
736797
<function D.f at 0x00C45070>
737798

738-
The function has a :term:`qualified name` attribute to support introspection::
739-
740-
>>> D.f.__qualname__
741-
'D.f'
742-
743-
Dotted access from an instance calls :meth:`__get__` which returns a bound
744-
method object::
799+
The interesting behavior occurs during dotted access from an instance. The
800+
dotted lookup calls :meth:`__get__` which returns a bound method object::
745801

746802
>>> d = D()
747803
>>> d.f
@@ -752,9 +808,13 @@ instance::
752808

753809
>>> d.f.__func__
754810
<function D.f at 0x1012e5ae8>
811+
755812
>>> d.f.__self__
756813
<__main__.D object at 0x1012e1f98>
757814

815+
If you have ever wondered where *self* comes from in regular methods or where
816+
*cls* comes from in class methods, this is it!
817+
758818

759819
Static Methods and Class Methods
760820
--------------------------------
@@ -798,8 +858,8 @@ in statistical work but does not directly depend on a particular dataset.
798858
It can be called either from an object or the class: ``s.erf(1.5) --> .9332`` or
799859
``Sample.erf(1.5) --> .9332``.
800860

801-
Since staticmethods return the underlying function with no changes, the example
802-
calls are unexciting::
861+
Since static methods return the underlying function with no changes, the
862+
example calls are unexciting::
803863

804864
class E:
805865
@staticmethod
@@ -840,7 +900,7 @@ for whether the caller is an object or a class::
840900

841901
This behavior is useful whenever the function only needs to have a class
842902
reference and does not care about any underlying data. One use for
843-
classmethods is to create alternate class constructors. The classmethod
903+
class methods is to create alternate class constructors. The classmethod
844904
:func:`dict.fromkeys` creates a new dictionary from a list of keys. The pure
845905
Python equivalent is::
846906

0 commit comments

Comments
 (0)