在 python3 中深度复制自定义 class 对象?

Making a deepcopy of custom class object in python3?

我正在尝试实现类似于如果 x 是一个列表那么 y = list(x) 应该是 x.

的深度复制的东西

到目前为止,我有以下 class 代码:

from copy import deepcopy
class CustomClass:
    def __init__(self, foo):
        if isinstance(foo, CustomClass):
            self = deepcopy(foo)
        else:
            self._foo = foo

现在如果我运行

A = CustomClass(bar)
B = CustomClass(A)
print(B._foo)

我收到一个错误 AttributeError: 'CustomClass' object has no attribute '_foo'。我不确定这里出了什么问题。例如,如果我要在 if 语句中添加 print(self._foo) 行,我会看到 deepcopy 已成功分配给 self。有没有办法实现这个深度复制功能?

正如评论中所说,分配给 self 没有帮助。问题是当 __init__(...) 被调用时,class 已经创建并且引用保存在 class 之外(即 Python interpreter/runtime)。而且,由于 __init__(...) 没有 return 任何东西(特别是它自己的参考),因此无法从那里更改外部参考,而您对 deepcopy 所做的尝试。

Python 中的对象创建,例如调用CustomClass(foo),实际上是一个两步过程。首先,在调用 __init__(...) 之前,运行时会调用 class 上的另一个静态方法,即 __new__(...)。这是构造实际对象的地方,这个 class 方法实际上 do return 对象的引用。因此,理论上应该可以在 __new__(...) 方法中执行 deepcopy(如果您知道如何执行)。但是,在调用 __new__(...) returns 之后,运行时将继续创建过程并调用 __init__(...) 方法。因此,在这种情况下,您还需要确保此调用不会更改新复制的对象中的任何内容。

不过,更简单的方法是让系统为您创建一个新对象(通过自动调用 __new__(...)),然后将新图像“形成”为(深度复制的)图像的原始对象。这可以通过深度复制原始对象的所有属性并将它们放入新对象中来完成。如果所有属性都是已知的,这可以通过显式列出它们并将(深度复制的)calues 分配给它们来完成:

class CustomClass:
    def __init__(self, foo):
        if isinstance(foo, CustomClass):
            # if foo is CustomClass, deepcopy all its attributes
            self._foo = deepcopy(foo._foo)
            # return to not perform "normal" initialization
            return
        # This will only execute if foo is _not_ a CustomClass object
        self._foo = foo 

但是,这种方法有点脆弱,因为它要求在复制过程中不会遗忘任何属性,并且不会动态添加新属性(或者它们不会被复制),例如有人在做类似

的事情
cc = CustomClass(4)
cc.dynamic = 5

cc.dynmaic 不会被复制(而 deepcopy 会)。因此,更好的方法是使用内部字典来保留对所有属性的引用,无论何时创建它们:

class CustomClass:
    def __init__(self, foo):
        if isinstance(foo, CustomClass):
            # if foo is CustomClass, deepcopy all its attributes
            self.__dict__ = {key: deepcopy(value) for key, value in foo.__dict__.items()}
            # return to not perform "normal" initialization
            return
        # This will only execute if foo is _not_ a CustomClass object
        self._foo = foo 

这里使用了对象的内部__dict__。它引用所有属性,包括任何已动态添加的属性,然后我 deepcopy 每个属性并将它们分配给我们新创建的对象,模仿“正常” deepcopy 调用的行为。