Minor tweaks to PEP 544 (#1046)
This PR contains mostly minor wording tweaks plus a paragraph explicitly allowing class objects as implementations of protocols, previously there were questions whether it is actually allowed, see python/mypy#4536.
This commit is contained in:
parent
1df4f57758
commit
8aecf9d07a
38
pep-0544.txt
38
pep-0544.txt
|
@ -703,8 +703,8 @@ intersection type construct could be added in future as specified by PEP 483,
|
|||
see `rejected`_ ideas for more details.
|
||||
|
||||
|
||||
``Type[]`` with protocols
|
||||
-------------------------
|
||||
``Type[]`` and class objects vs protocols
|
||||
-----------------------------------------
|
||||
|
||||
Variables and parameters annotated with ``Type[Proto]`` accept only concrete
|
||||
(non-protocol) subtypes of ``Proto``. The main reason for this is to allow
|
||||
|
@ -735,6 +735,23 @@ not explicitly typed, and such assignment creates a type alias.
|
|||
For normal (non-abstract) classes, the behavior of ``Type[]`` is
|
||||
not changed.
|
||||
|
||||
A class object is considered an implementation of a protocol if accessing
|
||||
all members on it results in types compatible with the protocol members.
|
||||
For example::
|
||||
|
||||
from typing import Any, Protocol
|
||||
|
||||
class ProtoA(Protocol):
|
||||
def meth(self, x: int) -> int: ...
|
||||
class ProtoB(Protocol):
|
||||
def meth(self, obj: Any, x: int) -> int: ...
|
||||
|
||||
class C:
|
||||
def meth(self, x: int) -> int: ...
|
||||
|
||||
a: ProtoA = C # Type check error, signatures don't match!
|
||||
b: ProtoB = C # OK
|
||||
|
||||
|
||||
``NewType()`` and type aliases
|
||||
------------------------------
|
||||
|
@ -762,8 +779,8 @@ aliases::
|
|||
CompatReversible = Union[Reversible[T], SizedIterable[T]]
|
||||
|
||||
|
||||
Modules as subtypes of protocols
|
||||
--------------------------------
|
||||
Modules as implementations of protocols
|
||||
---------------------------------------
|
||||
|
||||
A module object is accepted where a protocol is expected if the public
|
||||
interface of the given module is compatible with the expected protocol.
|
||||
|
@ -1177,7 +1194,8 @@ For example::
|
|||
|
||||
def do_stuff(o: Union[P, X]) -> int:
|
||||
if isinstance(o, P):
|
||||
return o.common_method_name(1) # oops, what if it's an X instance?
|
||||
return o.common_method_name(1) # Results in TypeError not caught
|
||||
# statically if o is an X instance.
|
||||
|
||||
Another potentially problematic case is assignment of attributes
|
||||
*after* instantiation::
|
||||
|
@ -1196,13 +1214,13 @@ Another potentially problematic case is assignment of attributes
|
|||
|
||||
def f(x: Union[P, int]) -> None:
|
||||
if isinstance(x, P):
|
||||
# static type of x is P here
|
||||
# Static type of x is P here.
|
||||
...
|
||||
else:
|
||||
# type of x is "int" here?
|
||||
# Static type of x is int, but can be other type at runtime...
|
||||
print(x + 1)
|
||||
|
||||
f(C()) # oops
|
||||
f(C()) # ...causing a TypeError.
|
||||
|
||||
We argue that requiring an explicit class decorator would be better, since
|
||||
one can then attach warnings about problems like this in the documentation.
|
||||
|
@ -1273,7 +1291,7 @@ Consider this example::
|
|||
|
||||
c = C()
|
||||
f(c) # Would typecheck if covariant subtyping
|
||||
# of mutable attributes were allowed
|
||||
# of mutable attributes were allowed.
|
||||
c.x >> 1 # But this fails at runtime
|
||||
|
||||
It was initially proposed to allow this for practical reasons, but it was
|
||||
|
@ -1292,7 +1310,7 @@ However, it was decided not to do this because of several downsides:
|
|||
|
||||
T = TypeVar('T')
|
||||
|
||||
class P(Protocol[T]): # Declared as invariant
|
||||
class P(Protocol[T]): # Protocol is declared as invariant.
|
||||
def meth(self) -> T:
|
||||
...
|
||||
class C:
|
||||
|
|
Loading…
Reference in New Issue