287 votes

Méthodes abstraites en Python

J'ai des difficultés à utiliser l'héritage avec Python. Alors que le concept me semble trop facile en Java, jusqu'à présent je n'ai pas réussi à le comprendre en Python, ce qui est surprenant, du moins pour moi.

J'ai un prototype qui suit :

class Shape():
   def __init__(self, shape_name):
       self.shape = shape_name

class Rectangle(Shape):
   def __init__(self, name):
       self.shape = name

Dans le code ci-dessus, comment puis-je créer une méthode abstraite qui devra être implémentée pour toutes les sous-classes ?

6 votes

Les interfaces abstraites et autres n'ont été introduites que récemment. Puisque python n'est pas compilé, vous devriez préciser votre intention dans la documentation plutôt que dans les classes abstraites.

0 votes

Également un très une bonne application pour les tests unitaires. Comme Python n'est pas compilé, je ne commence pas à faire confiance à un code avant de l'avoir testé à l'unité.

352voto

kevpie Points 5243

Avant l'introduction de l'abc, on voyait cela fréquemment.

class Base(object):
    def go(self):
        raise NotImplementedError("Please Implement this method")

class Specialized(Base):
    def go(self):
        print "Consider me implemented"

71 votes

À mon avis, cette réponse est plus pythique que la réponse acceptée, car elle suit l'adage python "nous sommes tous des adultes ici" - si une sous-classe n'implémente pas ce type de méthode abstraite, elle devra faire face aux conséquences.

2 votes

@EmmettJ.Butler Mais abc devrait être implémenté après tout. N'ai-je pas raison ?

2 votes

@Spiderman non, abc n'est pas pour les classes de base simples, abc est pour quand vous avez besoin de vérifications isinstance, similaire à isinstance(x, number) ou isinstance(x, collections.abc.Sequence).

316voto

pyfunc Points 31088

Quelque chose comme ça, en utilisant ABC

import abc

class Shape(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def method_to_implement(self, input):
        """Method documentation"""
        return

Lisez également ce bon tutoriel : https://pymotw.com/3/abc/

Vous pouvez également consulter zope.interface qui était utilisé avant l'introduction d'ABC dans python.

0 votes

Est-ce que l'ajout de cet élément a également un effet sur les autres éléments ? c'est-à-dire est-ce que d'autres parties de mon code de classe doivent être modifiées ?

0 votes

@user506710 : N'oubliez pas qu'elle n'est introduite que maintenant. Auparavant, Python utilisait le "duck typing".

2 votes

@user506710 : La seule autre partie de votre code qui pourrait avoir besoin d'être modifiée est si vous utilisez déjà une métaclasse. Dans ce cas, il suffit que votre métaclasse dérive de abc.ABCMeta au lieu de type et vous devriez vous en sortir. Si vous ne savez pas ce qu'est une métaclasse, ne vous en faites pas :-)

46voto

kindall Points 60645

Voir le module abc . En gros, vous définissez __metaclass__ = abc.ABCMeta sur la classe, puis décorer chaque méthode abstraite avec @abc.abstractmethod . Les classes dérivées de cette classe ne peuvent ensuite être instanciées que si toutes les méthodes abstraites ont été surchargées.

Si votre classe utilise déjà une métaclasse, dérivez-la à partir de ABCMeta plutôt que type et vous pouvez continuer à utiliser votre propre métaclasse.

Une alternative bon marché (et la meilleure pratique avant le abc a été introduit) serait de faire en sorte que toutes vos méthodes abstraites lèvent simplement une exception ( NotImplementedError est un bon exemple) de sorte que les classes qui en sont dérivées doivent surcharger cette méthode pour être utiles.

Toutefois, le abc est meilleure parce qu'elle empêche l'instanciation de ces classes (c'est-à-dire qu'elle "échoue plus rapidement"), et aussi parce que vous pouvez fournir une implémentation par défaut ou de base de chaque méthode qui peut être atteinte à l'aide de la méthode super() dans les classes dérivées.

0 votes

you can provide a default or base implementation of each method that can be reached using the super() function in derived classes vous ne pouvez pas utiliser super() sans abc ?

0 votes

Ce que ça veut dire, c'est que tu dois utiliser super() pour atteindre les implémentations de méthode dans un ABC, et non pas que vous devez utiliser abc afin d'utiliser super() . super() fonctionne avec n'importe quelle classe de style nouveau.

0 votes

J'étais seulement confus parce que c'était "abc est meilleur pour la motivation 1 et la motivation 2" mais la motivation 2 ne l'est pas. abc spécifique :)

13voto

demented hedgehog Points 733

Les classes de base abstraites sont de la magie profonde. De temps en temps, j'implémente quelque chose en les utilisant et je suis étonné de ma propre ingéniosité, mais peu de temps après, je me retrouve complètement désorienté par ma propre ingéniosité (cela pourrait bien être une limitation personnelle).

Une autre façon de faire cela (qui devrait se trouver dans les librairies std de python si vous voulez mon avis) est de créer un décorateur.

def abstractmethod(method):
    """
    An @abstractmethod member fn decorator.
    (put this in some library somewhere for reuse).

    """
    def default_abstract_method(*args, **kwargs):
        raise NotImplementedError('call to abstract method ' 
                                  + repr(method))
    default_abstract_method.__name__ = method.__name__    
    return default_abstract_method

class Shape(object):

    def __init__(self, shape_name):
       self.shape = shape_name

    @abstractmethod
    def foo(self):
        print "bar"
        return

class Rectangle(Shape):
    # note you don't need to do the constructor twice either
    pass  

r = Rectangle("x")
r.foo()

Je n'ai pas écrit au décorateur. Je me suis juste dit que quelqu'un l'aurait fait. Vous pouvez le trouver ici : http://code.activestate.com/recipes/577666-abstract-method-decorator/ Bien joué, Jimmy2times. Notez la discussion au bas de cette page concernant la sécurité de type du décorateur. (Cela pourrait être corrigé avec le module inspect si quelqu'un était si enclin).

3 votes

En quoi cela diffère-t-il de méthode abc.abstract ?

0 votes

Ce n'est pas une bonne affaire. Sous le capot, elles ne sont pas trop différentes, j'imagine. De manière pragmatique : "L'utilisation du décorateur abc.abstractmethod nécessite que la métaclasse de la classe soit ABCMeta ou qu'elle en soit dérivée".

7voto

Adam Norberg Points 2336

Vous ne pouvez pas, avec les primitives du langage. Comme on l'a dit, le paquet abc fournit cette fonctionnalité à partir de la version 2.6 de Python, mais il n'existe pas d'option pour Python 2.5 et les versions antérieures. Le site abc n'est pas une nouvelle fonctionnalité de Python ; au lieu de cela, il ajoute une fonctionnalité en ajoutant des contrôles explicites "cette classe dit-elle qu'elle fait ceci ?", avec des contrôles de cohérence implémentés manuellement pour provoquer une erreur pendant l'initialisation si de telles déclarations sont fausses.

Python est un langage à typage dynamique militant. Il ne spécifie pas de primitives de langage vous permettant d'empêcher un programme de compiler parce qu'un objet ne correspond pas aux exigences de type ; ceci ne peut être découvert qu'au moment de l'exécution. Si vous exigez qu'une sous-classe implémente une méthode, documentez-le, puis appelez la méthode en espérant aveuglément qu'elle sera présente.

S'il est là, fantastique, il fonctionne tout simplement ; c'est ce qu'on appelle dactylographie du canard et votre objet a fait suffisamment de bruit de canard pour satisfaire l'interface. Cela fonctionne très bien même si self est l'objet sur lequel vous appelez une telle méthode à des fins de remplacement obligatoire en raison de méthodes de base qui nécessitent des implémentations spécifiques de fonctionnalités (fonctions génériques), car self est une convention, et non quelque chose de spécial.

L'exception se trouve dans __init__ En effet, lorsque votre initialisateur est appelé, l'initialisateur du type dérivé ne l'a pas fait, et il n'a donc pas encore eu l'occasion d'agrafer ses propres méthodes à l'objet.

Si la méthode n'a pas été implémentée, vous obtiendrez un AttributeError (si elle n'existe pas du tout) ou un TypeError (si quelque chose portant ce nom existe mais que ce n'est pas une fonction ou qu'elle n'avait pas cette signature). C'est à vous de voir comment vous gérez cela : soit vous appelez cela une erreur de programmeur et vous laissez le programme se planter (et il "devrait" être évident pour un développeur python de savoir ce qui cause ce genre d'erreur : une interface de canard non satisfaite), soit vous attrapez et gérez ces exceptions lorsque vous découvrez que votre objet ne supporte pas ce que vous souhaitez qu'il supporte. Attraper AttributeError et TypeError est important dans de nombreuses situations, en fait.

0 votes

Qu'en est-il du module abc mentionné dans l'autre réponse ?

7 votes

Pour ce que ça vaut, Python est fortement typée, c'est juste dynamiquement typée.

1 votes

Ack. C'est la première fois que j'ai entendu parler de la abc module. Je laisse cependant cette réponse, car elle s'applique aux versions de Python antérieures à la 2.6 (CentOS est livré avec la 2.4), et la saisie en canard fonctionne toujours ici.

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