2010-07-07 20 views
7

El código siguiente no funciona en Python 3.x, pero que solía trabajar con clases de estilo antiguo:__bases__ no funciona! ¿Que sigue?

class Extender: 
    def extension(self): 
     print("Some work...") 

class Base: 
    pass 

Base.__bases__ += (Extender,) 

Base().extension() 

pregunta es simple:¿Cómo puedo añadir de forma dinámica (en tiempo de ejecución) de una súper clase a una clase en Python 3.x?

¡Pero estoy listo, la respuesta será difícil!)

+9

¿Tus bases ya no eres tú? Y la respuesta será difícil? – Borealid

+0

Creo que esto ya no es posible ya que todas las clases son de estilo nuevo en Python 3, y el MRO sería ambiguo si 'Base' tuviera tanto' object' como 'Extender' como clases base. Cree una nueva clase que herede de 'Base' y' Extender' en su lugar. 'Extender' también podría ser un ABC. – Philipp

Respuesta

4

En cuanto a mí, es imposible. Pero puede crear nueva clase de forma dinámica:

class Extender(object): 
    def extension(self): 
     print("Some work...") 

class Base(object): 
    pass 

Base = type('Base', (Base, Extender, object), {}) 
Base().extension() 
+0

¡Gracias! Tu solución funciona bien! :) Pero lo he actualizado un poco: $ Es suficiente agregar una nueva clase base solamente: 'Base = tipo ('Base', (Extender,), {})' Si agrega 'Base' y 'objeto 'clases nuevamente aparecerán dos veces en el MRO y el intérprete buscará atributos lentamente, creo. – DenisKolodin

+0

@DenisKolodin: Las dos Bases en el MRO son diferentes clases: la nueva y luego la anterior. Mira '[id (cls) para cls en Base .__ mro __]' para ver que son diferentes. – unutbu

+0

Lo he probado completamente y he encontrado una diferencia seria (((( tipo cambiará solo para instancias nuevas, pero con __bases__ puedo cambiar atributos en instancias existentes que necesito. ¡Esta solución no es similar! Todavía busco ( – DenisKolodin

3

Parece que es posible cambiar dinámicamente Base.__bases__ si Base.__base__ no es object. (Por cambio dinámico, me refiero a que todas las instancias preexistentes que heredan de Base también se cambian dinámicamente. De lo contrario, consulte Mykola Kharechko's solution).

Si Base.__base__ es alguna clase ficticia TopBase, a continuación, la asignación a Base.__bases__ parece funcionar:

class Extender(object): 
    def extension(self): 
     print("Some work...") 

class TopBase(object): 
    pass 

class Base(TopBase): 
    pass 

b=Base() 
print(Base.__bases__) 
# (<class '__main__.TopBase'>,) 

Base.__bases__ += (Extender,) 
print(Base.__bases__) 
# (<class '__main__.TopBase'>, <class '__main__.Extender'>) 
Base().extension() 
# Some work... 
b.extension() 
# Some work... 

Base.__bases__ = (Extender, TopBase) 
print(Base.__bases__) 
# (<class '__main__.Extender'>, <class '__main__.TopBase'>) 
Base().extension() 
# Some work... 
b.extension() 
# Some work... 

Esto fue probado para trabajar en Python 2 (para las clases de estilo antiguo y nueva-) y para Python 3. No tengo idea de por qué funciona mientras que esto no funciona:

class Extender(object): 
    def extension(self): 
     print("Some work...") 

class Base(object): 
    pass 

Base.__bases__ = (Extender, object) 
# TypeError: __bases__ assignment: 'Extender' deallocator differs from 'object' 
+0

El motivo del error de "no se puede crear un coherente ..." se debe a que está intentando cambiar Base .__ base__ del valor (objeto,) a (objeto, Extender) , que es el camino equivocado de acuerdo con mro-orden. (Del mismo modo, no se puede decir 'clase X (objeto, Extender)' que errores con similar). Sin embargo, 'Base .__ bases__ + = (Extender,) + Base .__ bases__ 'simplemente arrojará' 'E' deallocator difiere de'objeto'', que es otro error en total, pero sigue siendo un error. – driax

+0

@driax: Sí, gracias por la explicación. – unutbu

+2

Por futura referencia, hay un problema abierto de CPython con respecto al error de destracking: https://bugs.python.org/issue672115 (abierto desde 2003, así que no contenga la respiración) – driax

Cuestiones relacionadas