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


Answers

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

Eine Klassenmethode hingegen ist eine Methode, die als erstes Argument die Klasse, an der sie aufgerufen wurde, oder die Klasse der Instanz, an der sie aufgerufen wurde, ü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. Beobachten Sie zum Beispiel, 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
>>> 
Question

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




Analyze @staticmethod literally providing different insights.

A normal method of a class is an implicit dynamic method which takes the instance as first argument.
In contrast, a staticmethod does not take the instance as first argument, so is called 'static' .

A staticmethod is indeed such a normal function the same as those outside a class definition.
It is luckily grouped into the class just in order to stand closer where it is applied, or you might scroll around to find it.




In Python erhält eine Klassenmethode eine Klasse als das implizite erste Argument. Die Klasse der Objektinstanz wird implizit als erstes Argument übergeben. Dies kann nützlich sein, wenn man möchte, dass die Methode eine Factory der Klasse ist, da sie die eigentliche Klasse (die die Methode genannt hat) als erstes Argument erhält, man kann die richtige Klasse instanziieren, selbst wenn auch Unterklassen betroffen sind.

Eine statische Methode ist nur eine in einer Klasse definierte Funktion. Es weiß nichts über die Klasse oder Instanz, an der es aufgerufen wurde und erhält nur die Argumente, die ohne implizites erstes Argument übergeben wurden. Beispiel:

class Test(object):
    def foo(self, a):
        print "testing (%s,%s)"%(self,a)

    @classmethod
    def foo_classmethod(cls, a):
        print "testing foo_classmethod(%s,%s)"%(cls,a)

    @staticmethod
    def foo_staticmethod(a):
        print "testing foo_staticmethod(%s)"%a

test = Test()

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




Lassen Sie mich zuerst die Ähnlichkeit zwischen einer mit @classmethod vs @staticmethod dekorierten Methode zuerst erklären.

Ähnlichkeit: Beide können für die Klasse selbst und nicht nur für die Instanz der Klasse aufgerufen werden. Beide sind gewissermaßen Klassenmethoden .

Unterschied: Eine Klassenmethode erhält die Klasse selbst als erstes Argument, während eine statische Methode dies nicht tut.

Also ist eine statische Methode in gewissem Sinne nicht an die Klasse selbst gebunden und hängt einfach darin, nur weil sie 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',)



Um zu entscheiden, ob @staticmethod oder @classmethod verwendet werden @staticmethod @classmethod Sie 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



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 @classmethod ist ein Funktionsdecorator - Details finden Sie in der Beschreibung der Funktionsdefinitionen in Funktionsdefinitionen .

Es kann entweder für die Klasse (wie Cf() ) oder für eine Instanz (wie C().f() ) aufgerufen werden. 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 das implizierte erste Argument übergeben.

Klassenmethoden unterscheiden sich von C ++ oder statischen Java-Methoden. Wenn Sie diese möchten, staticmethod() 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 @staticmethod Formular ist ein Funktionsdecorator - Details finden Sie in der Beschreibung der Funktionsdefinitionen in Funktionsdefinitionen .

Es kann entweder für die Klasse (wie Cf() ) oder für eine Instanz (wie C().f() ) aufgerufen werden. Die Instanz wird außer für ihre Klasse ignoriert.

Die statischen Methoden in Python ähneln denen in Java oder C ++. Ein fortgeschrittenes Konzept finden Sie unter classmethod() in diesem Abschnitt.




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

Klassenmethode: Python hat im Gegensatz zu Java und C ++ kein Konstruktorüberladen. Um dies zu erreichen, können Sie die classmethod . folgendes Beispiel erklärt dies

Nehmen wir an, wir haben eine Person Klasse, die zwei Argumente first_name und last_name und die Instanz von Person erstellt.

class Person(object):

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

Jetzt, wenn die Anforderung kommt, wo Sie eine Klasse nur mit einem einzigen Namen erstellen müssen, nur ein first_name . Sie können so etwas in Python nicht machen.

Dadurch erhalten Sie einen Fehler, wenn Sie versuchen, ein Objekt (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

Mit @classmethod können Sie jedoch dasselbe @classmethod wie 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 die Instanz oder Klasse gebunden und Sie können das einfach mit dem Klassennamen aufrufen.

first_name wir an, im obigen Beispiel benötigen Sie eine first_name dass first_name 20 Zeichen nicht überschreiten darf. Sie können dies einfach tun.

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

und Sie könnten einfach anrufen mit Klassennamen

Person.validate_name("Gaurang Shah")



@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 ist eine Klassenmethode, also kann eine Unterklasse entscheiden, ihre Implementierung zu ändern, vielleicht weil sie ziemlich generisch ist und mehr als einen Clustertyp verarbeiten kann. Daher wäre es nicht genug, nur den Namen der Klasse zu überprüfen.




Eine andere Überlegung in Bezug auf staticmethod vs classmethod kommt mit Vererbung. Angenommen, Sie haben folgende Klasse:

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

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

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

Dies funktioniert, aber beachten Sie, dass die bar() Implementierung in der Foo2 ( Foo2 ) Foo2 nichts mehr für diese Klasse nutzen kann. Foo2 , Foo2 hatte eine Methode namens magic() , die Sie in der Foo2 Implementierung von bar() möchten:

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 Umgehung hier wäre, Foo2.magic() in bar() Foo2.magic() , aber dann wiederholst du dich selbst (wenn sich der Name von Foo2 ändert, musst du daran denken, diese bar() -Methode zu aktualisieren).

Für mich ist dies eine geringfügige Verletzung des Open / Closed-Prinzips , da eine in Foo getroffene Entscheidung sich auf Ihre Fähigkeit auswirkt, allgemeinen Code in einer abgeleiteten Klasse umzuformulieren (dh sie ist weniger für Erweiterungen offen). Wenn bar() eine classmethod wäre, würde es uns gut gehen:

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




@classmethod bedeutet : Wenn diese Methode aufgerufen wird, übergeben wir die Klasse als erstes Argument anstelle der Instanz dieser Klasse (wie wir es normalerweise mit Methoden tun). Dies bedeutet, dass Sie die Klasse und ihre Eigenschaften innerhalb dieser Methode anstelle einer bestimmten Instanz verwenden können.

@staticmethod bedeutet: Wenn diese Methode aufgerufen wird, übergeben wir keine Instanz der Klasse (wie normalerweise bei Methoden). Dies bedeutet, dass Sie eine Funktion in eine Klasse einfügen können, aber nicht auf die Instanz dieser Klasse zugreifen können (dies ist nützlich, wenn Ihre Methode die Instanz nicht verwendet).




Statische Methoden:

  • Einfache Funktionen ohne Selbstargument.
  • Arbeit an Klassenattributen; nicht auf Instanzattribute.
  • Kann über Klasse und Instanz aufgerufen werden.
  • Die eingebaute Funktion staticmethod () wird verwendet, um sie zu erstellen.

Vorteile von statischen Methoden:

  • Es lokalisiert den Funktionsnamen im Klassenbereich
  • Es verschiebt den Funktionscode näher an die Stelle, an der er verwendet wird
  • Praktischer zu importieren im Vergleich zu Funktionen auf Modulebene, da nicht jede Methode speziell importiert werden muss

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

Klassenmethoden:

  • Funktionen, die das erste Argument als Klassenname haben.
  • Kann über Klasse und Instanz aufgerufen werden.
  • Diese werden mit der eingebauten Funktion classmethod erstellt.

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



A quick hack-up ofotherwise identical methods in iPython reveals that @staticmethod yields marginal performance gains (in the nanoseconds), but otherwise it seems to serve no function. Also, any performance gains will probably be wiped out by the additional work of processing the method through staticmethod() during compilation (which happens prior to any code execution when you run a script).

For the sake of code readability I'd avoid @staticmethod unless your method will be used for loads of work, where the nanoseconds count.






Related