__nuevo__ y __init__ en Python

5 minutos de lectura

Avatar de usuario de Tarik
Tarik

Estoy aprendiendo Python y hasta ahora puedo decir las cosas a continuación sobre __new__ y __init__:

  1. __new__ es para la creación de objetos
  2. __init__ es para la inicialización de objetos
  3. __new__ se invoca antes __init__ como __new__ devuelve una nueva instancia y __init__ invocado después para inicializar el estado interno.
  4. __new__ es bueno para objetos inmutables ya que no se pueden cambiar una vez que se asignan. Entonces podemos devolver una nueva instancia que tiene un nuevo estado.
  5. Nosotros podemos usar __new__ y __init__ tanto para el objeto mutable como para su estado interior se puede cambiar.

Pero tengo otra pregunta ahora.

  1. Cuando creo una nueva instancia como a = MyClass("hello","world"), ¿cómo se pasan estos argumentos? Me refiero a cómo debo estructurar la clase usando __init__ y __new__ ya que son diferentes y ambos aceptan argumentos arbitrarios además del primer argumento predeterminado.
  2. self palabra clave es en términos de nombre se puede cambiar a otra cosa? pero me pregunto cls ¿En términos de nombre, está sujeto a cambios a otra cosa, ya que es solo un nombre de parámetro?

Hice algunos pequeños experimentos como los siguientes:

>>> class MyClass(tuple):
    def __new__(tuple):
        return [1,2,3]

y lo hice a continuación:

>>> a = MyClass()
>>> a
[1, 2, 3]

Aunque dije que quiero volver tupleeste código funciona bien y me devolvió [1,2,3]. Sabía que estábamos pasando los primeros parámetros como el tipo que queríamos recibir una vez que el __new__ se invoca la función. Estamos hablando de New función verdad? No conozco otros idiomas que devuelvan un tipo que no sea el tipo enlazado.

Y también hice otras cosas:

>>> issubclass(MyClass,list)
False
>>> issubclass(MyClass,tuple)
True
>>> isinstance(a,MyClass)
False
>>> isinstance(a,tuple)
False
>>> isinstance(a,list)
True

No hice más experimentos porque el más lejos no era brillante y decidí detenerme allí y decidí preguntarle a StackOverflow.

Las publicaciones SO que leí:

  1. Creación de objetos Python
  2. ¿El uso de Python de __new__ y __init__?

Avatar de usuario de Fred Foo
fred foo

cómo debo estructurar la clase usando __init__ y __new__ ya que son diferentes y ambos aceptan argumentos arbitrarios además del primer argumento predeterminado.

Sólo en raras ocasiones tendrá que preocuparse por __new__. Por lo general, solo definirá __init__ y dejar el valor predeterminado __new__ pasarle los argumentos del constructor.

self palabra clave es en términos de nombre se puede cambiar a otra cosa? pero me pregunto cls ¿En términos de nombre, está sujeto a cambios a otra cosa, ya que es solo un nombre de parámetro?

Ambos son solo nombres de parámetros sin un significado especial en el lenguaje. Pero su uso es un muy fuerte convención en la comunidad de Python; la mayoría de los pitonistas lo harán nunca cambiar los nombres self y cls en estos contextos y se confundirá cuando alguien más lo haga.

Tenga en cuenta que su uso de def __new__(tuple) vuelve a vincular el nombre tuple dentro de la función constructora. Cuando realmente se implementa __new__querrás hacerlo como

def __new__(cls, *args, **kwargs):
    # do allocation to get an object, say, obj
    return obj

Aunque dije que quiero volver tupleeste código funciona bien y me devolvió [1,2,3].

MyClass() tendrá el valor que __new__ devoluciones. No hay verificación de tipos implícita en Python; es responsabilidad del programador devolver el tipo correcto (“todos somos adultos que consienten aquí”). Poder devolver un tipo diferente al solicitado puede ser útil para implementar fábricas: puede devolver una subclase del tipo solicitado.

Esto también explica la issubclass/isinstance comportamiento que observa: la relación de subclase se deriva de su uso de class MyClass(tuple)el isinstance refleja que devuelve el tipo “incorrecto” de __new__.

Como referencia, consulte el requisitos para __new__ en el Referencia del lenguaje Python.

Editar: ok, aquí hay un ejemplo de uso potencialmente útil de __new__. La clase Eel realiza un seguimiento de cuántas anguilas están vivas en el proceso y se niega a asignar si esto excede un máximo.

class Eel(object):
    MAX_EELS = 20
    n_eels = 0

    def __new__(cls, *args, **kwargs):
        if cls.n_eels == cls.MAX_EELS:
            raise HovercraftFull()

        obj = super(Eel, cls).__new__(cls)
        cls.n_eels += 1
        return obj

    def __init__(self, voltage):
        self.voltage = voltage

    def __del__(self):
        type(self).n_eels -= 1

    def electric(self):
        """Is this an electric eel?"""
        return self.voltage > 0

Eso sí, hay formas más inteligentes de lograr este comportamiento.

  • Pero aún así, ¿podría darme un código de muestra que contenga ambos __new__ y __init__ con argumentos arbitrarios pasados.

    – Tarik

    12 de noviembre de 2011 a las 19:41

  • @Braveyard: heredar de object Llegar __new__. De hecho, es una buena idea heredar siempre de object a menos que esté escribiendo código de compatibilidad inversa para versiones muy antiguas de Python. En Python 3, las clases de estilo antiguo se han ido y la herencia de object está implícito en las definiciones de clase.

    – Fred Foo

    12 de noviembre de 2011 a las 20:13

  • no deberías llamar __init__ desde __new__Python lo hará solo si devuelve una instancia de tipo cls.

    – interjay

    13 de noviembre de 2011 a las 10:06

  • Agregaría a esto que el comportamiento aparentemente extraño de poder devolver algo de __new__ puede (al menos teóricamente) ser útil. Puedo imaginar algo como un BalancedBinaryTree class, que se puede inicializar a partir de una lista. Tiene subclases para LeafNode y EmptyTreey __new__ devuelve uno de esos si la lista de entrada tiene 0 o 1 elementos. Esencialmente, significa que puede implementar fábricas sin que lo parezca y, por lo tanto, puede cambiar si sus clases usan fábricas o creación directa de instancias sin necesariamente romper su interfaz.

    – ben

    13 de noviembre de 2011 a las 12:05

  • Te di +1 antes de llegar a tu ejemplo de anguilas. Ojalá pudiera dar otro para raise HovercraftFull

    – keithjgrant

    31 de enero de 2013 a las 13:23

¿Ha sido útil esta solución?