如何调试调用Python的copy.deepcopy()自定义类型的问题?

4 投票
3 回答
5722 浏览
提问于 2025-04-15 17:15

在我的代码中,我试图使用 copy.deepcopy 来复制一个类的实例。但是在某些情况下,它会出现错误,错误信息如下:

TypeError: 'object.__new__(NotImplementedType) is not safe, use NotImplementedType.__new__()'

经过一番调查,我发现可以用以下代码重现这个错误:

import copy
copy.deepcopy(__builtins__) 

问题似乎出在某个时刻,它试图复制一个叫 NotImplementedType 的内置类型。我的疑问是,为什么会这样呢?我在我的类中没有重写 __deepcopy__ 方法,而且这个错误并不是每次都会发生。有没有人能给我一些建议,帮我找出请求复制这个类型的来源?

我在 copy 模块中添加了一些调试代码,以确保确实是这个问题,但问题发生的地方在一个递归调用的深层次,导致我很难理解我看到的内容。

3 个回答

1

你可以重写 __deepcopy__ 方法:(查看Python文档)

为了让一个类自己定义复制的方式,它可以定义两个特别的方法 __copy__()__deepcopy__()。第一个方法是用来实现浅复制的;调用这个方法时,不需要传递额外的参数。第二个方法是用来实现深复制的;调用时会传递一个参数,就是一个叫做“memo”的字典。如果 __deepcopy__() 方法需要对某个组件进行深复制,它应该调用 deepcopy() 函数,第一个参数是要复制的组件,第二个参数是 memo 字典。

否则,你可以把模块保存在一个全局列表或者其他地方。

1

你可以通过使用 pickle 协议 来改变一个包含模块指针的类的深拷贝行为,这个协议是由 copy 模块支持的,具体内容可以在 这里 找到。特别地,你可以为这个类定义 __getstate____setstate__ 方法。例如:

>>> class MyClass:
...     def __getstate__(self):
...         state = self.__dict__.copy()
...         del state['some_module']
...         return state
...     def __setstate__(self, state):
...         self.__dict__.update(state)
...         self.some_module = some_module
3

最后我查了一下copy的源代码,找到了以下解决方案:

from copy import deepcopy, _deepcopy_dispatch
from types import ModuleType

class MyType(object):

    def __init__(self):
        self.module = __builtins__

    def copy(self):
        ''' Patch the deepcopy dispatcher to pass modules back unchanged '''
        _deepcopy_dispatch[ModuleType] = lambda x, m: x
        result = deepcopy(self)
        del _deepcopy_dispatch[ModuleType]
        return result

MyType().copy()

我知道这用了一个私有的API,但我找不到其他干净的方法来实现同样的效果。我在网上做了一个快速搜索,发现其他人也用过这个API,而且没有遇到什么问题。如果将来这个API有变化,我会承担后果。

我也知道这不是线程安全的(如果一个线程需要旧的行为,而我在另一个线程上做复制,那就麻烦了),但目前对我来说这不是问题。

希望这能在某个时候帮助到其他人。

撰写回答