«Если рабочий хочет хорошо выполнять свою работу, он должен сначала заточить свои инструменты» — Конфуций, «Аналитики Конфуция. Лу Лингун»
титульная страница > программирование > Утиная типизация соответствует подсказкам по типам: использование протоколов в Python

Утиная типизация соответствует подсказкам по типам: использование протоколов в Python

Опубликовано 4 сентября 2024 г.
Просматривать:720

Duck Typing Meets Type Hints: Using Protocols in Python

Динамичность Python и поддержка утиной печати уже давно хвалят за гибкость. Однако по мере того, как кодовые базы становятся больше и сложнее, преимущества статической проверки типов становятся все более очевидными. Но как мы можем совместить гибкость утиной типизации с безопасностью статической проверки типов? Введите класс протокола Python.

Из этого урока вы узнаете:

  1. Что такое утиная типизация и как она поддерживается в Python
  2. Плюсы и минусы утиного набора текста
  3. Как абстрактные базовые классы (ABC) пытаются решить проблемы типизации
  4. Как использовать протокол, чтобы получить лучшее от обоих миров: гибкость набора текста со статической проверкой типов

Понимание утиной печати

Утиная типизация — это концепция программирования, в которой тип или класс объекта менее важен, чем определяемые им методы. Он основан на идее: «Если что-то выглядит как утка, плавает как утка и крякает как утка, то, вероятно, это и есть утка».

В Python полностью поддерживается утиная типизация. Например:

class Duck:
    def quack(self):
        print("Quack!")

class Person:
    def quack(self):
        print("I'm imitating a duck!")

def make_it_quack(thing):  # Note: No type hint here
    thing.quack()

duck = Duck()
person = Person()

make_it_quack(duck)    # Output: Quack!
make_it_quack(person)  # Output: I'm imitating a duck!

В этом примере make_it_quack не заботится о типе объекта. Его волнует только то, что у этой штуки шарлатанский метод. Обратите внимание, что для параметраthing нет подсказки типа, что типично для кода с утиной типизацией, но может привести к проблемам в более крупных базах кода.

Плюсы и минусы утиного набора текста

Утиный набор текста дает ряд преимуществ:

  1. Гибкость: позволяет создавать более гибкий код, не привязанный к конкретным типам.
  2. Упрощенное повторное использование кода: вы можете использовать существующие классы в новых контекстах без изменений.
  3. Акцент на поведении: основное внимание уделяется тому, что объект может делать, а не тому, что он собой представляет.

Однако у него есть и недостатки:

  1. Отсутствие ясности: может быть неясно, какие методы должен реализовать объект.
  2. Ошибки выполнения: ошибки, связанные с типом, обнаруживаются только во время выполнения.
  3. Меньше поддержки IDE: IDE с трудом обеспечивают точное автозаполнение и проверку ошибок.

Решение ABC

Один из подходов к решению этих проблем — использование абстрактных базовых классов (ABC). Вот пример:

from abc import ABC, abstractmethod

class Quacker(ABC):
    @abstractmethod
    def quack(self):
        pass

class Duck(Quacker):
    def quack(self):
        print("Quack!")

class Person(Quacker):
    def quack(self):
        print("I'm imitating a duck!")

def make_it_quack(thing: Quacker):
    thing.quack()

duck = Duck()
person = Person()

make_it_quack(duck)
make_it_quack(person)

Хотя этот подход обеспечивает лучшую проверку типов и более понятный интерфейс, у него есть недостатки:

  1. Это требует наследования, что может привести к негибкой иерархии.
  2. Это не работает с существующими классами, которые вы не можете изменить.
  3. Это противоречит философии Python «утиной типизации».

Протоколы: лучшее из обоих миров

В Python 3.8 появился класс протокола, который позволяет нам определять интерфейсы без необходимости наследования. Вот как мы можем его использовать:

from typing import Protocol

class Quacker(Protocol):
    def quack(self):...

class Duck:
    def quack(self):
        print("Quack!")

class Person:
    def quack(self):
        print("I'm imitating a duck!")

def make_it_quack(thing: Quacker):
    thing.quack()

duck = Duck()
person = Person()

make_it_quack(duck)
make_it_quack(person)

Давайте разберемся:

  1. Мы определяем протокол Quacker, который определяет ожидаемый интерфейс.
  2. Нашим классам Duck и Person не нужно ничего наследовать.
  3. Мы можем использовать подсказки типов с make_it_quack, чтобы указать, что он ожидает Quacker.

Этот подход дает нам несколько преимуществ:

  1. Статическая проверка типов: IDE и средства проверки типов могут обнаруживать ошибки еще до выполнения.
  2. Наследование не требуется: существующие классы работают, пока у них есть правильные методы.
  3. Четкие интерфейсы: протокол четко определяет, какие методы ожидаются.

Вот более сложный пример, показывающий, как протоколы могут быть настолько сложными, насколько это необходимо (форма), сохраняя при этом классы предметной области (круг, прямоугольник) плоскими:

from typing import Protocol, List

class Drawable(Protocol):
    def draw(self): ...

class Resizable(Protocol):
    def resize(self, factor: float): ...

class Shape(Drawable, Resizable, Protocol):
    pass

def process_shapes(shapes: List[Shape]):
    for shape in shapes:
        shape.draw()
        shape.resize(2.0)

# Example usage
class Circle:
    def draw(self):
        print("Drawing a circle")

    def resize(self, factor: float):
        print(f"Resizing circle by factor {factor}")

class Rectangle:
    def draw(self):
        print("Drawing a rectangle")

    def resize(self, factor: float):
        print(f"Resizing rectangle by factor {factor}")

# This works with any class that has draw and resize methods,
# regardless of its actual type or inheritance
shapes: List[Shape] = [Circle(), Rectangle()]
process_shapes(shapes)

В этом примере Circle и Rectangle не наследуются от Shape или любого другого класса. Они просто реализуют необходимые методы (рисование и изменение размера). Функцияprocess_shapes может работать с любыми объектами, имеющими эти методы, благодаря протоколу Shape.

Краткое содержание

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

Используя протоколы, вы можете:

  1. Определите понятные интерфейсы для вашего кода
  2. Улучшенная среда разработки (проверка статического типа), поддержка и обнаружение ошибок раньше
  3. Сохраняйте гибкость утиного набора текста
  4. Используйте проверку типов для классов, которые вы не можете изменить.

Если вы хотите узнать больше о протоколах и подсказках типов в Python, ознакомьтесь с официальной документацией Python по модулю типизации или изучите расширенные инструменты статической проверки типов, такие как mypy.

Удачного программирования, и пусть ваши утки всегда крякают о безопасности типов!

Вы можете найти больше моего контента, включая мою рассылку, здесь

Заявление о выпуске Эта статья воспроизведена по адресу: https://dev.to/samkeen/duck-typing-meets-type-hints-using-protocols-in-python-4d8l?1 Если есть какие-либо нарушения, пожалуйста, свяжитесь с [email protected] удалить его
Последний учебник Более>

Изучайте китайский

Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.

Copyright© 2022 湘ICP备2022001581号-3