Spiegazione accurata di Armin Ronacher sopra, espandendo le sue risposte in modo che i principianti come me lo capiscano bene:
La differenza nei metodi definiti in una classe, sia statica che di istanza (esiste ancora un altro tipo - metodo di classe - non discusso qui in modo da saltarlo), risiede nel fatto che siano in qualche modo legati all'istanza della classe o meno. Ad esempio, dire se il metodo riceve un riferimento all'istanza della classe durante il runtime
class C:
a = []
def foo(self):
pass
C # this is the class object
C.a # is a list object (class property object)
C.foo # is a function object (class property object)
c = C()
c # this is the class instance
La __dict__proprietà del dizionario dell'oggetto classe contiene il riferimento a tutte le proprietà e i metodi di un oggetto classe e quindi
>>> C.__dict__['foo']
<function foo at 0x17d05b0>
il metodo foo è accessibile come sopra. Un punto importante da notare qui è che tutto in Python è un oggetto e quindi i riferimenti nel dizionario sopra puntano essi stessi ad altri oggetti. Permettetemi di chiamarli Class Property Objects - o come CPO nell'ambito della mia risposta per brevità.
Se un CPO è un descrittore, l'interprete python chiama il __get__()metodo del CPO per accedere al valore che contiene.
Per determinare se un CPO è un descrittore, l'interpretazione di Python verifica se implementa il protocollo descrittore. Implementare il protocollo descrittore significa implementare 3 metodi
def __get__(self, instance, owner)
def __set__(self, instance, value)
def __delete__(self, instance)
per es
>>> C.__dict__['foo'].__get__(c, C)
dove
self è il CPO (potrebbe essere un'istanza di list, str, function etc) ed è fornito dal runtime
instance è l'istanza della classe in cui è definito questo CPO (l'oggetto 'c' sopra) e deve essere esplicitamente fornito da noi
ownerè la classe in cui è definito questo CPO (l'oggetto di classe 'C' sopra) e deve essere fornito da noi. Tuttavia questo è perché lo stiamo chiamando sul CPO. quando lo chiamiamo sull'istanza, non è necessario fornirlo poiché il runtime può fornire l'istanza o la sua classe (polimorfismo)
value è il valore previsto per il CPO e deve essere fornito da noi
Non tutti i CPO sono descrittori. Per esempio
>>> C.__dict__['foo'].__get__(None, C)
<function C.foo at 0x10a72f510>
>>> C.__dict__['a'].__get__(None, C)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__get__'
Questo perché la classe list non implementa il protocollo descrittore.
Quindi l'argomento self in c.foo(self)è richiesto perché la sua firma del metodo è in realtà questa C.__dict__['foo'].__get__(c, C)(come spiegato sopra, C non è necessaria in quanto può essere scoperto o polimorfizzato) ed è anche per questo che si ottiene un TypeError se non si passa l'argomento dell'istanza richiesta.
Se noti che il metodo è ancora referenziato tramite la classe Oggetto C e l'associazione con l'istanza della classe viene ottenuta passando un contesto sotto forma dell'oggetto di istanza in questa funzione.
Questo è davvero fantastico poiché se hai scelto di non mantenere alcun contesto o nessun legame con l'istanza, tutto ciò che era necessario era scrivere una classe per avvolgere il descrittore CPO e sovrascrivere il suo __get__()metodo per non richiedere alcun contesto. Questa nuova classe è ciò che chiamiamo decoratore e viene applicata tramite la parola chiave@staticmethod
class C(object):
@staticmethod
def foo():
pass
L'assenza di contesto nel nuovo CPO foointegrato non genera un errore e può essere verificata come segue:
>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>
Il caso d'uso di un metodo statico è più di uno spazio dei nomi e della manutenibilità del codice (togliendolo da una classe e rendendolo disponibile in tutto il modulo, ecc.).
Forse è meglio scrivere metodi statici piuttosto che metodi di istanza ogni volta che è possibile, a meno che ovviamente non sia necessario contestualizzare i metodi (come accedere alle variabili di istanza, variabili di classe ecc.). Uno dei motivi è quello di facilitare la raccolta dei rifiuti non mantenendo riferimenti indesiderati agli oggetti.