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


Was ist der Unterschied zwischen einer mit dekorierten Funktion? @staticmethod und eines mit dekoriert @classmethod?


2671
2017-09-25 21:01


Ursprung


Antworten:


Vielleicht hilft ein bisschen Beispielcode: Beachten Sie den Unterschied in den Rufsignaturen von foo, class_foo und 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 ist der übliche Weg beschrieben, wie eine Objektinstanz eine Methode aufruft. Die Objektinstanz, a, wird implizit als erstes Argument übergeben.

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

Mit Klassenmethodenwird die Klasse der Objektinstanz implizit als erstes Argument übergeben self.

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

Sie können auch anrufen class_foo Verwenden der Klasse. In der Tat, wenn Sie etwas definieren zu sein Eine Klassenmethode ist wahrscheinlich, weil Sie beabsichtigen, sie von der Klasse und nicht von einer Klasseninstanz aus aufzurufen. A.foo(1) hätte einen TypeError ausgelöst, aber A.class_foo(1) funktioniert gut:

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

Eine Verwendung, die Leute für Klassenmethoden gefunden haben, ist zu erstellen vererbbare alternative Konstruktoren.


Mit statischen Methoden, weder self (die Objektinstanz) noch cls (Die Klasse) wird implizit als erstes Argument übergeben. Sie verhalten sich wie normale 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.


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

a ist gebunden an foo. Das ist mit dem Begriff "gebunden" gemeint:

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

Mit a.class_foo, a ist nicht verpflichtet class_fooeher die Klasse A ist gebunden an class_foo.

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

Hier, mit einer statischen Methode, obwohl es eine Methode ist, a.static_foo kommt einfach zurück eine gute Ole-Funktion ohne gebundene Argumente. static_foo erwartet 1 Argument und a.static_foo erwartet 1 Argument auch.

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

Und natürlich passiert das Gleiche, wenn Sie anrufen static_foo mit der Klasse A stattdessen.

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

2316
2017-11-03 19:13



EIN statische Methode ist eine Methode, die nichts über die Klasse oder Instanz weiß, an der sie aufgerufen wurde. Es erhält nur die Argumente, die übergeben wurden, kein implizites erstes Argument. In Python ist es praktisch nutzlos - Sie können einfach eine Modulfunktion anstelle einer statischen Methode verwenden.

EIN KlassenmethodeAuf der anderen Seite ist eine Methode, die die Klasse, an der sie aufgerufen wurde, oder die Klasse der Instanz, an der sie aufgerufen wurde, als erstes Argument übergeben wird. Dies ist nützlich, wenn Sie möchten, dass die Methode eine Factory für die Klasse ist: Da sie die tatsächliche Klasse als erstes Argument aufruft, können Sie immer die richtige Klasse instanziieren, selbst wenn Unterklassen beteiligt sind. Beobachte zum Beispiel wie dict.fromkeys(), eine Klassenmethode, gibt eine Instanz der Unterklasse zurück, 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
>>> 

654
2017-09-25 21:05



Grundsätzlich gilt @classmethod macht eine Methode, deren erstes Argument die Klasse ist, von der sie aufgerufen wird (und nicht die Klasseninstanz), @staticmethodhat keine impliziten Argumente.


110
2017-09-25 21:07



Offizielle Python-Dokumente:

@ Klassenmethode

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

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

Das @classmethod Form ist eine Funktion    Dekorateur - siehe Beschreibung von   Funktionsdefinitionen in Funktion   Definitionen für Details.

Es kann entweder in der Klasse aufgerufen werden   (sowie C.f()) oder auf einer Instanz   (sowie C().f()). Die Instanz ist   außer für seine Klasse ignoriert. Wenn ein   Klassenmethode wird für eine Ableitung aufgerufen   Klasse, das abgeleitete Klassenobjekt ist   als das implizierte erste Argument übergeben.

Klassenmethoden unterscheiden sich von C ++   oder statische Java-Methoden. wenn du willst   diese, seht ihr? staticmethod() in diesem   Sektion.

@StaticMethode

Eine statische Methode erhält keine   implizites erstes Argument. Um a zu deklarieren   statische Methode, verwende dieses Idiom:

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

Das @staticmethod Form ist eine Funktion    Dekorateur - siehe Beschreibung von   Funktionsdefinitionen in Funktion   Definitionen für Details.

Es kann entweder in der Klasse aufgerufen werden   (sowie C.f()) oder auf einer Instanz   (sowie C().f()). Die Instanz ist   außer für seine Klasse ignoriert.

Die statischen Methoden in Python sind ähnlich   zu denen in Java oder C ++ gefunden. Für ein   fortgeschrittenes Konzept, siehe    classmethod() in diesem Abschnitt.


76
2017-11-03 19:23



Hier ist ein kurzer Artikel zu dieser Frage

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

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


55
2017-11-03 19:02



Um zu entscheiden, ob zu verwenden @StaticMethode oder @ Klassenmethode Sie müssen in Ihre Methode schauen. Wenn Ihre Methode auf andere Variablen / Methoden in Ihrer Klasse zugreift, verwenden Sie @classmethod. Auf der anderen Seite, wenn Ihre Methode keine anderen Teile der Klasse berührt, dann 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

44
2018-04-22 15:40



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 einen Docstring bereitstellt, um jedes zu erklären:

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 es erklären a_normal_instance_method. Dies wird genau als "InstanzmethodeWenn eine Instanzmethode verwendet wird, wird sie als Teilfunktion verwendet (im Gegensatz zu einer Gesamtfunktion, die im Quellcode für alle Werte definiert ist). Wenn sie verwendet wird, wird das erste der Argumente als Instanz vordefiniert des Objekts mit all seinen gegebenen Attributen.Es ist die Instanz des Objekts gebunden, und es muss von einer Instanz des Objekts aufgerufen werden.Typischerweise wird es auf verschiedene Attribute der Instanz zugreifen.

Dies ist beispielsweise eine Instanz einer Zeichenfolge:

', '

Wenn wir die Instanzmethode verwenden, join auf dieser Zeichenfolge, um eine andere iterable zu verbinden, es ist ganz offensichtlich eine Funktion der Instanz, zusätzlich dazu, dass sie eine Funktion der iterierbaren Liste ist, ['a', 'b', 'c']:

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

Gebundene Methoden

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

Zum Beispiel bindet dies die str.join Methode zum ':' Beispiel:

>>> 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 tut es nicht nimm die Instanz als Argument.

Es ist einer Modulebene-Funktion sehr ähnlich.

Eine Modulebene-Funktion muss jedoch im Modul vorhanden sein und speziell an andere Stellen importiert werden, an denen sie verwendet wird.

Wenn es jedoch an das Objekt angehängt ist, wird es dem Objekt auch bequem durch Importieren und Vererben folgen.

Ein Beispiel für eine statische Methode ist str.maketrans, bewegt von der string Modul in Python 3. Es macht eine Übersetzungstabelle geeignet für den Verbrauch von str.translate. Es scheint ziemlich albern zu sein, wenn es aus einer Instanz einer Zeichenkette, wie unten gezeigt, verwendet wird, aber importiert die Funktion von der string Modul ist eher ungeschickt, und es ist schön, es aus der Klasse zu nennen, wie in str.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 immer weniger nützlichen String-Modul importieren:

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

Klassenmethode

Eine Klassenmethode ähnelt einer Instanzmethode insofern, als sie ein implizites erstes Argument benötigt, aber anstatt die Instanz zu übernehmen, benötigt sie die Klasse. Diese werden häufig als alternative Konstruktoren für eine bessere semantische Verwendung verwendet und unterstützen die Vererbung.

Das kanonischste Beispiel einer eingebauten 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 haben möchten.)

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

Wenn wir dict ableiten, 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'>

Siehe die Pandas Quellcode für andere ähnliche Beispiele alternativer Konstruktoren, und siehe auch die offizielle Python-Dokumentation zu classmethod und staticmethod.


38
2018-01-23 20:01



@decorators wurden in Python hinzugefügt 2.4 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 je nach Argument eine Instanz einer anderen Implementierung einer Klasse zurückgibt), 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 intern die Klasse Cluster verwendet. Die Klassenmethode benötigt nur Informationen über die Klasse und keine Instanz des Objekts.

Ein weiterer Vorteil der _is_cluster_for Methode Eine Klassenmethode ist so eine Unterklasse kann entscheiden, ihre Implementierung zu ändern, vielleicht weil es ziemlich generisch ist und mehr als einen Typ von Cluster behandeln kann, also nur den Namen der Klasse zu überprüfen wäre nicht genug.


27
2018-02-24 09:32



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

@classmethod ermöglicht Ihnen den einfachen Zugriff auf private Member, die der Klassendefinition zugeordnet sind. Dies ist eine großartige Möglichkeit, Singletons oder Factory-Klassen zu erstellen, die die Anzahl der Instanzen der erstellten Objekte steuern.

@staticmethod bietet marginale Leistungssteigerungen, aber ich muss noch eine produktive Verwendung einer statischen Methode innerhalb einer Klasse sehen, die nicht als eigenständige Funktion außerhalb der Klasse erreicht werden kann.


26
2018-05-19 15:27