本篇是《Python 从入门到初级》第 14 / 20 篇。示例使用 Python 3.12 兼容语法,完整目录结构与数据见配套源码包。
字典已经能描述一台设备,为什么还需要类?当一类数据还有共同的校验和行为时,可以把它们放在一个对象中。本篇通过设备档案理解类、实例、属性和方法。

1. 类是定义,实例是具体对象
Device 可以描述“设备有哪些属性、能做什么”;Device("web01", 72) 创建的是一台具体设备对象。另一台 db01 是另一个实例,各自持有自己的状态。
属性保存数据,方法表示对象相关的操作。self 指当前实例,调用 web.describe() 时,Python 会把 web 传给方法的 self。
它不是一个必须到处使用的新容器。只有一份简单记录时,字典可能已经足够;有一组需要保持一致的状态与行为时,类才更有价值。
2. 完整示例:设备状态与阈值判断
运行 python lesson14.py:
class Device:
def __init__(self, name, cpu_percent):
self.name = name
self.update_cpu(cpu_percent)
def update_cpu(self, value):
if not 0 <= value <= 100:
raise ValueError("CPU 百分比应在 0 到 100 之间")
self.cpu_percent = value
def is_busy(self, threshold=80):
return self.cpu_percent >= threshold
def describe(self):
return f"{self.name}: CPU={self.cpu_percent}%, busy={self.is_busy()}"
def main():
web = Device("web01", 72)
database = Device("db01", 91)
print(web.describe())
print(database.describe())
web.update_cpu(85)
print(web.describe())
if __name__ == "__main__":
main()
预期 web01 的 72% 低于默认 80%,busy 为 False;db01 的 91% 为 True;web01 更新到 85% 后,也变成 True。
这些是程序中保存的教学指标,不是现场 CPU 采样。是否应该告警,还需要持续时间、业务影响等额外规则,不能把一个布尔判断当成完整监控系统。
3. __init__ 为什么又调用 update_cpu
__init__ 用于初始化实例状态。它把 CPU 赋值交给 update_cpu,使创建时和更新时复用同一项范围验证。
如果分别写两份验证,一处允许 101,另一处不允许,规则就会分叉。把共同校验放在明确的方法里,可以减少这种不一致。
类的基本机制见 Python Classes 教程。
4. 返回结果的方法更容易组合
is_busy() 返回布尔值,调用者可以决定打印、统计、写报告还是生成告警。describe() 返回字符串,同样不直接决定输出设备。
这延续了第 8 篇的原则:先计算或描述,再由入口选择如何展示。不是每一个方法都应该内部 print。
Python 的普通属性并不会自动禁止外部赋值。本例约定通过 update_cpu 更新 CPU;需要更严格的属性约束时,可以后续学习 property 等机制。
5. 初级阶段还应知道的边界
类属性和实例属性不同,共享可变类属性可能让多个实例意外共用一份列表。实例自己的状态,通常在 __init__ 中建立。
继承用于表达某种“是”的关系,不适合仅为了少写几行就堆层级。复杂装饰器、多重继承和元类都不在本系列要求里。
类型标注也不会自动替你验证全部输入。示例调用方按约定提供数字;面向 CSV 或接口时,仍需在边界检查和转换。
6. 练习:理解独立状态
创建 web01、web02 两个实例,只更新 web01,再分别输出 describe。预期 web02 的值不变。
再传入 101,观察 ValueError;把 is_busy 的阈值改为 90,比较 85% 的结果。只有能解释对象各自存了什么、方法用了哪个状态,才算理解这段类代码。