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


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


2669
2017-09-25 21:01


origine


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

Sotto è il solito modo in cui un'istanza dell'oggetto chiama un metodo. L'istanza dell'oggetto, a, è implicitamente passato come primo argomento.

a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)

Con metodi di classe, la classe dell'istanza dell'oggetto viene passata implicitamente come primo argomento invece di self.

a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Puoi anche chiamare class_foo usando la classe. In effetti, se tu definisci qualcosa per essere un metodo di classe, è probabilmente perché si intende chiamarlo dalla classe piuttosto che da un'istanza di classe. A.foo(1) avrebbe sollevato un errore TypeE, ma A.class_foo(1) funziona bene:

A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Un uso che la gente ha trovato per i metodi di classe è quello di creare costruttori alternativi ereditabili.


Con metodi statici, nessuno dei due self (l'istanza dell'oggetto) né cls (la classe) è passata implicitamente come primo argomento. Si comportano come semplici funzioni, tranne 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 le funzioni che presentano una connessione logica con una classe alla classe.


foo è solo una funzione, ma quando chiami a.foo non si ottiene solo la funzione, ottieni una versione "parzialmente applicata" della funzione con l'istanza dell'oggetto a associato come primo argomento alla funzione. foo si aspetta 2 argomenti, mentre a.foo si aspetta solo 1 argomento.

a è legato a foo. Questo è ciò che si intende per "legato" sotto:

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

Con a.class_foo, a non è vincolato a class_foo, piuttosto la classe A è legato a class_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 ritorna una buona 'vecchia funzione senza argomenti vincolati. static_foo si aspetta 1 argomento, e a.static_foo si aspetta anche 1 argomento.

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

E naturalmente la stessa cosa succede quando chiami static_foo con la classe A anziché.

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

2315
2017-11-03 19:13



UN staticmethod è un metodo che non conosce la classe o l'istanza su cui è stato chiamato. Ottiene solo gli argomenti che sono stati passati, nessun primo argomento implicito. Fondamentalmente è inutile in Python: puoi semplicemente usare una funzione modulo invece di un metodo statico.

UN classmethod, 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 una factory per la classe: poiché ottiene la classe effettiva su cui è stato chiamato come primo argomento, è sempre possibile creare un'istanza della classe corretta, anche quando sono coinvolte sottoclassi. Osserva per esempio come dict.fromkeys(), un metodo class, restituisce un'istanza della sottoclasse quando viene chiamata su una sottoclasse:

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
... 
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>> 

654
2017-09-25 21:05



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


110
2017-09-25 21:07



Documenti Python ufficiali:

@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, usa questo   idioma:

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

Il @classmethod la forma è una funzione    decoratore - guarda la descrizione di   definizioni di funzioni in Funzione   definizioni per dettagli.

Può essere chiamato sia sulla classe   (ad esempio C.f()) o su un'istanza   (ad esempio C().f()). L'istanza è   ignorato tranne che per la sua classe. Se un   il metodo di classe è chiamato per un derivato   classe, l'oggetto classe derivato è   passato come primo argomento implicito.

I metodi di classe sono diversi dal 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 @staticmethod la forma è una funzione    decoratore - guarda la descrizione di   definizioni di funzioni in Funzione   definizioni per dettagli.

Può essere chiamato sia sulla classe   (ad esempio C.f()) o su un'istanza   (ad esempio C().f()). L'istanza è   ignorato tranne che per la sua classe.

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


76
2017-11-03 19:23



Qui è un breve articolo su questa domanda

La funzione @staticmethod non è altro che una funzione definita all'interno di una classe. È callabile senza prima istanziare la classe. La sua definizione è immutabile per via ereditaria.

La funzione @classmethod può anche essere richiamata senza creare un'istanza della classe, ma la sua definizione segue la classe Sub, non la classe Parent, tramite l'ereditarietà. Questo perché il primo argomento per la funzione @classmethod deve sempre essere cls (classe).


55
2017-11-03 19:02



Per decidere se usare @staticmethod o @classmethod devi guardare dentro il 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, allora 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

44
2018-04-22 15:40



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 una 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 di istanza normale

Prima spiegherò a_normal_instance_method. Questo è precisamente chiamato "metodo di istanzaQuando viene usato un metodo di istanza, esso viene usato come una funzione parziale (al contrario di una funzione totale, definita per tutti i valori quando viene visualizzata nel codice sorgente), cioè, quando viene utilizzato, il primo degli argomenti è predefinito come istanza dell'oggetto, con tutti i suoi attributi specificati: ha l'istanza dell'oggetto ad esso associato e deve essere richiamato da un'istanza dell'oggetto. In genere, accederà a vari attributi dell'istanza.

Ad esempio, questa è un'istanza di una stringa:

', '

se usiamo 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'

Metodi rilegati

I metodi di istanza possono essere associati tramite una ricerca punteggiata da utilizzare in un secondo momento.

Ad esempio, questo lega il str.join metodo per il ':' esempio:

>>> join_with_colons = ':'.join 

E più tardi possiamo usare questo come funzione che ha già il primo argomento associato ad esso. 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 lo fa non prendi l'istanza come argomento.

È molto simile a una funzione a livello di modulo.

Tuttavia, una funzione di livello modulo deve vivere nel modulo ed essere importata in modo speciale in altri luoghi in cui viene utilizzata.

Se è collegato all'oggetto, tuttavia, seguirà l'oggetto comodamente attraverso l'importazione e l'ereditarietà.

È un esempio di un metodo statico str.maketrans, spostato dal string modulo 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 importando la funzione dal file string il modulo è piuttosto goffo ed è bello poterlo chiamare dalla classe, come in str.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 di 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, accetta 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 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'>

Vedere il codice sorgente panda per altri esempi simili di costruttori alternativi, e vedere anche la documentazione ufficiale su Python classmethod e staticmethod.


38
2018-01-23 20:01



@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 in base all'argomento ottenuto), è possibile fare qualcosa come:

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 classmethod e statico, Il metodo statico appartiene chiaramente alla classe, poiché utilizza internamente la classe Cluster. Il metodo class ha solo bisogno di informazioni sulla classe e nessuna istanza dell'oggetto.

Un altro vantaggio di rendere il _is_cluster_for metodo un metodo di classe è così una sottoclasse può decidere di cambiare la sua implementazione, forse perché è piuttosto generica e può gestire più di un tipo di cluster, quindi non basta controllare il nome della classe.


27
2018-02-24 09:32