【问题标题】:Python muliple deepcopy behaviorsPython 多重深拷贝行为
【发布时间】:2012-08-17 22:34:12
【问题描述】:

假设我有两个类,比如ManagerGraph,其中每个 Graph 都有对其管理器的引用,并且每个管理器都有对其拥有的图形集合的引用。我希望能够做两件事

1) 复制一个图,它执行深度复制,但新图引用与旧图相同的管理器。

2) 复制一个经理,这会创建一个新的经理并复制它拥有的所有图表。

最好的方法是什么?我不想推出自己的 deepcopy 实现,但标准的 copy.deepcopy 似乎无法提供这种级别的灵活性。

【问题讨论】:

  • 你必须自己动手。它需要一个基于规则的副本

标签: python deep-copy


【解决方案1】:

您可以通过查看传递给__deepcopy__ 特殊方法的memodict 轻松做到这一点:

class Graph(object):
    def __init__(self, manager=None):
        self.manager = None if manager is None else weakref.ref(manager)
    def __deepcopy__(self, memodict):
        manager = self.manager()
        return Graph(memodict.get(id(manager), manager))

class Manager(object):
    def __init__(self, graphs=[]):
        self.graphs = graphs
        for g in self.graphs:
            g.manager = weakref.ref(self)

我在这里假设您使用weakref.ref 来打破GraphManager 之间的循环;如果您正在使用其他东西,请酌情调整。

>>> m = Manager([Graph(), Graph()])
>>> mc = copy.deepcopy(m)
>>> [g.manager() is mc for g in mc.graphs]
[True, True]
>>> copy.deepcopy(m.graphs[0]).manager() is m
True

【讨论】:

  • 我只是使用普通参考。当您进行垃圾回收时,无需处理弱引用,尽管在这种情况下内存使用无论如何都不是问题。
  • @Antimony 哦,很好。好吧,它适用于普通参考;只需删除weakref.ref 并将.manager() 更改为.manager
  • 你有任何关于重载__deepcopy__的信息吗?文档似乎没有解释您实际上是如何做到的,只是说这是可能的。
  • @Antimony 将memodict 来自id 的输入实例传递给深度复制的实例,并且应该返回一个深度复制的实例。此外,如果您从 __deepcopy__ 中调用 deepcopy,您应该将 memodict 作为额外参数传播给 deepcopy
【解决方案2】:

如果图中没有引用其他对象(只是简单的字段),那么copy.copy(graph) 应该复制,而copy.deepcopy(manager) 应该复制管理器及其图,假设有一个列表,例如manager.graphs

但总的来说,您是对的,copy 模块没有这种灵活性,对于稍微花哨的情况,您可能需要自己动手。

【讨论】:

  • 对不起,图实际上是一个复杂的结构,有很多对象和循环引用。如果可以的话,我会使用浅拷贝。
【解决方案3】:

像定义类的复制协议这样非常简单的事情呢?

import copy

class Graph(object):

    def __init__(self):
        self.data = [1,2,3]
        self.manager = None

    def __getstate__(self):
        return {
            'data': self.data,
            'manager': self.manager
        }

    def __setstate__(self, state):
        self.manager = state.pop('manager')
        for name, val in state.iteritems():
            setattr(self, name, copy.copy(val))


class Manager(object):

    def __init__(self):
        self.data = [4,5,6]
        self.graphs = []

    def __getstate__(self):
        return {
            'data': self.data,
            'graphs': self.graphs
        }

    def __setstate__(self, state):
        self.graphs = [copy.copy(g) for g in state.pop('graphs')]
        for name, val in state.iteritems():
            setattr(self, name, copy.copy(val))

您只需将经理定义为参考,其他所有内容都可以按照您想要的方式复制。

例子:

In [2]: m1 = Manager()

In [3]: g1 = Graph()

In [4]: g1.manager = m1

In [5]: g2 = copy.copy(g1)

In [6]: g2.manager is g1.manager
Out[6]: True

In [7]: g2.data is g1.data
Out[7]: False

In [8]: m1.graphs.extend([g1,g2])

In [9]: m2 = copy.copy(m1)

In [10]: m2.data is m1.data
Out[10]: False

In [11]: m2.graphs[0] is m1.graphs[0]
Out[11]: False

In [12]: m2.graphs[0].manager is m1.graphs[0].manager
Out[12]: True

【讨论】:

    猜你喜欢
    • 2017-10-14
    • 2015-01-13
    • 2011-02-09
    • 1970-01-01
    • 2012-04-12
    • 1970-01-01
    • 2011-08-24
    • 2010-10-24
    • 2010-09-15
    相关资源
    最近更新 更多