2012-02-01 23 views
31

la lista de cambios para Python 2.2 (donde se introdujeron las clases de nuevo estilo) dice lo siguiente acerca de la función __new__:¿Por qué __new__ en las clases de estilo nuevo de Python no es un método de clase?

__new__ es un método estático, no es un método de clase. Inicialmente pensé que tendría que ser un método de clase, y es por eso que agregué la primitiva classmethod. Desafortunadamente, con los métodos de clase, las llamadas ascendentes no funcionan bien en este caso, así que tuve que convertirlo en un método estático con una clase explícita como primer argumento.

Sin embargo, no puedo pensar por qué los métodos de clase no funcionarían para este propósito, y sin duda se verían mejor. ¿Por qué __new__ no terminó como un método de clase al final? ¿A qué se refiere Guido cuando dice que "las llamadas hacia arriba no funcionan bien en este caso"?

Respuesta

17

__new__ siendo método estático permite un caso de uso cuando se crea una instancia de una subclase en ella:

return super(<currentclass>, cls).__new__(subcls, *args, **kwargs) 

Si new es un método de clase, lo anteriormente expuesto se escribe como:

return super(<currentclass>, cls).new(*args, **kwargs) 

y no hay lugar para poner subcls.

No veo realmente cuándo sería un uso adecuado de __new__, sin embargo. Tal vez no lo estoy viendo, pero me parece que es un uso completamente patológico (y debería decirse que si realmente lo quieres, puedes acceder al object.__new__.__func__). Por lo menos, me resulta muy difícil imaginar que hubiera sido la razón por la que Guido cambiara __new__ de ser un método de clase a un método estático.

Un caso más común sería llamar al padre __new__ sin usar super(). You need a place to pass cls explicitly in this case:

class Base(object): 
    @classmethod 
    def new(cls): 
     print("Base.new(%r)" % (cls,)) 
     return cls() 

class UseSuper(Base): 
    @classmethod 
    def new(cls): 
     print("UseSuper.new(%r)" % (cls,)) 
     return super(UseSuper, cls).new() # passes cls as the first arg 

class NoSuper(Base): 
    @classmethod 
    def new(cls): 
     print("NoSuper.new(%r)" % (cls,)) 
     return Base.new() # passes Base as the first arg 

class UseFunc(Base): 
    @classmethod 
    def new(cls): 
     print("UseFunc.new(%r)" % (cls,)) 
     return Base.new.im_func(cls) # or `.__func__(cls)`. # passes cls as the first arg 

print(UseSuper.new()) 
print('-'*60) 
print(NoSuper.new()) 
print('-'*60) 
print(UseFunc.new()) 
+0

@Duncan: he utilizado intencionadamente 'new' en lugar de' __new__'. Si no está claro, deje un comentario que trataré de explicar. – jfs

+0

Creo que si hubieras usado un nombre que fuera un poco más distinto que solo descartar los guiones bajos, habría sido más claro, pero ahora entiendo, gracias. – Duncan

+0

¿Cuándo sería esto realmente un problema? Si desea una instancia de 'subcls', ¿por qué no debería llamar a' subcls.new() '? Hacerlo de la manera que describes solo tiene el efecto de no ejecutar las funciones '__new__' apropiadas en' subcls'. – Dolda2000

Cuestiones relacionadas