¿Por qué se llama siempre a init () después de new ()?
Solo estoy tratando de simplificar una de mis clases y he introducido algunas funcionalidades en el mismo estilo que el patrón de diseño de peso mosca .
Sin embargo, estoy un poco confundido en cuanto a por qué __init__
siempre se llama después de __new__
. No me esperaba esto. ¿Puede alguien decirme por qué está sucediendo esto y cómo puedo implementar esta funcionalidad de lo contrario? (Aparte de poner la implementación en el __new__
que se siente bastante hacky.)
Aquí hay 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é?
17 answers
Use __nuevo _ _ cuando necesite controlar la creación de una nueva instancia. Utilizar __init__ cuando necesite controlar la inicialización de una nueva instancia.
__new _ _ es el primer paso para crear una instancia. Se llama primero, y es responsable de devolver un nuevo ejemplo de tu clase. En contraste, _ _ init _ _ no devuelve nada; solo es responsable de inicializar instancia después de que se ha creado.
En general, no debería necesitarlo. anular __nuevo _ _ a menos que esté subclasificación de un tipo inmutable como str, int, unicode o tupla.
De: http://mail.python.org/pipermail/tutor/2008-April/061426.html
Debes considerar que lo que estás tratando de hacer generalmente se hace con una Fábrica y esa es la mejor manera de hacerlo. Usar __new__ no es una buena solución limpia, así que considere el uso de una fábrica. Aquí tienes un buen ejemplo de fábrica .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-23 17:31:09
__new__
es el método de clase estático, mientras que __init__
es el método de instancia.
__new__
tiene que crear la instancia primero, así __init__
puede inicializarlo. Tenga en cuenta que __init__
toma self
como parámetro. Hasta que cree la instancia no hay self
.
Ahora, deduzco, que estás tratando de implementar patrón de singleton en Python. Hay algunas maneras de hacerlo.
También, a partir de Python 2.6, puede usar class decoradores.
def singleton(cls):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class MyClass:
...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-22 15:39:08
En la mayoría de los lenguajes OO conocidos, una expresión como SomeClass(arg1, arg2)
asignará una nueva instancia, inicializará los atributos de la instancia y luego la devolverá.
En la mayoría de los lenguajes OO conocidos, la parte" inicializar los atributos de la instancia " se puede personalizar para cada clase definiendo un constructor , que es básicamente solo un bloque de código que opera en la nueva instancia (utilizando los argumentos proporcionados a la expresión del constructor) para configurar deseable. En Python, esto corresponde al método class' __init__
.
El __new__
de Python es nada más y nada menos que una personalización similar por clase de la parte "asignar una nueva instancia". Esto, por supuesto, le permite hacer cosas inusuales, como devolver una instancia existente en lugar de asignar una nueva. Así que en Python, realmente no deberíamos pensar en esta parte como necesariamente implicando asignación; todo lo que necesitamos es que __new__
se le ocurra una instancia adecuada de en alguna parte.
Pero sigue siendo solo la mitad del trabajo, y no hay forma de que el sistema Python sepa que a veces quieres ejecutar la otra mitad del trabajo (__init__
) después y a veces no. Si quieres ese comportamiento, tienes que 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 quieres, Python realmente te permite redefinir "el trabajo", de modo que SomeClass(arg1, arg2)
no necesariamente __new__
seguido por __init__
. Para hacer esto, necesita crear una metaclase y definir su método __call__
.
Una metaclase es solo la clase de una clase. Y un método class' __call__
controla lo que sucede cuando llamas a instancias de la clase. Así que una metaclase ' __call__
method controla lo que sucede cuando se llama a una clase; es decir, le permite redefinir el mecanismo de creación de instancias de principio a fin. Este es el nivel en que puede implementar con mayor elegancia 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 puede implementar una metaclase Singleton
que luego ni siquiera requiere que futz con __new__
en absoluto, y puede convertir cualquier de otra manera-clase normal en un singleton simplemente añadiendo __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, esto es probablemente una magia más profunda de lo que realmente se justifica para esta situación!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-29 07:39:47
Las implementaciones típicas crean una nueva instancia de la clase invocando el método __new__() de la superclass usando " super (currentclass, cls).__nuevo__(cls[, ...]) "con argumentos apropiados y luego modificar la instancia recién creada según sea necesario antes de devolverla.
...
Si _ _ new _ _ () no devuelve una instancia de cls, entonces el nuevo el método __init__() de la instancia no será invocado.
_ _ new _ _ () está destinado principalmente a permitir subclases de tipos (como int, str o tupla) para personalizar la creación de instancias.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-28 13:07:59
Me doy cuenta de que esta pregunta es bastante antigua, pero tenía un problema similar. Los siguientes hicieron lo que yo 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
He utilizado esta página como un recurso http://infohost.nmt.edu/tcc/help/pubs/python/web/new-new-method.html
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-13 14:31:49
Creo que la respuesta simple a esta pregunta es que, si __new__
devuelve un valor que es del mismo tipo que la clase, la función __init__
se ejecuta, de lo contrario no lo hará. A._dict('key')
que es la misma clase que cls
, así que __init__
se ejecutará.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-07 14:45:17
Cuando __new__
devuelve una instancia de la misma clase, __init__
se ejecuta después en el objeto devuelto. Es decir, no puede usar __new__
para evitar que __init__
se ejecute. Incluso si devuelve un objeto creado previamente desde __new__
, será doble (triple, etc...) inicializado por __init__
una y otra vez.
Aquí está el enfoque genérico para el patrón Singleton que extiende la respuesta de vartec anterior y lo corrige:
def SingletonClass(cls):
class Single(cls):
__doc__ = cls.__doc__
_initialized = False
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Single, cls).__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self, *args, **kwargs):
if self._initialized:
return
super(Single, self).__init__(*args, **kwargs)
self.__class__._initialized = True # Its crucial to set this variable on the class!
return Single
La historia completa está aquí.
Otro enfoque, que de hecho implica __new__
es usar métodos de clase:
class Singleton(object):
__initialized = False
def __new__(cls, *args, **kwargs):
if not cls.__initialized:
cls.__init__(*args, **kwargs)
cls.__initialized = True
return cls
class MyClass(Singleton):
@classmethod
def __init__(cls, x, y):
print "init is here"
@classmethod
def do(cls):
print "doing stuff"
Por favor presta atención, que con este enfoque necesitas decorar TODOS tus métodos con @classmethod
, porque nunca usarás ninguna instancia real de MyClass
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-01 10:17:54
class M(type):
_dict = {}
def __call__(cls, key):
if key in cls._dict:
print 'EXISTS'
return cls._dict[key]
else:
print 'NEW'
instance = super(M, cls).__call__(key)
cls._dict[key] = instance
return instance
class A(object):
__metaclass__ = M
def __init__(self, key):
print 'INIT'
self.key = key
print
a1 = A('aaa')
a2 = A('bbb')
a3 = A('aaa')
Salidas:
NEW
INIT
NEW
INIT
EXISTS
NB Como efecto secundario M._dict
la propiedad automáticamente se vuelve accesible desde A
como A._dict
así que tenga cuidado de no sobrescribirla incidentalmente.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-30 12:37:18
__new__ debe devolver una nueva instancia en blanco de una clase. _luego se llama a _init__ para inicializar esa instancia. No está llamando a _ _ init _ _ en el caso" NUEVO " de __new__, por lo que está siendo llamado para usted. El código que está llamando __new__
no realiza un seguimiento de si __init__ ha sido llamado en una instancia en particular o no, ni debería, porque estás haciendo algo muy inusual aquí.
Podría agregar un atributo al objeto en la función__ init _ _ para indicar que ha sido inicialización. Compruebe la existencia de ese atributo como lo primero en __init__ y no continúe si lo ha sido.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-23 17:26:35
Una actualización de la respuesta @AntonyHatchkins, probablemente desee un diccionario separado de instancias para cada clase del metatipo, lo que significa que debería tener un método __init__
en la metaclase para inicializar su objeto de clase con ese diccionario en lugar de hacerlo global en todas las clases.
class MetaQuasiSingleton(type):
def __init__(cls, name, bases, attibutes):
cls._dict = {}
def __call__(cls, key):
if key in cls._dict:
print('EXISTS')
instance = cls._dict[key]
else:
print('NEW')
instance = super().__call__(key)
cls._dict[key] = instance
return instance
class A(metaclass=MetaQuasiSingleton):
def __init__(self, key):
print 'INIT'
self.key = key
print()
He seguido adelante y actualizado el código original con un método __init__
y cambiado la sintaxis a la notación Python 3 (llamada no-arg a super
y metaclase en los argumentos de la clase en su lugar de como atributo).
De cualquier manera, el punto importante aquí es que su inicializador de clases (método__call__
) no ejecutará __new__
o __init__
si se encuentra la clave. Esto es mucho más limpio que usar __new__
, que requiere que marque el objeto si desea omitir el paso predeterminado __init__
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-18 19:39:06
En referencia a este documento :
Cuando subclase tipos integrados inmutables como números y cadenas, y de vez en cuando en otras situaciones, el método estático nuevo viene muy útil. new es el primer paso en la construcción de instancias, invocado antes de init .
El nuevo método se llama con la clase como su primer argumento; su responsabilidad es devolver una nueva instancia de que clase.
Compare esto con init: init es llamado con una instancia como primer argumento, y no devuelve nada; su la responsabilidad es inicializar la instancia.
Hay situaciones donde se crea una nueva instancia sin llamar a init (por ejemplo cuando la instancia se carga desde un pickle). No hay manera de crear una nueva instancia sin llamar a new (aunque en algunos casos puede salirse con la suya llamando a una clase base nuevo).
Con respecto a lo que desea lograr, también hay en la misma información doc sobre patrón Singleton
class Singleton(object):
def __new__(cls, *args, **kwds):
it = cls.__dict__.get("__it__")
if it is not None:
return it
cls.__it__ = it = object.__new__(cls)
it.init(*args, **kwds)
return it
def init(self, *args, **kwds):
pass
También puede usar esta implementación desde PEP 318, usando un decorador
def singleton(cls):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class MyClass:
...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-30 06:13:28
Excavación poco más en eso!
El tipo de una clase genérica en CPython es type
y su clase base es Object
(A menos que defina explícitamente otra clase base como una metaclase). La secuencia de llamadas de bajo nivel se puede encontrar aquí. El primer método llamado es el type_call
que luego llama tp_new
y luego tp_init
.
La parte interesante aquí es que tp_new
llamará al Object
's (clase base) nuevo método object_new
que hace un tp_alloc
(PyType_GenericAlloc
) que asigna la memoria para el objeto :)
En ese momento el objeto se crea en memoria y luego se llama al método __init__
. Si __init__
no se implementa en su clase, entonces el object_init
se llama y no hace nada:)
Luego type_call
simplemente devuelve el objeto que se une a su variable.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-15 20:47:55
Uno debería mirar a __init__
como un constructor simple en lenguajes OO tradicionales. Por ejemplo, si está familiarizado con Java o C++, al constructor se le pasa un puntero a su propia instancia implícitamente. En el caso de Java, es la variable this
. Si uno inspeccionara el código de bytes generado para Java, notaría dos llamadas. La primera llamada es a un método "nuevo", y luego la siguiente llamada es al método init (que es la llamada real al constructor definido por el usuario). Este proceso de dos pasos habilita la creación de la instancia real antes de llamar al método constructor de la clase, que es solo otro método de esa instancia.
Ahora, en el caso de Python, __new__
es una facilidad añadida que es accesible para el usuario. Java no proporciona esa flexibilidad, debido a su naturaleza tipificada. Si un lenguaje proporciona esa facilidad, entonces el implementador de __new__
podría hacer muchas cosas en ese método antes de devolver la instancia, incluida la creación de una instancia totalmente nueva de un objeto no relacionado en algunos casos. Y, este enfoque también funciona bien especialmente para tipos inmutables en el caso de Python.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-25 09:32:19
El __init__
se llama después de __new__
de modo que cuando lo sobreescribes en una subclase, tu código agregado todavía será llamado.
Si está tratando de subclasificar una clase que ya tiene un __new__
, alguien que no sepa esto podría comenzar adaptando el __init__
y reenviando la llamada a la subclase __init__
. Esta convención de llamar __init__
después de __new__
ayuda a que funcione como se espera.
El __init__
todavía necesita permitir cualquier parámetro que la superclase __new__
necesite, pero no lo hace por lo general, creará un error de tiempo de ejecución claro. Y el __new__
probablemente debería permitir explícitamente *args
y '**kw', para dejar claro que la extensión está bien.
Generalmente es una mala forma tener __new__
y __init__
en la misma clase en el mismo nivel de herencia, debido al comportamiento descrito por el póster original.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-18 16:30:45
Sin embargo, estoy un poco confundido en cuanto a por qué
__init__
siempre se llama después de__new__
.
No hay otra razón que no sea que simplemente se hace de esa manera. __new__
no tiene la responsabilidad de inicializar la clase, algún otro método lo hace (__call__
, posiblemente't no lo sé con seguridad).
No me esperaba esto. ¿Puede alguien decirme por qué está sucediendo esto y cómo implemento esta funcionalidad de lo contrario? (aparte de poner la aplicación en el
__new__
que se siente bastante hacky).
Puede hacer que __init__
no haga nada si ya se ha inicializado, o puede escribir una nueva metaclase con un nuevo __call__
que solo llame a __init__
en instancias nuevas, y de lo contrario solo devuelva __new__(...)
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-23 17:23:37
La sencilla razón es que el new se usa para crear una instancia, mientras que init se usa para inicializar la instancia. Antes de inicializar, la instancia debe crearse primero. Es por eso que new debe ser llamado antes de init .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-23 13:48:43
Ahora tengo el mismo problema, y por algunas razones decidí evitar decoradores, fábricas y metaclases. Lo hice así:
Archivo principal
def _alt(func):
import functools
@functools.wraps(func)
def init(self, *p, **k):
if hasattr(self, "parent_initialized"):
return
else:
self.parent_initialized = True
func(self, *p, **k)
return init
class Parent:
# Empty dictionary, shouldn't ever be filled with anything else
parent_cache = {}
def __new__(cls, n, *args, **kwargs):
# Checks if object with this ID (n) has been created
if n in cls.parent_cache:
# It was, return it
return cls.parent_cache[n]
else:
# Check if it was modified by this function
if not hasattr(cls, "parent_modified"):
# Add the attribute
cls.parent_modified = True
cls.parent_cache = {}
# Apply it
cls.__init__ = _alt(cls.__init__)
# Get the instance
obj = super().__new__(cls)
# Push it to cache
cls.parent_cache[n] = obj
# Return it
return obj
Clases de ejemplo
class A(Parent):
def __init__(self, n):
print("A.__init__", n)
class B(Parent):
def __init__(self, n):
print("B.__init__", n)
En uso
>>> A(1)
A.__init__ 1 # First A(1) initialized
<__main__.A object at 0x000001A73A4A2E48>
>>> A(1) # Returned previous A(1)
<__main__.A object at 0x000001A73A4A2E48>
>>> A(2)
A.__init__ 2 # First A(2) initialized
<__main__.A object at 0x000001A7395D9C88>
>>> B(2)
B.__init__ 2 # B class doesn't collide with A, thanks to separate cache
<__main__.B object at 0x000001A73951B080>
- Advertencia:No debe inicializar Parent, chocará con otras clases - a menos que defina caché separada en cada uno de los hijos, eso no es lo que queremos.
- Advertencia: Parece una clase con Padre como el abuelo se comporta raro. [No verificado]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-29 20:40:37