__copy__ 方法必须返回新实例,不能修改原对象;它仅负责顶层对象构造,不自动处理嵌套对象;签名错误或第三方对象可能使其失效。

__copy__ 方法必须返回新实例,不能只修改 self
直接在 __copy__ 里改当前对象(比如清空某个属性)是错的——浅拷贝的本质是“新建一个对象,复用原对象的引用”,不是“重置原对象”。Python 的 copy.copy() 会调用它,期望拿到一个新对象。如果返回 None 或没返回值,结果就是 None,后续操作必然报错。
正确做法是手动构造新实例,并按需调整属性:
import copy
<p>class Cache:
def <strong>init</strong>(self, data, meta=None):
self.data = data
self.meta = meta or {}</p><pre class="brush:python;toolbar:false;">def __copy__(self):
# 必须显式创建新实例
new_obj = Cache(self.data) # 浅拷贝:data 是引用,meta 是新 dict
new_obj.meta = self.meta.copy() # 避免共享 meta 字典
return new_objc1 = Cache([1, 2, 3], {"version": 1}) c2 = copy.copy(c1) c2.data.append(4) print(c1.data) # [1, 2, 3, 4] —— data 共享,符合浅拷贝预期 print(c2.meta is c1.meta) # False —— meta 不共享,是 copy 里主动处理的
__copy__ 不影响嵌套可变对象,这是浅拷贝的固有行为
很多人误以为实现 __copy__ 就能“深度隔离”,其实不然。只要没显式对嵌套对象做深拷贝,它们依然共享引用。比如 self.data 是 list,__copy__ 里没处理它,那 c1.data 和 c2.data 就是同一个 list。
- 想让
data也独立?得手动new_obj.data = self.data.copy()(仅限 list/tuple)或copy.copy(self.data) - 如果
data是自定义类,且该类没实现__copy__,copy.copy()会 fallback 到默认逻辑(字段级浅拷贝) - 别指望
__copy__自动递归处理所有属性——它只负责顶层对象的构造逻辑
和 __deepcopy__ 不要混用,参数签名不同
__copy__ 没参数;__deepcopy__ 必须带 memo 参数(用于缓存已拷贝对象,防止循环引用死循环)。如果写错了签名,比如:
def __copy__(self, memo): # 错!多了一个参数
...
会导致 copy.copy() 调用失败,抛出 TypeError: __copy__() takes 1 positional argument but 2 were given。
真正需要深拷贝时,才加 __deepcopy__:
def __deepcopy__(self, memo):
new_obj = Cache(copy.deepcopy(self.data, memo))
new_obj.meta = copy.deepcopy(self.meta, memo)
return new_obj
第三方库对象可能不走 __copy__,得看它自己是否支持
像 numpy.ndarray、torch.Tensor 这类对象,有自己的拷贝机制。copy.copy() 对它们通常直接返回原对象(因为底层数据共享更高效),根本不会触发你的 __copy__。如果你的类里存了这类对象,不能假设 __copy__ 一定生效。
- 验证方式:在
__copy__里加print("copied"),然后跑copy.copy(your_obj)看是否输出 - 遇到不触发的情况,要么改用
.copy()方法(如arr.copy()),要么在__copy__里显式处理这些字段 - 尤其注意 pandas DataFrame,它的
copy()默认是深拷贝,和 Python 原生copy.copy()行为不一致
真正容易漏掉的是:copy 只控制 copy.copy(),不影响赋值(=)、序列解包、函数参数传递这些隐式引用场景。那些地方根本不会调用它,得靠文档或约定提醒使用者。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











