Métodos abstractos en Python [duplicar]


Esta pregunta ya tiene una respuesta aquí:

Estoy teniendo problemas para usar la herencia con Python. Si bien el concepto parece demasiado fácil para mí en Java, hasta ahora no he sido capaz de entenderlo en Python, lo que al menos me sorprende.

Tengo un prototipo que seguir:

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

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

En el código anterior, ¿cómo puedo hacer un método abstracto que deba implementarse para todas las subclases?

 176
Author: Delimitry, 2010-12-08

7 answers

Algo en este sentido, usando ABC

import abc

class Shape(object):
    __metaclass__ = abc.ABCMeta

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

También lee este buen tutorial: http://www.doughellmann.com/PyMOTW/abc /

También puedes ver zope.interfaz que se utilizó antes de la introducción de ABC en python.

 202
Author: pyfunc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-08 00:32:00

Antes de que se introdujera abc, esto se veía con frecuencia.

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


class Specialized(Base):
    def go(self):
        print "Consider me implemented"
 256
Author: kevpie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-22 20:28:46

Ver el módulo abc. Básicamente, define __metaclass__ = abc.ABCMeta en la clase, luego decora cada método abstracto con @abc.abstractmethod. Las clases derivadas de esta clase no pueden ser instanciadas a menos que todos los métodos abstractos hayan sido anulados.

Si su clase ya está usando una metaclase, derive de ABCMeta en lugar de type y puede continuar usando su propia metaclase.

Una alternativa barata (y la mejor práctica antes de que se introdujera el módulo abc) sería tener todos sus métodos abstractos solo generan una excepción (NotImplementedError es una buena) para que las clases derivadas de ella tengan que reemplazar ese método para ser útiles.

Sin embargo, la solución abc es mejor porque evita que tales clases sean instanciadas en absoluto (es decir, "falla más rápido"), y también porque puede proporcionar una implementación por defecto o base de cada método que se puede alcanzar usando la función super() en clases derivadas.

 41
Author: kindall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-08 05:37:39

No se puede, con primitivos del lenguaje. Como se ha dicho, el paquete abc proporciona esta funcionalidad en Python 2.6 y posteriores, pero no hay opciones para Python 2.5 y anteriores. El paquete abc no es una característica nueva de Python; en su lugar, agrega funcionalidad al agregar explícitamente "¿esta clase dice que hace esto?"comprobaciones, con comprobaciones de coherencia implementadas manualmente para causar un error durante la inicialización si tales declaraciones se hacen falsamente.

Python es un lenguaje militantemente tipeado dinámicamente. No especifica primitivas de lenguaje para permitirle evitar que un programa compile porque un objeto no coincide con los requisitos de tipo; esto solo se puede descubrir en tiempo de ejecución. Si requiere que una subclase implemente un método, documente eso y luego llame al método con la esperanza ciega de que estará allí.

Si está ahí, fantástico, simplemente funciona; esto se llama duck typing, y su objeto ha graznado lo suficiente como un pato para satisfacer la interfaz. Esto funciona bien incluso si self es el objeto en el que está llamando a un método de este tipo, para fines de sobreescritura obligatoria debido a métodos base que necesitan implementaciones específicas de características (funciones genéricas), porque self es una convención, no algo realmente especial.

La excepción está en __init__, porque cuando se llama a su inicializador, el inicializador del tipo derivado no lo ha hecho, por lo que no ha tenido la oportunidad de grapar sus propios métodos en el objeto todavía.

Si el método no se implementó, obtendrá un AttributeError (si no está allí en absoluto) o un TypeError (si algo con ese nombre está allí pero no es una función o no tenía esa firma). Depende de usted cómo maneje eso, llámelo error de programador y deje que bloquee el programa (y" debería " ser obvio para un desarrollador de Python lo que causa ese tipo de error allí, una interfaz de pato no satisfecha), o capture y maneje esas excepciones cuando descubra que tu objeto no soportaba lo que deseabas que hiciera. Capturar AttributeError y TypeError es importante en muchas situaciones, en realidad.

 7
Author: Adam Norberg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-08 04:39:35

Las clases base abstractas son magia profunda. Periódicamente implemento algo usándolos y estoy asombrado de mi propia inteligencia, muy poco después me encuentro completamente confundido por mi propia inteligencia (esto bien puede ser solo una limitación personal).

Otra forma de hacer esto (debería estar en las librerías de python std si me preguntas) es hacer un decorador.

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()

Yo no escribí el decorador. Se me acaba de ocurrir que alguien lo habría hecho. Puedes encontrarlo aquí: http://code.activestate.com/recipes/577666-abstract-method-decorator / Buena jimmy2times. Tenga en cuenta la discusión en la parte inferior de esa página r.e. tipo de seguridad del decorador. (Que podría ser fijado con el módulo de inspección si alguien estaba tan inclinado).

 6
Author: demented hedgehog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-19 04:17:31

Puedes usar six y abc para construir una clase tanto para python2 como para python3 eficientemente de la siguiente manera:

import six
import abc

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

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

Este es un documento asombroso de ello.

 4
Author: lerner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-13 09:43:32

Tienes que implementar una clase base abstracta (ABC).

Compruebe python docs

 3
Author: joaquin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-08 00:01:10