I have been trying to implement a simple tree structure in Python. A tree begins at a single "root" node which has children, each of its children may have own children and so forth.
Now, I want to print the names of all nodes of the tree, that is I want to convert it to a list. I sought to employ recursiveness but unfortunately yielding recursively returns a sub-tree of generator objects which I cannot convert to nodes.
Could somebody help me and point out what I am doing wrong here please?
class Node:
def __init__(self,name):
self.name = name
self.children = []
self.parent = None
def appendChild(self,child):
self.children.append(child)
if child is not None:
child.parent = self
def listChildren(self):
yield self
for child in self.children:
yield child.listChildren()
raise StopIteration
# test
r = Node("root")
n = Node("name")
r.appendChild(n)
n.appendChild(Node("name2"))
n.appendChild(Node("name3"))
c = Node("child")
n.appendChild(c)
c.appendChild(Node("child2"))
c.appendChild(Node("child3"))
r.appendChild(Node("name4"))
r.appendChild(Node("name5"))
r.appendChild(Node("name6"))
for child in r.listChildren():
print child.name
Output:
Traceback (most recent call last):
File "C:/Users/User/Documents/TreeNode.py", line 40, in <module>
print child.name
AttributeError: 'generator' object has no attribute 'name'
A generator is supposed to be called when it is being iterated over, but in my case every child in r.listChildren() is, in turn, a generator object. If this is a design flaw, then I would have to look for another way of generating a list of node names.
Thank you in advance!
child.listChildren() will return a generator object and not the actual children. So you probably want to do something like:
def listChildren(self):
yield self
for child in self.children:
for c in child.listChildren():
yield c
raise StopIteration # PS: you don't need to do that explicitly
Alternatively if you use Python 3.3 you could do:
def listChildren(self):
yield self
for child in self.children:
yield from child.listChildren()
Related
I have a tree class in which the class gets initialized with a data, left, and right attributes.
in the same class I have a "save" method.
I am using a list as a queue.
I am attempting to create a "save" method which takes only one argument "data".
The purpose of this save method is to dequeue from my list, check that node to see if its empty and if it is then it saves my data there. Otherwise it enqueues the 2 children of that node into the list.
The purpose of this is to save data in level order into the tree.
Because the class gets initialized there is always at least 1 element in the tree which is the root node.
The issue i keep running into is that whenever i append the self.data (the root node, not the data im currently trying to add) into my list at the beginning of the save method it only saves the data there.
and obviously when I then try to append the left and right child of this int i get an error because the int has no left or right attributes.
I am wondering how to save the node in the list instead of the data at the node.
class Tree():
aqueue = []
def __init__(self, item):
self.item = item
self.leftchild = None
self.rightchild = None
self.aqueue.append(self.item)
def add(self, newitem):
temp = self.myqueue.pop(0)
if temp is None:
temp = Tree(newitem)
else:
self.aqueue.append(temp.leftchild)
self.aqueue.append(temp.rightcild)
temp.add(newitem)
self.aqueue.clear() #this is meant to clear queue of all nodes after the recursions are complete
self.aqueue.append(self.item) #this is meant to return the root node to the queue so that it is the only item for next time
There are a couple of obvious issues with your code: both the if and else branch return, so the code after will never run, temp == newitem is an equality expression, but even if it was an assignment it wouldn't do anything:
def add(self, newitem):
temp = self.myqueue.pop(0)
if temp == None: # should use temp is None
temp == newitem # temp = newitem still wouldn't do anything
return True
else:
self.aqueue.append(temp.leftchild)
self.aqueue.append(temp.rightcild)
return temp.add(newitem)
# you will never get here, since both branches of the if returns
self.aqueue.clear() # delete everything in the list..?
self.aqueue.append(self.item)
The following are my class definitions :
class logline:
def __init__(self,t,cmp,msg):
self.t = t
self.cmp = cmp
self.msg = msg
class cluster:
clusters = []
def __init__(self,status,log):
self.status = status
self.children = []
self.eventlogs = []
self.rep_msg = log.msg
self.addLog(log)
self.prev = None
if(status == 'root'):
cluster.clusters.append(self)
def prev(self):
return self.prev
def print_children(self):
for child in range(0,len(self.children)):
print(self.children[child].rep_msg)
self.children[child].print_logs()
def print_logs(self):
for log in self.eventlogs:
print(log.msg)
def add_child(self,status,log):
temp = cluster(status,log)
self.children.append(temp)
temp.prev=self
return temp
def addLog(self,log):
self.eventlogs.append(log)
Now, tree is my root cluster node
tree = cluster('root',log1)
and prev is my child cluster node added to tree
tree = tree.add_child('child',log6)
When I try:
tree = tree.prev()
I should get back tree, but it gives me the error:
tree = tree.prev()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'cluster' object is not callable
On the other hand :
callable(cluster)
evaluates to true
my class definitions are based off of: How can I implement a tree in Python? Are there any built in data structures in Python like in Java?
I've searched around but can't seem to find anything which matches my situation
Thanks in advance
Edit :
So, I am an absolute beginner in python, I should have probably led with that
>>> print(tree)
<__main__.cluster object at 0x02AF8590>
>>> print(tree.prev)
<__main__.cluster object at 0x02AEA270>
I'm assuming since I'm getting different locations for both statements, prev has been set to something
But I'm not able to go back to my parent node with
return self.prev
The cluster class itself is callable: when you call it it returns an instance of the class. However, an instance of the class isn't callable. But (you may ask) why is your code even trying to call an instance of the class?
Well, that's because the .add_child method returns the new temp instance, but it sets the temp.prev attribute to self, the parent instance. And that overrides the .prev method of that instance. So when you do tree.prev() it's trying to call that parent instance instead of the prev method.
Incidentally, the cluster.__init__ method also replaces the .prev method with None.
So you need to get rid of that name clash. I suggest you rename the attribute to ._prev.
prev is an attribute of your cluster instance tree (overriding the method of the same name).
cluster's constructor __init__ is what makes the cluster class callable. But when you instantiate the cluster class using __init__ you get an instance. This instance is not callable, unless you implement a __call__ method in your class.
To check this:
callable(cluster) #Returns True
callable(tree) #Returns False
I would need the simplest possible implementation for a data structure, that can be traversed in both parent->children and children->parent direction; so ideally the child should hold a reference to the parent as well.
Was thinking about a dictionary, where the children would simply hold a reference to their parent, similar to this:
# define the root node
a = {'name': 'trunk', 'value': 0, 'parent': None, 'children': []}
# add child
a['children'].append({'name': 'branch-1', 'value': 1,
'parent': a, 'children': []})
# and so on...
Is this safe to do? (Circular reference might impact garbage collection?) Does it make sense to do this? What would be simpler?
A simple Tree (Node) class, that can be traversed both ways:
class Tree(object):
def __init__(self, data, children=None, parent=None):
self.data = data
self.children = children or []
self.parent = parent
def add_child(self, data):
new_child = Tree(data, parent=self)
self.children.append(new_child)
return new_child
def is_root(self):
return self.parent is None
def is_leaf(self):
return not self.children
def __str__(self):
if self.is_leaf():
return str(self.data)
return '{data} [{children}]'.format(data=self.data, children=', '.join(map(str, self.children)))
> t = Tree('foo')
> bar = t.add_child('bar')
> baz = t.add_child('baz')
> print(t)
'foo [bar, baz]'
> print(bar.parent)
'foo [bar, baz]'
You would make a Node class.
The basic structure would look something like this, though honestly you could probably do it with dicts too. Just personally feel classes are cleaner looking.
class Node(object):
def __init__(self):
self.parent = None # Single object
self.child = [] # Array of objects
self.name = None
self.data = None
The rest depends on your needs. Some functions you may want built into your class (or if you use hashes, build out as methods in your script)
Update: which takes a specific node and updates its values/name/what
have you
Delete: which takes a specific node and removes it from the tree. If
you do this make sure to connect the deleted nodes children to the
deleted nodes parent.
Insert: which takes a specific point in the tree and adds a new node
into it. This should update the parent and children around the node.
Update children: appends children to node.child array. Should be
called from update parent as the two processes are self referential.
Update parent: Deletes self from parent.child array. Adds self to
new_parent.child array.
If you want to easily reference specific parts of a node, you can make a hash_map as a sort of table of contents
node_tree_map = {}
node_tree_map[node.name] = node
# node_tree_map['name'] would allow you quick access to bob's
# parent/children/value
# just by knowing the name but without having to traverse
# the whole tree to find it
The above will allow you to easily dive into specific nodes if necessary.
Btw, removing a node from being referenced in the tree or hash map would make garbage collection a non issue.
I try to parse an XML file.
The main goal is to get an specific tag with with an other specific parent tag.
I have to use an already existing file and i have to modify it.
def get_item_with_parents(self, parent, name, items=None, parents=None):
self.parents=parents
if items == None:
top_level = 1
items = []
self.parents = []
else:
top_level = 0
for child in self.children:
self._append_list(child.name)
if child.name == name and parent in self.parents:
print "inside if"
self._del_list()
items.append(child)
#print self.parents
child.get_item_with_parents(parent, name, items, self.parents)
if top_level:
return PacketList(items)
def _append_list(self, item):
self.parents.append(item)
def _del_list(self):
self.parents=[]
print "test"
print self.parents
The code should do the following (the child variable should basically mean the same as the parent thing):
I get the parent tag an the name of the tag which should be returned.
Then I check if I am the first recursion otherwise i will not return the packetlist at the end.
For each child I try to append its name to my self.parents list. Then I check if my packet has the right "name" and if yes I look if the "parent" is in self.parent.
So there occurs the problem now.
If the parent was in the list, I have to delete everything in my list. But this wont work.
The list will not be cleared. So if there is a next occurance of the tag with the name "name", it will be appended again even if it should not do, because the parent should not be in the list anymore
Can someone tell me why?
I found my misstake.
To clear a list i hav to call del parents[:]
parents=[] does not work(it seams it does simply nothing).
I'm trying to wrap my head around python generators and as a result, I'm trying to print infinitely nested objects using yield, but I find that I still get problems with blowing out the stack. Ideally, I'd like to be able to yield and print each item as it comes along but I can't figure out what I'm doing wrong:
class Parent:
def __init__(self, name, child=None):
self._name = name
self._child = child
def get_name(self):
return self._name
def get_child(self):
return self._child
def set_child(self, child):
self._child = child
def __iter__(self):
next_child = self._child.get_child()
if not next_child:
raise StopIteration
else:
self._child = next_child
yield next_child
def __str__(self):
return "%s has %s" % (self._name, self._child)
if __name__ == '__main__':
p1 = Parent("child")
p2 = Parent("child", p1)
p1.set_child(p2)
for t in p1:
print t
The error in your code, as noted by jonrsharpe, is due to the __str__ function, which tries to return:
child has child has child has child has child has ...
You probably mean:
def __str__(self):
return "%s has %s" % (self._name, self._child.get_name())
# return 'child has child'
Also, __iter__ should be a generator function. Generator functions need to contain a loop in order to continually produce values. So it should be something like:
def __iter__(self):
next_child = self._child.get_child()
while next_child:
yield next_child
next_child = next_child.get_child()
# When the function ends, it will automatically raise StopIteration
With the modifications, your code prints endless lines of child has child.
See also What does the yield keyword do in Python? for more information about generator functions.
The infinite recursion is happening at __str__ function. It has nothing to do with the __iter__ function.
when you do print t, it executes t._child.__str__ which in turn executes t._child._child.__str__ and so forth.
try changing the __str__ function definition to something simple like return self._name and you won't get a recursion depth exceeded error