2.21. 方法與屬性

方法 是定義在類別內部的函式。它的第一個參數是該方法被呼叫於其上的實例;慣例上將它命名為 self

2.21.1. 實例方法

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

輸出:

5.0

呼叫 p.magnitude() 會自動將 p 作為 self 傳入。方法主體透過 self 讀取並更新屬性,方式與函式透過參數讀取並更新名稱相同。

2.21.2. 實例屬性與類別屬性

指派給 self 的屬性 -- 通常在 __init__ 內部 -- 屬於那一個實例:

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

a.xb.x 是不同的儲存空間;變動其中一個並不會影響另一個。

在類別主體中、任何方法 之外 指派的名稱,則由該類別的每個實例共用:

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
頂端有一個類別區塊,持有共用的方法與類別屬性「kind」; 其下有兩個實例方框, 各自持有自己的「value」屬性,並有箭頭 從每個實例向上指向類別。

類別屬性存在於類別本身且為共用。實例屬性則存在於各個實例上。

類別屬性的存取方式與實例屬性相同(a.kind);Python 會先在實例上尋找,再到類別上尋找。指派給 a.kind 會建立一個新的 實例 屬性,遮蔽掉類別的那個,而 b.kind 則不受影響。

對於每個實例都應以相同方式看待的常數與預設值,使用類別屬性。對於每個物件都應擁有自己一份副本的狀態,使用實例屬性。

2.21.3. __str__ 與 __repr__

有兩個特殊方法控制著實例如何列印。__str__ 回傳由 print() 使用的「友善」字串;__repr__ 則回傳在 REPL 中以及容器顯示內部使用的「開發者」字串:

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

輸出:

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

如果只定義了 __repr__print() 會退而使用它 -- 因此單單一個寫得好的 __repr__ 通常就夠了。唯有當友善形式應與開發者形式看起來不同時,才一併定義 __str__。盡量讓 __repr__ 看起來像是能重新建立該物件的呼叫;當列印出的值明確無歧義時,除錯會變得輕鬆許多。