Qual è la differenza tra una funzione decorata con @staticmethod
e una decorata con @classmethod
?
Qual è la differenza tra una funzione decorata con @staticmethod
e una decorata con @classmethod
?
Risposte:
Forse un po 'di codice di esempio aiuterà: notare la differenza nelle firme di chiamata di foo
, class_foo
e static_foo
:
class A(object):
def foo(self, x):
print "executing foo(%s, %s)" % (self, x)
@classmethod
def class_foo(cls, x):
print "executing class_foo(%s, %s)" % (cls, x)
@staticmethod
def static_foo(x):
print "executing static_foo(%s)" % x
a = A()
Di seguito è riportato il solito modo in cui un'istanza di oggetto chiama un metodo. L'istanza dell'oggetto a
, viene implicitamente passata come primo argomento.
a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)
Con i metodi di classe, la classe dell'istanza dell'oggetto viene implicitamente passata come primo argomento anziché self
.
a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)
Puoi anche chiamare class_foo
usando la classe. In effetti, se definisci qualcosa come un metodo di classe, è probabilmente perché intendi chiamarlo dalla classe piuttosto che da un'istanza di classe. A.foo(1)
avrebbe sollevato un TypeError, ma A.class_foo(1)
funziona bene:
A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)
Un uso che le persone hanno trovato per i metodi di classe è quello di creare costruttori alternativi ereditabili .
Con staticmethods , né self
(l'istanza dell'oggetto) né cls
(la classe) vengono passati implicitamente come primo argomento. Si comportano come semplici funzioni, tranne per il fatto che puoi chiamarle da un'istanza o dalla classe:
a.static_foo(1)
# executing static_foo(1)
A.static_foo('hi')
# executing static_foo(hi)
I metodi statici vengono utilizzati per raggruppare funzioni che hanno una connessione logica con una classe alla classe.
foo
è solo una funzione, ma quando chiami a.foo
non ottieni solo la funzione, ottieni una versione "parzialmente applicata" della funzione con l'istanza dell'oggetto a
associata come primo argomento alla funzione. foo
prevede 2 argomenti, mentre a.foo
prevede solo 1 argomento.
a
è legato a foo
. Questo è ciò che si intende con il termine "vincolato" di seguito:
print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>
Con a.class_foo
, a
non è tenuto a class_foo
, piuttosto la classe A
è destinata aclass_foo
.
print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>
Qui, con un metodo statico, anche se è un metodo, a.static_foo
restituisce solo una buona funzione ole senza argomenti associati. static_foo
si aspetta 1 argomento e si
a.static_foo
aspetta anche 1 argomento.
print(a.static_foo)
# <function static_foo at 0xb7d479cc>
E ovviamente la stessa cosa accade quando chiami invece static_foo
con la classe A
.
print(A.static_foo)
# <function static_foo at 0xb7d479cc>
@staticmethod
potrebbe aiutare a organizzare il codice essendo sovrascrivibile dalle sottoclassi. Senza di esso avresti varianti della funzione fluttuante nello spazio dei nomi del modulo.
@staticmethod
: puoi usarlo per rimuovere l'innesto. Sto implementando un linguaggio di programmazione in Python - le funzioni definite dalla libreria usano un execute
metodo statico , dove le funzioni definite dall'utente richiedono argomenti di istanza (cioè il corpo della funzione). Questo decoratore elimina gli avvisi di "parametro auto inutilizzato" nell'ispettore PyCharm.
Un metodo statico è un metodo che non sa nulla della classe o dell'istanza su cui è stato chiamato. Ottiene solo gli argomenti che sono stati passati, nessun primo argomento implicito. In Python è praticamente inutile: puoi semplicemente usare una funzione del modulo invece di un metodo statico.
Un metodo di classe , d'altra parte, è un metodo che ottiene la classe su cui è stato chiamato, o la classe dell'istanza su cui è stato chiamato, come primo argomento. Ciò è utile quando si desidera che il metodo sia un factory per la classe: poiché ottiene la classe effettiva è stata chiamata come primo argomento, è sempre possibile creare un'istanza della classe giusta, anche quando sono coinvolte le sottoclassi. Osservare ad esempio come dict.fromkeys()
, un metodo di classe, restituisce un'istanza della sottoclasse quando viene chiamato in una sottoclasse:
>>> class DictSubclass(dict):
... def __repr__(self):
... return "DictSubclass"
...
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>>
Fondamentalmente @classmethod
crea un metodo il cui primo argomento è la classe da cui viene chiamato (piuttosto che l'istanza della classe), @staticmethod
non ha argomenti impliciti.
Documenti ufficiali di Python:
Un metodo di classe riceve la classe come primo argomento implicito, proprio come un metodo di istanza riceve l'istanza. Per dichiarare un metodo di classe, usa questo idioma:
class C: @classmethod def f(cls, arg1, arg2, ...): ...
Il
@classmethod
modulo è un decoratore di funzioni - per i dettagli vedere la descrizione delle definizioni delle funzioni in Definizioni funzioni .Può essere chiamato sulla classe (come
C.f()
) o su un'istanza (comeC().f()
). L'istanza viene ignorata ad eccezione della sua classe. Se viene chiamato un metodo di classe per una classe derivata, l'oggetto della classe derivata viene passato come primo argomento implicito.I metodi di classe sono diversi dai metodi statici C ++ o Java. Se vuoi quelli, vedi
staticmethod()
in questa sezione.
Un metodo statico non riceve un primo argomento implicito. Per dichiarare un metodo statico, utilizzare questo idioma:
class C: @staticmethod def f(arg1, arg2, ...): ...
Il
@staticmethod
modulo è un decoratore di funzioni - per i dettagli vedere la descrizione delle definizioni delle funzioni in Definizioni funzioni .Può essere chiamato sulla classe (come
C.f()
) o su un'istanza (comeC().f()
). L'istanza viene ignorata ad eccezione della sua classe.I metodi statici in Python sono simili a quelli trovati in Java o C ++. Per un concetto più avanzato, vedere
classmethod()
in questa sezione.
Ecco un breve articolo su questa domanda
La funzione @staticmethod non è altro che una funzione definita all'interno di una classe. È richiamabile senza prima istanziare la classe. La sua definizione è immutabile tramite eredità.
Anche la funzione @classmethod può essere richiamata senza creare un'istanza della classe, ma la sua definizione segue la sottoclasse, non la classe genitore, tramite ereditarietà. Questo perché il primo argomento per la funzione @classmethod deve essere sempre cls (classe).
Per decidere se usare @staticmethod o @classmethod devi guardare all'interno del tuo metodo. Se il tuo metodo accede ad altre variabili / metodi nella tua classe, usa @classmethod . D'altra parte, se il tuo metodo non tocca altre parti della classe, usa @staticmethod.
class Apple:
_counter = 0
@staticmethod
def about_apple():
print('Apple is good for you.')
# note you can still access other member of the class
# but you have to use the class instance
# which is not very nice, because you have repeat yourself
#
# For example:
# @staticmethod
# print('Number of apples have been juiced: %s' % Apple._counter)
#
# @classmethod
# print('Number of apples have been juiced: %s' % cls._counter)
#
# @classmethod is especially useful when you move your function to other class,
# you don't have to rename the class reference
@classmethod
def make_apple_juice(cls, number_of_apples):
print('Make juice:')
for i in range(number_of_apples):
cls._juice_this(i)
@classmethod
def _juice_this(cls, apple):
print('Juicing %d...' % apple)
cls._counter += 1
cls._counter
sarebbe comunque cls._counter
anche se il codice viene inserito in una classe diversa o il nome della classe viene modificato. Apple._counter
è specifico per la Apple
classe; per una classe diversa o quando il nome della classe viene modificato, è necessario modificare la classe di riferimento.
Qual è la differenza tra @staticmethod e @classmethod in Python?
Potresti aver visto il codice Python come questo pseudocodice, che dimostra le firme dei vari tipi di metodo e fornisce un docstring per spiegare ciascuno:
class Foo(object):
def a_normal_instance_method(self, arg_1, kwarg_2=None):
'''
Return a value that is a function of the instance with its
attributes, and other arguments such as arg_1 and kwarg2
'''
@staticmethod
def a_static_method(arg_0):
'''
Return a value that is a function of arg_0. It does not know the
instance or class it is called from.
'''
@classmethod
def a_class_method(cls, arg1):
'''
Return a value that is a function of the class and other arguments.
respects subclassing, it is called with the class it is called from.
'''
Prima spiegherò a_normal_instance_method
. Questo è precisamente chiamato un " metodo di istanza ". Quando viene usato un metodo di istanza, viene usato come una funzione parziale (al contrario di una funzione totale, definita per tutti i valori se visualizzati nel codice sorgente) che è, quando usato, il primo degli argomenti è predefinito come istanza del oggetto, con tutti i suoi attributi dati. Ha l'istanza dell'oggetto associato e deve essere chiamata da un'istanza dell'oggetto. In genere, accederà a vari attributi dell'istanza.
Ad esempio, questa è un'istanza di una stringa:
', '
se utilizziamo il metodo di istanza, join
su questa stringa, per unire un altro iterabile, è ovviamente una funzione dell'istanza, oltre ad essere una funzione dell'elenco iterabile ['a', 'b', 'c']
:
>>> ', '.join(['a', 'b', 'c'])
'a, b, c'
I metodi di istanza possono essere associati tramite una ricerca punteggiata per essere utilizzati in seguito.
Ad esempio, questo associa il str.join
metodo ':'
all'istanza:
>>> join_with_colons = ':'.join
E in seguito possiamo usarlo come una funzione a cui è già associato il primo argomento. In questo modo, funziona come una funzione parziale sull'istanza:
>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'
Il metodo statico non prende l'istanza come argomento.
È molto simile a una funzione a livello di modulo.
Tuttavia, una funzione a livello di modulo deve risiedere nel modulo ed essere importata appositamente in altri luoghi in cui viene utilizzata.
Se è collegato all'oggetto, tuttavia, seguirà comodamente l'oggetto anche attraverso l'importazione e l'ereditarietà.
Un esempio di metodo statico è str.maketrans
, spostato dal string
modulo in Python 3. Rende una tabella di traduzione adatta al consumo da parte di str.translate
. Sembra piuttosto sciocco se usato da un'istanza di una stringa, come dimostrato di seguito, ma importare la funzione dal string
modulo è piuttosto goffo, ed è bello poterlo chiamare dalla classe, come instr.maketrans
# demonstrate same function whether called from instance or not:
>>> ', '.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
>>> str.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
In python 2, devi importare questa funzione dal modulo stringa sempre meno utile:
>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'
Un metodo di classe è simile a un metodo di istanza in quanto accetta un primo argomento implicito, ma invece di prendere l'istanza, prende la classe. Spesso questi sono usati come costruttori alternativi per un migliore utilizzo semantico e supporteranno l'ereditarietà.
L'esempio più canonico di un metodo di classe incorporato è dict.fromkeys
. È usato come un costruttore alternativo di dict, (adatto per quando sai quali sono le tue chiavi e vuoi un valore predefinito per loro.)
>>> dict.fromkeys(['a', 'b', 'c'])
{'c': None, 'b': None, 'a': None}
Quando eseguiamo la sottoclasse di dict, possiamo usare lo stesso costruttore, che crea un'istanza della sottoclasse.
>>> class MyDict(dict): 'A dict subclass, use to demo classmethods'
>>> md = MyDict.fromkeys(['a', 'b', 'c'])
>>> md
{'a': None, 'c': None, 'b': None}
>>> type(md)
<class '__main__.MyDict'>
Vedi il codice sorgente dei panda per altri esempi simili di costruttori alternativi e vedi anche la documentazione ufficiale di Python su classmethod
e staticmethod
.
Ho iniziato a studiare il linguaggio di programmazione con C ++ e poi con Java e poi con Python e quindi anche questa domanda mi ha infastidito molto, fino a quando non ho capito il semplice utilizzo di ciascuno.
Metodo di classe: Python a differenza di Java e C ++ non ha un sovraccarico del costruttore. E quindi per raggiungere questo obiettivo potresti usare classmethod
. L'esempio seguente spiegherà questo
Consideriamo abbiamo una Person
classe che prende due argomenti first_name
e last_name
e crea l'istanza di Person
.
class Person(object):
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
Ora, se il requisito arriva dove devi creare una classe usando solo un singolo nome, solo un first_name
, non puoi fare qualcosa del genere in Python.
Questo ti darà un errore quando proverai a creare un oggetto (istanza).
class Person(object):
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
def __init__(self, first_name):
self.first_name = first_name
Tuttavia, è possibile ottenere la stessa cosa utilizzando @classmethod
come indicato di seguito
class Person(object):
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
@classmethod
def get_person(cls, first_name):
return cls(first_name, "")
Metodo statico: è piuttosto semplice, non è associato all'istanza o alla classe e puoi semplicemente chiamarlo usando il nome della classe.
Quindi diciamo nell'esempio sopra che hai bisogno di una convalida che first_name
non dovrebbe superare i 20 caratteri, puoi semplicemente farlo.
@staticmethod
def validate_name(name):
return len(name) <= 20
e potresti semplicemente chiamare usando class name
Person.validate_name("Gaurang Shah")
def __init__(self, first_name, last_name="")
al posto di classmethod get_person
. Anche il risultato sarà esattamente lo stesso in questo caso.
Penso che una domanda migliore sia "Quando useresti @classmethod vs @staticmethod?"
@classmethod ti consente di accedere facilmente ai membri privati associati alla definizione della classe. questo è un ottimo modo per creare singleton o classi factory che controllano il numero di istanze degli oggetti creati.
@staticmethod fornisce miglioramenti marginali delle prestazioni, ma devo ancora vedere un uso produttivo di un metodo statico all'interno di una classe che non può essere raggiunto come funzione autonoma al di fuori della classe.
@decorators sono stati aggiunti in python 2.4 Se stai usando python <2.4 puoi usare la funzione classmethod () e staticmethod ().
Ad esempio, se si desidera creare un metodo factory (una funzione che restituisce un'istanza di un'implementazione diversa di una classe a seconda dell'argomento che ottiene) è possibile fare qualcosa del tipo:
class Cluster(object):
def _is_cluster_for(cls, name):
"""
see if this class is the cluster with this name
this is a classmethod
"""
return cls.__name__ == name
_is_cluster_for = classmethod(_is_cluster_for)
#static method
def getCluster(name):
"""
static factory method, should be in Cluster class
returns a cluster object for the given name
"""
for cls in Cluster.__subclasses__():
if cls._is_cluster_for(name):
return cls()
getCluster = staticmethod(getCluster)
Si noti inoltre che questo è un buon esempio per l'utilizzo di un metodo di classe e di un metodo statico. Il metodo statico appartiene chiaramente alla classe, poiché utilizza internamente la classe Cluster. Il metodo class richiede solo informazioni sulla classe e nessuna istanza dell'oggetto.
Un altro vantaggio di rendere il _is_cluster_for
metodo un metodo di classe è che una sottoclasse può decidere di cambiarne l'implementazione, forse perché è piuttosto generica e può gestire più di un tipo di cluster, quindi il solo controllo del nome della classe non sarebbe sufficiente.
Metodi statici:
Vantaggi dei metodi statici:
Più conveniente da importare rispetto alle funzioni a livello di modulo poiché ogni metodo non deve essere importato in modo speciale
@staticmethod
def some_static_method(*args, **kwds):
pass
Metodi di classe:
Questi sono creati con la funzione integrata classmethod.
@classmethod
def some_class_method(cls, *args, **kwds):
pass
@staticmethod
disabilita semplicemente la funzione predefinita come descrittore del metodo. classmethod racchiude la tua funzione in un contenitore richiamabile che passa un riferimento alla classe proprietaria come primo argomento:
>>> class C(object):
... pass
...
>>> def f():
... pass
...
>>> staticmethod(f).__get__(None, C)
<function f at 0x5c1cf0>
>>> classmethod(f).__get__(None, C)
<bound method type.f of <class '__main__.C'>>
È un dato di fatto, classmethod
ha un sovraccarico di runtime ma consente di accedere alla classe proprietaria. In alternativa, consiglio di usare una metaclasse e di inserire i metodi di classe su quella metaclasse:
>>> class CMeta(type):
... def foo(cls):
... print cls
...
>>> class C(object):
... __metaclass__ = CMeta
...
>>> C.foo()
<class '__main__.C'>
c = C(); c.foo()
genera AttributeError, dovresti farlo type(c).foo()
. Questa potrebbe anche essere considerata una caratteristica: non riesco a pensare al motivo per cui vorresti farlo.
La guida definitiva su come usare metodi statici, di classe o astratti in Python è un buon collegamento per questo argomento e riassumilo come segue.
@staticmethod
La funzione non è altro che una funzione definita all'interno di una classe. È richiamabile senza prima istanziare la classe. La sua definizione è immutabile tramite eredità.
@classmethod
la funzione può anche essere richiamata senza creare un'istanza della classe, ma la sua definizione segue la sottoclasse, non la classe genitore, tramite ereditarietà, può essere sovrascritta dalla sottoclasse. Questo perché il primo argomento per la @classmethod
funzione deve essere sempre cls (classe).
Solo il primo argomento differisce :
Più in dettaglio...
Quando viene chiamato il metodo di un oggetto, viene automaticamente assegnato un ulteriore argomento self
come primo argomento. Questo è il metodo
def f(self, x, y)
deve essere chiamato con 2 argomenti. self
viene passato automaticamente ed è l'oggetto stesso .
Quando il metodo è decorato
@classmethod
def f(cls, x, y)
l'argomento fornito automaticamente non lo è self
, ma la classe di self
.
Quando il metodo è decorato
@staticmethod
def f(x, y)
al metodo non viene dato alcun argomento automatico. Vengono dati solo i parametri con cui viene chiamato.
classmethod
viene utilizzato principalmente per costruttori alternativi. staticmethod
non utilizza lo stato dell'oggetto. Potrebbe essere una funzione esterna a una classe. Inserisce solo all'interno della classe funzioni di raggruppamento con funzionalità simili (ad esempio, come Math
i metodi statici di classe Java )class Point
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def frompolar(cls, radius, angle):
"""The `cls` argument is the `Point` class itself"""
return cls(radius * cos(angle), radius * sin(angle))
@staticmethod
def angle(x, y):
"""this could be outside the class, but we put it here
just because we think it is logically related to the class."""
return atan(y, x)
p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)
angle = Point.angle(3, 2)
Lasciatemi dire la somiglianza tra un metodo decorato prima con @classmethod vs @staticmethod.
Somiglianza: entrambi possono essere chiamati sulla stessa Classe , piuttosto che solo sull'istanza della classe. Quindi, entrambi in un certo senso sono i metodi di Class .
Differenza: un metodo di classe riceverà la classe stessa come primo argomento, mentre un metodo statico no.
Quindi un metodo statico non è, in un certo senso, legato alla Classe stessa e si blocca solo perché può avere una funzionalità correlata.
>>> class Klaus:
@classmethod
def classmthd(*args):
return args
@staticmethod
def staticmthd(*args):
return args
# 1. Call classmethod without any arg
>>> Klaus.classmthd()
(__main__.Klaus,) # the class gets passed as the first argument
# 2. Call classmethod with 1 arg
>>> Klaus.classmthd('chumma')
(__main__.Klaus, 'chumma')
# 3. Call staticmethod without any arg
>>> Klaus.staticmthd()
()
# 4. Call staticmethod with 1 arg
>>> Klaus.staticmthd('chumma')
('chumma',)
Un'altra considerazione rispetto al metodo statico rispetto al metodo di classe viene fuori con l'eredità. Supponi di avere la seguente classe:
class Foo(object):
@staticmethod
def bar():
return "In Foo"
E poi vuoi eseguire l'override bar()
in una classe figlio:
class Foo2(Foo):
@staticmethod
def bar():
return "In Foo2"
Funziona, ma nota che ora l' bar()
implementazione nella classe figlio ( Foo2
) non può più sfruttare nulla di specifico per quella classe. Ad esempio, supponiamo che Foo2
abbia un metodo chiamato magic()
che desideri utilizzare Foo2
nell'implementazione di bar()
:
class Foo2(Foo):
@staticmethod
def bar():
return "In Foo2"
@staticmethod
def magic():
return "Something useful you'd like to use in bar, but now can't"
La soluzione qui sarebbe da chiamare Foo2.magic()
in bar()
, ma poi si sta ripetendo da soli (se il nome di Foo2
modifiche, si dovrà ricordarsi di aggiornare tale bar()
metodo).
Per me, questa è una leggera violazione del principio di apertura / chiusura , poiché una decisione presa Foo
influisce sulla tua capacità di refactificare il codice comune in una classe derivata (cioè è meno aperta all'estensione). Se bar()
fosse un classmethod
saremmo bene:
class Foo(object):
@classmethod
def bar(cls):
return "In Foo"
class Foo2(Foo):
@classmethod
def bar(cls):
return "In Foo2 " + cls.magic()
@classmethod
def magic(cls):
return "MAGIC"
print Foo2().bar()
dà: In Foo2 MAGIC
Proverò a spiegare la differenza di base usando un esempio.
class A(object):
x = 0
def say_hi(self):
pass
@staticmethod
def say_hi_static():
pass
@classmethod
def say_hi_class(cls):
pass
def run_self(self):
self.x += 1
print self.x # outputs 1
self.say_hi()
self.say_hi_static()
self.say_hi_class()
@staticmethod
def run_static():
print A.x # outputs 0
# A.say_hi() # wrong
A.say_hi_static()
A.say_hi_class()
@classmethod
def run_class(cls):
print cls.x # outputs 0
# cls.say_hi() # wrong
cls.say_hi_static()
cls.say_hi_class()
1 - possiamo chiamare direttamente metodi statici e di classe senza inizializzare
# A.run_self() # wrong
A.run_static()
A.run_class()
2- Il metodo statico non può chiamare il metodo self ma può chiamare altri metodi statici e di classe
3- Il metodo statico appartiene alla classe e non utilizzerà affatto l'oggetto.
4- Il metodo di classe non è associato a un oggetto ma a una classe.
@classmethod: può essere usato per creare un accesso globale condiviso a tutte le istanze create di quella classe ..... come l'aggiornamento di un record da parte di più utenti .... Ho trovato particolarmente utile utilizzare anche per la creazione di singoli ..: )
Metodo @static: non ha nulla a che fare con la classe o l'istanza a cui è associato ... ma per leggibilità può usare il metodo statico
Potresti voler considerare la differenza tra:
Class A:
def foo(): # no self parameter, no decorator
pass
e
Class B:
@staticmethod
def foo(): # no self parameter
pass
Questo è cambiato tra python2 e python3:
python2:
>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()
python3:
>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()
Quindi l'utilizzo @staticmethod
di metodi chiamati solo direttamente dalla classe è diventato opzionale in python3. Se vuoi chiamarli sia da classe che da istanza, devi comunque usare il @staticmethod
decoratore.
Gli altri casi sono stati ben coperti dalla risposta di Unutbus.
Un metodo di classe riceve la classe come primo argomento implicito, proprio come un metodo di istanza riceve l'istanza. È un metodo associato alla classe e non all'oggetto della classe. Ha accesso allo stato della classe in quanto accetta un parametro di classe che punta alla classe e non all'istanza dell'oggetto. Può modificare uno stato di classe che si applicherebbe a tutte le istanze della classe. Ad esempio, può modificare una variabile di classe che sarà applicabile a tutte le istanze.
D'altra parte, un metodo statico non riceve un primo argomento implicito, rispetto ai metodi di classe o ai metodi di istanza. E non è possibile accedere o modificare lo stato della classe. Appartiene solo alla classe perché dal punto di vista della progettazione è il modo corretto. Ma in termini di funzionalità non è legato, in fase di esecuzione, alla classe.
come linea guida, utilizzare metodi statici come utilità, utilizzare metodi di classe ad esempio come factory. O forse per definire un singleton. E usa i metodi di istanza per modellare lo stato e il comportamento delle istanze.
Spero di essere stato chiaro!
Il mio contributo dimostra la differenza tra @classmethod
, @staticmethod
ei metodi di istanza, compreso il modo di un'istanza può indirettamente chiamare un @staticmethod
. Ma invece di chiamare indirettamente un @staticmethod
da un'istanza, renderlo privato potrebbe essere più "pitone". Ottenere qualcosa da un metodo privato non è dimostrato qui, ma sostanzialmente è lo stesso concetto.
#!python3
from os import system
system('cls')
# % % % % % % % % % % % % % % % % % % % %
class DemoClass(object):
# instance methods need a class instance and
# can access the instance through 'self'
def instance_method_1(self):
return 'called from inside the instance_method_1()'
def instance_method_2(self):
# an instance outside the class indirectly calls the static_method
return self.static_method() + ' via instance_method_2()'
# class methods don't need a class instance, they can't access the
# instance (self) but they have access to the class itself via 'cls'
@classmethod
def class_method(cls):
return 'called from inside the class_method()'
# static methods don't have access to 'cls' or 'self', they work like
# regular functions but belong to the class' namespace
@staticmethod
def static_method():
return 'called from inside the static_method()'
# % % % % % % % % % % % % % % % % % % % %
# works even if the class hasn't been instantiated
print(DemoClass.class_method() + '\n')
''' called from inside the class_method() '''
# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '\n')
''' called from inside the static_method() '''
# % % % % % % % % % % % % % % % % % % % %
# >>>>> all methods types can be called on a class instance <<<<<
# instantiate the class
democlassObj = DemoClass()
# call instance_method_1()
print(democlassObj.instance_method_1() + '\n')
''' called from inside the instance_method_1() '''
# # indirectly call static_method through instance_method_2(), there's really no use
# for this since a @staticmethod can be called whether the class has been
# instantiated or not
print(democlassObj.instance_method_2() + '\n')
''' called from inside the static_method() via instance_method_2() '''
# call class_method()
print(democlassObj.class_method() + '\n')
''' called from inside the class_method() '''
# call static_method()
print(democlassObj.static_method())
''' called from inside the static_method() '''
"""
# whether the class is instantiated or not, this doesn't work
print(DemoClass.instance_method_1() + '\n')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""
I metodi di classe, come suggerisce il nome, vengono utilizzati per apportare modifiche alle classi e non agli oggetti. Per apportare modifiche alle classi, modificheranno gli attributi della classe (non gli attributi dell'oggetto), poiché è così che aggiorni le classi. Questo è il motivo per cui i metodi di classe prendono la classe (convenzionalmente indicata da 'cls') come primo argomento.
class A(object):
m=54
@classmethod
def class_method(cls):
print "m is %d" % cls.m
D'altra parte, i metodi statici vengono utilizzati per eseguire funzionalità che non sono legate alla classe, ovvero non leggeranno o scriveranno variabili di classe. Pertanto, i metodi statici non accettano le classi come argomenti. Sono utilizzati in modo che le classi possano eseguire funzionalità che non sono direttamente correlate allo scopo della classe.
class X(object):
m=54 #will not be referenced
@staticmethod
def static_method():
print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."
Analizza @staticmethod letteralmente fornendo diverse intuizioni.
Un metodo normale di una classe è un metodo dinamico implicito che prende l'istanza come primo argomento.
Al contrario, un metodo statico non considera l'istanza come primo argomento, quindi viene chiamato "statico" .
Un metodo statico è in effetti una funzione così normale come quella esterna a una definizione di classe.
Fortunatamente è raggruppato nella classe solo per avvicinarsi al punto in cui viene applicato, oppure puoi scorrere per trovarlo.
Penso che dare una versione puramente Python di staticmethod
eclassmethod
aiuterebbe a capire la differenza tra loro a livello linguistico.
Entrambi sono descrittori non di dati (sarebbe più facile capirli se si conoscono prima i descrittori ).
class StaticMethod(object):
"Emulate PyStaticMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, objtype=None):
return self.f
class ClassMethod(object):
"Emulate PyClassMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, cls=None):
def inner(*args, **kwargs):
if cls is None:
cls = type(obj)
return self.f(cls, *args, **kwargs)
return inner
staticmethod non ha accesso agli attibutes dell'oggetto, della classe o delle classi parent nella gerarchia dell'ereditarietà. Può essere chiamato direttamente nella classe (senza creare un oggetto).
classmethod non ha accesso agli attributi dell'oggetto. Tuttavia, può accedere agli attributi della classe e delle classi padre nella gerarchia dell'ereditarietà. Può essere chiamato direttamente nella classe (senza creare un oggetto). Se chiamato sull'oggetto, è uguale al metodo normale che non accede self.<attribute(s)>
e accedeself.__class__.<attribute(s)>
solo.
Pensa di avere una classe con b=2
, creeremo un oggetto e reimposteremo questo b=4
in esso. Il metodo statico non può accedere a nulla di precedente. Classmethod può accedere .b==2
solo, tramite cls.b
. Il metodo normale può accedere sia a: .b==4
via self.b
che .b==2
via self.__class__.b
.
Potremmo seguire lo stile KISS (mantenerlo semplice, stupido): non usare metodi statici e metodi di classe, non usare le classi senza istanziarle, accedere solo agli attributi dell'oggetto self.attribute(s)
. Ci sono lingue in cui OOP è implementato in quel modo e penso che non sia una cattiva idea. :)
Un rapido hacking di metodi ugualmente identici in iPython rivela che @staticmethod
produce guadagni di prestazioni marginali (nei nanosecondi), ma per il resto sembra non avere alcuna funzione. Inoltre, eventuali guadagni in termini di prestazioni saranno probabilmente spazzati via dall'ulteriore lavoro di elaborazione del metodo staticmethod()
durante la compilazione (che avviene prima di qualsiasi esecuzione del codice quando si esegue uno script).
Per motivi di leggibilità del codice, eviterei a @staticmethod
meno che il tuo metodo non venga utilizzato per un sacco di lavoro, dove contano i nanosecondi.