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()
Joan Venge
fonte

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.

dbr
fonte
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)
Richard Ambler
fonte
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
81

Sim, confira o decorador staticmethod :

>>> class C:
...     @staticmethod
...     def hello():
...             print "Hello World"
...
>>> C.hello()
Hello World
Paolo Bergantino
fonte
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 ...

hichris123
fonte
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'])
Aaron Hall
fonte
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
Filip Dupanović
fonte
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 .

Matt Woelk
fonte
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.

Aman Raheja
fonte
-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

Codemaker
fonte
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.

user1928764
fonte
Você não respondeu à pergunta, mas forneceu um exemplo.
Zixuan