Unterschied zwischen statischer Methode und Klassenmethode


3580

Was ist der Unterschied zwischen einer mit @staticmethodund einer dekorierten Funktion @classmethod?


11
Aus Gründen der Sauberkeit sind statische Methoden manchmal besser als Funktionen auf Modulebene in Python geeignet. Mit einer Modulfunktion ist es einfacher, nur die Funktion zu importieren, die Sie benötigen, und unnötiges "" zu vermeiden. Syntax (ich sehe dich an Objective-C). Klassenmethoden sind nützlicher, da sie in Kombination mit Polymorphismus verwendet werden können, um "Factory Pattern" -Funktionen zu erstellen. Dies liegt daran, dass Klassenmethoden die Klasse als impliziten Parameter empfangen.
FistOfFury

27
tl; dr >> Im Vergleich zu normalen Methoden kann auf die statischen Methoden und Klassenmethoden auch mit der Klasse zugegriffen werden. Im Gegensatz zu Klassenmethoden sind statische Methoden jedoch über die Vererbung unveränderlich.
Imsrgadich

4
In Verbindung stehender Vortrag von Raymond Hettinger zum Thema: youtube.com/watch?v=HTLu2DFOdTg
moooeeeep

genauer youtube.com/watch?v=HTLu2DFOdTg&feature=youtu.be&t=2689 Sie benötigen nur eine Klassenmethode für alternative Konstruktoren. Andernfalls können Sie staticmethod verwenden und auf jedes Klassenattribut (via. / Dot) über den irgendwie informativeren tatsächlichen "CLASSNAME" zugreifen, anstatt über cls wie in classmethod
Robert Nowak,

Antworten:


3137

Vielleicht ein bisschen von Beispielcode helfen: Beachten Sie den Unterschied in der Anruf Unterschriften foo, class_foound static_foo:

class A(object):
    def foo(self, x):
        print "executing foo(%s, %s)" % (self, x)

    @classmethod
    def class_foo(cls, x):
        print "executing class_foo(%s, %s)" % (cls, x)

    @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)" % x    

a = A()

Im Folgenden wird die übliche Art und Weise beschrieben, wie eine Objektinstanz eine Methode aufruft. Die Objektinstanz awird implizit als erstes Argument übergeben.

a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)

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

a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Sie können auch class_foomit der Klasse aufrufen . Wenn Sie etwas als Klassenmethode definieren, liegt dies wahrscheinlich daran, dass Sie es eher von der Klasse als von einer Klasseninstanz aus aufrufen möchten. A.foo(1)hätte einen TypeError ausgelöst, A.class_foo(1)funktioniert aber einwandfrei :

A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Eine Verwendung, die Menschen für Klassenmethoden gefunden haben, besteht darin, vererbbare alternative Konstruktoren zu erstellen .


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

a.static_foo(1)
# executing static_foo(1)

A.static_foo('hi')
# executing static_foo(hi)

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


fooist nur eine Funktion, aber wenn Sie aufrufen, erhalten a.fooSie nicht nur die Funktion, sondern eine "teilweise angewendete" Version der Funktion, wobei die Objektinstanz aals erstes Argument an die Funktion gebunden ist. fooerwartet 2 Argumente, während a.foonur 1 Argument erwartet wird.

aist gebunden an foo. Das ist es, was mit dem Begriff "gebunden" unten gemeint ist:

print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>

Mit a.class_foo, anicht gebunden class_foo, sondern die Klasse Agebunden ist , zu class_foo.

print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>

Hier gibt eine statische Methode, obwohl es sich um eine Methode handelt, a.static_foonur eine gute alte Funktion ohne gebundene Argumente zurück. static_fooerwartet 1 Argument und a.static_fooerwartet auch 1 Argument.

print(a.static_foo)
# <function static_foo at 0xb7d479cc>

Und natürlich passiert dasselbe, wenn Sie stattdessen static_foomit der Klasse anrufen A.

print(A.static_foo)
# <function static_foo at 0xb7d479cc>

182
Ich verstehe nicht, was der Haken bei der Verwendung von staticmethod ist. Wir können nur eine einfache Funktion außerhalb der Klasse verwenden.
Alcott

372
@Alcott: Möglicherweise möchten Sie eine Funktion in eine Klasse verschieben, da sie logisch zur Klasse gehört. Im Python-Quellcode (z. B. Multiprocessing, Turtle, Dist-Pakete) wird er verwendet, um "private" Funktionen mit einem Unterstrich aus dem Modul-Namespace zu "verbergen". Seine Verwendung konzentriert sich jedoch stark auf wenige Module - vielleicht ein Hinweis darauf, dass es sich hauptsächlich um eine stilistische Sache handelt. Obwohl ich kein Beispiel dafür finden konnte, @staticmethodkönnte es helfen, Ihren Code zu organisieren, indem er von Unterklassen überschrieben wird. Ohne sie würden Varianten der Funktion im Modul-Namespace herumschweben.
Unutbu

15
... zusammen mit einigen Erklärungen, wo und warum Instanz-, Klassen- oder statische Methoden verwendet werden sollen. Sie haben kein einziges Wort darüber gegeben, aber das OP hat auch nicht danach gefragt.
MestreLion

106
@Alcott: Wie nicht 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 die anderen. Daher ist es sinnvoll, das Modul nicht mit vielen "freien Funktionen" zu "verschmutzen", und es ist besser, eine statische zu verwenden Methode, als sich auf den schlechten Stil des Mischens von Klassen und Funktionsdefs im Code zu verlassen, nur um zu zeigen, dass sie "verwandt" sind
MestreLion

4
Eine weitere Verwendung für @staticmethod- Sie können damit Cruft entfernen. Ich implementiere eine Programmiersprache in Python - bibliotheksdefinierte Funktionen verwenden eine statische executeMethode, bei der benutzerdefinierte Funktionen Instanzargumente erfordern (dh den Funktionskörper). Dieser Dekorator eliminiert Warnungen vor "nicht verwendeten Parametern selbst" im PyCharm-Inspektor.
Tehwalrus

798

Eine statische Methode ist eine Methode, die nichts über die Klasse oder Instanz weiß, für die sie aufgerufen wurde. Es werden nur die Argumente abgerufen, die übergeben wurden, kein implizites erstes Argument. In Python ist es grundsätzlich nutzlos - Sie können einfach eine Modulfunktion anstelle einer statischen Methode verwenden.

Eine Klassenmethode hingegen ist eine Methode, die die Klasse, für die sie aufgerufen wurde, oder die Klasse der Instanz, für die sie aufgerufen wurde, als erstes Argument übergibt. Dies ist nützlich, wenn die Methode eine Factory für die Klasse sein soll: Da sie die tatsächliche Klasse erhält, für die sie als erstes Argument aufgerufen wurde, können Sie immer die richtige Klasse instanziieren, auch wenn Unterklassen beteiligt sind. Beobachten Sie beispielsweise, wie dict.fromkeys()eine Klassenmethode eine Instanz der Unterklasse zurückgibt, wenn sie für eine Unterklasse aufgerufen wird:

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

714
Eine statische Methode ist nicht nutzlos - sie ist eine Möglichkeit, eine Funktion in eine Klasse einzufügen (weil sie logischerweise dorthin gehört) und gleichzeitig anzuzeigen, dass kein Zugriff auf die Klasse erforderlich ist.
Tony Meyer

136
Daher nur "im Grunde" nutzlos. Eine solche Organisation sowie die Abhängigkeitsinjektion sind gültige Verwendungen statischer Methoden. Da jedoch Module und keine Klassen wie in Java die Grundelemente der Codeorganisation in Python sind, ist ihre Verwendung und Nützlichkeit selten.
Thomas Wouters

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

106
Vielleicht um der Erbschaft willen? Statische Methoden können wie Instanzmethoden und Klassenmethoden vererbt und überschrieben werden, und die Suche funktioniert wie erwartet (im Gegensatz zu Java). Statische Methoden werden nicht wirklich statisch aufgelöst, unabhängig davon, ob sie für die Klasse oder die Instanz aufgerufen werden. Der einzige Unterschied zwischen Klassen- und statischen Methoden ist das implizite erste Argument.
Haridsv

80
Sie erstellen auch einen saubereren Namespace und erleichtern das Verständnis der Funktion, die etwas mit der Klasse zu tun hat.
Imbrondir

149

Grundsätzlich @classmethodhat eine Methode, deren erstes Argument die Klasse ist, von der sie aufgerufen wird (und nicht die Klasseninstanz), @staticmethodkeine impliziten Argumente.


103

Offizielle Python-Dokumente:

@classmethod

Eine Klassenmethode empfängt die Klasse als implizites erstes Argument, genau wie eine Instanzmethode die Instanz empfängt. Verwenden Sie dieses Idiom, um eine Klassenmethode zu deklarieren:

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

Das @classmethodFormular ist ein Funktionsdekorateur. Weitere Informationen finden Sie in der Beschreibung der Funktionsdefinitionen unter Funktionsdefinitionen .

Es kann entweder für die Klasse (z. B. C.f()) oder für eine Instanz (z. B. C().f()) aufgerufen werden . Die Instanz wird bis auf ihre Klasse ignoriert. Wenn eine Klassenmethode für eine abgeleitete Klasse aufgerufen wird, wird das abgeleitete Klassenobjekt als implizites erstes Argument übergeben.

Klassenmethoden unterscheiden sich von statischen C ++ - oder Java-Methoden. Wenn Sie diese möchten, lesen Sie staticmethod()in diesem Abschnitt.

@staticmethod

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

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

Das @staticmethodFormular ist ein Funktionsdekorateur. Weitere Informationen finden Sie in der Beschreibung der Funktionsdefinitionen unter Funktionsdefinitionen .

Es kann entweder für die Klasse (z. B. C.f()) oder für eine Instanz (z. B. C().f()) aufgerufen werden . Die Instanz wird bis auf ihre Klasse ignoriert.

Statische Methoden in Python ähneln denen in Java oder C ++. Ein ausführlicheres Konzept finden Sie classmethod()in diesem Abschnitt.


Gibt es keinen Fehler in den Dokumenten? Sollte nicht bei staticmethod sein: "Die Instanz und ihre Klasse werden beide ignoriert." anstelle von "Die Instanz wird bis auf ihre Klasse ignoriert."?
Mirek

Es kann ein Fehler beim Ausschneiden und Einfügen sein, aber genau genommen können Sie keine Methode für eine Klasse aufrufen, wenn Sie die Klasse ignorieren.
Aaron Bentley

76

Hier ist ein kurzer Artikel zu dieser Frage

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

Die Funktion @classmethod kann auch aufgerufen werden, ohne die Klasse zu instanziieren. Ihre Definition folgt jedoch der Unterklasse und nicht der übergeordneten Klasse über die Vererbung. Das liegt daran, dass das erste Argument für die @ classmethod-Funktion immer cls (class) sein muss.


1
Bedeutet das also, dass ich durch die Verwendung einer statischen Methode immer an die übergeordnete Klasse und mit der Klassenmethode an die Klasse gebunden bin, in der ich die Klassenmethode deklariere (in diesem Fall die Unterklasse)?
Mohan Gulati

7
Nein. Wenn Sie eine statische Methode verwenden, sind Sie überhaupt nicht gebunden. Es gibt keinen impliziten ersten Parameter. Mit classmethod erhalten Sie als impliziten ersten Parameter die Klasse, für die Sie die Methode aufgerufen haben (wenn Sie sie direkt für eine Klasse aufgerufen haben), oder die Klasse der Instanz, für die Sie die Methode aufgerufen haben (wenn Sie sie für eine Instanz aufgerufen haben).
Matt Anderson

6
Könnte ein wenig erweitert werden, um zu zeigen, dass Klassenmethoden durch eine Klasse als erstes Argument direkten Zugriff auf andere Klassenattribute und -methoden haben, statische Methoden jedoch nicht (dafür müssten sie MyClass.attr fest codieren)
MestreLion

"Die Definition ist durch Vererbung unveränderlich." macht in Python keinen Sinn, Sie können eine statische Methode ganz gut überschreiben.
cz

68

Um zu entscheiden, ob Sie @staticmethod oder @classmethod verwenden möchten, müssen Sie sich Ihre Methode ansehen. Wenn Ihre Methode auf andere Variablen / Methoden in Ihrer Klasse zugreift, verwenden Sie @classmethod . Wenn Ihre Methode jedoch keine anderen Teile der Klasse berührt, verwenden Sie @staticmethod.

class Apple:

    _counter = 0

    @staticmethod
    def about_apple():
        print('Apple is good for you.')

        # note you can still access other member of the class
        # but you have to use the class instance 
        # which is not very nice, because you have repeat yourself
        # 
        # For example:
        # @staticmethod
        #    print('Number of apples have been juiced: %s' % Apple._counter)
        #
        # @classmethod
        #    print('Number of apples have been juiced: %s' % cls._counter)
        #
        #    @classmethod is especially useful when you move your function to other class,
        #       you don't have to rename the class reference 

    @classmethod
    def make_apple_juice(cls, number_of_apples):
        print('Make juice:')
        for i in range(number_of_apples):
            cls._juice_this(i)

    @classmethod
    def _juice_this(cls, apple):
        print('Juicing %d...' % apple)
        cls._counter += 1

Was wäre der Vorteil von classmethod und cls._counter gegenüber staticmethod und Apple._counter
Robert Nowak

1
cls._counterwäre auch dann cls._counternoch, wenn der Code in eine andere Klasse gestellt wird oder der Klassenname geändert wird. Apple._counterist spezifisch für die AppleKlasse; Für eine andere Klasse oder wenn der Klassenname geändert wird, müssten Sie die referenzierte Klasse ändern.
Kiamlaluno

53

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

Möglicherweise haben Sie Python-Code wie diesen Pseudocode gesehen, der die Signaturen der verschiedenen Methodentypen demonstriert und eine Dokumentzeichenfolge zur Erläuterung der einzelnen Methoden bereitstellt:

class Foo(object):

    def a_normal_instance_method(self, arg_1, kwarg_2=None):
        '''
        Return a value that is a function of the instance with its
        attributes, and other arguments such as arg_1 and kwarg2
        '''

    @staticmethod
    def a_static_method(arg_0):
        '''
        Return a value that is a function of arg_0. It does not know the 
        instance or class it is called from.
        '''

    @classmethod
    def a_class_method(cls, arg1):
        '''
        Return a value that is a function of the class and other arguments.
        respects subclassing, it is called with the class it is called from.
        '''

Die normale Instanzmethode

Zuerst werde ich erklären a_normal_instance_method. Dies wird genau als " Instanzmethode " bezeichnet " bezeichnet. Wenn eine Instanzmethode verwendet wird, wird sie als Teilfunktion verwendet (im Gegensatz zu einer Gesamtfunktion, die für alle Werte definiert ist, wenn sie im Quellcode angezeigt wird). Bei Verwendung wird das erste der Argumente als Instanz der vordefiniert Objekt mit all seinen gegebenen Attributen. Es ist an die Instanz des Objekts gebunden und muss von einer Instanz des Objekts aufgerufen werden. In der Regel wird auf verschiedene Attribute der Instanz zugegriffen.

Dies ist beispielsweise eine Instanz einer Zeichenfolge:

', '

Wenn wir die Instanzmethode joinfür diese Zeichenfolge verwenden, um eine andere iterable zu verbinden, ist dies ganz offensichtlich eine Funktion der Instanz und zusätzlich eine Funktion der iterierbaren Liste ['a', 'b', 'c']:

>>> ', '.join(['a', 'b', 'c'])
'a, b, c'

Gebundene Methoden

Instanzmethoden können zur späteren Verwendung über eine gepunktete Suche gebunden werden.

Dies bindet die str.joinMethode beispielsweise an die ':'Instanz:

>>> join_with_colons = ':'.join 

Und später können wir dies als eine Funktion verwenden, an die bereits das erste Argument gebunden ist. Auf diese Weise funktioniert es wie eine Teilfunktion für die Instanz:

>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'

Statische Methode

Die statische Methode funktioniert nicht nehmen Sie die Instanz als Argument.

Es ist einer Funktion auf Modulebene sehr ähnlich.

Eine Funktion auf Modulebene muss sich jedoch im Modul befinden und speziell an andere Orte importiert werden, an denen sie verwendet wird.

Wenn es jedoch an das Objekt angehängt ist, folgt es dem Objekt bequem durch Importieren und Vererbung.

Ein Beispiel für eine statische Methode ist str.maketrans, aus dem verschobenstring Verschieben Modul in Python 3. Dadurch wird eine Übersetzungstabelle für den Verbrauch durch geeignet str.translate. Es scheint ziemlich albern zu sein, wenn es von einer Instanz eines Strings verwendet wird, wie unten gezeigt, aber das Importieren der Funktion aus dem stringModul ist ziemlich umständlich, und es ist schön, sie aus der Klasse aufrufen zu können, wie instr.maketrans

# demonstrate same function whether called from instance or not:
>>> ', '.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
>>> str.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}

In Python 2 müssen Sie diese Funktion aus dem zunehmend weniger nützlichen String-Modul importieren:

>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'

Klassenmethode

Eine Klassenmethode ähnelt einer Instanzmethode darin, dass sie ein implizites erstes Argument verwendet, aber anstatt die Instanz zu verwenden, wird die Klasse verwendet. Häufig werden diese als alternative Konstruktoren für eine bessere semantische Verwendung verwendet und unterstützen die Vererbung.

Das kanonischste Beispiel für eine eingebaute Klassenmethode ist dict.fromkeys. Es wird als alternativer Konstruktor von dict verwendet (gut geeignet, wenn Sie wissen, was Ihre Schlüssel sind und einen Standardwert für sie wünschen).

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

Wenn wir diktieren, können wir denselben Konstruktor verwenden, der eine Instanz der Unterklasse erstellt.

>>> class MyDict(dict): 'A dict subclass, use to demo classmethods'
>>> md = MyDict.fromkeys(['a', 'b', 'c'])
>>> md
{'a': None, 'c': None, 'b': None}
>>> type(md)
<class '__main__.MyDict'>

Weitere ähnliche Beispiele für alternative Konstruktoren finden Sie im Pandas-Quellcode sowie in der offiziellen Python-Dokumentation zu classmethodund staticmethod.


43

Ich fing an, Programmiersprache mit C ++ und dann Java und dann Python zu lernen, und so störte mich diese Frage auch sehr, bis ich die einfache Verwendung von jedem verstand.

Klassenmethode: Python hat im Gegensatz zu Java und C ++ keine Konstruktorüberladung. Und um dies zu erreichen, könnten Sie verwenden classmethod. Das folgende Beispiel erklärt dies

Nehmen wir an, wir haben eine PersonKlasse, die zwei Argumente akzeptiert first_nameund last_namedie Instanz von erstellt Person.

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

Wenn die Anforderung kommt, dass Sie eine Klasse nur mit einem einzigen Namen erstellen müssen first_name, können Sie in Python so etwas nicht tun.

Dies gibt Ihnen eine Fehlermeldung, wenn Sie versuchen, ein Objekt (eine Instanz) zu erstellen.

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def __init__(self, first_name):
        self.first_name = first_name

Sie können jedoch dasselbe erreichen, @classmethodwie unten erwähnt

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @classmethod
    def get_person(cls, first_name):
        return cls(first_name, "")

Statische Methode: Dies ist ziemlich einfach, es ist nicht an eine Instanz oder Klasse gebunden, und Sie können dies einfach mit dem Klassennamen aufrufen.

Nehmen wir also an, Sie benötigen im obigen Beispiel eine Validierung, first_namedie 20 Zeichen nicht überschreiten sollte. Sie können dies einfach tun.

@staticmethod  
def validate_name(name):
    return len(name) <= 20

und Sie könnten einfach mit anrufen class name

Person.validate_name("Gaurang Shah")

2
Es ist ein alter Beitrag, aber eine pythonischere Methode, um einen Konstruktor zu erreichen, der entweder ein oder zwei Argumente akzeptiert, würde def __init__(self, first_name, last_name="")anstelle der Klassenmethode verwendet get_person. Auch das Ergebnis ist in diesem Fall genau das gleiche.
Akarilimano

31

Ich denke, eine bessere Frage ist "Wann würden Sie @classmethod vs @staticmethod verwenden?"

Mit @classmethod können Sie problemlos auf private Mitglieder zugreifen, die der Klassendefinition zugeordnet sind. Dies ist eine großartige Möglichkeit, Singletons oder Factory-Klassen zu erstellen, die die Anzahl der vorhandenen Instanzen der erstellten Objekte steuern.

@staticmethod bietet marginale Leistungssteigerungen, aber ich habe noch keine produktive Verwendung einer statischen Methode innerhalb einer Klasse gesehen, die als eigenständige Funktion außerhalb der Klasse nicht erreicht werden konnte.


31

@decorators wurden in Python 2.4 hinzugefügt. Wenn Sie Python <2.4 verwenden, können Sie die Funktionen classmethod () und staticmethod () verwenden.

Wenn Sie beispielsweise eine Factory-Methode erstellen möchten (eine Funktion, die eine Instanz einer anderen Implementierung einer Klasse zurückgibt, je nachdem, welches Argument sie erhält), können Sie Folgendes tun:

class Cluster(object):

    def _is_cluster_for(cls, name):
        """
        see if this class is the cluster with this name
        this is a classmethod
        """ 
        return cls.__name__ == name
    _is_cluster_for = classmethod(_is_cluster_for)

    #static method
    def getCluster(name):
        """
        static factory method, should be in Cluster class
        returns a cluster object for the given name
        """
        for cls in Cluster.__subclasses__():
            if cls._is_cluster_for(name):
                return cls()
    getCluster = staticmethod(getCluster)

Beachten Sie auch, dass dies ein gutes Beispiel für die Verwendung einer Klassenmethode und einer statischen Methode ist. Die statische Methode gehört eindeutig zur Klasse, da sie den Klassencluster intern verwendet. Die Klassenmethode benötigt nur Informationen über die Klasse und keine Instanz des Objekts.

Ein weiterer Vorteil, _is_cluster_forwenn Sie die Methode zu einer Klassenmethode machen, besteht darin, dass eine Unterklasse entscheiden kann, ihre Implementierung zu ändern, möglicherweise weil sie ziemlich allgemein gehalten ist und mehr als einen Clustertyp verarbeiten kann. Daher reicht es nicht aus, nur den Namen der Klasse zu überprüfen.


28

Statische Methoden:

  • Einfache Funktionen ohne Selbstargument.
  • Arbeiten Sie an Klassenattributen. nicht auf Instanzattributen.
  • Kann sowohl über die Klasse als auch über die Instanz aufgerufen werden.
  • Mit der integrierten Funktion staticmethod () werden sie erstellt.

Vorteile statischer Methoden:

  • Es lokalisiert den Funktionsnamen im Klassenbereich
  • Der Funktionscode wird näher an den Verwendungsort verschoben
  • Der Import ist im Vergleich zu Funktionen auf Modulebene bequemer, da nicht jede Methode speziell importiert werden muss

    @staticmethod
    def some_static_method(*args, **kwds):
        pass

Klassenmethoden:

  • Funktionen mit dem ersten Argument als Klassenname.
  • Kann sowohl über die Klasse als auch über die Instanz aufgerufen werden.
  • Diese werden mit der in classmethod integrierten Funktion erstellt.

     @classmethod
     def some_class_method(cls, *args, **kwds):
         pass

22

@staticmethodDeaktiviert einfach die Standardfunktion als Methodendeskriptor. classmethod umschließt Ihre Funktion mit einem aufrufbaren Container, der als erstes Argument einen Verweis auf die besitzende Klasse übergibt:

>>> class C(object):
...  pass
... 
>>> def f():
...  pass
... 
>>> staticmethod(f).__get__(None, C)
<function f at 0x5c1cf0>
>>> classmethod(f).__get__(None, C)
<bound method type.f of <class '__main__.C'>>

Hat zwar classmethodeinen Laufzeit-Overhead, ermöglicht aber den Zugriff auf die besitzende Klasse. Alternativ empfehle ich, eine Metaklasse zu verwenden und die Klassenmethoden auf diese Metaklasse anzuwenden:

>>> class CMeta(type):
...  def foo(cls):
...   print cls
... 
>>> class C(object):
...  __metaclass__ = CMeta
... 
>>> C.foo()
<class '__main__.C'>

1
Ein möglicher Nachteil einer Metaklasse, der mir sofort einfällt, ist, dass Sie die Klassenmethode nicht direkt für eine Instanz aufrufen können. c = C(); c.foo()löst AttributeError aus, das müssten Sie tun type(c).foo(). Dies könnte auch als eine Funktion angesehen werden - ich kann mir nicht vorstellen, warum Sie das möchten.
Aaron Hall

20

Die endgültige Anleitung zur Verwendung statischer, klassenbezogener oder abstrakter Methoden in Python ist ein guter Link für dieses Thema und fasst es wie folgt zusammen.

@staticmethodFunktion ist nichts anderes als eine innerhalb einer Klasse definierte Funktion. Es kann aufgerufen werden, ohne zuerst die Klasse zu instanziieren. Die Definition ist über die Vererbung unveränderlich.

  • Python muss keine gebundene Methode für ein Objekt instanziieren.
  • Dies erleichtert die Lesbarkeit des Codes und hängt nicht vom Status des Objekts selbst ab.

@classmethodDie Funktion kann auch aufgerufen werden, ohne die Klasse zu instanziieren. Ihre Definition folgt jedoch der Unterklasse. Die übergeordnete Klasse kann über die Vererbung von der Unterklasse überschrieben werden. Das liegt daran, dass das erste Argument für die @classmethodFunktion immer cls (Klasse) sein muss.

  • Factory-Methoden , mit denen eine Instanz für eine Klasse erstellt wird, beispielsweise mithilfe einer Vorverarbeitung.
  • Statische Methoden, die statische Methoden aufrufen : Wenn Sie statische Methoden in mehrere statische Methoden aufteilen, sollten Sie den Klassennamen nicht fest codieren, sondern Klassenmethoden verwenden

Danke @zangw - die ererbte Unveränderlichkeit der statischen Funktion ist der Hauptunterschied, wie es scheint
hard_working_ant

18

Nur das erste Argument unterscheidet sich :

  • normale Methode: Das aktuelle Objekt wird automatisch als (zusätzliches) erstes Argument übergeben
  • Klassenmethode: Die Klasse des aktuellen Objekts wird automatisch als (zusätzliches) Faustargument übergeben
  • statische Methode: Es werden keine zusätzlichen Argumente automatisch übergeben. Was Sie an die Funktion übergeben haben, erhalten Sie.

Ausführlicher...

normale Methode

Wenn die Methode eines Objekts aufgerufen wird, erhält es automatisch ein zusätzliches Argument selfals erstes Argument. Das heißt, Methode

def f(self, x, y)

muss mit 2 Argumenten aufgerufen werden. selfwird automatisch übergeben und ist das Objekt selbst .

Klassenmethode

Wenn die Methode dekoriert ist

@classmethod
def f(cls, x, y)

Das automatisch bereitgestellte Argument ist nicht self , sondern die Klasse von self .

statische Methode

Wenn die Methode dekoriert ist

@staticmethod
def f(x, y)

Die Methode erhält überhaupt kein automatisches Argument. Es werden nur die Parameter angegeben, mit denen es aufgerufen wird.

Verwendungen

  • classmethod wird hauptsächlich für alternative Konstruktoren verwendet.
  • staticmethodverwendet nicht den Status des Objekts. Es könnte eine Funktion außerhalb einer Klasse sein. Es wird nur in die Klasse eingefügt, um Funktionen mit ähnlichen Funktionen zu gruppieren (z. B. die Mathstatischen Methoden der Java- Klasse).
class Point
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def frompolar(cls, radius, angle):
        """The `cls` argument is the `Point` class itself"""
        return cls(radius * cos(angle), radius * sin(angle))

    @staticmethod
    def angle(x, y):
        """this could be outside the class, but we put it here 
just because we think it is logically related to the class."""
        return atan(y, x)


p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)

angle = Point.angle(3, 2)

17

Lassen Sie mich zunächst die Ähnlichkeit zwischen einer mit @classmethod dekorierten Methode und @staticmethod erläutern.

Ähnlichkeit: Beide können für die Klasse selbst aufgerufen werden und nicht nur für die Instanz der Klasse. In gewisser Weise sind beide Methoden von Class .

Unterschied: Eine Klassenmethode empfängt die Klasse selbst als erstes Argument, eine statische Methode nicht.

Eine statische Methode ist also gewissermaßen nicht an die Klasse selbst gebunden und hängt nur daran, nur weil sie möglicherweise eine verwandte Funktionalität hat.

>>> class Klaus:
        @classmethod
        def classmthd(*args):
            return args

        @staticmethod
        def staticmthd(*args):
            return args

# 1. Call classmethod without any arg
>>> Klaus.classmthd()  
(__main__.Klaus,)  # the class gets passed as the first argument

# 2. Call classmethod with 1 arg
>>> Klaus.classmthd('chumma')
(__main__.Klaus, 'chumma')

# 3. Call staticmethod without any arg
>>> Klaus.staticmthd()  
()

# 4. Call staticmethod with 1 arg
>>> Klaus.staticmthd('chumma')
('chumma',)

11

Eine weitere Überlegung in Bezug auf die statische Methode im Vergleich zur Klassenmethode betrifft die Vererbung. Angenommen, Sie haben die folgende Klasse:

class Foo(object):
    @staticmethod
    def bar():
        return "In Foo"

Und dann möchten Sie bar()in einer untergeordneten Klasse überschreiben :

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"

Dies funktioniert, aber beachten Sie, dass die bar()Implementierung in der untergeordneten Klasse ( Foo2) jetzt nichts Spezifisches mehr für diese Klasse nutzen kann. Zum Beispiel, sagen wir Foo2hatten eine Methode aufgerufen , magic()die Sie im verwenden möchten Foo2Umsetzung bar():

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"
    @staticmethod
    def magic():
        return "Something useful you'd like to use in bar, but now can't" 

Die Abhilfe wäre hier zu nennen Foo2.magic()in bar(), aber dann selbst Sie zu wiederholen (wenn der Name Foo2ändert, werden Sie dürfen nicht vergessen, dass aktualisieren bar()Methode).

Für mich ist dies eine leichte Verletzung des Open / Closed-Prinzips , da eine getroffene Entscheidung FooIhre Fähigkeit beeinträchtigt, allgemeinen Code in einer abgeleiteten Klasse umzugestalten (dh weniger offen für Erweiterungen). Wenn bar()ein waren classmethodwürden wir in Ordnung sein:

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

Gibt: In Foo2 MAGIC


7

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 # outputs 1
        self.say_hi()
        self.say_hi_static()
        self.say_hi_class()

    @staticmethod
    def run_static():
        print A.x  # outputs 0
        # A.say_hi() #  wrong
        A.say_hi_static()
        A.say_hi_class()

    @classmethod
    def run_class(cls):
        print cls.x # outputs 0
        # cls.say_hi() #  wrong
        cls.say_hi_static()
        cls.say_hi_class()

1 - Wir können statische und Klassenmethoden direkt aufrufen, ohne sie zu initialisieren

# A.run_self() #  wrong
A.run_static()
A.run_class()

2- Die statische Methode kann keine Selbstmethode aufrufen, kann jedoch andere statische Methoden und Klassenmethoden aufrufen

3- Statische Methoden gehören zur Klasse und verwenden überhaupt kein Objekt.

4- Die Klassenmethode ist nicht an ein Objekt gebunden, sondern an eine Klasse.


Anzeige 2: Sind Sie sicher? Wie könnte die statische Methode die Klassenmethode aufrufen? Es hat keinen Bezug dazu (auf seine Klasse).
Mirek

7

@classmethod: kann verwendet werden, um einen gemeinsamen globalen Zugriff auf alle Instanzen dieser Klasse zu erstellen ..... wie das Aktualisieren eines Datensatzes durch mehrere Benutzer .... Ich fand insbesondere, dass es auch beim Erstellen von Singletons voll ist ..: )

@static-Methode: hat nichts mit der Klasse oder Instanz zu tun, die mit ... verknüpft ist. Zur besseren Lesbarkeit kann jedoch eine statische Methode verwendet werden


5

Vielleicht möchten Sie den Unterschied berücksichtigen zwischen:

Class A:
    def foo():  # no self parameter, no decorator
        pass

und

Class B:
    @staticmethod
    def foo():  # no self parameter
        pass

Dies hat sich zwischen Python2 und Python3 geändert:

python2:

>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

python3:

>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

Also mit @staticmethod for-Methoden, die nur direkt von der Klasse aufgerufen werden, ist in python3 optional geworden. Wenn Sie sie sowohl aus der Klasse als auch aus der Instanz aufrufen möchten, müssen Sie weiterhin den @staticmethodDekorator verwenden.

Die anderen Fälle wurden durch die Antwort von unutbus gut abgedeckt.


5

Eine Klassenmethode empfängt die Klasse als implizites erstes Argument, genau wie eine Instanzmethode die Instanz empfängt. Es ist eine Methode, die an die Klasse und nicht an das Objekt der Klasse gebunden ist. Sie hat Zugriff auf den Status der Klasse, da ein Klassenparameter verwendet wird, der auf die Klasse und nicht auf die Objektinstanz verweist. Es kann einen Klassenstatus ändern, der für alle Instanzen der Klasse gilt. Beispielsweise kann eine Klassenvariable geändert werden, die auf alle Instanzen anwendbar ist.

Andererseits erhält eine statische Methode im Vergleich zu Klassenmethoden oder Instanzmethoden kein implizites erstes Argument. Und kann nicht auf den Klassenstatus zugreifen oder diesen ändern. Es gehört nur zur Klasse, weil dies aus gestalterischer Sicht der richtige Weg ist. Die Funktionalität ist jedoch zur Laufzeit nicht an die Klasse gebunden.

Verwenden Sie als Richtlinie statische Methoden als Dienstprogramme und Klassenmethoden beispielsweise als Factory. Oder vielleicht um einen Singleton zu definieren. Verwenden Sie Instanzmethoden, um den Status und das Verhalten von Instanzen zu modellieren.

Hoffe ich war klar!


4

Mein Beitrag zeigt den Unterschied zwischen @classmethod, @staticmethodund Instanzmethoden, einschließlich der Frage, wie eine Instanz indirekt a aufrufen kann @staticmethod. Aber anstatt indirekt ein @staticmethodvon einer Instanz aufzurufen , kann es "pythonischer" sein, es privat zu machen. Etwas von einer privaten Methode zu bekommen, wird hier nicht demonstriert, aber es ist im Grunde das gleiche Konzept.

#!python3

from os import system
system('cls')
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

class DemoClass(object):
    # instance methods need a class instance and
    # can access the instance through 'self'
    def instance_method_1(self):
        return 'called from inside the instance_method_1()'

    def instance_method_2(self):
        # an instance outside the class indirectly calls the static_method
        return self.static_method() + ' via instance_method_2()'

    # class methods don't need a class instance, they can't access the
    # instance (self) but they have access to the class itself via 'cls'
    @classmethod
    def class_method(cls):
        return 'called from inside the class_method()'

    # static methods don't have access to 'cls' or 'self', they work like
    # regular functions but belong to the class' namespace
    @staticmethod
    def static_method():
        return 'called from inside the static_method()'
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# works even if the class hasn't been instantiated
print(DemoClass.class_method() + '\n')
''' called from inside the class_method() '''

# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '\n')
''' called from inside the static_method() '''
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# >>>>> all methods types can be called on a class instance <<<<<
# instantiate the class
democlassObj = DemoClass()

# call instance_method_1()
print(democlassObj.instance_method_1() + '\n')
''' called from inside the instance_method_1() '''

# # indirectly call static_method through instance_method_2(), there's really no use
# for this since a @staticmethod can be called whether the class has been
# instantiated or not
print(democlassObj.instance_method_2() + '\n')
''' called from inside the static_method() via instance_method_2() '''

# call class_method()
print(democlassObj.class_method() + '\n')
'''  called from inside the class_method() '''

# call static_method()
print(democlassObj.static_method())
''' called from inside the static_method() '''

"""
# whether the class is instantiated or not, this doesn't work
print(DemoClass.instance_method_1() + '\n')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""

2

Klassenmethoden werden, wie der Name schon sagt, verwendet, um Änderungen an Klassen und nicht an den Objekten vorzunehmen. Um Änderungen an Klassen vorzunehmen, ändern sie die Klassenattribute (nicht die Objektattribute), da Sie auf diese Weise Klassen aktualisieren. Dies ist der Grund, warum Klassenmethoden die Klasse (üblicherweise mit 'cls' bezeichnet) als erstes Argument verwenden.

class A(object):
    m=54

    @classmethod
    def class_method(cls):
        print "m is %d" % cls.m

Statische Methoden hingegen werden verwendet, um Funktionen auszuführen, die nicht an die Klasse gebunden sind, dh keine Klassenvariablen lesen oder schreiben. Daher nehmen statische Methoden keine Klassen als Argumente an. Sie werden verwendet, damit Klassen Funktionen ausführen können, die nicht direkt mit dem Zweck der Klasse zusammenhängen.

class X(object):
    m=54 #will not be referenced

    @staticmethod
    def static_method():
        print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."

2

Analysieren Sie @staticmethod buchstäblich liefern verschiedene Erkenntnisse.

Eine normale Methode einer Klasse ist eine implizite dynamische Methode, die die Instanz als erstes Argument verwendet.
Im Gegensatz dazu nimmt eine statische Methode die Instanz nicht als erstes Argument und wird daher als "statisch" bezeichnet .

Eine statische Methode ist in der Tat eine solche normale Funktion wie die außerhalb einer Klassendefinition.
Es ist zum Glück in der Klasse zusammengefasst, nur um näher an der Stelle zu stehen, an der es angewendet wird, oder Sie können herumblättern, um es zu finden.


2

Ich denke, eine rein Python-Version von staticmethodund zu gebenclassmethod würde helfen, den Unterschied zwischen ihnen auf Sprachebene zu verstehen.

Beide sind Nicht-Daten-Deskriptoren (es wäre einfacher, sie zu verstehen, wenn Sie zuerst mit Deskriptoren vertraut sind ).

class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        return self.f


class ClassMethod(object):
    "Emulate PyClassMethod_Type() in Objects/funcobject.c"
    def __init__(self, f):
        self.f = f

    def __get__(self, obj, cls=None):
        def inner(*args, **kwargs):
            if cls is None:
                cls = type(obj)
            return self.f(cls, *args, **kwargs)
        return inner

1

staticmethod hat keinen Zugriff auf Attribute des Objekts, der Klasse oder der übergeordneten Klassen in der Vererbungshierarchie. Es kann direkt in der Klasse aufgerufen werden (ohne ein Objekt zu erstellen).

Klassenmethode hat keinen Zugriff auf Attribute des Objekts. Es kann jedoch auf Attribute der Klasse und der übergeordneten Klassen in der Vererbungshierarchie zugreifen. Es kann direkt in der Klasse aufgerufen werden (ohne ein Objekt zu erstellen). Wenn es für das Objekt aufgerufen wird, ist es dasselbe wie die normale Methode, die nicht self.<attribute(s)>zugreift und darauf zugreiftself.__class__.<attribute(s)> nur darauf .

Denken Sie, wir haben eine Klasse mit b=2, wir werden ein Objekt erstellen und dieses darauf b=4zurücksetzen. Staticmethod kann nicht auf nichts von vorherigen zugreifen. Classmethod kann .b==2nur über zugreifen cls.b. Die normale Methode kann auf beide zugreifen: .b==4über self.bund .b==2über self.__class__.b.

Wir könnten dem KISS-Stil folgen (halten Sie es einfach, dumm): Verwenden Sie keine statischen Methoden und Klassenmethoden, verwenden Sie keine Klassen, ohne sie zu instanziieren, greifen Sie nur auf die Attribute des Objekts zu self.attribute(s). Es gibt Sprachen, in denen das OOP auf diese Weise implementiert wird, und ich denke, es ist keine schlechte Idee. :) :)


Eine wichtige Sache für Klassenmethoden: Wenn Sie ein Attribut in der Klassenmethode ändern, haben alle vorhandenen Objekte dieser Klasse, die dieses Attribut nicht explizit festlegen, den geänderten Wert.
Mirek

-4

Ein schneller Überblick über andere identische Methoden in iPython zeigt, dass @staticmethodgeringfügige Leistungssteigerungen (in Nanosekunden) erzielt werden, ansonsten scheint dies jedoch keine Funktion zu erfüllen . Außerdem werden Leistungssteigerungen wahrscheinlich durch die zusätzliche Verarbeitung der Methode staticmethod()während der Kompilierung (die vor jeder Codeausführung beim Ausführen eines Skripts erfolgt) zunichte gemacht.

Aus Gründen der Lesbarkeit des Codes würde ich es vermeiden, es @staticmethodsei denn, Ihre Methode wird für viele Arbeiten verwendet, bei denen die Nanosekunden zählen.


7
"Sonst scheint keine Funktion zu erfüllen": nicht unbedingt wahr. Siehe obige Diskussion.
Keith Pinson
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.