Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

spec: clarify interaction of Final and dataclass #1669

Merged
merged 2 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/spec/dataclasses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,14 @@ This includes, but is not limited to, the following semantics:
* ClassVar attributes are not considered dataclass fields and are
`ignored by dataclass mechanisms <https://docs.python.org/3/library/dataclasses.html#class-variables>`_.

* Dataclass fields annotated with ``Final`` and initialized in the class body
are not considered class attributes unless explicitly annotated with
``ClassVar``. For example, ``x: Final[int] = 3`` in a dataclass body is a
dataclass field (and thus instance attribute) named ``x`` with a default
value of ``3`` in the generated ``__init__`` method, which cannot be
reassigned after ``__init__``. A final class variable on a dataclass should
be explicitly annotated as ``x: ClassVar[Final[int]] = 3``.


Undefined behavior
^^^^^^^^^^^^^^^^^^
Expand Down
22 changes: 15 additions & 7 deletions docs/spec/qualifiers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -147,19 +147,27 @@ be initialized in the ``__init__`` method (except in stub files)::
def __init__(self) -> None:
self.x = 1 # Good

Type checkers should infer a final attribute that is initialized in
carljm marked this conversation as resolved.
Show resolved Hide resolved
a class body as being a class variable. Variables should not be annotated
with both ``ClassVar`` and ``Final``.

``Final`` may only be used as the outermost type in assignments or variable
annotations. Using it in any other position is an error. In particular,
``Final`` can't be used in annotations for function arguments::
Type checkers should infer a final attribute that is initialized in a class
body as being a class variable, except in the case of :doc:`dataclasses`, where
``x: Final[int] = 3`` creates a dataclass field and instance-level final
attribute ``x`` with default value ``3``; ``x: ClassVar[Final[int]] = 3`` is
necessary to create a final class variable with value ``3``. In
non-dataclasses, combining ``ClassVar`` and ``Final`` is redundant, and type
checkers may choose to warn or error on the redundancy.

``Final`` may only be used in assignments or variable annotations. Using it in
any other position is an error. In particular, ``Final`` can't be used in
annotations for function arguments::

x: list[Final[int]] = [] # Error!

def fun(x: Final[List[int]]) -> None: # Error!
...

``Final`` may be wrapped only by other type qualifiers (e.g. ``ClassVar`` or
``Annotation``). It cannot be used in a type parameter (e.g.
``list[Final[int]]`` is not permitted.)

Note that declaring a name as final only guarantees that the name will
not be re-bound to another value, but does not make the value
immutable. Immutable ABCs and containers may be used in combination
Expand Down