deepcopy的第二个参数memo的目的是什么?

时间:2009-12-23 03:37:56

标签: python

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 个答案:

答案 0 :(得分:8)

这是memo dict,其中保持id-to-object对应以完美地重建复杂的对象图。很难“使用代码”,但是,让我们试试:

>>> 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是相同的对象(因为我们重用了备忘录)。

答案 1 :(得分:4)

您可以通过查看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

忽略第二个隐藏的dict参数。不要试图使用它。

答案 2 :(得分:4)

上面没有人举例说明如何使用它。

这就是我的所作所为:

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就像其他自定义类型或列表或字典一样。

我在高度纠结的对象图上使用了上述代码,效果非常好。

如果你还想使你的类可以选择(文件保存/加载),那么 getstate / setstate 就没有类似的备忘录参数,换句话说就是泡菜系统以某种方式跟踪已经引用的对象,因此您不必担心。

上面的内容适用于您继承的PyQt5类(以及酸洗 - 例如我可以深度查看或挑选自定义QMainWindow,QWidget,QGraphicsItem等)

如果构造函数中存在一些创建新对象的初始化代码,例如创建新CustomScene(QGraphicsScene)的CustomWidget(QWidget),但您想要将场景从一个CustomWidget腌制或复制到一个新的,然后一种方法是在new=True中创建__init__参数并说:

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(或者进行大量初始化的大型类)!您还应该在new=False方法中使用相同的设置(__setstate__),例如:

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

还有其他方法可以解决上述问题,但这是我经常收集和使用的方法。

为什么我也谈论酸洗?因为您通常需要在任何应用程序中,并且同时维护它们。如果您向类添加成员,则将其添加到setstate,getstate, deepcopy代码。我会规定,对于您创建的任何新类,如果您计划在应用程序中复制/粘贴文件保存/加载,则创建上述三种方法。另一种选择是JSON并自己保存/加载,但是还有很多工作要做,包括记忆。

所以为了支持上述所有方法,您需要__deepcopy__, __setstate__, and __getstate__个方法并导入deepcopy:

from copy import deepcopy

,当您编写pickle loader / saver函数(在其中调用pickle.load()/ pickle.dump()来加载/保存对象层次结构/图表时),以import _pickle as pickle获得最佳速度(_pickle是一些更快的C impl,通常与您的应用程序要求兼容)。

答案 3 :(得分:1)

以下是我用来向自己解释的快速插图:

a = [1,2,3]
memo = {}
b = copy.deepcopy(a,memo)
# now memo = {139907464678864: [1, 2, 3], 9357408: 1, 9357440: 2, 9357472: 3, 28258000: [1, 2, 3, [1, 2, 3]]}

key = 139907464678864
print(id(a) == key)               #True
print(id(b) == key)               #False
print(id(a) == id(memo[key]))     #False
print(id(b) == id(memo[key]))     #True
换句话说,

memo[id_of_initial_object] = copy_of_initial_object