I'm trying to make a model where nodes connect to one and other using OOP. I'd like each node to store the names of its inputs and connections within lists and have these lists as attributes. The code seems to function perfectly outside of the "connect" function.
It creates desired Connection object, but it creates erroneous attributes for the destination node and origin nodes. The desired attributes are:
A.inputs = [],
A.outputs = [B]
B.inputs = [A]
B.outputs = [0]
But instead of that I get:
A.inputs = [A]
A.outputs = [B}
B.inputs = [A]
B.outputs = [B}
CONNECTIONS = []
NODES = []
class Zone:
def __init__(self, height, width):
self.height = height
self.width = width
class Node:
def __init__(self, name, initial_activation, activation_function = "linear", inputs = [], outputs = [], location = (0,0)):
global NODES
self.name = name
self.activation = initial_activation
self.activation_function = activation_function
self.inputs = inputs
self.outputs = outputs
self.location = location
NODES.append(self)
def __repr__(self):
return(f"Node {self.name}")
class Connection:
def __init__(self, origin, destination):
self.origin = origin.name
self.destination = destination.name
def __repr__(self):
return(f"Connection from {self.origin} to {self.destination}")
def connect(origin, destination):
new_connection = Connection(origin, destination)
origin.outputs.append(destination.name)
destination.inputs.append(origin.name)
global CONNECTIONS
CONNECTIONS.append(new_connection)
A = Node("A",0)
B = Node("B", 0, location = (100,100))
A.__dict__ # verify it is correct before hand
B.__dict__
test_connection = Connection(A,B)
test_connection.__dict__
connect(A,B) # create connection
A.__dict__ # show erroneous result
B.__dict__
CONNECTIONS
CONNECTIONS[0].__dict__ # verify connection object is correct
I have tried to debug using print statements, but to no avail. I believe the problem is found within lines 33 and 34 but I cannot see an error in those lines.
I see that Node class has mutable default arguments values for variables inputs and outputs.
In code, you don't pass values for this argument so A.input refers to same list as B.input.
print(id(A.inputs) == id(B.inputs))
will print True.
You need to get rid off mutable default argument, f.e doing this:
class Node:
def __init__(self, name, initial_activation, activation_function="linear",
inputs=None, outputs=None, location=(0, 0)):
# Some code
self.inputs = inputs or []
self.outputs = outputs or []
Related
Hi I'm having a problem in this classes I created the parent class extracao_nia with the method aplica_extracao for having the similar part of the execution that I use in others class and the diferent part is in the transform method definined in the children class
but I'm having an issue that the variables that I defined as list() are Null variable when I execute the code:
AttributeError: 'NoneType' object has no attribute 'append'
class extracao_nia:
def __init__(self, d=1, h=1, m=15):
self._data_base = "database"
self.UM_DIA = datetime.timedelta(days=d)
self.UMA_HORA = datetime.timedelta(hours=h)
self.INTERVALO = datetime.timedelta(minutes=m)
#property
def data_base(self):
return self._data_base
def aplica_extracao(self, SQL):
fim_intervalo = self.inicio + self.INTERVALO#
pbar = self.cria_prog_bar(SQL)#
while (fim_intervalo <= self.FIM):#
self.connector.execute(SQL,(self.inicio.strftime('%Y-%m-%d %H:%M'),fim_intervalo.strftime('%Y-%m-%d %H:%M')))#
for log in self.connector:#
self.transforma(log)
self.inicio = fim_intervalo
fim_intervalo = self.inicio + self.INTERVALO
class usuarios_unicos(extracao_nia):
def __init__(self, d=1, h=1, m=15, file='nodes.json'):
self._data_base = "database"
self.UM_DIA = datetime.timedelta(days=d)
self.UMA_HORA = datetime.timedelta(hours=h)
self.INTERVALO = datetime.timedelta(minutes=m)
self.file = file
self.ids = list()
self.nodes = list()
self.list_cpf = list()
def transforma(self, log):
context = json.loads(log[0])['context']
output = json.loads(log[0])['output']
try:
nr_cpf = context['dadosDinamicos']['nrCpf']
conversation_id = context['conversation_id']
nodes_visited = output['output_watson']['nodes_visited']
i = self.ids.index(conversation_id)
atual = len(self.nodes[i])
novo = len(nodes_visited)
if novo > atual:
nodes[i] = nodes_visited
except KeyError:
pass
except ValueError:
self.ids.append(conversation_id)
self.nodes = self.nodes.append(nodes_visited)
self.list_cpf = self.list_cpf.append(nr_cpf)
list.append returns None since it is an in-place operation, so
self.nodes = self.nodes.append(nodes_visited)
will result in self.nodes being assigned None. Instead you can just use
self.nodes += nodes_visited
I wrote a Node class and created three objects, when I'm assigning a value of a variable in the 1st object, the same variable of 2nd object is getting updated, same for the third object. Here's the code,
class Node:
nodes_neighbour = []
nodes_neighbour_loc = []
def __init__(self,location):
self.location = location
def addNeighbours(self,neighbours):
i = 0
while(i < len(neighbours)):
self.nodes_neighbour.append(neighbours[i])
self.nodes_neighbour_loc.append(neighbours[i].location)
i = i + 1
def setCosts(self,g,f):
self.g = g
self.f = f
n1 = Node([10,10])
n2 = Node([50,10])
n3 = Node([90,10])
n1.addNeighbours([n2,n3])
print(n2.nodes_neighbour_loc)
and it's printing,
[[50, 10], [90, 10]]
What's the problem??
Thanks in advance :)
Your members nodes_neighbour and nodes_neighbour_loc are instances of the class and are shared. You probably wanted this:
class Node:
def __init__(self,location):
self.location = location
self.nodes_neighbour = []
self.nodes_neighbour_loc = []
I made a class to work with Heating wires:
class Heating_wire:
def __init__(self, ro, L,d,alpha):
self.ro = ro
self.L = L
self.d = d
self.alpha = alpha
self.RT = [1]
self.vector_T = [1]
def get_R20(self):
self.R_20 = self.ro*self.L/(np.pi*(self.d/2)**2)
def calcular_RT(self,vector_temp):
self.vector_T = vector_temp
self.RT = [self.R_20*(1 + temp*self.alpha) for temp in vector_temp ]
return self.RT
instantiate some objects:
kantal = Heating_wire(1.45,0.25,0.3,4e-5)
nicromo = Heating_wire(1.18,0.25,0.3,0.0004)
ferroniquel = Heating_wire(0.86,0.25,0.3,9.3e-4)
wires = [kantal,nicromo,ferroniquel]
And made a plot:
leg = []
vector_temp = np.linspace(20,1000,1000)
for wire in sorted(wires):
wire.get_R20()
wire.get_RT(vector_temp)
line, = plt.plot(wire.vector_T,wire.RT)
leg.append(line)
plt.legend(leg,sorted(wires))
The issue is that I'm not getting the right names in the legend but the reference to the objects:
If I add a name attribute
def __init__(self,name, ro, L,d,alpha):
self.name = name
I can append the names
leg = []
names= []
vector_temp = np.linspace(20,1000,1000)
for wire in sorted(wires):
wire.get_R20()
wire.get_RT(vector_temp)
line, = plt.plot(wire.vector_T,wire.RT)
leg.append(line)
names.append(wire.name)
plt.legend(leg,names,loc='best')
But I wonder if there is a simpler way t solve this using directly the names of the objects in the list of wires:
kantal = Heating_wire(1.45,0.25,0.3,4e-5)
nicromo = Heating_wire(1.18,0.25,0.3,0.0004)
ferroniquel = Heating_wire(0.86,0.25,0.3,9.3e-4)
wires = [kantal,nicromo,ferroniquel]
Just do it like this and there's no duplication:
wires = [
Heating_wire("kantal", 1.45,0.25,0.3,4e-5),
Heating_wire("nicromo", 1.18,0.25,0.3,0.0004),
Heating_wire("ferroniquel", 0.86,0.25,0.3,9.3e-4)
]
To answer your question, no, objects cannot access the names they were given.
I'm trying to rewrite a script and I'm stuck on making it easy to use. Basically it's an assembly script (like the reverse of destruction), where you input a load of variables such as location, whether the location is absolute or relative, scale, rotation, visibility, random offset, etc, to create an animation. The first version was very non user friendly, so I'm trying to get it working nicely from the start this time.
I've thought of how I'd like it to work, and I've managed to keep it clean, but there is a flaw. As you can see below, it'd be possible to use anything like SetGroup.frame[i].save(), which I don't want (and I don't want to put checks on if name is None throughout the class).
Here is the code I have:
class SetGroup(object):
def __init__(self, name=None, _frame_only=False):
if name is None and not _frame_only:
raise TypeError('name of group must be provided')
self.selection = None
self.origin = None
self.start = None
self.end = None
self.offset = 0
self.distance = None
self.random = 0
self.location = None
self.rotation = None
self.scale = None
self.visibility = None
if not _frame_only:
self.frame = defaultdict(lambda: SetGroup(_frame_only=True))
def save(self):
self.load()
#do a bit of error checking here
self.data[self.name] = {'ObjectSelection': self.selection,
'ObjectOrigin': self.origin,
'FrameStart': self.start,
'FrameEnd': self.end,
'FrameOffset': self.offset,
'FrameDistance': self.distance,
'FrameRandom': self.random,
'StartLocation': self.location,
'StartRotation': self.rotation,
'StartScale': self.scale,
'StartVisibility': self.visibility,
'ExtraFrames': self.frame}
pm.fileInfo['AssemblyScript'] = StoreData().save(self.data)
def load(self):
try:
self.data = StoreData().load(pm.fileInfo['AssemblyScript'])
except KeyError:
pm.fileInfo['AssemblyScript'] = StoreData().save({})
The way I'd like it to work is like this:
a = SetGroup('test')
a.location = ((0, 0, 0), True)
a.start = 0
a.end = 10
a.frame[5].location = ((10, 10, 10), False)
a.frame[5].scale = ((2, 1, 1), True)
a.save()
Unless anyone can think of a way which would make it more friendly to use, how would I separate location, rotation, scale, and visibility into another class and link them up again, so that they still work at the core level of the class, but also work for the frame dictionary too?
Edit - Got it working to a basic level:
class _MovementInfo(object):
def __init__(self, location=None, rotation=None, scale=None, visibility=None):
self.location = location
self.rotation = rotation
self.scale = scale
self.visibility = visibility
def __repr__(self):
return '_MovementInfo(location={x.location}, rotation={x.rotation}, scale={x.scale}, visibility={x.visibility}'.format(x=self)
Then I used this in the main class to merge the dictionaries:
self.__dict__.update({k: v for k, v in _MovementInfo().__dict__.iteritems() if '__' not in k})
self.frame = defaultdict(_MovementInfo)
I would change the code like this:
class SetGroup(_Movement):
def __init__(self, name=None):
if name is None:
# ...
super().__init__()
# ...
self.random = 0 # __init__ should end here
# ...
But you should check that all _MovementInfo's in all frames are _MovementInfo's or have inherited from them (to check this: isinstance(x, _MovementInfo)), but are not SetGroup's (to check this: not isinstance(x, SetGroup)).
super() is short for super(SetGroup, self) (you have to use the last option for python2), and is basicly an object that holds all things that the base class has, and allows you to call methods that modify the class calling it.
Or in code:
class A(object):
def __init__(self, y):
self.x = 2
self.y = y
class B(A):
def __init__(self, y, z):
super().__init__(y) # equivalent to: A.__init__(self, y)
self.z = z
b = B(3, 4)
# b's x is 2, b's y is 3 (both set by A.__init__, the last one was passed by B), and b's z is 4 (set by B.__init__)
I hope this helped,
CodenameLambda
I have a custom node class in python that is built into a graph (which is a dictionary). Since these take a while to create, I'd like to pickle them so that I don't have to reconstruct them everytime I run my code.
Unfortunately, because this graph has cycles, cPickle hits the maximum recursion depth:
RuntimeError: maximum recursion depth exceeded while pickling an object
This is my node object:
class Node:
def __init__(self, name):
self.name = name
self.uid = 0
self.parents = set()
self.children = set()
def __hash__(self):
return hash(self.name)
def __eq__(self, that):
return self.name == that.name
def __str__(self):
return "\n".join(["Name: " + self.name,
"\tChildren:" + ", ".join([c.name for c in self.children]),
"\tParents:" + ", ".join([p.name for p in self.parents])
]
)
This is how I build my graph:
def buildGraph(input):
graph = {}
idToNode = {}
for line in input:
## Input from text line by line looks like
## source.node -> target.node
source, arr, target = line.split()
if source in graph:
nsource = graph[source]
else:
nsource = Node(source)
nsource.uid = len(graph)
graph[source] = nsource
idToNode[nsource.uid] = nsource
if target in graph:
ntarget = graph[target]
else:
ntarget = Node(target)
ntarget.uid = len(graph)
graph[target] = ntarget
idToNode[ntarget.uid] = ntarget
nsource.children.add(ntarget)
ntarget.parents.add(nsource)
return graph
Then in my main, I have
graph = buildGraph(input_file)
bo = cPickle.dumps(graph)
and the second line is where I get my recursion depth error.
Are there any solutions outside of changing the structure of Node?
You need to prepare the object for pickle: if you have a cycle you need to break cycles and store this information in some other form.
Pickle use methods __getstate__ to prepare object to pickle (it call before) and __setstate__ to initialize object.
class SomethingPickled(object):
## Compress and uncycle data before pickle.
def __getstate__(self):
# deep copy object
state = self.__dict__.copy()
# break cycles
state['uncycled'] = self.yourUncycleMethod(state['cycled'])
del state['cycle']
# send to pickle
return state
## Expand data before unpickle.
def __setstate__(self, state):
# restore cycles
state['cycle'] = self.yourCycleMethod(state['uncycled'])
del state['uncycle']
self.__dict__.update(state)
I am sure than you will find idea how to break and join cycles :)
I don't think that the fact that your graph is cyclic is the problem -- pickle (and cPickle) should handle cyclic data structures just fine. I tried the following (with your definition of Node) and it worked just fine:
>>> n1 = Node('a')
>>> n2 = Node('b')
>>> n1.parents.add(n2)
>>> n2.parents.add(n1)
>>> n2.children.add(n1)
>>> n1.children.add(n1)
>>> import cPickle as pickle
>>> pickle.dumps(n1)
Indeed, even with large cycles I didn't run into a problem. E.g., this works fine for me:
>>> def node_cycle(n):
... start_node = prev_node = Node('node0')
... for i in range(n):
... node = Node('node%d' % (i+1))
... node.parents.add(prev_node)
... prev_node.children.add(node)
... prev_node = node
... start_node.parents.add(node)
... node.children.add(start_node)
>>> cycle = node_cycle(100000) # cycle of 100k nodes
>>> pickle.dumps(cycle)
(This was all tested on Python 2.7.1)
There are other reasons why pickle might end up with very deep recursion though, depending on the shape of your data structure. If this is the real problem, then you might be able to fix it with something like this:
>>> import sys
>>> sys.setrecursionlimit(10000)
Here, this modified node class holds only the names of the objects as strings in a node, and gives you a set with full "Node" objects when you retrieve either the "children" or the "parents" attribute of a node.
Internally there are no cycles - so it should avoid the infinity loop trap.You can implement additional auxiliar methods to ease navigation as you want.
class Node(object):
all_nodes = {}
def __new__(cls, name):
self = object.__new__(cls)
cls.all_nodes[name] = self
return self
def __getstate__(self):
self.all_nodes = self.__class__.all_nodes
return self.__dict__
def __setstate__(self, dct):
self.__class__.all_nodes = dct["all_nodes"]
del dct["all_nodes"]
self.__dict__ = dct
def __init__(self, name):
#self.all_nodes = self.__class__.all_nodes
self.name = name
self.uid = 0
self._parents = set()
self._children = set()
def __hash__(self):
return hash(self.name)
def __eq__(self, that):
return self.name == that.name
def __repr__(self):
return "\n" + "\n".join(["Name: " + self.name,
"\tChildren:" + ", ".join([c.name for c in self.children]),
"\tParents:" + ", ".join([p.name for p in self.parents])
]
)
def get_relations(self, which):
names = getattr(self, which)
return set(self.__class__.all_nodes[name] for name in names)
#property
def children(self):
return self.get_relations("_children")
#property
def parents(self):
return self.get_relations("_parents")
def __contains__(self, item):
return item.name in self._children
def add(self, child):
self._children.add(child.name)
child._parents.add(self.name)
connect_child = add
#example and testing:
from cPickle import loads, dumps
n1 = Node("n1")
n2 = Node("n2")
n3 = Node("n3")
n1.add(n2)
n2.add(n3)
n3.add(n1)
print n1, n2, n3
p1 = dumps(n1)
Node.all_nodes.clear()
p2 = loads(p1)
print p2
print p2.children
print p2.children.pop().children
print Node.all_nodes
The drawback is that it maintains a class dictionary named "all_nodes" where there are references to all actually created nodes. (Pickle is smart enough to only pickle this dictionary once for a given graph, since it is referenced by all Node objects) .
The problem with the class wide "all_nodes" reference is if you need to pickle and unpickle different sets of graphs 9let say you do create graphs g1 with a set of nodes, in another run, create a graph g2 with another set of nodes, and then if you unpickle g1, and later g2, the unpickling of g2 will override the node references for g1). If you need this to work, ask in a comment and I could come up with something - the easiser thing I can think off is having a "graph" class that will hold a dictionary to all the nodes (insteadof having it in the Node class)