Méthodes statiques en Python?

1720

Est-il possible d'avoir des méthodes statiques en Python que je pourrais appeler sans initialiser une classe, comme:

ClassName.static_method()
Joan Venge
la source

Réponses:

2027

Oui, en utilisant le décorateur de méthode statique

class MyClass(object):
    @staticmethod
    def the_static_method(x):
        print(x)

MyClass.the_static_method(2)  # outputs 2

Notez que certains codes peuvent utiliser l'ancienne méthode de définition d'une méthode statique, en utilisant staticmethodune fonction plutôt qu'un décorateur. Cela ne doit être utilisé que si vous devez prendre en charge les anciennes versions de Python (2.2 et 2.3)

class MyClass(object):
    def the_static_method(x):
        print(x)
    the_static_method = staticmethod(the_static_method)

MyClass.the_static_method(2)  # outputs 2

Ceci est entièrement identique au premier exemple (en utilisant @staticmethod), tout simplement en n'utilisant pas la belle syntaxe de décorateur

Enfin, utilisez staticmethod()avec parcimonie! Il y a très peu de situations où des méthodes statiques sont nécessaires en Python, et je les ai vues utilisées à plusieurs reprises où une fonction distincte de "niveau supérieur" aurait été plus claire.


Ce qui suit est textuellement extrait de la documentation :

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 formulaire @staticmethod 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 (comme C().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é, voirclassmethod() .

Pour plus d'informations sur les méthodes statiques, consultez la documentation sur la hiérarchie de types standard dans La hiérarchie de types standard .

Nouveau dans la version 2.2.

Modifié dans la version 2.4: Ajout de la syntaxe du décorateur de fonctions.

dbr
la source
15
Pourquoi ajouter une @staticmethoddécoration ou utiliser un pointeur de fonction, alors que vous pouvez simplement éviter le premier selfparamètre? Eh bien, pour un objet a, vous ne pourrez pas appeler a.your_static_method(), ce qui est autorisé dans d'autres langues, mais c'est quand même considéré comme une mauvaise pratique et le compilateur en avertit toujours
SomethingSomething
1
J'utilise python 2.7.12, avec @staticmethod decorator je ne peux pas appeler la première méthode statique définie. Son erreur givin: TypeError: l'objet 'staticmethod' n'est pas appelable
Supratim Samantray
Supratim Samantray, êtes-vous sûr? car ici, il est clairement indiqué qu'il peut être utilisé après 2.4.
realslimshanky
11
@SomethingSomething, si vous n'utilisez pas le nom de variable "self", mais n'ajoutez pas le décorateur, le premier argument par exemple arg1 sera toujours la référence à l'instance self. Le nom self n'est qu'une convention.
George Moutsopoulos
1
@GeorgeMoutsopoulos - d'accord général. Mais - vous pourrez quand même appeler la méthode comme ClassName.methodName(), comme si c'était une méthode statique, et alors aucune selfne sera fournie à la méthode. Comme vous l'avez dit, il sera toujours possible d'appeler également cette méthode en tant que ClassInstance.methodName()et selfsera fourni comme premier paramètre, quel que soit son nom.
Quelque chose quelque chose du
220

Je pense que Steven a en fait raison . Pour répondre à la question d'origine, alors, afin de configurer une méthode de classe, supposez simplement que le premier argument ne sera pas une instance appelante, puis assurez-vous que vous n'appelez la méthode qu'à partir de la classe.

(Notez que cette réponse fait référence à Python 3.x. Dans Python 2.x, vous obtiendrez un TypeError pour appeler la méthode sur la classe elle-même.)

Par exemple:

class Dog:
    count = 0 # this is a class variable
    dogs = [] # this is a class variable

    def __init__(self, name):
        self.name = name #self.name is an instance variable
        Dog.count += 1
        Dog.dogs.append(name)

    def bark(self, n): # this is an instance method
        print("{} says: {}".format(self.name, "woof! " * n))

    def rollCall(n): #this is implicitly a class method (see comments below)
        print("There are {} dogs.".format(Dog.count))
        if n >= len(Dog.dogs) or n < 0:
            print("They are:")
            for dog in Dog.dogs:
                print("  {}".format(dog))
        else:
            print("The dog indexed at {} is {}.".format(n, Dog.dogs[n]))

fido = Dog("Fido")
fido.bark(3)
Dog.rollCall(-1)
rex = Dog("Rex")
Dog.rollCall(0)

Dans ce code, la méthode "rollCall" suppose que le premier argument n'est pas une instance (comme ce serait le cas s'il était appelé par une instance au lieu d'une classe). Tant que "rollCall" est appelé à partir de la classe plutôt que d'une instance, le code fonctionnera correctement. Si nous essayons d'appeler "rollCall" à partir d'une instance, par exemple:

rex.rollCall(-1)

cependant, cela provoquerait une exception, car il enverrait deux arguments: lui-même et -1, et "rollCall" n'est défini que pour accepter un argument.

Incidemment, rex.rollCall () enverrait le nombre correct d'arguments, mais provoquerait également une exception, car maintenant n représenterait une instance Dog (c'est-à-dire rex) lorsque la fonction s'attend à ce que n soit numérique.

C'est là qu'intervient la décoration: si l'on précède la méthode "rollCall" avec

@staticmethod

puis, en déclarant explicitement que la méthode est statique, on peut même l'appeler à partir d'une instance. Maintenant,

rex.rollCall(-1)

travaillerait. L'insertion de @staticmethod avant une définition de méthode, alors, empêche une instance de s'envoyer comme argument.

Vous pouvez le vérifier en essayant le code suivant avec et sans la ligne @staticmethod mise en commentaire.

class Dog:
    count = 0 # this is a class variable
    dogs = [] # this is a class variable

    def __init__(self, name):
        self.name = name #self.name is an instance variable
        Dog.count += 1
        Dog.dogs.append(name)

    def bark(self, n): # this is an instance method
        print("{} says: {}".format(self.name, "woof! " * n))

    @staticmethod
    def rollCall(n):
        print("There are {} dogs.".format(Dog.count))
        if n >= len(Dog.dogs) or n < 0:
            print("They are:")
            for dog in Dog.dogs:
                print("  {}".format(dog))
        else:
            print("The dog indexed at {} is {}.".format(n, Dog.dogs[n]))


fido = Dog("Fido")
fido.bark(3)
Dog.rollCall(-1)
rex = Dog("Rex")
Dog.rollCall(0)
rex.rollCall(-1)
Richard Ambler
la source
8
Le premier exemple (appeler par classe sans @staticmethod) ne fonctionne pas pour moi sur Python 2.7. J'obtiens "TypeError: la méthode non liée rollCall () doit être appelée avec l'instance Dog comme premier argument (a obtenu l'instance à la place)"
tba
2
Cela ne fonctionne pas. Dog.rollCall (-1) et rex.rollCall (-1) renvoient la même choseTypeError: unbound method rollCall() must be called with Dog instance as first argument (got int instance instead)
Mittenchops
3
@MestreLion Je pense que ce n'est pas un énorme avantage, car il ne fait qu'embrouiller les méthodes statiques et d'instance et faire ressembler l'une à l'autre. Si vous savez qu'une méthode est statique, vous devez y accéder en tant que méthode statique. Le fait que la méthode n'ait pas besoin ou n'utilise pas votre instance devrait être évident partout où vous utilisez la méthode. J'utilise toujours T.my_static_method()ou type(my_t_instance).my_static_method(), car c'est plus clair et il est immédiatement évident que j'appelle une méthode statique.
Asad Saeeduddin
81

Oui, consultez le décorateur de méthode statique :

>>> class C:
...     @staticmethod
...     def hello():
...             print "Hello World"
...
>>> C.hello()
Hello World
Paolo Bergantino
la source
54

Vous n'avez pas vraiment besoin d'utiliser le @staticmethod décorateur. Il suffit de déclarer une méthode (qui n'attend pas le paramètre self) et de l'appeler à partir de la classe. Le décorateur n'est là que si vous voulez également pouvoir l'appeler à partir d'une instance (ce qui n'était pas ce que vous vouliez faire)

Surtout, vous n'utilisez que des fonctions ...

hichris123
la source
Cela ne fonctionnerait pas pour python 2.5.2 class Dummy: def static1(): print "hello from static1" @staticmethod def static2(): print "hello from static2" Dummy.static2() Dummy.static1() Sortie: bonjour de static2 Traceback <dernier appel le plus récent>: Fichier "ll.py", ligne 46, dans <module> Dummy.static1 () TypeError: la méthode non liée static1 () doit être appelé avec l'instance factice comme premier argument (n'a rien obtenu à la place)
Mahori
7
Cela ne fonctionne pas dans une classe. Python passera selfcomme premier argument à moins que vous ne le lui disiez. (voir: décorateur)
Navin
9
En fait, c'est faux en 2.7 et légal à partir de 3.X (testé très bien en 3.2). C'est-à-dire que vous ne pouvez pas appeler une méthode à partir du contexte d'une classe sans le décorateur @staticmethod en 2.7 et en dessous. En 3.2, cela fonctionne et insérera une selfréférence de manière appropriée en fonction de la façon dont elle est appelée. Cas de test: pastebin.com/12DDV7DB .
spinkus
2
Techniquement précis, mais une terrible solution. Le staticmethoddécorateur permet d'appeler la fonction à la fois sur la classe et une instance (cette solution échoue lors de l'appel de la fonction sur une instance).
Ethan Furman le
une méthode peut être appelée comme tout autre attribut d'un objet en utilisant la notation par points. class C: def callme(): print('called'); C.callme()
pouya
32

Méthodes statiques en Python?

Est-il possible d'avoir des méthodes statiques en Python pour que je puisse les appeler sans initialiser une classe, comme:

ClassName.StaticMethod()

Oui, des méthodes statiques peuvent être créées comme ceci (bien qu'il soit un peu plus Pythonic d'utiliser des traits de soulignement au lieu de CamelCase pour les méthodes):

class ClassName(object):

    @staticmethod
    def static_method(kwarg1=None):
        '''return a value that is a function of kwarg1'''

Ce qui précède utilise la syntaxe du décorateur. Cette syntaxe est équivalente à

class ClassName(object):

    def static_method(kwarg1=None):
        '''return a value that is a function of kwarg1'''

    static_method = staticmethod(static_method)

Cela peut être utilisé comme vous l'avez décrit:

ClassName.static_method()

Un exemple intégré d'une méthode statique est str.maketrans()en Python 3, qui était une fonction du stringmodule en Python 2.


Une autre option qui peut être utilisée comme vous le décrivez est la classmethod, la différence est que la méthode de classe obtient la classe comme premier argument implicite, et si elle est sous-classée, alors elle obtient la sous-classe comme premier argument implicite.

class ClassName(object):

    @classmethod
    def class_method(cls, kwarg1=None):
        '''return a value that is a function of the class and kwarg1'''

Notez que ce clsn'est pas un nom obligatoire pour le premier argument, mais la plupart des codeurs Python expérimentés le considéreront comme mal fait si vous utilisez autre chose.

Ils sont généralement utilisés comme constructeurs alternatifs.

new_instance = ClassName.class_method()

Un exemple intégré est dict.fromkeys():

new_dict = dict.fromkeys(['key1', 'key2'])
Aaron Hall
la source
11

Mis à part les particularités du comportement des objets de méthode statique , il existe un certain type de beauté que vous pouvez frapper avec eux lorsqu'il s'agit d'organiser votre code au niveau du module.

# garden.py
def trim(a):
    pass

def strip(a):
    pass

def bunch(a, b):
    pass

def _foo(foo):
    pass

class powertools(object):
    """
    Provides much regarded gardening power tools.
    """
    @staticmethod
    def answer_to_the_ultimate_question_of_life_the_universe_and_everything():
        return 42

    @staticmethod
    def random():
        return 13

    @staticmethod
    def promise():
        return True

def _bar(baz, quux):
    pass

class _Dice(object):
    pass

class _6d(_Dice):
    pass

class _12d(_Dice):
    pass

class _Smarter:
    pass

class _MagicalPonies:
    pass

class _Samurai:
    pass

class Foo(_6d, _Samurai):
    pass

class Bar(_12d, _Smarter, _MagicalPonies):
    pass

...

# tests.py
import unittest
import garden

class GardenTests(unittest.TestCase):
    pass

class PowertoolsTests(unittest.TestCase):
    pass

class FooTests(unittest.TestCase):
    pass

class BarTests(unittest.TestCase):
    pass

...

# interactive.py
from garden import trim, bunch, Foo

f = trim(Foo())
bunch(f, Foo())

...

# my_garden.py
import garden
from garden import powertools

class _Cowboy(garden._Samurai):
    def hit():
        return powertools.promise() and powertools.random() or 0

class Foo(_Cowboy, garden.Foo):
    pass

Il devient maintenant un peu plus intuitif et auto-documenté dans quel contexte certains composants sont destinés à être utilisés et il s'étend idéalement pour nommer des cas de test distincts ainsi que pour avoir une approche simple de la façon dont les modules de test sont mappés aux modules réels sous les tests pour les puristes. .

Je trouve fréquemment viable d'appliquer cette approche à l'organisation du code utilitaire d'un projet. Très souvent, les gens se précipitent immédiatement et créent un utilspackage et se retrouvent avec 9 modules dont l'un a 120 LOC et les autres sont au mieux deux douzaines de LOC. Je préfère commencer par cela et le convertir en package et créer des modules uniquement pour les bêtes qui les méritent vraiment:

# utils.py
class socket(object):
    @staticmethod
    def check_if_port_available(port):
        pass

    @staticmethod
    def get_free_port(port)
        pass

class image(object):
    @staticmethod
    def to_rgb(image):
        pass

    @staticmethod
    def to_cmyk(image):
        pass
Filip Dupanović
la source
10

L'option la plus simple est peut-être simplement de mettre ces fonctions en dehors de la classe:

class Dog(object):
    def __init__(self, name):
        self.name = name

    def bark(self):
        if self.name == "Doggy":
            return barking_sound()
        else:
            return "yip yip"

def barking_sound():
    return "woof woof"

En utilisant cette méthode, les fonctions qui modifient ou utilisent l'état d'objet interne (ont des effets secondaires) peuvent être conservées dans la classe et les fonctions utilitaires réutilisables peuvent être déplacées à l'extérieur.

Disons que ce fichier est appelé dogs.py. Pour les utiliser, vous devez appeler à la dogs.barking_sound()place de dogs.Dog.barking_sound.

Si vous avez vraiment besoin d' une méthode statique pour faire partie de la classe, vous pouvez utiliser le staticmethod décorateur.

Matt Woelk
la source
1

Ainsi, les méthodes statiques sont les méthodes qui peuvent être appelées sans créer l'objet d'une classe. Par exemple :-

    @staticmethod
    def add(a, b):
        return a + b

b = A.add(12,12)
print b

Dans l'exemple ci-dessus, la méthode addest appelée par le nom de classe et Anon par le nom d'objet.

Aman Raheja
la source
-3

Les méthodes statiques Python peuvent être créées de deux manières.

  1. Utilisation de staticmethod ()

    class Arithmetic:
        def add(x, y):
            return x + y
    # create add static method
    Arithmetic.add = staticmethod(Arithmetic.add)
    
    print('Result:', Arithmetic.add(15, 10))

Production:

Résultat: 25

  1. Utilisation de @staticmethod

    class Arithmetic:
    
    # create add static method
    @staticmethod
    def add(x, y):
        return x + y
    
    print('Result:', Arithmetic.add(15, 10))

Production:

Résultat: 25

Codemaker
la source
Vous venez de fournir des exemples et partagé les moyens de le faire. Vous n'avez pas expliqué ce que signifient les méthodes.
zixuan
-4

Je rencontre de temps en temps cette question. Le cas d'utilisation et l'exemple que j'aime est:

jeffs@jeffs-desktop:/home/jeffs  $ python36
Python 3.6.1 (default, Sep  7 2017, 16:36:03) 
[GCC 6.3.0 20170406] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cmath
>>> print(cmath.sqrt(-4))
2j
>>>
>>> dir(cmath)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atanh', 'cos', 'cosh', 'e', 'exp', 'inf', 'infj', 'isclose', 'isfinite', 'isinf', 'isnan', 'log', 'log10', 'nan', 'nanj', 'phase', 'pi', 'polar', 'rect', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau']
>>> 

Il n'est pas logique de créer un objet de classe cmath, car il n'y a pas d'état dans un objet cmath. Cependant, cmath est une collection de méthodes qui sont toutes liées d'une manière ou d'une autre. Dans mon exemple ci-dessus, toutes les fonctions de cmath agissent sur les nombres complexes d'une manière ou d'une autre.

user1928764
la source
Vous n'avez pas répondu à la question mais vous avez plutôt fourni un exemple.
zixuan