Quelle est la différence entre une fonction décorée @staticmethod
et une décorée avec @classmethod
?
Quelle est la différence entre une fonction décorée @staticmethod
et une décorée avec @classmethod
?
Réponses:
Peut-être qu'un petit exemple de code vous aidera à: remarquer la différence dans les signatures d'appel de foo
, class_foo
et 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()
Vous trouverez ci-dessous la manière habituelle dont une instance d'objet appelle une méthode. L'instance d'objet,, a
est implicitement passée comme premier argument.
a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)
Avec les méthodes de classe, la classe de l'instance d'objet est implicitement passée comme premier argument au lieu de self
.
a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)
Vous pouvez également appeler en class_foo
utilisant la classe. En fait, si vous définissez quelque chose comme étant une méthode de classe, c'est probablement parce que vous avez l'intention de l'appeler à partir de la classe plutôt qu'à partir d'une instance de classe. A.foo(1)
aurait déclenché une TypeError, mais A.class_foo(1)
fonctionne très bien:
A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)
Une utilisation que les gens ont trouvée pour les méthodes de classe est de créer des constructeurs alternatifs héritables .
Avec les méthodes statiques , ni self
(l'instance d'objet) ni cls
(la classe) n'est implicitement passé comme premier argument. Ils se comportent comme des fonctions simples, sauf que vous pouvez les appeler à partir d'une instance ou de la classe:
a.static_foo(1)
# executing static_foo(1)
A.static_foo('hi')
# executing static_foo(hi)
Les méthodes statiques sont utilisées pour regrouper des fonctions qui ont une connexion logique avec une classe à la classe.
foo
n'est qu'une fonction, mais lorsque vous appelez, a.foo
vous n'obtenez pas seulement la fonction, vous obtenez une version "partiellement appliquée" de la fonction avec l'instance d'objet a
liée comme premier argument de la fonction. foo
attend 2 arguments, alors qu'il a.foo
n'attend qu'un seul argument.
a
est lié à foo
. C'est ce que signifie le terme "lié" ci-dessous:
print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>
Avec a.class_foo
, a
n'est pas lié à class_foo
, plutôt la classe A
est liée à class_foo
.
print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>
Ici, avec une méthode statique, même s'il s'agit d'une méthode, a.static_foo
renvoie simplement une bonne fonction 'olé sans aucun argument lié. static_foo
attend 1 argument et
a.static_foo
attend également 1 argument.
print(a.static_foo)
# <function static_foo at 0xb7d479cc>
Et bien sûr, la même chose se produit lorsque vous appelez static_foo
à la A
place avec la classe .
print(A.static_foo)
# <function static_foo at 0xb7d479cc>
@staticmethod
pourrait aider à organiser votre code en étant surchargeable par les sous-classes. Sans cela, vous auriez des variantes de la fonction flottant dans l'espace de noms du module.
@staticmethod
Encore une utilisation pour - vous pouvez l'utiliser pour retirer la cruauté. J'implémente un langage de programmation en Python - les fonctions définies par la bibliothèque utilisent une execute
méthode statique , où les fonctions définies par l'utilisateur nécessitent des arguments d'instance (c'est-à-dire le corps de la fonction). Ce décorateur élimine les avertissements "paramètres auto non utilisés" dans l'inspecteur PyCharm.
Une méthode statique est une méthode qui ne sait rien de la classe ou de l'instance à laquelle elle a été appelée. Il obtient juste les arguments qui ont été passés, pas de premier argument implicite. C'est fondamentalement inutile en Python - vous pouvez simplement utiliser une fonction de module au lieu d'une méthode statique.
Une méthode de classe , en revanche, est une méthode qui obtient la classe à laquelle elle a été appelée, ou la classe de l'instance à laquelle elle a été appelée, comme premier argument. Ceci est utile lorsque vous voulez que la méthode soit une fabrique pour la classe: puisqu'elle obtient la classe réelle à laquelle elle a été appelée comme premier argument, vous pouvez toujours instancier la bonne classe, même lorsque des sous-classes sont impliquées. Observez par exemple comment dict.fromkeys()
, une méthode de classe, retourne une instance de la sous-classe lorsqu'elle est appelée sur une sous-classe:
>>> class DictSubclass(dict):
... def __repr__(self):
... return "DictSubclass"
...
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>>
Fait essentiellement @classmethod
une méthode dont le premier argument est la classe à partir de laquelle elle est appelée (plutôt que l'instance de classe), @staticmethod
n'a pas d'arguments implicites.
Documents officiels sur Python:
Une méthode de classe reçoit la classe comme premier argument implicite, tout comme une méthode d'instance reçoit l'instance. Pour déclarer une méthode de classe, utilisez cet idiome:
class C: @classmethod def f(cls, arg1, arg2, ...): ...
Le
@classmethod
formulaire est un décorateur de fonction - voir la description des définitions de fonction dans Définitions de fonction pour plus de détails.Il peut être appelé soit sur la classe (comme
C.f()
), soit sur une instance (commeC().f()
). L'instance est ignorée à l'exception de sa classe. Si une méthode de classe est appelée pour une classe dérivée, l'objet de classe dérivée est transmis comme premier argument implicite.Les méthodes de classe sont différentes des méthodes statiques C ++ ou Java. Si vous les voulez, voyez
staticmethod()
dans cette section.
Une méthode statique ne reçoit pas de premier argument implicite. Pour déclarer une méthode statique, utilisez cet idiome:
class C: @staticmethod def f(arg1, arg2, ...): ...
Le
@staticmethod
formulaire est un décorateur de fonction - voir la description des définitions de fonction dans Définitions de fonction pour plus de détails.Il peut être appelé soit sur la classe (comme
C.f()
), soit sur une instance (commeC().f()
). L'instance est ignorée à l'exception de sa classe.Les méthodes statiques en Python sont similaires à celles trouvées en Java ou C ++. Pour un concept plus avancé, voir
classmethod()
dans cette section.
Voici un petit article sur cette question
La fonction @staticmethod n'est rien de plus qu'une fonction définie à l'intérieur d'une classe. Il peut être appelé sans instancier la classe en premier. Sa définition est immuable via l'héritage.
La fonction @classmethod peut également être appelée sans instancier la classe, mais sa définition suit la classe Sub, pas la classe Parent, via l'héritage. En effet, le premier argument de la fonction @classmethod doit toujours être cls (classe).
Pour décider d'utiliser @staticmethod ou @classmethod, vous devez regarder à l'intérieur de votre méthode. Si votre méthode accède à d'autres variables / méthodes de votre classe, utilisez @classmethod . D'un autre côté, si votre méthode ne touche aucune autre partie de la classe, utilisez @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
serait toujours cls._counter
même si le code est placé dans une classe différente ou si le nom de la classe est modifié. Apple._counter
est spécifique à la Apple
classe; pour une classe différente, ou lorsque le nom de la classe est modifié, vous devrez modifier la classe référencée.
Quelle est la différence entre @staticmethod et @classmethod en Python?
Vous avez peut-être vu du code Python comme ce pseudocode, qui illustre les signatures des différents types de méthodes et fournit une docstring pour expliquer chacun d'eux:
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.
'''
Je vais d'abord expliquer a_normal_instance_method
. C'est ce qu'on appelle précisément une " méthode d'instance ". Lorsqu'une méthode d'instance est utilisée, elle est utilisée comme fonction partielle (par opposition à une fonction totale, définie pour toutes les valeurs lorsqu'elle est affichée dans le code source) qui est, lorsqu'elle est utilisée, le premier des arguments est prédéfini comme l'instance de la objet, avec tous ses attributs donnés. Il a l'instance de l'objet qui lui est liée et doit être appelée à partir d'une instance de l'objet. En règle générale, il accède à divers attributs de l'instance.
Par exemple, ceci est une instance d'une chaîne:
', '
si nous utilisons la méthode d'instance, join
sur cette chaîne, pour rejoindre un autre itératives, il est bien évidemment fonction de l'instance, en plus d'être une fonction de la liste itératives, ['a', 'b', 'c']
:
>>> ', '.join(['a', 'b', 'c'])
'a, b, c'
Les méthodes d'instance peuvent être liées via une recherche en pointillés pour une utilisation ultérieure.
Par exemple, cela lie la str.join
méthode à l' ':'
instance:
>>> join_with_colons = ':'.join
Et plus tard, nous pouvons l'utiliser comme une fonction qui a déjà le premier argument lié. De cette façon, cela fonctionne comme une fonction partielle sur l'instance:
>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'
La méthode statique ne prend pas l'instance comme argument.
Elle est très similaire à une fonction de niveau module.
Cependant, une fonction de niveau module doit vivre dans le module et être spécialement importée vers d'autres endroits où elle est utilisée.
S'il est attaché à l'objet, cependant, il suivra également l'objet de manière pratique à travers l'importation et l'héritage.
Un exemple de méthode statique est str.maketrans
, déplacé du string
module en Python 3. Il rend une table de traduction adaptée à la consommation de str.translate
. Cela semble plutôt idiot lorsqu'il est utilisé à partir d'une instance d'une chaîne, comme illustré ci-dessous, mais l'importation de la fonction depuis le string
module est plutôt maladroite, et il est agréable de pouvoir l'appeler à partir de la classe, comme dansstr.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}
En python 2, vous devez importer cette fonction à partir du module de chaîne de moins en moins utile:
>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'
Une méthode de classe est similaire à une méthode d'instance en ce qu'elle prend un premier argument implicite, mais au lieu de prendre l'instance, elle prend la classe. Ils sont fréquemment utilisés comme constructeurs alternatifs pour une meilleure utilisation sémantique et prendront en charge l'héritage.
L'exemple le plus canonique d'une méthode de classe intégrée est dict.fromkeys
. Il est utilisé comme un constructeur alternatif de dict, (bien adapté lorsque vous savez quelles sont vos clés et que vous voulez une valeur par défaut pour elles.)
>>> dict.fromkeys(['a', 'b', 'c'])
{'c': None, 'b': None, 'a': None}
Lorsque nous sous-classons dict, nous pouvons utiliser le même constructeur, ce qui crée une instance de la sous-classe.
>>> 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'>
Voir le code source pandas pour d'autres exemples similaires de constructeurs alternatifs, et voir également la documentation officielle de Python sur classmethod
et staticmethod
.
J'ai commencé à apprendre le langage de programmation avec C ++, puis Java et ensuite Python et cette question m'a donc beaucoup dérangé, jusqu'à ce que je comprenne l'utilisation simple de chacun.
Méthode de classe: Python contrairement à Java et C ++ n'a pas de surcharge de constructeur. Et pour cela, vous pouvez utiliser classmethod
. L'exemple suivant explique cela
Considérons que nous avons une Person
classe qui prend deux arguments first_name
et last_name
et crée l'instance de Person
.
class Person(object):
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
Maintenant, si l'exigence vient où vous devez créer une classe en utilisant un seul nom, juste un first_name
, vous ne pouvez pas faire quelque chose comme ça en Python.
Cela vous donnera une erreur lorsque vous essaierez de créer un objet (instance).
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
Cependant, vous pouvez réaliser la même chose en utilisant @classmethod
comme mentionné ci-dessous
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, "")
Méthode statique: c'est assez simple, ce n'est pas lié à une instance ou à une classe et vous pouvez simplement l'appeler en utilisant le nom de la classe.
Disons donc que dans l'exemple ci-dessus, vous avez besoin d'une validation qui first_name
ne doit pas dépasser 20 caractères, vous pouvez simplement le faire.
@staticmethod
def validate_name(name):
return len(name) <= 20
et vous pouvez simplement appeler en utilisant class name
Person.validate_name("Gaurang Shah")
def __init__(self, first_name, last_name="")
place de classmethod get_person
. Le résultat sera également exactement le même dans ce cas.
Je pense qu'une meilleure question est "Quand utiliseriez-vous @classmethod vs @staticmethod?"
@classmethod vous permet d'accéder facilement aux membres privés associés à la définition de classe. c'est un excellent moyen de faire des singletons ou des classes d'usine qui contrôlent le nombre d'instances des objets créés.
@staticmethod fournit des gains de performances marginaux, mais je n'ai pas encore vu une utilisation productive d'une méthode statique dans une classe qui ne pourrait pas être réalisée en tant que fonction autonome en dehors de la classe.
@decorators ont été ajoutés en python 2.4 Si vous utilisez python <2.4, vous pouvez utiliser les fonctions classmethod () et staticmethod ().
Par exemple, si vous souhaitez créer une méthode d'usine (une fonction renvoyant une instance d'une implémentation différente d'une classe en fonction de l'argument qu'elle obtient), vous pouvez faire quelque chose comme:
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)
Notez également qu'il s'agit d'un bon exemple d'utilisation d'une méthode de classe et d'une méthode statique. La méthode statique appartient clairement à la classe, car elle utilise la classe Cluster en interne. La méthode de classe n'a besoin que d'informations sur la classe et pas d'instance de l'objet.
Un autre avantage de faire de la _is_cluster_for
méthode une méthode de classe est qu'une sous-classe peut décider de modifier son implémentation, peut-être parce qu'elle est assez générique et peut gérer plus d'un type de cluster, donc il ne suffit pas de vérifier le nom de la classe.
Méthodes statiques:
Avantages des méthodes statiques:
Plus pratique pour importer que pour les fonctions au niveau du module car chaque méthode n'a pas besoin d'être spécialement importée
@staticmethod
def some_static_method(*args, **kwds):
pass
Méthodes de classe:
Ils sont créés avec la fonction intégrée classmethod.
@classmethod
def some_class_method(cls, *args, **kwds):
pass
@staticmethod
désactive simplement la fonction par défaut en tant que descripteur de méthode. classmethod enveloppe votre fonction dans un conteneur appelable qui transmet une référence à la classe propriétaire comme premier argument:
>>> 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'>>
En fait, classmethod
a une surcharge d'exécution mais permet d'accéder à la classe propriétaire. Alternativement, je recommande d'utiliser une métaclasse et de mettre les méthodes de classe sur cette métaclasse:
>>> class CMeta(type):
... def foo(cls):
... print cls
...
>>> class C(object):
... __metaclass__ = CMeta
...
>>> C.foo()
<class '__main__.C'>
c = C(); c.foo()
soulève AttributeError, vous auriez à faire type(c).foo()
. Cela pourrait également être considéré comme une fonctionnalité - je ne vois pas pourquoi vous voudriez le faire.
Le guide définitif sur la façon d'utiliser des méthodes statiques, de classe ou abstraites en Python est un bon lien pour cette rubrique, et résumez-le comme suit.
@staticmethod
fonction n'est rien de plus qu'une fonction définie à l'intérieur d'une classe. Il peut être appelé sans instancier la classe en premier. Sa définition est immuable via l'héritage.
@classmethod
La fonction peut également être appelée sans instancier la classe, mais sa définition suit la sous-classe, pas la classe parent, via l'héritage, peut être remplacée par la sous-classe. C'est parce que le premier argument pour la @classmethod
fonction doit toujours être cls (classe).
Seul le premier argument diffère :
Plus en détail...
Lorsque la méthode d'un objet est appelée, il reçoit automatiquement un argument supplémentaire self
comme premier argument. Autrement dit, la méthode
def f(self, x, y)
doit être appelé avec 2 arguments. self
est automatiquement passé, et c'est l'objet lui-même .
Quand la méthode est décorée
@classmethod
def f(cls, x, y)
l'argument fourni automatiquement n'est pas self
, mais la classe de self
.
Quand la méthode est décorée
@staticmethod
def f(x, y)
la méthode ne reçoit aucun argument automatique. Il ne reçoit que les paramètres avec lesquels il est appelé.
classmethod
est principalement utilisé pour les constructeurs alternatifs. staticmethod
n'utilise pas l'état de l'objet. Il pourrait s'agir d'une fonction externe à une classe. Il n'est mis à l'intérieur de la classe que pour regrouper des fonctions avec des fonctionnalités similaires (par exemple, comme Math
les méthodes statiques de classe Java )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)
Permettez-moi de dire la similitude entre une méthode décorée avec @classmethod vs @staticmethod en premier.
Similarité: deux peuvent être appelés sur la classe elle-même, plutôt que sur l' instance de la classe. Donc, les deux sont en quelque sorte des méthodes de classe .
Différence: une méthode de classe recevra la classe elle-même comme premier argument, contrairement à une méthode statique.
Ainsi, une méthode statique n'est pas, en un sens, liée à la classe elle-même et ne fait que s'y accrocher juste parce qu'elle peut avoir une fonctionnalité associée.
>>> 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',)
Une autre considération concernant la méthode statique par rapport à la méthode de classe concerne l'héritage. Disons que vous avez la classe suivante:
class Foo(object):
@staticmethod
def bar():
return "In Foo"
Et vous souhaitez ensuite remplacer bar()
dans une classe enfant:
class Foo2(Foo):
@staticmethod
def bar():
return "In Foo2"
Cela fonctionne, mais notez que maintenant l' bar()
implémentation dans la classe enfant ( Foo2
) ne peut plus profiter de quelque chose de spécifique à cette classe. Par exemple, supposons que vous disposiez d' Foo2
une méthode appelée magic()
que vous souhaitez utiliser dans leFoo2
implémentation de 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"
La solution de contournement ici serait d'appeler Foo2.magic()
à bar()
, mais vous vous répétez (si le nom des Foo2
modifications, vous devez vous rappeler de mettre à jour cette bar()
méthode).
Pour moi, c'est une légère violation du principe ouvert / fermé , car une décision prise dans Foo
impacte votre capacité à refactoriser le code commun dans une classe dérivée (c'est-à-dire qu'il est moins ouvert à l'extension). Sibar()
c'était un, classmethod
ça irait:
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()
Donne: In Foo2 MAGIC
Je vais essayer d'expliquer la différence de base en utilisant un exemple.
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 - on peut appeler directement des méthodes statiques et de classe sans initialiser
# A.run_self() # wrong
A.run_static()
A.run_class()
2- La méthode statique ne peut pas appeler la méthode self mais peut appeler d'autres méthodes statiques et de classe
3- La méthode statique appartient à la classe et n'utilisera pas du tout d'objet.
4- Les méthodes de classe ne sont pas liées à un objet mais à une classe.
@classmethod: peut être utilisé pour créer un accès global partagé à toutes les instances créées de cette classe ..... comme la mise à jour d'un enregistrement par plusieurs utilisateurs .... Je l'ai particulièrement trouvé utile lors de la création de singletons également ..: )
@static, méthode: n'a rien à voir avec la classe ou l'instance associée à ... mais pour la lisibilité peut utiliser une méthode statique
Vous voudrez peut-être considérer la différence entre:
Class A:
def foo(): # no self parameter, no decorator
pass
et
Class B:
@staticmethod
def foo(): # no self parameter
pass
Cela a changé entre python2 et python3:
python2:
>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()
python3:
>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()
Ainsi, l'utilisation @staticmethod
de méthodes for appelées uniquement directement à partir de la classe est devenue facultative en python3. Si vous souhaitez les appeler à la fois de la classe et de l'instance, vous devez toujours utiliser le @staticmethod
décorateur.
Les autres cas ont été bien couverts par la réponse unutbus.
Une méthode de classe reçoit la classe comme premier argument implicite, tout comme une méthode d'instance reçoit l'instance. C'est une méthode qui est liée à la classe et non à l'objet de la classe. Elle a accès à l'état de la classe car elle prend un paramètre de classe qui pointe vers la classe et non l'instance d'objet. Il peut modifier un état de classe qui s'appliquerait à toutes les instances de la classe. Par exemple, il peut modifier une variable de classe qui sera applicable à toutes les instances.
En revanche, une méthode statique ne reçoit pas de premier argument implicite, par rapport aux méthodes de classe ou aux méthodes d'instance. Et ne peut pas accéder ou modifier l'état de classe. Il n'appartient qu'à la classe car du point de vue de la conception, c'est la bonne façon. Mais en termes de fonctionnalité n'est pas lié, lors de l'exécution, à la classe.
à titre indicatif, utilisez des méthodes statiques comme utilitaires, utilisez des méthodes de classe par exemple comme usine. Ou peut-être pour définir un singleton. Et utilisez des méthodes d'instance pour modéliser l'état et le comportement des instances.
J'espère que j'étais clair!
Ma contribution démontre la différence entre @classmethod
, @staticmethod
et les méthodes d'instance, y compris comment une instance peut indirectement appeler @staticmethod
. Mais au lieu d'appeler indirectement un @staticmethod
depuis une instance, le rendre privé peut être plus "pythonique". Obtenir quelque chose à partir d'une méthode privée n'est pas démontré ici, mais c'est fondamentalement le même concept.
#!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)
'''
"""
Les méthodes de classe, comme son nom l'indique, sont utilisées pour apporter des modifications aux classes et non aux objets. Pour apporter des modifications aux classes, ils modifieront les attributs de classe (pas les attributs d'objet), car c'est ainsi que vous mettez à jour les classes. C'est la raison pour laquelle les méthodes de classe prennent la classe (classiquement désignée par «cls») comme premier argument.
class A(object):
m=54
@classmethod
def class_method(cls):
print "m is %d" % cls.m
Les méthodes statiques, d'autre part, sont utilisées pour effectuer des fonctionnalités qui ne sont pas liées à la classe, c'est-à-dire qu'elles ne liront ni n'écriront pas de variables de classe. Par conséquent, les méthodes statiques ne prennent pas les classes en argument. Ils sont utilisés pour que les classes puissent exécuter des fonctionnalités qui ne sont pas directement liées à l'objectif de la classe.
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."
Analysez littéralement @staticmethod fournissant des informations différentes.
Une méthode normale d'une classe est une méthode dynamique implicite qui prend l'instance comme premier argument.
En revanche, une méthode statique ne prend pas l'instance comme premier argument, elle est donc appelée «statique» .
Une méthode statique est en effet une fonction aussi normale que celles qui sont en dehors d'une définition de classe.
Heureusement, il est regroupé dans la classe afin de se rapprocher de l'endroit où il est appliqué, ou vous pouvez faire défiler pour le trouver.
Je pense que donner une version purement Python staticmethod
etclassmethod
aiderait à comprendre la différence entre eux au niveau du langage.
Les deux sont des descripteurs non liés aux données (il serait plus facile de les comprendre si vous connaissez d' abord les descripteurs ).
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 n'a pas accès aux attributs de l'objet, de la classe ou des classes parentes dans la hiérarchie d'héritage. Il peut être appelé directement à la classe (sans créer d'objet).
classmethod n'a pas accès aux attributs de l'objet. Il peut cependant accéder aux attributs de la classe et des classes parentes dans la hiérarchie d'héritage. Il peut être appelé directement à la classe (sans créer d'objet). Si elle est appelée sur l'objet, elle est identique à la méthode normale qui n'accède pas self.<attribute(s)>
et accèdeself.__class__.<attribute(s)>
uniquement.
Pensez que nous avons une classe avec b=2
, nous allons créer un objet et le réinitialiser b=4
dedans. Staticmethod ne peut accéder à rien de ce qui précède. Classmethod ne peut accéder .b==2
qu'à, via cls.b
. La méthode normale peut accéder à la fois: .b==4
via self.b
et .b==2
via self.__class__.b
.
Nous pourrions suivre le style KISS (garder les choses simples, stupides): n'utilisez pas de méthodes statiques et de méthodes de classe, n'utilisez pas de classes sans les instancier, accédez uniquement aux attributs de l'objet self.attribute(s)
. Il y a des langues où la POO est implémentée de cette façon et je pense que ce n'est pas une mauvaise idée. :)
Un hack-up rapide de méthodes identiques dans iPython révèle que cela @staticmethod
donne des gains de performances marginaux (en nanosecondes), mais sinon il ne semble pas avoir de fonction. En outre, tout gain de performances sera probablement annulé par le travail supplémentaire de traitement de la méthode viastaticmethod()
pendant la compilation (qui se produit avant toute exécution de code lorsque vous exécutez un script).
Par souci de lisibilité du code, j'éviterais à @staticmethod
moins que votre méthode ne soit utilisée pour des charges de travail, où les nanosecondes comptent.