用设计良好的类来组织代码:初始化、实例属性与类属性、继承及其取舍。
在 Kodokon 中打开本课你一直都在和 Python 对象打交道:字符串、列表、文件。编写自己的类,意味着把状态(属性)和行为(方法)打包到一个连贯的接口背后。一个常被误解的术语问题:__init__ 并不是构造函数。当 __init__ 收到 self 时,对象早已存在(由 __new__ 创建);它的职责是初始化这个实例。而 self 也没有任何神奇之处:它是每个实例方法的第一个参数,Python 在调用时会自动传入它。
class Account:
def __init__(self, owner, balance=0.0):
self.owner = owner
self.balance = balance
def deposit(self, amount):
if amount <= 0:
raise ValueError("amount must be > 0")
self.balance += amount
return self.balance
def __repr__(self):
return f"Account({self.owner!r}, {self.balance})"
account = Account("Ada")
account.deposit(120.0)
print(account)在生产环境中一个至关重要的区分:类属性声明在类体中,被所有实例共享;实例属性创建在 self 上,通常写在 __init__ 里。读取时,Python 先在实例上查找,找不到再退回到类,然后是父类。而 self.x = ... 这样的赋值总是创建一个实例属性,它会遮蔽同名的类属性。经典的现实陷阱:一个可变的类属性。
class Basket:
items = []
def add(self, item):
self.items.append(item)
a = Basket()
b = Basket()
a.add("apple")
print(b.items) # ['apple']: shared list!继承表达的是一种 "是一个" 的关系:Car 是一个 Vehicle。子类继承父类的方法,可以重写它们,而 super() 让它得以调用父类的版本 - 这在 __init__ 中不可或缺,否则你会短路掉父类的初始化。Python 沿着 MRO(方法解析顺序)来解析调用。不要习惯性地伸手去用继承:如果只是想复用代码而并不存在 "是一个" 的关系,请优先选择组合(一个对象持有另一个对象)。层级过深的继承树很脆弱:每个父类都会变成其所有子类的隐式 API。
class Vehicle:
def __init__(self, brand):
self.brand = brand
def describe(self):
return self.brand
class Car(Vehicle):
def __init__(self, brand, doors):
super().__init__(brand)
self.doors = doors
def describe(self):
base = super().describe()
return f"{base}, {self.doors} doors"
print(Car("Aster", 5).describe())