Was ist der Unterschied zwischen einer mit @staticmethod
und einer dekorierten Funktion @classmethod
?
Was ist der Unterschied zwischen einer mit @staticmethod
und einer dekorierten Funktion @classmethod
?
Antworten:
Vielleicht ein bisschen von Beispielcode helfen: Beachten Sie den Unterschied in der Anruf Unterschriften 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 wird die übliche Art und Weise 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)
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_foo
mit 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.
foo
ist nur eine Funktion, aber wenn Sie aufrufen, erhalten a.foo
Sie nicht nur die Funktion, sondern eine "teilweise angewendete" Version der Funktion, wobei die Objektinstanz a
als erstes Argument an die Funktion gebunden ist. foo
erwartet 2 Argumente, während a.foo
nur 1 Argument erwartet wird.
a
ist 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
, a
nicht gebunden class_foo
, sondern die Klasse A
gebunden 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_foo
nur eine gute alte Funktion ohne gebundene Argumente zurück. static_foo
erwartet 1 Argument und
a.static_foo
erwartet auch 1 Argument.
print(a.static_foo)
# <function static_foo at 0xb7d479cc>
Und natürlich passiert dasselbe, wenn Sie stattdessen static_foo
mit der Klasse anrufen A
.
print(A.static_foo)
# <function static_foo at 0xb7d479cc>
@staticmethod
könnte es helfen, Ihren Code zu organisieren, indem er von Unterklassen überschrieben wird. Ohne sie würden Varianten der Funktion im Modul-Namespace herumschweben.
@staticmethod
- Sie können damit Cruft entfernen. Ich implementiere eine Programmiersprache in Python - bibliotheksdefinierte Funktionen verwenden eine statische execute
Methode, bei der benutzerdefinierte Funktionen Instanzargumente erfordern (dh den Funktionskörper). Dieser Dekorator eliminiert Warnungen vor "nicht verwendeten Parametern selbst" im PyCharm-Inspektor.
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
>>>
Grundsätzlich @classmethod
hat eine Methode, deren erstes Argument die Klasse ist, von der sie aufgerufen wird (und nicht die Klasseninstanz), @staticmethod
keine impliziten Argumente.
Offizielle Python-Dokumente:
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
Formular 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.
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 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.
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.
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
cls._counter
wäre auch dann cls._counter
noch, wenn der Code in eine andere Klasse gestellt wird oder der Klassenname geändert wird. Apple._counter
ist spezifisch für die Apple
Klasse; Für eine andere Klasse oder wenn der Klassenname geändert wird, müssten Sie die referenzierte Klasse ändern.
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.
'''
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 join
fü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'
Instanzmethoden können zur späteren Verwendung über eine gepunktete Suche gebunden werden.
Dies bindet die str.join
Methode 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'
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 string
Modul 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'
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 classmethod
und staticmethod
.
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 Person
Klasse, die zwei Argumente akzeptiert first_name
und last_name
die 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, @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 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_name
die 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")
def __init__(self, first_name, last_name="")
anstelle der Klassenmethode verwendet get_person
. Auch das Ergebnis ist in diesem Fall genau das gleiche.
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.
@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_for
wenn 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.
Statische Methoden:
Vorteile statischer Methoden:
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:
Diese werden mit der in classmethod integrierten Funktion erstellt.
@classmethod
def some_class_method(cls, *args, **kwds):
pass
@staticmethod
Deaktiviert 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 classmethod
einen 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'>
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.
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.
@staticmethod
Funktion 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.
@classmethod
Die 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 @classmethod
Funktion immer cls (Klasse) sein muss.
Nur das erste Argument unterscheidet sich :
Ausführlicher...
Wenn die Methode eines Objekts aufgerufen wird, erhält es automatisch ein zusätzliches Argument self
als erstes Argument. Das heißt, Methode
def f(self, x, y)
muss mit 2 Argumenten aufgerufen werden. self
wird automatisch übergeben und ist das Objekt selbst .
Wenn die Methode dekoriert ist
@classmethod
def f(cls, x, y)
Das automatisch bereitgestellte Argument ist nicht self
, sondern die Klasse von self
.
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.
classmethod
wird hauptsächlich für alternative Konstruktoren verwendet. staticmethod
verwendet 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 Math
statischen 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)
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',)
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 Foo2
hatten eine Methode aufgerufen , magic()
die Sie im verwenden möchten Foo2
Umsetzung 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 Foo
Ihre Fähigkeit beeinträchtigt, allgemeinen Code in einer abgeleiteten Klasse umzugestalten (dh weniger offen für Erweiterungen). Wenn bar()
ein waren classmethod
wü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
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.
@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
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 @staticmethod
Dekorator verwenden.
Die anderen Fälle wurden durch die Antwort von unutbus gut abgedeckt.
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!
Mein Beitrag zeigt den Unterschied zwischen @classmethod
, @staticmethod
und Instanzmethoden, einschließlich der Frage, wie eine Instanz indirekt a aufrufen kann @staticmethod
. Aber anstatt indirekt ein @staticmethod
von 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)
'''
"""
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."
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.
Ich denke, eine rein Python-Version von staticmethod
und 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
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=4
zurücksetzen. Staticmethod kann nicht auf nichts von vorherigen zugreifen. Classmethod kann .b==2
nur über zugreifen cls.b
. Die normale Methode kann auf beide zugreifen: .b==4
über self.b
und .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. :) :)
Ein schneller Überblick über andere identische Methoden in iPython zeigt, dass @staticmethod
geringfü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 @staticmethod
sei denn, Ihre Methode wird für viele Arbeiten verwendet, bei denen die Nanosekunden zählen.