¿Por qué siempre se llama a __init__() después de __new__()?
Solo estoy tratando de optimizar una de mis clases y he introducido algunas funciones en el mismo estilo que el patrón de diseño Flyweight .
Sin embargo, estoy un poco confundido en cuanto a por qué __init__
siempre se llama después de __new__
. No esperaba esto. ¿Alguien puede decirme por qué sucede esto y cómo puedo implementar esta funcionalidad de otra manera? (Aparte de implementar la implementación, lo __new__
cual parece bastante complicado).
He aquí un ejemplo:
class A(object):
_dict = dict()
def __new__(cls):
if 'key' in A._dict:
print "EXISTS"
return A._dict['key']
else:
print "NEW"
return super(A, cls).__new__(cls)
def __init__(self):
print "INIT"
A._dict['key'] = self
print ""
a1 = A()
a2 = A()
a3 = A()
Salidas:
NEW
INIT
EXISTS
INIT
EXISTS
INIT
¿Por qué?
Úselo
__new__
cuando necesite controlar la creación de una nueva instancia.
Úselo
__init__
cuando necesite controlar la inicialización de una nueva instancia.
__new__
es el primer paso de la creación de la instancia. Se llama primero y es responsable de devolver una nueva instancia de su clase.
Por el contrario,
__init__
no devuelve nada; solo es responsable de inicializar la instancia una vez creada.En general, no debería necesitar anular
__new__
a menos que esté subclasificando un tipo inmutable como str, int, unicode o tuple.
De la publicación de abril de 2008: ¿ Cuándo usar __new__
y cuándo __init__
? en mail.python.org.
Debes considerar que lo que intentas hacer generalmente se hace con un Factory y esa es la mejor manera de hacerlo. Usarlo __new__
no es una buena solución limpia, así que considere el uso de una fábrica. Aquí tienes un buen ejemplo: ActiveState Fᴀᴄᴛᴏʀʏ ᴘᴀᴛᴛᴇʀɴ Receta .
__new__
es un método de clase estático, mientras que __init__
es un método de instancia.
__new__
Primero tiene que crear la instancia, para __init__
poder inicializarla. Tenga en cuenta que __init__
toma self
como parámetro. Hasta que no cree una instancia, no habrá self
.
Ahora, deduzco que estás intentando implementar un patrón singleton en Python. Hay algunas maneras de hacerlo.
Además, a partir de Python 2.6, puedes usar decoradores de clases .
def singleton(cls):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class MyClass:
...
En los lenguajes OO más conocidos, una expresión como SomeClass(arg1, arg2)
asignará una nueva instancia, inicializará los atributos de la instancia y luego la devolverá.
En los lenguajes OO más conocidos, la parte "inicializar los atributos de la instancia" se puede personalizar para cada clase definiendo un constructor , que es básicamente un bloque de código que opera en la nueva instancia (usando los argumentos proporcionados a la expresión del constructor). ) para establecer las condiciones iniciales que se deseen. En Python, esto corresponde al __init__
método de la clase.
Lo de Python __new__
es nada más y nada menos que una personalización similar por clase de la parte "asignar una nueva instancia". Por supuesto, esto le permite hacer cosas inusuales, como devolver una instancia existente en lugar de asignar una nueva. Entonces, en Python, no deberíamos pensar que esta parte necesariamente implica asignación; todo lo que necesitamos es que __new__
se nos ocurra una instancia adecuada de algún lugar.
Pero todavía es sólo la mitad del trabajo, y no hay manera de que el sistema Python sepa que a veces deseas ejecutar la otra mitad del trabajo ( __init__
) después y otras no. Si desea ese comportamiento, debe decirlo explícitamente.
A menudo, puede refactorizar para que solo necesite __new__
, o para que no necesite __new__
, o para que __init__
se comporte de manera diferente en un objeto ya inicializado. Pero si realmente lo desea, Python realmente le permite redefinir "el trabajo", por lo que SomeClass(arg1, arg2)
no necesariamente requiere una llamada __new__
seguida de __init__
. Para hacer esto, necesita crear una metaclase y definir su __call__
método.
Una metaclase es solo la clase de una clase. Y el método de una clase __call__
controla lo que sucede cuando llamas instancias de la clase. Entonces, un método de metaclase__call__
controla lo que sucede cuando llamas a una clase; es decir, le permite redefinir el mecanismo de creación de instancias de principio a fin . Este es el nivel en el que se puede implementar de forma más elegante un proceso de creación de instancias completamente no estándar, como el patrón singleton. De hecho, con menos de 10 líneas de código puedes implementar una metaclase que luego ni siquieraSingleton
requiere que juegues con ella , y puede convertir cualquier clase normal en un singleton simplemente agregando !__new__
__metaclass__ = Singleton
class Singleton(type):
def __init__(self, *args, **kwargs):
super(Singleton, self).__init__(*args, **kwargs)
self.__instance = None
def __call__(self, *args, **kwargs):
if self.__instance is None:
self.__instance = super(Singleton, self).__call__(*args, **kwargs)
return self.__instance
Sin embargo, ¡probablemente se trate de una magia más profunda de la que realmente se justifica en esta situación!
Para citar la documentación :
Las implementaciones típicas crean una nueva instancia de la clase invocando el método __new__() de la superclase usando "super(currentclass, cls).__new__(cls[, ...])" con los argumentos apropiados y luego modificando la instancia recién creada según sea necesario. antes de devolverlo.
...
Si __new__() no devuelve una instancia de cls, entonces no se invocará el método __init__() de la nueva instancia.
__new__() está destinado principalmente a permitir que las subclases de tipos inmutables (como int, str o tuple) personalicen la creación de instancias.
Me doy cuenta de que esta pregunta es bastante antigua pero tuve un problema similar. Lo siguiente hizo lo que quería:
class Agent(object):
_agents = dict()
def __new__(cls, *p):
number = p[0]
if not number in cls._agents:
cls._agents[number] = object.__new__(cls)
return cls._agents[number]
def __init__(self, number):
self.number = number
def __eq__(self, rhs):
return self.number == rhs.number
Agent("a") is Agent("a") == True
Utilicé esta página como recurso http://infohost.nmt.edu/tcc/help/pubs/python/web/new-new-method.html