【问题标题】:How to copy a Python class instance if deepcopy() does not work?如果 deepcopy() 不起作用,如何复制 Python 类实例?
【发布时间】:2018-06-28 13:28:48
【问题描述】:

我想在 python 中制作一个类实例的副本。我尝试了copy.deepcopy,但收到错误消息:

RuntimeError: 目前只有用户显式创建的变量(图叶)支持 deepcopy 协议

所以假设我有类似的东西:

class C(object):
    def __init__(self,a,b, **kwargs):
        self.a=a
        self.b=b
        for x, v in kwargs.items():
            setattr(self, x, v)

c = C(4,5,'r'=2)
c.a = 11
del c.b

现在我想制作一个相同的c 的深层副本,有没有简单的方法?

【问题讨论】:

  • 是的。确实。覆盖__copy__ dunder。或者__deepcopy__ 一个,这取决于您的需要。
  • 是的,您可以使用copy.deepcopy。所以只是c2 = copy.deepcopy(c) 然后vars(c2) == {'a': 11, 'r': 2}vars(c) == {'a': 11, 'r': 2} 但是你报告的回溯不会由你给出的类定义产生......
  • @cᴏʟᴅsᴘᴇᴇᴅ 注意,在这种情况下不需要。 copy 模块将处理未定义 __copy____deepcopy__ 的类型运行时错误。
  • @juanpa.arrivillaga 嗯...嗯...简历是“离题”吗?我已经投票了。
  • @juanpa.arrivillaga 是的,实际上我正在尝试复制torch.nn.Module 的元类,但这有点复杂,所以我在这里举一个更简单的例子。只是说明在我的情况下deepcopy 不起作用,所以我想要另一个解决方案。

标签: python class deep-copy


【解决方案1】:

是的,您可以使用 deepcopy 复制类实例:

from copy import deepcopy

c = C(4,5,'r'=2)
d = deepcopy(c)

这会在 'd' 中创建类实例 'c' 的副本。

【讨论】:

  • 我特别提到这对我不起作用。因为在我的情况下,deepcopy 给出了一个错误。我特别不想要使用deepcopy 的解决方案。
  • 我是从谷歌那里得到的,用于搜索“如何对类的实例进行深度复制”,这对我有用。所以,我必须投票......
  • deepcopy 使用pickle,它会在某些对象上中断:TypeError: can't pickle pygame.Surface objects - 这正是我在这里寻找解决方法的原因。所以这对我不起作用。
  • @lode 只有回答问题的答案才会被投票。这是一个明显的反对意见,因为回答者要么没有阅读,要么不理解这个问题。仅因为它在搜索结果中并帮助您对课程进行深度复制而对此进行投票是没有选择的,而应该对其进行投票。
【解决方案2】:

一种方法是在C 类中实现__copy__,如下所示:

class A:
    def __init__(self):
        self.var1 = 1
        self.var2 = 2
        self.var3 = 3

class C(A):
    def __init__(self, a=None, b=None, **kwargs):
        super().__init__()
        self.a = a
        self.b = b
        for x, v in kwargs.items():
            setattr(self, x, v)

    def __copy__(self):
        self.normalizeArgs()
        return C(self.a, self.b, kwargs=self.kwargs)

    # THIS IS AN ADDITIONAL GATE-KEEPING METHOD TO ENSURE 
    # THAT EVEN WHEN PROPERTIES ARE DELETED, CLONED OBJECTS
    # STILL GETS DEFAULT VALUES (NONE, IN THIS CASE)
    def normalizeArgs(self):
        if not hasattr(self, "a"):
            self.a      = None
        if not hasattr(self, "b"):
            self.b      = None
        if not hasattr(self, "kwargs"):
            self.kwargs = {}

cMain   = C(a=4, b=5, kwargs={'r':2})

del cMain.b
cClone  = cMain.__copy__()

cMain.a = 11

del  cClone.b
cClone2 = cClone.__copy__()

print(vars(cMain))
print(vars(cClone))
print(vars(cClone2))

【讨论】:

  • 这不起作用,我得到错误 ` 9 def __copy__(self): ---> 10 keywordArgs = vars(self)['kwargs'] 11 return C(self.a, self.b, kwargs=keywordArgs) 12 KeyError: 'kwargs'`
  • 即使我删除了一个变量,它也不应该工作。所以在做del c.a之后我应该可以做c_prime = c.__copy__()
  • 它还应该对参数进行深度复制。所以如果c.a == [1,2] 我不应该有c.a == c_prime.a
  • 它运行。但这并不能解决我的问题,因为除上面提到的其他问题外。如果我在脚本末尾添加以下行:cClone = cMain.__copy__(),(即在del 行之后),则会收到以下错误:AttributeError: 'C' object has no attribute 'b'
  • 如果我将您的线路 cMain = C(a=4, b=5, kwargs={'r':2}) 换成线路 cMain = C(4,5,'r'=2) 就像我的问题一样,它也不起作用。
【解决方案3】:

我基本上已经弄清楚了。我无法克服的唯一问题是知道所有类的一组可接受的初始化参数(__init__ 的参数)。所以我必须做出以下两个假设:

1) 我有一组 C 类的默认参数,我称之为 argsC。 2) C 中的所有对象都可以用空参数初始化。

在这种情况下我可以 第一: 从我要复制 c 的实例初始化类 C 的新实例:

c_copy = c.__class__(**argsC)

第二: 遍历c的所有属性并将c_copy属性设置为c属性的副本

for att in c.__dict__:
    setattr(c_copy, att, object_copy(getattr(c,att)))

其中object_copy 是我们正在构建的函数的递归应用程序。

最后: 删除c_copy中的所有属性,而不是c中的所有属性:

for att in c_copy.__dict__:
    if not hasattr(c, att):
        delattr(c_copy, att)

把这一切放在一起,我们有:

import copy

def object_copy(instance, init_args=None):
    if init_args:
        new_obj = instance.__class__(**init_args)
    else:
        new_obj = instance.__class__()
    if hasattr(instance, '__dict__'):
        for k in instance.__dict__ :
            try:
                attr_copy = copy.deepcopy(getattr(instance, k))
            except Exception as e:
                attr_copy = object_copy(getattr(instance, k))
            setattr(new_obj, k, attr_copy)

        new_attrs = list(new_obj.__dict__.keys())
        for k in new_attrs:
            if not hasattr(instance, k):
                delattr(new_obj, k)
        return new_obj
    else:
        return instance

所以把它们放在一起我们就有了:

argsC = {'a':1, 'b':1}
c = C(4,5,r=[[1],2,3])
c.a = 11
del c.b
c_copy = object_copy(c, argsC)
c.__dict__

{'a': 11, 'r': [[1], 2, 3]}

c_copy.__dict__

{'a': 11, 'r': [[1], 2, 3]}

c.__dict__

{'a': 11, 'r': [[1, 33], 2, 3]}

c_copy.__dict__

{'a': 11, 'r': [[1], 2, 3]}

这是期望的结果。如果可以,它会使用deepcopy,但对于会引发异常的情况,它可以不用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-06
    • 2016-03-21
    • 2016-01-20
    • 2016-03-05
    • 1970-01-01
    相关资源
    最近更新 更多