"Si un ouvrier veut bien faire son travail, il doit d'abord affûter ses outils." - Confucius, "Les Entretiens de Confucius. Lu Linggong"
Page de garde > La programmation > Duck Typing rencontre les indices de type : utilisation de protocoles en Python

Duck Typing rencontre les indices de type : utilisation de protocoles en Python

Publié le 2024-11-08
Parcourir:905

Duck Typing Meets Type Hints: Using Protocols in Python

La nature dynamique de Python et la prise en charge du typage duck ont ​​longtemps été louées pour leur flexibilité. Cependant, à mesure que les bases de code deviennent plus volumineuses et plus complexes, les avantages de la vérification de type statique deviennent de plus en plus évidents. Mais comment concilier la flexibilité du typage canard et la sécurité de la vérification de type statique ? Entrez dans la classe Protocol de Python.

Dans ce didacticiel, vous apprendrez :

  1. Qu'est-ce que le typage canard et comment il est pris en charge en Python
  2. Les avantages et les inconvénients de la saisie au canard
  3. Comment les classes de base abstraites (ABC) tentent de résoudre les problèmes de typage
  4. Comment utiliser le protocole pour tirer le meilleur parti des deux mondes : flexibilité de frappe en canard avec vérification de type statique

Comprendre la frappe du canard

Le typage Duck est un concept de programmation dans lequel le type ou la classe d'un objet est moins important que les méthodes qu'il définit. Il est basé sur l'idée que "S'il ressemble à un canard, nage comme un canard et cancane comme un canard, alors c'est probablement un canard."

En Python, la saisie en canard est entièrement prise en charge. Par exemple:

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!

Dans cet exemple, make_it_quack ne se soucie pas du type de chose. Il s'en soucie seulement que cette chose ait une méthode de charlatan. Notez qu'il n'y a pas d'indication de type pour le paramètre thing, ce qui est typique dans le code typé canard mais peut entraîner des problèmes dans des bases de code plus volumineuses.

Avantages et inconvénients de la frappe au canard

La saisie canard offre plusieurs avantages :

  1. Flexibilité : il permet un code plus flexible qui n'est pas lié à des types spécifiques.
  2. Réutilisation plus facile du code : vous pouvez utiliser les classes existantes dans de nouveaux contextes sans modification.
  3. L'accent est mis sur le comportement : il se concentre sur ce qu'un objet peut faire, plutôt que sur ce qu'il est.

Cependant, il présente également quelques inconvénients :

  1. Manque de clarté : il peut être difficile de savoir quelles méthodes un objet doit implémenter.
  2. Erreurs d'exécution : les erreurs liées au type ne sont détectées qu'au moment de l'exécution.
  3. Moins de prise en charge de l'IDE : les IDE ont du mal à fournir une saisie semi-automatique et une vérification des erreurs précises.

La solution ABC

Une approche pour résoudre ces problèmes consiste à utiliser des classes de base abstraites (ABC). Voici un exemple :

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)

Bien que cette approche offre une meilleure vérification de type et des interfaces plus claires, elle présente des inconvénients :

  1. Cela nécessite un héritage, ce qui peut conduire à des hiérarchies inflexibles.
  2. Cela ne fonctionne pas avec les classes existantes que vous ne pouvez pas modifier.
  3. Cela va à l'encontre de la philosophie du "duck typing" de Python.

Protocoles : le meilleur des deux mondes

Python 3.8 a introduit la classe Protocol, qui nous permet de définir des interfaces sans nécessiter d'héritage. Voici comment nous pouvons l'utiliser :

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)

Décomposons cela :

  1. Nous définissons un protocole Quacker qui spécifie l'interface que nous attendons.
  2. Nos classes Duck et Person n'ont pas besoin d'hériter de quoi que ce soit.
  3. Nous pouvons utiliser des indices de type avec make_it_quack pour spécifier qu'il attend un Quacker.

Cette approche nous apporte plusieurs avantages :

  1. Vérification de type statique : les IDE et les vérificateurs de type peuvent détecter les erreurs avant l'exécution.
  2. Aucun héritage requis : les classes existantes fonctionnent tant qu'elles disposent des bonnes méthodes.
  3. Interfaces claires : le protocole définit clairement les méthodes attendues.

Voici un exemple plus complexe montrant comment les protocoles peuvent être aussi complexes que nécessaire (forme), en gardant vos classes de domaine (cercle, rectangle) plates :

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)

Dans cet exemple, Circle et Rectangle n'héritent pas de Shape ou de toute autre classe. Ils implémentent simplement les méthodes requises (dessiner et redimensionner). La fonction process_shapes peut fonctionner avec tous les objets dotés de ces méthodes, grâce au protocole Shape.

Résumé

Les protocoles en Python fournissent un moyen puissant d'apporter le typage statique au code typé canard. Ils nous permettent de spécifier des interfaces dans le système de types sans nécessiter d'héritage, en conservant la flexibilité du typage duck tout en ajoutant les avantages de la vérification de type statique,

En utilisant les protocoles, vous pouvez :

  1. Définissez des interfaces claires pour votre code
  2. Obtenez un meilleur IDE (vérification de type statique), prenez en charge et détectez les erreurs plus tôt
  3. Maintenir la flexibilité de la saisie par canard
  4. Exploitez la vérification de type pour les classes que vous ne pouvez pas modifier.

Si vous souhaitez en savoir plus sur les protocoles et les indications de type en Python, consultez la documentation officielle de Python sur le module de saisie ou explorez les outils avancés de vérification de type statique comme mypy.

Bon codage, et que vos canards cancanent toujours avec la sécurité des types !

Vous pouvez trouver plus de mon contenu, y compris ma newsletter ici

Déclaration de sortie Cet article est reproduit sur : https://dev.to/samkeen/duck-typing-meets-type-hints-using-protocols-in-python-4d8l?1 En cas de violation, veuillez contacter [email protected] pour le supprimer
Dernier tutoriel Plus>

Clause de non-responsabilité: Toutes les ressources fournies proviennent en partie d'Internet. En cas de violation de vos droits d'auteur ou d'autres droits et intérêts, veuillez expliquer les raisons détaillées et fournir une preuve du droit d'auteur ou des droits et intérêts, puis l'envoyer à l'adresse e-mail : [email protected]. Nous nous en occuperons pour vous dans les plus brefs délais.

Copyright© 2022 湘ICP备2022001581号-3