En la mayoría de los lenguajes OO 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 OO conocidos, la parte "inicializar los atributos de la instancia" se puede personalizar para cada clase definiendo un constructor , que es básicamente 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 deseadas. En Python, esto corresponde al __init__
método de la clase .
Python __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 realmente en esta parte como necesariamente una asignación; todo lo que requerimos es que __new__
aparezca una instancia adecuada de algún lado.
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 y otras veces no. Si quieres ese comportamiento, tienes que decirlo explícitamente.
A menudo, puede refactorizar para que solo lo necesite __new__
, o para que no lo necesite __new__
, o para que se __init__
comporte de manera diferente en un objeto ya inicializado. Pero si realmente quieres, Python realmente te permite redefinir "el trabajo", por lo que eso SomeClass(arg1, arg2)
no necesariamente llama __new__
seguido de __init__
. Para hacer esto, debe crear una metaclase y definir su __call__
método.
Una metaclase es solo la clase de una clase. Y el __call__
método de una clase controla lo que sucede cuando se llaman instancias de la clase. Entonces , el método de una metaclase__call__
controla lo que sucede cuando se llama a una clase; es decir, le permite redefinir el mecanismo de creación de instancias de principio a fin . Este es el nivel en el que puede implementar de manera más elegante 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 puede implementar una Singleton
metaclase que entonces ni siquiera requieren que futz con __new__
nada , y puede convertir cualquier clase de otra manera normal en un producto único, simplemente añadiendo __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!