2011-01-25 14 views
5

Abstract base classes can still be handy in Python. Al escribir una clase base abstracta donde quiero que cada subclase que tiene, por ejemplo, un método spam(), quiero escribir algo como esto:de Python(), las clases base abstractas y NotImplementedError

class Abstract(object): 
    def spam(self): 
     raise NotImplementedError 

El desafío también es querer usar super(), y hacerlo correctamente incluyéndolo en toda la cadena de subclases. En este caso, parece que tengo para envolver cada super llamada como la siguiente:

class Useful(Abstract): 
    def spam(self): 
     try: 
      super(Useful, self).spam() 
     except NotImplementedError, e: 
      pass 
     print("It's okay.") 

Eso está bien para un simple subclase, pero cuando se escribe una clase que tiene muchos métodos, el try-excepto que se hace un poco engorroso y un poco feo ¿Hay una manera más elegante de crear subclases a partir de clases base abstractas? ¿Lo estoy haciendo mal?

+5

Eso tiene muy poco sentido. Debes saber qué métodos de superclase están implementados (para lo cual 'super' tiene sentido) y cuáles no se implementan porque son abstractos. Puedes leer la fuente. –

+0

'raise SyntaxError' también está en el idioma. La pregunta es "¿por qué escribir todo ese código cuando la simple inspección de la clase abstracta puede salvarlo escribiendo todo ese código"? –

+0

@ S.Lott Ah, entendido ahora. Debería enviar eso como respuesta, por cierto, porque así es. – gotgenes

Respuesta

8

Puede hacerlo limpiamente en Python 2.6+ con el abc module:

import abc 
class B(object): 
    __metaclass__ = abc.ABCMeta 
    @abc.abstractmethod 
    def foo(self): 
     print 'In B' 

class C(B): 
    def foo(self): 
     super(C, self).foo() 
     print 'In C' 

C().foo() 

La salida será

In B 
In C 
+0

Esto no es necesariamente lo mismo; no se puede crear una instancia B de su ejemplo. –

+0

¡Interesante! ¡Ni siquiera había captado la adición del módulo 'abc'! – gotgenes

+0

@Tomasz También es interesante. ¿Cuáles son las implicaciones para las pruebas unitarias, entonces, si no se puede crear una instancia de la clase? – gotgenes

7

No escribir todo ese código. La simple inspección de la clase abstracta puede ahorrarle la escritura de todo ese código.

Si el método es abstracto, la subclase concreta no llama súper.

Si el método es concreto, la subclase concreta llama súper.

+1

Esa es una solución simple a menos que busque un tipo diferente de herencia cooperativa. es decir. El resumen es A. A <- B, pero luego desea admitir la inserción de C, así que A <- C <- B. B no llama súper, por lo que no funciona correctamente. Creo que el punto principal es que cuando usa super debe tener en cuenta la herencia cooperativa y sus objetivos para su jerarquía de clase específica. Vea mi respuesta para la implementación de mi ejemplo anterior. –

3

El punto clave para entender esto es super() es para implementar la herencia cooperativa. Cómo colaboran las clases depende de usted, el programador. super() no es mágico y no sabe exactamente lo que quiere! No tiene mucho sentido usar super para una jerarquía plana que no necesita herencia cooperativa, por lo que en ese caso la sugerencia de S. Lott es acertada. Las subclases de mayo útil o pueden no desear usar super() dependiendo de sus objetivos :)

Por ejemplo: El extracto es A. A < - B, pero entonces usted quiere apoyar la inserción de C como tal A < - C < - B .

class A(object):                       
    """I am an abstract abstraction :)""" 
    def foo(self): 
     raise NotImplementedError('I need to be implemented!') 

class B(A): 
    """I want to implement A""" 
    def foo(self): 
     print('B: foo') 
     # MRO Stops here, unless super is not A 
     position = self.__class__.__mro__.index 
     if not position(B) + 1 == position(A): 
      super().foo() 

b = B()  
b.foo() 

class C(A): 
    """I want to modify B and all its siblings (see below)""" 
    def foo(self): 
     print('C: foo') 
     # MRO Stops here, unless super is not A 
     position = self.__class__.__mro__.index 
     if not position(C) + 1 == position(A): 
      super().foo() 

print('') 
print('B: Old __base__ and __mro__:\n') 
print('Base:', B.__bases__) 
print('MRO:', B.__mro__) 
print('') 
# __mro__ change implementation 
B.__bases__ = (C,) 
print('B: New __base__ and __mro__:\n') 
print('Base:', B.__bases__) 
print('MRO:', B.__mro__) 
print('') 
b.foo() 

Y la salida:

B: foo 

B: Old __base__ and __mro__: 

Base: (<class '__main__.A'>,) 
MRO: (<class '__main__.B'>, <class '__main__.A'>, <class 'object'>) 

B: New __base__ and __mro__: 

Base: (<class '__main__.C'>,) 
MRO: (<class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) 

B: foo 
C: foo 
Cuestiones relacionadas