python - ¿Hay alguna razón para elegir__new__ over__init__ cuando se define una metaclase?
metaclass python (4)
Siempre configuré metaclases algo como esto:
class SomeMetaClass(type):
def __new__(cls, name, bases, dict):
#do stuff here
Pero me encontré con una metaclase que se definió así:
class SomeMetaClass(type):
def __init__(self, name, bases, dict):
#do stuff here
¿Hay alguna razón para preferir uno sobre el otro?
Actualización : tenga en cuenta que estoy preguntando sobre el uso de __new__
y __init__
en una metaclase. Ya entiendo la diferencia entre ellos en otra clase. Pero en una metaclase, no puedo usar __new__
para implementar el almacenamiento en caché porque __new__
solo se llama a la creación de clases en una metaclase.
Como se ha dicho, si tiene la intención de alterar algo como las clases base o los atributos, tendrá que hacerlo en __new__
. Lo mismo es cierto para el name
de la clase, pero parece haber una peculiaridad con él. Cuando cambias el name
, no se propaga a __init__
, aunque, por ejemplo, attr
es.
Entonces tendrás:
class Meta(type):
def __new__(cls, name, bases, attr):
name = "A_class_named_" + name
return type.__new__(cls, name, bases, attr)
def __init__(cls, name, bases, attr):
print "I am still called ''" + name + "'' in init"
return super(Meta, cls).__init__(name, bases, attr)
class A(object):
__metaclass__ = Meta
print "Now I''m", A.__name__
huellas dactilares
I am still called ''A'' in init
Now I''m A_class_named_A
Esto es importante de saber, si __init__
llama a una súper metaclase que hace algo de magia adicional. En ese caso, uno tiene que cambiar el nombre de nuevo antes de llamar a super.__init__
.
Puede implementar el almacenamiento en caché. Person("Jack")
siempre devuelve un nuevo objeto en el segundo ejemplo, mientras que puede buscar una instancia existente en el primer ejemplo con __new__
(o no devolver nada si lo desea).
Puede ver la descripción completa en los documentos oficiales , pero básicamente se llama a __new__
antes de que se __new__
el nuevo objeto (con el fin de crearlo) y se __init__
después de que se crea el nuevo objeto (con el fin de inicializarlo).
Usar __new__
permite trucos como el almacenamiento en memoria caché de objetos (siempre devolviendo el mismo objeto para los mismos argumentos en lugar de crear nuevos) o produciendo objetos de una clase diferente a la solicitada (a veces utilizada para devolver subclases más específicas de la clase solicitada). En general, a menos que esté haciendo algo bastante extraño, __new__
tiene una utilidad limitada. Si no necesita invocar dicho engaño, __init__
con __init__
.
Si quiere alterar los atributos dict antes de que se cree la clase, o cambie las bases de la tupla, tiene que usar __new__
. Para cuando __init__
ve los argumentos, el objeto de clase ya existe. Además, debe usar __new__
si desea devolver algo que no sea una clase recién creada del tipo en cuestión.
Por otro lado, para el momento en que __init__
ejecuta, la clase existe. Por lo tanto, puede hacer cosas como dar una referencia a la clase recién creada a uno de sus objetos miembros.
Editar : cambió la redacción para dejar más claro que por "objeto", me refiero a objeto de clase.