Nelle lingue OO più note, un'espressione simile SomeClass(arg1, arg2)
alloca una nuova istanza, inizializza gli attributi dell'istanza e quindi la restituisce.
Nei linguaggi OO più noti, la parte "inizializza gli attributi dell'istanza" può essere personalizzata per ogni classe definendo un costruttore , che è fondamentalmente solo un blocco di codice che opera sulla nuova istanza (usando gli argomenti forniti all'espressione del costruttore ) per impostare le condizioni iniziali desiderate. In Python, questo corrisponde al __init__
metodo della classe .
Python __new__
non è niente di più e niente di meno che una personalizzazione per classe simile della parte "alloca una nuova istanza". Questo ovviamente ti consente di fare cose insolite come restituire un'istanza esistente piuttosto che allocarne una nuova. Quindi in Python, non dovremmo davvero pensare a questa parte come necessariamente all'allocazione; tutto ciò di cui abbiamo bisogno è che __new__
si presenti un'istanza adatta da qualche parte.
Ma è ancora solo metà del lavoro, e non c'è modo per il sistema Python di sapere che a volte vuoi eseguire l'altra metà del lavoro ( __init__
) in seguito e a volte no. Se vuoi quel comportamento, devi dirlo esplicitamente.
Spesso, puoi fare il refactoring in modo da averne solo bisogno __new__
, o così non ti serve __new__
, o in modo che __init__
si comporti diversamente su un oggetto già inizializzato. Ma se lo desideri davvero, Python ti consente effettivamente di ridefinire "il lavoro", in modo che SomeClass(arg1, arg2)
non sia necessariamente chiamato __new__
seguito da __init__
. Per fare ciò, è necessario creare una metaclasse e definirne il __call__
metodo.
Una metaclasse è solo la classe di una classe. E un __call__
metodo di classe controlla cosa succede quando chiamate istanze della classe. Così un metaclasse ' __call__
controlli di metodo che cosa succede quando si chiama una classe; cioè ti permette di ridefinire il meccanismo di creazione dell'istanza dall'inizio alla fine . Questo è il livello al quale è possibile implementare in modo elegante un processo di creazione di istanze completamente non standard come il modello singleton. In realtà, con meno di 10 righe di codice è possibile implementare un Singleton
metaclasse che poi non ha nemmeno richiedono di Futz con __new__
affatto , e in grado di trasformare qualsiasi classe altrimenti-normale in una Singleton con la semplice aggiunta __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
Tuttavia, questa è probabilmente una magia più profonda di quanto sia veramente garantito per questa situazione!