In a python application we have a tree made up of TreeNode objects, and we had to add a property on the TreeNode class that returns the path from the tree root to that node as a list. We have implemented this in a simple recursive way, however the code looks a little verbose for python (we suspect there is a terser way of expressing a simple algorithm like this in python). Does anyone know a more pythonic way of expressing this?
Here is a simplified version of our code - it is the definition of path_from_root we are looking to improve:
class TreeNode(object):
def __init__(self, value, parent=None):
self.value = value
self.parent = parent
#property
def path_from_root(self):
path = []
_build_path_from_root(self, path)
return path
def _build_path_from_root(node, path):
if node.parent:
_build_path_from_root(node.parent, path)
path.append(node)
Below are some unit tests that show how path_from_root works:
class TreePathAsListTests(unittest.TestCase):
def setUp(self):
self.root = TreeNode(value="root")
self.child_1 = TreeNode(value="child 1", parent=self.root)
self.child_2 = TreeNode(value="child 2", parent=self.root)
self.leaf_1a = TreeNode(value="leaf 1a", parent=self.child_1)
def test_path_from_root(self):
self.assertEquals([self.root, self.child_1, self.leaf_1a], self.leaf_1a.path_from_root)
self.assertEquals([self.root, self.child_2], self.child_2.path_from_root)
self.assertEquals([self.root], self.root.path_from_root)
Update: Accepted an answer that was a clear improvement, but definitely still interested in any other ways of expressing this.
I would do it this way:
#property
def path_from_root(self):
if self.parent:
return self.parent.path_from_root + [self]
return [self]
Related
I wan to create a python script that print out a directory tree.
I'm aware there are tons of information about the topic, and many ways to achieve it.
Still, my problem really is about recursion.
In order to face the problem i choosed a OOP way:
Create a Class TreeNode
Store some props and methods
calling in the os.walk function (ya i know I can use pathlib or other libs.)
recursively create parent-child relationship of folders/files
First, the Class TreeNode:
properties: data, children, parent
methods: add_child(),
get_level(), to get the level of the parent/child relation in order to print it later
print_tree(), to actually print the tree (desired result shown above code)
class Treenode:
def __init__(self, data):
self.data = data
self.children = []
self.parent = None
def add_child(self,child):
child.parent = self
self.children.append(child)
def get_level(self):
level = 0
p = self.parent
while p:
level += 1
p = p.parent
return level
def print_tree(self):
spaces = " " * self.get_level() * 3
prefix = spaces + "|__" if self.parent else ""
print(prefix + self.data)
for child in self.children:
child.print_tree()
Second, the probelm. Function to creating the tree
def build_tree(dir_path):
for root,dirs,files in os.walk(dir_path):
if dir_path == root:
for d in dirs:
directory = Treenode(d)
tree.add_child(directory)
for f in files:
file = Treenode(f)
tree.add_child(file)
working_directories = dirs
else:
for w in working_directories:
build_tree(os.path.join(dir_path,w))
return tree
Finally, the main method:
if __name__ == '__main__':
tree = Treenode("C:/Level0")
tree = build_tree("C:/Level0")
tree.print_tree()
pass
The output of this code would be:
C:/Level0
|__Level1
|__0file.txt
|__Level2
|__Level2b
|__1file1.txt
|__1file2.txt
|__Level3
|__2file1.txt
|__LEvel4
|__3file1.txt
|__4file1.txt
|__2bfile1.txt
The desired output should be:
C:/Level0
|__Level1
|__Level2
|__Level3
|__LEvel4
|__4file1.txt
|__3file1.txt
|__2file1.txt
|__Level2b
|__2bfile1.txt
|__1file1.txt
|__1file2.txt
|__0file.txt
The problem lays in the tree.add_child(directory), since everytime the code get there it add the new directory (or file) as child of the same "root tree". Not in tree.children.children..etc
So here's the problem. How do i get that. The if else statement in the build_tree() function is probably unecessary, i was trying to work my way around but no luck.
I know it's a dumb problem, coming from a lack of proper study of algorithms and data structures..
If you will to help though, i'm here to learn ^^
This will do what you want:
def build_tree(parent, dir_path):
child_list = os.listdir(dir_path)
child_list.sort()
for child in child_list:
node = Treenode(child)
parent.add_child(node)
child_path = os.path.join(dir_path, child)
if os.path.isdir(child_path):
build_tree(node, child_path)
Then, for your main code, use:
if __name__ == '__main__':
root_path = "C:/Level0"
tree = Treenode(root_path)
build_tree(tree, root_path)
tree.print_tree()
The main change was to use os.listdir rather than os.walk. The problem with os.walk is that it recursively walks the entire directory tree, which doesn't work well with the recursive build_tree, which wants to operate on a single level at a time.
You can use os.walk, but then don't use recursion, as you don't want to repeat the call to os.walk: one call gives all the data you need already. Instead use a dictionary to keep track of the hierarchy:
def build_tree(dir_path):
helper = { dir_path: Treenode(dir_path) }
for root, dirs, files in os.walk(dir_path, topdown=True):
for item in dirs + files:
node = helper[os.path.join(root, item)] = Treenode(item)
helper[root].add_child(node)
return helper[dir_path]
if __name__ == "__main__":
tree = build_tree("C:/Level0")
tree.print_tree()
class node:
def __init__(self,value=None):
self.value=value
self.left_child=None
self.right_child=None
self.parent=None
class binary_search_tree:
def __init__(self):
self.root=None
def insert(self,value):
if self.root==None:
self.root=node(value)
else:
self._insert(value,self.root)
def _insert(self,value,cur_node):
if value<cur_node.value:
if cur_node.left_child==None:
cur_node.left_child=node(value)
cur_node.left_child.parent=cur_node
else:
self._insert(value,cur_node.left_child)
elif value>cur_node.value:
if cur_node.right_child==None:
cur_node.right_child=node(value)
cur_node.right_child.parent=cur_node
else:
self._insert(value,cur_node.right_child)
else:
print("Value already in tree!")
def print_tree(self):
if self.root!=None:
self._print_tree(self.root)
def _print_tree(self,cur_node):
if cur_node!=None:
self._print_tree(cur_node.left_child)
print (str(cur_node.value))
self._print_tree(cur_node.right_child)
def height(self):
if self.root!=None:
return self._height(self.root,0)
else:
return 0
def _height(self,cur_node,cur_height):
if cur_node==None: return cur_height
left_height=self._height(cur_node.left_child,cur_height+1)
right_height=self._height(cur_node.right_child,cur_height+1)
return max(left_height,right_height)
def find(self,value):
if self.root!=None:
return self._find(value,self.root)
else:
return None
def _find(self,value,cur_node):
if value==cur_node.value:
return cur_node
elif value<cur_node.value and cur_node.left_child!=None:
return self._find(value,cur_node.left_child)
elif value>cur_node.value and cur_node.right_child!=None:
return self._find(value,cur_node.right_child)
def preorder(self):
print(self.value)
if self.left_child:
self.left_child.preorder()
if self.right_child:
self.right_child.preorder()
tree = binary_search_tree()
tree.insert(21)
tree.insert(26)
tree.insert(30)
tree.insert(9)
print(tree.preorder)
So I have this binary search tree class and it all works correctly, however I added a preorder traversal method inside of my class, and that is not working. Does anyone know how i could modify this method for it to work? I am looking for it to just print out the preorder list. Should I add the root or value as a parameter to the method? I am just looking to be able to get it to preorder traversal and just print it, thats all i really need.
Preorder would be just a matter of changing the order of statements in _print_tree(). Something like this:
def preorder(self):
if self.root!=None:
self._preorder(self.root)
def _preorder(self,cur_node):
if cur_node!=None:
print (str(cur_node.value))
self._preorder(cur_node.left_child)
self._preorder(cur_node.right_child)
And call it like tree.preorder()
I strongly recommend going through the theory or ordered tree traversal: https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/
The problem is that you are mixing and matching two approaches to organizing the recursion.
Approach 1:
def _insert(self,value,cur_node):
# skipping logic, until we get to a recursive call:
self._insert(value,cur_node.left_child)
The general logic in here is that the recursive calls call back to the same tree object, but pass the information about which node is being operated on as a parameter. This keeps the logic out of the Node class, which is just plain data storage.
Approach 2:
def preorder(self):
# the recursive calls look instead like this:
self.left_child.preorder()
This attempts to implement the recursion by delegating to the node instances, which requires them to have the behaviour. This whole block of code makes sense as a node method, since it is the nodes that have left_child and right_child attributes (that can be accessed from self). If you want to take this approach, then that code goes in the node class, and then in the binary_search_tree you would just need to delegate to the root node:
def preorder(self):
self.root.preorder()
Either way, it is better if you pick one approach and stick with it: either
have the tree class delegate to the root node, and give the nodes the needed functionality to do the recursion; or
have the tree class handle the entire recursion, by keeping track of the "current" node. (You can also easily convert this approach to iteration.)
def preorder(self):
currentNode = self
if currentNode is not None:
print(currentNode.value)
if currentNode.left:
currentNode.left.preorder()
if currentNode.right:
currentNode.right.preorder()
try this
So I have no idea what's causing this error. I don't know how to describe it without showing you, so here's the relevant part of my code:
class Node(object):
def __init__(self, contents, children):
self.contents = contents
self.children = children
def makeNode(district, parent):
new_contents = parent.contents
new_contents.append(district)
new = Node(new_contents, [])
parent.children.append(new)
return new
root = Node([], [])
data = [[1,1,'r'],[1,2,'d'],[1,2,'r'],[1,4,'d']]
makeNode(data, root)
Here's the problem: new.contents is changed as planned, but so is parent.contents. What happened?
As you've mentioned it in your comment, you've got to copy the contents pf 'parent.contents' into 'new_contents'. Otherwise, you're accessing the list by reference, which is obviously not what is intended.
So, your 'makeNode' function could start as follows:
def makeNode(district, parent):
new_content = copy.deepcopy(parent.contents)
...
I hope, I could clear things up for later readers... ;)
I'm coding a little game for my python course, and I want to integrate an inventory and item system. The possibilities offered by the item are variables (weapons, quest item, consumable or not, an so on).
I have read this tutorial (in French) about the pattern decorator (google translated in english) and I came with this:
(I am using python3)
class Item(object):
def __init__(self, caracts=None, inventory=None):
self.caracts = {}
if caracts:
self.caracts = caracts
self.inventory = inventory
class ItemDecorator(Item):
def __init__(self, item):
super().__init__()
self.item = item
self.caracts = item.caracts
class Consumable(ItemDecorator):
def __init__(self, item, amount=1):
super().__init__(item)
self._amount = 0
self.amount = amount
#property
def amount(self):
return self._amount
#amount.setter
def amount(self, value):
self._amount = max(0, value)
if self._amount == 0 and self.item.inventory:
self.item.inventory.remove(self)
#amount.deleter
def amount(self):
del self._amount
class Usable(ItemDecorator):
def __init__(self, item, action=None, consumable=None):
if not action:
action = lambda *args, **kwargs: None
self._use = action
self.consumable = consumable
def use(self, *args, **kwargs):
if self.consumable and self.consumable.amount <= 0:
raise CantBeUsedException("There is no consumable")
else:
if self.consumable:
self.consumable.amount -= 1
self._use(*args, **kwargs)
My idea is to be able to do this:
potion = Usable(Consumable(Item(), 3), use_potion)
print("isinstance(potion.item, Consumable): {}".format(
isinstance(potion.item, Consumable)))
potion.consumable = potion.item
for dummy in range(4):
try:
potion.use()
except CantBeUsedException as e:
print("Expected exception: {}".format(e))
But here comes my issue, line 4. The consumable used by the usable potion should be potion itself. But potion lost its consumable ability and only potion.item has it. It's even worst, because the order in which I call my decorator matters. potion = Consumable(Usable(Item(), use_potion), 3) leads me to do potion.item.use(), always this item that annoys me.
How can I simplify this? Knowing that a usable doesn't necessarily consume itself, or even something. In fact, I would like to be able to do this, no matter which decorator was called first:
potion = Consumable(Usable(Item(), use_potion), 3)
potion.consumable = potion
potion.use()
I don't manage to found a clean solution for my issue. Here is all the questions that come to my mind:
* Is this Decorator pattern adapted? (It looks so to my mind, but I can be wrong)
* If it's not the case, to your mind, wouldn't be an interface system (thus, with multiple heritage) a better solution?
* What did I do wrong to get stuck here?
How can I make this system really simple while still being extensible. For this, I think about this solution:
class ItemDecorator(Item):
def __init__(self, item):
super().__init__()
self.item = item
self.caracts = item.caracts
if hasattr(item, "amount"):
self.amount = item.amount
if hasattr(item, "use"):
self.use = item.use
But by doing so, don't I lose all the extensibility of the Decorator pattern? Indeed, I would need to update ItemDecorator each time I want to create a quite complex decorator. Thus, wouldn't I lose all the advantage of the decorator pattern?
Thank you very much for your help
Right now your classes layout is rather twisted, and Usable(Consumable(Item(), 3), use_potion) doesn't look pythonic.
I'd slightly redesign the system:
class ItemCapability:
pass
class Consumable(ItemCapability):
def __init__(self, amount):
super().__init__()
self.amount = amount
class Usable(ItemCapability):
pass
class Item:
def __init__(self, name):
self.name = name
self.capabilities = {}
def make(self, capability):
assert isinstance(capability, ItemCapability)
assert capability.__class__ not in self.capabilities
self.capabilities[capability.__class__] = capability
def has(self, capability_cls):
try:
return self.capabilities[capability_cls]
except KeyError:
return False
potion = Item('potion')
potion.make(Usable())
potion.make(Consumable(amount=10))
print(potion.has(Usable))
print(potion.has(Consumable))
This way you have a very simple to understand class system, and easy way to query your items for capabilities.
Hey everybody,
I've been trying to find a built-in function for extracting the root of a tree in python,
I haven't found something like that and I've been trying to build one of my own but I couldn't build something generic to fit all my needs.
Does anyone have something prepared or perhaps know how to extract this information from the tree structure in python?
thanks
You have to roll your own:
class Node(object):
def __init__(self, p=None):
self.parent = p
self.children = []
n1 = Node()
n2 = Node()
n1.children.append(n2)
n2.parent = n1
Of course you would want to have methods like addChild that would manage the .children and .parent attributes of the involved objects automatically.
Then you could write a method
def findRoot(node):
p = node
while p.parent != None:
p = p.parent
return p