"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Duck Typing cumple con las sugerencias de escritura: uso de protocolos en Python

Duck Typing cumple con las sugerencias de escritura: uso de protocolos en Python

Publicado el 2024-09-04
Navegar:303

Duck Typing Meets Type Hints: Using Protocols in Python

La naturaleza dinámica de Python y su compatibilidad con la escritura pato han sido elogiados durante mucho tiempo por su flexibilidad. Sin embargo, a medida que las bases de código crecen y se vuelven más complejas, los beneficios de la verificación de tipos estáticos se vuelven cada vez más evidentes. Pero, ¿cómo podemos conciliar la flexibilidad de la escritura pato con la seguridad de la verificación de tipos estática? Ingrese a la clase Protocolo de Python.

En este tutorial, aprenderás:

  1. Qué es el tipeo de pato y cómo se admite en Python
  2. Los pros y los contras de escribir pato
  3. Cómo las clases base abstractas (ABC) intentan resolver problemas de mecanografía
  4. Cómo utilizar el protocolo para obtener lo mejor de ambos mundos: flexibilidad de escritura con verificación de tipo estática

Comprender la escritura del pato

Duck tipeo es un concepto de programación donde el tipo o clase de un objeto es menos importante que los métodos que define. Se basa en la idea de que "si parece un pato, nada como un pato y grazna como un pato, entonces probablemente sea un pato".

En Python, la escritura pato es totalmente compatible. Por ejemplo:

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!

En este ejemplo, a make_it_quack no le importa el tipo de cosa. Sólo le importa que esa cosa tenga un método de curandero. Tenga en cuenta que no hay ninguna sugerencia de tipo para el parámetro de cosa, lo cual es típico en el código de tipo pato, pero puede generar problemas en bases de código más grandes.

Pros y contras de escribir con pato

La escritura con pato ofrece varias ventajas:

  1. Flexibilidad: permite un código más flexible que no está vinculado a tipos específicos.
  2. Reutilización de código más sencilla: puede utilizar clases existentes en nuevos contextos sin modificaciones.
  3. Énfasis en el comportamiento: Se centra en lo que un objeto puede hacer, más que en lo que es.

Sin embargo, también tiene algunos inconvenientes:

  1. Falta de claridad: puede no estar claro qué métodos necesita implementar un objeto.
  2. Errores de tiempo de ejecución: los errores relacionados con el tipo solo se detectan en tiempo de ejecución.
  3. Menos compatibilidad con IDE: los IDE tienen dificultades para proporcionar un autocompletado preciso y una verificación de errores.

La solución ABC

Un método para abordar estos problemas es utilizar clases base abstractas (ABC). Aquí tienes un ejemplo:

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)

Si bien este enfoque proporciona una mejor verificación de tipos e interfaces más claras, tiene desventajas:

  1. Requiere herencia, lo que puede conducir a jerarquías inflexibles.
  2. No funciona con clases existentes que no puedes modificar.
  3. Va en contra de la filosofía de "tipificación de pato" de Python.

Protocolos: lo mejor de ambos mundos

Python 3.8 introdujo la clase Protocolo, que nos permite definir interfaces sin requerir herencia. Así es como podemos usarlo:

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)

Desglosemos esto:

  1. Definimos un protocolo Quacker que especifica la interfaz que esperamos.
  2. Nuestras clases Duck y Person no necesitan heredar de nada.
  3. Podemos usar sugerencias de tipo con make_it_quack para especificar que espera un Quacker.

Este enfoque nos brinda varios beneficios:

  1. Comprobación de tipos estática: los IDE y los verificadores de tipos pueden detectar errores antes del tiempo de ejecución.
  2. No se requiere herencia: las clases existentes funcionan siempre que tengan los métodos correctos.
  3. Interfaces claras: El Protocolo define claramente qué métodos se esperan.

Aquí hay un ejemplo más complejo que muestra cómo los protocolos pueden ser tan complejos como sea necesario (Forma), manteniendo las clases de dominio (Círculo, Rectángulo) planas:

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)

En este ejemplo, Círculo y Rectángulo no heredan de Forma ni de ninguna otra clase. Simplemente implementan los métodos requeridos (dibujar y cambiar el tamaño). La función Process_shapes puede funcionar con cualquier objeto que tenga estos métodos, gracias al protocolo Shape.

Resumen

Los protocolos en Python proporcionan una forma poderosa de incorporar la escritura estática al código tipo pato. Nos permiten especificar interfaces en el sistema de tipos sin requerir herencia, manteniendo la flexibilidad de la escritura pato mientras agregan los beneficios de la verificación de tipos estática,

Al utilizar Protocolos, puedes:

  1. Defina interfaces claras para su código
  2. Obtenga un mejor IDE (verificación de tipo estático), soporte y detecte errores antes
  3. Mantener la flexibilidad de la escritura pato
  4. Verificación del tipo de apalancamiento para las clases que no puedes modificar.

Si desea obtener más información sobre protocolos y sugerencias de escritura en Python, consulte la documentación oficial de Python en el módulo de escritura o explore herramientas avanzadas de verificación de tipos estáticos como mypy.

¡Feliz codificación y que tus patos siempre graznen con seguridad tipográfica!

Puedes encontrar más contenido mío, incluido mi boletín aquí

Declaración de liberación Este artículo se reproduce en: https://dev.to/samkeen/duck-typing-meets-type-hints-using-protocols-in-python-4d8l?1 Si hay alguna infracción, comuníquese con [email protected] para borrarlo
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3