Qual é a diferença entre os seguintes métodos de classe?
Será que um é estático e o outro não?
class Test(object):
def method_one(self):
print "Called method_one"
def method_two():
print "Called method_two"
a_test = Test()
a_test.method_one()
a_test.method_two()
@classmethod
à definição. O primeiro parâmetro deve ser chamado emcls
vez deself
e receberá o objeto de classe em vez de uma instância de sua classe:Test.method_three()
ea_test.method_three()
é equivalente.self
argumento? Existe um forte caso de uso para isso?Respostas:
No Python, há uma distinção entre métodos vinculado e não vinculado .
Basicamente, uma chamada para uma função membro (como
method_one
), uma função vinculadaé traduzido para
ou seja, uma chamada para um método não vinculado. Por esse motivo, uma chamada para sua versão
method_two
falhará com umTypeError
Você pode alterar o comportamento de um método usando um decorador
O decorador diz à metaclasse padrão interna
type
(a classe de uma classe, veja esta pergunta ) para não criar métodos vinculadosmethod_two
.Agora, você pode invocar o método estático em uma instância ou diretamente na classe:
fonte
Os métodos em Python são uma coisa muito, muito simples, depois que você entende o básico do sistema descritor. Imagine a seguinte classe:
Agora vamos dar uma olhada nessa classe no shell:
Como você pode ver se você acessa o
foo
atributo na classe, obtém um método não acoplado, mas dentro do armazenamento da classe (o dict) existe uma função. Por que isso? A razão para isso é que a classe da sua classe implementa uma__getattribute__
que resolve descritores. Parece complexo, mas não é.C.foo
é aproximadamente equivalente a esse código nesse caso especial:Isso ocorre porque as funções têm um
__get__
método que as torna descritores. Se você tem uma instância de uma classe, é quase a mesma, apenas essaNone
é a instância da classe:Agora, por que o Python faz isso? Como o objeto de método liga o primeiro parâmetro de uma função à instância da classe. É daí que vem o eu. Agora, às vezes, você não quer que sua classe torne uma função um método, é aí que
staticmethod
entra em jogo:O
staticmethod
decorador agrupa sua classe e implementa um manequim__get__
que retorna a função agrupada como função e não como método:Espero que isso explique.
fonte
staticmethod
decorador agrupa sua classe (...) Essa frase é um pouco enganadora, pois a classe que está sendo agrupada é a classe do métodofoo
e não a classe na qualfoo
está definida.Quando você chama um membro da classe, o Python automaticamente usa uma referência ao objeto como o primeiro parâmetro. A variável
self
realmente não significa nada, é apenas uma convenção de codificação. Você poderia ligargargaloo
se quisesse. Dito isso, a chamada paramethod_two
geraria umTypeError
, porque o Python está automaticamente tentando passar um parâmetro (a referência ao seu objeto pai) para um método que foi definido como sem parâmetros.Para realmente fazê-lo funcionar, você pode anexar isso à sua definição de classe:
ou você pode usar o
@staticmethod
decorador de funções .fonte
fonte
Class.instance_method() # The class cannot use an instance method
pode usar. Apenas passe a instância manualmente:Class.instance_method(a)
TyeError
linha.a.class_method()
, parece quea.c
foi atualizado para1
, então a chamada aClass.class_method()
atualizou aClass.c
variável para2
. No entanto, quando você atribuiua.c=5
, por queClass.c
não foi atualizado5
?method_two não funcionará porque você está definindo uma função de membro, mas não está dizendo a ele do que a função é membro. Se você executar a última linha, receberá:
Se você estiver definindo funções-membro para uma classe, o primeiro argumento sempre deve ser 'próprio'.
fonte
Explicação precisa de Armin Ronacher acima, expandindo suas respostas para que iniciantes como eu entendam bem:
A diferença nos métodos definidos em uma classe, seja estática ou método de instância (ainda existe outro tipo - método de classe - não discutido aqui, para ignorá-lo), reside no fato de eles estarem de alguma forma vinculados à instância de classe ou não. Por exemplo, diga se o método recebe uma referência à instância da classe durante o tempo de execução
A
__dict__
propriedade de dicionário do objeto de classe mantém a referência a todas as propriedades e métodos de um objeto de classe e, portanto,o método foo está acessível como acima. Um ponto importante a ser observado aqui é que tudo em python é um objeto e, portanto, as referências no dicionário acima estão apontando para outros objetos. Deixe-me chamá-los de objetos de propriedade de classe - ou como CPO no escopo da minha resposta, por questões de brevidade.
Se um CPO é um descritor, o interpretador python chama o
__get__()
método do CPO para acessar o valor que ele contém.Para determinar se um CPO é um descritor, o interpretador python verifica se implementa o protocolo do descritor. Implementar o protocolo descritor é implementar 3 métodos
por exemplo
Onde
self
é o CPO (pode ser uma instância de list, str, function etc) e é fornecido pelo tempo de execuçãoinstance
é a instância da classe em que esse CPO é definido (o objeto 'c' acima) e precisa ser explicitamente fornecido por nósowner
é a classe em que esse CPO é definido (o objeto de classe 'C' acima) e precisa ser fornecido por nós. No entanto, isso ocorre porque estamos chamando o CPO. quando o chamamos na instância, não precisamos fornecer isso, pois o tempo de execução pode fornecer a instância ou sua classe (polimorfismo)value
é o valor pretendido para o CPO e precisa ser fornecido por nósNem todos os CPOs são descritores. Por exemplo
Isso ocorre porque a classe list não implementa o protocolo do descritor.
Portanto, o argumento self in
c.foo(self)
é necessário porque a assinatura do método é realmente essaC.__dict__['foo'].__get__(c, C)
(como explicado acima, C não é necessária, pois pode ser descoberta ou polimorfa). E é também por isso que você obtém um TypeError se não passar esse argumento de instância necessário.Se você notar que o método ainda é referenciado por meio da classe Objeto C e a ligação com a instância da classe é obtida através da passagem de um contexto na forma do objeto de instância para essa função.
Isso é impressionante, pois se você optou por não manter nenhum contexto ou ligação à instância, tudo o que era necessário era escrever uma classe para agrupar o CPO do descritor e substituir seu
__get__()
método para não exigir contexto. Essa nova classe é o que chamamos de decorador e é aplicada pela palavra-chave@staticmethod
A ausência de contexto no novo CPO empacotado
foo
não gera um erro e pode ser verificada da seguinte forma:O caso de uso de um método estático é mais um espaço para nome e manutenção de código (removendo-o de uma classe e disponibilizando-o em todo o módulo, etc.).
Talvez seja melhor escrever métodos estáticos do que métodos de instância sempre que possível, a menos que você precise contextualizar os métodos (como acessar variáveis de instância, variáveis de classe etc.). Um dos motivos é facilitar a coleta de lixo, não mantendo referências indesejadas a objetos.
fonte
isso é um erro.
primeiro de tudo, a primeira linha deve ser assim (tenha cuidado com as maiúsculas)
Sempre que você chama um método de uma classe, ele aparece como o primeiro argumento (daí o nome self) e method_two gera esse erro
fonte
O segundo não funcionará porque, quando você o chama dessa maneira, internamente, o python tenta chamá-lo com a instância a_test como o primeiro argumento, mas seu method_two não aceita nenhum argumento, portanto, não funcionará, você obterá um tempo de execução erro. Se você deseja o equivalente a um método estático, pode usar um método de classe. Há muito menos necessidade de métodos de classe em Python do que métodos estáticos em linguagens como Java ou C #. Na maioria das vezes, a melhor solução é usar um método no módulo, fora de uma definição de classe, aqueles que funcionam com mais eficiência do que os métodos de classe.
fonte
Class Test(object): @staticmethod def method_two(): print(“called method_two”)
um caso de uso, pensei em quando você deseja que a função faça parte de uma classe, mas não deseja que o usuário acesse a função diretamente. Portanto,method_two
pode ser chamado por outras funções dentro daTest
instância, mas não pode ser chamado usandoa_test.method_two()
. Se eu usardef method_two()
, isso funcionará para este caso de uso? Ou existe uma maneira melhor de modificar a definição da função, para que ela funcione conforme o pretendido no caso de uso acima?A chamada para method_two lançará uma exceção por não aceitar o auto parâmetro que o tempo de execução do Python passará automaticamente.
Se você deseja criar um método estático em uma classe Python, decore-o com o
staticmethod decorator
.fonte
Leia todos os documentos da Primeira classe do Guido. Tudo explicado claramente como nascem os métodos Unbound, Bound.
fonte
A definição de
method_two
é inválida. Quando você ligamethod_two
, você recebeTypeError: method_two() takes 0 positional arguments but 1 was given
do intérprete.Um método de instância é uma função limitada quando você o chama assim
a_test.method_two()
. Ele aceita automaticamenteself
, que aponta para uma instância deTest
, como seu primeiro parâmetro. Por meio doself
parâmetro, um método de instância pode acessar livremente atributos e modificá-los no mesmo objeto.fonte
Métodos não vinculados
Métodos não ligados são métodos que ainda não estão vinculados a nenhuma instância de classe específica.
Métodos vinculados
Métodos vinculados são aqueles que estão vinculados a uma instância específica de uma classe.
Como está documentado aqui , self pode se referir a coisas diferentes, dependendo da função ser vinculada, não vinculada ou estática.
Veja o seguinte exemplo:
Como não usamos a instância da classe -
obj
- na última chamada, podemos dizer que parece um método estático.Em caso afirmativo, qual é a diferença entre uma
MyClass.some_method(10)
chamada e uma chamada para uma função estática decorada com um@staticmethod
decorador?Ao usar o decorador, deixamos explicitamente claro que o método será usado sem criar uma instância para ele primeiro. Normalmente, não se espera que os métodos dos membros da classe sejam usados sem a instância e acessá-los pode causar possíveis erros, dependendo da estrutura do método.
Além disso, adicionando o
@staticmethod
decorador, estamos possibilitando ser alcançado também através de um objeto.Você não pode fazer o exemplo acima com os métodos da instância. Você pode sobreviver ao primeiro (como fizemos antes), mas o segundo será convertido em uma chamada não acoplada, o
MyClass.some_method(obj, 10)
que aumentará um,TypeError
já que o método de instância recebe um argumento e você, sem querer, tentou passar dois.Então, você pode dizer: "Se eu puder chamar métodos estáticos através de uma instância e uma classe,
MyClass.some_static_method
eMyClass().some_static_method
deve ser o mesmo método". Sim!fonte
Método vinculado = método de instância
Método não vinculado = método estático.
fonte