I have created a dict which holds lists as values, when I change one of these values all values in the dict end up changing. I thought it would be a classic pointing issue, however, deepcopy didn't help. Plus I believed that hardcoded assignments always created new items in memory.
The creation of the dictionary simplified (deepcopy can also be removed and achieve same results):
self.connections= {}
self.connections['20000']= copy.deepcopy([None, None, []])
self.connections['20001']= copy.deepcopy([None, None, []])
After creation we later change a value like this:
if self.nodes[ip1].connections[port1][0]==None and self.nodes[ip2].connections[port2][0]==None:
self.nodes[ip1].connections[port1]= copy.deepcopy([ip2, port2, []])
self.nodes[ip2].connections[port2]= copy.deepcopy([ip1, port1, []])
When stepping through the full code as below, I can see that the individual change to one key value changes all values. The code is for a simulated cluster for me to experiment with some distributed control algorithms.
sim.py
#~ Info
__doc__ = 'Creates a simulation of a running cluster'
#~ Imports
from node import Node
import threading
import pygame
import math
import copy
#~ Sim
class Sim:
nodes= {}
mutex = threading.Lock()
screen= pygame.display.set_mode((500, 500))
clock= pygame.time.Clock()
def __init__(self):
# Init nodes
for i in range(3):
self.nodes['192.168.0.{}'.format(i)]= Node(self, '192.168.0.{}'.format(i))
def run(self):
# Run nodes in diffrent threads
# for k,d in self.nodes.items():
# t= threading.Thread(target=Node.run, args=(d,))
# t.setDaemon= True
# t.start()
for k,d in self.nodes.items():
d.run()
# Drawing
diff_angle= 2*math.pi/len(self.nodes.keys())
s_colour= (255, 255, 0)
e_colour= (255, 255, 255)
n_colour= (0, 0, 255)
def rotate(angle):
s = math.sin(angle)
c = math.cos(angle)
return (250 + 150*c - 0*s, 250 + 150*s + 0*c)
while True:
self.screen.fill((0,0,0))
# Draw connections
for k,d in self.nodes.items():
for selport,(tarip, tarport, buffer) in d.connections.items():
if tarip!=None and tarport!=None:
start= int(k.split('.')[-1])
end= int(tarip[0].split('.')[-1])
if tarport=='20000' or tarport=='20001':
pygame.draw.line(self.screen, s_colour, rotate(start*diff_angle), rotate(end*diff_angle), 4)
else:
pygame.draw.line(self.screen, e_colour, rotate(start*diff_angle), rotate(end*diff_angle), 4)
# Draw nodes
for k,d in self.nodes.items():
ind= int(k.split('.')[-1])
pygame.draw.circle(self.screen, n_colour, rotate(ind*diff_angle), 20)
pygame.display.update()
self.clock.tick(60)
for k,d in self.nodes.items():
print(d)
print()
def connect_node_to_node(self, ip1, port1, ip2, port2):
self.mutex.acquire(blocking=True)
# Check both ports are clear then connect them
try:
if self.nodes[ip1].connections[port1][0]==None and self.nodes[ip2].connections[port2][0]==None:
self.nodes[ip1].connections[port1]= copy.deepcopy([ip2, port2, []])
self.nodes[ip2].connections[port2]= copy.deepcopy([ip1, port1, []])
self.mutex.release()
return True
# If either port doesn't exist
except KeyError:
pass
self.mutex.release()
return False
if __name__=='__main__':
pygame.init()
sim= Sim()
sim.run()
node.py
#~ Info
__doc__ = 'Holds the class for simulated nodes'
#~ Imports
import copy
#~ Node
class Node:
# <sel port> : [<tar ip>, <tar port>, []]
connections= {}
def __init__(self, network, ip):
self.network= network
self.ip= ip
self.connections['20000']= copy.deepcopy([None, None, []])
def __str__(self):
line= 'Node {}: ['.format(self.ip)
for selport,(tarip, tarport, buffer) in self.connections.items():
if tarip!=None and tarport!=None:
line+= '{}-{}:{},'.format(selport, tarip, tarport)
else:
line+= 'Listening'
return line+']'
def run(self):
self.connections['20001']= copy.deepcopy([None, None, []])
# while True:
for i in range(3):
if self.network.connect_node_to_node(self.ip, '20001', '192.168.0.0', '20000'):
return True