2.21. Metode i atributi

Metoda je funkcija definirana unutar klase. Prvi parametar je instanca nad kojom je metoda pozvana; konvencija je nazvati ga self.

2.21.1. Metode instance

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

Izlaz:

5.0

Poziv p.magnitude() automatski prosljeđuje p kao self. Tijela metoda čitaju i ažuriraju atribute kroz self na isti način kako funkcije čitaju i ažuriraju imena kroz parametre.

2.21.2. Atributi instance naspram atributa klase

Atributi pridruženi objektu self – obično unutar __init__ – pripadaju toj jednoj instanci:

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

a.x i b.x su različita pohrana; mijenjanje jednog ne utječe na drugi.

Imena pridružena u tijelu klase, izvan bilo koje metode, dijeli svaka instanca klase:

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
Blok klase na vrhu drži dijeljene metode i atribut klase "kind"; ispod njega, dvije kutije instanci svaka drži vlastiti atribut "value", sa strelicama od svake instance prema gore do klase.

Atributi klase žive na samoj klasi i dijeljeni su. Atributi instance žive na svakoj instanci.

Atributu klase pristupa se na isti način kao atributu instance (a.kind); Python prvo gleda na instanci, zatim na klasi. Pridruživanje a.kind stvorilo bi novi atribut instance koji zasjenjuje onaj klase, ostavljajući b.kind netaknutim.

Koristi atribute klase za konstante i zadane vrijednosti koje bi svaka instanca trebala vidjeti na isti način. Koristi atribute instance za stanje čiju bi vlastitu kopiju svaki objekt trebao posjedovati.

2.21.3. __str__ i __repr__

Dvije posebne metode upravljaju time kako se instanca ispisuje. __str__ vraća „prijateljski” niz koji koristi print(); __repr__ vraća „razvojni” niz koji se koristi na REPL-u i unutar prikaza spremnika:

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

Izlaz:

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

Ako je definiran samo __repr__, print() se vraća na njega – pa je jedan dobro napisan __repr__ obično dovoljan. Definiraj i __str__ samo kad bi prijateljski oblik trebao izgledati drukčije od razvojnog. Cilj je __repr__ koji izgleda poput poziva koji bi ponovno stvorio objekt; otklanjanje grešaka postaje dramatično lakše kad su ispisane vrijednosti nedvosmislene.