【问题标题】:Why is Python's deepcopy() of a class-object sharing class-variables?为什么类对象的 Python deep copy() 共享类变量?
【发布时间】:2020-10-21 05:10:52
【问题描述】:

我正在使用 deepcopy 对 Python 类进行深层复制,并更改复制对象的类变量。由于某种原因,被复制的原始类的类变量也被更改了。这是为什么呢?

当我使用'type' syntax 创建一个类时,这似乎没有发生。

import copy

class Test:
    a = 0

print(Test.a) # 0
b = copy.deepcopy(Test)
b.a = 1
print(Test.a) # 1 - why?

c = type('Test2', (Test,), {'a': 2})
print(Test.a) # still 1

【问题讨论】:

  • 这是一个静态类变量,不是实例变量。首先使用self 变量创建该类的实例,然后复制它,您将在self 变量上获得所需的结果。顺便说一句,deepcopysuper slow。可能不使用它,或者如果你这样做,实现__deepcopy__

标签: python deep-copy


【解决方案1】:

您的第一个问题:

正如 @Blckknght 在 cmets 中所指出的,当您将类定义对象传递给 copy.deepcopy() 时,您会遇到 deepcopy() 的行为,尽管是有意的,但令人惊讶/古怪——而不是深入类定义对象的副本,deepcopy() 仅返回对同一类定义对象的引用

为了验证这一点,

print ("b is:", b)
b_instance = b()
print ("b is Test ?:", (b is Test))

给予:

b is: <class '__main__.Test'>
b is Test ?: True             # deepcopy() hasn't made a copy at all !

下面是 the doc 关于 deepcopy() 的类定义对象所说的:

此模块不会复制模块、方法、堆栈跟踪等类型, 堆栈帧、文件、套接字、窗口、数组或任何类似类型。它 通过返回“复制”函数和类(浅层和深层) 原对象不变;这与这些方式兼容 由 pickle 模块处理。

所以,当你这样做时

b.a = 1

您仍在修改类Test 的类变量。

您的第二个问题:

当你这样做时

c = type('Test2', (Test,), {'a': 2})

正在创建一个名为Test2 的新类型,它有自己的类变量a,并将Test 作为其超类。

现在Test2.aTest.a 是两个不同的类变量,定义在两个不同的类中,并且可以相互独立地取值。一个是超类Test中名为a的类变量,另一个是子类Test2中同名a的类变量。

这就是为什么c.a 会给出2(你初始化它的值),而Test.a 会继续给出1

【讨论】:

  • 您可能希望您的初始代码块测试b is Test。您当前正在做的事情并没有最终表明 deepcopy 是类对象的无操作。你有点这么说,但不要解释这有点不寻常。通常deepcopy确实会创建一个包含相同内容的新对象,但它不会为类这样做(这是 Python 开发人员做出的设计决定,而不是绝对不能做的事情' t 被设计成另一种方式)。
  • @Blckknght,同意你的第一点,并编辑了我的答案,谢谢。你的第二点是deepcopy() 在类对象的情况下没有做一个真诚的复制。对此我不确定。我们在这里试图合理化的观察结果似乎表明deepcopy() 实际上在复制方面做得做得太好了。因此,它不是为一个全新的类创建一个类对象,而是创建一个描述完全相同的类Test 的类对象——这是一个正确的复制操作应该做的事情
  • 根本没有复制Testb = copy.deepcopy(Test) 等价于 b = Test,只是将新名称绑定到现有的类对象。
  • @Blckknght,我明白你的意思,更新我的答案,谢谢。
  • 该术语只是“类对象”或“类”,而不是“类定义对象”。
【解决方案2】:

您复制了类定义而不是实例本身。

你应该这样做:

import copy

class Test:
    def __init__(self,a):
        self.a = a

test1 = Test(0)
test2 = copy.deepcopy(test1)

print("Original:")
print("Test 1: " + str(test1.a)) #0
print("Test 2: " + str(test2.a)) #0

print("Modify test 2 to 10")
test2.a = 10
print("Test 1: " + str(test1.a)) #0
print("Test 2: " + str(test2.a)) #10

原文:

测试 1:0

测试 2:0

将测试 2 修改为 10

测试 1:0

测试 2:10

【讨论】:

  • 感谢杰克逊。由于我使用它的限制(SQLAlchemy mixins),我不得不复制类本身,而不是类的实例(因为这不是我的代码本身处理的东西)。
猜你喜欢
  • 1970-01-01
  • 2012-10-31
  • 2012-05-17
  • 1970-01-01
  • 2015-10-14
  • 1970-01-01
  • 2012-02-10
  • 1970-01-01
相关资源
最近更新 更多