I read PEP-544 (Protocols: Structural subtyping (static duck typing)).

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.

Overview

approach

Protocol definition

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

Definition of protocol members (variables)

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:

Protocol class inheritance

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).

Callback protocol

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]: ...

Impressions

Recommended Posts

I read PEP-544 (Protocols: Structural subtyping (static duck typing)).
I read PEP 560 (Core support for typing module and generic types)
I read PEP 613 (Explicit Type Aliases)
I read PEP 612 (Parameter Specification Variables)
I read PEP 604 (Complementary syntax for Union []).
I read PEP-362 (Function Signature Object) Memo
I read PEP 618 (Add Optional Length-Checking To zip)
I read PEP 584 (Add Union Operators To dict)
I read PEP 614 (Relaxing Grammar Restrictions On Decorators)
I read PEP-593 (Flexible function and variable annotations)