AttributeError for recursive method in python - python

I have a recursive method problem with python, the code is this:
class NodeTree(object):
def __init__(self, name, children):
self.name = name
self.children = children
def count(self):
# 1 + i children's nodes
count = 1
for c in self.children:
count += c.count()
return count
def create_tree(d):
N = NodeTree(d['name'], d['children'])
print N.count()
d1 = {'name':'musica', 'children':[{'name':'rock', 'children':[{'name':'origini','children':[]},
{'name':'rock&roll','children':[]},
{'name':'hard rock', 'children':[]}]},
{'name':'jazz', 'children':[{'name':'origini', 'children':[{'name':'1900', 'children':[]}]},
{'name':'ragtime', 'children':[]}, {'name':'swing', 'children':[]}]}]}
tree = create_tree(d1)
The error is this:
count += c.count()
AttributeError: 'dict' object has no attribute 'count'
I tried anything but it doesn't work.
Anyway, any suggestions?
Thanks!

That's because Python dictionaries do not have a count method.
It'll help if we go over line by line what your code is actually doing.
def count(self):
# 1 + i children's nodes
count = 1
for c in self.children: ## self.children is a list of dictionaries, so each c is a dictionary
count += c.count() ## We are getting .count() of c--which is a dictionary
return count
This is because we passed d1['children'] as self.children, which is a list of dictionaries: [<dict>, <dict>, <dict>, ... ].
Rather than count(), what you should do is call len on the dictionary, to get the number of keys it has, thus becoming:
for c in self.children:
count += len(c)

d['children'] is a list of dict, as you can see in d1 dict.
Now, when you iterate over your children, in NodeTree, which is essentially d['children'] only, you will get dictionary as each element: -
for c in self.children: // c is a `dict` type here
count += c.count() // there is not attribute as `count` for a `dict`.
And hence you got that error.

Well, for once, the create_tree function does not build the tree recursively. So you just add a Node on zero level and the the children are just dictionaries.
The following (modified) code (although quickly typed and sloppy) should do a recursive
build of the tree. Didn't check your count code, but assuming it is correct, it should work.
class NodeTree(object):
def __init__(self, name, children):
self.name = name
self.children = children
def count(self):
# 1 + i children's nodes
count = 1
for c in self.children:
count += c.count()
return count
def deep_create_tree(d):
if len(d["children"]) > 0:
chlds = []
for i in d["children"]:
chlds.append(deep_create_tree(i))
else:
chlds = []
n = NodeTree(d["name"], chlds)
return n
d1 = {'name':'musica', 'children':[{'name':'rock', 'children':[{'name':'origini','children':[]},{'name':'rock&roll','children':[]},{'name':'hard rock', 'children':[]}]},{'name':'jazz', 'children':[{'name':'origini', 'children':[{'name':'1900', 'children':[]}]},{'name':'ragtime', 'children':[]}, {'name':'swing', 'children':[]}]}]}
def scan_tree(tr):
print tr.name, tr.count()
for i in tr.children:
scan_tree(i)
tr = deep_create_tree(d1)
scan_tree(tr)

The best (and the only desirable) way is to have a recursive creation of your node tree. This can be done in two different ways, either make your NodeTree.__init__() recursively init all of the children (which again recursively init all of their children, etc) or you can make your create_tree() function recursive.
I'd personally use recursive __init__(), but it's your call.
Recursive __init__() for creating tree structure:
def __init__(self, name, children=[]):
self.name = name
self.children = []
for c in children:
self.children.append(
self.__class__(c['name'], c['children']))
This way self.children will contain other NodeTrees instead of dicts. Also you no longer need to declare empty children list, instead of:
d2 = {'name':'sterile', 'children':[]}
do
d2 = {'name':'sterile'}
And the initializer will automatically set children to []
If you want to use a recursive create_tree() function, it's also possible, and not a bad idea either. However you will still have to edit the __init__() -method, it no longer takes children as parameter. Or as I did here, it does, but you hardly ever use it.
# NodeTree
def __init__(self, name, children=None):
self.name = name
if children:
self.children = children
def create_tree(d):
N = NodeTree(d['name'])
for c in d['children']:
N.children.append(create_tree(c))
return N
This will have basically the same results.

Related

How do I find the maximum depth of a tree?

I have a tree data structure, defined as below:
class Tree(object):
def __init__(self, name='ROOT', children=None):
self.name = name
self.children = []
if children is not None:
for child in children:
self.add_child(child)
def __repr__(self):
return self.name
def add_child(self, node):
assert isinstance(node, Tree)
self.children.append(node)
I need to write a function to find the depth of the tree. Here is the function I wrote (takes a Tree as input, and returns an integer value as output), but it is not giving the right answer:
def depth(tree):
count = 1
if len(tree.children) > 0:
for child in tree.children:
count = count + 1
depth(child)
return count
How do I correct it?
While your depth(child) does do the recursive call, it does not do anything with the return value (the depth). You seem to be simply counting the nodes at a given level and calling that the depth (it's really the width).
What you need is something like (pseudo-code):
def maxDepth(node):
# No children means depth zero below.
if len(node.children) == 0:
return 0
# Otherwise get deepest child recursively.
deepestChild = 0
for each child in node.children:
childDepth = maxDepth(child)
if childDepth > deepestChild:
deepestChild = childDepth
# Depth of this node is one plus the deepest child.
return 1 + deepestChild
How about you just do a max of all the depth on each node recursively?
def max_depth(self, depth=0):
if not self.children:
return depth
return max(child.max_depth(depth + 1) for child in self.children)

How to access (and modify) arbitrary items in a hierarchy of classes in python

I have a class built in a way as follows for building a tree structure.
class Node(object):
def __init__(self, data):
self.data = data
self.children = []
def add_child(self, obj):
self.children.append(obj)
n00 = Node('n00')
n00.add_child(Node('n000'))
n00.add_child(Node('n001'))
n0 = Node('n0')
n0.add_child(n00)
n0.add_child(Node('n01'))
n0.add_child(Node('n02'))
n1 = Node('n1')
n1.add_child(Node('n10'))
n1.add_child(Node('n11'))
n20 = Node('n20')
n20.add_child(Node('n200'))
n20.add_child(Node('n201'))
n2 = Node('n2')
n2.add_child(n20)
n2.add_child(Node('n21'))
h = Node('head')
h.add_child(n00)
h.add_child(n01)
h.add_child(n02)
Now that when I want to only access an item, it can be done by with a simple function such as:
def access(tree, *id):
item = tree
for i in id:
item = item.children[i]
return item.data
print(access(h,0,1))
The problem is when I want to make changes to any node, I cannot use this method and always need to manually type in the lengthy members such as:
h.children[1].children[0].data = 'new value'
or
h.children[0].children[0].children[1].add_child(Node('n0010'))
Now whenever the depth of the tree gets deeper, it becomes quite painful to repeatedly type all this.
Is there a 'Python' way to make changes to items in a tree similar to the access method?
Just go ahead and modify the node: Use the same "walking" technique as in your access method, only then don't return item.data but assign a new value to it:
def modify(new_data, tree, *id):
item = tree
for i in id:
item = item.children[i]
item.data = new_data
Example:
print(access(h, 0, 1))
modify("n001new", h, 0, 1)
print(access(h, 0, 1))
Which prints:
n001
n001new
Same thing for adding children:
def insert_child(new_child, tree, *id):
item = tree
for i in id:
item = item.children[i]
item.add_child(new_child)
Call it like:
insert_child(Node('n0010'), h, 0, 1)
If you know the "path" in the tree of the node you wish to edit, you could create a method to your Node class to return that node using recursion. Something like this:
def getnode(self, path):
if len(path) > 1:
return self.children[path[0]].getnode(path[1:])
else:
return self.children[path[0]]
Here path is a tuple or a list. For example h.getnode((1, 0)).data is equivalent to h.children[1].children[0].data
You can improve this simple method with some try except block to prevent errors in case a tuple is too long or with wrong index, if needed.
EDIT
using the * operator maybe is simple:
def getnode(self, *path):
if len(path) > 1:
return self.children[path[0]].getnode(*path[1:])
else:
return self.children[path[0]]
this way you just write h.getnode(1, 0).data (no double parentheses)

Python: Create a Binary search Tree using a list

The objective of my code is to get each seperate word from a txt file and put it into a list and then making a binary search tree using that list to count the frequency of each word and printing each word in alphabetical order along with its frequency. Each word in the can only contain letters, numbers, -, or ' The part that I am unable to do with my beginner programming knowledge is to make the Binary Search Tree using the list I have (I am only able to insert the whole list in one Node instead of putting each word in a Node to make the tree). The code I have so far is this:
def read_words(filename):
openfile = open(filename, "r")
templist = []
letterslist = []
for lines in openfile:
for i in lines:
ii = i.lower()
letterslist.append(ii)
for p in letterslist:
if p not in ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',"'","-",' '] and p.isdigit() == False:
letterslist.remove(p)
wordslist = list("".join(letterslist).split())
return wordslist
class BinaryTree:
class _Node:
def __init__(self, value, left=None, right=None):
self._left = left
self._right = right
self._value = value
self._count = 1
def __init__(self):
self.root = None
def isEmpty(self):
return self.root == None
def insert(self, value) :
if self.isEmpty() :
self.root = self._Node(value)
return
parent = None
pointer = self.root
while (pointer != None) :
if value == pointer._value:
pointer._count += 1
return
elif value < pointer._value:
parent = pointer
pointer = pointer._left
else :
parent = pointer
pointer = pointer._right
if (value <= parent._value) :
parent._left = self._Node(value)
else :
parent._right = self._Node(value)
def printTree(self):
pointer = self.root
if pointer._left is not None:
pointer._left.printTree()
print(str(pointer._value) + " " + str(pointer._count))
if pointer._right is not None:
pointer._right.printTree()
def createTree(self,words):
if len(words) > 0:
for word in words:
BinaryTree().insert(word)
return BinaryTree()
else:
return None
def search(self,tree, word):
node = tree
depth = 0
count = 0
while True:
print(node.value)
depth += 1
if node.value == word:
count = node.count
break
elif word < node.value:
node = node.left
elif word > node.value:
node = node.right
return depth, count
def main():
words = read_words('sample.txt')
b = BinaryTree()
b.insert(words)
b.createTree(words)
b.printTree()
Since you're a beginner I'd advice to implement the tree methods with recursion instead of iteration since this will result to simpler implementation. While recursion might seem a bit difficult concept at first often it is the easiest approach.
Here's a draft implementation of a binary tree which uses recursion for insertion, searching and printing the tree, it should support the functionality you need.
class Node(object):
def __init__(self, value):
self.value = value
self.left = None
self.right = None
self.count = 1
def __str__(self):
return 'value: {0}, count: {1}'.format(self.value, self.count)
def insert(root, value):
if not root:
return Node(value)
elif root.value == value:
root.count += 1
elif value < root.value:
root.left = insert(root.left, value)
else:
root.right = insert(root.right, value)
return root
def create(seq):
root = None
for word in seq:
root = insert(root, word)
return root
def search(root, word, depth=1):
if not root:
return 0, 0
elif root.value == word:
return depth, root.count
elif word < root.value:
return search(root.left, word, depth + 1)
else:
return search(root.right, word, depth + 1)
def print_tree(root):
if root:
print_tree(root.left)
print root
print_tree(root.right)
src = ['foo', 'bar', 'foobar', 'bar', 'barfoo']
tree = create(src)
print_tree(tree)
for word in src:
print 'search {0}, result: {1}'.format(word, search(tree, word))
# Output
# value: bar, count: 2
# value: barfoo, count: 1
# value: foo, count: 1
# value: foobar, count: 1
# search foo, result: (1, 1)
# search bar, result: (2, 2)
# search foobar, result: (2, 1)
# search bar, result: (2, 2)
# search barfoo, result: (3, 1)
To answer your direct question, the reason why you are placing all of the words into a single node is because of the following statement inside of main():
b.insert(words)
The insert function creates a Node and sets the value of the node to the item you pass in. Instead, you need to create a node for each item in the list which is what your createTree() function does. The preceeding b.insert is not necessary.
Removing that line makes your tree become correctly formed, but reveals a fundamental problem with the design of your data structure, namely the printTree() method. This method seems designed to traverse the tree and recursively call itself on any child. In your initial version this function worked, because there the tree was mal-formed with only a single node of the whole list (and the print function simply printed that value since right and left were empty).
However with a correctly formed tree the printTree() function now tries to invoke itself on the left and right descendants. The descendants however are of type _Node, not of type BinaryTree, and there is no methodprintTree() declared for _Node objects.
You can salvage your code and solve this new error in one of two ways. First you can implement your BinaryTree.printTree() function as _Node.printTree(). You can't do a straight copy and paste, but the logic of the function won't have to change much. Or you could leave the method where it is at, but wrap each _left or _right node inside of a new BinaryTree so that they would have the necessary printTree() method. Doing this would leave the method where it is at, but you will still have to implement some kind of helper traversal method inside of _Node.
Finally, you could change all of your _Node objects to be _BinaryTree objects instead.
The semantic difference between a node and a tree is one of scope. A node should only be aware of itself, its direct children (left and right), and possibly its parent. A tree on the other hand can be aware of any of its descendents, no matter how far removed. This is accomplished by treating any child node as its own tree. Even a leaf, without any children at all can be thought of as a tree with a depth of 0. This behavior is what lets a tree work recursively. Your code is mixing the two together.

Python tree operations

I need to implement (or just use) a tree data structure on which I can perform:
1. Child additions at any specified position. The new child can itself be a big tree (need not be a singleton)
2. Subtree deletions and moving (to another node in the same tree)
3. Common traversal operations.
4. Access parent from child node.
First, is there any module I can use for this?
Second, if I were to implement this by myself, I've this concern:
When I do tree manipulations like moving subtrees, removing subtrees or adding new subtrees, I only wish to move the "references" to these tree nodes. For example, in C/C++ these operations can be performed by pointer manipulations and I can be assured that only the references are being moved.
Similarly, when I do tree "movements" I need to move only the reference - aka, a new copy of the tree should not be created at the destination.
I'm still in a "pointers" frame of thinking, and hence the question. May be, I don't need to do all this?
You can easily make your own tree with operator overloading. For example, here is a basic class with __add__ implemented :
class Node(object):
def __init__(self, value):
self.value = value
self.child = []
def add_child(self, child):
self.child.append(child)
def __add__(self, element):
if type(element) != Node:
raise NotImplementedError("Addition is possible only between 2 nodes")
self.value += element.value # i didn't get if you have to add also childs
return self # return the NODE object
So to answer to your second question, there is a python trick here. In __add__ you return self. Then, this return True:
a = Node(1)
b = Node(2)
print a is a + b
If you use a + b, this will modify the value a. a and b are, in fact, pointers. Then if you pass it as argument in a function, and you modify them in the function, the a and b instances will be modified. There is two different way to avoid this (maybe more, but this is the two i use) :
The first one is to directly modify the definition of __add__ :
def __add__(self, element):
# .../...
value = self.value + element.value
return Node(value) # you may add rows in order to copy childs
The second one is to add a copy method :
def copy(self):
# .../...
n = Node(self.value)
n.child = self.child[:] # Copy the list, in order to have 2 different instance of this list.
return n
This will allow you to do something like c = a.copy() + b and the assertion c is a will be false.
Hope I answered to your question.
Thi is an example for you:
class BinaryTree:
def __init__(self,rootObj):
self.key = rootObj
self.leftChild = None
self.rightChild = None
def insertLeft(self,newNode):
if self.leftChild == None:
self.leftChild = BinaryTree(newNode)
else:
t = BinaryTree(newNode)
t.leftChild = self.leftChild
self.leftChild = t
def insertRight(self,newNode):
if self.rightChild == None:
self.rightChild = BinaryTree(newNode)
else:
t = BinaryTree(newNode)
t.rightChild = self.rightChild
self.rightChild = t
def getRightChild(self):
return self.rightChild
def getLeftChild(self):
return self.leftChild
def setRootVal(self,obj):
self.key = obj
def getRootVal(self):
return self.key

How to use a generator to iterate over a tree's leafs

The problem:
I have a trie and I want to return the information stored in it. Some leaves have information (set as value > 0) and some leaves do not. I would like to return only those leaves that have a value.
As in all trie's number of leaves on each node is variable, and the key to each value is actually made up of the path necessary to reach each leaf.
I am trying to use a generator to traverse the tree postorder, but I cannot get it to work. What am I doing wrong?
My module:
class Node():
'''Each leaf in the trie is a Node() class'''
def __init__(self):
self.children = {}
self.value = 0
class Trie():
'''The Trie() holds all nodes and can return a list of their values'''
def __init__(self):
self.root = Node()
def add(self, key, value):
'''Store a "value" in a position "key"'''
node = self.root
for digit in key:
number = digit
if number not in node.children:
node.children[number] = Node()
node = node.children[number]
node.value = value
def __iter__(self):
return self.postorder(self.root)
def postorder(self, node):
if node:
for child in node.children.values():
self.postorder(child)
# Do my printing / job related stuff here
if node.value > 0:
yield node.value
Example use:
>>trie = Trie()
>>trie.add('foo', 3)
>>trie.add('foobar', 5)
>>trie.add('fobaz', 23)
>>for key in trie:
>>....print key
>>
3
5
23
I know that the example given is simple and can be solved using any other data structure. However, it is important for this program to use a trie as it is very beneficial for the data access patterns.
Thanks for the help!
Note: I have omitted newlines in the code block to be able to copy-paste with greater ease.
Change
self.postorder(child)
to
for n in self.postorder(child):
yield n
seems to make it work.
P.S. It is very helpful for you to left out the blank lines for ease of cut & paste :)

Categories

Resources