Diferença entre staticmethod e classmethod


3583

Qual é a diferença entre uma função decorada com @staticmethode uma decorada com @classmethod?


11
os métodos estáticos às vezes são melhores, pois o nível do módulo funciona em python para fins de limpeza. Com uma função de módulo, é mais fácil importar apenas a função necessária e evitar "." sintaxe (estou olhando para você, Objective-C). Os métodos de classe têm mais uso, pois podem ser usados ​​em combinação com o polimorfismo para criar funções de "padrão de fábrica". isso ocorre porque os métodos de classe recebem a classe como um parâmetro implícito.
FistOfFury

27
tl; dr >> quando comparados aos métodos normais, os métodos estáticos e os métodos de classe também podem ser acessados ​​usando a classe, mas, diferentemente dos métodos de classe, os métodos estáticos são imutáveis ​​por herança.
imsrgadich

4
Discussão relacionada por Raymond Hettinger sobre o tópico: youtube.com/watch?v=HTLu2DFOdTg
moooeeeep

mais preciso youtube.com/watch?v=HTLu2DFOdTg&feature=youtu.be&t=2689, você só precisa do método de classe para construtores alternativos. Caso contrário, você pode usar staticmethod e acesso de qualquer atributo de classe (via / cs.) Pela "CLASSNAME" de alguma forma mais informativa real em vez de cls como em classmethod
Robert Nowak

Respostas:


3138

Talvez um pouco de exemplo de código vai ajudar: Observe a diferença nas assinaturas de chamadas de foo, class_fooe static_foo:

class A(object):
    def foo(self, x):
        print "executing foo(%s, %s)" % (self, x)

    @classmethod
    def class_foo(cls, x):
        print "executing class_foo(%s, %s)" % (cls, x)

    @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)" % x    

a = A()

Abaixo está a maneira usual de uma instância de objeto chamar um método. A instância do objeto,, aé implicitamente passada como o primeiro argumento.

a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)

Com os métodos de classe, a classe da instância do objeto é passada implicitamente como o primeiro argumento em vez de self.

a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Você também pode ligar class_foousando a classe. De fato, se você definir algo para ser um método de classe, provavelmente é porque pretende chamá-lo da classe e não de uma instância de classe. A.foo(1)teria gerado um TypeError, mas A.class_foo(1)funciona muito bem:

A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Um uso que as pessoas encontraram para métodos de classe é criar construtores alternativos herdáveis .


Com métodos estáticos , nem self(a instância do objeto) nem cls(a classe) são implicitamente passados ​​como o primeiro argumento. Eles se comportam como funções simples, exceto que você pode chamá-los de uma instância ou da classe:

a.static_foo(1)
# executing static_foo(1)

A.static_foo('hi')
# executing static_foo(hi)

Os métodos estáticos são usados ​​para agrupar funções que possuem alguma conexão lógica com uma classe para a classe.


fooé apenas uma função, mas quando você chama, a.foovocê não apenas obtém a função, você obtém uma versão "parcialmente aplicada" da função com a instância do objeto avinculada como o primeiro argumento da função. fooespera 2 argumentos, enquanto a.fooespera apenas 1 argumento.

aestá vinculado a foo. Isso é o que significa o termo "vinculado" abaixo:

print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>

Com a.class_foo, anão está vinculado a class_foo, mas a classe Aestá vinculada class_foo.

print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>

Aqui, com um método static, mesmo que seja um método, a.static_fooapenas retorna uma boa função ole sem argumentos vinculados. static_fooespera 1 argumento e também a.static_fooespera 1 argumento.

print(a.static_foo)
# <function static_foo at 0xb7d479cc>

E é claro que a mesma coisa acontece quando você liga static_foopara a turma A.

print(A.static_foo)
# <function static_foo at 0xb7d479cc>

182
Eu não entendo qual é o problema de usar o método static. podemos apenas usar uma função simples fora da classe.
Alcott

372
@ Alcott: Você pode querer mover uma função para uma classe porque ela pertence logicamente à classe. No código fonte do Python (por exemplo, multiprocessamento, tartaruga, dist-packages), é usado para "ocultar" funções "privadas" com sublinhado único do espaço de nome do módulo. Seu uso, no entanto, é altamente concentrado em apenas alguns módulos - talvez uma indicação de que é principalmente uma coisa estilística. Embora eu não tenha encontrado nenhum exemplo disso, @staticmethodpode ajudar a organizar seu código sendo substituível por subclasses. Sem ele, você teria variantes da função flutuando no espaço de nome do módulo.
unutbu

15
... juntamente com algumas explicações sobre onde e por que usar instância, classe ou métodos estáticos. Você não deu uma única palavra sobre isso, mas o OP também não perguntou.
MestreLion

106
@Alcott: como disse Unutbu, métodos estáticos são uma característica da organização / estilo. Às vezes, um módulo tem muitas classes, e algumas funções auxiliares estão logicamente vinculadas a uma determinada classe e não a outras, portanto, faz sentido não "poluir" o módulo com muitas "funções livres" e é melhor usar um método estático. método de depender do estilo pobre de misturar classes e defs função juntos em código apenas para mostrar que eles são "afins"
MestreLion

4
Mais um uso para @staticmethod- você pode usá-lo para remover detritos. Estou implementando uma linguagem de programação em Python - as funções definidas pela biblioteca usam um executemétodo estático , em que as funções definidas pelo usuário exigem argumentos de instância (ou seja, o corpo da função). Esse decorador elimina avisos "não utilizados nos parâmetros" no inspetor PyCharm.
Tehwalrus 28/03

799

Um método estático é um método que não sabe nada sobre a classe ou instância em que foi chamado. Apenas obtém os argumentos que foram passados, nenhum primeiro argumento implícito. É basicamente inútil no Python - você pode simplesmente usar uma função de módulo em vez de um método estático.

A classmethod , por outro lado, é um método que é passada a classe que foi chamado, ou a classe da instância que foi chamado, como primeiro argumento. Isso é útil quando você deseja que o método seja uma fábrica para a classe: como ele obtém a classe real na qual foi chamada como primeiro argumento, você sempre pode instanciar a classe correta, mesmo quando há subclasses. Observe, por exemplo dict.fromkeys(), como , um método de classe, retorna uma instância da subclasse quando chamada em uma subclasse:

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
... 
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>> 

715
Um método estático não é inútil - é uma maneira de colocar uma função em uma classe (porque pertence logicamente a ela), enquanto indica que não requer acesso à classe.
Tony Meyer

136
Portanto, apenas "basicamente" inútil. Essa organização, assim como a injeção de dependência, são usos válidos de métodos estáticos, mas como os módulos, não as classes como em Java, são os elementos básicos da organização de código no Python, seu uso e utilidade são raros.
Thomas Wouters

40
O que é lógico sobre definir um método dentro de uma classe, quando não tem nada a ver com a classe ou suas instâncias?
Ben James

107
Talvez por causa da herança? Os métodos estáticos podem ser herdados e substituídos, assim como os métodos de instância e de classe, e a pesquisa funciona conforme o esperado (diferente do Java). Os métodos estáticos não são realmente resolvidos estaticamente, sejam chamados na classe ou instância, portanto, a única diferença entre métodos de classe e estáticos é o primeiro argumento implícito.
haridsv

80
Eles também criam um espaço para nome mais limpo e facilitam o entendimento de que a função tem algo a ver com a classe.
Imbrondir 22/08/11

149

Basicamente, @classmethodum método cujo primeiro argumento é a classe da qual é chamado (e não a instância da classe) @staticmethodnão possui argumentos implícitos.


103

Documentos oficiais do python:

@classmethod

Um método de classe recebe a classe como primeiro argumento implícito, assim como um método de instância recebe a instância. Para declarar um método de classe, use este idioma:

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

O @classmethodformulário é 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. Se um método de classe for chamado para uma classe derivada, o objeto da classe derivada será passado como o primeiro argumento implícito.

Os métodos de classe são diferentes dos métodos estáticos em C ++ ou Java. Se você quiser, veja staticmethod()nesta seção.

@staticmethod

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 @staticmethodformulário é 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()nesta seção.


Não há um erro nos documentos? Não deve estar no staticmethod: "A instância e sua classe são ambas ignoradas." em vez de "A instância é ignorada, exceto por sua classe".
mirek 2/03

Pode ser um erro de recortar e colar, mas, estritamente falando, você não pode chamar um método em uma classe se ignorar a classe.
Aaron Bentley

76

Aqui está um pequeno artigo sobre esta questão

A função @staticmethod nada mais é do que uma função definida dentro de uma classe. É possível chamar sem instanciar a classe primeiro. Sua definição é imutável por herança.

A função @classmethod também pode ser chamada sem instanciar a classe, mas sua definição segue a subclasse, não a classe pai, por herança. Isso ocorre porque o primeiro argumento para a função @classmethod deve sempre ser cls (class).


1
Então, isso significa que, usando um método static, eu estou sempre vinculado à classe Parent e com o método class class, ao qual declaro o método class (nesse caso, a subclasse)?
Mohan Gulati

7
Não. Ao usar um método estático, você não é obrigado; não há primeiro parâmetro implícito. Usando classmethod, você obtém como primeiro parâmetro implícito a classe na qual você chamou o método (se você o chamou diretamente em uma classe) ou a classe da instância em que você chamou o método (se você o chamou em uma instância).
Matt Anderson

6
Pode ser expandido um pouco para mostrar que, ao ter uma classe como primeiro argumento, os métodos de classe têm acesso direto a outros atributos e métodos de classe, enquanto os métodos estáticos não (eles precisam codificar MyClass.attr para isso)
MestreLion

"Sua definição é imutável por herança." não faz sentido no Python, você pode substituir um método estático muito bem.
cz

68

Para decidir se deseja usar @staticmethod ou @classmethod, é necessário procurar dentro do seu método. Se seu método acessar outras variáveis ​​/ métodos em sua classe, use @classmethod . Por outro lado, se o seu método não tocar em nenhuma outra parte da classe, use @staticmethod.

class Apple:

    _counter = 0

    @staticmethod
    def about_apple():
        print('Apple is good for you.')

        # note you can still access other member of the class
        # but you have to use the class instance 
        # which is not very nice, because you have repeat yourself
        # 
        # For example:
        # @staticmethod
        #    print('Number of apples have been juiced: %s' % Apple._counter)
        #
        # @classmethod
        #    print('Number of apples have been juiced: %s' % cls._counter)
        #
        #    @classmethod is especially useful when you move your function to other class,
        #       you don't have to rename the class reference 

    @classmethod
    def make_apple_juice(cls, number_of_apples):
        print('Make juice:')
        for i in range(number_of_apples):
            cls._juice_this(i)

    @classmethod
    def _juice_this(cls, apple):
        print('Juicing %d...' % apple)
        cls._counter += 1

qual seria a vantagem de classmethod e cls._counter vs. staticmethod e Apple._counter
Robert Nowak

1
cls._counterainda seria cls._countermesmo se o código fosse colocado em uma classe diferente ou se o nome da classe fosse alterado. Apple._counteré específico para a Appleclasse; para uma classe diferente ou quando o nome da classe for alterado, você precisará alterar a classe referenciada.
Kievlaluno

53

Qual é a diferença entre @staticmethod e @classmethod em Python?

Você pode ter visto código Python como este pseudocódigo, que demonstra as assinaturas dos vários tipos de métodos e fornece uma documentação para explicar cada um:

class Foo(object):

    def a_normal_instance_method(self, arg_1, kwarg_2=None):
        '''
        Return a value that is a function of the instance with its
        attributes, and other arguments such as arg_1 and kwarg2
        '''

    @staticmethod
    def a_static_method(arg_0):
        '''
        Return a value that is a function of arg_0. It does not know the 
        instance or class it is called from.
        '''

    @classmethod
    def a_class_method(cls, arg1):
        '''
        Return a value that is a function of the class and other arguments.
        respects subclassing, it is called with the class it is called from.
        '''

O método de instância normal

Primeiro eu vou explicar a_normal_instance_method. Isso é chamado precisamente de " método de instância ". Quando um método de instância é usado, ele é usado como uma função parcial (em oposição a uma função total, definida para todos os valores quando visualizados no código-fonte) que é, quando usado, o primeiro dos argumentos é predefinido como a instância do objeto, com todos os seus atributos. Ele tem a instância do objeto vinculada a ele e deve ser chamado de uma instância do objeto. Normalmente, ele acessará vários atributos da instância.

Por exemplo, esta é uma instância de uma sequência:

', '

se usarmos o método de instância, joinnessa string, para ingressar em outro iterável, é obviamente uma função da instância, além de ser uma função da lista iterável ['a', 'b', 'c']:

>>> ', '.join(['a', 'b', 'c'])
'a, b, c'

Métodos vinculados

Os métodos de instância podem ser vinculados por meio de uma pesquisa pontilhada para uso posterior.

Por exemplo, isso vincula o str.joinmétodo à ':'instância:

>>> join_with_colons = ':'.join 

E mais tarde, podemos usar isso como uma função que já tem o primeiro argumento vinculado a ela. Dessa maneira, funciona como uma função parcial na instância:

>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'

Método estático

O método estático não aceita a instância como argumento.

É muito semelhante a uma função no nível do módulo.

No entanto, uma função no nível do módulo deve residir no módulo e ser importada especialmente para outros locais onde é usada.

Se ele estiver anexado ao objeto, no entanto, ele seguirá o objeto convenientemente por meio de importação e herança também.

Um exemplo de método estático é str.maketransmovido do stringmódulo no Python 3. Torna uma tabela de conversão adequada para consumo por str.translate. Parece um pouco bobo quando usado a partir de uma instância de uma string, como demonstrado abaixo, mas importar a função do stringmódulo é um pouco desajeitado e é bom poder chamá-lo da classe, como emstr.maketrans

# demonstrate same function whether called from instance or not:
>>> ', '.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
>>> str.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}

No python 2, você precisa importar esta função do módulo de string cada vez menos útil:

>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'

Método de classe

Um método de classe é semelhante a um método de instância, pois leva um primeiro argumento implícito, mas, em vez de pegar a instância, leva a classe. Freqüentemente, eles são usados ​​como construtores alternativos para melhor uso semântico e suportam herança.

O exemplo mais canônico de um método de classe interno é dict.fromkeys. É usado como um construtor alternativo do dict (adequado para quando você sabe quais são suas chaves e deseja um valor padrão para elas.)

>>> dict.fromkeys(['a', 'b', 'c'])
{'c': None, 'b': None, 'a': None}

Quando ditamos uma subclasse, podemos usar o mesmo construtor, que cria uma instância da subclasse.

>>> class MyDict(dict): 'A dict subclass, use to demo classmethods'
>>> md = MyDict.fromkeys(['a', 'b', 'c'])
>>> md
{'a': None, 'c': None, 'b': None}
>>> type(md)
<class '__main__.MyDict'>

Veja o código fonte do pandas para outros exemplos similares de construtores alternativos, e também veja a documentação oficial do Python em classmethode staticmethod.


43

Comecei a aprender a linguagem de programação com C ++, Java e Python, e essa pergunta também me incomodou muito, até entender o uso simples de cada uma.

Método de classe: Python, ao contrário de Java e C ++, não possui sobrecarga de construtor. E assim, para conseguir isso, você pode usar classmethod. O exemplo a seguir explicará isso

Vamos considerar que temos uma Personclasse que recebe dois argumentos first_namee last_namee cria a instância Person.

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

Agora, se o requisito chegar, você precisará criar uma classe usando apenas um nome, apenas a first_name, não poderá fazer algo assim no Python.

Isso causará um erro quando você tentar criar um objeto (instância).

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def __init__(self, first_name):
        self.first_name = first_name

No entanto, você pode conseguir a mesma coisa usando @classmethodo mencionado abaixo

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @classmethod
    def get_person(cls, first_name):
        return cls(first_name, "")

Método estático: isso é bastante simples, não está vinculado a instância ou classe e você pode simplesmente chamar isso usando o nome da classe.

Então, digamos que no exemplo acima, você precisa de uma validação que first_namenão exceda 20 caracteres, basta fazer isso.

@staticmethod  
def validate_name(name):
    return len(name) <= 20

e você pode simplesmente chamar usando class name

Person.validate_name("Gaurang Shah")

2
É um post antigo, mas uma maneira mais pitônica de conseguir que o construtor aceite um ou dois argumentos estaria usando em def __init__(self, first_name, last_name="")vez do método de classe get_person. Também o resultado será exatamente o mesmo neste caso.
akarilimano 17/09/19

31

Acho que uma pergunta melhor é "Quando você usaria @classmethod vs @staticmethod?"

O @classmethod permite fácil acesso a membros privados associados à definição de classe. essa é uma ótima maneira de executar singletons ou classes de fábrica que controlam o número de instâncias dos objetos criados.

O @staticmethod fornece ganhos marginais de desempenho, mas ainda tenho que ver um uso produtivo de um método estático dentro de uma classe que não poderia ser alcançado como uma função autônoma fora da classe.


31

@ decorators foram adicionados no python 2.4 Se você estiver usando python <2.4, poderá usar as funções classmethod () e staticmethod ().

Por exemplo, se você deseja criar um método de fábrica (Uma função retornando uma instância de uma implementação diferente de uma classe, dependendo do argumento recebido), você pode fazer algo como:

class Cluster(object):

    def _is_cluster_for(cls, name):
        """
        see if this class is the cluster with this name
        this is a classmethod
        """ 
        return cls.__name__ == name
    _is_cluster_for = classmethod(_is_cluster_for)

    #static method
    def getCluster(name):
        """
        static factory method, should be in Cluster class
        returns a cluster object for the given name
        """
        for cls in Cluster.__subclasses__():
            if cls._is_cluster_for(name):
                return cls()
    getCluster = staticmethod(getCluster)

Observe também que este é um bom exemplo para o uso de um método de classe e um método estático. O método estático claramente pertence à classe, pois usa a classe Cluster internamente. O método de classe precisa apenas de informações sobre a classe e nenhuma instância do objeto.

Outro benefício de tornar o _is_cluster_formétodo um método de classe é que uma subclasse pode decidir mudar sua implementação, talvez porque seja bastante genérica e possa lidar com mais de um tipo de cluster; portanto, apenas verificar o nome da classe não seria suficiente.


28

Métodos estáticos:

  • Funções simples sem argumento próprio.
  • Trabalho em atributos de classe; não nos atributos da instância.
  • Pode ser chamado através de classe e instância.
  • A função interna staticmethod () é usada para criá-los.

Benefícios dos métodos estáticos:

  • Ele localiza o nome da função no classscope
  • Move o código da função para mais perto de onde é usado
  • Mais conveniente para importar versus funções no nível do módulo, pois cada método não precisa ser especialmente importado

    @staticmethod
    def some_static_method(*args, **kwds):
        pass

Métodos de classe:

  • Funções que têm o primeiro argumento como nome da classe.
  • Pode ser chamado através de classe e instância.
  • Eles são criados com a função incorporada do método classmethod.

     @classmethod
     def some_class_method(cls, *args, **kwds):
         pass

22

@staticmethodapenas desativa a função padrão como descritor de método. classmethod agrupa sua função em um contêiner que pode ser chamado que passa uma referência à classe proprietária como primeiro argumento:

>>> class C(object):
...  pass
... 
>>> def f():
...  pass
... 
>>> staticmethod(f).__get__(None, C)
<function f at 0x5c1cf0>
>>> classmethod(f).__get__(None, C)
<bound method type.f of <class '__main__.C'>>

Por uma questão de fato, classmethodpossui uma sobrecarga de tempo de execução, mas possibilita o acesso à classe proprietária. Como alternativa, eu recomendo usar uma metaclasse e colocar os métodos de classe nessa metaclasse:

>>> class CMeta(type):
...  def foo(cls):
...   print cls
... 
>>> class C(object):
...  __metaclass__ = CMeta
... 
>>> C.foo()
<class '__main__.C'>

1
Uma possível desvantagem de uma metaclasse para isso que me ocorre imediatamente é que você não pode chamar o método de classe diretamente em uma instância. c = C(); c.foo()gera AttributeError, você teria que fazer type(c).foo(). Isso também pode ser considerado um recurso - não consigo pensar no motivo pelo qual você gostaria.
Aaron Hall

20

O guia definitivo sobre como usar métodos estáticos, de classe ou abstratos no Python é um bom link para este tópico e resume-o da seguinte forma.

@staticmethodA função nada mais é do que uma função definida dentro de uma classe. É possível chamar sem instanciar a classe primeiro. Sua definição é imutável por herança.

  • O Python não precisa instanciar um método vinculado para o objeto.
  • Facilita a legibilidade do código e não depende do estado do próprio objeto;

@classmethodA função também pode ser chamada sem instanciar a classe, mas sua definição segue a subclasse, e não a classe pai, por herança, pode ser substituída pela subclasse. Isso ocorre porque o primeiro argumento para a @classmethodfunção deve sempre ser cls (classe).

  • Métodos de fábrica , usados ​​para criar uma instância para uma classe usando, por exemplo, algum tipo de pré-processamento.
  • Métodos estáticos que chamam métodos estáticos : se você dividir métodos estáticos em vários métodos estáticos, não deverá codificar o nome da classe, mas usar métodos de classe

Graças @zangw - a imutabilidade herdada da função estática é a principal diferença parece
hard_working_ant

18

Somente o primeiro argumento difere :

  • método normal: o objeto atual se passado automaticamente como um primeiro argumento (adicional)
  • classmethod: a classe do objeto atual é automaticamente passada como um argumento (adicional)
  • staticmethod: nenhum argumento extra é passado automaticamente. O que você passou para a função é o que obtém.

Em mais detalhes...

método normal

Quando o método de um objeto é chamado, ele recebe automaticamente um argumento extra selfcomo seu primeiro argumento. Ou seja, método

def f(self, x, y)

deve ser chamado com 2 argumentos. selfé passado automaticamente e é o próprio objeto .

método de classe

Quando o método é decorado

@classmethod
def f(cls, x, y)

o argumento fornecido automaticamente não é self , mas a classe de self .

método estático

Quando o método é decorado

@staticmethod
def f(x, y)

o método não recebe nenhum argumento automático. É dado apenas os parâmetros com os quais é chamado.

usos

  • classmethod é usado principalmente para construtores alternativos.
  • staticmethodnão usa o estado do objeto. Pode ser uma função externa a uma classe. Ele é colocado apenas dentro da classe para agrupar funções com funcionalidade semelhante (por exemplo, como Mathmétodos estáticos de classe do Java )
class Point
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def frompolar(cls, radius, angle):
        """The `cls` argument is the `Point` class itself"""
        return cls(radius * cos(angle), radius * sin(angle))

    @staticmethod
    def angle(x, y):
        """this could be outside the class, but we put it here 
just because we think it is logically related to the class."""
        return atan(y, x)


p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)

angle = Point.angle(3, 2)

17

Deixe-me dizer a semelhança entre um método decorado com @classmethod vs @staticmethod primeiro.

Semelhança: Ambos podem ser chamados na própria classe , e não apenas na instância da classe. Então, em certo sentido, os dois são métodos de Class .

Diferença: Um método de classe receberá a própria classe como o primeiro argumento, enquanto um método estático não.

Portanto, um método estático não é, de certo modo, vinculado à própria Classe e está apenas lá, apenas porque pode ter uma funcionalidade relacionada.

>>> class Klaus:
        @classmethod
        def classmthd(*args):
            return args

        @staticmethod
        def staticmthd(*args):
            return args

# 1. Call classmethod without any arg
>>> Klaus.classmthd()  
(__main__.Klaus,)  # the class gets passed as the first argument

# 2. Call classmethod with 1 arg
>>> Klaus.classmthd('chumma')
(__main__.Klaus, 'chumma')

# 3. Call staticmethod without any arg
>>> Klaus.staticmthd()  
()

# 4. Call staticmethod with 1 arg
>>> Klaus.staticmthd('chumma')
('chumma',)

11

Outra consideração em relação ao staticmethod vs classmethod surge com herança. Digamos que você tenha a seguinte turma:

class Foo(object):
    @staticmethod
    def bar():
        return "In Foo"

E você deseja substituir bar()em uma classe filho:

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"

Isso funciona, mas observe que agora a bar()implementação na classe filho ( Foo2) não pode mais tirar proveito de nada específico para essa classe. Por exemplo, digamos que Foo2tenha um método chamado magic()que você deseja usar na Foo2implementação de bar():

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"
    @staticmethod
    def magic():
        return "Something useful you'd like to use in bar, but now can't" 

A solução aqui seria chamar Foo2.magic()em bar(), mas então você está se repetindo (se o nome de Foo2mudanças, você tem que lembrar de atualizar esse bar()método).

Para mim, isso é uma pequena violação do princípio de abrir / fechar , pois uma decisão tomada Fooestá afetando sua capacidade de refatorar código comum em uma classe derivada (ou seja, é menos aberta à extensão). Se bar()fosse um classmethod, ficaríamos bem:

class Foo(object):
    @classmethod
    def bar(cls):
        return "In Foo"

class Foo2(Foo):
    @classmethod
    def bar(cls):
        return "In Foo2 " + cls.magic()
    @classmethod
    def magic(cls):
        return "MAGIC"

print Foo2().bar()

Dá: In Foo2 MAGIC


7

Vou tentar explicar a diferença básica usando um exemplo.

class A(object):
    x = 0

    def say_hi(self):
        pass

    @staticmethod
    def say_hi_static():
        pass

    @classmethod
    def say_hi_class(cls):
        pass

    def run_self(self):
        self.x += 1
        print self.x # outputs 1
        self.say_hi()
        self.say_hi_static()
        self.say_hi_class()

    @staticmethod
    def run_static():
        print A.x  # outputs 0
        # A.say_hi() #  wrong
        A.say_hi_static()
        A.say_hi_class()

    @classmethod
    def run_class(cls):
        print cls.x # outputs 0
        # cls.say_hi() #  wrong
        cls.say_hi_static()
        cls.say_hi_class()

1 - podemos chamar diretamente métodos estáticos e de classe sem inicializar

# A.run_self() #  wrong
A.run_static()
A.run_class()

2- O método estático não pode chamar método próprio, mas pode chamar outro método estático e de classe

3- O método estático pertence à classe e não utilizará nenhum objeto.

4- O método de classe não está vinculado a um objeto, mas a uma classe.


anúncio 2: você tem certeza? Como o método static pode chamar o método class? Não tem referência a ele (a sua classe).
mirek 2/03

7

@classmethod: pode ser usado para criar um acesso global compartilhado a todas as instâncias criadas dessa classe ... como atualizar um registro por vários usuários ... Particularmente, achei útil usar ao criar singletons também ..: )

@ método estático: não tem nada a ver com a classe ou instância associada a ... mas, para facilitar a leitura, pode-se usar o método estático


5

Você pode considerar a diferença entre:

Class A:
    def foo():  # no self parameter, no decorator
        pass

e

Class B:
    @staticmethod
    def foo():  # no self parameter
        pass

Isso mudou entre python2 e python3:

python2:

>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

python3:

>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

Portanto, o uso @staticmethodde métodos chamados apenas diretamente da classe tornou-se opcional no python3. Se você quiser chamá-los da classe e da instância, ainda precisará usar o@staticmethod decorador.

Os outros casos foram bem cobertos pela resposta da unutbus.


5

Um método de classe recebe a classe como primeiro argumento implícito, assim como um método de instância recebe a instância. É um método vinculado à classe e não ao objeto da classe. Ele tem acesso ao estado da classe, pois recebe um parâmetro da classe que aponta para a classe e não para a instância do objeto. Ele pode modificar um estado de classe que se aplicaria a todas as instâncias da classe. Por exemplo, ele pode modificar uma variável de classe que será aplicável a todas as instâncias.

Por outro lado, um método estático não recebe um primeiro argumento implícito, comparado com métodos de classe ou métodos de instância. E não pode acessar ou modificar o estado da classe. Pertence apenas à classe porque, do ponto de vista do design, é o caminho correto. Mas em termos de funcionalidade não está vinculado, em tempo de execução, à classe.

Como orientação, use métodos estáticos como utilitários, use métodos de classe, por exemplo, como fábrica. Ou talvez para definir um singleton. E use métodos de instância para modelar o estado e o comportamento das instâncias.

Espero ter sido claro!


4

Minha contribuição demonstra a diferença entre @classmethod, @staticmethode métodos de instância, incluindo como uma instância pode indiretamente chamar um @staticmethod. Mas, em vez de chamar indiretamente um @staticmethodde uma instância, torná-lo privado pode ser mais "pitônico". Obter algo de um método privado não é demonstrado aqui, mas é basicamente o mesmo conceito.

#!python3

from os import system
system('cls')
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

class DemoClass(object):
    # instance methods need a class instance and
    # can access the instance through 'self'
    def instance_method_1(self):
        return 'called from inside the instance_method_1()'

    def instance_method_2(self):
        # an instance outside the class indirectly calls the static_method
        return self.static_method() + ' via instance_method_2()'

    # class methods don't need a class instance, they can't access the
    # instance (self) but they have access to the class itself via 'cls'
    @classmethod
    def class_method(cls):
        return 'called from inside the class_method()'

    # static methods don't have access to 'cls' or 'self', they work like
    # regular functions but belong to the class' namespace
    @staticmethod
    def static_method():
        return 'called from inside the static_method()'
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# works even if the class hasn't been instantiated
print(DemoClass.class_method() + '\n')
''' called from inside the class_method() '''

# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '\n')
''' called from inside the static_method() '''
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# >>>>> all methods types can be called on a class instance <<<<<
# instantiate the class
democlassObj = DemoClass()

# call instance_method_1()
print(democlassObj.instance_method_1() + '\n')
''' called from inside the instance_method_1() '''

# # indirectly call static_method through instance_method_2(), there's really no use
# for this since a @staticmethod can be called whether the class has been
# instantiated or not
print(democlassObj.instance_method_2() + '\n')
''' called from inside the static_method() via instance_method_2() '''

# call class_method()
print(democlassObj.class_method() + '\n')
'''  called from inside the class_method() '''

# call static_method()
print(democlassObj.static_method())
''' called from inside the static_method() '''

"""
# whether the class is instantiated or not, this doesn't work
print(DemoClass.instance_method_1() + '\n')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""

2

Os métodos de classe, como o nome sugere, são usados ​​para fazer alterações nas classes e não nos objetos. Para fazer alterações nas classes, eles modificarão os atributos da classe (não os atributos do objeto), pois é assim que você atualiza as classes. Esta é a razão pela qual os métodos de classe tomam a classe (convencionalmente denominada por 'cls') como o primeiro argumento.

class A(object):
    m=54

    @classmethod
    def class_method(cls):
        print "m is %d" % cls.m

Os métodos estáticos, por outro lado, são usados ​​para executar funcionalidades que não estão vinculadas à classe, ou seja, elas não lêem ou escrevem variáveis ​​da classe. Portanto, métodos estáticos não aceitam classes como argumentos. Eles são usados ​​para que as classes possam executar funcionalidades que não estão diretamente relacionadas ao objetivo da classe.

class X(object):
    m=54 #will not be referenced

    @staticmethod
    def static_method():
        print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."

2

Analisar @staticmethod literalmente fornecendo informações diferentes.

Um método normal de uma classe é um método dinâmico implícito que toma a instância como primeiro argumento.
Por outro lado, um método estático não aceita a instância como primeiro argumento, portanto é chamado de 'estático' .

Um método estático é realmente uma função tão normal quanto as que estão fora de uma definição de classe.
Felizmente, é agrupado na classe apenas para ficar mais perto de onde é aplicado, ou você pode rolar para encontrá-lo.


2

Eu acho que dar uma versão puramente Python staticmethode classmethodajudaria a entender a diferença entre eles no nível da linguagem.

Ambos são descritores que não são de dados (seria mais fácil entendê-los se você estiver familiarizado com os descritores primeiro).

class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        return self.f


class ClassMethod(object):
    "Emulate PyClassMethod_Type() in Objects/funcobject.c"
    def __init__(self, f):
        self.f = f

    def __get__(self, obj, cls=None):
        def inner(*args, **kwargs):
            if cls is None:
                cls = type(obj)
            return self.f(cls, *args, **kwargs)
        return inner

1

staticmethod não tem acesso aos atributos do objeto, da classe ou das classes pai na hierarquia de herança. Pode ser chamado diretamente na classe (sem criar um objeto).

classmethod não tem acesso aos atributos do objeto. No entanto, ele pode acessar atributos da classe e das classes pai na hierarquia de herança. Pode ser chamado diretamente na classe (sem criar um objeto). Se chamado no objeto, é o mesmo que o método normal, que não self.<attribute(s)>acessa e acessaself.__class__.<attribute(s)> apenas.

Pense que temos uma classe b=2, vamos criar um objeto e redefinir isso para b=4ele. O método estático não pode acessar nada do anterior. O método de classe pode acessar .b==2apenas, via cls.b. O método normal pode acessar os dois: .b==4via self.be .b==2via self.__class__.b.

Poderíamos seguir o estilo KISS (simplifique, seja estúpido): Não use métodos estáticos e métodos de classe, não use classes sem instanciar, acesse apenas os atributos do objeto self.attribute(s). Existem idiomas em que o OOP é implementado dessa maneira e acho que não é uma má idéia. :)


Uma coisa importante a mais nos métodos de classe: Se você modificar um atributo no método de classe, todos os objetos existentes dessa classe que não definem explicitamente esse atributo terão o valor modificado.
mirek 2/03

-4

Uma rápida invasão de métodos idênticos no iPython revela que @staticmethodgera ganhos marginais de desempenho (em nanossegundos), mas, caso contrário, parece não ter nenhuma função. Além disso, quaisquer ganhos de desempenho provavelmente serão eliminados pelo trabalho adicional de processar o método staticmethod()durante a compilação (o que ocorre antes de qualquer execução de código quando você executa um script).

Por uma questão de legibilidade do código, eu evitaria, a @staticmethodmenos que seu método fosse usado para muito trabalho, onde os nanossegundos são importantes.


7
"Caso contrário, parece não ter nenhuma função": não é estritamente verdadeiro. Veja a discussão acima.
perfil completo de Keith Pinson
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.