[3.12] gh-101100: Fix sphinx warnings in `howto/*` (GH-127084) · Pull Request #127311 · python/cpython
The :class:`Ten` class is a descriptor whose :meth:`__get__` method always The :class:`!Ten` class is a descriptor whose :meth:`~object.__get__` method always returns the constant ``10``:
.. testcode::
Besides showing how descriptors can run computations, this example also reveals the purpose of the parameters to :meth:`__get__`. The *self* reveals the purpose of the parameters to :meth:`~object.__get__`. The *self* parameter is *size*, an instance of *DirectorySize*. The *obj* parameter is either *g* or *s*, an instance of *Directory*. It is the *obj* parameter that lets the :meth:`__get__` method learn the target directory. The *objtype* lets the :meth:`~object.__get__` method learn the target directory. The *objtype* parameter is the class *Directory*.
In the following example, *age* is the public attribute and *_age* is the
In this example, the :class:`Person` class has two descriptor instances, *name* and *age*. When the :class:`Person` class is defined, it makes a callback to :meth:`__set_name__` in *LoggedAccess* so that the field names can In this example, the :class:`!Person` class has two descriptor instances, *name* and *age*. When the :class:`!Person` class is defined, it makes a callback to :meth:`~object.__set_name__` in *LoggedAccess* so that the field names can be recorded, giving each descriptor its own *public_name* and *private_name*:
.. testcode::
An interactive session shows that the :class:`Person` class has called :meth:`__set_name__` so that the field names would be recorded. Here An interactive session shows that the :class:`!Person` class has called :meth:`~object.__set_name__` so that the field names would be recorded. Here we call :func:`vars` to look up the descriptor without triggering it:
.. doctest::
A :term:`descriptor` is what we call any object that defines :meth:`__get__`, :meth:`__set__`, or :meth:`__delete__`. A :term:`descriptor` is what we call any object that defines :meth:`~object.__get__`, :meth:`~object.__set__`, or :meth:`~object.__delete__`.
Optionally, descriptors can have a :meth:`__set_name__` method. This is only Optionally, descriptors can have a :meth:`~object.__set_name__` method. This is only used in cases where a descriptor needs to know either the class where it was created or the name of class variable it was assigned to. (This method, if present, is called even if the class is not a descriptor.)
This :class:`Validator` class is both an :term:`abstract base class` and a This :class:`!Validator` class is both an :term:`abstract base class` and a managed attribute descriptor:
.. testcode::
Custom validators need to inherit from :class:`Validator` and must supply a :meth:`validate` method to test various restrictions as needed. Custom validators need to inherit from :class:`!Validator` and must supply a :meth:`!validate` method to test various restrictions as needed.
Custom validators -----------------
Here are three practical data validation utilities:
1) :class:`OneOf` verifies that a value is one of a restricted set of options. 1) :class:`!OneOf` verifies that a value is one of a restricted set of options.
2) :class:`Number` verifies that a value is either an :class:`int` or 2) :class:`!Number` verifies that a value is either an :class:`int` or :class:`float`. Optionally, it verifies that a value is between a given minimum or maximum.
3) :class:`String` verifies that a value is a :class:`str`. Optionally, it 3) :class:`!String` verifies that a value is a :class:`str`. Optionally, it validates a given minimum or maximum length. It can validate a user-defined `predicate <https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)>`_ as well.
In general, a descriptor is an attribute value that has one of the methods in the descriptor protocol. Those methods are :meth:`__get__`, :meth:`__set__`, and :meth:`__delete__`. If any of those methods are defined for an the descriptor protocol. Those methods are :meth:`~object.__get__`, :meth:`~object.__set__`, and :meth:`~object.__delete__`. If any of those methods are defined for an attribute, it is said to be a :term:`descriptor`.
The default behavior for attribute access is to get, set, or delete the
If an object defines :meth:`__set__` or :meth:`__delete__`, it is considered a data descriptor. Descriptors that only define :meth:`__get__` are called If an object defines :meth:`~object.__set__` or :meth:`~object.__delete__`, it is considered a data descriptor. Descriptors that only define :meth:`~object.__get__` are called non-data descriptors (they are often used for methods but other uses are possible).
To make a read-only data descriptor, define both :meth:`__get__` and :meth:`__set__` with the :meth:`__set__` raising an :exc:`AttributeError` when called. Defining the :meth:`__set__` method with an exception raising To make a read-only data descriptor, define both :meth:`~object.__get__` and :meth:`~object.__set__` with the :meth:`~object.__set__` raising an :exc:`AttributeError` when called. Defining the :meth:`~object.__set__` method with an exception raising placeholder is enough to make it a data descriptor.
Instance lookup scans through a chain of namespaces giving data descriptors the highest priority, followed by instance variables, then non-data descriptors, then class variables, and lastly :meth:`__getattr__` if it is descriptors, then class variables, and lastly :meth:`~object.__getattr__` if it is provided.
If a descriptor is found for ``a.x``, then it is invoked with:
Note, there is no :meth:`__getattr__` hook in the :meth:`__getattribute__` code. That is why calling :meth:`__getattribute__` directly or with ``super().__getattribute__`` will bypass :meth:`__getattr__` entirely. Note, there is no :meth:`~object.__getattr__` hook in the :meth:`~object.__getattribute__` code. That is why calling :meth:`~object.__getattribute__` directly or with ``super().__getattribute__`` will bypass :meth:`~object.__getattr__` entirely.
Instead, it is the dot operator and the :func:`getattr` function that are responsible for invoking :meth:`__getattr__` whenever :meth:`__getattribute__` responsible for invoking :meth:`~object.__getattr__` whenever :meth:`~object.__getattribute__` raises an :exc:`AttributeError`. Their logic is encapsulated in a helper function:
The logic for a dotted lookup such as ``A.x`` is in :meth:`type.__getattribute__`. The steps are similar to those for :meth:`object.__getattribute__` but the instance dictionary lookup is replaced :meth:`!type.__getattribute__`. The steps are similar to those for :meth:`!object.__getattribute__` but the instance dictionary lookup is replaced by a search through the class's :term:`method resolution order`.
If a descriptor is found, it is invoked with ``desc.__get__(None, A)``.
The logic for super's dotted lookup is in the :meth:`__getattribute__` method for The logic for super's dotted lookup is in the :meth:`~object.__getattribute__` method for object returned by :func:`super`.
A dotted lookup such as ``super(A, obj).m`` searches ``obj.__class__.__mro__``
The mechanism for descriptors is embedded in the :meth:`__getattribute__` The mechanism for descriptors is embedded in the :meth:`~object.__getattribute__` methods for :class:`object`, :class:`type`, and :func:`super`.
The important points to remember are:
* Descriptors are invoked by the :meth:`__getattribute__` method. * Descriptors are invoked by the :meth:`~object.__getattribute__` method.
* Classes inherit this machinery from :class:`object`, :class:`type`, or :func:`super`.
* Overriding :meth:`__getattribute__` prevents automatic descriptor calls * Overriding :meth:`~object.__getattribute__` prevents automatic descriptor calls because all the descriptor logic is in that method.
* :meth:`object.__getattribute__` and :meth:`type.__getattribute__` make different calls to :meth:`__get__`. The first includes the instance and may * :meth:`!object.__getattribute__` and :meth:`!type.__getattribute__` make different calls to :meth:`~object.__get__`. The first includes the instance and may include the class. The second puts in ``None`` for the instance and always includes the class.
The implementation details are in :c:func:`!type_new` and :c:func:`!set_names` in :source:`Objects/typeobject.c`.
Since the update logic is in :meth:`type.__new__`, notifications only take Since the update logic is in :meth:`!type.__new__`, notifications only take place at the time of class creation. If descriptors are added to the class afterwards, :meth:`__set_name__` will need to be called manually. afterwards, :meth:`~object.__set_name__` will need to be called manually.
ORM example
We can use the :class:`Field` class to define `models We can use the :class:`!Field` class to define `models <https://en.wikipedia.org/wiki/Database_model>`_ that describe the schema for each table in a database:
Either the built-in :func:`property` or our :func:`Property` equivalent would Either the built-in :func:`property` or our :func:`!Property` equivalent would work in this example.
To support automatic creation of methods, functions include the :meth:`__get__` method for binding methods during attribute access. This :meth:`~object.__get__` method for binding methods during attribute access. This means that functions are non-data descriptors that return bound methods during dotted lookup from an instance. Here's how it works:
Accessing the function through the class dictionary does not invoke :meth:`__get__`. Instead, it just returns the underlying function object:: :meth:`~object.__get__`. Instead, it just returns the underlying function object::
>>> D.__dict__['f'] <function D.f at 0x00C45070>
Dotted access from a class calls :meth:`__get__` which just returns the Dotted access from a class calls :meth:`~object.__get__` which just returns the underlying function unchanged::
>>> D.f <function D.f at 0x00C45070>
The interesting behavior occurs during dotted access from an instance. The dotted lookup calls :meth:`__get__` which returns a bound method object:: dotted lookup calls :meth:`~object.__get__` which returns a bound method object::
>>> d = D() >>> d.f
To recap, functions have a :meth:`__get__` method so that they can be converted To recap, functions have a :meth:`~object.__get__` method so that they can be converted to a method when accessed as attributes. The non-data descriptor transforms an ``obj.f(*args)`` call into ``f(obj, *args)``. Calling ``cls.f(*args)`` becomes ``f(*args)``.
The :meth:`type.__new__` method takes care of adding member objects to class The :meth:`!type.__new__` method takes care of adding member objects to class variables:
.. testcode::
To use the simulation in a real class, just inherit from :class:`Object` and To use the simulation in a real class, just inherit from :class:`!Object` and set the :term:`metaclass` to :class:`Type`:
.. testcode::