pyplot - title matplotlib python




Significato di @classmethod e @staticmethod per principianti? (8)

Qualcuno potrebbe spiegarmi il significato di @classmethod e @staticmethod in python? Ho bisogno di sapere la differenza e il significato.

Per quanto ho capito, @classmethod dice a una classe che si tratta di un metodo che dovrebbe essere ereditato in sottoclassi, o ... qualcosa. Tuttavia, qual è il punto di questo? Perché non basta definire il metodo di classe senza aggiungere @classmethod o @staticmethod o qualsiasi @ definizioni?

tl; dr: quando dovrei usarli, perché dovrei usarli e come dovrei usarli?

Sono abbastanza avanzato con C ++, quindi usare concetti di programmazione più avanzati non dovrebbe essere un problema. Sentiti libero dandomi un esempio di C ++ corrispondente, se possibile.


Significato di @classmethod e @staticmethod ?

  • Un metodo è una funzione nello spazio dei nomi di un oggetto, accessibile come attributo.
  • Un metodo regolare (ad es. Istanza) ottiene l'istanza (che di solito chiamiamo self ) come primo argomento implicito.
  • Un metodo di classe ottiene la classe (di solito la chiamiamo cls ) come primo argomento implicito.
  • Un metodo statico non ha un primo argomento implicito (come una funzione normale).

quando dovrei usarli, perché dovrei usarli e come dovrei usarli?

Non hai bisogno né di un decoratore. Ma sul principio che si dovrebbe minimizzare il numero di argomenti per le funzioni (vedi Clean Coder), sono utili per fare proprio questo.

class Example(object):

    def regular_instance_method(self):
        """A function of an instance has access to every attribute of that 
        instance, including its class (and its attributes.)
        Not accepting at least one argument is a TypeError.
        Not understanding the semantics of that argument is a user error.
        """
        return some_function_f(self)

    @classmethod
    def a_class_method(cls):
        """A function of a class has access to every attribute of the class.
        Not accepting at least one argument is a TypeError.
        Not understanding the semantics of that argument is a user error.
        """
        return some_function_g(cls)

    @staticmethod
    def a_static_method():
        """A static method has no information about instances or classes
        unless explicitly given. It just lives in the class (and thus its 
        instances') namespace.
        """
        return some_function_h()

Per entrambi i metodi di istanza e i metodi di classe, non accettare almeno un argomento è un errore TypeE, ma non comprendere la semantica di quell'argomento è un errore dell'utente.

(Definisci some_function 's, ad esempio:

some_function_h = some_function_g = some_function_f = lambda x=None: x

e questo funzionerà.)

ricerche puntate su istanze e classi:

Una ricerca punteggiata su un'istanza viene eseguita in questo ordine: cerchiamo:

  1. un descrittore di dati nello spazio dei nomi della classe (come una proprietà)
  2. dati nell'istanza __dict__
  3. un descrittore non di dati nello spazio dei nomi della classe (metodi).

Nota, una ricerca tratteggiata su un'istanza viene invocata in questo modo:

instance = Example()
instance.regular_instance_method 

e i metodi sono attributi chiamabili:

instance.regular_instance_method()

metodi di istanza

L'argomento, self , è dato implicitamente tramite la ricerca tratteggiata.

È necessario accedere ai metodi di istanza dalle istanze della classe.

>>> instance = Example()
>>> instance.regular_instance_method()
<__main__.Example object at 0x00000000399524E0>

metodi di classe

L'argomento, cls , è implicitamente fornito tramite ricerca puntata.

È possibile accedere a questo metodo tramite un'istanza o la classe (o sottoclassi).

>>> instance.a_class_method()
<class '__main__.Example'>
>>> Example.a_class_method()
<class '__main__.Example'>

metodi statici

Nessun argomento viene fornito implicitamente. Questo metodo funziona come qualsiasi funzione definita (ad esempio) nello spazio dei nomi di un modulo, tranne che può essere cercata

>>> print(instance.a_static_method())
None

Ancora una volta, quando dovrei usarli, perché dovrei usarli?

Ognuno di questi è progressivamente più restrittivo nelle informazioni che passano il metodo rispetto ai metodi di istanza.

Usali quando non hai bisogno delle informazioni.

Ciò rende le tue funzioni e i tuoi metodi più facili da ragionare e da sbloccare.

Qual è più facile ragionare?

def function(x, y, z): ...

o

def function(y, z): ...

o

def function(z): ...

Le funzioni con meno argomenti sono più facili da ragionare. Sono anche più facili da separare.

Questi sono simili ai metodi statico, di classe e di istanza. Tenendo presente che quando abbiamo un'istanza, abbiamo anche la sua classe, ancora una volta, chiediti, che è più facile ragionare ?:

def an_instance_method(self, arg, kwarg=None):
    cls = type(self)             # Also has the class of instance!
    ...

@classmethod
def a_class_method(cls, arg, kwarg=None):
    ...

@staticmethod
def a_static_method(arg, kwarg=None):
    ...

Esempi incorporati

Ecco alcuni dei miei esempi di costruzione preferiti:

Il metodo statico str.maketrans era una funzione nel modulo string , ma è molto più conveniente per essere accessibile dallo spazio dei nomi str .

>>> 'abc'.translate(str.maketrans({'a': 'b'}))
'bbc'

Il metodo di classe dict.fromkeys restituisce un nuovo dizionario istanziato da un iterable di chiavi:

>>> dict.fromkeys('abc')
{'a': None, 'c': None, 'b': None}

Quando sottoclasse, vediamo che ottiene le informazioni sulla classe come un metodo di classe, che è molto utile:

>>> class MyDict(dict): pass
>>> type(MyDict.fromkeys('abc'))
<class '__main__.MyDict'> 

Il mio consiglio - Conclusione

Usa metodi statici quando non hai bisogno degli argomenti di classe o istanza, ma la funzione è correlata all'uso dell'oggetto, ed è conveniente che la funzione si trovi nello spazio dei nomi dell'oggetto.

Utilizza i metodi di classe quando non hai bisogno di informazioni sull'istanza, ma hai bisogno delle informazioni sulla classe forse per la sua altra classe o metodi statici, o forse se stesso come costruttore. (Non indicherai la classe in modo che le sottoclassi possano essere usate qui.)


Quando usare ciascuno

@staticmethod 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.

  • Python non deve istanziare un metodo vincolato per l'oggetto.
  • Facilita la leggibilità del codice: vedendo @staticmethod , sappiamo che il metodo non dipende dallo stato dell'oggetto stesso;

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

  • Metodi di fabbrica , che sono usati per creare un'istanza per una classe usando per esempio una sorta di pre-elaborazione.
  • Metodi statici che chiamano metodi statici : se dividi un metodo statico in diversi metodi statici, non devi codificare il nome della classe ma usare i metodi di classe

here buon collegamento a questo argomento.


In breve, @classmehtod trasforma un metodo normale in un metodo factory.

Esploriamolo con un esempio:

class PythonBook:
    def __init__(self, name, author):
        self.name = name
        self.author = author
    def __repr__(self):
        return f'Book: {self.name}, Author: {self.author}'

Senza un metodo di classe @, dovresti sforzarti di creare istanze una per una e sono scartate.

book1 = PythonBook('Learning Python', 'Mark Lutz')
In [20]: book1
Out[20]: Book: Learning Python, Author: Mark Lutz
book2 = PythonBook('Python Think', 'Allen B Dowey')
In [22]: book2
Out[22]: Book: Python Think, Author: Allen B Dowey

Come per esempio con @classmethod

class PythonBook:
    def __init__(self, name, author):
        self.name = name
        self.author = author
    def __repr__(self):
        return f'Book: {self.name}, Author: {self.author}'
    @classmethod
    def book1(cls):
        return cls('Learning Python', 'Mark Lutz')
    @classmethod
    def book2(cls):
        return cls('Python Think', 'Allen B Dowey')

Provalo:

In [31]: PythonBook.book1()
Out[31]: Book: Learning Python, Author: Mark Lutz
In [32]: PythonBook.book2()
Out[32]: Book: Python Think, Author: Allen B Dowey

Vedere? Le istanze vengono create correttamente all'interno di una definizione di classe e vengono raccolte insieme.

In conclusione, @classmethod decorator converte un metodo convenzionale in un metodo factory, Usando classmethods è possibile aggiungere tutti i costruttori alternativi necessari.


La risposta di Rostyslav Dzinko è molto appropriata. Ho pensato di poter evidenziare un altro motivo per cui dovresti scegliere @classmethod su @staticmethod quando stai creando un costruttore aggiuntivo.

Nell'esempio sopra, Rostyslav ha utilizzato @classmethod from_string come factory per creare oggetti Date da parametri altrimenti inaccettabili. Lo stesso può essere fatto con @staticmethod come mostrato nel seguente codice:

class Date:
  def __init__(self, month, day, year):
    self.month = month
    self.day   = day
    self.year  = year


  def display(self):
    return "{0}-{1}-{2}".format(self.month, self.day, self.year)


  @staticmethod
  def millenium(month, day):
    return Date(month, day, 2000)

new_year = Date(1, 1, 2013)               # Creates a new Date object
millenium_new_year = Date.millenium(1, 1) # also creates a Date object. 

# Proof:
new_year.display()           # "1-1-2013"
millenium_new_year.display() # "1-1-2000"

isinstance(new_year, Date) # True
isinstance(millenium_new_year, Date) # True

Quindi sia new_year che millenium_new_year sono istanze di Date class.

Ma, se si osserva da vicino, il processo di Fabbrica è hardcoded per creare oggetti Date , non importa cosa. Ciò significa che anche se la classe Date è sottoclasse, le sottoclassi creeranno comunque un oggetto Date semplice (senza alcuna proprietà della sottoclasse). Vedi quello nell'esempio qui sotto:

class DateTime(Date):
  def display(self):
      return "{0}-{1}-{2} - 00:00:00PM".format(self.month, self.day, self.year)


datetime1 = DateTime(10, 10, 1990)
datetime2 = DateTime.millenium(10, 10)

isinstance(datetime1, DateTime) # True
isinstance(datetime2, DateTime) # False

datetime1.display() # returns "10-10-1990 - 00:00:00PM"
datetime2.display() # returns "10-10-2000" because it's not a DateTime object but a Date object. Check the implementation of the millenium method on the Date class

datetime2 non è un'istanza di DateTime ? WTF? Beh, questo è dovuto al decoratore @staticmethod utilizzato.

Nella maggior parte dei casi, questo è indesiderato. Se quello che vuoi è un metodo Factory che sia a conoscenza della classe che lo ha chiamato, allora @classmethod è ciò di cui hai bisogno.

Riscrivere il Date.millenium as (questa è l'unica parte del codice sopra che cambia)

@classmethod
def millenium(cls, month, day):
    return cls(month, day, 2000)

assicura che la class non sia hardcoded ma piuttosto appresa. cls può essere qualsiasi sottoclasse. L' object risultante sarà giustamente un'istanza di cls . Proviamo questo.

datetime1 = DateTime(10, 10, 1990)
datetime2 = DateTime.millenium(10, 10)

isinstance(datetime1, DateTime) # True
isinstance(datetime2, DateTime) # True


datetime1.display() # "10-10-1990 - 00:00:00PM"
datetime2.display() # "10-10-2000 - 00:00:00PM"

Il motivo è che, come ormai @classmethod , @classmethod stato usato @staticmethod invece di @staticmethod


Sono un principiante in questo sito, ho letto tutte le risposte di cui sopra e ho ottenuto le informazioni che voglio. Tuttavia, non ho il diritto di alzare la voce. Quindi voglio iniziare da con la risposta perché la capisco.

  • @staticmethod non ha bisogno di self o cls come primo parametro del metodo
  • @staticmethod e @classmethod wrapped può essere chiamata per istanza o variabile di classe
  • @staticmethod funzione decorata @staticmethod impatto sulla proprietà immutabile di qualche tipo che l'ereditarietà della sottoclasse non può sovrascrivere la sua funzione di classe base che è racchiusa da un decoratore @staticmethod .
  • @classmethod need cls (Class name, puoi cambiare il nome della variabile se vuoi, ma non è consigliato) come primo parametro della funzione
  • @classmethod sempre usato in modo sottoclasse, l'ereditarietà delle sottoclassi può cambiare l'effetto della funzione della classe base, ovvero la funzione della classe base @classmethod potrebbe essere sovrascritta da sottoclassi diverse.

Un modo leggermente diverso di pensarci che potrebbe essere utile a qualcuno ... Un metodo di classe è usato in una superclasse per definire come dovrebbe comportarsi quel metodo quando viene chiamato da diverse classi figlie. Un metodo statico viene utilizzato quando vogliamo restituire la stessa cosa indipendentemente dalla classe figlio che stiamo chiamando.


@classmethod significa: quando viene chiamato questo metodo, passiamo la classe come primo argomento invece dell'istanza di quella classe (come facciamo normalmente con i metodi). Questo significa che puoi usare la classe e le sue proprietà all'interno di quel metodo piuttosto che una particolare istanza.

@staticmethod significa: quando viene chiamato questo metodo, non passiamo un'istanza della classe (come facciamo normalmente con i metodi). Ciò significa che puoi inserire una funzione all'interno di una classe ma non puoi accedere all'istanza di quella classe (ciò è utile quando il tuo metodo non usa l'istanza).


Una piccola compilation

@staticmethod Un modo per scrivere un metodo all'interno di una classe senza riferimento all'oggetto su cui viene chiamato. Quindi non c'è bisogno di passare argomenti impliciti come self o cls. È scritto esattamente come scritto fuori dalla classe, ma non è inutile in python perché se hai bisogno di incapsulare un metodo all'interno di una classe poiché questo metodo deve essere la parte di quella classe, @staticmethod è utile in Astuccio.

@classmethod È importante quando si desidera scrivere un metodo factory e in base a questo attributo personalizzato può essere allegato in una classe. Questo attributo può essere sovrascritto nella classe ereditata.

Un confronto tra questi due metodi può essere il seguente





class-method