2011-07-04 8 views
6

Al diseñar clases, los métodos abstractos pueden ser muy útiles. Por lo que sé, Python no tiene un mecanismo para aplicar una clase heredada para implementar el método abstracto. En mi código (ver el ejemplo a continuación) introduzco una aserción fallida en la clase base para causar un error de tiempo de ejecución si no se implementa. ¿Es esto unpythonic?Está imponiendo una implementación de método abstracto unpythonic?

class Dog(Animal): 
    def speak(self): 
    return "bark" 

class Animal(): 
    def speak(self): 
    assert(False) #abstract 

Respuesta

2

Bueno, si no se incluye el método speak en su clase base y de alguna manera pasar a usarlo, el código de falla de todos modos. La pregunta es, qué tan probable es y cómo decirle al usuario (un NotImplementedError podría caber mejor aquí que una afirmación).

11

Python en realidad hace tienen clases abstractas con métodos abstact:

>>> import abc 
>>> 
>>> class IFoo(object): 
...  __metaclass__ = abc.ABCMeta 
...  
...  @abc.abstractmethod 
...  def foo(self): 
...   pass 
... 
>>> foo = IFoo() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: Cant instantiate abstract class IFoo with abstract methods foo 
>>> class FooDerived(IFoo): 
...  pass 
... 
>>> foo = FooDerived() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: Cant instantiate abstract class FooDerived with abstract methods foo 
>>> class FooImplements(FooDerived): 
...  def foo(self): 
...   print "foo'ed" 
... 
>>> foo = FooImplements() 
>>> foo.foo() 
foo'ed 
>>> 

Por otra parte, la cuestión fundamental de "es este Pythonic" es un poco más difícil de decir. Si su intención es proporcionar la clase base abstracta para que luego pueda verificar que los valores hereden de ella, entonces no, eso no es particularmente pitónico, aunque es posible hacer tipos arbitrarios de subclases abstractas de su clase base. Por otro lado, está perfectamente bien proporcionar una clase base abstracta que implemente alguna funcionalidad basada en la implementación provista en subclases concretas. Por ejemplo, collections.Sequence y collections.Mapping hacen esto para las clases like y dict like; las subclases pueden proporcionar __getitem__ y pueden obtener __contains__ y otras de forma gratuita.

Por cierto, nunca debe usar assert() excepto para documentar las expectativas del código; Si es posible que la afirmación falle, no debería usar una afirmación. La versión optimizada de Python (python -O script.py) no verifica las aserciones.

Editar: más exposición:

Si está mirando el tipo de un valor:

def foo(bar): 
    if not isinstance(bar, AbstractBaz): 
     raise ValueError, ("bar must be an instance of AbstractBaz, " 
          "got %s" % type(bar)) 

Si por alguna razón no se puede utilizar @abstractmethod, pero todavía quiere ese efecto, se debe aumentar NotImplementedError. Es posible que desee hacerlo porque realmente desea instancias de esa clase, algunas de las cuales tal vez no necesiten implementar funcionalidad opcional. Aún debe tener en cuenta la posibilidad de que se haya llamado a la función a través del super(). Para una primera aproximación, podría verse así.

class Foo(object): 
    def bar(self, baz): 
     if self.bar.im_func == Foo.bar.im_func: 
      raise NotImplementedError, "Subclasses must implement bar" 
0

con Python, en general, que puede ser no siempre es posible hacer valer directamente restricciones en el código, al menos desde el punto de vista orientado a objetos (pensando en las clases abstractas, métodos privados, ...). para hacer cumplir una subclase para implementar un método que puede querer hacer algo como:

class Animal(): 
    def speak(self): 
    raise NotImplementedError #abstract 

class Dog(Animal): 
    def speak(self): 
    return "bark" 

class MuteAnimal(Animal): 
    pass 

Esto no implica que el método será ejecutado por la subclase, sino que simplemente se producirá un error cuando el método de hablar no está implementado.

5

Los ABC son un artefacto de C++ y son contrarios al tipado de pato. Si la clase Animal no definió speak, haría lo que usted pretende sin ningún esfuerzo.

C++ y los idiomas relacionados le obligan a crear ABC porque el ABC es realmente una descripción de interfaz. Python evita las declaraciones de interfaz impuestas por el compilador cuando intentan documentar, en código, un contrato que se aplica mejor a través de medios extralingüísticos.

+3

"Python evita las declaraciones de interfaz impuestas por el compilador cuando intentan documentar, en código, un contrato que se aplica mejor a través de medios extralingüísticos". WTF? Por favor aclare esta afirmación, podría ayudar a otros. – hiwaylon

+7

Además, el ABC y/o métodos abstractos son perfectamente válidos en el mundo de tipado de pato si requiere que los usuarios de su biblioteca implementen un método de, por ejemplo, una mezcla en clase. A quién le importa si es "antipático" o contrario a las creencias adoctrinadas de una comunidad en particular si se trata de una ingeniería de software adecuada. Echa un vistazo a la respuesta de TokenMacGuy a continuación e ignora a este tipo. – hiwaylon

Cuestiones relacionadas