Dans les nouvelles versions de Python, les annotations de type sont de plus en plus prises en charge et sont de plus en plus utilisées dans les bibliothèques, les frameworks et les projets Python. En plus de la documentation supplémentaire du code, les annotations de type permettent à des outils tels que mypy d'effectuer des vérifications de validation supplémentaires de manière statique et d'identifier d'éventuelles erreurs dans le code. Cet article parlera d'un, je pense, d'un sujet intéressant concernant la vérification de type statique en Python - les protocoles, ou comme indiqué dans PEP-544 , le typage statique du canard .

Contenu
, Python, , - :
, , , ,
– , , , , . , , . , , . , .
>>> class Meter:
... def __len__(self):
... return 1_000
...
>>> len([1, 2, 3])
3
>>> len("Duck typing...")
14
>>> len(Meter())
1000
len
, , __len__()
.
. , , – . .
(nominal type system) , , , . Duck
Bird
, Duck
, Bird
. Python, mypy , , , .
:
class Bird:
def feed(self) -> None:
print("Feeding the bird...")
class Duck(Bird):
def feed(self) -> None:
print("Feeding the duck...")
class Goose:
"""
- Bird.
"""
def feed(self) -> None:
print("Feeding the goose...")
def feed(bird: Bird) -> None:
bird.feed()
# OK
feed(Bird())
# OK
feed(Duck())
# Mypy error: Argument 1 to "feed" has incompatible type "Goose";
# expected "Bird"
feed(Goose())
# Mypy error: Argument 1 to "feed" has incompatible type "None";
# expected "Bird"
feed(None)
Goose
feed
, Bird
, mypy.
. , Java, C#, C++ .
(structural type system) , . , , compile time duck typing.
. , Go – , . , Go - , , .
– TypeScript, :
// TypeScript
interface Person {
name: String
age: Number
}
function show(person: Person) {
console.log("Name: " + person.name)
console.log("Age: " + person.age)
}
class Employee {
name: String
age: Number
constructor(name: String, age: Number) {
this.name = name
this.age = age
}
}
class Figure {}
// OK
show(new Employee("John", 30))
// OK
show({name: "Peter", age: 25})
// Error:
// Argument of type 'Figure' is not assignable to parameter of type 'Person'.
// Type 'Figure' is missing the following properties
// from type 'Person': name, age
show(new Figure())
Employee
Person
, . , Employee
name
age
. Figure
, , , , , Person
.
Python
Python 3.8 (PEP-544), Python. Python , . , , , , .
"" , ( , , mypy). , - , (abc.ABC
), .
:
import typing as t
# t.Iterable[int] -
def iterate_by(numbers: t.Iterable[int]) -> None:
for number in numbers:
print(number)
# OK
iterate_by([1, 2, 3])
# OK
iterate_by(range(1_000_000))
# Mypy error: Argument 1 to "iterate_by" has incompatible type "str";
# expected "Iterable[int]"
# note: Following member(s) of "str" have conflicts:
# note: Expected:
# note: def __iter__(self) -> Iterator[int]
# note: Got:
# note: def __iter__(self) -> Iterator[str]
iterate_by("duck")
Mypy , iterate_by
(, __iter__
).
, , mypy , .
# ...
class Fibonacci:
def __iter__(self) -> t.Iterator[int]:
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# OK
iterate_by(Fibonacci())
class Animals:
"""
, ,
,
.
"""
def __iter__(self) -> t.Iterator[str]:
yield from ["duck", "cat", "dog"]
# Mypy error: Argument 1 to "iterate_by" has incompatible type "Animals";
# expected "Iterable[int]"
iterate_by(Animals())
( typing
) . mypy.
, . mypy , .
:
import typing as t
class Figure(t.Protocol):
""" ."""
# ,
name: str
def calculate_area(self) -> float:
""" ."""
def calculate_perimeter(self) -> float:
""" ."""
def show(figure: Figure) -> None:
print(f"S ({figure.name}) = {figure.calculate_area()}")
print(f"P ({figure.name}) = {figure.calculate_perimeter()}")
Protocol
typing
. - , . - ( -).
, Figure
.
# ...
class Square:
name = ""
def __init__(self, size: float):
self.size = size
def calculate_area(self) -> float:
return self.size * self.size
def calculate_perimeter(self) -> float:
return 4 * self.size
def set_color(self, color: str) -> None:
"""
,
.
"""
self.color = color
# OK
show(Square(size=3.14))
, Square
Figure
. Mypy show
Figure
, Square
. , . , Figure
show
, Square
– ( ). , .
, mypy :
# ...
class Circle:
PI = 3.1415926
name = ""
def __init__(self, radius: float):
self.radius = radius
def calculate_perimeter(self) -> float:
return 2 * self.PI * self.radius
# Mypy error: Argument 1 to "show" has incompatible type "Circle";
# expected "Figure"
# note: 'Circle' is missing following 'Figure' protocol member:
# note: calculate_area
show(Circle(radius=1))
mypy , ( ).
, , . mypy , .
import typing as t
import abc
class Readable(t.Protocol):
@abc.abstractmethod
def read(self) -> str:
...
def get_size(self) -> int:
"""
-.
"""
return 1_000
# OK
class File(Readable):
def read(self) -> str:
return " "
# OK
print(File().get_size()) # 1000
# Mypy error: Return type "int" of "read" incompatible
# with return type "str" in supertype "Readable"
class WrongFile(Readable):
def read(self) -> int:
return 42
(abc.ABC
), , -. , , mypy .
runtime_checkable
, isinstance
issubclass
. , , mypy :
# ... Figure
s = Square(4)
# Mypy error: Only @runtime_checkable protocols can be used
# with instance and class checks
# isinstance(s, Figure)
isinstance(s, Figure)
, @runtime_checkable
, .
import typing as t
@t.runtime_checkable
class HasLength(t.Protocol):
def __len__(self) -> int:
...
# OK
print(isinstance("", HasLength)) # True
print(isinstance([1, 2, 3], HasLength)) # True
- , , PEP-544.
Python, . Mypy Python. , , , , .
Si vous avez quelque chose à ajouter sur les avantages et les inconvénients de la saisie structurée, veuillez partager vos réflexions dans les commentaires.
Notes (modifier)
Tous les exemples abordés dans cet article ont été testés en Python 3.9 / mypy 0.812.
Fichier de paramètres Mypy
Liens utiles
mypy
PEP-544 - Protocoles: sous-typage structurel
Duck Typing (Glossaire Python)
Système de type nominal (Wikipedia)
Système de type structurel (Wikipedia)
Le protocole Iterator: comment fonctionnent les boucles For en Python
Protocoles et sous-typage structurel (documentation mypy)
Aller interfaces