Basics¶
TL;DR
Nutree is a Python library for managing hierarchical data structures. It stores arbitrary data objects in nodes and provides methods for navigation, searching, and iteration.
Data Model¶
A Tree object is a shallow wrapper around a single,
invisible system root node. All visible toplevel nodes are direct children of
this root node.
Trees expose methods to iterate, search, copy, filter, serialize, etc.
A Node represents a single element in the tree.
It is a shallow wrapper around a user data instance, that adds navigation,
modification, and other functionality.
Main Node attributes are initialized on construction:
- parent (Node, readonly)
The direct ancestor (
node.parentis None for toplevel nodes). Usemove_to()to modify.- children (List[Node], readonly)
List of direct subnodes, may be empty. Children are added or removed using methods like
add(),prepend_child(),remove(),remove_children(),move_to(), etc.- data (object|str, readonly)
The user data payload. This may be a simple string or an arbitrary object instance.
Internally the tree maintains a map from data_id to the referencing Nodes. Useset_data()to modify this value.
The same data instance may be referenced by multiple nodes. In this case we call those nodes clones.- data_id (int, readonly):
The unique key of a data instance. This value is calculated as
hash(data)by default, but can be set to a custom value.
Useset_data()to modify this value.- meta (dict, readonly):
Nodeuses __slots__ for memory efficiency. As a side effect, it is not possible to assign new attributes to a node instance.
The meta slot can be used to attach arbitrary key/value pairs to a node.
Useget_meta(),set_meta(),update_meta(), andclear_meta(), to modify this value.- node_id (int, readonly):
The unique key of a Node instance. This value is calculated as
id(node)by default, but can be set to a custom value in the constructor. It cannot be changed later.- kind (str, readonly):
Used by
TypedNode(see Typed child nodes).
Adding Nodes¶
Nodes are usually created by adding a new data instance to a parent:
from nutree import Tree, Node
tree = Tree("Store")
n = tree.add("Records")
n.add("Let It Be")
n.add("Get Yer Ya-Ya's Out!")
n = tree.add("Books")
n.add("The Little Prince")
tree.print()
Tree<'Store'>
├── 'Records'
│ ├── 'Let It Be'
│ ╰── "Get Yer Ya-Ya's Out!"
╰── 'Books'
╰── 'The Little Prince'
Chaining¶
Since node.add() return a Node object we can chain calls. The node.up() method allows to select an ancestor node and the node.tree return the Tree instance:
tree = Tree()
tree.add("A").add("a1").up().add("a2").up(2).add("B")
tree.print()
Tree<>
├── 'A'
│ ├── 'a1'
│ ╰── 'a2'
╰── 'B'
or for friends of code golf:
Tree().add("A").add("a1").up().add("a2").up(2).add("B").tree.print()
See also
See Working with Objects for details on how to manage arbitrary objects, dicts, etc. instead of plain strings.
Iteration¶
Iterators are available for the hole tree or by branch. Different traversal methods are supported:
for node in tree:
# Depth-first, pre-order by default
...
# Alternatively use `visit` with a callback:
def callback(node, memo):
if node.name == "secret":
# Prevent visiting the child nodes:
return SkipBranch
if node.data.foobar == 17:
raise StopTraversal("found it")
# `res` contains the value passed to the `StopTraversal` constructor
res = tree.visit(callback) # res == "found it"
See also
See Search and Navigate for details on traversal.