211 votes

Appeler une méthode statique de classe dans le corps de la classe ?

Lorsque je tente d'utiliser une méthode statique à partir du corps de la classe, et que je définis la méthode statique à l'aide de la fonction intégrée staticmethod comme un 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):
  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 des descripteurs). et vous pouvez le contourner en convertissant manuellement _stat_func() en une méthode statique après sa dernière utilisation, comme ceci :

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 des moyens plus propres ou plus "pythoniques" d'accomplir cette tâche ?

232voto

Ben Points 22160

staticmethod Les objets ont apparemment un __func__ qui stocke la fonction brute originale (il est logique qu'ils aient dû le faire). Cela fonctionnera donc :

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

Par ailleurs, bien que je me doutais qu'un objet staticmethod avait une sorte d'attribut stockant la fonction originale, je n'avais aucune idée des spécificités. Dans l'esprit d'apprendre à 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 fouilles similaires dans une session interactive ( dir est très utile) peut souvent résoudre ce genre de questions très rapidement.

38voto

Ján Vorčák Points 3636

C'est la façon 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 de la Principe de la sécheresse . Cela me rappelle le raison pour laquelle il y a une nouvelle super() en Python 3 :)

Mais je suis d'accord avec les autres, généralement le meilleur choix est de définir une fonction au niveau du module.

Par exemple avec @staticmethod la récursion pourrait ne pas être très bonne (il faudrait briser 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 une méthode statique. Avec la fonction au niveau du module, tout sera correct.

12voto

Pedro Romano Points 4100

Pourquoi ne pas 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

12voto

Keith Points 13800

Ceci est dû au fait que staticmethod est un descripteur et qu'il nécessite une récupération d'attribut au niveau de la classe pour exercer le protocole du descripteur et obtenir le véritable appelable.

Du code source :

Elle peut être appelée soit sur la classe (par ex. C.f() ) ou sur une instance (par exemple C().f() ) ; l'instance est ignorée sauf pour sa classe.

Mais pas directement depuis l'intérieur de la classe pendant qu'elle est définie.

Mais comme un commentateur l'a mentionné, ce n'est pas vraiment une conception "Pythonique" du tout. Il suffit d'utiliser une fonction au niveau du module à la place.

8voto

schatten Points 157

Que pensez-vous de cette solution ? Elle ne repose pas sur la connaissance de @staticmethod implémentation du décorateur. La classe interne StaticMethod joue le rôle de conteneur des 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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X