Dans la plupart des langages OO bien connus, une expression comme SomeClass(arg1, arg2)
va allouer une nouvelle instance, initialiser les attributs de l'instance, puis la renvoyer.
Dans la plupart des langages OO bien connus, la partie "initialiser les attributs de l'instance" peut être personnalisée pour chaque classe en définissant un constructeur , qui est fondamentalement juste un bloc de code qui opère sur la nouvelle instance (en utilisant les arguments fournis à l'expression constructeur ) pour mettre en place les conditions initiales souhaitées. En Python, cela correspond à la __init__
méthode de la classe .
Python __new__
n'est rien de plus et rien de moins que la personnalisation par classe similaire de la partie "allouer une nouvelle instance". Bien sûr, cela vous permet de faire des choses inhabituelles telles que retourner une instance existante plutôt que d'en allouer une nouvelle. Donc, en Python, nous ne devrions pas vraiment penser à cette partie comme impliquant nécessairement l'allocation; tout ce dont nous avons besoin, c'est de __new__
trouver une instance appropriée quelque part.
Mais ce n'est encore que la moitié du travail, et il n'y a aucun moyen pour le système Python de savoir que parfois vous voulez exécuter l'autre moitié du travail ( __init__
) par la suite et parfois non. Si vous voulez ce comportement, vous devez le dire explicitement.
Souvent, vous pouvez refactoriser de sorte que vous n'ayez besoin que __new__
, ou que vous n'en ayez pas besoin __new__
, ou que cela __init__
se comporte différemment sur un objet déjà initialisé. Mais si vous le voulez vraiment, Python vous permet en fait de redéfinir "le travail", donc cela SomeClass(arg1, arg2)
n'appelle pas nécessairement __new__
suivi de __init__
. Pour ce faire, vous devez créer une métaclasse et définir sa __call__
méthode.
Une métaclasse n'est que la classe d'une classe. Et une __call__
méthode de classe contrôle ce qui se passe lorsque vous appelez des instances de la classe. Ainsi , une métaclasse « __call__
contrôles de méthode ce qui se produit lorsque vous appelez une classe; c'est-à-dire qu'il vous permet de redéfinir le mécanisme de création d'instance du début à la fin . Il s'agit du niveau auquel vous pouvez implémenter le plus élégamment un processus de création d'instance complètement non standard tel que le modèle singleton. En fait, avec moins de 10 lignes de code que vous pouvez mettre en œuvre une Singleton
métaclasse qui alors ne même pas vous obliger à Futz avec __new__
du tout , et peut transformer une classe autrement normale dans un singleton en ajoutant simplement __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
Cependant, c'est probablement une magie plus profonde que ce qui est vraiment justifié dans cette situation!