使用python deepcopy时的AttributeError

问题描述 投票:3回答:1

我有一个类被__eq____hash__覆盖,以使其对象充当字典键。每个对象还带有一个字典,由同一类的其他对象键入。当我尝试AttributeError整个结构时,我得到一个奇怪的deepcopy。我在OsX上使用Python 3.6.0。

Python docs看起来好像deepcopy使用memo字典来缓存它已经复制的对象,因此嵌套结构应该不是问题。那我做错了什么?我应该编写自己的__deepcopy__方法来解决这个问题吗?怎么样?

from copy import deepcopy


class Node:

    def __init__(self, p_id):
        self.id = p_id
        self.edge_dict = {}
        self.degree = 0

    def __eq__(self, other):
        return self.id == other.id

    def __hash__(self):
        return hash(self.id)

    def add_edge(self, p_node, p_data):
        if p_node not in self.edge_dict:
            self.edge_dict[p_node] = p_data
            self.degree += 1
            return True
        else:
            return False

if __name__ == '__main__':
    node1 = Node(1)
    node2 = Node(2)
    node1.add_edge(node2, "1->2")
    node2.add_edge(node1, "2->1")
    node1_copy = deepcopy(node1)

File ".../node_test.py", line 15, in __hash__
    return hash(self.id)
AttributeError: 'Node' object has no attribute 'id'
python deep-copy cyclic-reference
1个回答
5
投票

当你:时,循环依赖是deepcopy的问题:

  1. 具有必须进行散列并包含引用循环的类,以及
  2. 不确保在对象构造时建立与哈希相关(和相关的相关)不变量,而不仅仅是初始化

问题是取消对象(deepcopy,默认情况下,通过pickle和unpickling复制自定义对象,除非定义了特殊的__deepcopy__方法)创建空对象而不初始化它,然后尝试逐个填充其属性。当它试图填写node1的属性时,它需要初始化node2,而node1又依赖于部分创建的edge_dict(在两种情况下都归因于edge_dict)。当时它试图填写一个NodeNode,它添加到edge_dictid还没有设置__new__属性,所以哈希它的尝试失败了。

你可以通过使用pickle来确定这一点,以确保在初始化可变的,可能是递归的属性之前建立不变量,并定义__getnewargs__帮助器__getnewargs_ex__(或class Node: # __new__ instead of __init__ to establish necessary id invariant # You could use both __new__ and __init__, but that's usually more complicated # than you really need def __new__(cls, p_id): self = super().__new__(cls) # Must explicitly create the new object # Aside from explicit construction and return, rest of __new__ # is same as __init__ self.id = p_id self.edge_dict = {} self.degree = 0 return self # __new__ returns the new object def __getnewargs__(self): # Return the arguments that *must* be passed to __new__ return (self.id,) # ... rest of class is unchanged ... )以使其正确使用它们。具体来说,将您的类定义更改为:

object

注意:如果这是Python 2代码,请确保明确继承super()并将super(Node, cls)更改为__new__中的copy.deepcopy;给出的代码是更简单的Python 3代码。

只处理__new__而不支持酸洗或需要使用__getnewargs__ / copy(需要新式类)的替代解决方案将仅覆盖深度复制。您将在原始类上定义以下额外方法(并确保模块导入def __deepcopy__(self, memo): # Deepcopy only the id attribute, then construct the new instance and map # the id() of the existing copy to the new instance in the memo dictionary memo[id(self)] = newself = self.__class__(copy.deepcopy(self.id, memo)) # Now that memo is populated with a hashable instance, copy the other attributes: newself.degree = copy.deepcopy(self.degree, memo) # Safe to deepcopy edge_dict now, because backreferences to self will # be remapped to newself automatically newself.edge_dict = copy.deepcopy(self.edge_dict, memo) return newself ),否则保持不变:

qazxswpoi
© www.soinside.com 2019 - 2024. All rights reserved.