@@ -52,7 +52,7 @@ To use the descriptor, it must be stored as a class variable in another class::
52
52
53
53
class A:
54
54
x = 5 # Regular class attribute
55
- y = Ten() # Descriptor
55
+ y = Ten() # Descriptor instance
56
56
57
57
An interactive session shows the difference between normal attribute lookup
58
58
and descriptor lookup::
@@ -80,7 +80,6 @@ Dynamic lookups
80
80
81
81
Interesting descriptors typically run computations instead of doing lookups::
82
82
83
-
84
83
import os
85
84
86
85
class DirectorySize:
@@ -90,7 +89,7 @@ Interesting descriptors typically run computations instead of doing lookups::
90
89
91
90
class Directory:
92
91
93
- size = DirectorySize() # Descriptor
92
+ size = DirectorySize() # Descriptor instance
94
93
95
94
def __init__(self, dirname):
96
95
self.dirname = dirname # Regular instance attribute
@@ -147,11 +146,11 @@ the lookup or update::
147
146
148
147
class Person:
149
148
150
- age = LoggedAgeAccess() # Descriptor
149
+ age = LoggedAgeAccess() # Descriptor instance
151
150
152
151
def __init__(self, name, age):
153
152
self.name = name # Regular instance attribute
154
- self.age = age # Calls the descriptor
153
+ self.age = age # Calls __set__()
155
154
156
155
def birthday(self):
157
156
self.age += 1 # Calls both __get__() and __set__()
@@ -221,8 +220,8 @@ be recorded, giving each descriptor its own *public_name* and *private_name*::
221
220
222
221
class Person:
223
222
224
- name = LoggedAccess() # First descriptor
225
- age = LoggedAccess() # Second descriptor
223
+ name = LoggedAccess() # First descriptor instance
224
+ age = LoggedAccess() # Second descriptor instance
226
225
227
226
def __init__(self, name, age):
228
227
self.name = name # Calls the first descriptor
@@ -494,56 +493,98 @@ called. Defining the :meth:`__set__` method with an exception raising
494
493
placeholder is enough to make it a data descriptor.
495
494
496
495
497
- Invoking Descriptors
498
- --------------------
496
+ Overview of Descriptor Invocation
497
+ ---------------------------------
499
498
500
- A descriptor can be called directly by its method name. For example,
501
- ``d .__get__(obj ) ``.
499
+ A descriptor can be called directly with `` desc.__get__(obj) `` or
500
+ ``desc .__get__(None, cls ) ``.
502
501
503
502
But it is more common for a descriptor to be invoked automatically from
504
- attribute access. The expression ``obj.d `` looks up ``d `` in the dictionary of
505
- ``obj ``. If ``d `` defines the method :meth: `__get__ `, then ``d.__get__(obj) ``
506
- is invoked according to the precedence rules listed below.
503
+ attribute access.
504
+
505
+ The expression ``obj.x `` looks up the attribute ``x `` in the chain of
506
+ namespaces for ``obj ``. If the search finds a descriptor, its :meth: `__get__ `
507
+ method is invoked according to the precedence rules listed below.
507
508
508
509
The details of invocation depend on whether ``obj `` is an object, class, or
509
510
instance of super.
510
511
511
- **Objects **: The machinery is in :meth: `object.__getattribute__ `.
512
512
513
- It transforms ``b.x `` into ``type(b).__dict__['x'].__get__(b, type(b)) ``.
513
+ Invocation from an Instance
514
+ ---------------------------
515
+
516
+ Instance lookup scans through a chain of namespaces giving data descriptors
517
+ the highest priority, followed by instance variables, then non-data
518
+ descriptors, then class variables, and lastly :meth: `__getattr__ ` if it is
519
+ provided.
520
+
521
+ If a descriptor is found for ``a.x ``, then it is invoked with:
522
+ ``desc.__get__(a, type(a)) ``.
523
+
524
+ The logic for a dotted lookup is in :meth: `object.__getattribute__ `. Here is
525
+ a pure Python equivalent::
526
+
527
+ def object_getattribute(obj, name):
528
+ "Emulate PyObject_GenericGetAttr() in Objects/object.c"
529
+ null = object()
530
+ objtype = type(obj)
531
+ value = getattr(objtype, name, null)
532
+ if value is not null and hasattr(value, '__get__'):
533
+ if hasattr(value, '__set__') or hasattr(value, '__delete__'):
534
+ return value.__get__(obj, objtype) # data descriptor
535
+ try:
536
+ return vars(obj)[name] # instance variable
537
+ except (KeyError, TypeError):
538
+ pass
539
+ if hasattr(value, '__get__'):
540
+ return value.__get__(obj, objtype) # non-data descriptor
541
+ if value is not null:
542
+ return value # class variable
543
+ # Emulate slot_tp_getattr_hook() in Objects/typeobject.c
544
+ if hasattr(objtype, '__getattr__'):
545
+ return objtype.__getattr__(obj, name) # __getattr__ hook
546
+ raise AttributeError(name)
547
+
548
+ The :exc: `TypeError ` exception handler is needed because the instance dictionary
549
+ doesn't exist when its class defines :term: `__slots__ `.
514
550
515
- The implementation works through a precedence chain that gives data descriptors
516
- priority over instance variables, instance variables priority over non-data
517
- descriptors, and assigns lowest priority to :meth: `__getattr__ ` if provided.
518
551
519
- The full C implementation can be found in :c:func: ` PyObject_GenericGetAttr() ` in
520
- :source: ` Objects/object.c `.
552
+ Invocation from a Class
553
+ -----------------------
521
554
522
- **Classes **: The machinery is in :meth: `type.__getattribute__ `.
555
+ The logic for a dotted lookup such as ``A.x `` is in
556
+ :meth: `type.__getattribute__ `. The steps are similar to those for
557
+ :meth: `object.__getattribute__ ` but the instance dictionary lookup is replaced
558
+ by a search through the class's :term: `method resolution order `.
523
559
524
- It transforms `` A.x `` into `` A.__dict__['x'] .__get__(None, A) ``.
560
+ If a descriptor is found, it is invoked with `` desc .__get__(None, A) ``.
525
561
526
- The full C implementation can be found in :c:func: `type_getattro() ` in
527
- :source: `Objects/typeobject.c `.
562
+ The full C implementation can be found in :c:func: `type_getattro() ` and
563
+ :c:func: ` _PyType_Lookup() ` in : source: `Objects/typeobject.c `.
528
564
529
- **Super **: The machinery is in the custom :meth: `__getattribute__ ` method for
565
+
566
+ Invocation from Super
567
+ ---------------------
568
+
569
+ The logic for super's dotted lookup is in the :meth: `__getattribute__ ` method for
530
570
object returned by :class: `super() `.
531
571
532
- The attribute lookup ``super(A, obj).m `` searches ``obj.__class__.__mro__ `` for
533
- the base class ``B `` immediately following ``A `` and then returns
572
+ A dotted lookup such as ``super(A, obj).m `` searches ``obj.__class__.__mro__ ``
573
+ for the base class ``B `` immediately following ``A `` and then returns
534
574
``B.__dict__['m'].__get__(obj, A) ``. If not a descriptor, ``m `` is returned
535
- unchanged. If not in the dictionary, ``m `` reverts to a search using
536
- :meth: `object.__getattribute__ `.
575
+ unchanged.
537
576
538
- The implementation details are in :c:func: `super_getattro() ` in
577
+ The full C implementation can be found in :c:func: `super_getattro() ` in
539
578
:source: `Objects/typeobject.c `. A pure Python equivalent can be found in
540
- `Guido's Tutorial `_.
579
+ `Guido's Tutorial
580
+ <https://www.python.org/download/releases/2.2.3/descrintro/#cooperation> `_.
541
581
542
- .. _`Guido's Tutorial` : https://www.python.org/download/releases/2.2.3/descrintro/#cooperation
543
582
544
- **Summary **: The mechanism for descriptors is embedded in the
545
- :meth: `__getattribute__() ` methods for :class: `object `, :class: `type `, and
546
- :func: `super `.
583
+ Summary of Invocation Logic
584
+ ---------------------------
585
+
586
+ The mechanism for descriptors is embedded in the :meth: `__getattribute__() `
587
+ methods for :class: `object `, :class: `type `, and :func: `super `.
547
588
548
589
The important points to remember are:
549
590
@@ -652,7 +693,7 @@ Pure Python Equivalents
652
693
^^^^^^^^^^^^^^^^^^^^^^^
653
694
654
695
The descriptor protocol is simple and offers exciting possibilities. Several
655
- use cases are so common that they have been prepackaged into builtin tools.
696
+ use cases are so common that they have been prepackaged into built-in tools.
656
697
Properties, bound methods, static methods, and class methods are all based on
657
698
the descriptor protocol.
658
699
0 commit comments