web-dev-qa-db-fra.com

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

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?

102
martineau

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.

127
Ben

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.

15
Jan Vorcak

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. 

8
Keith

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
7
schatten

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
6
Pedro Romano

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

0
whimian