Несколько протоколов в ООП в Python
В Python класс может подходить сразу под несколько протоколов. Для этого не нужно перечислять протоколы в объявлении класса - достаточно иметь все нужные методы.
В этом протоколы похожи на интерфейсы PHP: можно описать несколько контрактов, а класс соберет методы из всех. Наследовать несколько абстрактных классов нельзя, а методы нескольких протоколов - можно.
Давайте попробуем на практике. Пусть есть протокол фигуры и протокол четырехугольника с геттерами сторон:
from typing import Protocol, runtime_checkable
@runtime_checkable
class FigureProto(Protocol):
def get_square(self) -> float:
...
def get_perimeter(self) -> float:
...
@runtime_checkable
class TetragonProto(Protocol):
def get_a(self) -> float:
...
def get_b(self) -> float:
...
def get_c(self) -> float:
...
def get_d(self) -> float:
...
Класс Quadrate реализует оба
контракта одним набором методов.
Протоколы в скобках класса писать
не обязательно:
class Quadrate:
def __init__(self, a):
self.__a = a
def get_a(self):
return self.__a
def get_b(self):
return self.__a
def get_c(self):
return self.__a
def get_d(self):
return self.__a
def get_square(self):
return self.__a * self.__a
def get_perimeter(self):
return 4 * self.__a
Проверим соответствие каждому протоколу
отдельно через isinstance:
quad = Quadrate(5)
print(isinstance(quad, FigureProto)) # True
print(isinstance(quad, TetragonProto)) # True
Однако, не имеет значения, квадрат это или трапеция - важно, что фигура имеет описанные методы и работает однотипно.
Тот же прием для User с именем
и возрастом: два протокола, один класс:
@runtime_checkable
class HasName(Protocol):
def name(self) -> str:
...
@runtime_checkable
class HasAge(Protocol):
def age(self) -> int:
...
class User:
def __init__(self, name, age):
self.__name = name
self.__age = age
def name(self):
return self.__name
def age(self):
return self.__age
Проверим оба протокола:
user = User('john', 25)
print(isinstance(user, HasName)) # True
print(isinstance(user, HasAge)) # True
Сделайте класс Rectangle,
подходящий под FigureProto
и TetragonProto.
Сделайте протокол CircleProto
с методами get_radius
и get_diameter.
Сделайте класс Disk с методами
FigureProto и CircleProto.
Проверьте isinstance для
каждого протокола.