¿Métodos estáticos en Python?

1719

¿Es posible tener métodos estáticos en Python a los que pueda llamar sin inicializar una clase, como:

ClassName.static_method()
Joan Venge
fuente

Respuestas:

2026

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 anterior para definir un método estático, staticmethodcomo una función en lugar de un decorador. Esto solo debe usarse si tiene que admitir 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 bonita sintaxis del decorador

Finalmente, ¡use con 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 donde una función separada de "nivel superior" habría sido más clara.


Lo siguiente es textual 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 modismo:

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

El formulario @staticmethod es un decorador de funciones ; consulte la descripción de las definiciones de funciones en Definiciones de funciones para obtener detalles.

Se puede invocar en la clase (como C.f()) 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 encontrados en Java o C ++. Para un concepto más avanzado, ver 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.

Cambiado en la versión 2.4: se agregó la sintaxis del decorador de funciones.

dbr
fuente
15
¿Por qué agregar @staticmethoddecoración o usar un puntero de función, cuando simplemente puede evitar el primer selfparámetro? Bueno, para un objeto a, no podrá llamar a.your_static_method(), lo que está permitido en otros idiomas, pero de todos modos se considera una mala práctica y el compilador siempre lo advierte
SomethingSomething
1
Estoy usando python 2.7.12, con el decorador @staticmethod no puedo llamar al primer método estático definido. Su error de givin
Supratim Samantray
Supratim Samantray, ¿estás seguro? porque aquí se dice claramente que se puede usar después de 2.4.
realslimshanky
11
@SomethingSomething, si no utiliza el nombre de variable "self", pero no agrega el decorador, el primer argumento, por ejemplo arg1, seguirá siendo la referencia a la instancia self. El nombre self es solo una convención.
George Moutsopoulos
1
@GeorgeMoutsopoulos: de acuerdo en general. Pero, de todos modos, podrá llamar al método como ClassName.methodName(), como si fuera estático, y luego no selfse proporcionará ninguno al método. Como dijiste, aún será posible llamar a este método como ClassInstance.methodName(), y selfse proporcionará como el primer parámetro, independientemente de su nombre.
Algo
220

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

(Tenga en cuenta que esta respuesta se refiere a Python 3.x. En Python 2.x obtendrá un TypeErrorpara 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" supone que el primer argumento no es una instancia (como lo sería si fuera invocado por una instancia en lugar de una clase). Mientras 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 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érico.

Aquí es donde entra la decoración: si precedemos el 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 la definición de un método, entonces, impide que una instancia se envíe como 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)
Richard Ambler
fuente
8
El primer ejemplo (llamar por clase sin @staticmethod) no funciona para mí en Python 2.7. Obtengo "TypeError: el método no vinculado rollCall () debe llamarse con la instancia de Dog como primer argumento (en su lugar, obtuve la instancia int)"
tba
2
Esto no funciona. Tanto Dog.rollCall (-1) como rex.rollCall (-1) devuelven lo mismoTypeError: unbound method rollCall() must be called with Dog instance as first argument (got int instance instead)
Mittenchops
3
@MestreLion Creo que esto no es un gran beneficio, ya que todo lo que hace es confundir los métodos estáticos y de instancia y hacer que uno se parezca al otro. Si sabe que un método es estático, debe acceder a él como método estático. El hecho de que el método no necesite o use su instancia debería ser evidente donde sea que use el método. Siempre uso T.my_static_method()o type(my_t_instance).my_static_method(), ya que esto es más claro y hace que sea inmediatamente obvio que estoy llamando a un método estático.
Asad Saeeduddin
81

Sí, echa un vistazo al decorador de métodos estáticos :

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

Realmente no necesitas usar el @staticmethoddecorador. Simplemente declarando un método (que no espera el autoparámetro) y llamarlo desde la clase. El decorador solo está allí en caso de que desee poder llamarlo también desde una instancia (que no era lo que quería hacer)

En su mayoría, solo usas funciones ...

hichris123
fuente
Esto no funcionaría para python 2.5.2 class Dummy: def static1(): print "hello from static1" @staticmethod def static2(): print "hello from static2" Dummy.static2() Dummy.static1() Salida: hola desde static2 Traceback <última llamada más reciente>: Archivo "ll.py", línea 46, en <module> Dummy.static1 () TypeError: método independiente static1 () debe ser llamado con instancia ficticia como primer argumento (no obtuvo nada en su lugar)
Mahori
77
Esto no funciona dentro de una clase. Python pasará selfcomo primer argumento a menos que le diga que no lo haga. (ver: decorador)
Navin
99
En realidad, esto es incorrecto en 2.7 y legal a partir de 3.X (probado bien en 3.2). Es decir, no se puede llamar a un método desde el contexto de una clase sin el decorador @staticmethod en 2.7 y siguientes. En 3.2 funciona e insertará una selfreferencia apropiadamente dependiendo de cómo se llame. Caso de prueba: pastebin.com/12DDV7DB .
spinkus
2
Técnicamente preciso, pero una solución terrible. El staticmethoddecorador permite llamar a la función tanto en la clase como en una instancia (esta solución falla cuando se llama a la función en una instancia).
Ethan Furman
Se puede llamar a un método como cualquier otro atributo de un objeto utilizando la notación de puntos. class C: def callme(): print('called'); C.callme()
pouya
32

¿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 manera (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 sintaxis del decorador. Esta sintaxis 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 se puede usar tal como lo describió:

ClassName.static_method()

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


Otra opción que puede usarse como lo describe es la classmethoddiferencia, es que el método de clase obtiene la clase como un primer argumento implícito, y si se subclasifica, 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 clsno es un nombre obligatorio para el primer argumento, pero los codificadores Python más experimentados considerarán que está mal hecho si usa cualquier otra cosa.

Estos se usan típicamente como constructores alternativos.

new_instance = ClassName.class_method()

Un ejemplo incorporado es dict.fromkeys():

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

Además de las particularidades de cómo se comportan los objetos de método estático , hay un cierto tipo de belleza que puede sorprender con ellos cuando se trata de organizar su código de 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 se documenta por sí mismo en qué contexto se deben usar ciertos componentes y resulta ideal para nombrar casos de prueba distintos, así como tener un enfoque directo sobre cómo los módulos de prueba se asignan a módulos reales bajo pruebas para puristas .

Frecuentemente encuentro viable aplicar este enfoque para organizar el código de utilidad de un proyecto. Muy a menudo, las personas se apresuran de inmediato y crean un utilspaquete y terminan con 9 módulos de los cuales uno tiene 120 LOC y el resto son dos docenas de LOC en el mejor de los casos. 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
Filip Dupanović
fuente
10

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"

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

Digamos que este archivo se llama dogs.py. Para usarlos, llamarías en dogs.barking_sound()lugar de dogs.Dog.barking_sound.

Si realmente necesita un método estático para ser parte de la clase, puede usar el decorador de métodos estáticos .

Matt Woelk
fuente
1

Por lo tanto, los métodos estáticos son los métodos que se pueden invocar sin crear el objeto de una clase. Por ejemplo :-

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

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

En el ejemplo anterior, el método addse llama por el nombre de la clase, Ano por el nombre del objeto.

Aman Raheja
fuente
-3

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

  1. Usando 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. Usando @staticmethod

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

Salida:

Resultado: 25

Codemaker
fuente
Usted acaba de proporcionar ejemplos y compartió las maneras de cómo puede hacerlo. No explicaste lo que significan los métodos.
zixuan
-4

Me encuentro con esta pregunta de vez en cuando. El caso de uso y 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.

usuario1928764
fuente
No respondiste la pregunta, sino que proporcionaste un ejemplo.
zixuan