Was ist der Unterschied zwischen einer Methode dekoriert mit @staticmethod
und einer Methode, die mit @classmethod
dekoriert ist?
Sehr gemocht hat die Einfachheit dieser Antwort. Wünschte, ich könnte 100 Stimmen dafür geben.
Was ist der Unterschied zwischen einer Methode dekoriert mit @staticmethod
und einer Methode, die mit @classmethod
dekoriert ist?
Eine weitere Überlegung im Hinblick auf staticmethod vs. classmethod ergibt sich bei Vererbung. Angenommen, Sie haben die folgende Klasse:
class Foo(object):
@staticmethod
def bar():
return "In Foo"
Und Sie möchten dann bar()
in einer abgeleiteten Klasse überschreiben:
class Foo2(Foo):
@staticmethod
def bar():
return "In Foo2"
Dies funktioniert, aber beachten Sie, dass nun die bar()
-Implementierung in der abgeleiteten Klasse (Foo2
) nicht mehr spezifisch auf diese Klasse zugeschnitten sein kann. Angenommen, Foo2
hätte eine Methode namens magic()
, die Sie in der bar()
-Implementierung von Foo2
verwenden möchten:
class Foo2(Foo):
@staticmethod
def bar():
return "In Foo2"
@staticmethod
def magic():
return "Etwas Nützliches, das Sie in bar verwenden möchten, aber jetzt nicht können"
Die Lösung hier wäre, Foo2.magic()
in bar()
aufzurufen, aber dann wiederholen Sie sich (wenn sich der Name von Foo2
ändert, müssen Sie daran denken, diese bar()
-Methode zu aktualisieren).
Meiner Meinung nach ist dies eine leichte Verletzung des open/closed-Prinzips, da eine Entscheidung, die in Foo
getroffen wurde, Ihre Fähigkeit beeinflusst, gemeinsamen Code in einer abgeleiteten Klasse umzustrukturieren (d.h. sie ist weniger offen für Erweiterung). Wenn bar()
eine classmethod
wäre, wären wir in Ordnung:
class Foo(object):
@classmethod
def bar(cls):
return "In Foo"
class Foo2(Foo):
@classmethod
def bar(cls):
return "In Foo2 " + cls.magic()
@classmethod
def magic(cls):
return "MAGIC"
print Foo2().bar()
Ergebnis: In Foo2 MAGIC
Auch: Historische Anmerkung: Guido Van Rossum (der Schöpfer von Python) bezeichnete staticmethod
einmal als "Unfall": https://mail.python.org/pipermail/python-ideas/2012-May/014969.html
Wir alle wissen, wie begrenzt static methods sind. (Sie waren im Grunde genommen ein Unfall -- damals, als ich in den Python 2.2-Tagen neue Klassen und Deskriptoren erfand, wollte ich Klassenmethoden implementieren, aber ich verstand sie zunächst nicht und implementierte versehentlich zuerst static methods. Dann war es zu spät, um sie zu entfernen und nur Klassenmethoden bereitzustellen.
Auch: https://mail.python.org/pipermail/python-ideas/2016-July/041189.html
Ehrlich gesagt, war staticmethod so etwas wie ein Fehler - ich versuchte, etwas Ähnliches wie Java class methods zu realisieren, aber als es veröffentlicht wurde, stellte ich fest, dass classmethod wirklich gebraucht wurde. Aber es war zu spät, um staticmethod loszuwerden.
Der Unterschied tritt auf, wenn Vererbung vorhanden ist.
Angenommen, es gibt zwei Klassen - Parent
und Child
. Wenn man @staticmethod
verwenden möchte, sollte die Methode print_name
zweimal geschrieben werden, da der Name der Klasse in der Print-Anweisung geschrieben werden sollte.
class Parent:
_class_name = "Parent"
@staticmethod
def print_name():
print(Parent._class_name)
class Child(Parent):
_class_name = "Child"
@staticmethod
def print_name():
print(Child._class_name)
Parent.print_name()
Child.print_name()
Bei @classmethod
ist es jedoch nicht erforderlich, die Methode print_name
zweimal zu schreiben.
class Parent:
_class_name = "Parent"
@classmethod
def print_name(cls):
print(cls._class_name)
class Child(Parent):
_class_name = "Child"
Parent.print_name()
Child.print_name()
Ich werde versuchen, den grundlegenden Unterschied anhand eines Beispiels zu erklären.
class A(object):
x = 0
def say_hi(self):
pass
@staticmethod
def say_hi_static():
pass
@classmethod
def say_hi_class(cls):
pass
def run_self(self):
self.x += 1
print self.x # gibt 1 aus
self.say_hi()
self.say_hi_static()
self.say_hi_class()
@staticmethod
def run_static():
print A.x # gibt 0 aus
A.say_hi_static()
A.say_hi_class()
@classmethod
def run_class(cls):
print cls.x # gibt 0 aus
cls.say_hi_static()
cls.say_hi_class()
1 - Wir können Static- und Classmethoden direkt aufrufen, ohne zu initialisieren
# A.run_self() # falsch
A.run_static()
A.run_class()
2- Eine Static-Methode kann keine Self-Methode aufrufen, kann aber andere Static- und Classmethoden aufrufen
3- Static-Methode gehört der Klasse an und verwendet überhaupt kein Objekt.
4- Klassenmethode sind nicht an ein Objekt, sondern an eine Klasse gebunden.
Python wird mit mehreren integrierten Dekoratoren geliefert. Die drei wichtigsten sind:
@classmethod
@staticmethod
@property
Zunächst einmal ist zu beachten, dass jede Funktion einer Klasse mit einer Instanz dieser Klasse aufgerufen werden kann (nachdem wir diese Klasse initialisiert haben).
@classmethod ist die Art und Weise, eine Funktion aufzurufen, nicht nur als Instanz einer Klasse, sondern auch direkt durch die Klasse selbst als ihr erster Argument.
@staticmethod ist eine Möglichkeit, eine Funktion in eine Klasse zu setzen (weil sie logisch dort hingehört), während angegeben wird, dass sie keinen Zugriff auf die Klasse benötigt (also müssen wir in der Funktionsdefinition nicht self
verwenden).
Betrachten wir die folgende Klasse:
class DecoratorTest(object):
def __init__(self):
pass
def doubler(self, x):
return x*2
@classmethod
def class_doubler(cls, x):
"""
Anstelle von 'self' müssen wir 'cls' verwenden;
'cls' verweist auf die Klasse anstelle
einer Instanz der Klasse
"""
return x*2
@staticmethod
def static_doubler(x):
"""
Hier brauchen wir kein 'self' hinzuzufügen;
static_doubler() könnte einfach
eine Funktion außerhalb der Klasse sein.
"""
return x*2
Schauen wir uns an, wie es funktioniert:
decor = DecoratorTest()
print(decor.doubler(5))
# 10
# Ein Aufruf mit einer Instanz einer Klasse
print(decor.class_doubler(5))
# 10
# Ein direkter Aufruf durch die Klasse selbst
print(DecoratorTest.class_doubler(5))
# 10
# staticmethod kann genauso aufgerufen werden wie classmethod.
# als Instanz der Klasse
print(decor.static_doubler(5))
# 10
# oder als direkter Aufruf
print(DecoratorTest.static_doubler(5))
# 10
Hier können Sie einige Anwendungsfälle für diese Methoden sehen.
Bonus: Sie können über den @property
Dekorator hier lesen
Instanzmethode:
+
Kann Objektinstanzstatus ändern
+
Kann Klassenstatus ändern
Klassenmethode:
-
Kann Objektinstanzstatus nicht ändern
+
Kann Klassenstatus ändern
Statische Methode:
-
Kann Objektinstanzstatus nicht ändern
-
Kann Klassenstatus nicht ändern
class MyClass:
'''
Instanzmethode hat ein obligatorisches erstes Attribut self, das die Instanz selbst darstellt.
Die Instanzmethode muss von einer instanziierten Instanz aufgerufen werden.
'''
def method(self):
return 'Instanzmethode aufgerufen', self
'''
Klassenmethode besitzt ein obligatorisches erstes Attribut cls, das die Klasse selbst darstellt.
Die Klassenmethode kann von einer Instanz oder direkt von der Klasse aufgerufen werden.
Das gängigste Anwendungsszenario ist das Definieren einer Factory-Methode.
'''
@classmethod
def class_method(cls):
return 'Klassenmethode aufgerufen', cls
'''
Statische Methode hat keine Attribute von Instanzen oder der Klasse.
Sie kann auch von einer Instanz oder direkt von der Klasse aufgerufen werden.
Das gängigste Anwendungsszenario ist das Definieren einiger Hilfs- oder Hilfsfunktionen, die eng mit der Klasse verwandt sind.
'''
@staticmethod
def static_method():
return 'Statische Methode aufgerufen'
obj = MyClass()
print(obj.method())
print(obj.class_method()) # MyClass.class_method()
print(obj.static_method()) # MyClass.static_method()
Ausgabe:
('Instanzmethode aufgerufen', <__main__.MyClass object at 0x100fb3940>)
('Klassenmethode aufgerufen', )
Statische Methode aufgerufen
Bei der Instanzmethode hatten wir tatsächlich Zugriff auf die Objektinstanz, richtig, also dies war eine Instanz einer MyClass-Objektinstanz, während bei der Klassenmethode wir Zugriff auf die Klasse selbst hatten. Aber nicht auf eines der Objekte, weil die Klassenmethode nicht wirklich ein Objekt benötigt. Sie können jedoch sowohl eine Klassenmethode als auch eine statische Methode an einer Objektinstanz aufrufen. Das wird funktionieren, es macht keinen Unterschied, also wenn Sie hier die statische Methode aufrufen, wird sie funktionieren und wissen, welche Methode Sie aufrufen möchten.
Statische Methoden werden verwendet, um einige Hilfsaufgaben zu erledigen, und Klassenmethoden werden für Fabrikmethode verwendet. Die Fabrikmethode kann Klassenobjekte für verschiedene Anwendungsfälle zurückgeben.
Und schließlich ein kurzes Beispiel zur besseren Verständnis:
class Student:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
@classmethod
def get_from_string(cls, name_string: str):
first_name, last_name = name_string.split()
if Student.validate_name(first_name) and Student.validate_name(last_name):
return cls(first_name, last_name)
else:
print('Ungültige Namen')
@staticmethod
def validate_name(name):
return len(name) <= 10
stackoverflow_student = Student.get_from_string('Vorname Nachname')
print(stackoverflow_student.first_name) # Vorname
print(stackoverflow_student.last_name) # Nachname
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
27 Stimmen
Statische Methoden sind manchmal als Funktionen auf Modulniveau in Python besser aufgehoben für die Sauberkeit. Mit einer Modulfunktion ist es einfacher, nur die Funktion zu importieren, die Sie benötigen und unnötige "."-Syntax zu vermeiden (ich sehe dich an, Objective-C). Klassenmethoden haben mehr Verwendung, da sie in Kombination mit Polymorphismus verwendet werden können, um Funktionen im "Factory Pattern" zu erstellen. Dies liegt daran, dass Klassenmethoden die Klasse als impliziten Parameter erhalten.
64 Stimmen
Tl;dr >> Im Vergleich zu normalen Methoden können statische Methoden und Klassenmethoden auch über die Klasse aufgerufen werden, aber im Gegensatz zu Klassenmethoden sind statische Methoden vererbungsfest.
9 Stimmen
Ähnlicher Vortrag von Raymond Hettinger zum Thema: youtube.com/watch?v=HTLu2DFOdTg
2 Stimmen
More precise youtube.com/watch?v=HTLu2DFOdTg&feature=youtu.be&t=2689 Sie benötigen nur Klassenmethode für alternative Konstruktoren. Andernfalls können Sie staticmethod verwenden und auf jedes Klassenattribut (über. / Dot) mit dem etwas informativeren tatsächlichen "Klassenname" anstelle von cls wie in classmethod zugreifen.