Hopefully the final set of edits.
This commit is contained in:
parent
2a00e91583
commit
2074efb171
112
pep-0557.rst
112
pep-0557.rst
|
@ -6,7 +6,7 @@ Type: Standards Track
|
|||
Content-Type: text/x-rst
|
||||
Created: 02-Jun-2017
|
||||
Python-Version: 3.7
|
||||
Post-History: 08-Sep-2017, 25-Nov-2017
|
||||
Post-History: 08-Sep-2017, 25-Nov-2017, 30-Nov-2017
|
||||
|
||||
Notice for Reviewers
|
||||
====================
|
||||
|
@ -57,7 +57,7 @@ to the InventoryItem class::
|
|||
self.unit_price = unit_price
|
||||
self.quantity_on_hand = quantity_on_hand
|
||||
def __repr__(self):
|
||||
return f'InventoryItem(name={self.name!r},unit_price={self.unit_price!r},quantity_on_hand={self.quantity_on_hand!r})'
|
||||
return f'InventoryItem(name={self.name!r}, unit_price={self.unit_price!r}, quantity_on_hand={self.quantity_on_hand!r})'
|
||||
def __eq__(self, other):
|
||||
if other.__class__ is self.__class__:
|
||||
return (self.name, self.unit_price, self.quantity_on_hand) == (other.name, other.unit_price, other.quantity_on_hand)
|
||||
|
@ -83,7 +83,7 @@ to the InventoryItem class::
|
|||
return (self.name, self.unit_price, self.quantity_on_hand) >= (other.name, other.unit_price, other.quantity_on_hand)
|
||||
return NotImplemented
|
||||
|
||||
Data Classes saves you from writing and maintaining these functions.
|
||||
Data Classes saves you from writing and maintaining these methods.
|
||||
|
||||
Rationale
|
||||
=========
|
||||
|
@ -157,11 +157,16 @@ Note that ``__annotations__`` is guaranteed to be an ordered mapping,
|
|||
in class declaration order. The order of the fields in all of the
|
||||
generated methods is the order in which they appear in the class.
|
||||
|
||||
The ``dataclass`` decorator will add various "dunder" methods to the
|
||||
class, described below. If any of the added methods already exist on the
|
||||
class, a ``TypeError`` will be raised. The decorator returns the same
|
||||
class that is called on: no new class is created.
|
||||
|
||||
The ``dataclass`` decorator is typically used with no parameters and
|
||||
no parentheses. However, it also supports the following logical
|
||||
signature::
|
||||
|
||||
def dataclass(*, init=True, repr=True, eq=True, compare=True, hash=None, frozen=False)
|
||||
def dataclass(*, init=True, repr=True, eq=True, order=True, hash=None, frozen=False)
|
||||
|
||||
If ``dataclass`` is used just as a simple decorator with no
|
||||
parameters, it acts as if it has the default values documented in this
|
||||
|
@ -175,7 +180,7 @@ signature. That is, these three uses of ``@dataclass`` are equivalent::
|
|||
class C:
|
||||
...
|
||||
|
||||
@dataclass(init=True, repr=True, eq=True, compare=True, hash=None, frozen=False)
|
||||
@dataclass(init=True, repr=True, eq=True, order=True, hash=None, frozen=False)
|
||||
class C:
|
||||
...
|
||||
|
||||
|
@ -184,24 +189,23 @@ The parameters to ``dataclass`` are:
|
|||
- ``init``: If true (the default), a ``__init__`` method will be
|
||||
generated.
|
||||
|
||||
- ``repr``: If true (the default), a ``__repr__`` function will be
|
||||
- ``repr``: If true (the default), a ``__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)``.
|
||||
``InventoryItem(name='widget', unit_price=3.0, quantity_on_hand=10)``.
|
||||
|
||||
- ``eq``: If true (the default), ``__eq__`` and ``__ne__`` 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.
|
||||
|
||||
- ``compare``: If true (the default), ``__lt__``, ``__le__``,
|
||||
- ``order``: If true (the default), ``__lt__``, ``__le__``,
|
||||
``__gt__``, and ``__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
|
||||
``compare`` is True, then ``eq`` is ignored, and ``__eq__`` and
|
||||
``__ne__`` will be automatically generated.
|
||||
``order`` is true and ``eq`` is false, a ``ValueError`` is raised.
|
||||
|
||||
- ``hash``: Either a bool or ``None``. If ``None`` (the default), the
|
||||
``__hash__`` method is generated according to how ``eq`` and
|
||||
|
@ -235,10 +239,14 @@ Python syntax::
|
|||
b: int = 0 # assign a default value for 'b'
|
||||
|
||||
In this example, both ``a`` and ``b`` will be included in the added
|
||||
``__init__`` function, which will be defined as::
|
||||
``__init__`` method, which will be defined as::
|
||||
|
||||
def __init__(self, a: int, b: int = 0):
|
||||
|
||||
``TypeError`` will be raised if a field without a default value
|
||||
follows a field with a default value. This is true either when this
|
||||
occurs in a single class, or as a result of class inheritance.
|
||||
|
||||
For common and simple use cases, no other functionality is required.
|
||||
There are, however, some Data Class features that require additional
|
||||
per-field information. To satisfy this need for additional
|
||||
|
@ -266,10 +274,10 @@ The parameters to ``field()`` are:
|
|||
specify both ``default`` and ``default_factory``.
|
||||
|
||||
- ``init``: If true (the default), this field is included as a
|
||||
parameter to the generated ``__init__`` function.
|
||||
parameter to the generated ``__init__`` method.
|
||||
|
||||
- ``repr``: If true (the default), this field is included in the
|
||||
string returned by the generated ``__repr__`` function.
|
||||
string returned by the generated ``__repr__`` method.
|
||||
|
||||
- ``compare``: If True (the default), this field is included in the
|
||||
generated equality and comparison methods (``__eq__``, ``__gt__``,
|
||||
|
@ -278,10 +286,16 @@ The parameters to ``field()`` are:
|
|||
- ``hash``: This can be a bool or ``None``. If True, this field is
|
||||
included in the generated ``__hash__`` method. If ``None`` (the
|
||||
default), use the value of ``compare``: this would normally be the
|
||||
expected behavior. A field needs to be considered in the hash if
|
||||
expected behavior. A field should be considered in the hash if
|
||||
it's used for comparisons. Setting this value to anything other
|
||||
than ``None`` is discouraged.
|
||||
|
||||
One possible reason to set ``hash=False`` but ``compare=True`` would
|
||||
be if a field is expensive to compute a hash value for, that field
|
||||
is needed for equality testing, and there are other fields that
|
||||
contribute to the type's hash value. Even if a field is excluded
|
||||
from the hash, it will still be used for comparisons.
|
||||
|
||||
- ``metadata``: This can be a mapping or None. None is treated as an
|
||||
empty dict. This value is wrapped in ``types.MappingProxyType`` to
|
||||
make it read-only, and exposed on the Field object. It is not used
|
||||
|
@ -291,12 +305,11 @@ The parameters to ``field()`` are:
|
|||
|
||||
If the default value of a field is specified by a call to ``field()``,
|
||||
then the class attribute for this field will be replaced by the
|
||||
specified ``default`` value, if one is provided in the call to
|
||||
``field()``. If no ``default`` is provided, then the class attribute
|
||||
will be deleted. The intent is that after the ``dataclass`` decorator
|
||||
runs, the class attributes will all contain the default values for the
|
||||
fields, just as if the default value itself were specified. For
|
||||
example, after::
|
||||
specified ``default`` value. If no ``default`` is provided, then the
|
||||
class attribute will be deleted. The intent is that after the
|
||||
``dataclass`` decorator runs, the class attributes will all contain
|
||||
the default values for the fields, just as if the default value itself
|
||||
were specified. For example, after::
|
||||
|
||||
@dataclass
|
||||
class C:
|
||||
|
@ -322,17 +335,19 @@ object directly. Its documented attributes are:
|
|||
- ``type``: The type of the field.
|
||||
|
||||
- ``default``, ``default_factory``, ``init``, ``repr``, ``hash``,
|
||||
``compare``, and ``metadata`` have the identical meaning as they do
|
||||
in the ``field()`` declaration.
|
||||
``compare``, and ``metadata`` have the identical meaning and values
|
||||
as they do in the ``field()`` declaration.
|
||||
|
||||
Other attributes may exist, but they are private.
|
||||
Other attributes may exist, but they are private and must not be
|
||||
inspected or relied on.
|
||||
|
||||
post-init processing
|
||||
--------------------
|
||||
|
||||
The generated ``__init__`` code will call a method named
|
||||
``__post_init__``, if it is defined on the class. It will
|
||||
be called as ``self.__post_init__()``.
|
||||
``__post_init__``, if it is defined on the class. It will be called
|
||||
as ``self.__post_init__()``. If not ``__init__`` method is generated,
|
||||
then ``__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::
|
||||
|
@ -355,12 +370,11 @@ Class variables
|
|||
|
||||
One place where ``dataclass`` actually inspects the type of a field is
|
||||
to determine if a field is a class variable as defined in PEP 526. It
|
||||
does this by checking if the type of the field is of type
|
||||
``typing.ClassVar``. If a field is a ``ClassVar``, it is excluded
|
||||
from consideration as a field and is ignored by the Data Class
|
||||
mechanisms. For more discussion, see [#]_. Such ``ClassVar``
|
||||
pseudo-fields are not returned by the module-level ``fields()``
|
||||
function.
|
||||
does this by checking if the type of the field is ``typing.ClassVar``.
|
||||
If a field is a ``ClassVar``, it is excluded from consideration as a
|
||||
field and is ignored by the Data Class mechanisms. For more
|
||||
discussion, see [#]_. Such ``ClassVar`` pseudo-fields are not
|
||||
returned by the module-level ``fields()`` function.
|
||||
|
||||
Init-only variables
|
||||
-------------------
|
||||
|
@ -375,7 +389,7 @@ parameters to the generated ``__init__`` method, and are passed to
|
|||
the optional ``__post_init__`` method. They are not otherwise used
|
||||
by Data Classes.
|
||||
|
||||
For example, suppose a field will be initialized from a database, if a
|
||||
For example, suppose a field will be initialzed from a database, if a
|
||||
value is not provided when creating the class::
|
||||
|
||||
@dataclass
|
||||
|
@ -730,12 +744,16 @@ parameters. They are passed first to ``__init__`` which passes them
|
|||
to ``__post_init__`` where user code can use them as needed.
|
||||
|
||||
The only real difference between alternate classmethod constructors
|
||||
and ``InitVar`` pseudo-fields is in object creation. With
|
||||
``InitVar``s, using ``__init__`` and the module-level ``replace()``
|
||||
function ``InitVar``'s must always be specified. With alternate
|
||||
classmethod constructors the additional initialization parameters are
|
||||
always optional. Which approach is more appropriate will be
|
||||
application-specific, but both approaches are supported.
|
||||
and ``InitVar`` pseudo-fields is in regards to required non-field
|
||||
parameters during object creation. With ``InitVar``s, using
|
||||
``__init__`` and the module-level ``replace()`` function ``InitVar``'s
|
||||
must always be specified. Consider the case where a ``context``
|
||||
object is needed to create an instance, but isn't stored as a field.
|
||||
With alternate classmethod constructors the ``context`` parameter is
|
||||
always optional, because you could still create the object by going
|
||||
through ``__init__`` (unless you suppress its creation). Which
|
||||
approach is more appropriate will be application-specific, but both
|
||||
approaches are supported.
|
||||
|
||||
Rejected ideas
|
||||
==============
|
||||
|
@ -783,6 +801,24 @@ see [#]_.
|
|||
Examples
|
||||
========
|
||||
|
||||
Custom __init__ method
|
||||
----------------------
|
||||
|
||||
Sometimes the generated ``__init__`` method does not suffice. For
|
||||
example, suppose you wanted to have an object to store ``*args`` and
|
||||
``**kwargs``::
|
||||
|
||||
@dataclass(init=False)
|
||||
class ArgHolder:
|
||||
args: List[Any]
|
||||
kwargs: Mapping[Any, Any]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
a = ArgHolder(1, 2, three=3)
|
||||
|
||||
A complicated example
|
||||
---------------------
|
||||
|
||||
|
|
Loading…
Reference in New Issue