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:

graphimage

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 input 1 3 , other input 3 9
  • this not solely relate text file input far line-ordering goes because good neighbor 3 input before 1 bad neighbor of 3 after other bad neighbor.
  • when run printneighborsneighbors(4), 9 , 6 have neighbors listed correctly 1 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

Popular posts from this blog

html - Outlook 2010 Anchor (url/address/link) -

javascript - Why does running this loop 9 times take 100x longer than running it 8 times? -

Getting gateway time-out Rails app with Nginx + Puma running on Digital Ocean -