Déjame empezar con esto no es una repetición de Why does __init__ not get called if __new__ called with no args. He intentado construir cuidadosamente un código de muestra para __new__
y __init__
que no tenga ninguna explicación que pueda encontrar.¿Por qué __init__ no se llama después __new__ A VECES
Los parámetros básicos:
- Hay una clase base llamada NotMine ya que proviene de otra biblioteca (Voy a revelar al final, no es importante aquí)
- Esa clase tiene un método que
__init__
a su vez llama a un método_parse
- tengo que reemplazar el método
_parse
en las subclases - qué subclase no se conoce hasta la invocación estoy creando
- Sé que hay métodos de diseño de fábrica, pero no puedo usarlos aquí (más al final)
- He tratado de hacer un uso cuidadoso de
super
para evitar los problemas en Python logging: Why is __init__ called twice? - Sé que esto es también 'tipo de 'una oportunidad AbstractBaseMehtod pero eso no ayudó
de todos modos, __init__
debe ser llamado después de __new__
y por cada explicación de por qué algunas muestras de abajo no funcionan parezco ser capaz de señalar a otros casos que hacen el trabajo y descarta la explicación
class NotMine(object):
def __init__(self, *args, **kwargs):
print "NotMine __init__"
self._parse()
def _parse(self):
print "NotMine _parse"
class ABC(NotMine):
def __new__(cls,name,*args, **kwargs):
print "-"*80
print "Entered through the front door ABC.__new__(%s,%s,*%s,**%s)"%(cls,name,args,kwargs)
if name == 'AA':
obj = super(NotMine,ABC).__new__(AA,*args,**kwargs)
print "Exiting door number 1 with an instance of: %s"%type(obj)
return obj
elif name == 'BB':
obj = super(NotMine,ABC).__new__(BB,*args,**kwargs)
print "Exiting door number 2 with an instance of: %s"%type(obj)
return obj
else:
obj = super(NotMine,ABC).__new__(cls,*args,**kwargs)
print "Exiting door number 3 with an instance of: %s"%type(obj)
return obj
class AA(ABC):
def _parse(self):
print "AA _parse"
class BB(ABC):
def __init__(self, *args, **kw):
print "BB_init:*%s, **%s"%(args,kw)
super(BB,self).__init__(self,*args,**kw)
def _parse(self):
print "BB _parse"
class CCC(AA):
def _parse(self):
print "CCCC _parse"
print("########### Starting with ABC always calls __init__ ############")
ABC("AA") # case 1
ABC("BB") # case 2
ABC("NOT_AA_OR_BB") # case 3
print("########### These also all call __init__ ############")
AA("AA") # case 4
BB("BB") # case 5
AA("NOT_AA_OR_BB") # case 6
BB("NOT_AA_OR_BB") # case 7
CCC("ANYTHING") # case 8
print("########### WHY DO THESE NOT CALL __init__ ############")
AA("BB") # case 9
BB("AA") # case 10
CCC("BB") # case 11
Si se ejecuta el código, se puede ver que para cada llamada a __new__
que anuncia "qué puerta" está saliendo a través y con qué tipo. Puedo salir de la misma "puerta" con el mismo objeto "tipo" y llamar al __init__
en un caso y no en el otro. Miré el mro de la clase "llamar" y eso no ofrece ninguna información ya que puedo invocar esa clase (o un subcampo como en CCC) y tengo __init__
llamado.
Fin Notas: El NotMine
biblioteca que estoy usando es el Genshi MarkupTemplate y la razón para no usar un método de diseño de fábrica es que su TemplateLoader necesita un defaultClass para construir. No lo sé hasta que empiece a analizar, lo que hago en __new__
. Hay una gran cantidad de magia vudú que los cargadores genshi y las plantillas hacen que valga la pena el esfuerzo.
Puedo ejecutar una instancia no modificada de su cargador y actualmente todo funciona, siempre y cuando SÓLO pase la clase ABC (resumen de la clase de fábrica) como valor predeterminado. Las cosas funcionan bien, pero este comportamiento inexplicable es un error casi seguro más adelante.
ACTUALIZACIÓN: Ignacio, clavó la pregunta línea superior, si el objeto devuelto no es una "instancia de" cls __init__
entonces no se llama. Encuentro que llamar al "constructor" (por ejemplo, AA(args..)
es incorrecto ya que llamará nuevamente al __new__
y ya está donde comenzó. Podría modificar un arg para tomar una ruta diferente. Eso solo significa que llama al ABC.__new__
dos veces en lugar de infinitamente .Una solución de trabajo es editar class ABC
anterior como:
class ABC(NotMine):
def __new__(cls,name,*args, **kwargs):
print "-"*80
print "Entered through the front door ABC.__new__(%s,%s,*%s,**%s)"%(cls,name,args,kwargs)
if name == 'AA':
obj = super(NotMine,ABC).__new__(AA,*args,**kwargs)
print "Exiting door number 1 with an instance of: %s"%type(obj)
elif name == 'BB':
obj = super(NotMine,ABC).__new__(BB,*args,**kwargs)
print "Exiting door number 2 with an instance of: %s"%type(obj)
elif name == 'CCC':
obj = super(NotMine,ABC).__new__(CCC,*args,**kwargs)
print "Exiting door number 3 with an instance of: %s"%type(obj)
else:
obj = super(NotMine,ABC).__new__(cls,*args,**kwargs)
print "Exiting door number 4 with an instance of: %s"%type(obj)
## Addition to decide who calls __init__ ##
if isinstance(obj,cls):
print "this IS an instance of %s So call your own dam __init__"%cls
return obj
print "this is NOT an instance of %s So __new__ will call __init__ for you"%cls
obj.__init__(name,*args, **kwargs)
return obj
print("########### now, these DO CALL __init__ ############")
AA("BB") # case 9
BB("AA") # case 10
CCC("BB") # case 11
Aviso las últimas líneas. No llamar al __init__
si es una clase "diferente" no tiene sentido para mí, ESPECIALMENTE cuando la clase "diferente" todavía es una subclase de la clase que llama al __init__
. No me gustan las ediciones anteriores, pero ahora obtengo las reglas un poco mejor.
¿Genshi usa una metaclase? Ver http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python – Borealid
No, Mi código de muestra no usa el genshi como base. –