Qual é a vantagem de usar métodos estáticos em Python?

91

Encontrei um erro de método desvinculado em python com o 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)

Depois de ler muitos posts úteis aqui, descobri como poderia adicionar @staticmethodacima para fazer o código funcionar. Eu sou um novato em python. Alguém pode explicar por que se deseja definir métodos estáticos? Ou, por que nem todos os métodos são definidos como métodos estáticos?

Curious2learn
fonte
1
Esta é uma pergunta estranha. Os métodos estáticos são uma necessidade do projeto . Não é uma coisa de "vantagem". Você os usa porque você deve. É um recurso de design de uma classe ter métodos estáticos. Você está perguntando quais são os métodos estáticos em primeiro lugar? Acho que a pergunta poderia ser reformulada para definir mais claramente o que você precisa saber.
S.Lott
15
Não, não queria saber o que são. O que eu queria saber era porque é uma "necessidade", o que ficou claro a partir das respostas de outros. É quando você o definiria, em vez dos métodos não estáticos. Obrigado.
Curious2learn
3
@ S.Lott: Quando é necessário usar um método estático em vez de usar um método de classe normal? Até onde eu sei, um método de classe pode fazer tudo que um método estático pode. O método estático tem "vantagens" listadas em outra parte deste post, mas não consigo ver nenhuma razão pela qual um método de classe não pode ser usado em qualquer lugar onde um método estático pode ser usado, tornando-o assim uma necessidade.
RFV

Respostas:

131

Os métodos estáticos têm uso limitado, porque eles não têm acesso aos atributos de uma instância de uma classe (como um método regular), e eles não têm acesso aos atributos da própria classe (como um método de classe tem )

Portanto, eles não são úteis para os métodos do dia-a-dia.

No entanto, eles podem ser úteis para agrupar alguma função de utilidade junto com uma classe - por exemplo, uma conversão simples de um tipo para outro - que não precisa de acesso a nenhuma informação além dos parâmetros fornecidos (e talvez alguns atributos globais para o módulo. )

Eles podem ser colocados fora da classe, mas agrupá-los dentro da classe pode fazer sentido onde eles só são aplicáveis ​​lá.

Você também pode fazer referência ao método por meio de uma instância ou classe, em vez do nome do módulo, o que pode ajudar o leitor a entender a que instância o método está relacionado.

Estranheza
fonte
9
@ Curious2learn: Nem todos , mas alguns métodos são úteis como métodos estáticos. Pense em uma Localeclasse de exemplo , cujas instâncias serão locales (duh). Um getAvailableLocales()método seria um bom exemplo de um método estático de tal classe: ele claramente pertence à classe Locale, enquanto também claramente não pertence a nenhuma instância em particular.
MestreLion
1
... e também não é um método de classe, já que pode não precisar acessar nenhum dos métodos das classes.
MestreLion
1
Um editor aponta que um método estático pode acessar os atributos da classe, navegando explicitamente para baixo a partir de class_name.attribute, apenas não cls.attributeou self.attribute. Isso é verdadeiro para atributos "públicos". Por convenção, você não deve acessar atributos ocultos com um sublinhado ou nomes mutilados com dois sublinhados, dessa forma. Isso também significa que seu código ficará mais frágil quando os atributos mudarem de lugar na hierarquia de herança.
Oddthinking
1
Uma citação de Guido que ajuda a decidir quando usar métodos estáticos (nunca): "Todos nós sabemos como os métodos estáticos são limitados. (Eles são basicamente um acidente - na época do Python 2.2, quando eu estava inventando novas classes e descritores de estilo , Eu pretendia implementar métodos de classe, mas a princípio não os entendi e
implantei
192

Consulte este artigo para obter uma explicação detalhada.

TL; DR

1. Elimina o uso de selfargumento.

2. Reduz o uso de memória porque o Python não precisa instanciar um método vinculado para cada objeto instanciado:

>>>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. Melhora a legibilidade do código, significando que o método não depende do estado do próprio objeto.

4. Permite a sobrescrita de método, pois se o método fosse definido no nível do módulo (ou seja, fora da classe), uma subclasse não seria capaz de sobrescrever esse método.

zanetu
fonte
3
Esta deve ser a resposta aceita. O fato de que o método estático em quaisquer instâncias e a própria classe são o mesmo objeto é uma vantagem real, especialmente quando você tem muitas instâncias (por exemplo, cada instância para um registro de banco de dados mutável).
Zhuoyun Wei
1
+1 e concordo com @ZhuoyunWei. A única resposta que explica os poucos motivos pelos quais um método estático às vezes é preferível a um método de classe (embora 1 e 3 sejam realmente o mesmo motivo).
Alex
2
A melhor resposta para "por que o método estático", mas a pergunta original também perguntou "por que nem todos os métodos são estáticos?". A abreviação de "sem acesso a atributos de instância" também abrangeria isso.
Exu de
1
IMO, a única vantagem real é que você pode sobrescrevê-lo em uma subclasse, mas mesmo que tenha algum sentimento de que você está fazendo algo errado em termos de OOP, em que caso de uso você faria isso?
Boris Churzin
1
3. - se não existe um estado, então por que criar uma classe em primeiro lugar? as funções do módulo seriam igualmente boas, com menos código.
Alex
23

Isso não vai exatamente ao ponto da sua pergunta real, mas como você disse que é um novato em Python, talvez seja útil, e ninguém mais apareceu e disse isso explicitamente.

Eu nunca teria corrigido o código acima tornando o método um método estático. Eu teria abandonado a aula e apenas escrito uma função:

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

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

Se você tiver muitas funções relacionadas, pode agrupá-las em um módulo - ou seja, colocá-las todas no mesmo arquivo, com nome, sample.pypor exemplo; então

import sample

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

Ou eu teria adicionado um __init__método à classe e criado uma instância com métodos úteis:

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)

(Eu também alterei as convenções de caso no exemplo acima para corresponder ao estilo recomendado pelo PEP 8.)

Uma das vantagens do Python é que ele não o força a usar classes para tudo. Você pode usá-los somente quando houver dados ou estado que devem ser associados aos métodos, que é para que servem as classes. Caso contrário, você pode usar funções, para que servem as funções.

Vicki Laidler
fonte
Obrigado pelo comentário. Eu precisava de uma aula neste caso porque quero trabalhar com a amostra desenhada. Não, eu não usei um método estático, mas queria aprender sobre, já que me deparei com o termo ao procurar informações sobre a mensagem de erro que recebi. Mas seu conselho sobre como coletar as funções em um módulo sem definir uma classe seria útil para outras funções de que preciso. Então, obrigado.
Curious2learn
4
+1: Essa foi uma boa explicação sobre alguns equívocos que o OP tinha. E você foi muito honesto para dizer, antecipadamente, que foram não realmente responder sua pergunta sobre os métodos estáticos, mas em vez deu uma muito melhor solução dizendo que seu problema não requer aulas em tudo.
MestreLion
22

Por que alguém iria querer definir métodos estáticos ?

Suponha que temos um classchamado Mathentão

ninguém vai querer criar um objeto de class Math
e então invocar métodos como ceile floore fabssobre ele.

Então, nós os fazemos static.

Por exemplo fazendo

>> Math.floor(3.14)

é muito melhor que

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

Portanto, eles são úteis de alguma forma. Você não precisa criar uma instância de uma classe para usá-los.

Por que nem todos os métodos são definidos como métodos estáticos ?

Eles não têm acesso a variáveis ​​de instância.

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

É por isso que não tornamos todos os métodos estáticos.

Pratik Deoghare
fonte
10
Mas por que não um pacote de matemática? Python tem pacotes para isso, você não precisa de uma definição de classe para criar um namespace.
extraneon
6
@extraneon: Sim cara, eu sei disso, mas eu queria ter algo simples e familiar como explicação, então usei Math. É por isso que capitalizei M.
Pratik Deoghare
6
O OP não perguntou o que são métodos estáticos. Ele perguntou qual é a vantagem deles. Você está explicando como usá-los, não como são úteis. Em seu exemplo particular, um namespace faria muito mais sentido.
Javier
4
-1: Explicação boa e correta, mas um exemplo muito mal escolhido: não há nenhuma razão para sua maquilagem Mathser uma classe em primeiro lugar, um módulo seria mais adequado. Tente encontrar um exemplo de uma classe que faça sentido como uma classe, não uma que "ninguém queira criar um objeto" .
MestreLion
3
E então dê um exemplo de caso de uso legítimo para um (ou alguns ) dos métodos das classes ser estático, mas não todos . Se todos os métodos de uma classe forem estáticos, a classe deve ser um módulo. Se nenhum for estático, você não está respondendo à pergunta.
MestreLion
15

Quando você chama um objeto de função de uma instância de objeto, ele se torna um 'método vinculado' e obtém o próprio objeto de instância que é passado como primeiro argumento.

Quando você chama um classmethodobjeto (que envolve um objeto de função) em uma instância de objeto, a classe do objeto de instância é passada como um primeiro argumento.

Quando você chama um staticmethodobjeto (que envolve um objeto de função), nenhum primeiro argumento implícito é usado.

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
fonte
3
+1: Finalmente, uma ótima explicação sobre o que são métodos estáticos e métodos de classe. Embora você não tenha explicado por que alguém iria querer usar um método estático, pelo menos você explicou claramente em um exemplo simples que ambos são muito melhores do que os documentos oficiais.
MestreLion
5

As alternativas para um staticmethodsão: classmethod, instancemethod, e function. Se você não sabe o que são, role para baixo até a última seção. Se a staticmethodfor melhor do que qualquer uma dessas alternativas, depende de para que propósito foi escrito.

vantagens do método estático Python

  • Se você não precisa acessar os atributos ou métodos da classe ou instância, a staticmethodé melhor do que a classmethodou instancemethod. Dessa forma, fica claro (do @staticmethoddecorador) que o estado da classe e da instância não é lido ou modificado. No entanto, o uso de um functiontorna essa distinção ainda mais clara (veja as desvantagens).
  • A assinatura de chamada de a staticmethodé igual à de a classmethodou instancemethod, a saber <instance>.<method>(<arguments>). Portanto, ele pode ser facilmente substituído por um dos três, se for necessário posteriormente ou em uma classe derivada. Você não pode fazer isso com um simples function.
  • A staticmethodpode ser usado em vez de a functionpara deixar claro que subjetivamente pertence a uma classe e para evitar conflitos de espaço de nomes.

desvantagens do método estático Python

  • Ele não pode acessar atributos ou métodos da instância ou classe.
  • A assinatura de chamada de a staticmethodé igual à de a classmethodou instancemethod. Isso mascara o fato de que staticmethodnão lê nem modifica nenhuma informação do objeto. Isso torna o código mais difícil de ler. Por que não usar apenas um function?
  • A staticmethodé difícil de reutilizar se você precisar chamá-lo de fora da classe / instância onde foi definido. Se houver possibilidade de reutilização, a functioné a melhor escolha.
  • O staticmethodé raramente usado, então as pessoas que lêem um código que o inclui podem demorar um pouco mais para lê-lo.

alternativas para um método estático em Python

Para discutir as vantagens do staticmethod, precisamos saber quais são as alternativas e como elas diferem umas das outras.

  • O staticmethodpertence a uma classe, mas não pode acessar ou modificar nenhuma instância ou informação de classe.

Existem três alternativas para isso:

  • O classmethodtem acesso à classe do chamador.
  • O instancemethodtem acesso à instância do chamador e sua classe.
  • Não functiontem nada a ver com aulas. É o mais próximo em capacidade do staticmethod.

Veja como isso se parece no 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
fonte
3

os métodos estáticos são ótimos porque você não precisa declarar uma instância do objeto ao qual o método pertence.

O site de python possui uma excelente documentação sobre métodos estáticos aqui:
http://docs.python.org/library/functions.html#staticmethod

David Fox
fonte
Obrigado David. Mas por que então não definir todo método como um método estático, já que eles também funcionam em instâncias. Existem desvantagens em fazer isso?
Curious2learn
4
@ Curious2learn: Não, com métodos estáticos você não tem acesso à instância: A instância é ignorada, exceto por sua classe.
Felix Kling
4
Esse argumento seria verdadeiro em Java, onde funções não podem viver por si mesmas, mas são sempre definidas no contexto de uma classe. Mas em Python você pode ter funções e funções de classe estática. Essa resposta realmente não mostra por que escolher um método estático em vez de um método que não está em uma classe.
extraneon
1
@extraneon - é mais uma questão de preferências organizacionais de código; ter métodos estáticos dá uma opção extra.
Charles Duffy,
@Felix - Obrigado. Isso esclarece por que todo método não deve ser um método estático.
Curious2learn
1

Os métodos estáticos quase não têm razão de ser no Python. Você pode usar métodos de instância ou métodos de classe.

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
fonte
Você disse "quase". Existe um lugar onde eles podem ser melhores do que as alternativas?
Javier
@Javier: Não consigo pensar em nenhum, mas provavelmente existe um, por que esse método seria incluído na biblioteca Python de outra forma?
Georg Schölly
1
@Javier, @Georg: Você acredita muito que o corpus do Python não tem lixo.
Charles Merriam
-1: Esta resposta não apresenta nenhum caso de uso staticmethodou nenhuma explicação do que classmethodé, por que e como é "melhor" do que os estáticos.
MestreLion
@CharlesMerriam: É claro que os métodos estáticos têm seu uso, caso contrário, teria sido descartado ou obsoleto no Python 3 (onde a maior parte do lixo legado foi removido). Não há uma única palavra contra staticmethodnos documentos para apoiar sua alegação de que é cruft, ou a alegação de Georg que classmethoddeveria ser usada no lugar)
MestreLion
1

Como as funções de namespacing são boas (como foi apontado anteriormente):

  1. Quando quero ser explícito sobre métodos que não alteram o estado do objeto, uso métodos estáticos. Isso desencoraja as pessoas da minha equipe a começar a alterar os atributos do objeto nesses métodos.

  2. Quando eu refatoro um código realmente podre, começo tentando fazer o máximo de métodos @staticmethodpossível. Isso me permite extrair esses métodos em uma classe - embora eu concorde, isso raramente é algo que eu uso, mas foi útil algumas vezes.

vlad-ardelean
fonte
1

Em minha estimativa, não há um único benefício de desempenho em usar @staticmethods em comparação com apenas definir a função fora e separada da classe que, de outra forma, seria uma @staticmethod.

A única coisa que eu diria que justifica sua existência é a conveniência. Métodos estáticos são comuns em outras linguagens de programação populares, então por que não python? Se você deseja criar uma função com comportamento que está intimamente associado à classe para a qual está criando, mas ela não acessa / modifica os dados internos de uma instância da classe de uma forma que justifique conceituá-la como uma dessa classe, em seguida, coloque um @staticmethodacima dele e qualquer pessoa que ler seu código aprenderá imediatamente muito sobre a natureza do método e sua relação com a classe.

Uma coisa que gosto de fazer ocasionalmente é colocar funcionalidades que minha classe usa muito internamente em programas privados @staticmethod. Dessa forma, eu não entulho a API exposta por meu módulo com métodos que ninguém usando meu módulo precisaria ver, muito menos usar.

Garrett Gutierrez
fonte