Lorsque j'essaie d'utiliser une méthode statique dans le corps de la classe et de définir la méthode statique à l'aide de la fonction intégrée staticmethod
en tant que décorateur, comme ceci:
class Klass(object):
@staticmethod # use as decorator
def _stat_func():
return 42
_ANS = _stat_func() # call the staticmethod
def method(self):
ret = Klass._stat_func() + Klass._ANS
return ret
Je reçois l'erreur suivante:
Traceback (most recent call last):<br>
File "call_staticmethod.py", line 1, in <module>
class Klass(object):
File "call_staticmethod.py", line 7, in Klass
_ANS = _stat_func()
TypeError: 'staticmethod' object is not callable
Je comprends pourquoi cela se produit (liaison de descripteur) et je peux contourner ce problème en convertissant manuellement _stat_func()
en méthode statique après sa dernière utilisation, comme suit:
class Klass(object):
def _stat_func():
return 42
_ANS = _stat_func() # use the non-staticmethod version
_stat_func = staticmethod(_stat_func) # convert function to a static method
def method(self):
ret = Klass._stat_func() + Klass._ANS
return ret
Donc ma question est:
Existe-t-il de meilleures façons, comme dans le cas des nettoyeurs ou plus "pythoniques", de le faire?
Les objets staticmethod
ont apparemment un attribut __func__
stockant la fonction brute d'origine (ce qui est logique). Donc cela fonctionnera:
class Klass(object):
@staticmethod # use as decorator
def stat_func():
return 42
_ANS = stat_func.__func__() # call the staticmethod
def method(self):
ret = Klass.stat_func()
return ret
En passant, même si je soupçonnais qu'un objet staticmethod avait une sorte d'attribut stockant la fonction d'origine, je n'avais aucune idée des détails. Dans le but d'enseigner à quelqu'un à pêcher plutôt que de lui donner un poisson, voici ce que j'ai fait pour enquêter et le découvrir (un C & P de ma session Python):
>>> class Foo(object):
... @staticmethod
... def foo():
... return 3
... global z
... z = foo
>>> z
<staticmethod object at 0x0000000002E40558>
>>> Foo.foo
<function foo at 0x0000000002E3CBA8>
>>> dir(z)
['__class__', '__delattr__', '__doc__', '__format__', '__func__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> z.__func__
<function foo at 0x0000000002E3CBA8>
Des types de fouilles similaires dans une session interactive (dir
est très utile) peuvent souvent résoudre ce type de question très rapidement.
C'est comme ça que je préfère:
class Klass(object):
@staticmethod
def stat_func():
return 42
_ANS = stat_func.__func__()
def method(self):
return self.__class__.stat_func() + self.__class__._ANS
Je préfère cette solution à Klass.stat_func
, en raison du principe DRY . Cela me rappelle la raison pour laquelle il existe une nouvelle super()
dans Python 3 :)
Mais je suis d’accord avec les autres, le meilleur choix est généralement de définir une fonction au niveau du module.
Par exemple, avec la fonction @staticmethod
, la récursivité peut ne pas sembler très bonne (vous devez enfreindre le principe DRY en appelant Klass.stat_func
à l'intérieur de Klass.stat_func
). C'est parce que vous n'avez pas de référence à self
dans la méthode statique . Avec une fonction au niveau du module, tout se passera bien.
Cela est dû au fait que la méthode statique est un descripteur et nécessite une extraction d'attribut au niveau classe pour exercer le protocole de descripteur et obtenir le véritable appelable.
A partir du code source:
Il peut être appelé soit sur la classe (par exemple,
C.f()
), soit sur une instance (par exemple,C().f()
); l'instance est ignorée à l'exception de sa classe.
Mais pas directement de l'intérieur de la classe pendant sa définition.
Mais comme l’a mentionné un intervenant, il ne s’agit pas vraiment d’une conception «pythonique». Utilisez simplement une fonction de niveau module à la place.
Qu'en est-il de cette solution? Il ne repose pas sur la connaissance de la mise en œuvre de @staticmethod
décorateur. La classe interne StaticMethod joue en tant que conteneur de fonctions d'initialisation statiques.
class Klass(object):
class StaticMethod:
@staticmethod # use as decorator
def _stat_func():
return 42
_ANS = StaticMethod._stat_func() # call the staticmethod
def method(self):
ret = self.StaticMethod._stat_func() + Klass._ANS
return ret
Qu'en est-il d'injecter l'attribut de classe après la définition de la classe?
class Klass(object):
@staticmethod # use as decorator
def stat_func():
return 42
def method(self):
ret = Klass.stat_func()
return ret
Klass._ANS = Klass.stat_func() # inject the class attribute with static method value
Selon le blog ci-dessous, lors de l'appel d'une méthode statique dans une classe, la fonction appelant doit être une méthode de classe. L'ajout de @classmethod
à votre définition de méthode peut donc résoudre le problème.
Le guide définitif sur l'utilisation des méthodes statiques, de classes ou abstraites en Python