2.21. Metodit ja attribuutit

Metodi on luokan sisällä määritelty funktio. Ensimmäinen parametri on instanssi, jolle metodia kutsutaan; sopimus on nimetä se self.

2.21.1. Instanssimetodit

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

Tuloste:

5.0

Kutsu p.magnitude() välittää p-olion automaattisesti self-parametrina. Metodirungot lukevat ja päivittävät attribuutteja self-olion kautta samalla tavalla kuin funktiot lukevat ja päivittävät nimiä parametrien kautta.

2.21.2. Instanssi- vs. luokka-attribuutit

self-olioon sijoitetut attribuutit – tyypillisesti __init__-metodin sisällä – kuuluvat tuolle yhdelle instanssille:

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

a.x ja b.x ovat eri tallennustilaa; toisen muuttaminen ei vaikuta toiseen.

Luokkarunkoon, minkä tahansa metodin ulkopuolelle, sijoitetut nimet ovat luokan jokaisen instanssin jakamia:

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
Luokkalohko ylhäällä, joka pitää jaetut metodit ja luokka-attribuutin "kind"; sen alla kaksi instanssilaatikkoa, jotka kumpikin pitävät oman "value"-attribuuttinsa, nuolet kummastakin instanssista ylös luokkaan.

Luokka-attribuutit elävät itse luokassa ja ovat jaettuja. Instanssiattribuutit elävät kussakin instanssissa.

Luokka-attribuuttiin päästään käsiksi samalla tavalla kuin instanssiattribuuttiin (a.kind); Python katsoo ensin instanssista, sitten luokasta. Sijoittaminen kohteeseen a.kind loisi uuden instanssiattribuutin, joka varjostaa luokka-attribuutin jättäen b.kind-arvon koskemattomaksi.

Käytä luokka-attribuutteja vakioille ja oletuksille, jotka jokaisen instanssin pitäisi nähdä samalla tavalla. Käytä instanssiattribuutteja tilalle, josta jokaisella oliolla pitäisi olla oma kopionsa.

2.21.3. __str__ ja __repr__

Kaksi erikoismetodia ohjaa sitä, miten instanssi tulostuu. __str__ palauttaa ”ystävällisen” merkkijonon, jota print() käyttää; __repr__ palauttaa ”kehittäjän” merkkijonon, jota käytetään REPL:ssä ja säiliöiden näytöissä:

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

Tuloste:

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

Jos vain __repr__ on määritelty, print() turvautuu siihen – joten yksi hyvin kirjoitettu __repr__ riittää yleensä. Määrittele myös __str__ vain, kun ystävällisen muodon pitäisi näyttää erilaiselta kuin kehittäjän muoto. Tähtää sellaiseen __repr__-muotoon, joka näyttää kutsulta, joka loisi olion uudelleen; virheiden jäljitys helpottuu dramaattisesti, kun tulostetut arvot ovat yksiselitteisiä.