python - Storing objects in class instance dictionary has unexpected results -
apologies in advance lengthy post , has time take look. complete working example @ end of post.
i understanding behavior of code. wrote 2 simple graph-oriented classes, 1 nodes , 1 graph itself. graph has dictionary track instance of node according index
, self.nodes
, node keeps list of neighbors, self.neighbors
(these self's
graph
, node
respectively).
what's strange can complete list of node's neighbors going through graph
instance nodes
dictionary, if try neighbors of neighbors accessing node through node's neighbors
list, node no neighbors, showing incorrect information. example, once read in , process graph, can print out each node , neighbors perfectly, calling graph
instances's listnodes()
, gives me 1 example graph:
(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)
so can access neighbors of node when access directly self.nodes dictionary in graph instance. however, cannot access neighbors of node's neighbors via node's list of neighbors. example when run printneighborsneighbors(3)
, implemented so:
def printneighborsneighbors(self, start_num): node = self.nodes[start_num] print(node.neighbors)
here output:
[(i = 1, neighbors: ), (i = 8, neighbors: 3), (i = 9, neighbors: )]
this indicates node 1 , 9 have 0 neighbors, that's entirely wrong. graph looks this:
here ordering input of neighbors:
5 1 1 2 1 4 1 3 3 8 4 9 3 9 4 6 6 7
here class implementations:
class node: def __init__(self, i): self.index = self.neighbors = [] def createneighbor(self, neighbor): self.neighbors.append(neighbor) def __str__(self): neighbors = [str(n.index) n in self.neighbors] return "(i = %d, neighbors: %s)"%(self.index, " ".join(neighbors)) def __repr__(self): return str(self)
and
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) n in node.neighbors: print(n.neighbors) def listnodes(self): node in self.nodes.values(): print(node)
here's i'm thinking:
- this not solely relate input text file left-right ordering on per-line basis because
3
has 2 "bad" neighbors (where info lost) , 1 input1 3
, other input3 9
- this not solely relate text file input far line-ordering goes because
good
neighbor 3 input before 1bad
neighbor of 3 after otherbad
neighbor. - when run
printneighborsneighbors(4)
,9
,6
have neighbors listed correctly1
has nothing listed. seems all-or-nothing error. either you've got true neighbors or don't have list of neighbors @ all. part confusing. it's not matter of overwriting object, feels more kind of c++ style object slicing.
i can around problem going through graph dictionary, i'd know what's going on here. seems misunderstanding important how python handles these objects.
thanks corrections or suggestions of try.
following mk's suggestion here's working example:
input.txt
1 9 9 5 1 1 2 1 4 1 3 3 8 4 9 3 9 4 6 6 7 8
and ran .py should work:
import copy collections import defaultdict class node: def __init__(self, i): self.index = self.neighbors = [] self.currentpath = [] def createneighbor(self, neighbor): self.neighbors.append(neighbor) def __str__(self): neighbors = [str(n.index) 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): node in self.nodes.values(): print(node) f = open('input.txt', 'r') t = int(f.readline()) _ in range(t): graph = graph() n, m = f.readline().split() n = int(n) m = int(m) _ in range(m): x, y = f.readline().split() x = int(x) y = int(y) nodex = node(x) nodey = node(y) graph.neighbornodes(nodex, nodey) s = int(f.readline()) print("running graph.listnodes") graph.listnodes() print("running print neighbors neighbors") graph.printneighborsneighbors(4)
the problem enforcing uniqueness of node objects index. when neighbornodes() method called gets newly created node instance add self.nodes() if needed, "correct": record 1 instance of node per index. still creating new instance of node throw away, except first pass node.createneighbor() method , throw-away instance gets recorded node's neighbor. result 1 direction of the neighborhood relationship recorded.
here 1 possible fix:
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]
but don't it. in reality need change stop creating throw-away instances, not memory, performance, readability , correctness. add method called getnode(n) graph, return node object if exists or create (and register) new 1 if doesn't exist yet. make node constructors private (probably no way in python) no 1 else graph can create them.
Comments
Post a Comment