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
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.