4626 Stimmen

Was ist der Unterschied zwischen @staticmethod und @classmethod in Python?

Was ist der Unterschied zwischen einer Methode dekoriert mit @staticmethod und einer Methode, die mit @classmethod dekoriert ist?

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

26voto

Adam Parkin Punkte 16245

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.

13voto

H.H Punkte 244

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

0 Stimmen

Sehr gemocht hat die Einfachheit dieser Antwort. Wünschte, ich könnte 100 Stimmen dafür geben.

11voto

Rizwan Mumtaz Punkte 3685

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.

11voto

illuminato Punkte 619

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

11voto

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.com

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.

Powered by:

X