You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何set未像其他内置容器使用__newobj__反序列化?继承类拷贝问题解析

继承set的自定义容器类使用copy/deepcopy时的异常问题

最近编写了一批继承自内置list、dict和set类型的自定义容器类,意外发现继承set的类实例使用copy模块的copy和deepcopy函数拷贝时会触发异常,而其他自定义类可正常工作。

最小可复现示例(MWE)

import copy

class SetBase(set):
    def __init__(self, iterable, name, master):
        super().__init__(iterable)
        self.name = name
        self.master = master
    
set_1 = SetBase([1, 2], name='set', master='master')
set_2 = copy.deepcopy(set_1)

报错回溯信息

Traceback (most recent call last):
  File "/Users/test.py", line 10, in <module>
    set_2 = copy.deepcopy(set_1)
            ^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/copy.py", line 162, in deepcopy
    y = _reconstruct(x, memo, *rv)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/copy.py", line 253, in _reconstruct
    y = func(*args)
        ^^^^^^^^^^^
TypeError: SetBase.__init__() missing 2 required positional arguments: 'name' and 'master'

SetBase是继承自内置set的类,其__init__方法额外接收name和master两个参数。经排查,报错根源在于set类的__reduce_ex__方法。

进一步探索

测试各内置类型的__reduce_ex__方法,得到如下结果:

>>> "".__reduce_ex__(5)
(<function __newobj__ at 0x104d41800>, (<class 'str'>, ''), None, None, None)
>>> [1,2].__reduce_ex__(5)
(<function __newobj__ at 0x104d41800>, (<class 'list'>,), None, <list_iterator object at 0x104c43df0>, None)
>>> {1:2}.__reduce_ex__(5)
(<function __newobj__ at 0x104d41800>, (<class 'dict'>,), None, None, <dict_itemiterator object at 0x104c5f830>)
>>> (1,2).__reduce_ex__(5)
(<function __newobj__ at 0x104d41800>, (<class 'tuple'>, (1, 2)), None, None, None)
>>> {1,2}.__reduce_ex__(5)
(<class 'set'>, ([1, 2],), None)

可以看到,list、dict、tuple使用__newobj__函数进行反序列化创建拷贝实例,而set直接使用自身类。继承set的类遵循该模式,创建拷贝实例后会显式调用__init__方法,但因参数不全触发异常。

疑问

  • 是什么导致set的__reduce_ex__方法如此特殊?
  • 为何不采用看似更健壮的__newobj__方式?
  • 是否有底层因素阻碍set实现该机制?

可能的修复方案

针对上述示例,可通过重写__reduce__方法解决问题:

class SetBase(set):
    def __init__(self, iterable, name, master):
        super().__init__(iterable)
        self.name = name
        self.master = master

    def __reduce__(self):
        return (self.__class__, (list(self), self.name, self.master))

在实际场景中,若自定义容器有更复杂的属性和状态,可为iterable之外的额外参数设置默认值作为临时解决方案。


内容的提问来源于stack exchange,提问作者ZHANG Wuji

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 19:02:04