¿Cuál es la ventaja de usar métodos estáticos en Python?

90

Me encontré con un error de método no vinculado en Python con el código

import random

class Sample(object):
'''This class defines various methods related to the sample'''

    def drawSample(samplesize,List):
        sample=random.sample(List,samplesize)
        return sample

Choices=range(100)
print Sample.drawSample(5,Choices)

Después de leer muchas publicaciones útiles aquí, pensé cómo podría agregar @staticmethodarriba para que el código funcione. Soy un novato en Python. ¿Alguien puede explicar por qué uno querría definir métodos estáticos? O, ¿por qué no todos los métodos se definen como métodos estáticos?

Curious2learn
fuente
1
Ésta es una pregunta extraña. Los métodos estáticos son una necesidad de diseño . No es una "ventaja". Los usa porque debe hacerlo. Es una característica de diseño de una clase tener métodos estáticos. ¿Está preguntando qué métodos estáticos son en primer lugar? Creo que la pregunta podría reformularse para definir más claramente lo que necesita saber.
S.Lott
15
No, no quería saber cuáles son. Lo que quería saber era por qué se trataba de una "necesidad", que quedó claro en las respuestas dadas por otros. Es entonces cuando lo definiría en lugar de los métodos no estáticos. Gracias.
Curious2learn
3
@ S.Lott: ¿Cuándo es necesario usar un método estático en lugar de usar un método de clase normal? Por lo que puedo decir, un método de clase puede hacer todo lo que puede hacer un método estático. El método estático tiene "ventajas" como se enumeran en otra parte de esta publicación, pero no puedo ver ninguna razón por la que un método de clase no se pueda usar en ningún lugar donde se pueda usar un método estático, por lo que es una necesidad.
RFV

Respuestas:

128

Los métodos estáticos tienen un uso limitado, porque no tienen acceso a los atributos de una instancia de una clase (como lo hace un método regular), y no tienen acceso a los atributos de la clase en sí (como lo hace un método de clase ).

Por lo tanto, no son útiles para los métodos del día a día.

Sin embargo, pueden ser útiles para agrupar alguna función de utilidad junto con una clase, por ejemplo, una simple conversión de un tipo a otro, que no necesita acceso a ninguna información aparte de los parámetros proporcionados (y quizás algunos atributos globales del módulo. )

Pueden colocarse fuera de la clase, pero agruparlos dentro de la clase puede tener sentido donde solo sean aplicables allí.

También puede hacer referencia al método a través de una instancia o la clase, en lugar del nombre del módulo, lo que puede ayudar al lector a comprender con qué instancia está relacionado el método.

Pensamiento extraño
fuente
8
@ Curious2learn: No todos , pero algunos métodos son útiles como métodos estáticos. Piense en una Localeclase de ejemplo , cuyas instancias serán locales (obvio). Un getAvailableLocales()método sería un buen ejemplo de un método estático de dicha clase: claramente pertenece a la clase Locale, mientras que tampoco pertenece a ninguna instancia en particular.
MestreLion
... y tampoco es un método de clase, ya que puede que no necesite acceder a ninguno de los métodos de las clases.
MestreLion
Un editor señala que un método estático puede acceder a los atributos de la clase, navegando explícitamente hacia abajo desde class_name.attribute, simplemente no cls.attributeo self.attribute. Esto es cierto para los atributos "públicos". Por convención, no debe acceder a los atributos ocultos con un guión bajo, o nombres alterados con dos guiones bajos, de esta manera. También significa que su código será más frágil cuando los atributos cambien de lugar en la jerarquía de herencia.
Oddthinking
Una cita de Guido que ayuda a decidir cuándo usar métodos estáticos (nunca): "Todos sabemos lo limitados que son los métodos estáticos (son básicamente un accidente, en los días de Python 2.2 cuando estaba inventando clases y descriptores de nuevo estilo , Tenía la intención de implementar métodos de clase, pero al principio no los entendí e implementé accidentalmente métodos estáticos primero. Luego fue demasiado tarde para eliminarlos y solo proporcionar métodos de clase ".
Boris Churzin
189

Consulte este artículo para obtener una explicación detallada.

TL; DR

1.Elimina el uso de selfargumentos.

Reduce el uso de memoria porque Python no tiene que crear una instancia de un método enlazado para cada objeto iniciado:

>>>RandomClass().regular_method is RandomClass().regular_method
False
>>>RandomClass().static_method is RandomClass().static_method
True
>>>RandomClass.static_method is RandomClass().static_method
True

3. Mejora la legibilidad del código, lo que significa que el método no depende del estado del objeto en sí.

Permite la invalidación del método en el sentido de que si el método se definiera a nivel de módulo (es decir, fuera de la clase), una subclase no podría invalidar ese método.

zanetu
fuente
3
Esta debería ser la respuesta aceptada. El hecho de que el método estático en cualquier instancia y la clase en sí sean el mismo objeto es una ventaja real, especialmente cuando tiene muchas instancias (por ejemplo, cada instancia para un registro de base de datos mutable).
Zhuoyun Wei
+1 y de acuerdo con @ZhuoyunWei. La única respuesta que explica las pocas razones por las que un método estático a veces es preferible que un método de clase (aunque 1 y 3 son realmente la misma razón).
Alex
1
La mejor respuesta a "por qué método estático", pero la pregunta original también era "¿por qué no todos los métodos son estáticos?". El breve "sin acceso a los atributos de la instancia" también cubriría eso.
Exu
En mi opinión, la única ventaja real es que puede anularlo en una subclase, pero incluso eso da la sensación de que está haciendo algo mal en cuanto a OOP, ¿en qué caso de uso haría eso?
Boris Churzin
23

Esto no es exactamente el punto de su pregunta real, pero como ha dicho que es un novato en Python, tal vez sea útil, y nadie más ha salido y lo ha dicho explícitamente.

Nunca hubiera arreglado el código anterior haciendo del método un método estático. O habría abandonado la clase y solo habría escrito una función:

def drawSample(samplesize,List):
    sample=random.sample(List,samplesize)
    return sample

Choices=range(100)
print drawSample(5,Choices)

Si tiene muchas funciones relacionadas, puede agruparlas en un módulo, es decir, ponerlas todas en el mismo archivo, llamado sample.pypor ejemplo; entonces

import sample

Choices=range(100)
print sample.drawSample(5,Choices)

O habría agregado un __init__método a la clase y creado una instancia que tenía métodos útiles:

class Sample(object):
'''This class defines various methods related to the sample'''

    def __init__(self, thelist):
        self.list = thelist

    def draw_sample(self, samplesize):
        sample=random.sample(self.list,samplesize)
        return sample

choices=Sample(range(100))
print choices.draw_sample(5)

(También cambié las convenciones de casos en el ejemplo anterior para que coincida con el estilo recomendado por PEP 8.)

Una de las ventajas de Python es que no te obliga a usar clases para todo. Puede usarlos solo cuando hay datos o estados que deberían estar asociados con los métodos, para eso son las clases. De lo contrario, puede usar funciones, que es para lo que están las funciones.

Vicki Laidler
fuente
Gracias por el comentario. Necesitaba una clase en este caso porque quiero trabajar con la muestra extraída. No, no utilicé un método estático, pero quería aprender, ya que encontré el término al buscar información sobre el mensaje de error que recibí. Pero su consejo sobre la recopilación de funciones en un módulo sin definir una clase sería útil para otras funciones que necesito. Así que gracias.
Curious2learn
4
+1: Esa fue una buena explicación sobre algunos conceptos erróneos que tenía el OP. Y fue muy honesto al decir, por adelantado, que en realidad no estaba respondiendo a su pregunta sobre los métodos estáticos, sino que dio una solución mucho mejor diciendo que su problema no requiere clases en absoluto.
MestreLion
22

¿Por qué querría uno definir métodos estáticos ?

Supongamos que tenemos un classllamado Mathentonces

nadie querrá crear el objeto de class Math
y luego invocar métodos como ceily floory fabsen él.

Entonces los hacemos static.

Por ejemplo haciendo

>> Math.floor(3.14)

es mucho mejor que

>> mymath = Math()
>> mymath.floor(3.14)

Entonces son útiles de alguna manera. No es necesario crear una instancia de una clase para usarlos.

¿Por qué no todos los métodos se definen como métodos estáticos ?

No tienen acceso a variables de instancia.

class Foo(object):
    def __init__(self):
        self.bar = 'bar'

    def too(self):
        print self.bar

    @staticmethod
    def foo():
        print self.bar

Foo().too() # works
Foo.foo() # doesn't work

Es por eso que no hacemos que todos los métodos sean estáticos.

Pratik Deoghare
fuente
10
Pero, ¿por qué no un paquete de matemáticas? Python tiene paquetes para eso, no necesita una definición de clase para crear un espacio de nombres.
Extraneon
6
@extraneon: Sí amigo, lo sé, pero quería tener algo simple y familiar para la explicación, así que usé Math. Por eso escribí con mayúscula M.
Pratik Deoghare
6
El OP no preguntó qué son los métodos estáticos. Preguntó cuál era su ventaja. Estás explicando cómo usarlos, no cómo son útiles. En su ejemplo particular, un espacio de nombres tendría mucho más sentido.
Javier
4
-1: Explicación buena y correcta, pero un ejemplo muy mal elegido: Mathen primer lugar, no hay ninguna razón para que hayas creado una clase, un módulo encajaría mejor. Intente encontrar un ejemplo de una clase que tenga sentido como clase, no una de la que "nadie querrá crear un objeto" .
MestreLion
3
Y luego dé un ejemplo de un caso de uso legítimo para que uno (o algunos ) de los métodos de las clases sean estáticos, pero no todos . Si todos los métodos de una clase son estáticos, la clase debería ser un módulo. Si ninguno es estático, entonces no está respondiendo a la pregunta.
MestreLion
13

Cuando llama a un objeto de función desde una instancia de objeto, se convierte en un 'método enlazado' y obtiene que el objeto de instancia en sí se pasa como primer argumento.

Cuando llama a un classmethodobjeto (que envuelve un objeto de función) en una instancia de objeto, la clase del objeto de instancia se pasa como primer argumento.

Cuando llama a un staticmethodobjeto (que envuelve un objeto de función), no se utiliza un primer argumento implícito.

class Foo(object):

    def bar(*args):
        print args

    @classmethod
    def baaz(*args):
        print args

    @staticmethod
    def quux(*args):
        print args

>>> foo = Foo()

>>> Foo.bar(1,2,3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method bar() must be called with Foo instance as first argument (got int instance instead)
>>> Foo.baaz(1,2,3)
(<class 'Foo'>, 1, 2, 3)
>>> Foo.quux(1,2,3)
(1, 2, 3)

>>> foo.bar(1,2,3)
(<Foo object at 0x1004a4510>, 1, 2, 3)
>>> foo.baaz(1,2,3)
(<class 'Foo'>, 1, 2, 3)
>>> foo.quux(1,2,3)
(1, 2, 3)
Matt Anderson
fuente
3
+1: Finalmente, una gran explicación sobre qué son los métodos estáticos y los métodos de clase. Aunque no explicó por qué uno querría usar un método estático, al menos explicó claramente en un ejemplo simple qué ambos son mucho mejores que los documentos oficiales.
MestreLion
3

Los métodos estáticos son excelentes porque no es necesario declarar una instancia del objeto al que pertenece el método.

El sitio de Python tiene una excelente documentación sobre métodos estáticos aquí:
http://docs.python.org/library/functions.html#staticmethod

David Fox
fuente
Gracias David. Pero, ¿por qué no definir cada método como un método estático, ya que también funcionan en instancias? ¿Existen inconvenientes al hacerlo?
Curious2learn
4
@ Curious2learn: No, con métodos estáticos no tienes acceso a la instancia: la instancia se ignora excepto por su clase.
Felix Kling
4
Ese argumento sería cierto en Java, donde las funciones no pueden vivir por sí mismas sino que siempre se definen en el contexto de una clase. Pero en Python puedes tener funciones y funciones de clase estáticas. Esta respuesta realmente no muestra por qué elegir un método estático en lugar de un método que no está en una clase.
Extraneon
1
@extraneon: es más una cuestión de preferencias organizativas de código; tener métodos estáticos le da a uno la opción adicional.
Charles Duffy
@Felix - Gracias. Esto aclara por qué no todos los métodos deben ser estáticos.
Curious2learn
3

Las alternativas a una staticmethodson: classmethod, instancemethody function. Si no sabe cuáles son, desplácese hasta la última sección. Si a staticmethodes mejor que cualquiera de estas alternativas, depende del propósito con el que esté escrito.

ventajas del método estático de Python

  • Si no necesita acceso a los atributos o métodos de la clase o instancia, a staticmethodes mejor que a classmethodo instancemethod. De esa manera, está claro (del @staticmethoddecorador) que el estado de la clase y la instancia no se lee ni se modifica. Sin embargo, el uso de a functionhace que esa distinción sea aún más clara (consulte las desventajas).
  • La firma de llamada de a staticmethodes la misma que la de a classmethodo instancemethod, a saber <instance>.<method>(<arguments>). Por lo tanto, se puede reemplazar fácilmente por uno de los tres si se necesita más adelante o en una clase derivada. No puedes hacer eso con un simple function.
  • Se staticmethodpuede usar A en lugar de a functionpara dejar en claro que pertenece subjetivamente a una clase y evitar conflictos de espacio de nombres.

desventajas del método estático de Python

  • No puede acceder a los atributos o métodos de la instancia o clase.
  • La firma de llamada de a staticmethodes la misma que la de a classmethodo instancemethod. Esto enmascara el hecho de que el staticmethodno lee ni modifica la información del objeto. Esto hace que el código sea más difícil de leer. ¿Por qué no usar simplemente un function?
  • A staticmethodes difícil de reutilizar si alguna vez necesita llamarlo desde fuera de la clase / instancia donde se definió. Si existe alguna posibilidad de reutilización, a functiones la mejor opción.
  • La staticmethodrara vez se utiliza, por lo que la gente que lee el código que incluye uno puede tomar un poco más de tiempo para leerlo.

alternativas a un método estático en Python

Para abordar el tema de las ventajas del staticmethod, necesitamos saber cuáles son las alternativas y en qué se diferencian entre sí.

  • El staticmethodpertenece a una clase pero no puede acceder ni modificar ninguna instancia o información de clase.

Hay tres alternativas para ello:

  • El classmethodtiene acceso a la clase de la persona que llama.
  • El instancemethodtiene acceso a la instancia de la persona que llama y su clase.
  • No functiontiene nada que ver con las clases. Es el más cercano en capacidad al staticmethod.

Así es como se ve esto en el código:

# function
# has nothing to do with a class
def make_cat_noise(asker_name):
    print('Hi %s, mieets mieets!' % asker_name)

# Yey, we can make cat noises before we've even defined what a cat is!
make_cat_noise('JOey')  # just a function

class Cat:
    number_of_legs = 4

    # special instance method __init__
    def __init__(self, name):
        self.name = name

    # instancemethod
    # the instance (e.g. Cat('Kitty')) is passed as the first method argument
    def tell_me_about_this_animal(self, asker_name):
        print('Hi %s, This cat has %d legs and is called %s'
              % (asker_name, self.number_of_legs, self.name))

    # classmethod
    # the class (e.g. Cat) is passed as the first method argument
    # by convention we call that argument cls
    @classmethod
    def tell_me_about_cats(cls, asker_name):
        print("Hi %s, cats have %d legs."
              % (asker_name, cls.number_of_legs))
        # cls.name  # AttributeError because only the instance has .name
        # self.name  # NameError because self isn't defined in this namespace

    # staticmethod
    # no information about the class or the instance is passed to the method
    @staticmethod
    def make_noise(asker_name):
        print('Hi %s, meooow!' % asker_name)
        # class and instance are not accessible from here

# one more time for fun!
make_cat_noise('JOey')  # just a function

# We just need the class to call a classmethod or staticmethod:
Cat.make_noise('JOey')  # staticmethod
Cat.tell_me_about_cats('JOey')  # classmethod
# Cat.tell_me_about_this_animal('JOey')  # instancemethod -> TypeError

# With an instance we can use instancemethod, classmethod or staticmethod
mycat = Cat('Kitty')  # mycat is an instance of the class Cat
mycat.make_noise('JOey')  # staticmethod
mycat.tell_me_about_cats('JOey')  # classmethod
mycat.tell_me_about_this_animal('JOey')  # instancemethod
Joooeey
fuente
1

Debido a que las funciones de espacio de nombres son agradables (como se señaló anteriormente):

  1. Cuando quiero ser explícito sobre los métodos que no cambian el estado del objeto, utilizo métodos estáticos. Esto desalienta a las personas de mi equipo a comenzar a cambiar los atributos del objeto en esos métodos.

  2. Cuando refactorizo ​​un código realmente podrido, empiezo por intentar crear tantos métodos @staticmethodcomo sea posible. Esto me permite luego extraer estos métodos en una clase, aunque estoy de acuerdo, esto es algo que rara vez uso, me resultó útil algunas veces.

Vlad-Ardelean
fuente
1

En mi opinión, no hay un beneficio de rendimiento único al usar @staticmethods en comparación con simplemente definir la función fuera y separada de la clase a la que de otro modo sería una @staticmethod.

Lo único que diría que justifica su existencia es la conveniencia. Los métodos estáticos son comunes en otros lenguajes de programación populares, entonces, ¿por qué no Python? Si desea crear una función con un comportamiento que está muy estrechamente asociado con la clase para la que la está creando, pero en realidad no accede / modifica los datos internos de una instancia de la clase de una manera que justifique su conceptualización como un típico método de esa clase, luego abofetee un @staticmethodencima de él y cualquiera que lea su código aprenderá inmediatamente mucho sobre la naturaleza del método y su relación con la clase.

Una cosa que a veces me gusta hacer es colocar la funcionalidad que mi clase usa mucho internamente en los @staticmethods privados . De esa manera, no abarrotaré la API expuesta por mi módulo con métodos que nadie que use mi módulo necesitaría ver y mucho menos usar.

Garrett Gutiérrez
fuente
0

Los métodos estáticos casi no tienen razón de ser en Python. Utiliza métodos de instancia o métodos de clase.

def method(self, args):
    self.member = something

@classmethod
def method(cls, args):
    cls.member = something

@staticmethod
def method(args):
    MyClass.member = something
    # The above isn't really working
    # if you have a subclass
Georg Schölly
fuente
Dijiste "casi". ¿Existe algún lugar donde puedan ser mejores que las alternativas?
Javier
@Javier: No puedo pensar en uno, pero probablemente haya uno, ¿por qué ese método se incluiría en la biblioteca de Python de otra manera?
Georg Schölly
1
@Javier, @Georg: Tienes mucha fe en que el corpus de Python no tiene sentido.
Charles Merriam
-1: Esta respuesta no presenta ningún caso de uso de staticmethod, o cualquier explicación de lo que es un classmethodes o por qué y cómo es "mejor" que los estáticos.
MestreLion
@CharlesMerriam: Por supuesto, los métodos estáticos tienen su uso, de lo contrario, se habría eliminado o desaprobado en Python 3 (donde se eliminó la mayor parte del cruft heredado). No hay una sola palabra staticmethoden contra en los documentos para respaldar su afirmación de que es cruft o la afirmación de Georg que classmethoddebería usarse en su lugar).
MestreLion