Cosa sono i "metodi di classe" e i "metodi di istanza", in Python?


37

C'è stata una discussione in chat relativa a una domanda (la domanda stessa è irrilevante per questa), che ha rivelato che forse non conosco Python.

A mio avviso, sebbene la terminologia differisca a seconda delle lingue, in genere possiamo classificare le funzioni come:

  • funzioni [gratuite]
  • metodi statici / funzioni dei membri statici
  • metodi non statici / funzioni membro non statiche

Apparentemente in Python esiste un altro tipo di funzione che non rientra nelle categorie sopra, quella che è un metodo ma "non conosce la sua classe".

Cosa sono i "metodi di classe" e i "metodi di istanza", in Python?


1
Se qualcuno è interessato alla discussione in chat, inizia qui: chat.stackexchange.com/transcript/message/26479002#26479002
yannis

1
IMO una risposta migliore è fornita da un diverso sito StackExchange: stackoverflow.com/questions/136097/...
Trevor Boyd Smith

Cordiali saluti, il link viene fornito in fondo all'accettato ... ma penso che molte persone mancheranno il link o non lo raggiungeranno mai, quindi l'ho copiato qui.
Trevor Boyd Smith,

Risposte:


49

La breve risposta

  • un metodo di istanza conosce la sua istanza (e da ciò la sua classe)
  • un metodo di classe conosce la sua classe
  • un metodo statico non conosce la sua classe o istanza

La lunga risposta

Metodi di classe

Un metodo di classe è uno che appartiene alla classe nel suo insieme. Non richiede un'istanza. Invece, la classe verrà automaticamente inviata come primo argomento. Un metodo di classe viene dichiarato con il @classmethoddecoratore.

Per esempio:

class Foo(object):
    @classmethod
    def hello(cls):
        print("hello from %s" % cls.__name__)
Foo.hello()
-> "Hello from Foo"
Foo().hello()
-> "Hello from Foo"

Metodi di istanza

D'altra parte, un metodo di istanza richiede un'istanza per chiamarla e non richiede decoratore. Questo è di gran lunga il tipo più comune di metodo.

class Foo(object):
    def hello(self):
        print("hello from %s" % self.__class__.__name__)
Foo.hello()
-> TypeError: hello() missing 1 required positional argument: 'self'

(nota: quanto sopra è con python3; con python2 otterrai un errore leggermente diverso)

Metodi statici

Un metodo statico è simile a un metodo di classe, ma non otterrà l'oggetto classe come parametro automatico. Viene creato utilizzando il @staticmethoddecoratore.

class Foo(object):
    @staticmethod
    def hello(cls):
        print("hello from %s" % cls.__name__)

Foo.hello()
-> TypeError: hello() missing 1 required positional argument: 'cls'

Link alla documentazione

Ecco i collegamenti alla documentazione relativa a python3:

La documentazione del modello di dati ha questo da dire sulla differenza tra metodi di classe e metodi statici:

Oggetti metodo statico Gli oggetti metodo statico forniscono un modo per sconfiggere la trasformazione degli oggetti funzione in oggetti metodo descritti sopra. Un oggetto metodo statico è un wrapper attorno a qualsiasi altro oggetto, generalmente un oggetto metodo definito dall'utente. Quando un oggetto metodo statico viene recuperato da una classe o un'istanza di classe, l'oggetto effettivamente restituito è l'oggetto avvolto, che non è soggetto a ulteriori trasformazioni. Gli oggetti del metodo statico non sono essi stessi richiamabili, sebbene lo siano di solito gli oggetti che avvolgono. Gli oggetti del metodo statico vengono creati dal costruttore staticmethod () incorporato.

Oggetti metodo di classe Un oggetto metodo di classe, come un oggetto metodo statico, è un wrapper attorno a un altro oggetto che altera il modo in cui l'oggetto viene recuperato da classi e istanze di classe. Il comportamento degli oggetti dei metodi di classe in seguito a tale recupero è descritto sopra, in "Metodi definiti dall'utente". Gli oggetti del metodo di classe vengono creati dal costruttore incorporato classmethod ().

Domande correlate


4
@LightnessRacesinOrbit: non credo che i metodi statici vengano usati particolarmente spesso in Python. Usando il mio sistema come campione casuale, quando cerco tutti i pacchetti che ho installato, solo circa l'1% di tutti i metodi sono metodi statici (che francamente era più di quanto mi aspettassi).
Bryan Oakley,

1
I "metodi statici" sono in genere un odore di codice e sono scoraggiati, ad esempio, dalla guida di stile di Gogole Python.
9000

3
Penso che la risposta sarebbe migliore se attirasse l'attenzione sulle sottoclassi, che è dove i metodi di classe ottengono una certa utilità.
Winston Ewert,

1
@ user2357112: ho semplicemente contato tutte le istanze di "^ def(", quindi ho contato tutte le istanze di @staticmethod. Molto poco scientifico, ma probabilmente era all'interno del campo da baseball.
Bryan Oakley,

2
@Kashyap: TL; DR si applica al primo paragrafo. Penso che funzioni meglio in alto che in basso.
Bryan Oakley,

8

Cosa sono i "metodi di classe" e i "metodi di istanza", in Python?

  • Un "metodo di istanza" utilizza le informazioni contenute nell'istanza per capire quale valore restituire (o quale effetto collaterale fare). Questi sono molto comuni.

  • Un "metodo di classe" utilizza le informazioni sulla classe (e non un'istanza di quella classe) per influire su ciò che fa (in genere vengono utilizzate per creare nuove istanze come costruttori alternativi e quindi non sono incredibilmente comuni).

  • Un "metodo statico" non utilizza alcuna informazione sulla classe o sull'istanza per calcolare ciò che fa. Di solito è solo in classe per comodità. (Pertanto, anche questi non sono molto comuni.)

Una funzione di X

Ricorda la classe di matematica, "y è una funzione di x f(x),?" Appliciamolo nel codice:

y = function(x)

Ciò che è implicito sopra è che poiché xpuò cambiare, ypuò cambiare quando xcambia. Questo è ciò che si intende quando diciamo che " yè una funzione di x"

Cosa sarà yquando zè 1? 2? 'FooBarBaz'?

ynon è una funzione di z, quindi zpuò essere qualsiasi cosa e non influire sul risultato della funzione supponendo che la nostra functionsia una funzione pura. (Se accede zcome variabile globale, allora non è una funzione pura - questo è ciò che si intende per purezza funzionale.)

Tieni presente quanto sopra mentre leggi le seguenti descrizioni:

Metodi di istanza

Un metodo di istanza è una funzione che è una funzione di un'istanza . La funzione accetta implicitamente l'istanza come argomento e l'istanza viene utilizzata dalla funzione per determinare l'output della funzione.

Un esempio integrato di un metodo di istanza è str.lower:

>>> 'ABC'.lower()
'abc'

str.lower viene chiamato sull'istanza della stringa e utilizza le informazioni contenute nell'istanza per capire quale nuova stringa restituire.

Metodi di classe:

Ricorda, in Python, tutto è un oggetto. Ciò significa che la classe è un oggetto e può essere passata come argomento a una funzione.

Un metodo di classe è una funzione che è una funzione della classe . Accetta la classe come argomento.

Un esempio incorporato è dict.fromkeys:

>>> dict.fromkeys('ABC')
{'C': None, 'B': None, 'A': None}

La funzione conosce implicitamente la propria classe, la funzione utilizza la classe per influire sull'output della funzione e crea una nuova di quella classe dall'iterabile. Un OrderedDict lo dimostra quando si utilizza lo stesso metodo:

>>> from collections import OrderedDict
>>> OrderedDict.fromkeys('ABC')
OrderedDict([('A', None), ('B', None), ('C', None)])

Il metodo class utilizza informazioni sulla classe (e non un'istanza di quella classe) per influire sul tipo di classe da restituire.

Metodi statici

Citi un metodo che "non conosce la sua classe" - questo è un metodo statico in Python. È semplicemente collegato per comodità all'oggetto classe. Opzionalmente potrebbe essere una funzione separata in un altro modulo, ma la sua firma di chiamata sarebbe la stessa.

Un metodo statico non è una funzione né della classe né dell'oggetto.

Un esempio incorporato di un metodo statico è str.maketrans di Python 3.

>>> str.maketrans('abc', 'bca')
{97: 98, 98: 99, 99: 97}

Dato un paio di argomenti, crea un dizionario che non è una funzione della sua classe.

È comodo perché strè sempre disponibile nello spazio dei nomi globale, quindi puoi usarlo facilmente con la funzione di traduzione:

>>> 'abracadabra'.translate(str.maketrans('abc', 'bca'))
'bcrbabdbcrb'

In Python 2, devi accedervi dal stringmodulo:

>>> 'abracadabra'.translate(str.maketrans('abc', 'bca'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'str' has no attribute 'maketrans'
>>> import string
>>> 'abracadabra'.translate(string.maketrans('abc', 'bca'))
'bcrbabdbcrb'

Esempio

class AClass(object):
    """In Python, a class may have several types of methods: 
    instance methods, class methods, and static methods
    """

    def an_instance_method(self, x, y, z=None):
        """this is a function of the instance of the object
        self is the object's instance
        """
        return self.a_class_method(x, y)

    @classmethod
    def a_class_method(cls, x, y, z=None):
        """this is a function of the class of the object
        cls is the object's class
        """
        return cls.a_static_method(x, y, z=z)

    @staticmethod
    def a_static_method(x, y, z=None):
        """this is neither a function of the instance or class to 
        which it is attached
        """
        return x, y, z

Istanziamo:

>>> instance = AClass()

Ora l'istanza può chiamare tutti i metodi:

>>> instance.an_instance_method('x', 'y')
('x', 'y', None)
>>> instance.a_static_method('x', 'y')
('x', 'y', None)
>>> instance.a_class_method('x', 'y')
('x', 'y', None)

Ma la classe di solito non è chiamata a chiamare il metodo di istanza, anche se si prevede che chiamerà gli altri:

>>> AClass.a_class_method('x', 'y')
('x', 'y', None)
>>> AClass.a_static_method('x', 'y')
('x', 'y', None)
>>> AClass.an_instance_method('x', 'y')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: an_instance_method() missing 1 required positional argument: 'y'

Dovresti passare esplicitamente l'istanza per chiamare il metodo dell'istanza:

>>> AClass.an_instance_method(instance, 'x', 'y')
('x', 'y', None)

"Questo è un metodo statico in Python. È semplicemente collegato per comodità all'oggetto classe. Potrebbe facoltativamente essere una funzione separata in un altro modulo, ma la sua firma di chiamata sarebbe la stessa." Sembra più confuso che conveniente. Perché dovresti associare una funzione a una classe se il suo corpo non ha alcuna nozione di quella classe.
Corse di leggerezza con Monica il

@LightnessRacesinOrbit Ho approfondito ogni punto
Aaron Hall il

Personalmente, il modo in cui l'ho sempre visto è che un metodo di istanza è un'operazione su una particolare istanza, un metodo di classe è una sorta di costruttore (per comodità, niente di simile MyClass(1,2,3)ma invece di MyClass.get_special(1,2,3)), e un metodo statico come solo essendo una funzione normale che potrebbe essere autonoma comunque.
Zizouz212,

Sì, i metodi di classe vengono generalmente utilizzati per costruttori alternativi (vedere l'esempio my dict.fromkeys sopra, vedere anche il codice sorgente pandas.DataFrame), ma questa non è una caratteristica determinante. A volte ho semplicemente bisogno di accedere ai dati memorizzati a livello di classe da un metodo, senza accedere all'istanza. Se stai chiamando type (self) (o peggio, self .__ class__) e non stai usando self direttamente in un metodo di istanza, è un buon segno che dovresti considerarlo un metodo di classe. Poiché i metodi di classe non richiedono istanze, le rende più facili da unittest.
Aaron Hall,

4
  • Un "metodo di istanza" è un metodo che ottiene l'oggetto di istanza come primo parametro, che per convenzione viene chiamato self. Questo è il valore predefinito.

  • Un "metodo statico" è un metodo senza il selfparametro iniziale . Questo è implementato con il decoratore @staticmethod .

  • Un "metodo di classe" è un metodo in cui il parametro iniziale non è un oggetto di istanza, ma l'oggetto di classe ( vedere questa parte dei documenti Python per quello che è un "oggetto di classe"), che per convenzione viene chiamato cls. Questo è implementato con il decoratore @classmethod .

L'uso tipico del termine "metodo statico" in C / C ++ / etc si applica sia ai metodi "statici" di Python sia ai metodi di "classe" Python, poiché entrambi questi tipi di metodi non hanno un riferimento all'istanza. In C / C ++ / etc, i metodi normalmente hanno accesso implicito a tutti i membri / metodi non di classe della classe, motivo per cui questa distinzione esiste solo in linguaggi come Python / Ruby / etc.


Un metodo statico in C / C ++ ha un riferimento all'oggetto classe? O semplicemente "membri" della classe? Possono creare nuovi membri della classe? :)
Aaron Hall il

3
@AaronHall In C / C ++, non esiste un oggetto classe e non è necessario un riferimento ad esso per chiamare metodi statici. Basta scrivere Class::staticMethod()e basta (purché non ti sia proibito chiamarlo perché staticMethod()è privato o qualcosa del genere).
Ixrec,
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.