diff --git a/peps/pep-0767.rst b/peps/pep-0767.rst index 8f2cfc90f17..507afc01ac0 100644 --- a/peps/pep-0767.rst +++ b/peps/pep-0767.rst @@ -98,8 +98,7 @@ Today, there are three major ways of achieving read-only attributes, honored by Protocols --------- -A read-only attribute ``name: T`` on a :class:`~typing.Protocol` in principle -defines two requirements: +Suppose a :class:`~typing.Protocol` member ``name: T`` defining two requirements: 1. ``hasattr(obj, "name")`` 2. ``isinstance(obj.name, T)`` @@ -251,7 +250,12 @@ Initialization Assignment to a read-only attribute can only occur in the class declaring the attribute. There is no restriction to how many times the attribute can be assigned to. -The assignment must be allowed in the following contexts: +Depending on the kind of the attribute, they can be assigned to at different sites: + +Instance Attributes +''''''''''''''''''' + +Assignment to an instance attribute must be allowed in the following contexts: * In ``__init__``, on the instance received as the first parameter (likely, ``self``). * In ``__new__``, on instances of the declaring class created via a call @@ -267,9 +271,6 @@ Additionally, a type checker may choose to allow the assignment: * In ``@classmethod``\ s, on instances of the declaring class created via a call to the class' or super-class' ``__new__`` method. -Note that a child class cannot assign to any read-only attributes of a parent class -in any of the aforementioned contexts, unless the attribute is redeclared. - .. code-block:: python from collections import abc @@ -332,6 +333,25 @@ in any of the aforementioned contexts, unless the attribute is redeclared. self.numerator, self.denominator = f.as_integer_ratio() return self +Class Attributes +'''''''''''''''' + +Read-only class attributes are attributes annotated as both ``ReadOnly`` and ``ClassVar``. +Assignment to such attributes must be allowed in the following contexts: + +* At declaration in the body of the class. +* In ``__init_subclass__``, on the class object received as the first parameter (likely, ``cls``). + +.. code-block:: python + + class URI: + protocol: ReadOnly[ClassVar[str]] = "" + + def __init_subclass__(cls, protocol: str = "") -> None: + cls.protocol = protocol + + class File(URI, protocol="file"): ... + When a class-level declaration has an initializing value, it can serve as a `flyweight `_ default for instances: @@ -367,8 +387,8 @@ protocols or ABCs):: Subtyping --------- -Read-only attributes are covariant. This has a few subtyping implications. -Borrowing from :pep:`705#inheritance`: +The inability to reassign read-only attributes makes them covariant. +This has a few subtyping implications. Borrowing from :pep:`705#inheritance`: * Read-only attributes can be redeclared as writable attributes, descriptors or class variables:: @@ -493,9 +513,6 @@ Interaction with Other Type Qualifiers This is consistent with the interaction of ``ReadOnly`` and :class:`typing.TypedDict` defined in :pep:`705`. -An attribute annotated as both ``ReadOnly`` and ``ClassVar`` can only be assigned to -at declaration in the class body. - An attribute cannot be annotated as both ``ReadOnly`` and ``Final``, as the two qualifiers differ in semantics, and ``Final`` is generally more restrictive. ``Final`` remains allowed as an annotation of attributes that are only implied @@ -604,23 +621,6 @@ to allow initialization in. This however could easily result in users mistakenly or purposefully breaking the aforementioned invariants. It is also a fairly big ask for a relatively niche feature. -``ReadOnly[ClassVar[...]]`` and ``__init_subclass__`` ------------------------------------------------------ - -Should read-only class variables be assignable to within the declaring class' -``__init_subclass__``? - -.. code-block:: python - - class URI: - protocol: ReadOnly[ClassVar[str]] = "" - - def __init_subclass__(cls, protocol: str = "") -> None: - cls.foo = protocol - - class File(URI, protocol="file"): ... - - Footnotes =========