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
class C:
a = []
def foo(self):
pass
C # this is the class object
C.a # is a list object (class property object)
C.foo # is a function object (class property object)
c = C()
c # this is the class instance
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,
>>> C.__dict__['foo']
<function foo at 0x17d05b0>
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
def __get__(self, instance, owner)
def __set__(self, instance, value)
def __delete__(self, instance)
por exemplo
>>> C.__dict__['foo'].__get__(c, C)
Onde
self
é o CPO (pode ser uma instância de list, str, function etc) e é fornecido pelo tempo de execução
instance
é a instância da classe em que esse CPO é definido (o objeto 'c' acima) e precisa ser explicitamente fornecido por nós
owner
é 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ós
Nem todos os CPOs são descritores. Por exemplo
>>> C.__dict__['foo'].__get__(None, C)
<function C.foo at 0x10a72f510>
>>> C.__dict__['a'].__get__(None, C)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__get__'
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
class C(object):
@staticmethod
def foo():
pass
A ausência de contexto no novo CPO empacotado foo
não gera um erro e pode ser verificada da seguinte forma:
>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>
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.