Recherche…


Définition de la métaclasse ABCMeta

Les classes abstraites sont des classes destinées à être héritées mais évitent d'implémenter des méthodes spécifiques, ne laissant que les signatures de méthode que les sous-classes doivent implémenter.

Les classes abstraites sont utiles pour définir et appliquer des abstractions de classe à un niveau élevé, similaire au concept d'interfaces dans des langages typés, sans qu'il soit nécessaire d'implémenter une méthode.

Une approche conceptuelle pour définir une classe abstraite consiste à extraire les méthodes de classe, puis à générer une erreur NotImplementedError si on y accède. Cela empêche les classes enfants d'accéder aux méthodes parentes sans les remplacer en premier. Ainsi:

class Fruit:
    
    def check_ripeness(self):
        raise NotImplementedError("check_ripeness method not implemented!")


class Apple(Fruit):
    pass


a = Apple()
a.check_ripeness() # raises NotImplementedError

La création d'une classe abstraite de cette manière empêche l'utilisation inappropriée de méthodes qui ne sont pas remplacées, et encourage certainement les méthodes à définir dans les classes enfants, mais elle n'applique pas leur définition. Avec le module abc nous pouvons empêcher les classes enfants d'être instanciées lorsqu'elles ne remplacent pas les méthodes de classes abstraites de leurs parents et ancêtres:

from abc import ABCMeta

class AbstractClass(object):
    # the metaclass attribute must always be set as a class variable 
    __metaclass__ = ABCMeta

   # the abstractmethod decorator registers this method as undefined
   @abstractmethod 
   def virtual_method_subclasses_must_define(self):
       # Can be left completely blank, or a base implementation can be provided
       # Note that ordinarily a blank interpretation implicitly returns `None`, 
       # but by registering, this behaviour is no longer enforced.

Il est maintenant possible de simplement sous-classer et remplacer:

class Subclass(AbstractClass):
    def virtual_method_subclasses_must_define(self):
        return

Pourquoi / Comment utiliser ABCMeta et @abstractmethod

Les classes de base abstraites (ABC) imposent les classes dérivées qui implémentent des méthodes particulières de la classe de base.

Pour comprendre comment cela fonctionne et pourquoi nous devrions l’utiliser, jetons un coup d’œil à un exemple que Van Rossum apprécierait. Disons que nous avons une classe de base "MontyPython" avec deux méthodes (blague & ligne de frappe) qui doivent être implémentées par toutes les classes dérivées.

class MontyPython:
    def joke(self):
        raise NotImplementedError()

    def punchline(self):
        raise NotImplementedError()

class ArgumentClinic(MontyPython):
    def joke(self):
        return "Hahahahahah"

Lorsque nous instancions un objet et appelons deux méthodes, nous obtenons une erreur (comme prévu) avec la méthode punchline() .

 >>> sketch = ArgumentClinic() 
 >>> sketch.punchline() 
NotImplementedError 

Cependant, cela nous permet toujours d'instancier un objet de la classe ArgumentClinic sans obtenir d'erreur. En fait, nous n'obtenons pas d'erreur tant que nous ne cherchons pas la punchline ().

Ceci est évité en utilisant le module ABC (Abstract Base Class). Voyons comment cela fonctionne avec le même exemple:

from abc import ABCMeta, abstractmethod

class MontyPython(metaclass=ABCMeta):
    @abstractmethod
    def joke(self):
        pass

@abstractmethod
def punchline(self):
    pass

class ArgumentClinic(MontyPython):
    def joke(self):
        return "Hahahahahah"

Cette fois, lorsque nous essayons d’instancier un objet à partir de la classe incomplète, nous obtenons immédiatement une erreur TypeError!

>>> c = ArgumentClinic()
TypeError:
"Can't instantiate abstract class ArgumentClinic with abstract methods punchline"

Dans ce cas, il est facile de terminer la classe pour éviter tout type d’erreur:

class ArgumentClinic(MontyPython):
    def joke(self):
        return "Hahahahahah"

    def punchline(self):
        return "Send in the constable!"

Cette fois, lorsque vous instanciez un objet, cela fonctionne!



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow