Question

Parce que je suis habitué aux anciennes façons de taper de canard en Python, je ne comprends pas la nécessité d'ABC (classes abstraites de base). aide est bon sur la façon de les utiliser.

J'ai essayé de lire la raison d'être dans le PEP , mais il a au dessus de ma tête. Si je cherchais un conteneur de séquence mutable, je vérifier __setitem__, ou plus probablement essayer de l'utiliser ( EAFP ). Je ne suis pas venu à travers une utilisation de la vie réelle pour les numéros module, qui ne ABCs d'utilisation, mais qui est le plus proche, je dois comprendre.

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

Était-ce utile?

La solution

Version courte

ABCs offrent un niveau supérieur de contrat sémantique entre les clients et les classes mises en œuvre.

Version longue

Il y a un contrat entre une classe et ses appelants. Les promesses de la classe à faire certaines choses et ont certaines propriétés.

Il existe différents niveaux au contrat.

A un niveau très bas, le contrat pourrait inclure le nom d'une méthode ou son nombre de paramètres.

Dans un langage statiquement typé, ce contrat serait effectivement appliqué par le compilateur. En Python, vous pouvez utiliser EAFP ou l'introspection pour confirmer que l'objet inconnu répond à ce contrat prévu.

Mais il y a aussi de plus haut niveau, les promesses sémantiques dans le contrat.

Par exemple, s'il existe une méthode de __str__(), il est prévu de retourner une représentation de chaîne de l'objet. Il peut supprimer tout le contenu de l'objet, valider la transaction et cracher une page blanche de l'imprimante ... mais il y a une compréhension commune de ce qu'il doit faire, décrit dans le manuel Python.

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

Définition d'une classe abstraite de base (ABC) est un moyen de produire un contrat entre les implémenteurs de classe et les appelants. Il est non seulement 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 promettez de suivre toutes les règles décrites dans les commentaires, y compris la sémantique de la méthode print().

canard de frappe Python présente de nombreux avantages en termes de flexibilité sur-typage statique, mais il ne résout pas tous les problèmes. ABC offrent une solution intermédiaire entre la forme libre de Python et de la servitude et la discipline d'un langage statiquement typé.

Autres conseils

@ La réponse de Oddthinking n'est pas mal, mais je pense qu'il manque le real , pratique raison Python a ABCs dans un monde de canard frappe.

Les méthodes abstraites sont propres, mais à mon avis, ils ne sont pas vraiment remplir les cas d'utilisation ne sont pas déjà couverts par la saisie de canard. puissance réelle de classes de base abstraites réside dans la façon dont ils vous permettent de personnaliser le comportement des isinstance et issubclass . (__subclasshook__ est essentiellement une API conviviale au-dessus de son Python __instancecheck__ et __subclasscheck__ crochets.) l'adaptation intégrée des constructions à travailler sur des types personnalisés est tout à fait partie de la philosophie de Python.

Le code source de Python est exemplaire. Voici comment collections.Container est défini dans la bibliothèque standard ( au moment de la rédaction):

class Container(metaclass=ABCMeta):
    __slots__ = ()

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

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Container:
            if any("__contains__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

Cette définition de __subclasshook__ dit que toute la classe avec un attribut __contains__ est considéré comme une sous-classe de conteneur, même si elle ne la sous-classe pas directement. Je peux donc écrire ceci:

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

>>> issubclass(ContainAllTheThings, collections.Container)
True
>>> isinstance(ContainAllTheThings(), collections.Container)
True

En d'autres termes, si vous implémentez l'interface droite, vous êtes une sous-classe! ABCs fournissent une manière formelle pour définir les interfaces en Python, tout en restant fidèle à l'esprit de canard frappe. D'ailleurs, cela fonctionne d'une manière qui honore ouvert-fermé Principe .

modèle objet de Python regards superficiellement similaire à celle d'un système plus OO « traditionnel » (je veux dire par Java *) - nous avons yer classes, yer objets, yer méthodes - mais quand vous grattez la surface, vous trouverez quelque chose beaucoup plus riche et plus souple. De même, la notion de Python de classes de base abstraites peut être reconnaissable à un développeur Java, mais dans la pratique, ils sont destinés à un usage très différent.

Je trouve parfois moi-même écrire des fonctions polymorphes qui peuvent agir sur un seul élément ou une collection d'articles, et je trouve isinstance(x, collections.Iterable) beaucoup plus lisible que hasattr(x, '__iter__') ou un bloc équivalent try...except. (Si vous ne saviez pas Python, qui de ces trois rendrait l'intention de la plus claire de code?)

Cela dit, je trouve que j'ai rarement besoin d'écrire mon propre ABC et je découvre généralement la nécessité d'un à refactoring. Si je vois une fonction polymorphique faire beaucoup de contrôles d'attributs, ou beaucoup de fonctions qui font les mêmes contrôles d'attributs, cette odeur suggère l'existence d'un ABC en attente d'être extrait.

* sans entrer dans le débat de savoir si Java est un système OO "traditionnel" ...


Addendum : Même si une classe de base abstraite peut remplacer le comportement de isinstance et issubclass, il ne reste pas entrer dans le MRO de la sous-classe virtuelle. Ceci est un piège potentiel pour les clients. Pas tous les objets pour lesquels 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

Malheureusement, celui-ci de ceux « juste ne pas le faire » pièges (dont Python a relativement peu!): Éviter de définir ABCs à la fois un __subclasshook__ et des méthodes non abstraites. De plus, vous devez faire votre définition de __subclasshook__ compatible avec l'ensemble des méthodes abstraites vos définit de ABC.

Une caractéristique pratique de VARA est que si vous ne mettez pas en œuvre toutes les méthodes nécessaires (et propriétés) vous obtenez une erreur sur instanciation, plutôt qu'une AttributeError , potentiellement beaucoup plus tard, lorsque vous essayez réellement d'utiliser la méthode manquante.

from abc import ABCMeta, abstractmethod

# python2
class Base(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def foo(self):
        pass

    @abstractmethod
    def bar(self):
        pass

# python3
class Base(object, metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass

    @abstractmethod
    def bar(self):
        pass

class Concrete(Base):
    def foo(self):
        pass

    # We forget to declare `bar`


c = Concrete()
# TypeError: "Can't instantiate abstract class Concrete with abstract methods bar"

Exemple de https://dbader.org/blog/abstract-base- les classes-en-python

Edit: pour inclure la syntaxe python3, @PandasRocks remercie

Il fera déterminer si un objet prend en charge un protocole donné sans avoir à vérifier la présence de toutes les méthodes dans le protocole ou sans déclencher une exception profonde en territoire « ennemi » en raison du non-support beaucoup plus facile.

faire de la méthode abstraite sûr que ce soit la méthode que vous appelez dans la classe parent doit être apparaître dans la classe des enfants. Voici noraml façon d'appeler à l'aide et abstraite. Le programme écrit dans python3

façon normale d'appeler

class Parent:
def methodone(self):
    raise NotImplemented()

def methodtwo(self):
    raise NotImplementedError()

class Son(Parent):
   def methodone(self):
       return 'methodone() is called'

c = Son()
c.methodone()
  
    

'methodone () est appelée'

  
c.methodtwo()
  
    

NotImplementedError

  

Avec la méthode abstraite

from abc import ABCMeta, abstractmethod

class Parent(metaclass=ABCMeta):
    @abstractmethod
    def methodone(self):
        raise NotImplementedError()
    @abstractmethod
    def methodtwo(self):
        raise NotImplementedError()

class Son(Parent):
    def methodone(self):
        return 'methodone() is called'

c = Son()
  
    

TypeError. Instanciation classe abstraite Fils avec des méthodes abstraites methodtwo

  

Depuis methodtwo n'est pas appelé dans la classe des enfants que nous avons eu erreur. La mise en œuvre est inférieure à

from abc import ABCMeta, abstractmethod

class Parent(metaclass=ABCMeta):
    @abstractmethod
    def methodone(self):
        raise NotImplementedError()
    @abstractmethod
    def methodtwo(self):
        raise NotImplementedError()

class Son(Parent):
    def methodone(self):
        return 'methodone() is called'
    def methodtwo(self):
        return 'methodtwo() is called'

c = Son()
c.methodone()
  
    

'methodone () est appelée'

  
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top