2.21. Métodos y atributos

Un método es una función definida dentro de una clase. El primer parámetro es la instancia sobre la que se llama al método; la convención es nombrarlo self.

2.21.1. Métodos de instancia

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())

Salida:

5.0

La llamada p.magnitude() pasa p como self automáticamente. Los cuerpos de los métodos leen y actualizan atributos a través de self de la misma forma que las funciones leen y actualizan nombres a través de los parámetros.

2.21.2. Atributos de instancia frente a atributos de clase

Los atributos asignados a self (normalmente dentro de __init__) pertenecen a esa única instancia:

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

a.x y b.x son almacenamientos distintos; mutar uno no afecta al otro.

Los nombres asignados en el cuerpo de la clase, fuera de cualquier método, son compartidos por todas las instancias de la clase:

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
Un bloque de clase en la parte superior que contiene los métodos compartidos y el atributo de clase "kind"; debajo, dos cajas de instancia que contienen cada una su propio atributo "value", con flechas que van de cada instancia hacia arriba a la clase.

Los atributos de clase residen en la propia clase y son compartidos. Los atributos de instancia residen en cada instancia.

A un atributo de clase se accede de la misma manera que a uno de instancia (a.kind); Python busca primero en la instancia y luego en la clase. Asignar a a.kind crearía un nuevo atributo de instancia que oculta el de la clase, dejando b.kind intacto.

Usa atributos de clase para constantes y valores por defecto que todas las instancias deban ver de la misma forma. Usa atributos de instancia para el estado del que cada objeto deba tener su propia copia.

2.21.3. __str__ y __repr__

Dos métodos especiales controlan cómo se imprime una instancia. __str__ devuelve la cadena «amigable» que usa print(); __repr__ devuelve la cadena «de desarrollador» que se usa en el REPL y dentro de las visualizaciones de contenedores:

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

Salida:

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

Si solo se define __repr__, print() recurre a él, de modo que un único __repr__ bien escrito suele bastar. Define también __str__ solo cuando la forma amigable deba verse distinta de la forma de desarrollador. Procura que el __repr__ se parezca a la llamada que recrearía el objeto; depurar resulta muchísimo más fácil cuando los valores impresos no son ambiguos.