如果 deepcopy() 不起作用,如何复制 Python class 实例?
How to copy a Python class instance if deepcopy() does not work?
我想在 python 中制作一个 class 实例的副本。我试过 copy.deepcopy
但我收到错误消息:
RuntimeError: Only Variables created explicitly by the user (graph leaves) support the deepcopy protocol at the moment
所以假设我有这样的东西:
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
做一个完全相同的深拷贝,有没有简单的方法?
是的,您可以使用 deepcopy 复制 class 实例:
from copy import deepcopy
c = C(4,5,'r'=2)
d = deepcopy(c)
这会在 'd' 中创建 class 实例 'c' 的副本。
一种方法是在 C
Class 中实现 __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))
我已经基本弄明白了。我无法克服的唯一问题是知道所有 classes 的一组可接受的初始化参数(__init__
的参数)。所以我必须做出以下两个假设:
1) 我有一组 class C
的默认参数,我称之为 argsC
。
2) C
中的所有对象都可以用空参数初始化。
这样的话我可以
首先:
从我要复制的实例 class 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
,但对于会引发异常的情况,它可以不使用。
我想在 python 中制作一个 class 实例的副本。我试过 copy.deepcopy
但我收到错误消息:
RuntimeError: Only Variables created explicitly by the user (graph leaves) support the deepcopy protocol at the moment
所以假设我有这样的东西:
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
做一个完全相同的深拷贝,有没有简单的方法?
是的,您可以使用 deepcopy 复制 class 实例:
from copy import deepcopy
c = C(4,5,'r'=2)
d = deepcopy(c)
这会在 'd' 中创建 class 实例 'c' 的副本。
一种方法是在 C
Class 中实现 __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))
我已经基本弄明白了。我无法克服的唯一问题是知道所有 classes 的一组可接受的初始化参数(__init__
的参数)。所以我必须做出以下两个假设:
1) 我有一组 class C
的默认参数,我称之为 argsC
。
2) C
中的所有对象都可以用空参数初始化。
这样的话我可以
首先:
从我要复制的实例 class 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
,但对于会引发异常的情况,它可以不使用。