Domanda

Qual è la differenza tra una funzione e decorate con @staticmethod e decorato con @classmethod?

È stato utile?

Soluzione

Forse un po 'di codice di esempio aiuterà: nota 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 è 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 A.foo(1) usando la classe. In effetti, se definisci qualcosa che deve essere un metodo di classe, è probabilmente perché si intende chiamarlo dalla classe piuttosto che da un'istanza di classe. A.class_foo(1) avrebbe sollevato un TypeError, ma cls 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 è creare costruttori alternativi ereditabili .


Con staticmethods , né a.foo (l'istanza dell'oggetto) né a.class_foo (la classe) viene passato 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.


A è solo una funzione, ma quando chiami a.static_foo non ottieni solo la funzione, ottieni un " parzialmente applicato " versione della funzione con l'istanza dell'oggetto <=> associata come primo argomento alla funzione. <=> prevede 2 argomenti, mentre <=> prevede solo 1 argomento.

<=> è associato a <=>. Questo è ciò che si intende con il termine & Quot; bound & Quot; qui di seguito:

print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>

Con <=>, <=> non è associato a <=>, ma la classe <=> è associata a <=>.

print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>

Qui, con un metodo statico, anche se è un metodo, <=> restituisce semplicemente una buona funzione ole senza argomenti associati. <=> prevede 1 argomento e <=> prevede anche 1 argomento.

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

E ovviamente la stessa cosa accade quando si chiama <=> con la classe <=> invece.

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

Altri suggerimenti

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 viene passato 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. Ad esempio, osserva 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 su Python:

@classmethod

  

Un metodo di classe riceve la classe come   primo argomento implicito, proprio come un   il metodo di istanza riceve l'istanza.   Per dichiarare un metodo di classe, utilizzare questo   linguaggio:

class C:
    @classmethod
    def f(cls, arg1, arg2, ...): ... 
     

Il modulo @classmethod è una funzione    decoratore & # 8211; vedere la descrizione di   definizioni delle funzioni in Funzione   definizioni per i dettagli.

     

Può essere chiamato sulla classe   (come C.f()) o su un'istanza   (come C().f()). L'istanza è   ignorato tranne per la sua classe. Se una   viene chiamato un metodo di classe per un derivato   classe, l'oggetto della classe derivata è   passato come primo argomento implicito.

     

I metodi di classe sono diversi da C ++   o metodi statici Java. Se vuoi   quelli, vedi staticmethod() in questo   sezione.

@staticmethod

  

Un metodo statico non riceve un   primo argomento implicito. Per dichiarare a   metodo statico, usa questo idioma:

class C:
    @staticmethod
    def f(arg1, arg2, ...): ... 
     

Il modulo @staticmethod è una funzione    decoratore & # 8211; vedere la descrizione di   definizioni delle funzioni in Funzione   definizioni per i dettagli.

     

Può essere chiamato sulla classe   (come classmethod()) o su un'istanza   (come <=>). L'istanza è   ignorato ad eccezione della sua classe.

     

I metodi statici in Python sono simili   a quelli trovati in Java o C ++. Per un   concetto più avanzato, vedi    <=> in questa sezione.

Qui è un breve articolo su questo domanda

  

La funzione @staticmethod non è altro che una funzione definita all'interno di una classe. È richiamabile senza prima istanziare la classe. La definizione di & # 8217; è immutabile tramite ereditarietà.

     

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à. Quello & # 8217; s perché il primo argomento per la funzione @classmethod deve essere sempre cls (classe).

Per decidere se utilizzare @staticmethod o @classmethod devi guardare all'interno del tuo metodo. Se il tuo metodo accede ad altre variabili / metodi nella tua classe, utilizza @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
  

Qual è la differenza tra @staticmethod e @classmethod in Python?

Potresti aver visto il codice Python come questo pseudocodice, che mostra le firme dei vari tipi di metodo e fornisce una documentazione per spiegare ciascuno di essi:

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 & Quot; metodo di istanza & Quot ;. 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 dell'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'

Metodi associati

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

Ad esempio, questo vincola il metodo str.join 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 appositamente importata 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 modulo string in Python 3. Rende una tabella di traduzione adatta al consumo di str.translate. Sembra piuttosto sciocco se usato da un'istanza di una stringa, come dimostrato di seguito, ma importare la funzione dal modulo dict.fromkeys è piuttosto goffo ed è bello poterlo chiamare dalla classe, come in classmethod

# 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 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 è staticmethod. È 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 panda per altri simili esempi di costruttori alternativi e vedere anche la documentazione ufficiale di Python su <=> e <=> .

Ho iniziato ad imparare il linguaggio di programmazione con C ++ e poi Java e poi Python e quindi 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 che abbiamo una Person classe che accetta due argomenti first_name e last_name e crea l'istanza di @classmethod.

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 class 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, puoi ottenere lo stesso risultato utilizzando <=> 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: questo è piuttosto semplice, non è legato 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 <=> non dovrebbe superare i 20 caratteri, puoi semplicemente farlo.

@staticmethod  
def validate_name(name):
    return len(name) <= 20

e puoi semplicemente chiamare usando <=>

Person.validate_name("Gaurang Shah")

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

Inoltre, osserva 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 metodo _is_cluster_for un metodo di classe è che una sottoclasse può decidere di cambiare la sua implementazione, forse perché è piuttosto generica e può gestire più di un tipo di cluster, quindi solo controllando il nome della classe sarebbe non essere abbastanza.

Penso che una domanda migliore è: "Quando vuoi usare il @classmethod vs @staticmethod?"

@classmethod consente un facile accesso ai membri privati che sono associati alla definizione di classe.questo è un ottimo modo per fare singleton, o la fabbrica di classi in grado di controllare il numero di istanze di oggetti creati esiste.

@staticmethod fornisce marginale guadagni in prestazioni, ma devo ancora vedere un uso produttivo di un metodo statico di una classe che non poteva essere raggiunto come funzione autonoma, al di fuori della classe.

Metodi statici:

  • Funzioni semplici senza argomento personale.
  • Lavora sugli attributi di classe; non sugli attributi dell'istanza.
  • Può essere chiamato sia tramite classe che 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 appositamente

    @staticmethod
    def some_static_method(*args, **kwds):
        pass
    

Metodi di classe:

  • Funzioni che hanno il primo argomento come classname.
  • Può essere chiamato sia tramite classe che istanza.
  • 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'>

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

La funzione

@staticmethod non è altro che una funzione definita all'interno di una classe. È richiamabile senza prima istanziare la classe. La definizione di & # 8217; è immutabile tramite ereditarietà.

  • Python non deve creare un'istanza di un metodo associato per l'oggetto.
  • Semplifica la leggibilità del codice e non dipende dallo stato dell'oggetto stesso;
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à, può essere ignorata dalla sottoclasse. Che & # 8217; s perché il primo argomento per la funzione <=> 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 si divide un metodo statico in diversi metodi statici, non è necessario codificare il nome della classe ma utilizzare i metodi di classe

Lasciatemi dire la somiglianza tra un metodo decorato prima con @classmethod vs @staticmethod.

Somiglianza: entrambi possono essere chiamati sulla classe stessa, piuttosto che sulla istanza della classe. Quindi, entrambi in un certo senso sono metodi di classe .

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 lì perché potrebbe 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 a metodo statico vs 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 sovrascrivere bar() in una classe figlio:

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"

Funziona, ma nota che ora l'implementazione Foo2 nella classe figlio (magic()) non può più sfruttare nulla di specifico per quella classe. Ad esempio, supponiamo che Foo2.magic() avesse un metodo chiamato Foo che si desidera utilizzare nell'implementazione classmethod di In Foo2 MAGIC:

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 alternativa sarebbe quella di chiamare <=> in <=>, ma poi ti stai ripetendo (se il nome di <=> cambia, dovrai ricordarti di aggiornare quel metodo <=>).

Per me, questa è una leggera violazione del principio aperto / chiuso , poiché una decisione presa in <=> sta influenzando la tua capacità di refactificare il codice comune in una classe derivata (cioè è meno aperta all'estensione). Se <=> fosse un <=> staremmo 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à: <=>

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 class

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 aggiornare un record da più utenti .... In particolare l'ho trovato utile anche durante 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

Il mio contributo dimostra la differenza tra @classmethod, @staticmethod e i metodi di istanza, incluso il modo in cui un'istanza può chiamare indirettamente un <=>. Ma invece di chiamare indirettamente un <=> da un'istanza, renderlo privato potrebbe essere più & Quot; pythonic. & Quot; 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

I metodi statici, d'altra parte, sono usati per eseguire funzionalità che non sono legate alla classe, cioè non leggeranno o scriveranno variabili di classe. Pertanto, i metodi statici non accettano le classi come argomenti. Vengono 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."

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'uso di @staticmethod per i metodi chiamati solo direttamente dalla classe è diventato facoltativo in python3. Se vuoi chiamarli sia da classe che da istanza, devi comunque usare <=> decoratore.

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

Analizza @staticmethod letteralmente fornendo informazioni diverse.

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 al di fuori di una definizione di classe.
Fortunatamente è raggruppato nella classe solo per avvicinarsi al punto in cui viene applicato, oppure è possibile scorrere per trovarlo.

Risposta breve. Il primo argomento:

  • metodo normale: il primo argomento è l'oggetto corrente
  • classmethod: il primo argomento è la classe dell'oggetto corrente
  • metodo statico: il primo argomento è stato rimosso

Risposta più lunga:

metodo normale

Quando viene chiamato il metodo di un oggetto, viene automaticamente assegnato un ulteriore argomento self come primo argomento. Cioè, metodo

def f(self, x, y)

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

metodo di classe

Quando il metodo è decorato

@classmethod
def f(cls, x, y)

l'argomento fornito automaticamente non è staticmethod, ma la classe di Math.

metodo statico

Quando il metodo è decorato

@staticmethod
def f(x, y)

al metodo non viene dato nessun argomento automatico. Vengono dati solo i parametri con cui viene chiamato.

usi

  • <=> viene utilizzato principalmente per costruttori alternativi.
  • <=> 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 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):
        return atan(y, x)


p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)

angle = Point.angle(3, 2)

Un rapido hacking di metodi identici in iPython rivela che @staticmethod produce miglioramenti marginali delle prestazioni (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 attraverso staticmethod() durante la compilazione (che si verifica prima di qualsiasi esecuzione del codice quando si esegue uno script).

Per motivi di leggibilità del codice, eviterei <=> a meno che il tuo metodo non venga utilizzato per un sacco di lavoro, dove contano i nanosecondi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top