类定义使用的语法很少。 先写 class 关键字,后面跟类名和冒号。 在缩进的 class body 内部使用 def 创建方法:

# simple_class.py

class Simple:
    def __init__(self, text):
        print("Inside the Simple constructor")
        self.s = text
    # Two methods:
    def show(self, msg=""):
        if msg:
            print(f"{msg}:", self.s)
        else:
            print(self.s)
    def show_twice(self):
        self.show()  # Calling another method
        self.show()
# demo_simple_class.py
from simple_class import Simple

x = Simple("Constructor argument")  # Create an object
#: Inside the Simple constructor
x.show()
#: Constructor argument
x.show("A message")
#: A message: Constructor argument
x.show_twice()
#: Constructor argument
#: Constructor argument

普通 method 需要当前对象的引用。 定义 method 时,必须显式把这个引用写成第一个参数。 Python 程序员传统上把这个引用命名为 self, 虽然你完全可以使用任何 identifier(但使用其他名称很可能会让人困惑)。 如果要引用对象的 attribute 或它的其他 method, 必须通过 self。

# forgot_self.py

class Oops:
    def show():  # Missing the self parameter
        print("never runs")

try:
    Oops().show()  # type: ignore
except TypeError as e:
    print(e)
#: Oops.show() takes 0 positional arguments but 1 was given

当你针对某个对象调用 method,例如 x.show() 时, Python 会自动传入对象引用。 错误消息中的 “1” 指的就是这个引用。 一个遗漏 self 的 method 无法接收它。 type checker 无需运行程序就能看到这个错误, 所以该调用带有 # type: ignore,表明错误是故意写出来的。

第一个 method __init__() 是初始化器(initializer)。 双下划线表明它是一个 dunder。 __new__() method 才是构造器(constructor),但你很少需要直接使用它 (24 · 单例(Singleton)展示了一个必须使用它的场景)。 大多数程序员仍然把 __init__() 称为 constructor, 因为它承担了其他 OOP 语言里 constructor 的工作。 本书也遵循这一习惯。

Python 会在对象创建期间自动调用 constructor。 在示例里,创建对象看起来就像调用一个与 class 同名的函数。

在 C++ 或 Java 中,你会在 class body 内部、method 外部声明 object-level field。 Python 不会以这种方式声明它们。 要创建 object attribute,需要在某个 method 内部通过 self 给它命名 (通常是在 constructor 中,但不一定)。 当 method 运行时,这条赋值会为该 attribute 建立存储。 如果像 C++/Java 那样直接在 class body 中给某个名称赋值, 这个名称反而会成为 class-level attribute (类似 C++/Java 中的 static field)。 09 · 类属性 会展示对这种共享存储赋值时会发生什么。 如果只有 annotation 而没有 value, 也就是外观上最像 C++ 或 Java field declaration 的形式,则两者都不会发生: 它不会存储任何东西,只会记录类型。 类属性 和把数据类作为类型 会使用这种形式。

display_object() 是一个在元编程中构建的小型 introspection helper, 它通过打印对象的 attribute 和 method 来展示对象的形状:

# display_simple.py
from display import display_object
from simple_class import Simple

x = Simple("Constructor argument")
#: Inside the Simple constructor
display_object(x)
#: [Attributes]
#:   • s = 'Constructor argument'
#: [Methods]
#:   • show(self, msg='')
#:   • show_twice(self)

instance 自己携带一个 attribute s, 而两个 method 属于 class。 constructor __init__() 是 dunder, 所以 display_object() 默认把它隐藏起来。

继承

由于 Python 是动态类型的,它并不要求对象先声明某个 interface。 它关心的是能否把相应操作应用到对象上。 在 C++ 或 Java 中使用继承时, 你经常只是为了建立一个共同 interface 而继承。 Python 不同。 在 Python 中,你继承的是实现,也就是复用 base class 的代码。 Python 当然也提供一种无需 inheritance 就能命名 interface 的方式, 即08 · 静态类型中的 Protocol; 它描述函数所需要的对象形状,而不是强制要求某个 base class。

首先,像从 module 中导入其他名称一样导入 base class (参见06 · 模块与包)。 然后,在派生 class 名称后的圆括号中列出 base class 即可继承。 Python 支持 multiple inheritance,因此可以列出多个 class, 虽然20 · 重新思考对象 主张优先使用 protocol 而不是 multiple inheritance。 下面的例子从 simple_class module 导入并 subclass Simple。 暂时忽略 @override decorator。 使用 @override 标记重写 会解释它:

# simple2.py
from typing import override
from simple_class import Simple

class Simple2(Simple):  # Simple2 inherits Simple
    def __init__(self, text):
        print("Inside Simple2 constructor")
        # Call the base-class constructor with super():
        super().__init__(text)
    def display(self):
        self.show("Called from display()")
    @override
    def show(self, msg=""):
        print("Overridden show() method")
        # Call the base-class method from inside
        # the overridden method:
        super().show(msg)

class Different:
    def show(self):
        print("Not derived from Simple")
# demo_simple2.py
from simple2 import Different, Simple2

x = Simple2("Simple2 constructor argument")
#: Inside Simple2 constructor
#: Inside the Simple constructor
x.display()
#: Overridden show() method
#: Called from display(): Simple2 constructor argument
x.show()
#: Overridden show() method
#: Simple2 constructor argument
x.show_twice()  # Inherited from Simple
#: Overridden show() method
#: Simple2 constructor argument
#: Overridden show() method
#: Simple2 constructor argument
def f(obj):  # Works on any obj with a show()
    obj.show()
f(x)
#: Overridden show() method
#: Simple2 constructor argument
f(Different())
#: Not derived from Simple

Simple2 继承自 Simple。 在 constructor 中,super().__init__() 调用 base-class constructor。 在 display() 中,可以把 show() 作为 self 的 method 来调用。 如果 override 某个 method、但仍然希望调用 base-class 版本, 就像这里重写后的 show() 一样,通过 super() 调用。