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__ 看起来像是能重新创建该对象的那个调用;当打印出的值毫不含糊时,调试会变得轻松得多。