web-dev-qa-db-fra.com

Méthodes abstraites dans Python

J'ai des difficultés à utiliser l'héritage avec Python. Bien que le concept me semble trop facile dans Java, jusqu'à présent, je n'ai pas été en mesure de le comprendre dans Python, ce qui me surprend au moins.

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 créer une méthode abstraite à implémenter pour toutes les sous-classes?

204
user506710

Quelque chose dans ce sens, 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: http://www.doughellmann.com/PyMOTW/abc/

Vous pouvez également vérifier zope.interface qui était utilisé avant l'introduction de ABC en python.

238
pyfunc

Avant de présenter abc, cela se voyait souvent.

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


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

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

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

Une alternative peu coûteuse (et la meilleure pratique avant l'introduction du module abc serait de faire en sorte que toutes vos méthodes abstraites lèvent juste une exception (NotImplementedError est une bonne solution) afin que les classes qui en dérivent aient pour remplacer cette méthode pour être utile.

Cependant, la solution abc est préférable car elle empêche l’instanciation de telles 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 accessible à l'aide de la fonction super() dans les classes dérivées.

42
kindall

Vous ne pouvez pas, avec les primitives de langage. Comme on l'a appelé, le paquet abc fournit cette fonctionnalité dans Python 2.6 et versions ultérieures, mais il n'y a pas d'options pour Python 2.5 et versions antérieures. Le package abc n'est pas une nouvelle fonctionnalité de Python; au lieu de cela, il ajoute des fonctionnalités en ajoutant explicitement "cette classe dit-elle qu'elle le fait?" vérifie, avec des vérifications de cohérence implémentées manuellement pour provoquer une erreur lors de l'initialisation si de telles déclarations sont faussement faites.

Python est un langage à typage dynamique et militant. Il ne spécifie pas de primitives de langage pour vous permettre d'empêcher la compilation d'un programme car un objet ne correspond pas aux exigences de type. cela ne peut être découvert qu'au moment de l'exécution. Si vous avez besoin qu'une sous-classe implémente une méthode, documentez-la, puis appelez la méthode en aveugle en espérant qu'elle sera là.

Si c'est là, fantastique, cela fonctionne simplement; cela s'appelle frappe de canard , et votre objet a suffisamment sonné comme un 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 , aux fins des remplacements obligatoires dus à la base méthodes qui nécessitent des implémentations spécifiques de fonctionnalités (fonctions génériques), car self est une convention, rien de vraiment spécial.

L'exception est dans __init__, car lors de l'appel de votre initialiseur, l'initialiseur du type dérivé ne l'a pas encore été. Il n'a donc pas encore eu la possibilité d'agrafer ses propres méthodes sur l'objet.

Si la méthode n'a pas été implémentée, vous obtiendrez un AttributeError (s'il n'y en a pas du tout) ou un TypeError (si quelque chose portant ce nom existe mais qui n'est ni une fonction ni une signature). C’est à vous de décider comment vous allez gérer cela: appelez-le une erreur de programmeur et laissez-le bloquer le programme (et cela "devrait" être évident pour un développeur python, quelle est la cause de ce type d'erreur là-bas, une interface non blindée ), ou attrapez et gérez ces exceptions lorsque vous découvrez que votre objet ne supporte pas ce que vous souhaiteriez. Capturer AttributeError et TypeError est important dans beaucoup de situations.

7
Adam Norberg

Les classes de base abstraites sont une magie profonde. Périodiquement, je mets en œuvre quelque chose qui les utilise et je suis émerveillé par ma propre intelligence. Très peu de temps après, je me trouve complètement dérouté par ma propre intelligence (cela pourrait bien être juste une limitation personnelle).

Une autre façon de le faire (devrait être dans le pythonsi lib si vous me demandez) est de faire 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.__= method.__   
    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 le décorateur. Il m'est juste arrivé que quelqu'un l'aurait fait. Vous pouvez le trouver ici: http://code.activestate.com/recipes/577666-abstract-method-decorator/ Good one jimmy2times. Notez la discussion au bas de cette page r.e. tapez la sécurité du décorateur. (Cela pourrait être corrigé avec le module inspect si quelqu'un était si enclin).

6
demented hedgehog

Vous pouvez utiliser six et abc pour construire efficacement une classe pour python2 et python3 comme suit:

import six
import abc

@six.add_metaclass(abc.ABCMeta)
class MyClass(object):
    """
    documentation
    """

    @abc.abstractmethod
    def initialize(self, para=None):
        """
        documentation
        """
        raise NotImplementedError

This en est un document awesomoe.

4
Lerner Zhang

Vous devez implémenter une classe de base abstraite (ABC).

Vérifiez python docs

2
joaquin