Skip to content

Commit

Permalink
[3.11] pythongh-103810: Fix broken references in dataclasses (pythonG…
Browse files Browse the repository at this point in the history
…H-103811)

Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
(cherry picked from commit 0f23eda)

Co-authored-by: Olga Matoula <olgamatoula@gmail.com>
  • Loading branch information
olgarithms authored and ambv committed Apr 24, 2023
1 parent 9f43643 commit 319ab15
Showing 1 changed file with 74 additions and 72 deletions.
146 changes: 74 additions & 72 deletions Doc/library/dataclasses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
--------------

This module provides a decorator and functions for automatically
adding generated :term:`special method`\s such as :meth:`__init__` and
:meth:`__repr__` to user-defined classes. It was originally described
adding generated :term:`special method`\s such as :meth:`~object.__init__` and
:meth:`~object.__repr__` to user-defined classes. It was originally described
in :pep:`557`.

The member variables to use in these generated methods are defined
Expand All @@ -31,7 +31,7 @@ using :pep:`526` type annotations. For example, this code::
def total_cost(self) -> float:
return self.unit_price * self.quantity_on_hand

will add, among other things, a :meth:`__init__` that looks like::
will add, among other things, a :meth:`~object.__init__` that looks like::

def __init__(self, name: str, unit_price: float, quantity_on_hand: int = 0):
self.name = name
Expand Down Expand Up @@ -86,105 +86,105 @@ Module contents

The parameters to :func:`dataclass` are:

- ``init``: If true (the default), a :meth:`__init__` method will be
- ``init``: If true (the default), a :meth:`~object.__init__` method will be
generated.

If the class already defines :meth:`__init__`, this parameter is
If the class already defines :meth:`~object.__init__`, this parameter is
ignored.

- ``repr``: If true (the default), a :meth:`__repr__` method will be
- ``repr``: If true (the default), a :meth:`~object.__repr__` method will be
generated. The generated repr string will have the class name and
the name and repr of each field, in the order they are defined in
the class. Fields that are marked as being excluded from the repr
are not included. For example:
``InventoryItem(name='widget', unit_price=3.0, quantity_on_hand=10)``.

If the class already defines :meth:`__repr__`, this parameter is
If the class already defines :meth:`~object.__repr__`, this parameter is
ignored.

- ``eq``: If true (the default), an :meth:`__eq__` method will be
- ``eq``: If true (the default), an :meth:`~object.__eq__` method will be
generated. This method compares the class as if it were a tuple
of its fields, in order. Both instances in the comparison must
be of the identical type.

If the class already defines :meth:`__eq__`, this parameter is
If the class already defines :meth:`~object.__eq__`, this parameter is
ignored.

- ``order``: If true (the default is ``False``), :meth:`__lt__`,
:meth:`__le__`, :meth:`__gt__`, and :meth:`__ge__` methods will be
- ``order``: If true (the default is ``False``), :meth:`~object.__lt__`,
:meth:`~object.__le__`, :meth:`~object.__gt__`, and :meth:`~object.__ge__` methods will be
generated. These compare the class as if it were a tuple of its
fields, in order. Both instances in the comparison must be of the
identical type. If ``order`` is true and ``eq`` is false, a
:exc:`ValueError` is raised.

If the class already defines any of :meth:`__lt__`,
:meth:`__le__`, :meth:`__gt__`, or :meth:`__ge__`, then
If the class already defines any of :meth:`~object.__lt__`,
:meth:`~object.__le__`, :meth:`~object.__gt__`, or :meth:`~object.__ge__`, then
:exc:`TypeError` is raised.

- ``unsafe_hash``: If ``False`` (the default), a :meth:`__hash__` method
- ``unsafe_hash``: If ``False`` (the default), a :meth:`~object.__hash__` method
is generated according to how ``eq`` and ``frozen`` are set.

:meth:`__hash__` is used by built-in :meth:`hash()`, and when objects are
:meth:`~object.__hash__` is used by built-in :meth:`hash()`, and when objects are
added to hashed collections such as dictionaries and sets. Having a
:meth:`__hash__` implies that instances of the class are immutable.
:meth:`~object.__hash__` implies that instances of the class are immutable.
Mutability is a complicated property that depends on the programmer's
intent, the existence and behavior of :meth:`__eq__`, and the values of
intent, the existence and behavior of :meth:`~object.__eq__`, and the values of
the ``eq`` and ``frozen`` flags in the :func:`dataclass` decorator.

By default, :func:`dataclass` will not implicitly add a :meth:`__hash__`
By default, :func:`dataclass` will not implicitly add a :meth:`~object.__hash__`
method unless it is safe to do so. Neither will it add or change an
existing explicitly defined :meth:`__hash__` method. Setting the class
existing explicitly defined :meth:`~object.__hash__` method. Setting the class
attribute ``__hash__ = None`` has a specific meaning to Python, as
described in the :meth:`__hash__` documentation.
described in the :meth:`~object.__hash__` documentation.

If :meth:`__hash__` is not explicitly defined, or if it is set to ``None``,
then :func:`dataclass` *may* add an implicit :meth:`__hash__` method.
If :meth:`~object.__hash__` is not explicitly defined, or if it is set to ``None``,
then :func:`dataclass` *may* add an implicit :meth:`~object.__hash__` method.
Although not recommended, you can force :func:`dataclass` to create a
:meth:`__hash__` method with ``unsafe_hash=True``. This might be the case
:meth:`~object.__hash__` method with ``unsafe_hash=True``. This might be the case
if your class is logically immutable but can nonetheless be mutated.
This is a specialized use case and should be considered carefully.

Here are the rules governing implicit creation of a :meth:`__hash__`
method. Note that you cannot both have an explicit :meth:`__hash__`
Here are the rules governing implicit creation of a :meth:`~object.__hash__`
method. Note that you cannot both have an explicit :meth:`~object.__hash__`
method in your dataclass and set ``unsafe_hash=True``; this will result
in a :exc:`TypeError`.

If ``eq`` and ``frozen`` are both true, by default :func:`dataclass` will
generate a :meth:`__hash__` method for you. If ``eq`` is true and
``frozen`` is false, :meth:`__hash__` will be set to ``None``, marking it
generate a :meth:`~object.__hash__` method for you. If ``eq`` is true and
``frozen`` is false, :meth:`~object.__hash__` will be set to ``None``, marking it
unhashable (which it is, since it is mutable). If ``eq`` is false,
:meth:`__hash__` will be left untouched meaning the :meth:`__hash__`
:meth:`~object.__hash__` will be left untouched meaning the :meth:`~object.__hash__`
method of the superclass will be used (if the superclass is
:class:`object`, this means it will fall back to id-based hashing).

- ``frozen``: If true (the default is ``False``), assigning to fields will
generate an exception. This emulates read-only frozen instances. If
:meth:`__setattr__` or :meth:`__delattr__` is defined in the class, then
:meth:`~object.__setattr__` or :meth:`~object.__delattr__` is defined in the class, then
:exc:`TypeError` is raised. See the discussion below.

- ``match_args``: If true (the default is ``True``), the
``__match_args__`` tuple will be created from the list of
parameters to the generated :meth:`__init__` method (even if
:meth:`__init__` is not generated, see above). If false, or if
parameters to the generated :meth:`~object.__init__` method (even if
:meth:`~object.__init__` is not generated, see above). If false, or if
``__match_args__`` is already defined in the class, then
``__match_args__`` will not be generated.

.. versionadded:: 3.10

- ``kw_only``: If true (the default value is ``False``), then all
fields will be marked as keyword-only. If a field is marked as
keyword-only, then the only effect is that the :meth:`__init__`
keyword-only, then the only effect is that the :meth:`~object.__init__`
parameter generated from a keyword-only field must be specified
with a keyword when :meth:`__init__` is called. There is no
with a keyword when :meth:`~object.__init__` is called. There is no
effect on any other aspect of dataclasses. See the
:term:`parameter` glossary entry for details. Also see the
:const:`KW_ONLY` section.

.. versionadded:: 3.10

- ``slots``: If true (the default is ``False``), :attr:`__slots__` attribute
- ``slots``: If true (the default is ``False``), :attr:`~object.__slots__` attribute
will be generated and new class will be returned instead of the original one.
If :attr:`__slots__` is already defined in the class, then :exc:`TypeError`
If :attr:`~object.__slots__` is already defined in the class, then :exc:`TypeError`
is raised.

.. versionadded:: 3.10
Expand Down Expand Up @@ -215,7 +215,7 @@ Module contents
b: int = 0 # assign a default value for 'b'

In this example, both ``a`` and ``b`` will be included in the added
:meth:`__init__` method, which will be defined as::
:meth:`~object.__init__` method, which will be defined as::

def __init__(self, a: int, b: int = 0):

Expand Down Expand Up @@ -256,13 +256,13 @@ Module contents
error to specify both ``default`` and ``default_factory``.

- ``init``: If true (the default), this field is included as a
parameter to the generated :meth:`__init__` method.
parameter to the generated :meth:`~object.__init__` method.

- ``repr``: If true (the default), this field is included in the
string returned by the generated :meth:`__repr__` method.
string returned by the generated :meth:`~object.__repr__` method.

- ``hash``: This can be a bool or ``None``. If true, this field is
included in the generated :meth:`__hash__` method. If ``None`` (the
included in the generated :meth:`~object.__hash__` method. If ``None`` (the
default), use the value of ``compare``: this would normally be
the expected behavior. A field should be considered in the hash
if it's used for comparisons. Setting this value to anything
Expand All @@ -275,8 +275,8 @@ Module contents
is excluded from the hash, it will still be used for comparisons.

- ``compare``: If true (the default), this field is included in the
generated equality and comparison methods (:meth:`__eq__`,
:meth:`__gt__`, et al.).
generated equality and comparison methods (:meth:`~object.__eq__`,
:meth:`~object.__gt__`, et al.).

- ``metadata``: This can be a mapping or None. None is treated as
an empty dict. This value is wrapped in
Expand All @@ -287,7 +287,7 @@ Module contents
namespace in the metadata.

- ``kw_only``: If true, this field will be marked as keyword-only.
This is used when the generated :meth:`__init__` method's
This is used when the generated :meth:`~object.__init__` method's
parameters are computed.

.. versionadded:: 3.10
Expand Down Expand Up @@ -431,21 +431,21 @@ Module contents
Class, raises :exc:`TypeError`. If values in ``changes`` do not
specify fields, raises :exc:`TypeError`.

The newly returned object is created by calling the :meth:`__init__`
The newly returned object is created by calling the :meth:`~object.__init__`
method of the dataclass. This ensures that
:meth:`__post_init__`, if present, is also called.
:ref:`__post_init__ <post-init-processing>`, if present, is also called.

Init-only variables without default values, if any exist, must be
specified on the call to :func:`replace` so that they can be passed to
:meth:`__init__` and :meth:`__post_init__`.
:meth:`~object.__init__` and :ref:`__post_init__ <post-init-processing>`.

It is an error for ``changes`` to contain any fields that are
defined as having ``init=False``. A :exc:`ValueError` will be raised
in this case.

Be forewarned about how ``init=False`` fields work during a call to
:func:`replace`. They are not copied from the source object, but
rather are initialized in :meth:`__post_init__`, if they're
rather are initialized in :ref:`__post_init__ <post-init-processing>`, if they're
initialized at all. It is expected that ``init=False`` fields will
be rarely and judiciously used. If they are used, it might be wise
to have alternate class constructors, or perhaps a custom
Expand Down Expand Up @@ -476,7 +476,7 @@ Module contents
:const:`KW_ONLY` is otherwise completely ignored. This includes the
name of such a field. By convention, a name of ``_`` is used for a
:const:`KW_ONLY` field. Keyword-only fields signify
:meth:`__init__` parameters that must be specified as keywords when
:meth:`~object.__init__` parameters that must be specified as keywords when
the class is instantiated.

In this example, the fields ``y`` and ``z`` will be marked as keyword-only fields::
Expand All @@ -497,20 +497,22 @@ Module contents

.. exception:: FrozenInstanceError

Raised when an implicitly defined :meth:`__setattr__` or
:meth:`__delattr__` is called on a dataclass which was defined with
Raised when an implicitly defined :meth:`~object.__setattr__` or
:meth:`~object.__delattr__` is called on a dataclass which was defined with
``frozen=True``. It is a subclass of :exc:`AttributeError`.

.. _post-init-processing:

Post-init processing
--------------------

The generated :meth:`__init__` code will call a method named
:meth:`__post_init__`, if :meth:`__post_init__` is defined on the
The generated :meth:`~object.__init__` code will call a method named
:meth:`!__post_init__`, if :meth:`!__post_init__` is defined on the
class. It will normally be called as ``self.__post_init__()``.
However, if any ``InitVar`` fields are defined, they will also be
passed to :meth:`__post_init__` in the order they were defined in the
class. If no :meth:`__init__` method is generated, then
:meth:`__post_init__` will not automatically be called.
passed to :meth:`!__post_init__` in the order they were defined in the
class. If no :meth:`~object.__init__` method is generated, then
:meth:`!__post_init__` will not automatically be called.

Among other uses, this allows for initializing field values that
depend on one or more other fields. For example::
Expand All @@ -524,10 +526,10 @@ depend on one or more other fields. For example::
def __post_init__(self):
self.c = self.a + self.b

The :meth:`__init__` method generated by :func:`dataclass` does not call base
class :meth:`__init__` methods. If the base class has an :meth:`__init__` method
The :meth:`~object.__init__` method generated by :func:`dataclass` does not call base
class :meth:`~object.__init__` methods. If the base class has an :meth:`~object.__init__` method
that has to be called, it is common to call this method in a
:meth:`__post_init__` method::
:meth:`!__post_init__` method::

@dataclass
class Rectangle:
Expand All @@ -541,12 +543,12 @@ that has to be called, it is common to call this method in a
def __post_init__(self):
super().__init__(self.side, self.side)

Note, however, that in general the dataclass-generated :meth:`__init__` methods
Note, however, that in general the dataclass-generated :meth:`~object.__init__` methods
don't need to be called, since the derived dataclass will take care of
initializing all fields of any base class that is a dataclass itself.

See the section below on init-only variables for ways to pass
parameters to :meth:`__post_init__`. Also see the warning about how
parameters to :meth:`!__post_init__`. Also see the warning about how
:func:`replace` handles ``init=False`` fields.

Class variables
Expand All @@ -569,8 +571,8 @@ if the type of a field is of type ``dataclasses.InitVar``. If a field
is an ``InitVar``, it is considered a pseudo-field called an init-only
field. As it is not a true field, it is not returned by the
module-level :func:`fields` function. Init-only fields are added as
parameters to the generated :meth:`__init__` method, and are passed to
the optional :meth:`__post_init__` method. They are not otherwise used
parameters to the generated :meth:`~object.__init__` method, and are passed to
the optional :ref:`__post_init__ <post-init-processing>` method. They are not otherwise used
by dataclasses.

For example, suppose a field will be initialized from a database, if a
Expand All @@ -597,12 +599,12 @@ Frozen instances
It is not possible to create truly immutable Python objects. However,
by passing ``frozen=True`` to the :meth:`dataclass` decorator you can
emulate immutability. In that case, dataclasses will add
:meth:`__setattr__` and :meth:`__delattr__` methods to the class. These
:meth:`~object.__setattr__` and :meth:`~object.__delattr__` methods to the class. These
methods will raise a :exc:`FrozenInstanceError` when invoked.

There is a tiny performance penalty when using ``frozen=True``:
:meth:`__init__` cannot use simple assignment to initialize fields, and
must use :meth:`object.__setattr__`.
:meth:`~object.__init__` cannot use simple assignment to initialize fields, and
must use :meth:`~object.__setattr__`.

Inheritance
-----------
Expand Down Expand Up @@ -630,14 +632,14 @@ example::
The final list of fields is, in order, ``x``, ``y``, ``z``. The final
type of ``x`` is ``int``, as specified in class ``C``.

The generated :meth:`__init__` method for ``C`` will look like::
The generated :meth:`~object.__init__` method for ``C`` will look like::

def __init__(self, x: int = 15, y: int = 0, z: int = 10):

Re-ordering of keyword-only parameters in :meth:`__init__`
----------------------------------------------------------
Re-ordering of keyword-only parameters in :meth:`~object.__init__`
------------------------------------------------------------------

After the parameters needed for :meth:`__init__` are computed, any
After the parameters needed for :meth:`~object.__init__` are computed, any
keyword-only parameters are moved to come after all regular
(non-keyword-only) parameters. This is a requirement of how
keyword-only parameters are implemented in Python: they must come
Expand All @@ -658,7 +660,7 @@ fields, and ``Base.x`` and ``D.z`` are regular fields::
z: int = 10
t: int = field(kw_only=True, default=0)

The generated :meth:`__init__` method for ``D`` will look like::
The generated :meth:`~object.__init__` method for ``D`` will look like::

def __init__(self, x: Any = 15.0, z: int = 10, *, y: int = 0, w: int = 1, t: int = 0):

Expand All @@ -667,7 +669,7 @@ the list of fields: parameters derived from regular fields are
followed by parameters derived from keyword-only fields.

The relative ordering of keyword-only parameters is maintained in the
re-ordered :meth:`__init__` parameter list.
re-ordered :meth:`~object.__init__` parameter list.


Default factory functions
Expand All @@ -679,10 +681,10 @@ example, to create a new instance of a list, use::

mylist: list = field(default_factory=list)

If a field is excluded from :meth:`__init__` (using ``init=False``)
If a field is excluded from :meth:`~object.__init__` (using ``init=False``)
and the field also specifies ``default_factory``, then the default
factory function will always be called from the generated
:meth:`__init__` function. This happens because there is no other
:meth:`~object.__init__` function. This happens because there is no other
way to give the field an initial value.

Mutable default values
Expand Down

0 comments on commit 319ab15

Please sign in to comment.