2.21. Métodos e atributos

Um método é uma função definida dentro de uma classe. O primeiro parâmetro é a instância sobre a qual o método é chamado; a convenção é nomeá-lo self.

2.21.1. Métodos de instância

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def magnitude(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5

p = Point(3, 4)
print(p.magnitude())

Saída:

5.0

A chamada p.magnitude() passa p como self automaticamente. Os corpos dos métodos leem e atualizam atributos por meio de self da mesma forma que as funções leem e atualizam nomes por meio de parâmetros.

2.21.2. Atributos de instância vs. de classe

Atributos atribuídos a self – normalmente dentro de __init__ – pertencem àquela única instância:

a = Point(1, 2)
b = Point(10, 20)
a.x = 99
print(a.x, b.x)           # 99 10

a.x e b.x são armazenamentos diferentes; modificar um não afeta o outro.

Nomes atribuídos no corpo da classe, fora de qualquer método, são compartilhados por todas as instâncias da classe:

class Counter:
    kind = "tally"        # class attribute -- shared

    def __init__(self):
        self.value = 0    # instance attribute -- per instance

a = Counter()
b = Counter()
print(a.kind, b.kind)     # tally tally
Um bloco de classe no topo contendo os métodos compartilhados e o atributo de classe "kind"; abaixo dele, duas caixas de instância, cada uma contendo seu próprio atributo "value", com setas de cada instância apontando para a classe.

Atributos de classe vivem na própria classe e são compartilhados. Atributos de instância vivem em cada instância.

Um atributo de classe é alcançado da mesma forma que um de instância (a.kind); o Python procura primeiro na instância e depois na classe. Atribuir a a.kind criaria um novo atributo de instância que oculta o da classe, deixando b.kind intacto.

Use atributos de classe para constantes e valores padrão que toda instância deve ver da mesma maneira. Use atributos de instância para o estado do qual cada objeto deve ter sua própria cópia.

2.21.3. __str__ e __repr__

Dois métodos especiais controlam como uma instância é impressa. __str__ retorna a string “amigável” usada por print(); __repr__ retorna a string “de desenvolvedor” usada no REPL e dentro de exibições de contêineres:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return "(" + str(self.x) + ", " + str(self.y) + ")"

    def __repr__(self):
        return "Point(" + str(self.x) + ", " + str(self.y) + ")"

p = Point(3, 4)
print(p)            # uses __str__
print([p, p])       # uses __repr__ inside the list

Saída:

(3, 4)
[Point(3, 4), Point(3, 4)]

Se apenas __repr__ estiver definido, print() recorre a ele – então um único __repr__ bem escrito costuma ser suficiente. Defina __str__ também apenas quando a forma amigável deva parecer diferente da forma de desenvolvedor. Busque um __repr__ que se pareça com a chamada que recriaria o objeto; a depuração fica drasticamente mais fácil quando os valores impressos são inequívocos.