Frage Bedeutung von @ classmethod und @ staticmethod für Anfänger?


Könnte mir jemand die Bedeutung von erklären? @classmethod und @staticmethod in Python? Ich muss den Unterschied und die Bedeutung wissen.

So weit ich das verstehe, @classmethod teilt einer Klasse mit, dass es sich um eine Methode handelt, die in Unterklassen oder ... etwas vererbt werden soll. Worum geht es jedoch? Warum definieren Sie nicht einfach die Klassenmethode, ohne sie hinzuzufügen? @classmethod oder @staticmethod oder irgendein @ Definitionen?

tl; dr:  wann sollte ich sie benutzen, Warum sollte ich sie benutzen, und Wie soll ich sie benutzen?

Ich bin ziemlich fortgeschritten mit C ++, daher sollte die Verwendung fortgeschrittener Programmierkonzepte kein Problem sein. Fühlen Sie sich frei, geben Sie mir ein entsprechendes C ++ - Beispiel, wenn möglich.


1251
2017-08-29 13:37


Ursprung


Antworten:


Obwohl classmethod und staticmethod sind sehr ähnlich, es gibt einen kleinen Unterschied in der Verwendung für beide Entitäten: classmethod muss einen Verweis auf ein Klassenobjekt als ersten Parameter haben, während staticmethod kann überhaupt keine Parameter haben.

Beispiel

class Date(object):

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

    @classmethod
    def from_string(cls, date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        date1 = cls(day, month, year)
        return date1

    @staticmethod
    def is_date_valid(date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        return day <= 31 and month <= 12 and year <= 3999

date2 = Date.from_string('11-09-2012')
is_date = Date.is_date_valid('11-09-2012')

Erläuterung

Nehmen wir ein Beispiel für eine Klasse an, die sich mit Datumsinformationen befasst (das ist unser Standardbaustein zum Kochen):

class Date(object):

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

Diese Klasse könnte natürlich verwendet werden, um Informationen über bestimmte Daten zu speichern (ohne Zeitzoneninformationen; angenommen, alle Daten sind in UTC dargestellt).

Hier haben wir __init__, ein typischer Initialisierer von Python-Klasseninstanzen, der Argumente als typisch erhält instancemethodmit dem ersten nicht optionalen Argument (self), die auf eine neu erstellte Instanz verweist.

Klassenmethode

Wir haben einige Aufgaben, die mit Hilfe von classmethods.

Nehmen wir an, wir wollen eine Menge schaffen Date Klasseninstanzen mit Datumsinformationen, die von einer äußeren Quelle kommen, die als eine Zeichenkette des nächsten Formats codiert ist ('tt-mm-jjjj'). Das müssen wir an verschiedenen Stellen unseres Quellcodes im Projekt tun.

Was wir hier tun müssen, ist:

  1. Analysiere eine Zeichenkette, um Tag, Monat und Jahr als drei ganzzahlige Variablen oder ein 3-Punkte-Tupel, bestehend aus dieser Variablen, zu erhalten.
  2. Instanziieren Date indem diese Werte an den Initialisierungsaufruf übergeben werden.

Das wird so aussehen:

day, month, year = map(int, string_date.split('-'))
date1 = Date(day, month, year)

Zu diesem Zweck hat C ++ eine Funktion wie Überladen, aber Python fehlt diese Funktion - also hier, wenn classmethod gilt. Lass uns ein neues erstellen "Konstrukteur".

    @classmethod
    def from_string(cls, date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        date1 = cls(day, month, year)
        return date1

date2 = Date.from_string('11-09-2012')

Sehen wir uns die obige Implementierung genauer an und überprüfen Sie, welche Vorteile wir hier haben:

  1. Wir haben das Parsen von Datumsstrings an einer Stelle implementiert und es ist jetzt wiederverwendbar.
  2. Encapsulation funktioniert hier gut (wenn Sie denken, dass Sie String-Parsing als eine einzige Funktion an anderer Stelle implementieren könnten, passt diese Lösung besser zum OOP-Paradigma).
  3. cls ist ein Objekt, das hält Klasse selbst, keine Instanz der Klasse. Es ist ziemlich cool, denn wenn wir unsere erben Date Klasse, alle Kinder werden haben from_string auch definiert.

Statische Methode

Wie wäre es mit staticmethod? Es ist ziemlich ähnlich zu classmethod nimmt aber keine obligatorischen Parameter (wie eine Klassenmethode oder Instanzmethode).

Sehen wir uns den nächsten Anwendungsfall an.

Wir haben eine Datumsangabe, die wir irgendwie validieren wollen. Mit dieser Aufgabe ist auch logisch verbunden Date Klasse, die wir bisher verwendet haben, erfordert aber immer noch keine Instanziierung.

Hier ist wo staticmethod kann nützlich sein. Schauen wir uns das nächste Stück Code an:

    @staticmethod
    def is_date_valid(date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        return day <= 31 and month <= 12 and year <= 3999

    # usage:
    is_date = Date.is_date_valid('11-09-2012')

Also, wie wir aus der Verwendung von sehen können staticmethodWir haben keinen Zugriff darauf, was die Klasse ist - es ist im Grunde genommen nur eine Funktion, syntaktisch wie eine Methode, aber ohne Zugriff auf das Objekt und seine Interna (Felder und andere Methoden), während die Klassenmethode dies tut.


2154
2017-08-29 14:03



Rostyslav Dzinkos Antwort ist sehr passend. Ich dachte, ich könnte einen anderen Grund hervorheben, den du wählen solltest @classmethod Über @staticmethod wenn Sie einen zusätzlichen Konstruktor erstellen.

Im obigen Beispiel benutzte Rostyslav die @classmethod  from_string als eine Fabrik zu schaffen Date Objekte von ansonsten inakzeptablen Parametern. Das Gleiche kann mit getan werden @staticmethod wie im folgenden Code gezeigt:

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

Also beides new_year und millenium_new_year sind Instanzen von Date Klasse.

Aber wenn Sie genau hinsehen, ist der Factory-Prozess fest programmiert Date Objekte egal was. Was das bedeutet ist, dass selbst wenn die Date Die Klasse ist unterklassifiziert, die Unterklassen erstellen immer noch plain Date Objekt (ohne eine Eigenschaft der Unterklasse). Sehen Sie das im folgenden Beispiel:

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 ist keine Instanz von DateTime? WTF? Nun, das ist wegen der @staticmethod Dekorateur verwendet.

In den meisten Fällen ist dies unerwünscht. Wenn Sie eine Factory-Methode verwenden möchten, die die Klasse kennt, die sie aufgerufen hat, dann @classmethod ist was du brauchst.

Umschreiben der Date.millenium as (das ist der einzige Teil des obigen Codes, der sich ändert)

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

sorgt dafür, dass die class ist nicht hart codiert, sondern gelernt. cls kann jede Unterklasse sein. Das Ergebnis object wird zu Recht eine Instanz von sein cls. Lass es uns testen.

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"

Der Grund ist, wie Sie inzwischen wissen, @classmethod wurde anstelle von verwendet @staticmethod


712
2018-01-30 13:35



@classmethod bedeutet: Wenn diese Methode aufgerufen wird, übergeben wir die Klasse als erstes Argument anstelle der Instanz dieser Klasse (wie wir es normalerweise mit Methoden tun). Dies bedeutet, dass Sie die Klasse und ihre Eigenschaften innerhalb dieser Methode anstelle einer bestimmten Instanz verwenden können.

@staticmethod bedeutet: Wenn diese Methode aufgerufen wird, übergeben wir keine Instanz der Klasse (wie wir es normalerweise mit Methoden tun). Dies bedeutet, dass Sie eine Funktion in eine Klasse einfügen können, aber nicht auf die Instanz dieser Klasse zugreifen können (dies ist nützlich, wenn Ihre Methode die Instanz nicht verwendet).


195
2017-08-29 13:40



@staticmethod Funktion ist nichts anderes als eine in einer Klasse definierte Funktion. Es kann aufgerufen werden, ohne zuerst die Klasse zu instanziieren. Die Definition ist durch Vererbung unveränderbar.

  • Python muss nicht instanziieren gebundene Methode für ein Objekt.
  • Es erleichtert die Lesbarkeit des Codes: Sehen @StaticMethodeWir wissen, dass die Methode nicht vom Zustand des Objekts selbst abhängt.

@classmethod Funktion auch aufrufbar, ohne die Klasse zu instanziieren, aber ihre Definition folgt Sub-Klasse, nicht Parent-Klasse, über Vererbung, kann durch Unterklasse überschrieben werden. Das ist, weil das erste Argument für @classmethodFunktion muss immer sein cls (class).

  • Fabrik Methoden, die zum Erstellen einer Instanz für eine Klasse verwendet werden, die beispielsweise eine Art Vorverarbeitung verwendet.
  • Statische Methoden, die statische Methoden aufrufenWenn Sie statische Methoden in mehrere statische Methoden aufteilen, sollten Sie den Klassennamen nicht fest codieren, sondern Klassenmethoden verwenden

Hier ist ein guter Link zu diesem Thema.


53
2018-04-14 07:12



Man würde verwenden @classmethod wenn er / sie das Verhalten der Methode ändern möchte, basierend darauf, welche Unterklasse die Methode aufruft. Denken Sie daran, dass wir in einer Klassenmethode einen Verweis auf die aufrufende Klasse haben.

Wenn Sie static verwenden, möchten Sie, dass das Verhalten in den Unterklassen unverändert bleibt

Beispiel:

class Hero:

  @staticmethod
  def say_hello():
     print("Helllo...")

  @classmethod
  def say_class_hello(cls):
     if(cls.__name__=="HeroSon"):
        print("Hi Kido")
     elif(cls.__name__=="HeroDaughter"):
        print("Hi Princess")

class HeroSon(Hero):
  def say_son_hello(self):
     print("test  hello")



class HeroDaughter(Hero):
  def say_daughter_hello(self):
     print("test  hello daughter")


testson = HeroSon()

testson.say_class_hello() #Output: "Hi Kido"

testson.say_hello() #Outputs: "Helllo..."

testdaughter = HeroDaughter()

testdaughter.say_class_hello() #Outputs: "Hi Princess"

testdaughter.say_hello() #Outputs: "Helllo..."

27
2017-08-21 07:18



Eine kleine Zusammenstellung

@StaticMethode Eine Methode zum Schreiben einer Methode innerhalb einer Klasse ohne Bezug auf das Objekt, an dem sie aufgerufen wird. Also keine Notwendigkeit, implizites Argument wie self oder cls zu übergeben. Es ist genauso geschrieben, wie außerhalb der Klasse geschrieben, aber es ist nicht von Nutzen in Python, denn wenn Sie eine Methode innerhalb einer Klasse kapseln müssen, da diese Methode der Teil dieser Klasse sein muss, ist @staticmethod praktisch Fall.

@ Klassenmethode Es ist wichtig, wenn Sie eine Factory-Methode schreiben möchten und diese benutzerdefinierten Attribute in einer Klasse angehängt werden können. Dieses Attribut kann in der geerbten Klasse überschrieben werden.

Ein Vergleich zwischen diesen zwei Methoden kann wie folgt sein

Table


26
2017-07-19 16:43



Bedeutung von @classmethod und @staticmethod?

  • Eine Methode ist eine Funktion im Namespace eines Objekts, auf die als Attribut zugegriffen werden kann.
  • Eine reguläre (d. H. Instanz) Methode ruft die Instanz ab (wir nennen sie normalerweise) self) als das implizite erste Argument.
  • EIN Klasse Methode bekommt die Klasse (wir nennen es normalerweise cls) als das implizite erste Argument.
  • EIN statisch Methode erhält kein implizites erstes Argument (wie eine reguläre Funktion).

Wann sollte ich sie verwenden, warum sollte ich sie verwenden und wie sollte ich sie verwenden?

Du nicht brauchen Dekorateur. Aber nach dem Prinzip, dass Sie die Anzahl der Argumente für Funktionen minimieren sollten (siehe Clean Coder), sind sie nützlich, um genau das zu tun.

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

Für beide Instanzmethoden und Klassenmethoden akzeptiert kein TypeError mindestens ein Argument, aber die Semantik dieses Arguments ist kein Benutzerfehler.

(Definieren some_functionz. B .:

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

und das wird funktionieren.)

Gepunktetes Suchen nach Instanzen und Klassen:

Eine gepunktete Suche in einer Instanz wird in dieser Reihenfolge durchgeführt - wir suchen nach:

  1. ein Datendeskriptor im Klassennamespace (wie eine Eigenschaft)
  2. Daten in der Instanz __dict__
  3. ein Nicht-Datendeskriptor im Klassennamespace (Methoden).

Beachten Sie, dass eine punktierte Suche in einer Instanz wie folgt aufgerufen wird:

instance = Example()
instance.regular_instance_method 

und Methoden sind aufrufbare Attribute:

instance.regular_instance_method()

Instanzmethoden

Das Argument, self, wird implizit über die punktierte Suche gegeben.

Sie müssen auf Instanzmethoden von Instanzen der Klasse zugreifen.

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

Klassenmethoden

Das Argument, cls, wird implizit über das punktierte Nachschlagen gegeben.

Sie können auf diese Methode über eine Instanz oder die Klasse (oder Unterklassen) zugreifen.

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

statische Methoden

Implizit sind keine Argumente angegeben. Diese Methode funktioniert wie jede Funktion, die zum Beispiel im Namespace eines Moduls definiert ist, außer dass sie nachgeschlagen werden kann

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

Wann sollte ich sie wieder benutzen, warum sollte ich sie benutzen?

Jeder von ihnen ist in den Informationen, die er die Methode Methode gegen Instanz übergeben, zunehmend restriktiver.

Verwenden Sie sie, wenn Sie die Informationen nicht benötigen.

Dies erleichtert es Ihnen, Ihre Funktionen und Methoden zu analysieren und zu testen.

Was ist einfacher zu verstehen?

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

oder

def function(y, z): ...

oder

def function(z): ...

Die Funktionen mit weniger Argumenten sind einfacher zu verstehen. Sie sind auch leichter zu testen.

Diese ähneln Instanz-, Klassen- und statischen Methoden. Denken Sie daran, dass, wenn wir eine Instanz haben, wir auch ihre Klasse haben, fragen Sie sich erneut, worüber man leichter nachdenken kann:

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):
    ...

Eingebaute Beispiele

Hier sind ein paar meiner Lieblingsbeispiele:

Das str.maketrans statische Methode war eine Funktion in der string Modul, aber es ist viel bequemer, es von der zugänglich zu sein str Namensraum.

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

Das dict.fromkeys Die Klassenmethode gibt ein neues Wörterbuch zurück, das aus einem iterierbaren Schlüssel besteht:

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

Bei Unterklassen sehen wir, dass es die Klasseninformationen als eine Klassenmethode erhält, was sehr nützlich ist:

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

Mein Rat - Schlussfolgerung

Verwenden Sie statische Methoden, wenn Sie die Klassen- oder Instanzargumente nicht benötigen, die Funktion jedoch mit der Verwendung des Objekts verknüpft ist und die Funktion sich im Namespace des Objekts befindet.

Verwenden Sie Klassenmethoden, wenn Sie keine Instanzinformationen benötigen, aber die Klasseninformationen vielleicht für ihre anderen Klassen- oder statischen Methoden oder vielleicht selbst als Konstruktor benötigen. (Sie würden die Klasse nicht hart codieren, so dass Unterklassen hier verwendet werden könnten.)


18
2017-10-24 16:09



Ich bin ein Anfänger auf dieser Seite, ich habe alle obigen Antworten gelesen, und habe die Information bekommen, was ich will. Ich habe jedoch kein Recht auf Verbesserung. Also möchte ich StackOverflow mit der Antwort starten, so wie ich es verstehe.

  • @staticmethod benötigt nicht selbst oder cls als erster Parameter der Methode
  • @staticmethod und @classmethod Wrapped-Funktion könnte von Instanz oder Klassenvariable aufgerufen werden
  • @staticmethod Englisch: www.weisang.info/index.php?id=143&t...h=fbcd8dcdcd Die verzierte Funktion wirkt sich auf eine Art 'unveränderliche Eigenschaft' aus, dass die Unterklassenvererbung ihre Basisklassenfunktion nicht überschreiben kann, die von a umschlossen ist @staticmethod Dekorateur.
  • @classmethod brauche cls (Klassenname, du könntest den Variablennamen ändern, wenn du willst, aber es wird nicht empfohlen) als erster Parameter der Funktion
  • @classmethod Die Unterklassenvererbung, die immer von der Unterklassenart verwendet wird, kann den Effekt der Basisklassenfunktion ändern, d.h. @classmethod Die Wrapped-Base-Class-Funktion könnte von verschiedenen Unterklassen überschrieben werden.

1
2017-07-23 11:38



Eine etwas andere Art, darüber nachzudenken, die für jemanden nützlich sein könnte ... Eine Klassenmethode wird in einer Oberklasse verwendet, um zu definieren, wie sich diese Methode verhalten soll, wenn sie von verschiedenen untergeordneten Klassen aufgerufen wird. Eine statische Methode wird verwendet, wenn das gleiche zurückgegeben werden soll, unabhängig von der untergeordneten Klasse, die wir aufrufen.


0
2018-01-24 22:59