Tarik
Estoy aprendiendo Python y hasta ahora puedo decir las cosas a continuación sobre __new__
y __init__
:
__new__
es para la creación de objetos__init__
es para la inicialización de objetos__new__
se invoca antes__init__
como__new__
devuelve una nueva instancia y__init__
invocado después para inicializar el estado interno.__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.- Nosotros podemos usar
__new__
y__init__
tanto para el objeto mutable como para su estado interior se puede cambiar.
Pero tengo otra pregunta ahora.
- 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. self
palabra clave es en términos de nombre se puede cambiar a otra cosa? pero me preguntocls
¿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 tuple
este 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í:
- Creación de objetos Python
- ¿El uso de Python de __new__ y __init__?
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 preguntocls
¿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
tuple
este 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 deobject
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 deobject
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 tipocls
.– 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 unBalancedBinaryTree
class, que se puede inicializar a partir de una lista. Tiene subclases paraLeafNode
yEmptyTree
y__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