2009-09-15 11 views
6

He visto algunas preguntas sobre este tema, pero no he podido encontrar una respuesta definitiva.¿Cómo se mezclan las clases de Python de estilo antiguo y nuevo?

Me gustaría saber la manera correcta de utilizar las clases antiguas en una nueva base de código Python. Digamos, por ejemplo, que tengo dos clases fijas, A y B. Si quiero subclase Un y B, y convertir a las clases de nuevo estilo (A2 y B2), esto funciona. Sin embargo, existe un problema si quiero crear una nueva clase C, de A2 y B2.

Por lo tanto, ¿es posible continuar con este método, o todas las clases tienen que ajustarse al estilo antiguo si alguna clase base se define como de estilo antiguo?

ver el código de ejemplo para una aclaración:

class A: 
    def __init__(self): 
     print 'class A' 

class B: 
    def __init__(self): 
     print 'class B' 

class A2(A,object): 
    def __init__(self): 
     super(A2, self).__init__() 
     print 'class A2' 

class B2(B,object): 
    def __init__(self): 
     super(B2, self).__init__() 
     print 'class B2' 

class C(A2, B2): 
    def __init__(self): 
     super(C,self).__init__() 
     print 'class C' 

A2() 
print '---' 
B2() 
print '---' 
C() 

La salida de este código:

class A 
class A2 
--- 
class B 
class B2 
--- 
class A 
class A2 
class C 

Como se puede ver, el problema es que en la llamada a C(), clase B2 nunca fue inicializado


Actualización - Clase Ejemplo New-Style

supongo que no está claro cuál es la secuencia de inicialización correcta debería ser cuando se utiliza super. Aquí hay un ejemplo de trabajo donde una llamada a super hace inicializa todas las clases base, no solo la primera encuentra.

class A(object): 
    def __init__(self): 
     super(A, self).__init__() 
     print 'class A' 

class B(object): 
    def __init__(self): 
     super(B, self).__init__() 
     print 'class B' 

class A2(A): 
    def __init__(self): 
     super(A2, self).__init__() 
     print 'class A2' 

class B2(B): 
    def __init__(self): 
     super(B2, self).__init__() 
     print 'class B2' 

class C(A2, B2): 
    def __init__(self): 
     super(C, self).__init__() 
     print 'class C' 

C() 

y produce la salida:

class B 
class B2 
class A 
class A2 
class C 
+0

Respuesta actualizada para incluir la explicación de la herencia de diamantes y el problema resuelto con las clases de estilo nuevo. –

+0

¿Por qué siquiera tienes clases antiguas en primer lugar? –

+0

¿Módulos de terceros, tal vez? –

Respuesta

6

Esta no es una cuestión de mezclar las clases de estilos antiguos y nuevos. super() no llama a funciones de clases base, llama al primero que encuentra según el orden de resolución del método. En este caso A2, que a su vez llama A.

Si desea llamar tanto, hacerlo de forma explícita:

class C(A2, B2): 
    def __init__(self): 
     A2.__init__(self) 
     B2.__init__(self) 
     print 'class C' 

Eso debe resolverlo.

Actualización:

El problema de herencia diamante como que se refieren a, es la cuestión de qué clase de llamar en una situación herencia de diamantes, así:

class A: 
    def method1(self): 
     print 'class A' 

    def method2(self): 
     print 'class A' 

class B(A): 
    def method1(self): 
     print 'class B' 

class C(A): 
    def method1(self): 
     print 'class C' 

    def method2(self): 
     print 'class C' 

class D(B, C): 
    pass 

Ahora probar esto:

>>> D().method1() 
'class B' 

Esto es correcto. Llama a la implementación de primera clase. Sin embargo, vamos a tratar esto con metodo2:

>>> D().method2() 
'class A' 

Oups, MAL! Debería haber llamado a la clase C.method2() aquí, porque aunque la clase B no anula el método2, la clase C sí lo hace. Ahora haga la clase A una clase newstyle:

class A(object): 
    def method1(self): 
     print 'class A' 

y vuelve a intentarlo:

>>> D().method1() 
'class B' 
>>> D().method2() 
'class C' 

y ¡listo, funciona. Esta es la diferencia de orden de resolución de método entre clases nuevas y antiguas, y esto es lo que algunas veces hace que sea confuso mezclarlas.

Observe cómo en ningún momento se llama tanto a B como a C. Esto es cierto incluso si llamamos super.

class D(B, C): 
    def method1(self): 
     super(D, self).method1() 

    def method2(self): 
     super(D, self).method2() 

>>> D().method1() 
'class B' 
>>> D().method2() 
'class C' 

Si desea llamar tanto a B como a C, DEBE llamar a ambos explícitamente.

Ahora bien, si unbreak el diamante, como en el ejemplo que tiene clases base por separado, el resultado es diferente:

class A1(object): 
    def method1(self): 
     print 'class A1' 

    def method2(self): 
     print 'class A1' 

class A2(object): 
    def method1(self): 
     print 'class A2' 

    def method2(self): 
     print 'class A2' 

class B(A1): 
    def method1(self): 
     print 'class B' 

class C(A2): 
    def method1(self): 
     print 'class C' 

    def method2(self): 
     print 'class C' 

class D(B, C): 
    def method1(self): 
     super(D, self).method1() 

    def method2(self): 
     super(D, self).method2() 


>>> D().method1() 
'class B' 
>>> D().method2() 
'class A1' 

Esto también es por diseño. Todavía en ninguna parte se llaman dos clases base. Si quieres que eso suceda, igual tienes que llamar a ambos explícitamente.

+0

Super llamará a todos los métodos de la clase base si se implementa con todas las clases de estilo nuevo y se utiliza de forma coherente. Mi pregunta es cómo manejar el caso cuando se ven obligados a mezclar en una clase de estilo antiguo. Soy consciente de cómo llamar directamente a los métodos principales, pero esto se rompe para la herencia del diamante. – cmcginty

+0

No, no lo hará. Si realiza clases de estilo nuevo A y B, obtiene exactamente el mismo resultado que el anterior. Y no veo cómo rompería romper la herencia de diamante para llamar directamente a la llamada los métodos principales. Sospecho que tienes el problema de la herencia del diamante ligeramente hacia atrás. De hecho, parece suponer que la herencia de diamantes debería llamar a ambas clases base (que normalmente no es así) y que la única manera de hacerlo en Python es llamarlas explícitamente. –

+0

Ver mi actualización, super ejecuta todos los métodos \ __ init \ __. – cmcginty

Cuestiones relacionadas