π‘ Tips and TricksΒΆ
List DirectoryΒΆ
To list directories recursively using bigtree, we can use the glob built-in Python package to extract a list of paths.
import glob
from bigtree import list_to_tree, print_tree
# Get all directories recursively
path_list = []
for f in glob.glob("./**/*.py", recursive=True):
path_list.append(f)
# Construct tree
root = list_to_tree(path_list)
# View tree
print_tree(root, max_depth=3)
Extending NodesΒΆ
Nodes can be extended from BaseNode or Node class to have extended functionalities or add pre-/post-assign checks.
For example,
Nodeclass extendsBaseNodeand added thenamefunctionality with pre-assign checks to ensure no duplicate path names.
Population Node (add functionality/method/property)ΒΆ
from bigtree import Node, print_tree
class PopulationNode(Node):
def __init__(self, name: str, population: int = 0, **kwargs):
super().__init__(name, **kwargs)
self._population = population
@property
def population(self):
if self.is_leaf:
return self._population
return sum([child.population for child in self.children])
@property
def percentage(self):
if self.is_root:
return 1
return round(self.population / self.root.population, 2)
root = PopulationNode("World")
b1 = PopulationNode("Country A", parent=root)
c1 = PopulationNode("State A1", 100, parent=b1)
c2 = PopulationNode("State A2", 50, parent=b1)
b2 = PopulationNode("Country B", 200, parent=root)
b3 = PopulationNode("Country C", 100, parent=root)
print_tree(root, attr_list=["population", "percentage"])
# World [population=450, percentage=1]
# βββ Country A [population=150, percentage=0.33]
# β βββ State A1 [population=100, percentage=0.22]
# β βββ State A2 [population=50, percentage=0.11]
# βββ Country B [population=200, percentage=0.44]
# βββ Country C [population=100, percentage=0.22]
Read-Only Node (add pre-/post-assign checks)ΒΆ
import pytest
from bigtree import Node
from typing import List
class ReadOnlyNode(Node):
def __init__(self, *args, **kwargs):
self.__readonly = False
super().__init__(*args, **kwargs)
self.__readonly = True
def _Node__pre_assign_parent(self, new_parent: Node):
if self.__readonly:
raise RuntimeError("Nodes cannot be reassigned for ReadOnlyNode")
def _Node__pre_assign_children(self, new_children: List[Node]):
if self.__readonly:
raise RuntimeError("Nodes cannot be reassigned for ReadOnlyNode")
a = ReadOnlyNode("a")
b = ReadOnlyNode("b", parent=a)
c = ReadOnlyNode("c", parent=a)
with pytest.raises(RuntimeError):
c.parent = b
with pytest.raises(RuntimeError):
a.children = [b, c]
Merging TreesΒΆ
To merge two separate trees into one, we can use the tree modify module.
In this example, we are merging two trees that have similar node b.
Children of node b from both trees are retained as long as merge_children=True is set.
If only children of other tree is desired, set overriding=True instead.
from bigtree import Node, copy_nodes_from_tree_to_tree
# Construct trees
downloads_folder = Node("Downloads")
pictures_folder = Node("Pictures", parent=downloads_folder)
photo1 = Node("photo1.jpg", parent=pictures_folder)
file1 = Node("file1.doc", parent=downloads_folder)
documents_folder = Node("Documents")
pictures_folder = Node("Pictures", parent=documents_folder)
photo2 = Node("photo2.jpg", parent=pictures_folder)
# Validate tree structure
downloads_folder.show()
# Downloads
# βββ Pictures
# β βββ photo1.jpg
# βββ file1.doc
documents_folder.show()
# Documents
# βββ Pictures
# βββ photo2.jpg
# Merge trees
copy_nodes_from_tree_to_tree(
from_tree=documents_folder,
to_tree=downloads_folder,
from_paths=["Documents/Pictures"],
to_paths=["Downloads/Pictures"],
merge_children=True, # set overriding=True to override existing children
)
# Validate tree structure
downloads_folder.show()
# Downloads
# βββ Pictures
# β βββ photo1.jpg
# β βββ photo2.jpg
# βββ file1.doc
Trees with Weighted EdgesΒΆ
Edge weights should be defined in the child node for the parent-child edge since each node can only have one parent.
We can simply add weight attribute to the Node class.
However, if we want to visualize the weighted tree, we can create a WeightedNode class to generate the edge attribute dictionary.
from bigtree import Node, tree_to_dot
class WeightedNode(Node):
def __init__(self, name, weight=0, **kwargs):
super().__init__(name, **kwargs)
self.weight = weight
@property
def edge_attr(self):
"""Edge attribute for pydot diagram
Label for edge label, penwidth for edge width
"""
return {"label": self.weight, "penwidth": self.weight}
# Construct weighted tree
root = WeightedNode("a")
b = WeightedNode("b", parent=root, weight=1)
c = WeightedNode("c", parent=root, weight=2)
d = WeightedNode("d", parent=b, weight=3)
graph = tree_to_dot(root, node_colour="gold", edge_attr="edge_attr")
graph.write_png("assets/docs/weighted_tree.png")
