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

3890voto

unutbu Punkte 769083

Vielleicht hilft ein Beispielcode etwas: Beachten Sie den Unterschied in den Aufrufsignaturen von foo, class_foo und static_foo:

class A(object):
    def foo(self, x):
        print(f"Ausführen von foo({self}, {x})")

    @classmethod
    def class_foo(cls, x):
        print(f"Ausführen von class_foo({cls}, {x})")

    @staticmethod
    def static_foo(x):
        print(f"Ausführen von static_foo({x})")

a = A()

Im Folgenden wird die übliche Art dargestellt, wie eine Objektinstanz eine Methode aufruft. Die Objektinstanz a wird implizit als erstes Argument übergeben.

a.foo(1)
# Ausführen von foo(<__main__.A object at 0xb7dbef0c>, 1)

Mit Klassenmethoden wird die Klasse der Objektinstanz implizit als erstes Argument übergeben, anstelle von self.

a.class_foo(1)
# Ausführen von class_foo(, 1)

Sie können auch class_foo mit der Klasse aufrufen. Tatsächlich, wenn Sie etwas als Klassenmethode definieren, ist es wahrscheinlich, weil Sie beabsichtigen, sie von der Klasse anstelle einer Klasseninstanz aufzurufen. A.foo(1) hätte einen TypeError ausgelöst, aber A.class_foo(1) funktioniert einwandfrei:

A.class_foo(1)
# Ausführen von class_foo(, 1)

Eine Verwendung von Klassenmethoden ist die Erstellung von erblichen alternativen Konstruktoren.


Mit Statischen Methoden wird weder self (die Objektinstanz) noch cls (die Klasse) implizit als erstes Argument übergeben. Sie verhalten sich wie normale Funktionen, außer dass Sie sie von einer Instanz oder von der Klasse aus aufrufen können:

a.static_foo(1)
# Ausführen von static_foo(1)

A.static_foo('hi')
# Ausführen von static_foo(hi)

Statische Methoden werden verwendet, um Funktionen, die eine logische Verbindung zu einer Klasse haben, zur Klasse zu gruppieren.


foo ist nur eine Funktion, aber wenn Sie a.foo aufrufen, erhalten Sie nicht nur die Funktion, sondern eine "teilweise angewendete" Version der Funktion, bei der die Objektinstanz a als erstes Argument an die Funktion gebunden ist. foo erwartet 2 Argumente, während a.foo nur 1 Argument erwartet.

a ist an foo gebunden. Das ist damit gemeint, wenn unten von "gebunden" die Rede ist:

print(a.foo)
# >

Bei a.class_foo ist a nicht an class_foo gebunden, sondern die Klasse A ist an class_foo gebunden.

print(a.class_foo)
# >

Hier wird auch bei einer statischen Methode, obwohl es sich um eine Methode handelt, a.static_foo einfach eine gute alte Funktion ohne gebundene Argumente zurückgegeben. static_foo erwartet 1 Argument und a.static_foo erwartet auch 1 Argument.

print(a.static_foo)
# 

Und natürlich geschieht dasselbe, wenn Sie static_foo mit der Klasse A aufrufen.

print(A.static_foo)
#

250 Stimmen

Ich verstehe nicht, worin der Trick beim Verwenden von staticmethod liegt. Wir können einfach eine einfache Funktion außerhalb der Klasse verwenden.

502 Stimmen

@Alcott: Du könntest eine Funktion in eine Klasse verschieben, weil sie logisch zur Klasse gehört. Im Python-Quellcode (z. B. multiprocessing, turtle, dist-packages) wird es verwendet, um einzelne Unterstrich-„private“ Funktionen aus dem Modul-Namespace zu "verstecken". Der Gebrauch ist jedoch hauptsächlich auf nur wenige Module konzentriert - vielleicht ein Hinweis darauf, dass es hauptsächlich eine stilistische Angelegenheit ist. Obwohl ich kein Beispiel dafür finden konnte, könnte @staticmethod helfen, deinen Code zu organisieren, indem er von Unterklassen überschreibbar ist. Ohne dies würden Varianten der Funktion im Modul-Namespace herumliegen.

155 Stimmen

@Alcott: Wie von unutbu erwähnt, sind statische Methoden ein organisatorisches/stilistisches Merkmal. Manchmal hat ein Modul viele Klassen, und einige Hilfsfunktionen sind logisch an eine bestimmte Klasse gebunden und nicht an andere, daher macht es Sinn, das Modul nicht mit vielen "freien Funktionen" zu "verschmutzen", und es ist besser, eine statische Methode zu verwenden, anstatt sich auf den schlechten Stil zu verlassen, Klassen und Funktionsdefinitionen im Code zu mischen, nur um zu zeigen, dass sie "zusammenhängen".

953voto

Thomas Wouters Punkte 124421

Ein staticmethod ist eine Methode, die nichts über die Klasse oder Instanz weiß, auf der sie aufgerufen wurde. Sie erhält nur die Argumente, die übergeben wurden, kein implizites erstes Argument.

Ein classmethod ist dagegen eine Methode, der die Klasse, auf der sie aufgerufen wurde, oder die Klasse der Instanz, auf der sie aufgerufen wurde, als erstes Argument übergeben wird. Dies ist nützlich, wenn die Methode eine Fabrik für die Klasse sein soll: Da sie die tatsächliche Klasse als erstes Argument erhält, können Sie immer die richtige Klasse instanziieren, auch wenn Unterklassen beteiligt sind. Beachten Sie zum Beispiel, wie dict.fromkeys(), eine classmethod, beim Aufruf auf einer Unterklasse eine Instanz der Unterklasse zurückgibt:

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
... 
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>>

858 Stimmen

Eine statische Methode ist nicht nutzlos - es handelt sich um eine Möglichkeit, eine Funktion in einer Klasse zu platzieren (weil sie logischerweise dorthin gehört), während angezeigt wird, dass sie keinen Zugriff auf die Klasse erfordert.

155 Stimmen

Daher nur 'grundsätzlich' nutzlos. Eine solche Organisation sowie Dependency Injection sind gültige Verwendungen von statischen Methoden, aber da Module, nicht Klassen wie in Java, die grundlegenden Elemente der Codeorganisation in Python sind, ist ihre Verwendung und Nützlichkeit selten.

48 Stimmen

Was ist logisch daran, eine Methode innerhalb einer Klasse zu definieren, wenn sie weder etwas mit der Klasse noch mit ihren Instanzen zu tun hat?

193voto

Terence Simpson Punkte 4162

Grundsätzlich erstellt @classmethod eine Methode, deren erstes Argument die Klasse ist, von der sie aufgerufen wird (anstatt der Klasseninstanz), @staticmethod hat keine impliziten Argumente.

0 Stimmen

Die anderen Antworten bringen unnötige Verwirrung in eine einfache Antwort. Dies ist buchstäblich das einzige, was sich ändert.

140voto

Du D. Punkte 4404

Um zu entscheiden, ob Sie @staticmethod oder @classmethod verwenden sollen, müssen Sie Ihren Methode genauer betrachten. Wenn Ihre Methode auf andere Variablen/Methoden in Ihrer Klasse zugreift, verwenden Sie @classmethod. Andererseits, wenn Ihre Methode keine anderen Teile der Klasse berührt, verwenden Sie @staticmethod.

class Apple:

    _counter = 0

    @staticmethod
    def about_apple():
        print('Apfel ist gut für dich.')

        # Beachten Sie, dass Sie weiterhin auf andere Elemente der Klasse zugreifen können,
        # aber Sie müssen die Klasseninstanz verwenden
        # was nicht sehr schön ist, weil Sie sich wiederholen müssen
        # 
        # Zum Beispiel:
        # @staticmethod
        # print('Anzahl der Äpfel, die gepresst wurden: %s' % Apple._counter)
        #
        # @classmethod
        # print('Anzahl der Äpfel, die gepresst wurden: %s' % cls._counter)
        #
        # @classmethod ist besonders nützlich, wenn Sie Ihre Funktion in eine andere Klasse verschieben,
        # Sie müssen die referenzierte Klasse nicht umbenennen

    @classmethod
    def make_apple_juice(cls, anzahl_äpfel):
        print('Saft herstellen:')
        for i in range(anzahl_äpfel):
            cls._juice_this(i)

    @classmethod
    def _juice_this(cls, apfel):
        print('Apfel %d pressen...' % apfel)
        cls._counter += 1

0 Stimmen

Was wäre der Vorteil von classmethod und cls._counter gegen staticmethod und Apple._counter

7 Stimmen

cls._counter würde auch dann cls._counter sein, wenn der Code in eine andere Klasse verschoben wird oder der Klassenname geändert wird. Apple._counter ist spezifisch für die Klasse Apple; für eine andere Klasse oder wenn der Klassenname geändert wird, müssten Sie die referenzierte Klasse ändern.

0 Stimmen

Ich hoffe, mit "move" meinst du nicht "kopieren" lol

127voto

Chris B. Punkte 2494

Offizielle Python-Dokumentation:

@classmethod

Ein Klassenmethode erhält die Klasse als implizites erstes Argument, genauso wie eine Instanzmethode die Instanz erhält. Um eine Klassenmethode zu deklarieren, verwenden Sie diese Idiom:

class C:
    @classmethod
    def f(cls, arg1, arg2, ...): ... 

Die @classmethod-Form ist ein Funktion Dekorator – siehe die Beschreibung von Funktionendefinitionen in Funktionsdefinitionen für Details.

Es kann entweder auf der Klasse aufgerufen werden (wie z.B. C.f()) oder auf einer Instanz (wie z.B. C().f()). Die Instanz wird außer für ihre Klasse ignoriert. Wenn eine Klassenmethode für eine abgeleitete Klasse aufgerufen wird, wird das abgeleitete Klassenobjekt als impliziertes erstes Argument übergeben.

Klassenmethoden unterscheiden sich von C++ oder Java-Statikmethoden. Wenn Sie das möchten, sehen Sie sich staticmethod() in diesem Abschnitt an.

@staticmethod

Eine statische Methode erhält kein implizites erstes Argument. Um eine statische Methode zu deklarieren, verwenden Sie dieses Idiom:

class C:
    @staticmethod
    def f(arg1, arg2, ...): ... 

Die @staticmethod-Form ist ein Funktion Dekorator – siehe die Beschreibung von Funktionendefinitionen in Funktionsdefinitionen für Details.

Es kann entweder auf der Klasse aufgerufen werden (wie z.B. C.f()) oder auf einer Instanz (wie z.B. C().f()). Die Instanz wird außer für ihre Klasse ignoriert.

Statische Methoden in Python sind ähnlich denen in Java oder C++. Für ein fortgeschritteneres Konzept, sehen Sie classmethod() in diesem Abschnitt an.

0 Stimmen

Gibt es nicht einen Fehler in den Dokumenten? Sollte es nicht bei staticmethod sein: "Die Instanz und ihre Klasse werden beide ignoriert." anstelle von "Die Instanz wird ignoriert, außer ihrer Klasse."?

0 Stimmen

Es kann sich um einen Copy-and-Paste-Fehler handeln, aber streng genommen kann man keine Methode einer Klasse aufrufen, wenn man die Klasse ignoriert.

0 Stimmen

Du liegst falsch! "Statische Elemente" in Java entsprechen den "Klassen-Elementen" in Python (der Unterschied besteht darin, dass du in Java auf ein öffentliches statisches Attribut von einem Objekt aus zugreifen kannst, aber in Python nicht). "Statische Methoden" in Java entsprechen den "classmethods" in Python, die auf den Klassenstatus (Klassen-Elemente) zugreifen können. "Statischemethoden" in Python sind eine relativ nutzlose Methode, die weder auf den Klassenstatus noch auf den Instanzstatus zugreift und wie eine reine Funktion außerhalb der Klassendefinition ist.

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