328 votes

Pourquoi utiliser les classes de base abstraites en Python?

Utilisé pour les anciennes façons de duck-typing en Python, je n'ai pas réussi à comprendre le besoin de l'ABC (classes de base abstraites). L' aide est une bonne façon de les utiliser.

J'ai essayé de lire la justification dans le PEP, mais il est passé au dessus de ma tête. Si je cherchais une mutable conteneur de séquence, je voudrais vérifier pour __setitem__, ou plus probablement essayé de l'utiliser (l'aeap). Je n'ai pas rencontré de la vie réelle d'utilisation pour les numéros de module, qui utilise de l'Abc, mais qui est le plus proche que j'ai à la compréhension.

Quelqu'un peut-il m'expliquer la raison d'être, s'il vous plaît?

338voto

Benjamin Hodgson Points 3510

@Oddthinking la réponse n'est pas mal, mais je crois qu'il manque le réel, pratique de la raison Python a b a ba dans un monde de canard-typage.

Les méthodes abstraites sont utiles, mais les classes de base abstraites " vrai pouvoir réside dans la façon dont ils vous permettent de personnaliser le comportement de l' isinstance et issubclass. (__subclasshook__ est essentiellement une plus respectueuses de l'API sur le dessus de Python __instancecheck__ et __subclasscheck__ crochets.) L'adaptation intégrée dans le comportement de travailler sur le type personnalisé est très bien partie de Python de la philosophie.

Pour ré-utiliser l'exemple de l' collections.Container - ici est la façon dont il est défini dans le standard de la bibliothèque:

class Container:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __contains__(self, x):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Container:
            if _hasattr(C, "__contains__"):
                return True
        return NotImplemented

Cette définition de l' __subclasshook__ dit que toute la classe avec un __contains__ attribut est considéré comme une sous-classe de Conteneur, même si elle n'est pas sous-classe directement. Donc, je peux écrire ceci:

class ContainAllTheThings(object):
    def __contains__(self, item):
        return True

print(issubclass(ContainAllTheThings, collections.Container))
# prints True
print(isinstance(ContainAllTheThings(), collections.Container))
# prints True

En d'autres termes, si vous mettez en œuvre le droit de l'interface, vous êtes une sous-classe! Abc fournir un moyen formel de définir des interfaces en Python, tout en restant fidèle à l'esprit de canard-typage. Par ailleurs, cela fonctionne d'une manière qui honore la Ouvert-Fermé Principe.

Parfois, je me retrouve à écrire fonctions polymorphes qui peuvent agir sur un seul élément ou d'un ensemble d'éléments, et je trouve isinstance(x, collections.Iterable) à être beaucoup plus lisible qu' hasattr(x, '__iter__') (ou équivalent try...except bloc. (Si vous ne savez pas Python, lequel de ces trois rendrait l'intention de le code plus clair?)

Je trouve que j'ai rarement besoin d'écrire ma propre ABC, et j'ai généralement découvrir la nécessité pour l'un par la refactorisation. Si je vois une fonction polymorphe de faire beaucoup de l'attribut de vérification, ou beaucoup de fonctions à faire le même attribut de contrôles, l'odeur suggère l'existence d'un ABC en attente d'être extraites.


Addendum: Même si une classe de base abstraite pouvez remplacer le comportement de l' isinstance et issubclass, cela ne fonctionne toujours pas entrer dans la MRO de la sous-classe virtuelle. C'est un écueil potentiel pour les clients: pas tous les objets qui isinstance(x, MyABC) == True a les méthodes définies sur MyABC.

class MyABC(metaclass=abc.ABCMeta):
    def abc_method(self):
        pass
    @classmethod
    def __subclasshook__(cls, C):
        return True

class C(object):
    pass

# typical client code
c = C()
if isinstance(c, MyABC):  # will be true
    c.abc_method()  # raises AttributeError

La solution est simple: ne pas définir Abc avec un __subclasshook__ et non de méthodes abstraites. En outre, vous devriez faire votre définition de l' __subclasshook__ compatible avec l'ensemble des méthodes abstraites votre ABC définit.

209voto

Oddthinking Points 8946

Version courte: Abc offrent un niveau élevé de sémantique contrat entre les clients et la mise en œuvre des classes.

Version longue:

Il y a un contrat entre une classe et ses appelants. La classe promet de faire certaines choses et certaines propriétés.

Il y a différents niveaux du contrat.

À un niveau très faible, le contrat peut inclure le nom d'une méthode ou de son nombre de paramètres.

Dans un staticly tapé la langue, que le contrat serait effectivement appliqué par le compilateur. En Python, vous pouvez utiliser l'aeap ou l'introspection afin de confirmer que l'objet inconnu répond à cette attente d'un contrat.

Mais il y a aussi de plus haut niveau sémantique des promesses dans le contrat.

Par exemple, si il y a un __str__() méthode, il est prévu pour renvoyer une chaîne de caractères de la représentation de l'objet. Il pourrait supprimer tout le contenu de l'objet, de valider la transaction et cracher une page vierge de l'imprimante... mais il y a une compréhension commune de ce qu'il devrait faire, décrit dans le Python manuel.

C'est un cas particulier, où la sémantique contrat est décrit dans le manuel. Quelle devrait être l' print() méthode? Faut-il écrire l'objet d'une imprimante ou d'une ligne à l'écran, ou autre chose? Cela dépend - il vous suffit de lire les commentaires pour comprendre la pleine contrat ici. Un morceau de code client qui vérifie simplement que l' print() méthode existe a confirmé l'existence de la partie du contrat, qu'un appel de méthode peut être fait, mais pas qu'il y ait un accord sur le niveau supérieur de la sémantique de l'appel.

La définition d'une Classe de Base Abstraite (ABC) est un moyen de produire un contrat entre la classe, les maîtres d'œuvre et les appelants. Il n'est pas juste une liste de noms de méthode, mais une compréhension partagée de ce que ces méthodes devraient faire. Si vous héritez de cette ABC, vous vous engagez à respecter toutes les règles décrites dans les commentaires, y compris la sémantique de l' print() méthode.

Python en tapant duck a de nombreux avantages en termes de souplesse au cours de statique-typage, mais il ne résout pas tous les problèmes. L'abc offre une solution intermédiaire entre la forme libre de Python et à la servitude et à la discipline d'un staticly tapé la langue.

27voto

Il sera beaucoup plus facile de déterminer si un objet prend en charge un protocole donné sans avoir à vérifier la présence de toutes les méthodes du protocole ou sans déclencher une exception au plus profond du territoire "ennemi" en raison d'un non-support.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X