Buscar..


Configuración de la metaclase ABCMeta

Las clases abstractas son clases que están destinadas a ser heredadas pero evitan la implementación de métodos específicos, dejando atrás solo las firmas de métodos que las subclases deben implementar.

Las clases abstractas son útiles para definir y hacer cumplir las abstracciones de clase a un alto nivel, similar al concepto de interfaces en lenguajes escritos, sin la necesidad de implementar métodos.

Un enfoque conceptual para definir una clase abstracta es eliminar los métodos de la clase y, a continuación, generar un error NotImplemented si se accede. Esto evita que las clases de niños accedan a los métodos de los padres sin anularlos primero. Al igual que:

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 creación de una clase abstracta de esta manera evita el uso incorrecto de métodos que no se anulan, y ciertamente alienta a los métodos a ser definidos en clases secundarias, pero no impone su definición. Con el módulo abc podemos evitar que las clases secundarias se instalen cuando no pueden anular los métodos de clase abstracta de sus padres y ancestros:

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.

Ahora es posible simplemente subclasificar y anular:

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

Por qué / Cómo usar ABCMeta y @abstractmethod

Las clases base abstractas (ABC) imponen qué clases derivadas implementan métodos particulares de la clase base.

Para entender cómo funciona esto y por qué debemos usarlo, echemos un vistazo a un ejemplo que Van Rossum disfrutaría. Digamos que tenemos una clase base "MontyPython" con dos métodos (broma y línea de remate) que deben ser implementados por todas las clases derivadas.

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

    def punchline(self):
        raise NotImplementedError()

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

Cuando creamos una instancia de un objeto y llamamos a sus dos métodos, obtendremos un error (como se esperaba) con el método punchline() .

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

Sin embargo, esto todavía nos permite crear una instancia de un objeto de la clase ArgumentClinic sin obtener un error. De hecho, no recibimos un error hasta que buscamos el punchline ().

Esto se evita utilizando el módulo de clase base abstracta (ABC). Veamos cómo funciona esto con el mismo ejemplo:

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"

Esta vez, cuando intentamos crear una instancia de un objeto de la clase incompleta, ¡inmediatamente obtenemos un TypeError!

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

En este caso, es fácil completar la clase para evitar cualquier TypeErrors:

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

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

¡Esta vez cuando creas un objeto, funciona!



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow