Metodi statici in Python?


1719

È possibile avere metodi statici in Python che potrei chiamare senza inizializzare una classe, come:

ClassName.static_method()

Risposte:


2026

Sì, usando il decoratore di metodi statici

class MyClass(object):
    @staticmethod
    def the_static_method(x):
        print(x)

MyClass.the_static_method(2)  # outputs 2

Si noti che alcuni codici potrebbero utilizzare il vecchio metodo di definizione di un metodo statico, utilizzando staticmethodcome funzione anziché come decoratore. Questo dovrebbe essere usato solo se devi supportare le versioni antiche di Python (2.2 e 2.3)

class MyClass(object):
    def the_static_method(x):
        print(x)
    the_static_method = staticmethod(the_static_method)

MyClass.the_static_method(2)  # outputs 2

Questo è del tutto identico al primo esempio (usando @staticmethod), semplicemente non usando la bella sintassi del decoratore

Infine, usa staticmethod()con parsimonia! Ci sono pochissime situazioni in cui i metodi statici sono necessari in Python e li ho visti usati molte volte in cui una funzione separata di "livello superiore" sarebbe stata più chiara.


Quanto segue è letteralmente dalla documentazione :

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 modulo @staticmethod è un decoratore di funzioni - per i dettagli vedere la descrizione delle definizioni delle funzioni nelle Definizioni delle 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, vedi classmethod().

Per ulteriori informazioni sui metodi statici, consultare la documentazione sulla gerarchia dei tipi standard in Gerarchia dei tipi standard .

Novità nella versione 2.2.

Modificato nella versione 2.4: aggiunta la sintassi del decoratore di funzioni.


15
Perché aggiungere @staticmethoddecorazioni o usare un puntatore a funzione, quando puoi semplicemente evitare il primo selfparametro? Bene, per un oggetto a, non sarai in grado di chiamare a.your_static_method(), il che è consentito in altre lingue, ma è comunque considerato una cattiva pratica e il compilatore ti avvisa sempre di questo
SomethingSomething

1
Sto usando Python 2.7.12, con @staticmethod decorator non riesco a chiamare il primo metodo statico definito. Il suo errore givin: TypeError: l'oggetto 'staticmethod' non è richiamabile
Supratim Samantray,

Supratim Samantray, sei sicuro? perché qui si afferma chiaramente che può essere utilizzato dopo la 2.4.
realslimshanky,

11
@SomethingSomething, se non si utilizza il nome della variabile "self", ma non si aggiunge il decoratore, il primo argomento, ad esempio arg1, rimarrà comunque il riferimento all'istanza self. Il nome self è solo una convenzione.
George Moutsopoulos,

1
@GeorgeMoutsopoulos - generalmente d'accordo. Ma - sarai comunque in grado di chiamare il metodo come ClassName.methodName(), come se fosse statico, e quindi non selfverrà fornito il metodo. Come hai detto, sarà comunque possibile chiamare questo metodo come ClassInstance.methodName()e selfverrà fornito come primo parametro, indipendentemente dal nome.
Qualcosa del

220

Penso che Steven abbia effettivamente ragione . Per rispondere alla domanda originale, quindi, al fine di impostare un metodo di classe, supponi semplicemente che il primo argomento non sarà un'istanza chiamante, quindi assicurati di chiamare solo il metodo dalla classe.

(Nota che questa risposta si riferisce a Python 3.x. In Python 2.x otterrai un TypeErrorper chiamare il metodo sulla classe stessa.)

Per esempio:

class Dog:
    count = 0 # this is a class variable
    dogs = [] # this is a class variable

    def __init__(self, name):
        self.name = name #self.name is an instance variable
        Dog.count += 1
        Dog.dogs.append(name)

    def bark(self, n): # this is an instance method
        print("{} says: {}".format(self.name, "woof! " * n))

    def rollCall(n): #this is implicitly a class method (see comments below)
        print("There are {} dogs.".format(Dog.count))
        if n >= len(Dog.dogs) or n < 0:
            print("They are:")
            for dog in Dog.dogs:
                print("  {}".format(dog))
        else:
            print("The dog indexed at {} is {}.".format(n, Dog.dogs[n]))

fido = Dog("Fido")
fido.bark(3)
Dog.rollCall(-1)
rex = Dog("Rex")
Dog.rollCall(0)

In questo codice, il metodo "rollCall" presuppone che il primo argomento non sia un'istanza (come sarebbe se fosse chiamato da un'istanza anziché da una classe). Finché "rollCall" viene chiamato dalla classe anziché da un'istanza, il codice funzionerà correttamente. Se proviamo a chiamare "rollCall" da un'istanza, ad esempio:

rex.rollCall(-1)

tuttavia, genererebbe un'eccezione perché invierebbe due argomenti: se stesso e -1, e "rollCall" è definito solo per accettare un argomento.

Per inciso, rex.rollCall () invierebbe il numero corretto di argomenti, ma provocherebbe anche un'eccezione perché ora n rappresenterebbe un'istanza Dog (cioè rex) quando la funzione prevede che n sia numerica.

È qui che entra in gioco la decorazione: se precediamo il metodo "rollCall" con

@staticmethod

quindi, dichiarando esplicitamente che il metodo è statico, possiamo persino chiamarlo da un'istanza. Adesso,

rex.rollCall(-1)

funzionerebbe. L'inserimento di @staticmethod prima di una definizione di metodo, quindi, impedisce a un'istanza di inviare se stessa come argomento.

Puoi verificarlo provando il seguente codice con e senza la riga @staticmethod commentata.

class Dog:
    count = 0 # this is a class variable
    dogs = [] # this is a class variable

    def __init__(self, name):
        self.name = name #self.name is an instance variable
        Dog.count += 1
        Dog.dogs.append(name)

    def bark(self, n): # this is an instance method
        print("{} says: {}".format(self.name, "woof! " * n))

    @staticmethod
    def rollCall(n):
        print("There are {} dogs.".format(Dog.count))
        if n >= len(Dog.dogs) or n < 0:
            print("They are:")
            for dog in Dog.dogs:
                print("  {}".format(dog))
        else:
            print("The dog indexed at {} is {}.".format(n, Dog.dogs[n]))


fido = Dog("Fido")
fido.bark(3)
Dog.rollCall(-1)
rex = Dog("Rex")
Dog.rollCall(0)
rex.rollCall(-1)

8
Il primo esempio (chiamare per classe senza @staticmethod) non funziona per me su Python 2.7. Ottengo "TypeError: il metodo non associato rollCall () deve essere chiamato con l'istanza Dog come primo argomento (invece è stato int int)"
tba

2
Questo non funziona. Sia Dog.rollCall (-1) che rex.rollCall (-1) restituiscono lo stessoTypeError: unbound method rollCall() must be called with Dog instance as first argument (got int instance instead)
Mittenchops,

3
@MestreLion Penso che questo non sia un grande vantaggio, dal momento che tutto ciò che fa è confondere i metodi statici e di istanza e rendere l'uno simile all'altro. Se sai che un metodo è statico, dovresti accedervi come metodo statico. Il fatto che il metodo non abbia bisogno o usi la tua istanza dovrebbe essere evidente ovunque tu usi il metodo. Uso sempre T.my_static_method()o type(my_t_instance).my_static_method(), poiché questo è più chiaro e rende immediatamente ovvio che sto chiamando un metodo statico.
Asad Saeeduddin,

81

Sì, dai un'occhiata al decoratore del metodo statico :

>>> class C:
...     @staticmethod
...     def hello():
...             print "Hello World"
...
>>> C.hello()
Hello World

54

Non hai davvero bisogno di usare il @staticmethoddecoratore. Dichiarare semplicemente un metodo (che non prevede il parametro auto) e chiamarlo dalla classe. Il decoratore è lì solo nel caso in cui tu voglia essere in grado di chiamarlo anche da un'istanza (che non era quello che volevi fare)

Principalmente, usi solo le funzioni però ...


Questo non funzionerebbe con Python 2.5.2 class Dummy: def static1(): print "hello from static1" @staticmethod def static2(): print "hello from static2" Dummy.static2() Dummy.static1() Output: ciao da static2 Traceback <ultima chiamata più recente>: file "ll.py", riga 46, in <module> Dummy.static1 () TypeError: metodo non associato static1 () deve essere chiamato con istanza fittizia come primo argomento (invece non ha ottenuto nulla)
Mahori

7
Questo non funziona all'interno di una classe. Python passerà selfcome primo argomento a meno che tu non gli dica di non farlo. (vedi: decoratore)
Navin,

9
In realtà questo è sbagliato in 2.7 e legale a partire da 3.X (testato bene in 3.2). Cioè non puoi chiamare un metodo dal contesto di una classe senza il decoratore @staticmethod in 2.7 e sotto. In 3.2 funziona e inserirà un selfref in modo appropriato a seconda di come è stato chiamato. Caso di prova: pastebin.com/12DDV7DB .
spinkus,

2
Tecnicamente accurato, ma una soluzione terribile. Il staticmethoddecoratore consente di chiamare la funzione sia sulla classe che su un'istanza (questa soluzione non riesce quando si chiama la funzione su un'istanza).
Ethan Furman,

un metodo può essere chiamato come qualsiasi altro attributo di un oggetto usando la notazione punto. class C: def callme(): print('called'); C.callme()
pouya,

32

Metodi statici in Python?

È possibile avere metodi statici in Python in modo da poterli chiamare senza inizializzare una classe, come:

ClassName.StaticMethod()

Sì, i metodi statici possono essere creati in questo modo (anche se è un po 'più Pythonic per utilizzare i trattini bassi invece di CamelCase per i metodi):

class ClassName(object):

    @staticmethod
    def static_method(kwarg1=None):
        '''return a value that is a function of kwarg1'''

Quanto sopra utilizza la sintassi del decoratore. Questa sintassi è equivalente a

class ClassName(object):

    def static_method(kwarg1=None):
        '''return a value that is a function of kwarg1'''

    static_method = staticmethod(static_method)

Questo può essere usato proprio come hai descritto:

ClassName.static_method()

Un esempio incorporato di un metodo statico è str.maketrans()in Python 3, che era una funzione nel stringmodulo in Python 2.


Un'altra opzione che può essere usata mentre descrivi è la classmethod, la differenza è che il metodo class ottiene la classe come primo argomento implicito e, se viene sottoclassata, ottiene la sottoclasse come primo argomento implicito.

class ClassName(object):

    @classmethod
    def class_method(cls, kwarg1=None):
        '''return a value that is a function of the class and kwarg1'''

Nota che clsnon è un nome richiesto per il primo argomento, ma i programmatori Python più esperti lo considereranno mal fatto se usi qualcos'altro.

Questi sono in genere utilizzati come costruttori alternativi.

new_instance = ClassName.class_method()

Un esempio incorporato è dict.fromkeys():

new_dict = dict.fromkeys(['key1', 'key2'])

11

A parte le particolarità di come si comportano gli oggetti metodo statico , c'è un certo tipo di bellezza che puoi colpire quando si tratta di organizzare il tuo codice a livello di modulo.

# garden.py
def trim(a):
    pass

def strip(a):
    pass

def bunch(a, b):
    pass

def _foo(foo):
    pass

class powertools(object):
    """
    Provides much regarded gardening power tools.
    """
    @staticmethod
    def answer_to_the_ultimate_question_of_life_the_universe_and_everything():
        return 42

    @staticmethod
    def random():
        return 13

    @staticmethod
    def promise():
        return True

def _bar(baz, quux):
    pass

class _Dice(object):
    pass

class _6d(_Dice):
    pass

class _12d(_Dice):
    pass

class _Smarter:
    pass

class _MagicalPonies:
    pass

class _Samurai:
    pass

class Foo(_6d, _Samurai):
    pass

class Bar(_12d, _Smarter, _MagicalPonies):
    pass

...

# tests.py
import unittest
import garden

class GardenTests(unittest.TestCase):
    pass

class PowertoolsTests(unittest.TestCase):
    pass

class FooTests(unittest.TestCase):
    pass

class BarTests(unittest.TestCase):
    pass

...

# interactive.py
from garden import trim, bunch, Foo

f = trim(Foo())
bunch(f, Foo())

...

# my_garden.py
import garden
from garden import powertools

class _Cowboy(garden._Samurai):
    def hit():
        return powertools.promise() and powertools.random() or 0

class Foo(_Cowboy, garden.Foo):
    pass

Ora diventa un po 'più intuitivo e auto-documentante in quale contesto devono essere usati determinati componenti e si adatta in modo ideale per nominare casi di test distinti, oltre ad avere un approccio diretto al modo in cui i moduli di test si associano ai moduli effettivi sotto test per puristi .

Trovo spesso fattibile applicare questo approccio all'organizzazione del codice di utilità di un progetto. Abbastanza spesso, le persone si affrettano immediatamente e creano un utilspacchetto e finiscono con 9 moduli di cui uno ha 120 LOC e il resto sono due dozzine di LOC al massimo. Preferisco iniziare con questo e convertirlo in un pacchetto e creare moduli solo per gli animali che li meritano davvero:

# utils.py
class socket(object):
    @staticmethod
    def check_if_port_available(port):
        pass

    @staticmethod
    def get_free_port(port)
        pass

class image(object):
    @staticmethod
    def to_rgb(image):
        pass

    @staticmethod
    def to_cmyk(image):
        pass

10

Forse l'opzione più semplice è quella di mettere quelle funzioni al di fuori della classe:

class Dog(object):
    def __init__(self, name):
        self.name = name

    def bark(self):
        if self.name == "Doggy":
            return barking_sound()
        else:
            return "yip yip"

def barking_sound():
    return "woof woof"

Utilizzando questo metodo, le funzioni che modificano o utilizzano lo stato dell'oggetto interno (hanno effetti collaterali) possono essere mantenute nella classe e le funzioni di utilità riutilizzabili possono essere spostate all'esterno.

Diciamo che questo file si chiama dogs.py. Per usarli, dovresti chiamare dogs.barking_sound()invece di dogs.Dog.barking_sound.

Se hai davvero bisogno di un metodo statico per far parte della classe, puoi usare il decoratore di metodi statici .


1

Quindi, i metodi statici sono i metodi che possono essere chiamati senza creare l'oggetto di una classe. Per esempio :-

    @staticmethod
    def add(a, b):
        return a + b

b = A.add(12,12)
print b

Nell'esempio sopra il metodo addviene chiamato dal nome della classe e Anon dal nome dell'oggetto.


-3

I metodi statici Python possono essere creati in due modi.

  1. Utilizzando staticmethod ()

    class Arithmetic:
        def add(x, y):
            return x + y
    # create add static method
    Arithmetic.add = staticmethod(Arithmetic.add)
    
    print('Result:', Arithmetic.add(15, 10))

Produzione:

Risultato: 25

  1. Utilizzando @staticmethod

    class Arithmetic:
    
    # create add static method
    @staticmethod
    def add(x, y):
        return x + y
    
    print('Result:', Arithmetic.add(15, 10))

Produzione:

Risultato: 25


Hai appena fornito esempi e condiviso i modi in cui puoi farlo. Non hai spiegato cosa significano i metodi.
Zixuan,

-4

Incontro questa domanda di volta in volta. Il caso d'uso e l'esempio a cui sono affezionato sono:

jeffs@jeffs-desktop:/home/jeffs  $ python36
Python 3.6.1 (default, Sep  7 2017, 16:36:03) 
[GCC 6.3.0 20170406] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cmath
>>> print(cmath.sqrt(-4))
2j
>>>
>>> dir(cmath)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atanh', 'cos', 'cosh', 'e', 'exp', 'inf', 'infj', 'isclose', 'isfinite', 'isinf', 'isnan', 'log', 'log10', 'nan', 'nanj', 'phase', 'pi', 'polar', 'rect', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau']
>>> 

Non ha senso creare un oggetto di classe cmath, perché non esiste uno stato in un oggetto cmath. Tuttavia, cmath è una raccolta di metodi che sono tutti correlati in qualche modo. Nel mio esempio sopra, tutte le funzioni di cmath agiscono su numeri complessi in qualche modo.


Non hai risposto alla domanda ma hai fornito un esempio.
Zixuan,
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.