O que são "métodos de classe" e "métodos de instância", em Python?

37

Houve uma discussão no chat relacionada a uma pergunta (a pergunta em si é irrelevante para essa), que revelou que talvez eu não conheça Python.

Em minha opinião, embora a terminologia seja diferente entre os idiomas, geralmente podemos categorizar funções como:

  • funções [livres]
  • métodos estáticos / funções de membro estáticas
  • métodos não estáticos / funções membro não estáticas

Aparentemente, no Python, existe outro tipo de função que não se encaixa nas categorias acima, uma que é um método, mas "não conhece sua classe".

O que são "métodos de classe" e "métodos de instância", em Python?

Corridas de leveza com Monica
fonte
11
Se alguém estiver interessado na discussão sobre o bate-papo, comece aqui: chat.stackexchange.com/transcript/message/26479002#26479002
yannis
11
A IMO fornece uma resposta melhor por outro site da stackexchange: stackoverflow.com/questions/136097/…
Trevor Boyd Smith
Para sua informação, o link é fornecido na parte inferior do aceito ... mas acho que muitas pessoas vão perder o link ou nunca chegar a ele, então copiei aqui.
Trevor Boyd Smith

Respostas:

49

A resposta curta

  • um método de instância conhece sua instância (e a partir disso, sua classe)
  • um método de classe conhece sua classe
  • um método estático não conhece sua classe ou instância

A resposta longa

Métodos de classe

Um método de classe é aquele que pertence à classe como um todo. Não requer uma instância. Em vez disso, a classe será enviada automaticamente como o primeiro argumento. Um método de classe é declarado com o @classmethoddecorador.

Por exemplo:

class Foo(object):
    @classmethod
    def hello(cls):
        print("hello from %s" % cls.__name__)
Foo.hello()
-> "Hello from Foo"
Foo().hello()
-> "Hello from Foo"

Métodos de instância

Por outro lado, um método de instância requer uma instância para chamá-lo e não requer decorador. Esse é de longe o tipo mais comum de método.

class Foo(object):
    def hello(self):
        print("hello from %s" % self.__class__.__name__)
Foo.hello()
-> TypeError: hello() missing 1 required positional argument: 'self'

(nota: o acima é com python3; com python2 você receberá um erro ligeiramente diferente)

Métodos estáticos

Um método estático é semelhante a um método de classe, mas não obtém o objeto de classe como um parâmetro automático. É criado usando o @staticmethoddecorador.

class Foo(object):
    @staticmethod
    def hello(cls):
        print("hello from %s" % cls.__name__)

Foo.hello()
-> TypeError: hello() missing 1 required positional argument: 'cls'

Links de documentação

Aqui estão os links para a documentação python3 relevante:

A documentação do modelo de dados tem a dizer sobre a diferença entre métodos de classe e métodos estáticos:

Objetos de método estático Os objetos de método estático fornecem uma maneira de derrotar a transformação de objetos de função em objetos de método descritos acima. Um objeto de método estático é um invólucro em torno de qualquer outro objeto, geralmente um objeto de método definido pelo usuário. Quando um objeto de método estático é recuperado de uma classe ou instância de classe, o objeto realmente retornado é o objeto agrupado, que não está sujeito a nenhuma transformação adicional. Os objetos do método estático não são chamados, embora os objetos que eles envolvem geralmente sejam. Os objetos de método estático são criados pelo construtor staticmethod () interno.

Objetos de método de classe Um objeto de método de classe, como um objeto de método estático, é um invólucro em torno de outro objeto que altera a maneira pela qual esse objeto é recuperado de classes e instâncias de classe. O comportamento dos objetos de método de classe após essa recuperação é descrito acima, em "Métodos definidos pelo usuário". Os objetos de método de classe são criados pelo construtor classmethod () interno.

Perguntas relacionadas

Bryan Oakley
fonte
4
@LightnessRacesinOrbit: Eu não acho que métodos estáticos sejam usados ​​particularmente frequentemente em python. Usando meu sistema como uma amostra aleatória, quando procuro em todos os pacotes que instalei, apenas cerca de 1% de todos os métodos são estáticos (o que, francamente, era mais do que eu esperava).
Bryan Oakley
11
"Métodos estáticos" são geralmente um cheiro de código e são desencorajados, por exemplo, pelo guia de estilo do Gogole Python.
9000
3
Eu acho que a resposta seria melhor se chamasse atenção para as subclasses, que é onde os métodos de classe obtêm alguma utilidade.
Winston Ewert
11
@ user2357112: Simplesmente contei todas as instâncias de "^ def(", depois contei todas as instâncias de @staticmethod. Muito pouco científico, mas provavelmente estava dentro do estádio.
Bryan Oakley
2
@ Kashyap: o TL; DR se aplica ao primeiro parágrafo. Eu acho que funciona melhor no topo do que no fundo.
Bryan Oakley
8

O que são "métodos de classe" e "métodos de instância", em Python?

  • Um "método de instância" usa as informações contidas na instância para descobrir qual valor retornar (ou qual efeito colateral fazer). Estes são muito comuns.

  • Um "método de classe" usa informações sobre a classe (e não uma instância dessa classe) para afetar o que faz (elas geralmente são usadas para criar novas instâncias como construtores alternativos e, portanto, não são incrivelmente comuns).

  • Um "método estático" não usa nenhuma informação sobre a classe ou instância para calcular o que faz. Geralmente é apenas na classe por conveniência. (Como tal, também não são muito comuns.)

Uma função de X

Lembre-se da aula de matemática, "y é uma função de x f(x),?" Vamos aplicar isso no código:

y = function(x)

O que está implícito acima é que, uma vez que xpode mudar, ypode mudar quando xmuda. É isso que queremos dizer quando dizemos que " yé uma função dex "

O que yserá quando zé 1? 2? 'FooBarBaz'?

ynão é uma função de z, portanto zpode ser qualquer coisa e não afetar o resultado da função, assumindo que nossa functioné uma função pura. (Se acessarz como uma variável global, não será uma função pura - é isso que significa pureza funcional.)

Lembre-se do que foi dito acima ao ler as seguintes descrições:

Métodos de instância

Um método de instância é uma função que é uma função de uma instância . A função aceita implicitamente a instância como argumento e a instância é usada pela função para determinar a saída da função.

Um exemplo interno de um método de instância é str.lower:

>>> 'ABC'.lower()
'abc'

str.lower é chamado na instância da sequência e usa as informações contidas na instância para descobrir qual nova sequência retornar.

Métodos de classe:

Lembre-se, em Python, tudo é um objeto. Isso significa que a classe é um objeto e pode ser passada como argumento para uma função.

Um método de classe é uma função que é uma função da classe . Ele aceita a classe como argumento.

Um exemplo embutido é dict.fromkeys:

>>> dict.fromkeys('ABC')
{'C': None, 'B': None, 'A': None}

A função conhece implicitamente sua própria classe, a função usa a classe para afetar a saída da função e cria um novo dessa classe a partir do iterável. Um OrderedDict demonstra isso ao usar o mesmo método:

>>> from collections import OrderedDict
>>> OrderedDict.fromkeys('ABC')
OrderedDict([('A', None), ('B', None), ('C', None)])

O método da classe usa informações sobre a classe (e não uma instância dessa classe) para afetar qual tipo de classe retornar.

Métodos estáticos

Você mencionou um método que "não conhece sua classe" - este é um método estático no Python. É apenas anexado por conveniência ao objeto de classe. Opcionalmente, poderia ser uma função separada em outro módulo, mas sua assinatura de chamada seria a mesma.

Um método estático não é uma função da classe nem do objeto.

Um exemplo interno de um método estático é o str.maketrans do Python 3.

>>> str.maketrans('abc', 'bca')
{97: 98, 98: 99, 99: 97}

Dado alguns argumentos, ele cria um dicionário que não é função de sua classe.

É conveniente porque strestá sempre disponível no espaço de nomes global, para que você possa usá-lo facilmente com a função de conversão:

>>> 'abracadabra'.translate(str.maketrans('abc', 'bca'))
'bcrbabdbcrb'

No Python 2, você precisa acessá-lo no stringmódulo:

>>> 'abracadabra'.translate(str.maketrans('abc', 'bca'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'str' has no attribute 'maketrans'
>>> import string
>>> 'abracadabra'.translate(string.maketrans('abc', 'bca'))
'bcrbabdbcrb'

Exemplo

class AClass(object):
    """In Python, a class may have several types of methods: 
    instance methods, class methods, and static methods
    """

    def an_instance_method(self, x, y, z=None):
        """this is a function of the instance of the object
        self is the object's instance
        """
        return self.a_class_method(x, y)

    @classmethod
    def a_class_method(cls, x, y, z=None):
        """this is a function of the class of the object
        cls is the object's class
        """
        return cls.a_static_method(x, y, z=z)

    @staticmethod
    def a_static_method(x, y, z=None):
        """this is neither a function of the instance or class to 
        which it is attached
        """
        return x, y, z

Vamos instanciar:

>>> instance = AClass()

Agora a instância pode chamar todos os métodos:

>>> instance.an_instance_method('x', 'y')
('x', 'y', None)
>>> instance.a_static_method('x', 'y')
('x', 'y', None)
>>> instance.a_class_method('x', 'y')
('x', 'y', None)

Mas a classe normalmente não se destina a chamar o método de instância, embora se espere chamar os outros:

>>> AClass.a_class_method('x', 'y')
('x', 'y', None)
>>> AClass.a_static_method('x', 'y')
('x', 'y', None)
>>> AClass.an_instance_method('x', 'y')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: an_instance_method() missing 1 required positional argument: 'y'

Você precisaria passar explicitamente a instância para chamar o método da instância:

>>> AClass.an_instance_method(instance, 'x', 'y')
('x', 'y', None)
Aaron Hall
fonte
"este é um método estático em Python. Ele é apenas anexado por conveniência ao objeto de classe. Poderia ser opcionalmente uma função separada em outro módulo, mas sua assinatura de chamada seria a mesma." Parece mais confuso do que conveniente. Por que você anexaria uma função a uma classe se seu corpo não tiver noção dessa classe?
Lightness Races com Monica
@LightnessRacesinOrbit que eu elaborei em cada ponto
Aaron Hall
Pessoalmente, a maneira como sempre vi isso é que um método de instância é uma operação em uma instância específica, um método de classe é um tipo de construtor (por conveniência, nada parecido MyClass(1,2,3)com isso, em vez disso MyClass.get_special(1,2,3)) e um método estático como apenas sendo uma função normal que poderia ficar sozinha de qualquer maneira.
Zizouz212
Sim, os métodos de classe geralmente são usados ​​para construtores alternativos (veja meu exemplo dict.fromkeys acima, veja também o código-fonte pandas.DataFrame), mas essa não é uma característica definidora. Às vezes, basta acessar os dados armazenados no nível da classe a partir de um método, sem acessar a instância. Se você está chamando type (self) (ou pior, self .__ class__) e não usa self diretamente em um método de instância, esse é um bom sinal de que você deve considerá-lo um método de classe. Como os métodos de classe não exigem instâncias, os torna mais fáceis de unir.
Aaron Hall
4
  • Um "método de instância" é um método que recebe o objeto de instância como seu primeiro parâmetro, que por convenção é chamado self. Esse é o padrão.

  • Um "método estático" é um método sem o selfparâmetro inicial . Isso é implementado com o decorador @staticmethod .

  • Um "método de classe" é um método em que o parâmetro inicial não é um objeto de instância, mas o objeto de classe ( consulte esta parte dos documentos do Python sobre o que é um "objeto de classe"), que por convenção é chamado cls. Isso é implementado com o decorador @classmethod .

O uso típico do termo "método estático" em C / C ++ / etc se aplica aos métodos "estático" e "classe" do Python, pois esses dois tipos de métodos não têm referência à instância. Em C / C ++ / etc, os métodos normalmente têm acesso implícito a todos os membros / métodos que não são de instância da classe, e é provavelmente por isso que essa distinção existe apenas em linguagens como Python / Ruby / etc.

Ixrec
fonte
Um método estático no C / C ++ tem uma referência ao objeto de classe? Ou apenas "membros" da classe? Eles podem criar novos membros da classe? :)
Aaron Hall
3
@AaronHall No C / C ++, não existe um objeto de classe e você não precisa de uma referência a ele para chamar métodos estáticos. Você acabou de escrever Class::staticMethod()e é isso (contanto que você não seja proibido de chamá-lo porque staticMethod()é privado ou algo assim).
Ixrec 29/12/15