面向对象编程¶
类适合把状态、约束和行为组织成明确接口,但不是所有数据都需要类。 简单转换可用函数,简单记录可用 dataclass,存在稳定行为边界时再设计普通类。
类与实例¶
class Counter:
unit = "items"
def __init__(self, start: int = 0) -> None:
self.value = start
def increment(self, step: int = 1) -> int:
self.value += step
return self.value
Counter 是类,Counter() 创建实例。self 是实例方法的第一个参数,
名称虽非语法关键字,但应始终遵守该约定。
实例属性与类属性¶
self.value 属于每个实例;Counter.unit 属于类并由实例查找共享。
实例方法、类方法与静态方法¶
class Temperature:
def __init__(self, celsius: float) -> None:
self.celsius = celsius
@classmethod
def from_fahrenheit(cls, value: float) -> "Temperature":
return cls((value - 32) * 5 / 9)
@staticmethod
def is_valid(value: float) -> bool:
return value >= -273.15
封装与命名约定¶
单下划线 _cache 表示内部实现,调用者应避免依赖。
双前导下划线 __token 会名称改写为类似 _ClassName__token:
property¶
属性可在保持点号接口的同时执行验证:
class Account:
def __init__(self, balance: int = 0) -> None:
self.balance = balance
@property
def balance(self) -> int:
return self._balance
@balance.setter
def balance(self, value: int) -> None:
if value < 0:
raise ValueError("balance cannot be negative")
self._balance = value
dataclass¶
dataclasses.dataclass 可为数据载体生成 __init__、__repr__、__eq__ 等:
from dataclasses import dataclass, field
@dataclass
class Finding:
title: str
severity: int = 0
tags: list[str] = field(default_factory=list)
def __post_init__(self) -> None:
if not 0 <= self.severity <= 10:
raise ValueError("severity must be between 0 and 10")
default_factory,确保每个实例获得独立列表。
dataclass 不会根据类型注解自动校验类型,必要时在边界显式验证。
frozen 与 slots¶
frozen=True 阻止常规属性赋值,但不等于对象图深度不可变或安全防篡改。
slots=True 从 Python 3.10 起可用,可减少部分实例开销并限制随意新增属性;
它会影响继承、弱引用和反射行为,应该基于需求使用。
特殊方法¶
特殊方法让对象参与语言协议:
from dataclasses import dataclass
@dataclass(frozen=True)
class Vector:
x: int
y: int
def __add__(self, other: object) -> "Vector":
if not isinstance(other, Vector):
return NotImplemented
return Vector(self.x + other.x, self.y + other.y)
def __len__(self) -> int:
return 2
NotImplemented,让 Python 尝试反向操作或正确报错。
__str__ 面向用户,__repr__ 面向调试,后者应尽量明确且避免泄密。
不依赖 del 清理资源¶
__del__ 的调用时机、解释器关闭状态和循环引用场景不适合可靠资源管理。
文件、锁和连接应使用上下文管理器:
class Session:
def __enter__(self) -> "Session":
self.open()
return self
def __exit__(self, exc_type, exc, traceback) -> bool:
self.close()
return False
__exit__ 返回真值会压制异常,通常应返回 False 或 None,除非明确处理了异常。
继承与 super¶
class NamedCounter(Counter):
def __init__(self, name: str, start: int = 0) -> None:
super().__init__(start)
self.name = name
super() 按方法解析顺序 MRO 委托,不只是“调用直接父类”。
可用 ClassName.mro() 查看顺序。多继承中的协作方法应使用兼容签名并持续调用 super()。
重写与多态¶
子类可重写方法,但应遵守基类契约:接受兼容输入、提供兼容输出、保持关键语义。
from html import escape
class Renderer:
def render(self, text: str) -> str:
raise NotImplementedError
class HtmlRenderer(Renderer):
def render(self, text: str) -> str:
return f"<p>{escape(text, quote=True)}</p>"
abc.ABC / @abstractmethod,
或在类型层使用 Protocol 实现结构化子类型。
组合优于继承¶
继承表达“是一个”且共享稳定契约;组合表达“拥有一个/使用一个”:
class ReportService:
def __init__(self, renderer: Renderer) -> None:
self.renderer = renderer
def build(self, text: str) -> str:
return self.renderer.render(text)
相等、哈希与排序¶
如果定义 __eq__,应同时思考对象是否可哈希。
可变对象通常不应根据可变字段提供哈希,否则放入集合后可能无法正确查找。
@dataclass(frozen=True) 在字段可哈希时通常适合值对象。
排序可用 @dataclass(order=True),但字段声明顺序必须确实代表业务排序语义。
属性访问与反射¶
getattr()、setattr()、hasattr() 支持动态属性访问:
测试类¶
测试应关注公开行为而非内部字段:
依赖通过构造器注入能减少全局状态和复杂替换。 纯数据类测试验证默认值、边界和序列化;行为类还应验证状态转换与异常。何时不用类¶
- 无状态转换:使用函数。
- 少量固定字段:考虑 dataclass、NamedTuple 或 TypedDict。
- 只有一个方法且无状态:模块函数通常更直接。
- 只是为了模仿 Java 式“工具类”:改用模块命名空间。 好的类有明确职责、较小公开接口和可维护的不变量,而不是属性与方法的随意集合。
小结¶
Python OOP 依靠约定与协议,而非严格访问控制。 优先清晰数据模型、组合、上下文管理器和可测试接口,谨慎使用继承与元编程。