deepcopy的第二个参数memo有什么用?

26 投票
4 回答
12465 浏览
提问于 2025-04-15 17:18
from copy import* 
a=[1,2,3,4]
c={'a':'aaa'}
print c
#{'a': 'aaa'}
b=deepcopy(a,c)
print b

print c
# print {'a': 'aaa', 10310992: 3, 10310980: 4, 10311016: 1, 11588784: [1, 2, 3, 4, [1, 2, 3, 4]], 11566456: [1, 2, 3, 4], 10311004: 2}

为什么C语言会这样打印

请尽量使用代码,而不是文字,因为我的英语不是很好,谢谢

在django.utils.tree.py文件中

def __deepcopy__(self, memodict):
        """
        Utility method used by copy.deepcopy().
        """
        obj = Node(connector=self.connector, negated=self.negated)
        obj.__class__ = self.__class__
        obj.children = deepcopy(self.children, memodict)
        obj.subtree_parents = deepcopy(self.subtree_parents, memodict)
        return obj



import copy
memo = {}
x1 = range(5)
x2=range(6,9)
x3=[2,3,4,11]
y1 = copy.deepcopy(x1, memo)
y2=copy.deepcopy(x2, memo)
y3=copy.deepcopy(x3,memo)
print memo
print id(y1),id(y2),id(y3)
y1[0]='www'
print y1,y2,y3
print memo

打印:

{10310992: 3, 10310980: 4, 10311016: 1, 11588784: [0, 1, 2, 3, 4, [0, 1, 2, 3, 4]], 10311028: 0, 11566456: [0, 1, 2, 3, 4], 10311004: 2}
{11572448: [6, 7, 8], 10310992: 3, 10310980: 4, 10311016: 1, 11572368: [2, 3, 4, 11], 10310956: 6, 10310896: 11, 10310944: 7, 11588784: [0, 1, 2, 3, 4, [0, 1, 2, 3, 4], 6, 7, 8, [6, 7, 8], 11, [2, 3, 4, 11]], 10311028: 0, 11566456: [0, 1, 2, 3, 4], 10310932: 8, 10311004: 2}
11572408 11581280 11580960
['www', 1, 2, 3, 4] [6, 7, 8] [2, 3, 4, 11]
{11572448: [6, 7, 8], 10310992: 3, 10310980: 4, 10311016: 1, 11572368: [2, 3, 4, 11], 10310956: 6, 10310896: 11, 10310944: 7, 11588784: [0, 1, 2, 3, 4, [0, 1, 2, 3, 4], 6, 7, 8, [6, 7, 8], 11, [2, 3, 4, 11]], 10311028: 0, 11566456: ['www', 1, 2, 3, 4], 10310932: 8, 10311004: 2}

4 个回答

7

你可以通过查看Python的在线文档来了解更多信息:

http://docs.python.org/library/copy.html

deepcopy()这个函数是递归的,它会逐层深入到一个复杂的对象中。它会用一个字典来记录它之前见过的对象,以防止出现无限循环。你可以完全忽略这个字典。

class A(object):
    def __init__(self, *args):
        self.lst = args

class B(object):
    def __init__(self):
        self.x = self

def my_deepcopy(arg):
    try:
        obj = type(arg)()  # get new, empty instance of type arg
        for key in arg.__dict__:
            obj.__dict__[key] = my_deepcopy(arg.__dict__[key])
        return obj
    except AttributeError:
        return type(arg)(arg)  # return new instance of a simple type such as str

a = A(1, 2, 3)
b = B()
b.x is b  # evaluates to True
c = my_deepcopy(a)  # works fine
c = my_deepcopy(b)  # stack overflow, recurses forever

from copy import deepcopy
c = deepcopy(b)  # this works because of the second, hidden, dict argument

只需忽略第二个隐藏的字典参数,不要尝试使用它。

18

上面没有人给出一个好的使用示例。

这是我做的:

def __deepcopy__(self, memo):
    copy = type(self)()
    memo[id(self)] = copy
    copy._member1 = self._member1
    copy._member2 = deepcopy(self._member2, memo)
    return copy

这里的 member1 是一个不需要深拷贝的对象(比如字符串或整数),而 member2 是需要深拷贝的对象,比如其他自定义类型、列表或字典。

我在处理复杂的对象关系时使用了上面的代码,效果非常好。

如果你还想让你的类可以被保存到文件(也就是可序列化),那么在 getstatesetstate 中并没有类似的 memo 参数,也就是说,pickle 系统会自动跟踪已经引用的对象,所以你不需要担心这个。

上述方法适用于你从中继承的 PyQt5 类(同时也支持序列化,比如我可以深拷贝或序列化一个自定义的 QMainWindow、QWidget、QGraphicsItem 等等)。

如果你的构造函数中有一些初始化代码会创建新对象,比如一个 CustomWidget(QWidget) 会创建一个新的 CustomScene(QGraphicsScene),但你想把场景从一个 CustomWidget 复制到另一个,那么一种方法是在你的 __init__ 中添加一个 new=True 参数,并这样写:

def __init__(..., new=True):
    ....
    if new:
       self._scene = CustomScene()

def __deepcopy__(self, memo):
    copy = type(self)(..., new=False)
    ....
    copy._scene = deepcopy(self._scene, memo)
    ....

这样可以确保你不会重复创建 CustomScene(或者其他需要大量初始化的大类)!你还应该在你的 __setstate__ 方法中使用相同的设置(new=False),例如:

 def __setstate__(self, data):
     self.__init__(...., new=False)
     self._member1 = data['member 1']
     .....

还有其他方法可以解决这个问题,但这是我最终选择并经常使用的方法。

我为什么要提到序列化呢?因为在任何应用中你通常都会需要这两者,并且你需要同时维护它们。如果你给你的类添加一个成员,你就需要在 setstate、getstate 和深拷贝的代码中都添加它。我建议你为每个新类创建这三个方法,如果你打算在应用中进行复制/粘贴和文件保存/加载。另一种选择是使用 JSON 并自己处理保存/加载,但那样你需要做更多的工作,包括记忆化。

所以为了支持以上所有功能,你需要 __deepcopy__, __setstate__, 和 __getstate__ 方法,并且要导入深拷贝:

from copy import deepcopy

当你编写你的 pickle 加载/保存函数时(也就是调用 pickle.load()/ pickle.dump() 来加载/保存你的对象层次结构/图形),最好使用 import _pickle as pickle 来获得更快的速度(_pickle 是一个更快的 C 实现,通常与你的应用需求兼容)。

18

这是一个叫做 memo 的字典,它用来保存对象的 ID 和对象之间的对应关系,这样可以完美地重建复杂的对象结构。虽然“使用代码”有点难,但我们来试试:

>>> import copy
>>> memo = {}
>>> x = range(5)
>>> y = copy.deepcopy(x, memo)
>>> memo
{399680: [0, 1, 2, 3, 4], 16790896: 3, 16790884: 4, 16790920: 1,
 438608: [0, 1, 2, 3, 4, [0, 1, 2, 3, 4]], 16790932: 0, 16790908: 2}
>>> 

还有

>>> id(x)
399680
>>> for j in x: print j, id(j)
... 
0 16790932
1 16790920
2 16790908
3 16790896
4 16790884

所以你可以看到这些 ID 是完全正确的。此外:

>>> for k, v in memo.items(): print k, id(v)
... 
399680 435264
16790896 16790896
16790884 16790884
16790920 16790920
438608 435464
16790932 16790932
16790908 16790908

你可以看到(不可变的)整数的身份。

这里有一个图:

>>> z = [x, x]
>>> t = copy.deepcopy(z, memo)
>>> print id(t[0]), id(t[1]), id(y)
435264 435264 435264

所以你可以看到所有的子副本和 y 是同一个对象(因为我们重用了 memo)。

撰写回答