I am somehow new to python. I needed to use tree to store some data (file paths), The problem is when I generate the tree it seems that all objects after the root reference the same object, although step by step debugging showed the opposite. Here is my (minimized) code:
first the node class:
class PathElement:
Element = ""
IsStatic = True
Children = []
ChildrenCount = 0
def __init__(self, Element, IsStatic=True):
self.Element = Element
self.IsStatic = IsStatic
if not IsStatic:
self.Element = []
def AddChild(self, Child):
print(self, " ", Child)
self.Children.append(Child)
self.ChildrenCount = len(self.Children)
return Child
The Children is list of PathElement nodes. The code that build the tree:
def UnFoldAndCheck(self):
Path = PathElement("root")
Handler = Path
Index = 0
Count = len(self.Path)
while Index < Count:
element = self.Path[Index]
if something:
Child = None
Child = PathElement(element)
Handler.AddChild(Child)
Handler = None #Those added to debug the problem
Handler = Child
elif other_thing:
if condition:
if some_large_condition:
ChildExec = None
ChildExec = PathElement(element, False)
for i in range(0, 5):
ChildExec.Element.append(self.Path[Index + i])
Handler.AddChild(ChildExec)
Handler = None
Handler = ChildExec
Index += 4
elif another_condition:
ChildOp = None
ChildOp = PathElement(element, False)
Handler.AddChild(ChildOp)
Handler = None
Handler = ChildOp
elif some_else_condition:
if condition:
ChildExec = None
ChildExec = PathElement(element, False)
for i in range(0, 3):
ChildExec.Element.append(self.Path[Index + i])
Handler.AddChild(ChildExec)
Handler = None
Handler = ChildExec
Index += 2
elif different_condition:
ChildExec = None
ChildExec = PathElement(element, False)
for i in range(0, 3):
ChildExec.Element.append(self.Path[Index + i])
Handler.AddChild(ChildExec)
Handler = None
Handler = ChildExec
Index += 1
Index += 1
return Path
My problem is that after the tree is built when I use it it will have always same structure:
root -> object with 3 exact nodes -> same object -> same object to infinity
while the expected is:
root -> object -> first children -> second children -> third children -> etc
I'm sure the problem is related to how python handle object references but I can't see where the problem exactly. Any help?
Update:
I reproduced the problem with smaller code (same class PathElement):
from PathElement import PathElement
Path = PathElement("root")
Handler = Path
for i in range(1,6):
Child = PathElement("child"+str(i))
Handler.AddChild(Child)
Handler = Child
Tree = Path
while True:
print(Tree.Element)
if len(Tree.Children) > 0:
Tree = Tree.Children[0]
else:
break
This code will make infinite loop
I guess you come from Java or a similar language. It's important to stick with Python's conventions (Jakob Sachs gave you va link to the Style Guide for Python Code) because that makes your mistakes are easier to identify.
Now, what's wrong here? When you wrote:
class PathElement():
Children = []
Element = ""
IsStatic = True
ChildrenCount = 0
You don't give the initial value of instance fields. You create an initialize class (static) fields. Hence, Children is a static field of the class PathElement. Here's a illustration of that:
class A():
i = []
a = A()
b = A()
a.i.append(1)
b.i.append(2)
assert a.i == b.i == [1,2]
What happens when you try to make read the leftmost part of the tree (child 0, child 0 of child 0, ...)?
while True:
print(Tree.Element)
if len(Tree.Children) > 0:
Tree = Tree.Children[0]
else:
break
Just replace Tree.Children by what it is really: PathElement.Children, that is the static field Children of the class PathElement:
while True:
print(Tree.Element)
if len(PathElement.Children) > 0:
Tree = PathElement.Children[0] # Tree has always the same value.
else:
break
Now, a example of what you can write:
class PathElement:
def __init__(self, element):
self.__element = element
self.__children = []
def add_child(self, child):
self.__children.append(child)
def children(self):
return list(self.__children)
def element(self):
return self.__element
path = ["a", "b", "c", "d", "e", "f"]
root = PathElement("root")
handler = root
while path:
child = PathElement(path.pop(0)) # you can put some conditions here, take more elements of path, ...
handler.add_child(child)
handler = child
def dfs(node):
for c in node.children():
yield c.element()
yield from dfs(c)
print (list(dfs(root)))
# a b c d e f
Related
I have below method where self contains a data structure as below
self.place = "India"
self.children = ["Tamil Nadu", "Karnataka"]
self.parent
Method
def get_node(self, value):
if value is None:
return self
if self.place == value:
return self
for node in self.children:
if node.place == value:
return node
elif len(node.children) > 0:
return node.get_node(value)
So via recursion, I am iterating on all possible child nodes to find the node I am looking for via return node.get_node(value) but I observed that, iteration happening via "Tamil Nadu" but not via "Karnataka".
I understood that, it took the first element of the list and then continued from there, but not coming back to 2nd element of the list.
is this expected behavior from recursion or am I doing something wrong ?
Full code( In case needed for testing)
class TreeNode:
def __init__(self, place):
self.place = place
self.children = []
self.parent = None
def add_child(self, child):
child.parent = self
self.children.append(child)
def print_tree(self):
prefix = ""
if self.parent is None:
print(self.place)
else:
prefix = prefix + (" " * self.get_level() * 3)
prefix = prefix + "|__"
print(prefix + self.place)
for child in self.children:
child.print_tree()
def get_level(self):
level = 0
p = self.parent
while p:
level = level + 1
p = p.parent
return level
def get_node(self, value):
if value is None:
return self
if self.place == value:
return self
for node in self.children:
if node.place == value:
return node
elif len(node.children) > 0:
return node.get_node(value)
def tree_map(self, nodes):
for node in nodes:
self.add_child(TreeNode(node))
def build_places():
root = TreeNode("Global")
india = TreeNode("India")
usa = TreeNode("USA")
root.add_child(india)
root.add_child(usa)
india_nodes = ["Gujarat" ,"Karnataka"]
gujarath_nodes = [ "Ahmedabad", "Baroda"]
karnataka_nodes = ["Bangalore", "Mysore"]
usa_nodes = ["New Jersey", "California"]
newjersey_nodes = ["Princeton", "Trenton"]
california_nodes = ["San Franciso", "Mountain View", "Palo Alto"]
for node in india_nodes:
india.add_child(TreeNode(node))
for node in usa_nodes:
usa.add_child(TreeNode(node))
gujarath_node = root.get_node("Gujarat")
print(gujarath_node.place)
for node in gujarath_nodes:
gujarath_node.add_child(TreeNode(node))
karnataka_node = root.get_node("Karnataka")
print(karnataka_node.place)
return root
if __name__ == "__main__":
root = build_places()
root.print_tree()
The problem is that in your loop you are always exiting the loop in its first iteration (when the node has at least some children). You should only exit on success, not when the recursive call comes back without success.
So change the loop to this:
for node in self.children:
if node.place == value:
return node
elif len(node.children) > 0:
result = node.get_node(value)
if result:
return result
Secondly, there is a strange base case you have at the start of this function. I would replace this:
if value is None:
return self
With:
if value is None:
return None
...since you didn't look for the value in that case: so then (in my opinion) it is not right to return a node instance (which might have any value -- you didn't verify it). It seems more consistent to return None or to remove this whole if block and not treat None in a special way.
I want to figure out how to print all nodes at a specific level. Right now, I can get to that level but I can only print out a part of the nodes. How would I get it to print all the nodes from all branches instead of nodes from one branch? I tried recursively calling get_level_nodes but it keeps outputting an error.
import random
class Node(object):
def __init__(self, value):
self.value = value
self.children = []
self.parent = None
def create_children(self, infects, depth):
# root node
if depth == 0:
return
for i in range(infects):
rand2 = random.random()
if rand2 <= 0.37:
if rand2 <= 0.02:
child = Node('NA')
else:
child = Node('CA')
else:
if rand2 <= 0.5:
child = Node('NS')
else:
child = Node('CS')
child.parent = self
child.grandparent = self.parent
self.children.append(child)
# recursive call to create more child nodes
child.create_children(infects, depth-1)
def tree_level(self):
level = 0
p = self.parent
while p:
level += 1
p = p.parent
return level
def print_tree(self):
spaces = ' ' * self.tree_level() * 2
prefix = spaces + '|__' if self.parent else ''
print(prefix + self.value, self.quarantined)
if self.children:
for child in self.children:
if child.value != None:
child.print_tree()
def get_level_nodes(self, cur_level):
level = 0
c = self.children
while c:
level += 1
c = self.children
if level == cur_level:
return c
if __name__ == "__main__":
rand1 = random.random()
if rand1 <= .35:
a = Node('CA')
else:
a = Node('CA')
a.create_children(2, 5) # create_children(R0, depth)
for child in a.get_level_nodes(4):
print(child.value)
a.print_tree()
Your get_level_nodes function has some issues:
c never changes value: it always represents self.children, so you are not actually moving down in the tree. You should somewhere iterate over those children and extend your collection of nodes with the children of these children.
You start out with self.children, but that list of nodes already represents the second level in the tree. You should foresee that the function can return the top-level of the tree, i.e. a list with just the root node in it.
I'll assume that you use the definition of "level" as specified in Wikipedia, although other definitions exist:
Level
1 + the number of edges between a node and the root, i.e. (depth + 1)
Solution:
def get_level_nodes(self, cur_level):
nodes = [self]
for i in range(cur_level-1): # assuming that cur_level is at least 1
children = []
for node in nodes:
children.extend(node.children)
nodes = children
return nodes
need a help with Dijkstra. I found a lot of codes on the internet, but I can't use any of them, because I'm not given a graph, but just lists of Vertexes & Edges into createGraph function. It's a homework and I gotta have some attributes in classes.
This is what I have:
class Vertex:
def __init__(self, id, name):
self.id = id
self.name = name
self.minDistance = float('inf')
self.previousVertex = None
self.edges = []
self.visited = False
class Edge:
def __init__(self, source, target, weight):
self.source = source
self.target = target
self.weight = weight
class Dijkstra:
def __init__(self):
self.vertexes = []
self.result = 0
def createGraph(self, vertexes, edgesToVertexes):
for i in range(len(vertexes)):
self.vertexes.append(vertexes[i])
for j in range(len(edgesToVertexes)):
if edgesToVertexes[j].source == vertexes[i].id:
vertexes[i].edges.append(edgesToVertexes[j])
def getVertexes(self):
return self.vertexes
def findMinID(self):
maxDistance = 1000000
curVertex = None
result = None
for i in range(len(self.vertexes)):
self.vertexes[i] = curVertex
if curVertex.visited is False and curVertex.minDistance < maxDistance:
curVertex = result
curVertex.minDistance = maxDistance
else:
pass
self.result = result
return
def computePath(self, sourceId):
start = None
end = None
road = None
while start is None:
if Vertex.id == sourceId:
start = Vertex
start.minDistance = 0
start.visited = True
for i in range(len(start.edges)):
start.edges[i].target = end
start.edges[i].weight = road
if road < end.minDistance:
end.minDistance = start.minDistance + road
end.previousVertex = start.id
else:
pass
self.findMinID()
self.computePath(self.result.id)
I'm still beginner so I tried to keep it simple, but it's not working as it raises error:
'type' object is not subscriptable
or:
AttributeError: type object 'Vertex' has no attribute 'id'
which makes absolutely no sense to me why.
I can use any help, thanks in advance!
When you put the line:
self.vertexes = Vertex
you are assigning the variable to the actual class. Probably what you wanted to do was make an empty list, as you append to it later:
self.vertexes = []
I would assume this is where the error comes from, as if you ever try to iterate over self.vertexes, you are iterating over the Vertex class, which is impossible and throws that error.
You also have later:
start = Vertex
Try initializing the start, like:
start = Vertex(sourceId, "vertex")
Also, the line before that you have
if Vertex.id == sourceId:
meaning that you might want to make the id variable in Vertex static:
class Vertex:
id = 0
def __init__(self, id, name):
self.id = id
id += 1
Some suggestions: class tutorial in python
Edit:
To find the vertex that has the id you want, use a filter:
start = None
for v in self.vertexes:
if v.id == sourceId:
start = Vertex(sourceId, v.name)
start.minDistance = 0
break
I am trying to write a simple method to link the nodes of a tree together in this way:
Every leaf is linked to the previous and the next leaf in the tree
Every non-leaf is linked to the previous and the next leaf in the tree
For example, if we have this tree:
A
/ | \
B C D
/ \ / \
E F G H
|
I
This should be the result of the method:
B.nextToken = E
C.prevToken = B
E.nextToken = F
E.prevToken = B
F.nextToken = I
C.nextToken = I
H.prevToken = I
Here is the method code:
prevToken = None
def depthFirstTraverseTokenLinking(tree):
global prevToken
if len(tree.children) == 0:
tree.prevToken = prevToken
if prevToken != None :
prevToken.nextToken = tree # Is something wrong with this line?
prevToken = tree
return
for c in tree.children:
depthFirstTraverseTokenLinking(c)
tree.prevToken = tree.children[0].prevToken
tree.nextToken = tree.children[-1].nextToken
For some strange reason, the non-leaves aren't linked to the next leaves, for example:
C.nextToken = None
Although
F.nextToken = I
I wonder why is that happening? The last lines at the end of the recursive function should grantee that a parent will have the same next as its last child!
The problem is, when you visit C, you traverse only it's children E & F.
"I" hasn't been visited yet, so C.children[-1].nextToken == None because only visiting "I" will set F.nextToken
Solution: you'll have to do a run on all leaves first, then a second run on the internal nodes.
For example:
prevToken = None
def depthFirstTraverseTokenLinking(tree):
depthFirstTraverseTokenLinkingPhase1(tree)
depthFirstTraverseTokenLinkingPhase2(tree)
def depthFirstTraverseTokenLinkingPhase1(tree):
global prevToken
if len(tree.children) == 0:
tree.prevToken = prevToken
if prevToken != None :
prevToken.nextToken = tree # Is something wrong with this line?
prevToken = tree
return
for c in tree.children:
depthFirstTraverseTokenLinkingPhase1(c)
def depthFirstTraverseTokenLinkingPhase2(tree):
if len(tree.children) == 0:
return
for c in tree.children:
depthFirstTraverseTokenLinkingPhase2(c)
if tree.children[0].prevToken is not None:
tree.prevToken = tree.children[0].prevToken
else:
tree.prevToken = tree.children[0]
if tree.children[-1].nextToken is not None:
tree.nextToken = tree.children[-1].nextToken
else:
tree.nextToken = tree.children[-1]
Also note the change for the prevToken/nextToken of internal nodes. This is needed if you want them to link to the actual first/last leaf.
Alternatively, use generators an an instance-checking loop
The generator yields the node as the base case if the node has no children, else another generator to travel down the tree. Caveat here is that node.children is ordered from left to right.
def leafs(node):
if len(node.children) == 0:
yield node
else:
for child in node.children:
yield leafs(child)
...and a loop with stack of generators... This got uglier as I wrote it - I think you could clean it up a bit and get rid of the while True...
current_node = leafs(a)
stack = []
last_node = None
while True:
if isinstance(current_node, types.GeneratorType):
stack.append(current_node)
current_node = current_node.next()
else:
if last_node and last_node != current_node:
last_node.nextToken = current_node
current_node.prevToken = last_node
last_node = current_node
try:
current_node = stack[-1].next()
except StopIteration:
stack.pop()
except IndexError:
break
when I run this program, it print NameError global name 'viewAll' is not defined
I' m a C programmer, I didn't know why.
viewAll(this) is defined in class binTree,
Platform:Python 2.7 in windows 7 64 bit
#!/usr/bin/python
#-*-coding:gbk-*-
class binTree():
def __init__(this, left = None, right = None, data = 0):
if data == 0:
this = None
else:
this.leftNode = left
this.rightNode = right
this.data = data
def viewAll(this):
if this != None:
print this.data,
viewAll(this.leftNode)
viewAll(this.rightNode)
def creatBT():
temp = input('Please input a number, input "0" for end!')
if temp == 0:
return None
else:
tree = binTree()
tree.data = temp
tree.leftNode = creatBT()
tree.rightNode = creatBT()
return tree
if __name__ == "__main__":
root = creatBT()
root.viewAll()
You need to do a Python tutorial -- you're not understanding how the instance object works in Python instance methods.
Your problem is here:
def viewAll(this):
if this != None:
print this.data,
viewAll(this.leftNode)
viewAll(this.rightNode)
You need to access viewAll on the instance you want to call it on:
def viewAll(this):
if this != None:
print this.data,
this.leftNode.viewAll()
this.rightNode.viewAll()
I'm not sure what you're intending to do here:
if data == 0:
this = None
but all you are actually doing is pointing the name this at None in the scope of that one function call. It doesn't change anything about the class instance or anything outside the function.
So, in viewAll,
if this != None:
will always be True, because this is again the instance you've called viewAll on -- it hasn't been and can't be set to None.
class binTree():
# I removed __init__ to show you weren't using it
def viewAll(self):
print self.data,
# don't try to show a node that is empty
if self.leftNode:
self.leftNode.viewAll()
if self.rightNode:
self.rightNode.viewAll()
def creatBT():
try:
# don't use input
temp = int(raw_input('Please input a number, input "0" for end!'))
except ValueError:
# in case they don't put in a number
temp = 0
if temp == 0:
return None
else:
tree = binTree()
tree.data = temp
tree.leftNode = creatBT()
tree.rightNode = creatBT()
return tree
if __name__ == "__main__":
root = creatBT()
# don't try to show the nodes if none were created
if root:
root.viewAll()
Please conform to the standard and use self instead of this.
def viewAll(self):
print self.data,
self.viewAll(self.leftNode)
self.viewAll(self.rightNode)
There is no need to test if self (or this) is None.
edit This solution is not correct. Please see agf's answer.