如何调试调用Python的copy.deepcopy()自定义类型的问题?
在我的代码中,我试图使用 copy.deepcopy
来复制一个类的实例。但是在某些情况下,它会出现错误,错误信息如下:
TypeError: 'object.__new__(NotImplementedType) is not safe, use NotImplementedType.__new__()'
经过一番调查,我发现可以用以下代码重现这个错误:
import copy
copy.deepcopy(__builtins__)
问题似乎出在某个时刻,它试图复制一个叫 NotImplementedType
的内置类型。我的疑问是,为什么会这样呢?我在我的类中没有重写 __deepcopy__
方法,而且这个错误并不是每次都会发生。有没有人能给我一些建议,帮我找出请求复制这个类型的来源?
我在 copy
模块中添加了一些调试代码,以确保确实是这个问题,但问题发生的地方在一个递归调用的深层次,导致我很难理解我看到的内容。
3 个回答
你可以重写 __deepcopy__ 方法:(查看Python文档)
为了让一个类自己定义复制的方式,它可以定义两个特别的方法 __copy__() 和 __deepcopy__()。第一个方法是用来实现浅复制的;调用这个方法时,不需要传递额外的参数。第二个方法是用来实现深复制的;调用时会传递一个参数,就是一个叫做“memo”的字典。如果 __deepcopy__() 方法需要对某个组件进行深复制,它应该调用 deepcopy() 函数,第一个参数是要复制的组件,第二个参数是 memo 字典。
否则,你可以把模块保存在一个全局列表或者其他地方。
你可以通过使用 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
最后我查了一下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有变化,我会承担后果。
我也知道这不是线程安全的(如果一个线程需要旧的行为,而我在另一个线程上做复制,那就麻烦了),但目前对我来说这不是问题。
希望这能在某个时候帮助到其他人。