【问题标题】:copy.deepcopy raises TypeError on objects with self-defined __new__() methodcopy.deepcopy 在具有自定义 __new__() 方法的对象上引发 TypeError
【发布时间】:2012-05-24 00:58:03
【问题描述】:

我想实现一个符号类型,它跟踪我们已经拥有的符号(保存在_sym_table),如果它们存在则返回它们,否则创建新的。代码:

# -*- coding: utf-8 -*-

_sym_table = {}

class Symbol(object):
    def __new__(cls, sym):
        if sym not in _sym_table:
            return super().__new__(cls)
        else:
            return _sym_table[sym]

    def __init__(self, sym):
        self.sym = sym
        _sym_table[sym] = self

    def __str__(self):
        return self.sym

    def __cmp__(self, other):
        return self is other

    def __hash__(self):
        return self.sym.__hash__()

但是当我在这样的Symbol 实例列表上调用copy.deepcopy 时,会引发异常:

a = Symbol('a')
b = Symbol('b')
s = [a, b]
t = copy.deepcopy(s)

错误信息:

Traceback (most recent call last):
  File "xxx.py", line 7, in <module>
    t = copy.deepcopy(s)
  File "/usr/lib/python3.2/copy.py", line 147, in deepcopy
    y = copier(x, memo)
  File "/usr/lib/python3.2/copy.py", line 209, in _deepcopy_list
    y.append(deepcopy(a, memo))
  File "/usr/lib/python3.2/copy.py", line 174, in deepcopy
    y = _reconstruct(x, rv, 1, memo)
  File "/usr/lib/python3.2/copy.py", line 285, in _reconstruct
    y = callable(*args)
  File "/usr/lib/python3.2/copyreg.py", line 88, in __newobj__
    return cls.__new__(cls, *args)
TypeError: __new__() takes exactly 2 arguments (1 given)

所以我的问题是:

  • 如何使用自定义的__new__ 方法对这些对象进行深层复制?
  • 关于何时以及如何使用copy.deepcopy 有什么建议吗?

非常感谢!

【问题讨论】:

    标签: python deep-copy


    【解决方案1】:

    一个问题是deepcopycopy 无法知道将哪些参数传递给__new__,因此它们仅适用于不需要构造函数参数的类。

    你可以有__init__参数的原因是复制对象时不会调用__init__,但必须调用__new__来创建新对象。

    因此,如果您想控制复制,则必须定义特殊的 __copy____deepcopy__ 方法:

    def __copy__(self):
        return self
    
    def __deepcopy__(self, memo):
        return self
    

    顺便说一句,singletonsevil,在 python 中并不真正需要。

    【讨论】:

      【解决方案2】:

      在我看来,您希望 Symbol 实例是单例。但是,当您想要实例的精确副本(即与原始实例相同的不同实例)时,应该使用 Deepcopy。

      所以这里的用法有点与 deepcopy 的目的相矛盾。如果你想让它工作,你可以在 Symbol 上定义__deepcopy__ 方法。

      【讨论】:

        【解决方案3】:

        定义__getnewargs__ — 这样您不仅可以copydeepcopy,还可以pickle

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-09-07
          • 1970-01-01
          • 2012-06-20
          • 1970-01-01
          • 1970-01-01
          • 2015-06-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多