2.21. Metodi e attributi

Un metodo è una funzione definita all’interno di una classe. Il primo parametro è l’istanza su cui il metodo viene chiamato; per convenzione lo si chiama self.

2.21.1. Metodi di istanza

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

Output:

5.0

La chiamata p.magnitude() passa automaticamente p come self. I corpi dei metodi leggono e aggiornano gli attributi tramite self allo stesso modo in cui le funzioni leggono e aggiornano i nomi tramite i parametri.

2.21.2. Attributi di istanza vs di classe

Gli attributi assegnati a self – tipicamente all’interno di __init__ – appartengono a quella singola istanza:

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

a.x e b.x sono archiviazioni diverse; modificare uno non influisce sull’altro.

I nomi assegnati nel corpo della classe, al di fuori di qualsiasi metodo, sono condivisi da ogni istanza della 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
Un blocco di classe in alto che contiene i metodi condivisi e l' attributo di classe «kind»; sotto di esso, due riquadri di istanza che contengono ciascuno il proprio attributo «value», con frecce da ogni istanza che salgono verso la classe.

Gli attributi di classe risiedono sulla classe stessa e sono condivisi. Gli attributi di istanza risiedono su ciascuna istanza.

Un attributo di classe si raggiunge nello stesso modo di uno di istanza (a.kind); Python cerca prima sull’istanza, poi sulla classe. Assegnare a a.kind creerebbe un nuovo attributo di istanza che oscura quello di classe, lasciando b.kind intatto.

Usa gli attributi di classe per costanti e valori predefiniti che ogni istanza dovrebbe vedere allo stesso modo. Usa gli attributi di istanza per lo stato di cui ogni oggetto dovrebbe possedere la propria copia.

2.21.3. __str__ e __repr__

Due metodi speciali controllano come un’istanza viene stampata. __str__ restituisce la stringa «amichevole» usata da print(); __repr__ restituisce la stringa «per sviluppatori» usata al REPL e all’interno delle visualizzazioni dei contenitori:

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

Output:

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

Se è definito solo __repr__, print() ricade su di esso – quindi un singolo __repr__ ben scritto è di solito sufficiente. Definisci anche __str__ solo quando la forma amichevole dovrebbe apparire diversa da quella per sviluppatori. Punta a un __repr__ che assomigli alla chiamata che ricreerebbe l’oggetto; il debug diventa molto più facile quando i valori stampati sono inequivocabili.