Qual é a diferença entre uma função decorada com @staticmethod
e uma decorada com @classmethod
?
python
oop
methods
python-decorators
Daryl Spitzer
fonte
fonte
Respostas:
Talvez um pouco de exemplo de código vai ajudar: Observe a diferença nas assinaturas de chamadas de
foo
,class_foo
estatic_foo
: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.Com os métodos de classe, a classe da instância do objeto é passada implicitamente como o primeiro argumento em vez de
self
.Você também pode ligar
class_foo
usando 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, masA.class_foo(1)
funciona muito bem: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) nemcls
(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: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.foo
você não apenas obtém a função, você obtém uma versão "parcialmente aplicada" da função com a instância do objetoa
vinculada como o primeiro argumento da função.foo
espera 2 argumentos, enquantoa.foo
espera apenas 1 argumento.a
está vinculado afoo
. Isso é o que significa o termo "vinculado" abaixo:Com
a.class_foo
,a
não está vinculado aclass_foo
, mas a classeA
está vinculadaclass_foo
.Aqui, com um método static, mesmo que seja um método,
a.static_foo
apenas retorna uma boa função ole sem argumentos vinculados.static_foo
espera 1 argumento e tambéma.static_foo
espera 1 argumento.E é claro que a mesma coisa acontece quando você liga
static_foo
para a turmaA
.fonte
@staticmethod
pode 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.@staticmethod
- você pode usá-lo para remover detritos. Estou implementando uma linguagem de programação em Python - as funções definidas pela biblioteca usam umexecute
mé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.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:fonte
Basicamente,
@classmethod
um método cujo primeiro argumento é a classe da qual é chamado (e não a instância da classe)@staticmethod
não possui argumentos implícitos.fonte
Documentos oficiais do python:
@classmethod
@staticmethod
fonte
Aqui está um pequeno artigo sobre esta questão
fonte
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.
fonte
cls._counter
ainda seriacls._counter
mesmo se o código fosse colocado em uma classe diferente ou se o nome da classe fosse alterado.Apple._counter
é específico para aApple
classe; para uma classe diferente ou quando o nome da classe for alterado, você precisará alterar a classe referenciada.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:
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,
join
nessa 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']
: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.join
método à':'
instância: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:
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.maketrans
movido dostring
módulo no Python 3. Torna uma tabela de conversão adequada para consumo porstr.translate
. Parece um pouco bobo quando usado a partir de uma instância de uma string, como demonstrado abaixo, mas importar a função dostring
módulo é um pouco desajeitado e é bom poder chamá-lo da classe, como emstr.maketrans
No python 2, você precisa importar esta função do módulo de string cada vez menos útil:
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.)Quando ditamos uma subclasse, podemos usar o mesmo construtor, que cria uma instância da subclasse.
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
classmethod
estaticmethod
.fonte
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á issoVamos considerar que temos uma
Person
classe que recebe dois argumentosfirst_name
elast_name
e cria a instânciaPerson
.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).
No entanto, você pode conseguir a mesma coisa usando
@classmethod
o mencionado abaixoMé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_name
não exceda 20 caracteres, basta fazer isso.e você pode simplesmente chamar usando
class name
fonte
def __init__(self, first_name, last_name="")
vez do método de classeget_person
. Também o resultado será exatamente o mesmo neste caso.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.
fonte
@ 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:
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_for
mé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.fonte
Métodos estáticos:
Benefícios dos métodos estáticos:
Mais conveniente para importar versus funções no nível do módulo, pois cada método não precisa ser especialmente importado
Métodos de classe:
Eles são criados com a função incorporada do método classmethod.
fonte
@staticmethod
apenas 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:Por uma questão de fato,
classmethod
possui 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:fonte
c = C(); c.foo()
gera AttributeError, você teria que fazertype(c).foo()
. Isso também pode ser considerado um recurso - não consigo pensar no motivo pelo qual você gostaria.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.
@staticmethod
A 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.@classmethod
A 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@classmethod
função deve sempre ser cls (classe).fonte
Somente o primeiro argumento difere :
Em mais detalhes...
método normal
Quando o método de um objeto é chamado, ele recebe automaticamente um argumento extra
self
como seu primeiro argumento. Ou seja, métododeve ser chamado com 2 argumentos.
self
é passado automaticamente e é o próprio objeto .método de classe
Quando o método é decorado
o argumento fornecido automaticamente não é
self
, mas a classe deself
.método estático
Quando o método é decorado
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.staticmethod
nã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, comoMath
métodos estáticos de classe do Java )fonte
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.
fonte
Outra consideração em relação ao staticmethod vs classmethod surge com herança. Digamos que você tenha a seguinte turma:
E você deseja substituir
bar()
em uma classe filho: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 queFoo2
tenha um método chamadomagic()
que você deseja usar naFoo2
implementação debar()
:A solução aqui seria chamar
Foo2.magic()
embar()
, mas então você está se repetindo (se o nome deFoo2
mudanças, você tem que lembrar de atualizar essebar()
método).Para mim, isso é uma pequena violação do princípio de abrir / fechar , pois uma decisão tomada
Foo
está afetando sua capacidade de refatorar código comum em uma classe derivada (ou seja, é menos aberta à extensão). Sebar()
fosse umclassmethod
, ficaríamos bem:Dá:
In Foo2 MAGIC
fonte
Vou tentar explicar a diferença básica usando um exemplo.
1 - podemos chamar diretamente métodos estáticos e de classe sem inicializar
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.
fonte
@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
fonte
Você pode considerar a diferença entre:
e
Isso mudou entre python2 e python3:
python2:
python3:
Portanto, o uso
@staticmethod
de 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.
fonte
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!
fonte
Minha contribuição demonstra a diferença entre
@classmethod
,@staticmethod
e métodos de instância, incluindo como uma instância pode indiretamente chamar um@staticmethod
. Mas, em vez de chamar indiretamente um@staticmethod
de 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.fonte
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.
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.
fonte
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.
fonte
Eu acho que dar uma versão puramente Python
staticmethod
eclassmethod
ajudaria 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).
fonte
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 parab=4
ele. O método estático não pode acessar nada do anterior. O método de classe pode acessar.b==2
apenas, viacls.b
. O método normal pode acessar os dois:.b==4
viaself.b
e.b==2
viaself.__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. :)fonte
Uma rápida invasão de métodos idênticos no iPython revela que
@staticmethod
gera 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étodostaticmethod()
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
@staticmethod
menos que seu método fosse usado para muito trabalho, onde os nanossegundos são importantes.fonte