Différence entre la classe abstraite et l'interface en Python


Réponses:


618

Ce que vous verrez parfois est le suivant:

class Abstract1( object ):
    """Some description that tells you it's abstract,
    often listing the methods you're expected to supply."""
    def aMethod( self ):
        raise NotImplementedError( "Should have implemented this" )

Parce que Python n'a pas (et n'a pas besoin) de contrat d'interface formel, la distinction de style Java entre abstraction et interface n'existe pas. Si quelqu'un fait l'effort de définir une interface formelle, ce sera aussi une classe abstraite. Les seules différences seraient dans l'intention déclarée dans la docstring.

Et la différence entre l'abstrait et l'interface est une chose à couper les cheveux lorsque vous avez tapé du canard.

Java utilise des interfaces car il n'a pas d'héritage multiple.

Parce que Python a un héritage multiple, vous pouvez également voir quelque chose comme ça

class SomeAbstraction( object ):
    pass # lots of stuff - but missing something

class Mixin1( object ):
    def something( self ):
        pass # one implementation

class Mixin2( object ):
    def something( self ):
        pass # another

class Concrete1( SomeAbstraction, Mixin1 ):
    pass

class Concrete2( SomeAbstraction, Mixin2 ):
    pass

Cela utilise une sorte de superclasse abstraite avec des mixins pour créer des sous-classes concrètes disjointes.


5
S. Lott, voulez-vous dire qu'en raison de la frappe du canard, la distinction entre has-a (interface) et is-a (héritage) n'est pas substantielle?
Lorenzo

3
La différence entre l'abstrait et l'interface est une chose à couper les cheveux lorsque vous avez tapé du canard. Je ne sais pas ce que signifie "substantiel". C'est «réel» - il a du contenu - du point de vue du design. Mais d'un point de vue linguistique, il n'y a peut-être aucun soutien. Vous pouvez adopter des conventions pour faire la distinction entre une classe abstraite et une définition de classe d'interface en Python.
S.Lott,

26
@ L.DeLeo - êtes-vous sûr que votre notion de has-a vs is-a est correcte? Je considère généralement la distinction comme une variable membre has-a = par rapport à l'héritage is-a = (classe parent ou interface). Pensez comparable ou liste en Java; ce sont des relations is-a, qu'il s'agisse d'interfaces ou de classes abstraites.
dimo414

43
NotImplementedError("Class %s doesn't implement aMethod()" % (self.__class__.__name__))est un message d'erreur plus informatif :)
naught101

9
@Lorenzo une relation has-a n'a rien à voir avec l'héritage, le typage canard, les interfaces et les classes abstraites (les quatre se réfèrent aux relations is-a).
Karl Richter

197

Quelle est la différence entre la classe abstraite et l'interface en Python?

Une interface, pour un objet, est un ensemble de méthodes et d'attributs sur cet objet.

En Python, nous pouvons utiliser une classe de base abstraite pour définir et appliquer une interface.

Utilisation d'une classe de base abstraite

Par exemple, disons que nous voulons utiliser l'une des classes de base abstraites du collectionsmodule:

import collections
class MySet(collections.Set):
    pass

Si nous essayons de l'utiliser, nous obtenons un TypeErrorcar la classe que nous avons créée ne prend pas en charge le comportement attendu des ensembles:

>>> MySet()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MySet with abstract methods
__contains__, __iter__, __len__

Nous sommes donc tenus de mettre en œuvre au moins __contains__ , __iter__et __len__. Utilisons cet exemple d'implémentation de la documentation :

class ListBasedSet(collections.Set):
    """Alternate set implementation favoring space over speed
    and not requiring the set elements to be hashable. 
    """
    def __init__(self, iterable):
        self.elements = lst = []
        for value in iterable:
            if value not in lst:
                lst.append(value)
    def __iter__(self):
        return iter(self.elements)
    def __contains__(self, value):
        return value in self.elements
    def __len__(self):
        return len(self.elements)

s1 = ListBasedSet('abcdef')
s2 = ListBasedSet('defghi')
overlap = s1 & s2

Implémentation: création d'une classe de base abstraite

Nous pouvons créer notre propre classe de base abstraite en définissant la métaclasse sur abc.ABCMetaet en utilisant le abc.abstractmethoddécorateur sur les méthodes pertinentes. La métaclasse ajoutera les fonctions décorées à l' __abstractmethods__attribut, empêchant l'instanciation jusqu'à ce qu'elles soient définies.

import abc

Par exemple, "effable" est défini comme quelque chose qui peut être exprimé en mots. Supposons que nous voulions définir une classe de base abstraite qui soit effable, en Python 2:

class Effable(object):
    __metaclass__ = abc.ABCMeta
    @abc.abstractmethod
    def __str__(self):
        raise NotImplementedError('users must define __str__ to use this base class')

Ou en Python 3, avec le léger changement de déclaration de métaclasse:

class Effable(object, metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def __str__(self):
        raise NotImplementedError('users must define __str__ to use this base class')

Maintenant, si nous essayons de créer un objet effable sans implémenter l'interface:

class MyEffable(Effable): 
    pass

et essayez de l'instancier:

>>> MyEffable()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyEffable with abstract methods __str__

On nous dit que nous n'avons pas terminé le travail.

Maintenant, si nous nous conformons en fournissant l'interface attendue:

class MyEffable(Effable): 
    def __str__(self):
        return 'expressable!'

on peut alors utiliser la version concrète de la classe dérivée de la classe abstraite:

>>> me = MyEffable()
>>> print(me)
expressable!

Il y a d'autres choses que nous pourrions faire avec cela, comme enregistrer des sous-classes virtuelles qui implémentent déjà ces interfaces, mais je pense que cela dépasse le cadre de cette question. Les autres méthodes présentées ici devraient adapter cette méthode en utilisant leabc module.

Conclusion

Nous avons démontré que la création d'une classe de base abstraite définit des interfaces pour des objets personnalisés en Python.


101

Python> = 2.6 a des classes de base abstraites .

Les classes de base abstraites (ABC abrégés) complètent le typage canard en fournissant un moyen de définir des interfaces lorsque d'autres techniques comme hasattr () seraient maladroites. Python est livré avec de nombreux ABC intégrés pour les structures de données (dans le module collections), les nombres (dans le module nombres) et les flux (dans le module io). Vous pouvez créer votre propre ABC avec le module abc.

Il y a aussi le module Zope Interface , qui est utilisé par des projets en dehors de zope, comme twisted. Je ne le connais pas vraiment, mais il y a une page wiki ici qui pourrait aider.

En général, vous n'avez pas besoin du concept de classes abstraites ou d'interfaces en python (édité - voir la réponse de S.Lott pour plus de détails).


2
Que gagnez-vous en utilisant les ABC en Python?
CpILL

38

Python n'a pas vraiment de concept.

Il utilise le typage canard, ce qui a supprimé le besoin d'interfaces (au moins pour l'ordinateur :-))

Python <= 2.5: Les classes de base existent évidemment, mais il n'y a aucun moyen explicite de marquer une méthode comme 'virtuelle pure', donc la classe n'est pas vraiment abstraite.

Python> = 2.6: des classes de base abstraites existent ( http://docs.python.org/library/abc.html ). Et vous permettent de spécifier des méthodes qui doivent être implémentées dans des sous-classes. Je n'aime pas beaucoup la syntaxe, mais la fonctionnalité est là. La plupart du temps, il est probablement préférable d'utiliser la frappe de canard du côté client «utilisant».


3
Python 3.0 ajoute de véritables classes de base abstraites. Ils sont utilisés dans le module des collections ainsi que dans d'autres endroits. docs.python.org/3.0/library/abc.html
Lara Dougan

Une référence sur les raisons pour lesquelles la saisie de canards supprime le besoin d'interfaces serait utile. Il ne me semble pas évident que le typage de canard, que je comprends comme la capacité de "pousser" n'importe quelle méthode ou attribut sur n'importe quel objet, signifierait que vous n'avez pas besoin de spécifier les comportements requis (et que le compilateur vous rappelle pour les implémenter), c'est ainsi que je comprends les classes de base abstraites.
Reb.Cabin

C'est moins le typage canard que la prise en charge de l'héritage multiple qui supprime la ligne artificielle entre l'interface et la classe abstraite que par exemple Java a dessinée.
masi

35

D'une manière plus simple à expliquer: Une interface est un peu comme un moule à muffins vide. Il s'agit d'un fichier de classe avec un ensemble de définitions de méthodes sans code.

Une classe abstraite est la même chose, mais toutes les fonctions n'ont pas besoin d'être vides. Certains peuvent avoir du code. Ce n'est pas strictement vide.

Pourquoi différencier: Il n'y a pas beaucoup de différence pratique en Python, mais au niveau de la planification d'un grand projet, il pourrait être plus courant de parler d'interfaces, car il n'y a pas de code. Surtout si vous travaillez avec des programmeurs Java habitués au terme.


+1 pour la distinction où ABC peut avoir ses propres implémentations - ce qui semble être un moyen très cool de se déjouer
Titou

17

En général, les interfaces sont utilisées uniquement dans les langages qui utilisent le modèle de classe à héritage unique. Dans ces langages à héritage unique, les interfaces sont généralement utilisées si une classe peut utiliser une méthode ou un ensemble de méthodes particulier. Toujours dans ces langages à héritage unique, les classes abstraites sont utilisées pour avoir des variables de classe définies en plus d’aucune ou plusieurs méthodes, ou pour exploiter le modèle d’héritage unique pour limiter la gamme de classes qui pourraient utiliser un ensemble de méthodes.

Les langages qui prennent en charge le modèle à héritage multiple ont tendance à utiliser uniquement des classes ou des classes de base abstraites et non des interfaces. Étant donné que Python prend en charge l'héritage multiple, il n'utilise pas d'interfaces et vous souhaitez utiliser des classes de base ou des classes de base abstraites.

http://docs.python.org/library/abc.html


2

Les classes abstraites sont des classes qui contiennent une ou plusieurs méthodes abstraites. En plus des méthodes abstraites, les classes abstraites peuvent avoir des méthodes statiques, de classe et d'instance. Mais en cas d'interface, il n'aura que des méthodes abstraites pas les autres. Par conséquent, il n'est pas obligatoire d'hériter de la classe abstraite mais il est obligatoire d'hériter de l'interface.


1

Pour être complet, nous devons mentionner PEP3119 où ABC a été introduit et comparé aux interfaces, et le commentaire original de Talin .

La classe abstraite n'est pas une interface parfaite:

  • appartient à la hiérarchie d'héritage
  • est mutable

Mais si vous envisagez de l'écrire à votre façon:

def some_function(self):
     raise NotImplementedError()

interface = type(
    'your_interface', (object,),
    {'extra_func': some_function,
     '__slots__': ['extra_func', ...]
     ...
     '__instancecheck__': your_instance_checker,
     '__subclasscheck__': your_subclass_checker
     ...
    }
)

ok, rather as a class
or as a metaclass
and fighting with python to achieve the immutable object
and doing refactoring
...

vous réaliserez assez rapidement que vous inventez la roue pour finalement atteindre abc.ABCMeta

abc.ABCMeta a été proposé comme un ajout utile de la fonctionnalité d'interface manquante, et c'est assez juste dans un langage comme python.

Certes, il a pu être amélioré davantage lors de l'écriture de la version 3, et en ajoutant une nouvelle syntaxe et un concept d'interface immuable ...

Conclusion:

The abc.ABCMeta IS "pythonic" interface in python
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.