【问题标题】:Making a deepcopy of custom class object in python3?在 python3 中制作自定义类对象的深度复制?
【发布时间】:2020-12-04 14:25:32
【问题描述】:

我正在尝试实现类似于如果x 是一个列表,那么y = list(x) 应该是x 的深层副本。

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

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。有没有办法实现这个 deepcopy 功能?

【问题讨论】:

  • 因为 B 满足 if 语句 if isinstance(foo, CustomClass) 所以永远不会创建 self._foo
  • @coderoftheday 对不起,我不完全理解。我想我有点迷茫为什么self = deepcopy(A) 没有将属性A._foo 复制到B
  • 但是ACustomClass 的一个实例,那么为什么要创建self._foo
  • 分配给self在函数外部绝对没有影响(在这种情况下,在函数内部绝对没有影响,因为它随后不使用self)。

标签: python python-3.x class deep-copy


【解决方案1】:

正如 cmets 中所说,分配给 self 没有帮助。问题是当__init__(...) 被调用时,类已经被创建并且引用被保存在类之外(即由Python解释器/运行时)。而且,由于 __init__(...) 不返回任何内容(特别是不是它自己的引用),因此无法从那里更改外部引用,而您尝试 deepcopy 会这样做。

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

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

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 每个属性并将它们分配给我们新创建的对象,模仿“正常”deepcopycall 的行为。

【讨论】:

  • 感谢您的详细解释,非常感谢! :)
猜你喜欢
  • 2017-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-09
  • 2011-07-25
  • 2016-07-07
  • 2012-12-15
  • 2012-06-13
相关资源
最近更新 更多