Métodos estáticos en Python?

¿Es posible tener métodos estáticos en Python para poder llamarlos sin inicializar una clase, como:

ClassName.StaticMethod ( ) 

Sí, usando el decorador de método estático

 class MyClass(object): @staticmethod def the_static_method(x): print x MyClass.the_static_method(2) # outputs 2 

Tenga en cuenta que algunos códigos pueden usar el método antiguo de definir un método estático, usando el método staticmethod como una función en lugar de un decorador. Solo debe usarse si tiene que ser compatible con versiones antiguas de Python (2.2 y 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 

Esto es completamente idéntico al primer ejemplo (usando @staticmethod ), simplemente no usando la syntax del decorador agradable

Por último, utilice staticmethod() moderación! Hay muy pocas situaciones en las que los métodos estáticos son necesarios en Python, y he visto que se usan muchas veces en las que una función de “nivel superior” separada hubiera sido más clara.


Lo siguiente es literalmente de la documentación :

Un método estático no recibe un primer argumento implícito. Para declarar un método estático, use este idioma:

 class C: @staticmethod def f(arg1, arg2, ...): ... 

La forma de @staticmethod es un decorador de funciones; consulte la descripción de las definiciones de funciones en las definiciones de funciones para obtener más información.

Se puede llamar en la clase (como Cf() ) o en una instancia (como C().f() ). La instancia se ignora a excepción de su clase.

Los métodos estáticos en Python son similares a los que se encuentran en Java o C ++. Para un concepto más avanzado, vea classmethod() .

Para obtener más información sobre métodos estáticos, consulte la documentación sobre la jerarquía de tipos estándar en La jerarquía de tipos estándar .

Nuevo en la versión 2.2.

Modificado en la versión 2.4: Se ha agregado la syntax del decorador de funciones.

Creo que Steven tiene razón . Para responder a la pregunta original, entonces, para configurar un método de clase, simplemente asum que el primer argumento no será una instancia de llamada, y luego asegúrese de que solo llame al método de la clase.

(Tenga en cuenta que esta respuesta se refiere a Python 3.x. En Python 2.x obtendrá un TypeError para llamar al método en la propia clase).

Por ejemplo:

 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) 

En este código, el método "rollCall" asume que el primer argumento no es una instancia (como lo sería si fuera llamada por una instancia en lugar de una clase). Siempre que se llame a "rollCall" desde la clase en lugar de una instancia, el código funcionará bien. Si intentamos llamar a "rollCall" desde una instancia, por ejemplo:

 rex.rollCall(-1) 

sin embargo, provocaría que se generara una excepción porque enviaría dos argumentos: sí mismo y -1, y "rollCall" solo se define para aceptar un argumento.

Por cierto, rex.rollCall () enviaría el número correcto de argumentos, pero también provocaría una excepción porque ahora n representaría una instancia de Dog (es decir, rex) cuando la función espera que n sea numérica.

Aquí es donde entra la decoración: si precede al método "rollCall" con

 @staticmethod 

luego, al declarar explícitamente que el método es estático, incluso podemos llamarlo desde una instancia. Ahora,

 rex.rollCall(-1) 

trabajaría. La inserción de @staticmethod antes de una definición de método, entonces, impide que una instancia se envíe a sí misma como un argumento.

Puede verificar esto probando el siguiente código con y sin la línea @staticmethod comentada.

 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) 

Sí, echa un vistazo al decorador de método estático :

 >>> class C: ... @staticmethod ... def hello(): ... print "Hello World" ... >>> C.hello() Hello World 

Realmente no necesitas usar el decorador @staticmethod . Simplemente declare un método (que no espera el parámetro auto) y llámelo desde la clase. El decorador solo está allí en caso de que quieras poder llamarlo desde una instancia también (que no era lo que querías hacer)

Mayormente, solo usas funciones aunque …

Métodos estáticos en Python?

¿Es posible tener métodos estáticos en Python para poder llamarlos sin inicializar una clase, como:

 ClassName.StaticMethod() 

Sí, los métodos estáticos se pueden crear de esta forma (aunque es un poco más Pythonic usar guiones bajos en lugar de CamelCase para los métodos):

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

Lo anterior usa la syntax decoradora. Esta syntax es equivalente a

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

Esto puede ser usado tal como lo describiste:

 ClassName.static_method() 

Un ejemplo integrado de un método estático es str.maketrans() en Python 3, que era una función en el módulo de string en Python 2.


Otra opción que puede usarse como usted describe es el classmethod , la diferencia es que el método de clase obtiene la clase como un primer argumento implícito, y si se clasifica en una subclase, obtiene la subclase como el primer argumento implícito.

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

Tenga en cuenta que cls no es un nombre obligatorio para el primer argumento, pero los codificadores de Python más experimentados lo considerarán mal hecho si usa cualquier otra cosa.

Estos se utilizan típicamente como constructores alternativos.

 new_instance = ClassName.class_method() 

Un ejemplo incorporado es dict.fromkeys() :

 new_dict = dict.fromkeys(['key1', 'key2']) 

Aparte de las particularidades de cómo se comportan los objetos de método estático , hay un cierto tipo de belleza que puede atacar con ellos cuando se trata de organizar su código a nivel de módulo.

 # 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 

Ahora se vuelve un poco más intuitivo y autodocumentado en el contexto en el que se supone que se usarán ciertos componentes y se resaltará de manera ideal para nombrar distintos casos de prueba, además de tener un enfoque directo de cómo los módulos de prueba se asignan a los módulos reales bajo pruebas para los puristas. .

Con frecuencia me parece viable aplicar este enfoque para organizar el código de utilidad de un proyecto. Muy a menudo, las personas se apresuran y crean inmediatamente un paquete de utils y terminan con 9 módulos, de los cuales uno tiene 120 LOC y el rest son dos docenas de LOC como máximo. Prefiero comenzar con esto y convertirlo en un paquete y crear módulos solo para las bestias que realmente los merecen:

 # 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 

Quizás la opción más simple es simplemente poner esas funciones fuera de la clase:

 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" 

Usando este método, las funciones que modifican o usan el estado de objeto interno (tienen efectos secundarios) se pueden mantener en la clase, y las funciones de utilidad reutilizables se pueden mover afuera.

Digamos que este archivo se llama dogs.py Para usar estos, llamaría dogs.barking_sound() lugar de dogs.Dog.barking_sound .

Si realmente necesita un método estático para formar parte de la clase, puede utilizar el decorador de método estático.

En Python 3:

Aquí hay otro bueno para usar métodos no estáticos, ya que eran métodos estáticos.

 class Account: # here is the main class # a non-static method def login(self, url, email, password): # as a convention self is passed as a # placeholder for the class Object itself self.login_url = url self.user_email = email self.user_password = password print(self.login_url, self.user_email, self.user_password) 

Llama a un método no estático como estático

 """ * Referencing the Account.login(self, url, email, password) * Just call the `Account` object as the self """ Account.login(Account, "https://example.com/login", "email@example.com", "password_example") :$ https://example.com/login email@example.com password_example 

Los métodos estáticos de Python se pueden crear de dos maneras.

  1. Utilizando 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)) 

Salida:

Resultado: 25

  1. Utilizando @staticmethod

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

Salida:

Resultado: 25

 class Stuff(object): @staticmethod def show(): print("Done") Stuff.show() 

Me encuentro con esta pregunta de vez en cuando. El caso de uso y el ejemplo que me gusta es:

 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'] >>> 

No tiene sentido crear un objeto de clase cmath, porque no hay estado en un objeto cmath. Sin embargo, cmath es una colección de métodos que están relacionados de alguna manera. En mi ejemplo anterior, todas las funciones en cmath actúan sobre números complejos de alguna manera.