Differenza tra metodo statico e metodo di classe


3583

Qual è la differenza tra una funzione decorata con @staticmethode una decorata con @classmethod?


11
i metodi statici a volte sono meglio come funzioni a livello di modulo in Python per motivi di pulizia. Con una funzione del modulo è più facile importare solo la funzione che ti serve e prevenire inutili "." sintassi (ti sto guardando Objective-C). i metodi di classe hanno un uso maggiore poiché possono essere usati in combinazione con il polimorfismo per creare funzioni di "modello di fabbrica". questo perché i metodi di classe ricevono la classe come parametro implicito.
FistOfFury il

27
tl; dr >> rispetto ai metodi normali, è possibile accedere anche ai metodi statici e ai metodi di classe utilizzando la classe, ma a differenza dei metodi di classe, i metodi statici sono immutabili tramite ereditarietà.
imsrgadich,


più preciso youtube.com/watch?v=HTLu2DFOdTg&feature=youtu.be&t=2689 hai bisogno solo di un metodo di classe per costruttori alternativi. Altrimenti puoi usare il metodo statico e accedere a qualsiasi attributo di classe (via. / Punto) dall'attuale "CLASSNAME" in qualche modo più informativo invece di cls come nel metodo di classe
Robert Nowak,

Risposte:


3138

Forse un po 'di codice di esempio aiuterà: notare la differenza nelle firme di chiamata di foo, class_fooe 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_foousando 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.foonon ottieni solo la funzione, ottieni una versione "parzialmente applicata" della funzione con l'istanza dell'oggetto aassociata come primo argomento alla funzione. fooprevede 2 argomenti, mentre a.fooprevede 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, anon è 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_foorestituisce solo una buona funzione ole senza argomenti associati. static_foosi aspetta 1 argomento e si a.static_fooaspetta anche 1 argomento.

print(a.static_foo)
# <function static_foo at 0xb7d479cc>

E ovviamente la stessa cosa accade quando chiami invece static_foocon la classe A.

print(A.static_foo)
# <function static_foo at 0xb7d479cc>

182
Non capisco qual è il trucco per l'utilizzo di staticmethod. possiamo semplicemente usare una semplice funzione fuori dalla classe.
Alcott,

372
@Alcott: potresti voler spostare una funzione in una classe perché appartiene logicamente alla classe. Nel codice sorgente di Python (ad es. Multiprocessing, turtle, dist-package), viene utilizzato per "nascondere" funzioni "private" di sottolineatura singola dallo spazio dei nomi del modulo. Il suo utilizzo, tuttavia, è altamente concentrato in pochi moduli, forse un'indicazione che è principalmente una cosa stilistica. Anche se non sono riuscito a trovare alcun esempio di ciò, @staticmethodpotrebbe aiutare a organizzare il codice essendo sovrascrivibile dalle sottoclassi. Senza di esso avresti varianti della funzione fluttuante nello spazio dei nomi del modulo.
unutbu,

15
... insieme ad alcune spiegazioni su dove e perché usare metodi di istanza, classe o statici. Non hai dato una sola parola a riguardo, ma nemmeno l'OP ha chiesto a riguardo.
MestreLion,

106
@Alcott: come ha detto unutbu, i metodi statici sono una caratteristica organizzativa / stilistica. A volte un modulo ha molte classi e alcune funzioni di supporto sono logicamente legate a una determinata classe e non alle altre, quindi ha senso non "inquinare" il modulo con molte "funzioni libere", ed è meglio usare una statica metodo che fare affidamento sul cattivo stile di mescolare classi e funzioni def insieme nel codice solo per mostrare che sono "correlate"
MestreLion

4
Un altro uso per @staticmethod: puoi usarlo per rimuovere l'innesto. Sto implementando un linguaggio di programmazione in Python - le funzioni definite dalla libreria usano un executemetodo 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.
Tehwalrus,

799

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
>>> 

715
Un metodo statico non è inutile: è un modo per inserire una funzione in una classe (perché appartiene logicamente lì), mentre indica che non richiede l'accesso alla classe.
Tony Meyer,

136
Quindi solo 'sostanzialmente' inutile. Tale organizzazione, così come l'iniezione di dipendenza, sono usi validi dei metodi statici, ma poiché i moduli, non le classi come in Java, sono gli elementi base dell'organizzazione del codice in Python, il loro uso e utilità sono rari.
Thomas Wouters,

40
Cosa c'è di logico nella definizione di un metodo all'interno di una classe, quando non ha nulla a che fare con la classe o le sue istanze?
Ben James,

107
Forse per l'eredità? I metodi statici possono essere ereditati e sovrascritti proprio come i metodi di istanza e i metodi di classe e la ricerca funziona come previsto (diversamente da Java). I metodi statici non vengono realmente risolti staticamente se chiamati sulla classe o sull'istanza, quindi l'unica differenza tra metodi di classe e statici è il primo argomento implicito.
haridsv,

80
Creano anche uno spazio dei nomi più pulito e facilita la comprensione della funzione che ha a che fare con la classe.
Imbrondir,

149

Fondamentalmente @classmethodcrea un metodo il cui primo argomento è la classe da cui viene chiamato (piuttosto che l'istanza della classe), @staticmethodnon ha argomenti impliciti.


103

Documenti ufficiali di Python:

@classmethod

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 @classmethodmodulo è 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 (come C().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.

@staticmethod

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 @staticmethodmodulo è 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 (come C().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.


Non c'è un errore nei documenti? Non dovrei essere su staticmethod: "L'istanza e la sua classe sono entrambe ignorate." invece di "L'istanza viene ignorata ad eccezione della sua classe."?
Mirek,

Potrebbe trattarsi di un errore "taglia e incolla", ma a rigore non è possibile chiamare un metodo su una classe se si ignora la classe.
Aaron Bentley

76

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).


1
Quindi questo significa che usando un metodo statico sono sempre legato alla classe Parent e con il metodo class sono legato alla classe in cui dichiaro il metodo class (in questo caso la sottoclasse)?
Mohan Gulati,

7
No. Usando un metodo statico non sei affatto vincolato; non esiste un primo parametro implicito. Usando classmethod, ottieni come primo parametro implicito la classe su cui hai chiamato il metodo (se l'hai chiamato direttamente su una classe) o la classe dell'istanza su cui hai chiamato il metodo (se l'hai chiamato su un'istanza).
Matt Anderson,

6
Potrebbe essere espanso un po 'per mostrare che, avendo una classe come primo argomento, i metodi di classe hanno accesso diretto ad altri attributi e metodi di classe, mentre i metodi statici no (dovrebbero codificare MyClass.attr per quello)
MestreLion

"La sua definizione è immutabile tramite eredità." non ha alcun senso in Python, puoi scavalcare un metodo statico bene.
CZ

68

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

quale sarebbe il vantaggio di classmethod e cls._counter rispetto a staticmethod e Apple._counter
Robert Nowak,

1
cls._countersarebbe comunque cls._counteranche se il codice viene inserito in una classe diversa o il nome della classe viene modificato. Apple._counterè specifico per la Appleclasse; per una classe diversa o quando il nome della classe viene modificato, è necessario modificare la classe di riferimento.
kiamlaluno,

53

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.
        '''

Il metodo dell'istanza normale

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, joinsu 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'

Metodi associati

I metodi di istanza possono essere associati tramite una ricerca punteggiata per essere utilizzati in seguito.

Ad esempio, questo associa il str.joinmetodo ':'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'

Metodo statico

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 stringmodulo 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 stringmodulo è 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'

Metodo di classe

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 classmethode staticmethod.


43

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 Personclasse che prende due argomenti first_namee last_namee 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 @classmethodcome 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")

2
Si tratta di un vecchio post, ma il modo più divinatorio per raggiungere costruttore accettare uno o due argomenti sarebbero utilizzando def __init__(self, first_name, last_name="")al posto di classmethod get_person. Anche il risultato sarà esattamente lo stesso in questo caso.
akarilimano,

31

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.


31

@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_formetodo 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.


28

Metodi statici:

  • Funzioni semplici senza argomento personale.
  • Lavora sugli attributi di classe; non sugli attributi dell'istanza.
  • Può essere chiamato sia in classe che in istanza.
  • La funzione integrata staticmethod () viene utilizzata per crearli.

Vantaggi dei metodi statici:

  • Localizza il nome della funzione nel classscope
  • Sposta il codice funzione più vicino a dove viene utilizzato
  • 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:

  • Funzioni che hanno il primo argomento come classname.
  • Può essere chiamato sia in classe che in istanza.
  • Questi sono creati con la funzione integrata classmethod.

     @classmethod
     def some_class_method(cls, *args, **kwds):
         pass

22

@staticmethoddisabilita 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, classmethodha 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'>

1
Un possibile aspetto negativo di una metaclasse per questo che mi viene subito in mente è che non puoi chiamare il metodo class direttamente su un'istanza. 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.
Aaron Hall

20

La guida definitiva su come usare metodi statici, di classe o astratti in Python è un buon collegamento per questo argomento e riassumilo come segue.

@staticmethodLa funzione non è altro che una funzione definita all'interno di una classe. È richiamabile senza prima istanziare la classe. La sua definizione è immutabile tramite eredità.

  • Python non deve istanziare un metodo associato per l'oggetto.
  • Facilita la leggibilità del codice e non dipende dallo stato dell'oggetto stesso;

@classmethodla 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 @classmethodfunzione deve essere sempre cls (classe).

  • Metodi di fabbrica , utilizzati per creare un'istanza per una classe utilizzando ad esempio una sorta di pre-elaborazione.
  • Metodi statici che chiamano metodi statici : se dividi un metodo statico in diversi metodi statici, non dovresti codificare il nome della classe ma usare metodi di classe

Grazie @zangw - l'immutabilità ereditata della funzione statica è la differenza fondamentale che sembra
hard_working_ant

18

Solo il primo argomento differisce :

  • metodo normale: l'oggetto corrente se passato automaticamente come primo argomento (aggiuntivo)
  • classmethod: la classe dell'oggetto corrente viene automaticamente passata come argomento di pugno (aggiuntivo)
  • staticmethod: nessun argomento aggiuntivo viene passato automaticamente. Quello che hai passato alla funzione è quello che ottieni.

Più in dettaglio...

metodo normale

Quando viene chiamato il metodo di un oggetto, viene automaticamente assegnato un ulteriore argomento selfcome primo argomento. Questo è il metodo

def f(self, x, y)

deve essere chiamato con 2 argomenti. selfviene passato automaticamente ed è l'oggetto stesso .

metodo di classe

Quando il metodo è decorato

@classmethod
def f(cls, x, y)

l'argomento fornito automaticamente non lo è self , ma la classe di self .

metodo statico

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.

usi

  • classmethod viene utilizzato principalmente per costruttori alternativi.
  • staticmethodnon 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 Mathi 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)

17

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',)

11

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 Foo2abbia un metodo chiamato magic()che desideri utilizzare Foo2nell'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 Foo2modifiche, si dovrà ricordarsi di aggiornare tale bar()metodo).

Per me, questa è una leggera violazione del principio di apertura / chiusura , poiché una decisione presa Fooinfluisce sulla tua capacità di refactificare il codice comune in una classe derivata (cioè è meno aperta all'estensione). Se bar()fosse un classmethodsaremmo 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


7

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.


annuncio 2: sei sicuro? Come può il metodo statico chiamare il metodo di classe? Non ha riferimenti ad esso (alla sua classe).
Mirek,

7

@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


5

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 @staticmethoddi metodi chiamati solo direttamente dalla classe è diventato opzionale in python3. Se vuoi chiamarli sia da classe che da istanza, devi comunque usare il @staticmethoddecoratore.

Gli altri casi sono stati ben coperti dalla risposta di Unutbus.


5

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!


4

Il mio contributo dimostra la differenza tra @classmethod, @staticmethodei metodi di istanza, compreso il modo di un'istanza può indirettamente chiamare un @staticmethod. Ma invece di chiamare indirettamente un @staticmethodda 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)
'''
"""

2

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."

2

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.


2

Penso che dare una versione puramente Python di staticmethodeclassmethod 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

1

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=4in esso. Il metodo statico non può accedere a nulla di precedente. Classmethod può accedere .b==2solo, tramite cls.b. Il metodo normale può accedere sia a: .b==4via self.bche .b==2via 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. :)


Una cosa importante in più per i metodi di classe: se si modifica un attributo nel metodo di classe, tutti gli oggetti esistenti di questa classe che non impostano esplicitamente questo attributo avranno il valore modificato.
Mirek,

-4

Un rapido hacking di metodi ugualmente identici in iPython rivela che @staticmethodproduce 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 @staticmethodmeno che il tuo metodo non venga utilizzato per un sacco di lavoro, dove contano i nanosecondi.


7
"Altrimenti sembra non avere alcuna funzione": non strettamente vero. Vedi la discussione sopra.
Keith Pinson,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.