Métodos estáticos em Python?


1719

É possível ter métodos estáticos em Python que eu poderia chamar sem inicializar uma classe, como:

ClassName.static_method()

Respostas:


2026

Sim, usando o decorador staticmethod

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

MyClass.the_static_method(2)  # outputs 2

Observe que algum código pode usar o método antigo de definir um método estático, usando staticmethodcomo uma função e não como um decorador. Isso só deve ser usado se você tiver que suportar versões antigas do Python (2.2 e 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

Isso é totalmente idêntico ao primeiro exemplo (usando @staticmethod), mas não usando a sintaxe agradável do decorador

Finalmente, use staticmethod()com moderação! Existem muito poucas situações em que métodos estáticos são necessários no Python, e eu os vi usados ​​muitas vezes em que uma função separada de "nível superior" seria mais clara.


A seguir, é apresentado literalmente na documentação :

Um método estático não recebe um primeiro argumento implícito. Para declarar um método estático, use este idioma:

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

O formulário @staticmethod é um decorador de funções - consulte a descrição das definições de funções em Definições de funções para obter detalhes.

Pode ser chamado na classe (como C.f()) ou em uma instância (como C().f()). A instância é ignorada, exceto por sua classe.

Os métodos estáticos no Python são semelhantes aos encontrados em Java ou C ++. Para um conceito mais avançado, consulte classmethod().

Para obter mais informações sobre métodos estáticos, consulte a documentação sobre a hierarquia de tipos padrão em A hierarquia de tipos padrão .

Novo na versão 2.2.

Alterado na versão 2.4: Sintaxe do decorador de funções adicionada.


15
Por que adicionar @staticmethoddecoração ou usar um ponteiro de função, quando você pode simplesmente evitar o primeiro selfparâmetro? Bem, para um objeto a, você não será capaz de chamada a.your_static_method(), o que é permitido em outros idiomas, mas é assim mesmo considerado uma má prática eo compilador sempre adverte sobre isso
SomethingSomething

1
Estou usando python 2.7.12, com decorador @staticmethod não consigo chamar o primeiro método estático definido. Seu erro givin: TypeError: objeto 'staticmethod' não é exigível
Supratim Samantray

Supratim Samantray, você tem certeza? porque aqui está claramente afirmado que pode ser usado após o 2.4.
realslimshanky

11
@AlgoAlgo, se você não usar o nome da variável "self", mas não adicionar o decorador, o primeiro argumento, por exemplo, arg1, ainda será a referência à própria instância. O nome self é apenas uma convenção.
George Moutsopoulos

1
@GeorgeMoutsopoulos - concordou geralmente. Mas - você poderá, de qualquer maneira, chamar o método como ClassName.methodName()se fosse um método estático e, em seguida, nenhum selfserá fornecido ao método. Como você disse, ainda será possível também chamar esse método como ClassInstance.methodName()e selfserá fornecido como o primeiro parâmetro, independentemente do nome.
SomethingSomething

220

Eu acho que Steven está realmente certo . Para responder à pergunta original, então, para configurar um método de classe, simplesmente assuma que o primeiro argumento não será uma instância de chamada e, em seguida, certifique-se de chamar o método apenas da classe.

(Observe que esta resposta se refere ao Python 3.x. No Python 2.x, você receberá um TypeErrorpor chamar o método na própria classe.)

Por exemplo:

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)

Nesse código, o método "rollCall" assume que o primeiro argumento não é uma instância (como seria se fosse chamado por uma instância e não por uma classe). Desde que "rollCall" seja chamado da classe e não de uma instância, o código funcionará bem. Se tentarmos chamar "rollCall" de uma instância, por exemplo:

rex.rollCall(-1)

no entanto, faria com que uma exceção fosse lançada porque enviaria dois argumentos: ele próprio e -1, e "rollCall" é definido apenas para aceitar um argumento.

Aliás, rex.rollCall () enviaria o número correto de argumentos, mas também causaria uma exceção, porque agora n representaria uma instância Dog (ou seja, rex) quando a função espera que n seja numérico.

É aqui que entra a decoração: se precedermos o método "rollCall" com

@staticmethod

então, declarando explicitamente que o método é estático, podemos chamá-lo de uma instância. Agora,

rex.rollCall(-1)

podia funcionar. A inserção de @staticmethod antes de uma definição de método impede que uma instância se envie como argumento.

Você pode verificar isso tentando o seguinte código com e sem a linha @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)

8
O primeiro exemplo (chamar por classe sem @staticmethod) não funciona para mim no Python 2.7. Eu recebo "TypeError: método desacoplado Rollcall () deve ser chamado com instância Cão como primeiro argumento (exemplo int tem vez)"
TBA

2
Isso não funciona. Ambos Dog.rollCall (-1) e rex.rollCall (-1) retornar o mesmoTypeError: unbound method rollCall() must be called with Dog instance as first argument (got int instance instead)
Mittenchops

3
@MestreLion Acho que isso não é um grande benefício, pois tudo o que faz é confundir métodos estáticos e de instância e fazer com que um pareça o outro. Se você sabe que um método é estático, deve acessá-lo como um método estático. O fato de o método não precisar ou usar sua instância deve ser evidente sempre que você usar o método. Eu sempre uso T.my_static_method()ou type(my_t_instance).my_static_method(), pois isso é mais claro e torna imediatamente óbvio que estou chamando um método estático.
Asad Saeeduddin


54

Você realmente não precisa usar o @staticmethoddecorador. Apenas declarando um método (que não espera o parâmetro self) e chamá-lo da classe O decorador está lá apenas no caso de você querer chamá-lo também de uma instância (que não era o que você queria fazer)

Principalmente, você apenas usa funções ...


Isso não funcionaria para o python 2.5.2. class Dummy: def static1(): print "hello from static1" @staticmethod def static2(): print "hello from static2" Dummy.static2() Dummy.static1() Saída: hello from static2 Traceback <última chamada mais recente>: Arquivo "ll.py", linha 46, em <module> Dummy.static1 () TypeError: o método não estático static1 () deve ser chamado com instância manequim como primeiro argumento (não tem nada em seu lugar)
Mahori

7
Isso não funciona dentro de uma classe. Python passará selfcomo o primeiro argumento, a menos que você o diga. (ver: decorador)
Navin

9
Na verdade, isso está errado em 2.7 e legal a partir de 3.X (teste bem em 3.2). Ou seja, você não pode chamar um método do contexto de uma classe sem o decorador @staticmethod no 2.7 e abaixo. No 3.2, ele funciona e irá inserir um selfref de forma apropriada, dependendo de como é chamado. Caso de teste: pastebin.com/12DDV7DB .
Spinkus

2
Tecnicamente preciso, mas uma solução terrível. O staticmethoddecorador permite chamar a função na classe e em uma instância (essa solução falha ao chamar a função em uma instância).
Ethan Furman

um método pode ser chamado como qualquer outro atributo de um objeto usando a notação de ponto. class C: def callme(): print('called'); C.callme()
pouya

32

Métodos estáticos em Python?

É possível ter métodos estáticos em Python para que eu possa chamá-los sem inicializar uma classe, como:

ClassName.StaticMethod()

Sim, métodos estáticos podem ser criados assim (embora seja um pouco mais Pythonic usar sublinhados em vez do CamelCase para métodos):

class ClassName(object):

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

O exemplo acima usa a sintaxe do decorador. Essa sintaxe é equivalente a

class ClassName(object):

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

    static_method = staticmethod(static_method)

Isso pode ser usado exatamente como você descreveu:

ClassName.static_method()

Um exemplo interno de um método estático está str.maketrans()no Python 3, que era uma função no stringmódulo do Python 2.


Outra opção que pode ser usada como você descreve é classmethod: a diferença é que o método de classe obtém a classe como primeiro argumento implícito e, se for subclassificado, obtém a subclasse como primeiro 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'''

Observe que esse clsnão é um nome obrigatório para o primeiro argumento, mas os codificadores Python mais experientes o considerarão mal feito se você usar qualquer outra coisa.

Estes são normalmente usados ​​como construtores alternativos.

new_instance = ClassName.class_method()

Um exemplo embutido é dict.fromkeys():

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

11

Além das particularidades de como os objetos do método estático se comportam, há um certo tipo de beleza que você pode encontrar com eles quando se trata de organizar o código no nível do 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

Agora, torna-se um pouco mais intuitivo e auto-documentado no contexto em que certos componentes devem ser usados ​​e é ideal para nomear casos de teste distintos, além de ter uma abordagem direta de como os módulos de teste são mapeados para módulos reais sob testes para puristas .

Costumo achar viável aplicar essa abordagem à organização do código de utilidade de um projeto. Muitas vezes, as pessoas imediatamente correm para criar um utilspacote e terminam com 9 módulos, dos quais um tem 120 LOC e o restante são duas dúzias de LOC, na melhor das hipóteses. Prefiro começar com isso e convertê-lo em um pacote e criar módulos apenas para os animais que realmente os merecem:

# 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

10

Talvez a opção mais simples seja colocar essas funções fora da 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"

Usando esse método, as funções que modificam ou usam o estado interno do objeto (têm efeitos colaterais) podem ser mantidas na classe e as funções reutilizáveis ​​do utilitário podem ser movidas para fora.

Digamos que esse arquivo seja chamado dogs.py. Para usá-los, você chamaria em dogs.barking_sound()vez de dogs.Dog.barking_sound.

Se você realmente precisa de um método estático para fazer parte da classe, pode usar o decorador staticmethod .


1

Portanto, métodos estáticos são os métodos que podem ser chamados sem criar o objeto de uma classe. Por exemplo :-

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

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

No exemplo acima, o método addé chamado pelo nome da classe e Anão pelo nome do objeto.


-3

Os métodos estáticos do Python podem ser criados de duas maneiras.

  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))

Resultado:

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))

Resultado:

Resultado: 25


Você acabou de fornecer exemplos e compartilhar as maneiras de como fazer isso. Você não explicou o que os métodos significam.
zixuan 10/09/19

-4

Encontro essa pergunta de tempos em tempos. O caso de uso e o exemplo de que gosto é:

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

Não faz sentido criar um objeto da classe cmath, porque não há estado em um objeto cmath. No entanto, cmath é uma coleção de métodos todos relacionados de alguma forma. No meu exemplo acima, todas as funções no cmath atuam em números complexos de alguma maneira.


Você não respondeu à pergunta, mas forneceu um exemplo.
Zixuan
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.