¿Por qué siempre se llama a __init__() después de __new__()?

Resuelto Dan asked hace 15 años • 20 respuestas

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é?

Dan avatar Mar 24 '09 00:03 Dan
Aceptado

Ú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 .

mpeterson avatar Mar 23 '2009 17:03 mpeterson

__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 selfcomo 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:
  ...
vartec avatar Mar 23 '2009 17:03 vartec

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!

Ben avatar Dec 29 '2011 07:12 Ben

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.

tgray avatar Mar 23 '2009 17:03 tgray

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

 avatar May 13 '2013 14:05