@@ -13,7 +13,7 @@ Descriptor HowTo Guide
1313:term: `Descriptors <descriptor> ` let objects customize attribute lookup,
1414storage, and deletion.
1515
16- This HowTo guide has three major sections:
16+ This guide has four major sections:
1717
18181) The "primer" gives a basic overview, moving gently from simple examples,
1919 adding one feature at a time. It is a great place to start.
@@ -25,6 +25,11 @@ This HowTo guide has three major sections:
2525 detailed mechanics of how descriptors work. Most people don't need this
2626 level of detail.
2727
28+ 4) The last section has pure Python equivalents for built-in descriptors that
29+ are written in C. Read this if you're curious about how functions turn
30+ into bound methods or about how to implement common tools like
31+ :func: `classmethod `, :func: `staticmethod `, and :func: `property `.
32+
2833
2934Primer
3035^^^^^^
@@ -99,7 +104,7 @@ different, updated answers each time::
99104 3
100105 >>> os.system('touch games/newfile') # Add a fourth file to the directory
101106 0
102- >>> g.size
107+ >>> g.size # Automatically updated
103108 4
104109 >>> s.size # The songs directory has twenty files
105110 20
@@ -197,7 +202,7 @@ be recorded, giving each descriptor its own *public_name* and *private_name*::
197202
198203 import logging
199204
200- logging.basicConfig(level=logging.INFO, force=True )
205+ logging.basicConfig(level=logging.INFO)
201206
202207 class LoggedAccess:
203208
@@ -259,7 +264,7 @@ A :term:`descriptor` is what we call any object that defines :meth:`__get__`,
259264:meth: `__set__ `, or :meth: `__delete__ `.
260265
261266Optionally, 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
267+ used in cases where a descriptor needs to know either the class where it was
263268created or the name of class variable it was assigned to.
264269
265270Descriptors get invoked by the dot operator during attribute lookup. If a
@@ -318,7 +323,7 @@ managed attribute descriptor::
318323 def validate(self, value):
319324 pass
320325
321- Custom validators need to subclass from :class: `Validator ` and supply a
326+ Custom validators need to inherit from :class: `Validator ` and must supply a
322327:meth: `validate ` method to test various restrictions as needed.
323328
324329
@@ -334,8 +339,9 @@ Here are three practical data validation utilities:
334339 minimum or maximum.
335340
3363413) :class: `String ` verifies that a value is a :class: `str `. Optionally, it
337- validates a given minimum or maximum length. Optionally, it can test for
338- another predicate as well.
342+ validates a given minimum or maximum length. It can validate a
343+ user-defined `predicate
344+ <https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)> `_ as well.
339345
340346::
341347
@@ -398,7 +404,7 @@ Here's how the data validators can be used in a real class::
398404 class Component:
399405
400406 name = String(minsize=3, maxsize=10, predicate=str.isupper)
401- kind = OneOf('plastic ', 'metal')
407+ kind = OneOf('wood ', 'metal', 'plastic ')
402408 quantity = Number(minvalue=0)
403409
404410 def __init__(self, name, kind, quantity):
@@ -426,9 +432,7 @@ Abstract
426432--------
427433
428434Defines descriptors, summarizes the protocol, and shows how descriptors are
429- called. Examines a custom descriptor and several built-in Python descriptors
430- including functions, properties, static methods, and class methods. Shows how
431- each works by giving a pure Python equivalent and a sample application.
435+ called. Provides an example showing how object relational mappings work.
432436
433437Learning about descriptors not only provides access to a larger toolset, it
434438creates a deeper understanding of how Python works and an appreciation for the
@@ -519,34 +523,27 @@ The full C implementation can be found in :c:func:`PyObject_GenericGetAttr()` in
519523
520524It transforms ``A.x `` into ``A.__dict__['x'].__get__(None, A) ``.
521525
522- In pure Python, it looks like this::
523-
524- def __getattribute__(cls, key):
525- "Emulate type_getattro() in Objects/typeobject.c"
526- v = object.__getattribute__(cls, key)
527- if hasattr(v, '__get__'):
528- return v.__get__(None, cls)
529- return v
526+ The full C implementation can be found in :c:func: `type_getattro() ` in
527+ :source: `Objects/typeobject.c `.
530528
531529**Super **: The machinery is in the custom :meth: `__getattribute__ ` method for
532530object returned by :class: `super() `.
533531
534532The attribute lookup ``super(A, obj).m `` searches ``obj.__class__.__mro__ `` for
535533the base class ``B `` immediately following ``A `` and then returns
536- ``B.__dict__['m'].__get__(obj, A) ``.
537-
538- If not a descriptor, ``m `` is returned unchanged. If not in the dictionary,
539- ``m `` reverts to a search using :meth: `object.__getattribute__ `.
534+ ``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__ `.
540537
541538The implementation details are in :c:func: `super_getattro() ` in
542539:source: `Objects/typeobject.c `. A pure Python equivalent can be found in
543540`Guido's Tutorial `_.
544541
545542.. _`Guido's Tutorial` : https://www.python.org/download/releases/2.2.3/descrintro/#cooperation
546543
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 `.
544+ **Summary **: The mechanism for descriptors is embedded in the
545+ :meth: `__getattribute__() ` methods for :class: `object `, :class: ` type `, and
546+ :func: `super `.
550547
551548The important points to remember are:
552549
@@ -586,15 +583,16 @@ place at the time of class creation. If descriptors are added to the class
586583afterwards, :meth: `__set_name__ ` will need to be called manually.
587584
588585
589- Descriptor Example
590- ------------------
586+ ORM Example
587+ -----------
591588
592589The following code is simplified skeleton showing how data descriptors could
593590be used to implement an `object relational mapping
594591<https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping> `_.
595592
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::
593+ The essential idea is that the data is stored in an external database. The
594+ Python instances only hold keys to the database's tables. Descriptors take
595+ care of lookups or updates::
598596
599597 class Field:
600598
@@ -609,8 +607,8 @@ actual data is stored in an external table that is being dynamically updated::
609607 conn.execute(self.store, [value, obj.key])
610608 conn.commit()
611609
612- We can use the :class: `Field ` to define "models" that describe the schema for
613- each table in a database::
610+ We can use the :class: `Field ` class to define "models" that describe the schema
611+ for each table in a database::
614612
615613 class Movie:
616614 table = 'Movies' # Table name
@@ -650,10 +648,13 @@ it can be updated::
650648 >>> Movie('Star Wars').director
651649 'J.J. Abrams'
652650
651+ Pure Python Equivalents
652+ ^^^^^^^^^^^^^^^^^^^^^^^
653+
653654The 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
656- based on the descriptor protocol.
655+ use cases are so common that they have been prepackaged into builtin tools.
656+ Properties, bound methods, static methods, and class methods are all based on
657+ the descriptor protocol.
657658
658659
659660Properties
@@ -746,7 +747,7 @@ prepended to the other arguments. By convention, the instance is called
746747Methods can be created manually with :class: `types.MethodType ` which is
747748roughly equivalent to::
748749
749- class Method :
750+ class MethodType :
750751 "Emulate Py_MethodType in Objects/classobject.c"
751752
752753 def __init__(self, func, obj):
@@ -770,7 +771,7 @@ during dotted lookup from an instance. Here's how it works::
770771 "Simulate func_descr_get() in Objects/funcobject.c"
771772 if obj is None:
772773 return self
773- return types. MethodType(self, obj)
774+ return MethodType(self, obj)
774775
775776Running the following class in the interpreter shows how the function
776777descriptor works in practice::
@@ -816,8 +817,8 @@ If you have ever wondered where *self* comes from in regular methods or where
816817*cls * comes from in class methods, this is it!
817818
818819
819- Static Methods and Class Methods
820- --------------------------------
820+ Static Methods
821+ --------------
821822
822823Non-data descriptors provide a simple mechanism for variations on the usual
823824patterns of binding functions into methods.
@@ -883,6 +884,10 @@ Using the non-data descriptor protocol, a pure Python version of
883884 def __get__(self, obj, objtype=None):
884885 return self.f
885886
887+
888+ Class Methods
889+ -------------
890+
886891Unlike static methods, class methods prepend the class reference to the
887892argument list before calling the function. This format is the same
888893for whether the caller is an object or a class::
@@ -897,12 +902,11 @@ for whether the caller is an object or a class::
897902 >>> print(F().f(3))
898903 ('F', 3)
899904
900-
901- This behavior is useful whenever the function only needs to have a class
902- reference and does not care about any underlying data. One use for
903- class methods is to create alternate class constructors. The classmethod
904- :func: `dict.fromkeys ` creates a new dictionary from a list of keys. The pure
905- Python equivalent is::
905+ This behavior is useful whenever the method only needs to have a class
906+ reference and does rely on data stored in a specific instance. One use for
907+ class methods is to create alternate class constructors. For example, the
908+ classmethod :func: `dict.fromkeys ` creates a new dictionary from a list of
909+ keys. The pure Python equivalent is::
906910
907911 class Dict:
908912 ...
@@ -934,7 +938,7 @@ Using the non-data descriptor protocol, a pure Python version of
934938 cls = type(obj)
935939 if hasattr(obj, '__get__'):
936940 return self.f.__get__(cls)
937- return types. MethodType(self.f, cls)
941+ return MethodType(self.f, cls)
938942
939943The code path for ``hasattr(obj, '__get__') `` was added in Python 3.9 and
940944makes it possible for :func: `classmethod ` to support chained decorators.
0 commit comments