Python supporta tre tipi di metodi nelle definizioni di classe: metodi di istanza, metodi di classe e metodi statici. Differiscono per modo di invocazione e accesso ai dati della classe e dell'istanza.
Storia:
Inizialmente, i metodi di istanza (quelli con "self") erano l'unico tipo di comportamento, si presumeva che i metodi chiamassero sempre un comportamento o modificassero/leggessero i dati di un oggetto specifico. Successivamente, in Python sono stati introdotti i metodi di classe (con "cls"), che forniscono comportamento per l'intera classe (ad esempio, costruttori alternativi), e metodi statici, simili a normali funzioni, ma associati alla classe.
Problema:
A volte è necessario un comportamento generale per tutte le istanze (metodi statici). A volte, l'operazione deve essere disponibile "per l'intera classe" (ad esempio, creazione di un'istanza). Tuttavia, se definisci in modo errato il tipo di metodo, puoi incorrere in bug (ad esempio, tentare accidentalmente di modificare la classe tramite un metodo di istanza, o viceversa).
Soluzione:
Esempio di codice:
class MyClass: def instance_method(self): return f"Istanza: {self}" @classmethod def class_method(cls): return f"Classe: {cls}" @staticmethod def static_method(): return "Questo è un metodo statico" obj = MyClass() print(obj.instance_method()) # Istanza: <MyClass object...> print(MyClass.class_method()) # Classe: <class 'MyClass'> print(MyClass.static_method()) # Questo è un metodo statico
Caratteristiche chiave:
È possibile chiamare un metodo di istanza direttamente tramite la classe?
Sì, ma è necessario passare esplicitamente l'oggetto:
MyClass.instance_method(obj)
cosa che raramente è appropriato.
Comportamento dei metodi statici durante l'ereditarietà: possono essere sovrascritti?
Sì, è possibile dichiarare un staticmethod nella sottoclasse con lo stesso nome, e questo verrà chiamato quando si accede dal discendente.
Perché utilizzare metodi di classe se è sempre possibile usare metodi statici con cls come argomento?
cls non è solo il primo argomento: in classmethod Python inserisce automaticamente il corrispondente classe come cls, anche se la chiamata avviene da un discendente. Questo consente di creare una gerarchia di costruttori alternativi senza una rigida dipendenza dal genitore.
Esempio:
class Base: @classmethod def make(cls): return cls() class Child(Base): pass Child.make() # restituirà Child, non Base
Nel progetto sono stati utilizzati metodi normali per creare istanze alternative (ad esempio, create_from_json). A causa di ciò, durante l'ereditarietà, il metodo restituiva sempre un oggetto della classe base, e non del discendente.
Pro:
Contro:
Sono state implementate fabbriche con classmethod, che restituiscono istanze della classe corrente (cls()), anche se richiamate da un discendente.
Pro:
Contro: