2.21. Metoder och attribut

En metod är en funktion definierad inuti en klass. Den första parametern är den instans som metoden anropas på; konventionen är att namnge den self.

2.21.1. Instansmetoder

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

Utmatning:

5.0

Anropet p.magnitude() skickar automatiskt p som self. Metodkroppar läser och uppdaterar attribut genom self på samma sätt som funktioner läser och uppdaterar namn genom parametrar.

2.21.2. Instans- kontra klassattribut

Attribut som tilldelas self – vanligtvis inuti __init__ – tillhör just den ena instansen:

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

a.x och b.x är olika lagring; att mutera den ena påverkar inte den andra.

Namn som tilldelas i klasskroppen, utanför någon metod, delas av varje instans av klassen:

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
Ett klassblock högst upp som håller delade metoder och klassattributet "kind"; under det två instanslådor som var och en håller sitt eget "value"-attribut, med pilar från varje instans upp till klassen.

Klassattribut bor på själva klassen och är delade. Instansattribut bor på varje instans.

Ett klassattribut nås på samma sätt som ett instansattribut (a.kind); Python tittar först på instansen, sedan på klassen. Att tilldela a.kind skulle skapa ett nytt instansattribut som skuggar klassattributet och lämnar b.kind orört.

Använd klassattribut för konstanter och standardvärden som varje instans ska se på samma sätt. Använd instansattribut för tillstånd som varje objekt ska äga sin egen kopia av.

2.21.3. __str__ och __repr__

Två specialmetoder styr hur en instans skrivs ut. __str__ returnerar den ”vänliga” sträng som används av print(); __repr__ returnerar den ”utvecklarvänliga” sträng som används vid REPL och inuti behållarvisningar:

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

Utmatning:

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

Om endast __repr__ är definierad faller print() tillbaka på den – så en enda välskriven __repr__ räcker oftast. Definiera __str__ också endast när den vänliga formen ska se annorlunda ut än utvecklarformen. Sikta på en __repr__ som ser ut som anropet som skulle återskapa objektet; felsökning blir dramatiskt enklare när utskrivna värden är otvetydiga.