Sólo estoy tratando de racionalizar una de mis clases y han introducido algunas de las funciones en el mismo estilo que el optimización del rendimiento patrón de diseño.

Sin embargo, estoy un poco confundido en cuanto a por qué __init__ siempre es llamado después de __new__. No me esperaba esto. ¿Alguien puede decirme por qué ocurre esto y cómo puedo implementar esta funcionalidad de otra manera? (Aparte de poner la aplicación en el __new__ que se siente bastante chapucero.)

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

OriginalEl autor Dan | 2009-03-23

17 Comentarios

  1. 486

    Uso __nuevo__ cuando usted necesita para controlar
    la creación de una nueva instancia. Uso
    __init__ cuando usted necesita para controlar la inicialización de una nueva instancia.

    __nuevo__ es el primer paso de la creación de la instancia. Se llama primera, y es
    responsables de la devolución de un nuevo
    instancia de la clase. En contraste,
    __init__ no devuelve nada; es sólo responsable de inicializar la
    instancia después de que se haya creado.

    En general, no es necesario
    reemplazar __nuevo__ a menos que usted está
    crear subclases de una inmutable tipo como
    str, int, unicode o tupla.

    De: http://mail.python.org/pipermail/tutor/2008-April/061426.html

    Debe tener en cuenta que lo que estamos tratando de hacer es que se realiza habitualmente con una Fábrica y esa es la mejor manera de hacerlo. El uso de __nuevo__ no es una buena solución limpia así que por favor, considere el uso de una fábrica. Aquí tienes un buen ejemplo de la fábrica.

    La manera en que se ponga ahora mismo es un Singleton, no una Fábrica.
    Terminamos con __new__ dentro de una clase de Fábrica, que se ha convertido en bastante limpia, así que gracias por tu aporte.
    Lo siento, no estoy de acuerdo que el uso de __new__ debe ser estrictamente limitada a los casos previstos. He encontrado que es muy útil para la aplicación extensible clase genérica de las fábricas — consulte mi respuesta a la pregunta el uso Inadecuado de __new__ para generar las clases en Python? un ejemplo de ello.

    OriginalEl autor

  2. 153

    __new__ es estático método de clase, mientras que __init__ es el método de instancia.
    __new__ tiene que crear la instancia de la primera, así que __init__ puede inicializar. Tenga en cuenta que __init__ toma self como parámetro. Hasta que crear una instancia que no hay self.

    Ahora, entiendo que usted está tratando de implementar patrón singleton en Python. Hay un par de maneras de hacerlo.

    También, como de Python 2.6, se puede utilizar 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:
      ...
    De largo, yo no entiendo muy bien cómo funciona el «@singleton»? Debido a que el decorador devuelve una función, pero Miclase es una clase.
    ¿Por qué el diccionario? Desde cls siempre será el mismo y se obtiene un nuevo diccionario, por ejemplo, para cada singleton va a crear un diccionario con sólo un elemento en él.
    Ninguna opinión sea necesario — el docs de acuerdo con usted.
    sí, el decorador devuelve una función. pero tanto la clase y la función son un callable. Creo instancias = {} debe ser una variable global.
    Alcott tiene un buen punto. Esto se traduce en el nombre MyClass de estar vinculados a una función que devuelve instancias de la clase original. Pero ahora hay ninguna manera para referirse a la clase original en todo, y MyClass ser una función rompe isinstance/issubclass cheques, el acceso a los atributos de la clase/métodos directamente como MyClass.something, nombrando a la clase en super llamadas, etc, etc. Si eso es un problema o no, depende de la clase en la que vaya a aplicar (y el resto del programa).

    OriginalEl autor vartec

  3. 126

    En la mayoría de los conocidos OO de idiomas, una expresión como SomeClass(arg1, arg2) asignará una nueva instancia, inicializar la instancia de atributos y, a continuación, volver.

    En la mayoría de los conocidos OO de idiomas, el «inicializar la instancia de los atributos de» parte puede ser personalizada para cada clase mediante la definición de una constructor, que es básicamente un bloque de código que opera sobre la nueva instancia (el uso de los argumentos para el constructor de la expresión) para el conjunto de todas las condiciones iniciales son deseado. En Python, esto corresponde a la clase’ __init__ método.

    Python __new__ es nada más y nada menos que en similar por la clase de personalización de la «asignar una nueva instancia de parte». Por supuesto, esto le permite hacer cosas inusuales, tales como devolver una instancia existente en lugar de asignar una nueva. Así que en Python, no deberíamos pensar en esta parte como involucrar necesariamente la asignación; todo lo que necesitamos es que __new__ viene con un adecuado ejemplo de algún lugar.

    Pero todavía es sólo la mitad del trabajo, y no hay manera de que el sistema Python para saber que a veces desea ejecutar la otra mitad del trabajo (__init__) posteriormente, y a veces no. Si desea que el comportamiento, se dice de forma tan explícita.

    A menudo, usted puede refactorizar por lo que sólo necesita __new__, o por lo que no necesitas __new__, o para que __init__ se comporta de forma diferente en un objeto inicializado. Pero si usted realmente quiere, Python hace en realidad le permiten redefinir «el trabajo», por lo que SomeClass(arg1, arg2) no necesariamente __new__ seguido por __init__. Para hacer esto, usted necesita para crear un metaclass, y definir su __call__ método.

    Un metaclass es justo la clase de una clase. Y una clase’ __call__ método controla lo que sucede cuando usted llame a las instancias de la clase. Así que un metaclass__call__ método controla lo que sucede cuando usted llama una clase; es decir, que permite que usted redefinir la instancia de creación de mecanismo de principio a fin. Este es el nivel en el que puede más elegante implementar completamente no-estándar de la creación de la instancia de proceso tales como el patrón singleton. De hecho, con menos de 10 líneas de código que se puede implementar un Singleton metaclass que entonces no es necesario que preocuparte más por __new__ en todos los, y se puede convertir cualquier lo contrario-a una clase 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, esto es probablemente lo más profundo de la magia de la que realmente garantizados por esta situación!

    OriginalEl autor Ben

  4. 20

    Citar el documentación:

    Implementaciones típicas crear una nueva instancia de la clase mediante la invocación de
    la superclase __nuevo__() utilizando el método «super(currentclass,
    cls).__nuevo__(cls[, …])»con argumentos adecuados y, a continuación,
    la modificación de la recién creada instancia como sea necesario antes de devolverlo.

    Si __nuevo__() no devuelve una instancia de cls, a continuación, el nuevo
    de la instancia de __init__() método no será invocado.

    __nuevo__() está destinado principalmente para permitir que las subclases de inmutable
    tipos (como int, str, o tupla) para personalizar la creación de la instancia.

    If __new__() does not return an instance of cls, then the new instance's __init__() method will not be invoked. Que es un punto importante. Si devuelve una instancia diferente, original __init__ nunca es llamado.

    OriginalEl autor tgray

  5. 10

    Me doy cuenta de que esta pregunta es bastante viejo, pero yo tenía un problema similar.
    El siguiente hizo 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

    Nota: __nuevo__ siempre devuelve un objeto apropiado, así que __init__ es llamado siempre – incluso si la instancia ya existe.

    OriginalEl autor Tobias

  6. 8

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

    OriginalEl autor PMN

  7. 8

    Cuando __new__ devuelve una instancia de la misma clase, __init__ se ejecuta después, el objeto devuelto. I. e. NO se puede utilizar __new__ para evitar __init__ que se va a ejecutar. Incluso si regresa creado previamente objeto de __new__, va a ser el doble (o triple, etc…) inicializado por __init__ una y otra vez.

    Aquí es el enfoque genérico para patrón Singleton que se extiende vartec respuesta anterior y 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

    Historia completa es aquí.

    Otro enfoque, que de hecho implica __new__ es el uso de classmethods:

    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, preste atención, de que con este enfoque que usted necesita para decorar TODOS los métodos con @classmethod, porque nunca lo uso real de la instancia de MyClass.

    OriginalEl autor Zaar Hai

  8. 5
    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 un efecto secundario M._dict propiedad automáticamente se convierte en accesible desde A como A._dict así que tenga cuidado de no sobrescribir dicho sea de paso.

    Falta un __init__ método que establece cls._dict = {}. Usted probablemente no quiere un diccionario compartida por todas las clases de este metatype (pero +1 a la idea).

    OriginalEl autor Antony Hatchkins

  9. 4

    __nuevo__ debe devolver una imagen nueva instancia de una clase. __init__ a continuación, se llama a inicializar la instancia. No está llamando a __init__ en el «NUEVO» caso de __nuevo__, así que está siendo llamado para usted. El código que llama a __new__ no mantener un seguimiento de si __init__ ha sido llamado en un caso particular o no, ni debe, porque estás haciendo algo muy raro aquí.

    Se podría añadir un atributo al objeto en el __init__ de la función para indicar que se ha inicializado. Comprobar la existencia de ese atributo como el primero en __init__ y no proceder más si lo ha sido.

    OriginalEl autor Andrew Wilkinson

  10. 4

    Una actualización a @AntonyHatchkins respuesta, usted probablemente querrá una por separado diccionario de instancias para cada clase de la metatype, lo que significa que debe tener un __init__ método en el metaclass para inicializar el objeto de la clase con el diccionario en lugar de a nivel mundial a través de 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()

    Me han salido adelante y actualiza el código original con un __init__ método y cambiado la sintaxis de Python 3 la notación (no-arg llamada a super y metaclass en la clase de argumentos en lugar de como un atributo).

    De cualquier manera, el punto importante aquí es que el inicializador de clase (__call__ método) no ejecutar bien __new__ o __init__ si la clave se encuentra. Esto es mucho más limpio que el uso de __new__, que requiere que usted para marcar el objeto si desea omitir el valor predeterminado __init__ paso.

    OriginalEl autor Mad Physicist

  11. 3

    Refiriéndose a este doc:

    Cuando subclases inmutable tipos integrados como números y cadenas,
    y en ocasiones, en otras situaciones, el método estático nueva viene
    en la mano. nueva es el primer paso en la instancia de la construcción, que se invoca en
    antes de init.

    La nueva se llama al método de la clase en su
    primer argumento; su responsabilidad es la de devolver una nueva instancia de la que
    clase.

    Comparar esto con init: init se llama con un ejemplo
    como primer argumento, y no devuelve nada; su
    la responsabilidad es para inicializar la instancia.

    Hay situaciones
    donde una nueva instancia es creada sin llamar init (por ejemplo
    cuando la instancia se carga de un apuro). No hay manera de crear
    una nueva instancia sin llamar nueva (aunque en algunos casos puede
    salirse con la llamada de una clase base de la nueva).

    Con respecto a lo que desea lograr, también en el mismo doc info sobre el 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

    usted también puede utilizar esta aplicación de PEP 318, utilizando un decorador

    def singleton(cls):
        instances = {}
        def getinstance():
            if cls not in instances:
                instances[cls] = cls()
            return instances[cls]
        return getinstance
    
    @singleton
    class MyClass:
      ...
    Llamar a una forma degenerada de init de __new__ suena muy chapucero. Esto es lo que metaclasses.

    OriginalEl autor octoback

  12. 3

    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 se defina explícitamente otra clase base como un metaclass). La secuencia de bajo nivel, las llamadas se pueden encontrar aquí. El primer método es llamado el type_call que, a continuación, llama tp_new y, a continuación,tp_init.

    La parte interesante aquí es que tp_new va a llamar a la Object‘s (clase base) nuevo método object_new que hace un tp_alloc ( PyType_GenericAlloc ), que asigna la memoria para el objeto de : a)

    En ese momento el objeto se crea en la memoria y, a continuación, el __init__ método se llama. Si __init__ no está implementado en la clase, a continuación, el object_init se llama y no hace nada 🙂

    Luego type_call sólo devuelve el objeto que se une a la variable.

    OriginalEl autor xpapazaf

  13. 3

    Uno debe buscar en __init__ como un simple constructor tradicional OO de idiomas. Por ejemplo, si usted está familiarizado con Java o C++, el constructor se le pasa un puntero a su propia instancia de forma implícita. En el caso de Java, es la this variable. Si uno fuera a inspeccionar el código de bytes generado por Java, uno se daría cuenta de dos llamadas. La primera llamada es a un «nuevo» método y, a continuación, siguiente llamada al método init (que es la llamada a la definida por el usuario constructor). Este proceso de dos pasos que permite la creación de la instancia real antes de llamar al método constructor de la clase que es un método de instancia.

    Ahora, en el caso de Python, __new__ es una instalación adicional que es accesible para el usuario. Java no proporciona la flexibilidad, debido a su escrito la naturaleza. Si un idioma a condición de que la instalación, a continuación, el implementador de __new__ podría hacer muchas cosas en que el método antes de devolver la instancia, incluyendo la creación de una nueva instancia de un no relacionados objeto en algunos casos. Y, además, este enfoque funciona bien para especialmente para inmutable tipos en el caso de Python.

    OriginalEl autor Guru Devanla

  14. 1

    La __init__ se llama después de que __new__ de modo que cuando se reemplaza en una subclase, su código agregado aún llamado.

    Si usted está tratando de crear subclases de una clase que ya tiene un __new__, alguien conscientes de que esto podría empezar con la adaptación de la __init__ y el reenvío de la llamada a la subclase __init__. Este convenio de llamar __init__ después de __new__ ayuda a que funcione como se esperaba.

    La __init__ todavía necesita para permitir que cualquiera de los parámetros de la superclase __new__ necesario, pero de no hacerlo, se suele crear un claro error de tiempo de ejecución. Y el __new__ debe probablemente para permitir explícitamente *args y ‘**kw», para dejar claro que la extensión está bien.

    Es generalmente mala forma de tener tanto __new__ y __init__ en la misma clase en el mismo nivel de la herencia, porque el comportamiento del cartel original descrito.

    OriginalEl autor Jay Obermark

  15. 0

    Sin embargo, estoy un poco confundido en cuanto a por qué __init__ siempre es llamado después de __new__.

    No mucho de una razón distinta de la que sólo se hace de esa manera. __new__ no tiene la responsabilidad de inicialización de la clase, algún otro método (__call__, posiblemente, no sé a ciencia cierta).

    No me esperaba esto. ¿Alguien puede decirme por qué ocurre esto y cómo puedo implementar esta funcionalidad de otra manera? (aparte de poner la aplicación en el __new__ que se siente bastante chapucero).

    Podría tener __init__ hacer nada si ya se ha inicializado, o usted podría escribir un nuevo metaclass con un nuevo __call__ que sólo llamadas de __init__ en nuevas instancias, y que de otro modo devuelve __new__(...).

    OriginalEl autor Devin Jeanpierre

  16. 0

    La simple razón es que el nueva se utiliza para la creación de una instancia, mientras que init se utiliza para inicializar la instancia. Antes de la inicialización, la instancia debe ser creado primero. Es por eso que nueva debe ser llamada antes de init.

    OriginalEl autor ZQ Hu

  17. 0

    Ahora tengo el mismo problema, y por algunas razones por las que me decidí a evitar decoradores, fábricas y metaclasses. Yo 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

    Ejemplo clases

    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 los Padres, se chocan con otras clases – a menos que usted define caché independiente en cada uno de los niños, que no es lo que queremos.
    • Advertencia: parece una clase con los Padres como los abuelos se comporta extraño. [Unverified]

    Intentarlo en línea!

    OriginalEl autor RedClover

Dejar respuesta

Please enter your comment!
Please enter your name here