Рубрики
Без рубрики

Практические применения протоколов в Python

Возьмите свой бесплатный обед. Tagged с помощью Python, протоколов, учебного пособия, программирования.

Там нет такой вещи, как бесплатный обед. В объектно-ориентированном коде, Протоколы Предложите очень близкое приближение. В этом контексте протокол представляет собой набор поведения, который, если он выставлен типом, гарантирует определенные другие поведения.

Например, рассмотрим гипотетический класс, ЧЕЛОВЕК :

class Human:

    def __init__(
        self,
        name: str,
        unique_id: int
    ) -> None:

        self.name = name
        self.unique_id = unique_id
        return

    @classmethod
    def decode(cls: Type[H], data: Any) -> H:
        return cls(
            name=data['name'],
            unique_id=data['unique_id']
        )

Этот класс имеет инициализатор и метод одного класса, который, как мы предполагаем, используется для декодирования десериализованных представлений людей. Например, когда они извлекаются из базы данных.

Предположим, мы хотим иметь возможность декодировать многие ЧЕЛОВЕК экземпляры одновременно. Возможно, наша база данных возвращает списки ЧЕЛОВЕК Анкет Мы могли бы добавить новый метод класса:

# continues `Human` definition from above

    @classmethod
    def decode_many(cls: Type[H], data: Any) -> List[H]:
        return [cls.decode(h) for h in data]

Достаточно просто. Теперь мы можем позвонить Human.decode () в отдельных случаях и Human.decode_many () в списках.

Считай, что ничего в .decode_many () Функциональный корпус зависит от фактической реализации ЧЕЛОВЕК Анкет .decode () Метод совершенно непрозрачен к .decode_many () и .decode_many () не нужно знать о .decode () Реализация для того, чтобы выполнить свою работу отлично.

Имея это в виду, рассмотрите второй класс, Электронная почта Анкет Предположим, это определено следующим образом:

class Email:

    def __init__(
        self,
        body: str,
        confirmed: bool
    ) -> None:
        self.body = body
        self.confirmed = confirmed
        return

    @classmethod
    def decode(cls: Type[E], data: Any) -> E:
        return cls(
            body=data['body'],
            confirmed=data['confirmed']
        )

    @classmethod
    def decode_many(cls: Type[E], data: Any) -> List[E]:
        return [cls.decode(e) for e in data]

Это следует заметить это Email.decode_many () идентично Human.decode_many () Анкет Мы могли бы написать .decode_many () в общей форме следующим образом:

def decode_many(
    a: Type[ATypeWithDecodeMethod],
    b: Any
) -> ATypeWithDecodeMethod:
    return [a.decode(c) for c in b]

На английском языке: «Возьмите любой тип, имеющий метод

Имея .decode () Метод это поведение, которое, если показано типом, гарантирует другое поведение : Способность питаться .decode_many () . Мы можем использовать слово «протокол», чтобы описать требование к иметь .decode () Метод и класс, демонстрирующий это требование, сказано соответствовать протоколу Анкет

Мы можем написать такой протокол, как так:

class Decodable:

    @classmethod
    def decode(cls: Type[D], data: Any) -> D:
        raise NotImplementedError

    @classmethod
    def decode_many(cls: Type[D], data: Any) -> List[D]:
        return [cls.decode(d) for d in data]

Наш оригинал ЧЕЛОВЕК Определение уже имеет .decode () метод Таким образом, на практике это уже соответствует Декорабельно протокол. Мы можем идентифицировать, что это соответствует, так что он получает доступ к .decode_many () :

class Human(Decodable):
    # definition remains otherwise unchanged from that
    # presented earlier in this article.

Теперь мы можем позвонить Human.decode_many () и получить список ЧЕЛОВЕК Случаи в ответ, несмотря на определение ЧЕЛОВЕК не включая какое -либо .decode_many () метод Мы можем сделать то же самое с Электронная почта , так что мы не дублируем код.

Это хорошее время, чтобы сделать паузу, рассмотреть слон в комнате: что отличает «протокол» от обычного наследства старого класса? Нет формального определения протокола в Python, так как же может Декорабельно быть протоколом?

Ответ заключается в том, что в этом контексте «протокол» – это то, на что вы, как программист, определяете его, в своем собственном уме. Так получилось, что Python’s класс Ключевое слово обеспечивает поведение, необходимое для реализации отношений протоколов в коде Python.

Некоторые языки дают явные инструменты протокола: Swift имеет протокол , У Java Аннотация и C# есть интерфейс . Каждый из них отличается нюансами, но все стремятся определить код, который определяет поведение, которое, если показано типом, гарантирует определенное другое поведение.

Ключ к эффективности протоколов в Python заключается в том, что протокол не должен иметь инициализатора Анкет Любой класс, принимающий протокол, должен быть в состоянии сделать это, сохраняя при этом полную ответственность и контроль над своей собственной инициализацией.

Точно так же, как проверка статического типа, неизменность собственности или любая другая парадигма Python, вам решать, хотите ли вы подчиняться правилам, которые вы определяете для своих протоколов.

Истинная радость приходит, когда мы решаем, что хотим Декорабельно Объекты, чтобы иметь возможность Сделай больше вещей . Любые новые возможности, которые мы добавляем в Декорабельно Протокол, который не изменяет требования к протоколу, по сути, бесплатный.

Например, предположим, что наша база данных может вернуть дополнительные данные. То есть в ответ на запрос мы можем получить некоторые данные, или мы можем получить Нет Анкет Наш .decode () Определение метода не делает такие пособия. Если он кормит Нет это сбой.

Мы можем добавить новый метод, как SO:

# Continues the `Decodable` definition from above
    @classmethod
    def optionally_decode(cls: Type[D], data: Any) -> Optional[D]:
        if data is None:
            return None
        return cls.decode(data)

Теперь оба ЧЕЛОВЕК и Электронная почта может быть необязательно декодирован, и нам даже не нужно было трогать их определения. Пойдем еще дальше. Предположим, мы хотим иметь возможность загружать ЧЕЛОВЕК и Электронная почта Из необработанных данных json-serialized:

# Continues the `Decodable` definition from above
    @classmethod
    def deserialise(cls: Type[D], serial: str) -> D:
        return cls.decode(json.loads(serial))

Опять же, не касаясь Электронная почта или ЧЕЛОВЕК , мы добавили новые возможности в обоих. Не совсем бесплатный обед, но настолько прост, что мы могли бы назвать его одним.

Оригинал: “https://dev.to/hugh_jeremy/practical-applications-of-protocols-in-python-20i0”