Introduction¶
Overview¶
anytree is split into the following parts:
Node Classes
Node
: a simple tree node with at least a name attribute and any number of additional attributes.AnyNode
: a generic tree node and any number of additional attributes.NodeMixin
: extends any python class to a tree node.
Node Resolution
Tree Iteration Strategies
PreOrderIter
: iterate over tree using pre-order strategyPostOrderIter
: iterate over tree using post-order strategyLevelOrderIter
: iterate over tree using level-order strategyLevelOrderGroupIter
: iterate over tree using level-order strategy returning group for every levelZigZagGroupIter
: iterate over tree using level-order strategy returning group for every level
Tree Rendering
RenderTree
using the following styles:
Basics¶
The only tree relevant information is the parent attribute. If None the node is root node. If set to another node, the node becomes the child of it.
>>> from anytree import Node, RenderTree
>>> udo = Node("Udo")
>>> marc = Node("Marc")
>>> lian = Node("Lian", parent=marc)
>>> print(RenderTree(udo))
Node('/Udo')
>>> print(RenderTree(marc))
Node('/Marc')
└── Node('/Marc/Lian')
Every node has a children
attribute with a tuple of all children:
>>> udo.children
()
>>> marc.children
(Node('/Marc/Lian'),)
>>> lian.children
()
Add: Single Node Attach
Just set the parent attribute and the node becomes a child node:
>>> marc.parent = udo
>>> print(RenderTree(udo))
Node('/Udo')
└── Node('/Udo/Marc')
└── Node('/Udo/Marc/Lian')
Delete: Single Node Detach
A node becomes a root node, if you set the parent attribute to None:
>>> lian.is_root
False
>>> lian.parent = None
>>> lian.is_root
True
The node is deleted from the tree:
>>> print(RenderTree(udo))
Node('/Udo')
└── Node('/Udo/Marc')
Modify Multiple Child Nodes
Assume the following tree:
>>> n = Node("n")
>>> a = Node("a", parent=n)
>>> b = Node("b", parent=n)
>>> c = Node("c", parent=n)
>>> d = Node("d")
>>> n.children
(Node('/n/a'), Node('/n/b'), Node('/n/c'))
Modifying the children attribute modifies multiple child nodes. It can be set to any iterable.
>>> n.children = [a, b]
>>> n.children
(Node('/n/a'), Node('/n/b'))
Node c is removed from the tree. In case of an existing reference, the node c does not vanish and is the root of its own tree.
>>> c
Node('/c')
Adding works likewise.
>>> d
Node('/d')
>>> n.children = [a, b, d]
>>> n.children
(Node('/n/a'), Node('/n/b'), Node('/n/d'))
>>> d
Node('/n/d')
Detach/Attach Protocol¶
A node class implementation might implement the notification slots
_pre_detach(parent)
, _post_detach(parent)
,
_pre_attach(parent)
, _post_attach(parent)
.
These methods are protected methods,
intended to be overwritten by child classes of NodeMixin
/Node
.
They are called on modifications of a nodes parent attribute.
Never call them directly from API.
This will corrupt the logic behind these methods.
>>> class NotifiedNode(Node):
... def _pre_detach(self, parent):
... print("_pre_detach", parent)
... def _post_detach(self, parent):
... print("_post_detach", parent)
... def _pre_attach(self, parent):
... print("_pre_attach", parent)
... def _post_attach(self, parent):
... print("_post_attach", parent)
Notification on attach:
>>> a = NotifiedNode("a")
>>> b = NotifiedNode("b")
>>> c = NotifiedNode("c")
>>> c.parent = a
_pre_attach NotifiedNode('/a')
_post_attach NotifiedNode('/a')
Notification on change:
>>> c.parent = b
_pre_detach NotifiedNode('/a')
_post_detach NotifiedNode('/a')
_pre_attach NotifiedNode('/b')
_post_attach NotifiedNode('/b')
If the parent equals the old value, the notification is not triggered:
>>> c.parent = b
Notification on detach:
>>> c.parent = None
_pre_detach NotifiedNode('/b')
_post_detach NotifiedNode('/b')
Important
An exception raised by _pre_detach(parent)
and _pre_attach(parent)
will prevent the tree structure to be updated.
The node keeps the old state.
An exception raised by _post_detach(parent)
and _post_attach(parent)
does not rollback the tree structure modification.
Custom Separator¶
By default a slash character (/) separates nodes. This separator can be overwritten:
>>> class MyNode(Node):
... separator = "|"
>>> udo = MyNode("Udo")
>>> dan = MyNode("Dan", parent=udo)
>>> marc = MyNode("Marc", parent=udo)
>>> print(RenderTree(udo))
MyNode('|Udo')
├── MyNode('|Udo|Dan')
└── MyNode('|Udo|Marc')
The resolver takes the custom separator also into account:
>>> from anytree import Resolver
>>> r = Resolver()
>>> r.glob(udo, "|Udo|*")
[MyNode('|Udo|Dan'), MyNode('|Udo|Marc')]