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

10 minutos de lectura

¿Por que siempre se llama a init despues de new
Dan

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 de peso mosca.

Sin embargo, estoy un poco confundido en cuanto a por qué __init__ siempre se llama después __new__. No esperaba esto. ¿Alguien puede decirme por qué sucede esto y cómo puedo implementar esta funcionalidad de otra manera? (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é?

  • También estaba tratando de entender el patrón de diseño, y la primera vez que escuché sobre: ​​el patrón de diseño de peso ligero … y un enlace muy bueno que tiene un ejemplo en casi todos los idiomas populares.

    – Emma Y.

    7 mayo 2020 a las 22:10

  • ¿No es un singleton?

    – Derek

    13 de marzo de 2021 a las 19:07

Utilizar __new__ 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 de la creación de instancias. Se llama primero y es responsable de devolver una nueva instancia de su clase.

A diferencia de,
__init__ no devuelve nada; solo es responsable de inicializar la instancia después de que se haya creado.

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__ contra __init__? en mail.python.org.

Debes considerar que lo que estás tratando de hacer generalmente se hace con un Fábrica y esa es la mejor manera de hacerlo. Utilizando __new__ no es una buena solución limpia, así que considere el uso de una fábrica. Aquí hay un buen ejemplo: ActiveState Fᴀᴄᴛᴏʀʏ ᴘᴀᴛᴛᴇʀɴ Receta.

  • terminó usando __new__ dentro de una clase Factory, que se ha vuelto bastante limpia, así que gracias por tu aporte.

    – Dan

    25 de marzo de 2009 a las 18:09

  • Lo siento, no estoy de acuerdo con que el uso de __new__ debe limitarse estrictamente a los casos señalados. Lo encontré muy útil para implementar fábricas de clases genéricas extensibles; vea mi respuesta a la pregunta Uso inadecuado de __new__ generar clases en Python? para ver un ejemplo de cómo hacerlo.

    – martineau

    3 de febrero de 2015 a las 0:19

  • estoy totalmente en desacuerdo con __new__ siendo una mala solución aquí. El patrón de fábrica es necesario en lenguajes que restringen a los constructores para que actúen como inicializadores (lo que le impide devolver un objeto existente); como la mayoría de los patrones de diseño (especialmente la plétora de ellos inventados específicamente para Java en los primeros días, debido a la inflexibilidad del lenguaje), es una forma de solucionar las limitaciones del lenguaje de manera consistente. Python no tiene esa limitación; tu usas __new__ para este caso, y @classmethod constructores alternativos para la construcción a partir de argumentos variantes.

    – ShadowRanger

    22 de junio de 2020 a las 16:18

  • El enlace para la receta del patrón de fábrica no funciona.

    – paradoja

    18 de junio de 2021 a las 14:08

¿Por que siempre se llama a init despues de new
vartec

__new__ es un método de clase estático, mientras que __init__ es un método de instancia.
__new__ tiene que crear la instancia primero, entonces __init__ puede inicializarlo. Tenga en cuenta que __init__ toma self como parámetro. Hasta que crees una instancia, no hay self.

Ahora, supongo, que estás tratando de implementar patrón único en Python. Hay algunas maneras de hacer eso.

Además, a partir de Python 2.6, puede usar la clase decoradores.

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
  ...

  • @Tyler Long, no entiendo muy bien cómo funciona el “@singleton”. Porque el decorador devuelve una función pero MyClass es una clase.

    – Alcott

    19 de septiembre de 2011 a las 13:33

  • ¿Por qué el diccionario? Dado que cls siempre será el mismo y obtiene un diccionario nuevo, por ejemplo, para cada singleton, está creando un diccionario con solo un elemento.

    –Winston Ewert

    20 de septiembre de 2011 a las 0:20

  • @Alcott: No se necesita opinión — los documentos de acuerdo con usted.

    –Ethan Furman

    20 de septiembre de 2011 a las 0:29

  • @Alcott. sí, el decorador devuelve una función. pero tanto la clase como la función son invocables. Creo que las instancias = {} deberían ser una variable global.

    – Tyler Liu

    12 de noviembre de 2011 a las 3:43

  • @TylerLong Alcott tiene un buen punto. Esto da como resultado el nombre MyClass estar vinculado a una función que devuelve instancias de la clase original. Pero ahora no hay forma de referirse a la clase original en absoluto, y MyClass al ser una función se rompe isinstance/issubclass cheques, acceso a atributos/métodos de clase directamente como MyClass.somethingnombrando la clase en super llamadas, etc., etc. Si eso es un problema o no, depende de la clase a la que lo esté aplicando (y el resto del programa).

    – ben

    21 de marzo de 2012 a las 22:48

En la mayoría de los lenguajes orientados a objetos 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 la mayoría de los lenguajes orientados a objetos más conocidos, la parte “iniciar los atributos de la instancia” se puede personalizar para cada clase definiendo un constructor, que básicamente es solo un bloque de código que opera en la nueva instancia (usando los argumentos proporcionados a la expresión del constructor) para configurar las condiciones iniciales que se deseen. En Python, esto corresponde a la clase’ __init__ método.

de pitón __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 en esta parte como algo que implica necesariamente la asignación; todo lo que requerimos es que __new__ se le ocurre una instancia adecuada de alguna parte.

Pero todavía es solo la mitad del trabajo, y no hay forma de que el sistema Python sepa que a veces desea ejecutar la otra mitad del trabajo (__init__) después ya veces no. Si quieres ese comportamiento, tienes que decirlo explícitamente.

A menudo, puede refactorizar para que solo necesite __new__o por lo que no necesita __new__o para que __init__ se comporta de manera diferente en un objeto ya inicializado. Pero si realmente lo desea, Python le permite redefinir “el trabajo”, de modo que SomeClass(arg1, arg2) no necesariamente llama __new__ seguido por __init__. Para hacer esto, necesita crear una metaclase y definir su __call__ método.

Una metaclase es solo la clase de una clase. Y una clase’ __call__ El método controla lo que sucede cuando llamas a instancias de la clase. entonces un metaclase__call__ El método controla lo que sucede cuando llamas a una clase; es decir, te permite redefinir el mecanismo de creación de instancias de principio a fin. Este es el nivel en el 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 puedes implementar un Singleton metaclase que ni siquiera requiere que juegues con __new__ en absolutoy puede girar ninguna clase por lo demás normal en un singleton simplemente agregando __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, esta es probablemente una magia más profunda de lo que realmente se justifica para esta situación!

  • Increíble: o simple y no hace que trabajar dentro de su clase sea molesto como lo haría decorarlo.

    – Descanso

    24 de enero de 2021 a las 7:40

1646965630 393 ¿Por que siempre se llama a init despues de new
gris

Para citar el 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 devolverla.

Si __new__() no devuelve una instancia de cls, entonces no se invocará el método __init__() de la nueva instancia.

__new__() está diseñado principalmente para permitir subclases de tipos inmutables (como int, str o tuple) para personalizar 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

Usé esta página como un recurso. http://infohost.nmt.edu/tcc/help/pubs/python/web/new-new-method.html

  • Nota: __new__ siempre devuelve un objeto apropiado, por lo que siempre se llama a __init__, incluso si la instancia ya existe.

    – Pensamiento extraño

    3 oct 2014 a las 16:18


Cuándo __new__ devuelve una instancia de la misma clase, __init__ se ejecuta después en el objeto devuelto. es decir, NO puedes usar __new__ para prevenir __init__ de ser ejecutado. 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 del patrón Singleton que amplía la respuesta de vartec anterior y la 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 es 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"

Preste atención, que con este enfoque necesita decorar TODOS sus métodos con @classmethodporque nunca usarás ninguna instancia real de MyClass.

  • Nota: __new__ siempre devuelve un objeto apropiado, por lo que siempre se llama a __init__, incluso si la instancia ya existe.

    – Pensamiento extraño

    3 oct 2014 a las 16:18


¿Por que siempre se llama a init despues de new
PMN

Creo que la respuesta simple a esta pregunta es que, si __new__ devuelve un valor que es del mismo tipo que la clase, el __init__ la función se ejecuta, de lo contrario no lo hará. En este caso, su código devuelve A._dict('key') que es de la misma clase que clsentonces __init__ será ejecutado.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad