Qual è il vantaggio di utilizzare metodi statici in Python?


90

Mi sono imbattuto in un errore di metodo non associato in Python con il codice

import random

class Sample(object):
'''This class defines various methods related to the sample'''

    def drawSample(samplesize,List):
        sample=random.sample(List,samplesize)
        return sample

Choices=range(100)
print Sample.drawSample(5,Choices)

Dopo aver letto molti post utili qui, ho capito come avrei potuto aggiungere @staticmethodsopra per far funzionare il codice. Sono un principiante di Python. Qualcuno può spiegare perché si vorrebbe definire metodi statici? Oppure, perché non tutti i metodi sono definiti come metodi statici?


1
Questa è una domanda strana. I metodi statici sono una necessità di progettazione . Non è una cosa "vantaggiosa". Li usi perché devi. È una caratteristica di progettazione di una classe avere metodi statici. Stai chiedendo quali sono i metodi statici in primo luogo? Penso che la domanda potrebbe essere riformulata per definire più chiaramente ciò che devi sapere.
S.Lott

15
No, non volevo sapere cosa sono. Quello che volevo sapere era perché è una "necessità", che è diventato chiaro dalle risposte date da altri. Questo è quando lo definiresti piuttosto che i metodi non statici. Grazie.
Curious2learn

3
@ S.Lott: Quand'è che l'utilizzo di un metodo statico è una necessità rispetto all'utilizzo di un normale metodo di classe? Per quanto ne so, un metodo di classe può fare tutto ciò che può fare un metodo statico. Il metodo statico ha "vantaggi" come elencato altrove in questo post, ma non riesco a vedere alcun motivo per cui un metodo di classe non possa essere utilizzato in qualsiasi luogo in cui possa essere utilizzato un metodo statico, rendendolo quindi una necessità.
RFV

Risposte:


128

I metodi statici hanno un uso limitato, perché non hanno accesso agli attributi di un'istanza di una classe (come fa un metodo normale) e non hanno accesso agli attributi della classe stessa (come fa un metodo di classe ).

Quindi non sono utili per i metodi quotidiani.

Tuttavia, possono essere utili per raggruppare alcune funzioni di utilità insieme a una classe - ad esempio una semplice conversione da un tipo a un altro - che non necessita dell'accesso ad alcuna informazione a parte i parametri forniti (e forse alcuni attributi globali al modulo. )

Potrebbero essere messi fuori dalla classe, ma raggrupparli all'interno della classe può avere senso laddove sono applicabili solo lì.

Puoi anche fare riferimento al metodo tramite un'istanza o la classe, piuttosto che il nome del modulo, che può aiutare il lettore a capire a quale istanza il metodo è correlato.


8
@ Curious2learn: non tutti , ma alcuni metodi sono utili come metodi statici. Pensa a una Localeclasse di esempio , le cui istanze saranno locales (duh). Un getAvailableLocales()metodo sarebbe un bell'esempio di un metodo statico di tale classe: chiaramente appartiene alla classe Locale, mentre chiaramente non appartiene a nessuna particolare istanza.
MestreLion

... e non è nemmeno un metodo di classe, poiché potrebbe non essere necessario accedere a nessuno dei metodi delle classi.
MestreLion

Un editor sottolinea che un metodo statico può accedere agli attributi della classe, navigando esplicitamente verso il basso da class_name.attribute, semplicemente no cls.attributeo self.attribute. Questo è vero per gli attributi "pubblici". Per convenzione, non dovresti accedere ad attributi nascosti con un trattino basso, o nomi alterati con due trattini bassi, in questo modo. Significa anche che il tuo codice sarà più fragile quando gli attributi cambieranno posizione nella gerarchia di ereditarietà.
Oddthinking

Una citazione di Guido che aiuta a decidere quando usare i metodi statici (mai): "Sappiamo tutti quanto siano limitati i metodi statici. (Sono fondamentalmente un incidente - ai tempi di Python 2.2 quando stavo inventando classi e descrittori di nuovo stile , Volevo implementare metodi di classe ma all'inizio non li capivo e prima ho implementato accidentalmente metodi statici. Poi era troppo tardi per rimuoverli e fornire solo metodi di classe. "
Boris Churzin

189

Vedi questo articolo per una spiegazione dettagliata.

TL; DR

1.Elimina l'uso di selfargomenti.

2.Riduce l'utilizzo della memoria perché Python non deve istanziare un metodo vincolato per ogni oggetto istiantato:

>>>RandomClass().regular_method is RandomClass().regular_method
False
>>>RandomClass().static_method is RandomClass().static_method
True
>>>RandomClass.static_method is RandomClass().static_method
True

3. Migliora la leggibilità del codice, a significare che il metodo non dipende dallo stato dell'oggetto stesso.

4. Consente l'override del metodo in quanto se il metodo fosse definito a livello di modulo (cioè al di fuori della classe) una sottoclasse non sarebbe in grado di sovrascrivere quel metodo.


3
Questa dovrebbe essere la risposta accettata. Il fatto che il metodo statico su qualsiasi istanza e la classe stessa siano lo stesso oggetto è un vero vantaggio, specialmente quando si hanno molte istanze (ad esempio ogni istanza per un record di database modificabile).
Zhuoyun Wei

+1 e sono d'accordo con @ZhuoyunWei. L'unica risposta che spiega i pochi motivi per cui un metodo statico a volte è preferibile rispetto a un metodo di classe (sebbene 1 e 3 siano in realtà la stessa ragione).
Alex

1
La migliore risposta a "perché metodo statico", ma la domanda originale chiedeva anche "perché non tutti i metodi sono statici?" Il breve "nessun accesso agli attributi dell'istanza" coprirebbe anche questo.
Exu

IMO l'unico vero vantaggio è che puoi sovrascriverlo in una sottoclasse, ma anche questo ha la sensazione che tu stia facendo qualcosa di sbagliato in termini di OOP, in quale caso d'uso lo faresti?
Boris Churzin

23

Questo non è esattamente il punto della tua domanda reale, ma dal momento che hai detto che sei un principiante di Python forse sarà utile, e nessun altro è uscito e lo ha detto esplicitamente.

Non avrei mai corretto il codice precedente rendendo il metodo un metodo statico. Avrei abbandonato la classe e avrei semplicemente scritto una funzione:

def drawSample(samplesize,List):
    sample=random.sample(List,samplesize)
    return sample

Choices=range(100)
print drawSample(5,Choices)

Se hai molte funzioni correlate, puoi raggrupparle in un modulo - cioè, metterle tutte nello stesso file, chiamato sample.pyad esempio; poi

import sample

Choices=range(100)
print sample.drawSample(5,Choices)

Oppure avrei aggiunto un __init__metodo alla classe e creato un'istanza che aveva metodi utili:

class Sample(object):
'''This class defines various methods related to the sample'''

    def __init__(self, thelist):
        self.list = thelist

    def draw_sample(self, samplesize):
        sample=random.sample(self.list,samplesize)
        return sample

choices=Sample(range(100))
print choices.draw_sample(5)

(Ho anche cambiato le convenzioni del caso nell'esempio sopra per abbinare lo stile raccomandato da PEP 8.)

Uno dei vantaggi di Python è che non ti obbliga a usare le classi per tutto. Puoi usarli solo quando ci sono dati o stati che dovrebbero essere associati ai metodi, a questo servono le classi. Altrimenti puoi usare le funzioni, a questo servono.


Grazie per il commento. In questo caso avevo bisogno di una lezione perché voglio lavorare con il campione disegnato. No, non ho utilizzato un metodo statico, ma volevo saperne di più, dal momento che mi sono imbattuto nel termine durante la ricerca di informazioni sul messaggio di errore che ho ricevuto. Ma il tuo consiglio su come raccogliere le funzioni in un modulo senza definire una classe sarebbe utile per altre funzioni di cui ho bisogno. Quindi grazie.
Curious2learn

4
+1: Questa è stata una bella spiegazione su alcune idee sbagliate che l'OP aveva. E sei stato molto onesto nel dire, in anticipo, che in realtà non stavi rispondendo alla sua domanda sui metodi statici, ma hai piuttosto dato una soluzione molto migliore dicendo che il suo problema non richiede affatto lezioni.
MestreLion

22

Perché si vorrebbe definire metodi statici ?

Supponiamo di avere una classchiamata Mathallora

nessuno vorrà creare un oggetto class Math
e quindi invocare metodi come ceile floore fabssu di esso.

Quindi li facciamo static.

Ad esempio facendo

>> Math.floor(3.14)

è molto meglio di

>> mymath = Math()
>> mymath.floor(3.14)

Quindi sono utili in qualche modo. Non è necessario creare un'istanza di una classe per utilizzarli.

Perché non tutti i metodi sono definiti come metodi statici ?

Non hanno accesso alle variabili di istanza.

class Foo(object):
    def __init__(self):
        self.bar = 'bar'

    def too(self):
        print self.bar

    @staticmethod
    def foo():
        print self.bar

Foo().too() # works
Foo.foo() # doesn't work

Ecco perché non rendiamo statici tutti i metodi.


10
Ma perché non un pacchetto di matematica? Python ha pacchetti per questo, non hai bisogno di una definizione di classe per creare uno spazio dei nomi.
extraneon

6
@extraneon: Sì amico, lo so, ma volevo avere qualcosa di semplice e familiare per la spiegazione, quindi l'ho usato Math. Ecco perché ho scritto in maiuscolo M.
Pratik Deoghare

6
L'OP non ha chiesto cosa sono i metodi statici. Ha chiesto qual è il loro vantaggio. Stai spiegando come usarli, non come sono utili. Nel tuo esempio particolare, uno spazio dei nomi avrebbe molto più senso.
Javier

4
-1: Spiegazione buona e corretta, ma un esempio scelto molto male: non c'è motivo per cui ti sei inventato Mathper essere una classe in primo luogo, un modulo sarebbe più adatto. Prova a trovare un esempio di una classe che abbia senso come classe, non uno di cui "nessuno vorrà creare un oggetto" .
MestreLion

3
E poi fornire un esempio di un caso d'uso legittimo per uno (o alcuni ) dei metodi delle classi per essere statici, ma non tutti . Se tutti i metodi di una classe sono statici, la classe dovrebbe essere un modulo. Se nessuno è statico, non stai rispondendo alla domanda.
MestreLion

13

Quando chiami un oggetto funzione da un'istanza di un oggetto, diventa un "metodo vincolato" e ottiene che l'oggetto istanza stesso viene passato come primo argomento.

Quando chiami un classmethodoggetto (che avvolge un oggetto funzione) su un'istanza di oggetto, la classe dell'oggetto istanza viene passata come primo argomento.

Quando si chiama un staticmethodoggetto (che racchiude un oggetto funzione), non viene utilizzato alcun primo argomento implicito.

class Foo(object):

    def bar(*args):
        print args

    @classmethod
    def baaz(*args):
        print args

    @staticmethod
    def quux(*args):
        print args

>>> foo = Foo()

>>> Foo.bar(1,2,3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method bar() must be called with Foo instance as first argument (got int instance instead)
>>> Foo.baaz(1,2,3)
(<class 'Foo'>, 1, 2, 3)
>>> Foo.quux(1,2,3)
(1, 2, 3)

>>> foo.bar(1,2,3)
(<Foo object at 0x1004a4510>, 1, 2, 3)
>>> foo.baaz(1,2,3)
(<class 'Foo'>, 1, 2, 3)
>>> foo.quux(1,2,3)
(1, 2, 3)

3
+1: Finalmente un'ottima spiegazione su cosa sono i metodi statici e i metodi di classe. Anche se non hai spiegato perché si vorrebbe usare un metodo statico, almeno hai spiegato chiaramente in un semplice esempio cosa sono entrambi molto meglio di quanto facciano i documenti ufficiali.
MestreLion

3

i metodi statici sono ottimi perché non è necessario dichiarare un'istanza dell'oggetto a cui appartiene il metodo.

Il sito di python ha un'ottima documentazione sui metodi statici qui:
http://docs.python.org/library/functions.html#staticmethod


Grazie David. Ma perché allora non definire ogni metodo come un metodo statico, dal momento che funzionano anche sulle istanze. Ci sono degli svantaggi nel farlo?
Curious2learn

4
@ Curious2learn: No, con i metodi statici non hai accesso all'istanza: l'istanza viene ignorata ad eccezione della sua classe.
Felix Kling

4
Questo argomento sarebbe vero in Java, dove le funzioni non possono vivere da sole ma sono sempre definite nel contesto di una classe. Ma in Python puoi avere funzioni e funzioni di classe statiche. Questa risposta non mostra realmente perché scegliere un metodo statico al posto di un metodo non in una classe.
extraneon

1
@extraneon: è più una questione di preferenze organizzative del codice; avere metodi statici offre un'opzione extra.
Charles Duffy

@Felix - Grazie. Questo chiarisce perché ogni metodo non dovrebbe essere un metodo statico.
Curious2learn

3

Le alternative ad un staticmethodsono: classmethod, instancemethod, e function. Se non sai cosa sono, scorri fino all'ultima sezione. Se a staticmethodè migliore di una qualsiasi di queste alternative, dipende da quale scopo è stato scritto.

vantaggi del metodo statico Python

  • Se non è necessario accedere agli attributi o ai metodi della classe o dell'istanza, a staticmethodè meglio di a classmethodo instancemethod. In questo modo è chiaro (dal @staticmethoddecoratore) che lo stato della classe e dell'istanza non viene letto o modificato. Tuttavia, l'utilizzo di a functionrende questa distinzione ancora più chiara (vedere svantaggi).
  • La firma richiamo di un staticmethodè la stessa di quella di una classmethodo instancemethod, cioè <instance>.<method>(<arguments>). Quindi può essere facilmente sostituito da uno dei tre se necessario in seguito o in una classe derivata. Non puoi farlo con un semplice function.
  • A staticmethodpuò essere utilizzato al posto di a functionper chiarire che appartiene soggettivamente a una classe e per prevenire conflitti di spazio dei nomi.

svantaggi del metodo statico Python

  • Non può accedere ad attributi o metodi dell'istanza o della classe.
  • La firma della chiamata di a staticmethodè la stessa di quella di a classmethodo instancemethod. Ciò maschera il fatto che in staticmethodrealtà non legge o modifica alcuna informazione sull'oggetto. Ciò rende il codice più difficile da leggere. Perché non usare solo un function?
  • A staticmethodè difficile da riutilizzare se è necessario chiamarlo dall'esterno della classe / istanza in cui è stato definito. Se esiste un potenziale di riutilizzo, a functionè la scelta migliore.
  • Il staticmethodè usato raramente, così la gente a leggere il codice che include uno può prendere un po 'di più per leggerlo.

alternative a un metodo statico in Python

Per affrontare e discutere i vantaggi di staticmethod, dobbiamo sapere quali sono le alternative e come differiscono l'una dall'altra.

  • La staticmethodappartiene ad una classe ma non può accedere o modificare qualsiasi informazione istanza o classe.

Ci sono tre alternative ad esso:

  • L' classmethodha accesso alla classe del chiamante.
  • L' instancemethodha accesso all'istanza del chiamante e la sua classe.
  • Non functionha niente a che fare con le lezioni. È la capacità più vicina al staticmethod.

Ecco come appare nel codice:

# function
# has nothing to do with a class
def make_cat_noise(asker_name):
    print('Hi %s, mieets mieets!' % asker_name)

# Yey, we can make cat noises before we've even defined what a cat is!
make_cat_noise('JOey')  # just a function

class Cat:
    number_of_legs = 4

    # special instance method __init__
    def __init__(self, name):
        self.name = name

    # instancemethod
    # the instance (e.g. Cat('Kitty')) is passed as the first method argument
    def tell_me_about_this_animal(self, asker_name):
        print('Hi %s, This cat has %d legs and is called %s'
              % (asker_name, self.number_of_legs, self.name))

    # classmethod
    # the class (e.g. Cat) is passed as the first method argument
    # by convention we call that argument cls
    @classmethod
    def tell_me_about_cats(cls, asker_name):
        print("Hi %s, cats have %d legs."
              % (asker_name, cls.number_of_legs))
        # cls.name  # AttributeError because only the instance has .name
        # self.name  # NameError because self isn't defined in this namespace

    # staticmethod
    # no information about the class or the instance is passed to the method
    @staticmethod
    def make_noise(asker_name):
        print('Hi %s, meooow!' % asker_name)
        # class and instance are not accessible from here

# one more time for fun!
make_cat_noise('JOey')  # just a function

# We just need the class to call a classmethod or staticmethod:
Cat.make_noise('JOey')  # staticmethod
Cat.tell_me_about_cats('JOey')  # classmethod
# Cat.tell_me_about_this_animal('JOey')  # instancemethod -> TypeError

# With an instance we can use instancemethod, classmethod or staticmethod
mycat = Cat('Kitty')  # mycat is an instance of the class Cat
mycat.make_noise('JOey')  # staticmethod
mycat.tell_me_about_cats('JOey')  # classmethod
mycat.tell_me_about_this_animal('JOey')  # instancemethod

1

Perché le funzioni di spaziatura dei nomi sono utili (come è stato sottolineato in precedenza):

  1. Quando voglio essere esplicito sui metodi che non cambiano lo stato dell'oggetto, utilizzo metodi statici. Questo scoraggia le persone del mio team a iniziare a modificare gli attributi dell'oggetto in quei metodi.

  2. Quando eseguo il refactoring di codice veramente marcio, inizio cercando di creare quanti più metodi @staticmethodpossibili. Questo mi permette quindi di estrarre questi metodi in una classe - anche se sono d'accordo, questo è qualcosa che uso raramente, è stato utile un paio di volte.


1

A mio avviso, non esiste un unico prestazioni vantaggio di usare @staticmethods rispetto ad appena definire la funzione al di fuori e separati dalla classe che sarebbe altrimenti una @staticmethoddi.

L'unica cosa che direi che giustifica la loro esistenza è la comodità. I metodi statici sono comuni in altri linguaggi di programmazione popolari, quindi perché non Python? Se si desidera creare una funzione con un comportamento strettamente associato alla classe per la quale la si sta creando, ma in realtà non si accede / modifica i dati interni di un'istanza della classe in un modo che giustifichi la sua concettualizzazione come tipica metodo di quella classe quindi schiaffo @staticmethodsopra e chiunque legga il tuo codice imparerà immediatamente molto sulla natura del metodo e sulla sua relazione con la classe.

Una cosa che di tanto in tanto mi piace fare è inserire funzionalità che la mia classe usa molto internamente nei messaggi privati @staticmethod. In questo modo non ingombrerò l'API esposta dal mio modulo con metodi che nessuno che utilizza il mio modulo avrebbe mai bisogno di vedere, figuriamoci usare.


0

I metodi statici non hanno quasi alcun motivo per essere in Python. Si utilizzano metodi di istanza o metodi di classe.

def method(self, args):
    self.member = something

@classmethod
def method(cls, args):
    cls.member = something

@staticmethod
def method(args):
    MyClass.member = something
    # The above isn't really working
    # if you have a subclass

Hai detto "quasi". C'è un posto dove possono essere migliori delle alternative?
Javier

@ Javier: non riesco a pensarne uno, ma probabilmente ce n'è uno, perché altrimenti quel metodo dovrebbe essere incluso nella libreria Python?
Georg Schölly

1
@Javier, @Georg: Hai grande fiducia che il corpus di Python non abbia cruft.
Charles Merriam

-1: questa risposta non presenta alcun caso d'uso staticmethodo alcuna spiegazione di cosa classmethodsia o perché e come sia "migliore" di quelli statici.
MestreLion

@CharlesMerriam: Ovviamente i metodi statici hanno il loro utilizzo, altrimenti sarebbe stato abbandonato o deprecato in Python 3 (dove è stata rimossa la maggior parte del legacy cruft). Non c'è una sola parola contro staticmethodnei documenti per supportare la tua affermazione che è cruft, o l'affermazione di Georg che classmethoddovrebbe essere usata al suo posto).
MestreLion
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.