Skip to content

对象、拷贝与异常

名字与对象

python
a = [1, 2]
b = a
b.append(3)
print(a)  # [1, 2, 3]

赋值绑定名字,不复制对象。id 表示对象身份;是否可变决定能否通过某个引用观察到原地变化。

python
a = a + [4]   # 创建新 List 并重新绑定
a += [4]      # List 通常原地扩展

+= 调用就地运算协议;不可变对象无法原地改变,因此会生成新对象。不要仅凭运算符外观推断是否修改原对象。

浅拷贝与深拷贝

python
from copy import copy, deepcopy

outer = [[1], [2]]
shallow = copy(outer)
deep = deepcopy(outer)
  • 浅拷贝创建新外层容器,内部元素仍是相同引用。
  • 深拷贝递归复制对象图,并用 Memo 处理循环引用和共享关系。
  • 深拷贝不一定合理:数据库连接、锁、文件、缓存和领域实体可能不应复制。

equality 与 identity

== 调用值相等协议,is 比较身份:

python
value is None
type(value) is int
isinstance(value, int)

一般类型判断使用 isinstance,它支持继承和抽象基类。严格排除 bool 时要注意 boolint 的子类。

hash 与可变性

Dict Key 与 Set 元素必须 Hashable,并且对象相等时 Hash 必须相同。List、Dict、Set 因可变而不能作为 Key;Tuple 只有所有元素都 Hashable 时才可 Hash。

自定义类实现 __eq__ 后通常需要同步考虑 __hash__。把参与 Hash 的字段设为可变会使对象加入 Set 后无法再正确定位。

try、else 与 finally

python
try:
    result = parse(raw)
except ValueError as exc:
    handle(exc)
else:
    persist(result)
finally:
    release()
  • else 只在 try 没有抛异常时执行,可缩小 except 意外捕获范围。
  • finally 无论正常、异常还是 return 都会执行。
  • finallyreturn 会覆盖原返回值或正在传播的异常,应避免。
  • except Exception 不捕获 KeyboardInterruptSystemExitBaseException 子类。

异常转换使用 raise NewError(...) from exc 保留因果链;明确隐藏上下文才使用 from None

with 与上下文管理器

python
with open(path, encoding="utf-8") as stream:
    content = stream.read()

with 确保 __exit__ 在离开代码块时执行。__exit__ 返回真值会抑制异常,编写自定义 Context Manager 时不要无意吞掉错误。

多个资源可写在一个 with 中;动态数量的资源使用 contextlib.ExitStack。异步资源使用 async with,不能用同步 Context Manager 假装完成异步关闭。