在类实例字典中存储对象具有意外结果

时间:2015-09-29 23:33:15

标签: python python-2.7 oop

提前为漫长的帖子道歉,并感谢任何有时间看一看的人。完成后的完整工作示例。

我想帮助理解代码的行为。我写了两个简单的面向图的类,一个用于节点,另一个用于图本身。该图有一个字典,用于根据节点indexself.nodes跟踪节点的实例,节点保留邻居列表self.neighbors(这些self's用于{分别为{1}}和Graph

奇怪的是,我总是可以通过Node实例Graph字典获得节点邻居的完整列表,但如果我试图通过另一个节点访问节点来获取邻居的邻居节点的nodes列表,我经常得到一个没有邻居的节点,显示不正确的信息。例如,一旦我读入并处理图形,我就可以通过调用neighbors实例的Graph来完美地打印出每个节点及其邻居,这给出了一个示例图:

listNodes()

因此,当我直接从图形实例中的self.nodes字典访问节点时,我可以访问节点的邻居。但是,我无法通过节点的邻居列表访问节点邻居的邻居。例如,当我运行(i = 1, neighbors: 5 2 4 3) (i = 2, neighbors: 1) (i = 3, neighbors: 1 8 9) (i = 4, neighbors: 1 9 6) (i = 5, neighbors: 1) (i = 6, neighbors: 4 7) (i = 7, neighbors: 6) (i = 8, neighbors: 3) (i = 9, neighbors: 4 3) 时,执行方式如下:

printNeighborsNeighbors(3)

这是输出:

def printNeighborsNeighbors(self, start_num):
    node = self.nodes[start_num]
    print(node.neighbors)

这表明节点1和9具有零邻居,但这完全错误。该图如下所示:

graphImage

以下是邻居输入的顺序:

[(i = 1, neighbors: ), (i = 8, neighbors: 3), (i = 9, neighbors: )]

以下是类实现:

5 1
1 2
1 4
1 3
3 8
4 9
3 9
4 6
6 7

class Node:   
    def __init__(self, i):
        self.index = i
        self.neighbors = []

    def createNeighbor(self, neighbor):
        self.neighbors.append(neighbor)

    def __str__(self):
        neighbors = [str(n.index) for n in self.neighbors]
        return "(i = %d, neighbors: %s)"%(self.index, " ".join(neighbors))

    def __repr__(self):
        return str(self)

这就是我的想法:

  • 这不仅仅与每行输入文本文件左右排序有关,因为class Graph: def __init__(self): self.nodes = defaultdict(lambda: False) def neighborNodes(self, node, neighbor): if not self.nodes[node.index]: self.nodes[node.index] = node if not self.nodes[neighbor.index]: self.nodes[neighbor.index] = neighbor self.nodes[node.index].createNeighbor(neighbor) self.nodes[neighbor.index].createNeighbor(node) def printNeighborsNeighbors(self, start_num): node = self.nodes[start_num] print(node.neighbors) for n in node.neighbors: print(n.neighbors) def listNodes(self): for node in self.nodes.values(): print(node) 有两个“坏”邻居(信息丢失),一个输入为{{1} }另一个输入为3
  • 就行排序而言,这并不仅仅与文本文件输入有关,因为3的1 3邻居在一个3 9邻居之前输入但是在另一个{{1}之后输入邻居。
  • 当我运行good时,badbad正确列出了他们的邻居,但printNeighborsNeighbors(4)没有列出任何内容。所以这似乎是一个全有或全无的错误。你要么拥有所有真正的邻居,要么你根本没有邻居名单。这部分是最令人困惑的。这不是覆盖对象的问题,这更像是某种C ++风格的对象切片。

我总是可以通过图表字典轻松解决这个问题,但我想知道这里发生了什么。好像我误解了Python如何处理这些对象的重要事情。

感谢您对尝试的内容进行任何更正或建议。

按照MK的建议,这是一个有效的例子:

input.txt中

9

我刚刚运行了这个.py所以它应该可以工作:

6

1 个答案:

答案 0 :(得分:3)

问题在于您通过索引强制执行节点对象的唯一性。当调用neighborNodes()方法时,它会获得一个新创建的Node实例,只有在需要时才会添加到self.nodes(),这是"正确":它只会记录每个索引的一个Node实例。但是你仍然在创建一个新的Node实例,除非你首先将它传递给Node.createNeighbor()方法,并且该丢弃实例被记录为节点的邻居。结果,只记录了邻居关系的一个方向。

这是一个可能的解决方案:

if not self.nodes[node.index]:
    self.nodes[node.index] = node
else:
    node = self.nodes[node.index]

if not self.nodes[neighbor.index]:
    self.nodes[neighbor.index] = neighbor
else:
    neighbor = self.nodes[neighbor.index]

但我不喜欢它。实际上,您需要更改它以停止创建一次性实例,这对内存,性能,可读性和正确性都有好处。 你可以在Graph中添加一个名为getNode(n)的方法,如果它已经存在,它将返回一个节点对象,如果它还不存在,则创建一个新的(并注册)一个新的节点对象。然后你会使Node构造函数私有(可能没办法在Python中这样做),除了Graph之外没有其他人可以创建它们。