使用Python copy.deepcopy时出现TypeError是什么原因?
问题结论
既不是copy.deepcopy的调用方式有误,也不是copy.deepcopy本身存在通用bug,报错根源是自定义的Hours子类重写__new__方法时,没有兼容datetime.timedelta原生的对象重建协议,和deepcopy的对象构造传参逻辑不匹配。
根因详解
Python的copy.deepcopy处理C实现的内置不可变类型(比如timedelta)时,不会直接拷贝内存,而是通过对象自带的__reduce__方法获取重建对象所需的构造函数、位置参数,再调用构造函数生成新对象。
原生datetime.timedelta内部实际只存储days、seconds、microseconds三个值,所有其他初始化参数(hours、weeks、minutes等)都会在构造时换算为这三个单位存储,因此它的__reduce__方法返回的构造参数固定为这三个值组成的元组。
你定义的Hours类重写的__new__方法仅接受cls、hours两个位置参数,deepcopy拿到三个位置参数传入构造时,算上隐式传入的cls总共是4个参数,和方法签名不匹配,就触发了看到的类型错误。
可以直接运行以下代码验证timedelta的重建参数逻辑:
import datetime h = datetime.timedelta(hours=2) print(h.__reduce__()) # 输出: (<class 'datetime.timedelta'>, (0, 7200, 0)) # 元组里的三个值依次是days=0, seconds=7200(即2小时), microseconds=0
修复方案
两种方案均可解决问题,按需选择即可:
- 方案1:调整
__new__方法签名,兼容原生timedelta的参数传递规则,适配deepcopy、pickle等依赖对象重建协议的场景
import copy import datetime class Hours(datetime.timedelta): def __new__(cls, hours=0, days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, weeks=0): return super().__new__( cls, hours=hours, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, weeks=weeks ) h1 = Hours(2) h2 = copy.deepcopy(h1) # 正常运行 print(h1 == h2, type(h2) is Hours) # 输出: True True
- 方案2:自定义
__reduce__方法,明确指定deepcopy重建Hours对象时的传参逻辑
import copy import datetime class Hours(datetime.timedelta): def __new__(cls, hours): return super().__new__(cls, hours=hours) def __reduce__(self): # 返回(构造类, 构造参数元组),直接传入小时数 return (self.__class__, (self.total_seconds() / 3600,)) h1 = Hours(2) h2 = copy.deepcopy(h1) # 正常运行
内容的提问来源于stack exchange,提问作者user118967
相关产品推荐
相关产品推荐

