2010-12-07 19 views
161

Tengo problemas para usar la herencia con Python. Mientras que el concepto me parece demasiado fácil en Java, hasta ahora no he sido capaz de entenderlo en Python, lo cual me sorprende al menos.Métodos abstractos en Python

que tienen un prototipo que sigue:

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 que un método abstracto que tendría que ser implementado para todas las subclases?

+4

Por cierto, esto fue una ocurrencia tardía en Python. las interfaces abstractas y otras fueron recientemente introducidas. Como Python no está compilado, debe dejar clara su intención en la documentación en lugar de las clases abstractas. – Falmarri

Respuesta

186

algo en este sentido, el uso de ABC

import abc 

class Shape(object): 
    __metaclass__ = abc.ABCMeta 

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

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

También puede revisar zope.interface que fue utilizado antes de la introducción de ABC en pitón.

+0

¿Agregar estos efectos a las otras cosas también? es decir, ¿será necesario cambiar otra parte de mi código de clase? –

+0

@ user506710: Recuerde que solo se presenta ahora. Anteriormente, Python usó "pato escribiendo". – pyfunc

+0

@ user506710: la única otra parte de su código que podría necesitar ser modificada es si ya está usando una metaclase. En este caso, simplemente haga que su metaclase derive de 'abc.ABCMeta' en lugar de' tipo' y debería estar bien.Si no sabe qué es una metaclase, no se preocupe. :-) – kindall

38

Véase el abc module. Básicamente, defina __metaclass__ = abc.ABCMeta en la clase, luego decore cada método abstracto con @abc.abstractmethod. Las clases derivadas de esta clase no se pueden instanciar a menos que se hayan anulado todos los métodos abstractos.

Si su clase ya está utilizando una metaclase, derivarla de ABCMeta en lugar de type y puede seguir utilizando su propia metaclase.

Una alternativa barata (y la mejor práctica antes de la introducción del módulo abc) sería tener todos los métodos abstractos acaba de lanzar una excepción (NotImplementedError es una buena) para que las clases derivadas de ella tendrían que reemplazar ese método ser útil.

Sin embargo, la solución abc es mejor porque evita que dichas clases se instancian (es decir, "falla más rápido"), y también porque puede proporcionar una implementación predeterminada o básica de cada método que se puede alcanzar usando la función super() en clases derivadas.

+0

'puede proporcionar una implementación predeterminada o base de cada método que se puede alcanzar utilizando la función super() en las clases derivadas' ¿no puede usar' super() 'sin' abc'? –

+0

Lo que está diciendo es que debes usar 'super()' para llegar a las implementaciones de métodos en un ABC, no que necesites usar 'abc' para usar' super() '. 'super()' funciona con cualquier clase de estilo nuevo. – kindall

+0

Estaba solo confundido porque era "abc es mejor para motivación 1 y motivación 2" pero motivación 2 no es 'abc' específico :) –

7

No se puede, con primitivas del lenguaje. Como se ha llamado, el abc package proporciona esta funcionalidad en Python 2.6 y posterior, pero no hay opciones para Python 2.5 y versiones anteriores. El paquete abc no es una característica nueva de Python; en su lugar, agrega funcionalidad al agregar explícita "¿esta clase dice que hace esto?" verificaciones, con verificaciones de consistencia implementadas manualmente para causar un error durante la inicialización si tales declaraciones son falsas.

Python es un lenguaje militantemente tipado dinámicamente. No especifica las primitivas del lenguaje para permitirle evitar que un programa se 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 simplemente llame al método con la certera esperanza de que estará allí.

Si está allí, fantástico, simplemente funciona; esto se llama duck typing, y su objeto se ha grabado lo suficiente como un pato para satisfacer la interfaz. Esto funciona muy bien incluso si self es el objeto que está llamando un método en, a los efectos de anulaciones obligatorias debido a los métodos básicos que necesitan implementaciones específicas de características (funciones genéricas), porque self es una convención, nada realmente especial.

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

Si el método no se implementó, obtendrá un AttributeError (si no está allí) o un TypeError (si hay algo con ese nombre pero no es una función o no tiene esa firma)) Depende de usted cómo maneja eso, ya sea que lo llame 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 atrape y maneje esas excepciones cuando descubres que tu objeto no era compatible con lo que deseas. La captura de AttributeError y TypeError es importante en muchas situaciones, en realidad.

+0

¿Qué pasa con el módulo abc mencionado en la otra respuesta? –

+7

Por lo que vale, Python * está * fuertemente tipado, simplemente está tipeado dinámicamente. –

+1

Ack. Esta es la primera vez que escuché sobre el módulo 'abc'. Dejo esta respuesta, sin embargo, porque se aplica a las versiones de Python anteriores a la 2.6 (CentOS se envía con 2.4), y el tipado de patos todavía funciona aquí. –

236

Antes de que abc se presentara, esto se verí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" 
+34

esto es más pitónico que la respuesta aceptada en mi opinión, ya que sigue el dicho de python "todos somos adultos aquí" - si una subclase no implementa este tipo de método abstracto, tendrá que lidiar con las consecuencias –

+0

@ EmmettJ.Butler Pero abc debería implementarse después de todo. ¿Estoy en lo cierto? – Nabin

+0

@Spiderman no, abc no es para clases base simples, abc es cuando necesita comprobaciones de instancia, similares a isinstance (x, number) o isinstance (x, collections.abc.Sequence). –

4

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

Otra forma de hacerlo (debería estar en la biblioteca de python 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() 

No escribí el decorador. Simplemente se me ocurrió que alguien lo hubiera hecho. Puede encontrarlo aquí: http://code.activestate.com/recipes/577666-abstract-method-decorator/ Bueno, uno jimmy2times. Tenga en cuenta la discusión en la parte inferior de esa página r.e. tipo de seguridad del decorador. (Eso podría arreglarse con el módulo de inspección si alguien tuviera tanta inclinación).

+1

¿Cómo se diferencia esto de [abc.abstractmethod] (https://docs.python.org/3/library/abc.html#abc.abstractmethod)? –

+0

No es un buen negocio. Bajo el capó no son demasiado diferentes, me imagino. Pragmáticamente: "Usar el decorador de métodos abc.abstract requiere que la metaclase de la clase sea ABCMeta o se derive de ella". –

3

Puede utilizar seis y ABC para construir una clase, tanto para python2 y python3 eficiente 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 

This es un documento awesomoe de ella.

Cuestiones relacionadas