In a discussion flow, I decided to read PEP 544 --Protocols: Structural subtyping (static duck typing), so my own Make a note of your understanding.
A class that inherits typing.Protocol
is called a protocol class (or simply a protocol).
As a protocol member, what type and what kind of member (variable, method) can be defined in the protocol class.
The following example defines the SupportsClose
protocol, which" has one method called close ()
with no arguments and no return value.
from typing import Protocol
class SupportsClose(Protocol):
def close(self) -> None:
pass
For example, the following class can be said to meet the SupportsClose
protocol.
class Resource:
...
def close(self) -> None:
self.file.close()
self.lock.release()
Therefore, the following code is type-checked as intended.
def close_all(things: Iterable[SupportsClose]) -> None:
for t in things:
t.close()
f = open('foo.txt')
r = Resource()
close_all([f, r]) # OK!
close_all([1]) # Error: 'int' has no 'close' method
Variable annotations can be used to define variables as protocol members.
Note that if you initialize a variable in a method, it will not be a protocol member, so self.temp
in the code below will be ignored (it will not affect the protocol type checking).
from typing import Protocol, List
class Template(Protocol):
name: str # This is a protocol member
value: int = 0 # This one too (with default)
def method(self) -> None:
self.temp: List[int] = [] # Error in type checker
By the way, let's use typing.ClassVar
for class variables [^ 1].
[^ 1]: I tried experimenting at hand, but it doesn't work ...?: Thinking:
Up to this point, the protocol class has been described as being treated as a type (type annotation), but since the protocol class is a normal Python class, it may be inherited to create a derived class.
class PColor(Protocol):
@abstractmethod
def draw(self) -> str:
...
def complex_method(self) -> int:
# some complex code here
class NiceColor(PColor):
def draw(self) -> str:
return "deep blue"
If inherited, the members are inherited to the derived class as they are, so of course it is considered to satisfy the protocol [^ 2].
If you just do type checking, you don't need to inherit it, so it seems good to think that sharing the implementation is an advantage when using this usage. An example of using it in combination with ʻabc.abstractmethod` is also introduced, and it is an ABC (Abstract Base Classes) -like introduction [^ 3].
[^ 2]: It is possible to override it with a derived class so that it does not satisfy the protocol, but since it is an irregular usage, the explanation is omitted. [^ 3]: Or rather, the behavior other than the type hint seems to be almost equivalent to ABC (at runtime).
He said it would be nice to use a protocol class with a __call__ ()
method to represent complex callable interfaces such as variadic, overloads, and Generic. This seems convenient.
from typing import Optional, List, Protocol
class Combiner(Protocol):
def __call__(self, *vals: bytes,
maxlen: Optional[int] = None) -> List[bytes]: ...
Recommended Posts