Trees are a common data structure and there are many different ways to implement them.
This package provides a common interface to access and operate on these objects.
## Installing ##
Use [pip](https://pip.pypa.io/en/stable/getting-started/) to install abstracttree:
```sh
$ pip install --upgrade abstracttree
```
## Usage ##
You can start by implementing the mixins below. Otherwise, a lot of trees are supported out of the box.
### Mixins ###
```mermaid
graph TD;
Tree[Tree];
MutableTree[MutableTree];
DownTree[DownTree];
Tree[Tree];
MutableTree[MutableTree];
MutableDownTree[MutableDownTree];
MutableTree[MutableTree];
BinaryDownTree[BinaryDownTree];
BinaryTree[BinaryTree];
Tree-->MutableTree;
DownTree-->Tree;
DownTree-->MutableDownTree;
MutableDownTree-->MutableTree;
DownTree-->BinaryDownTree;
BinaryDownTree-->BinaryTree;
Tree-->BinaryTree;
```
| ABC | Inherits from | Abstract Methods | Mixin Methods |
|-------------------|---------------------------|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `DownTree` | | `children` | `nodes`, `nodes.preorder()`, `nodes.postorder()`, `nodes.levelorder()`, `descendants`, `leaves`, `levels`, `levels.zigzag()`, `is_leaf`, `transform()`, `nid` |
| `Tree` | `DownTree` | `parent` | `root`, `is_root`, `ancestors`, `path`, `siblings` |
| `MutableDownTree` | `DownTree` | `add_child()`, `remove_child()` | `add_children()` |
| `MutableTree` | `MutableDownTree`, `Tree` | | `detach()` |
| `BinaryDownTree` | `DownTree` | `left_child`, `right_child` | `children`, `nodes.inorder()`, `descendants.inorder()` |
| `BinaryTree` | `BinaryDownTree`, `Tree` | | |
For example, to create a simple tree with children (but no parent):
```python
from abstracttree import DownTree, print_tree
class MyTree(DownTree):
def __init__(self, value, children=()):
self.value = value
self._children = children
def __str__(self):
return "MyTree " + str(self.value)
@property
def children(self):
return self._children
tree = MyTree(1, children=[MyTree(2), MyTree(3)])
print_tree(tree)
# This generates the following output:
# MyTree 1
# ├─ MyTree 2
# └─ MyTree 3
```
### Generics ##
Unfortunately, not all trees inherit from the mixins above. Yet, some objects still have treelike behaviour.
Therefore, AbstractTree provides support for a slightly weaker protocol.
The following objects are `TreeLike`:
- All objects that support `obj.children` and `obj.parent`.
- Builtins classes `pathlib.Path` and `zipfile.Path`.
- Third party tree classes from [anytree](https://github.com/c0fec0de/anytree), [bigtree](https://github.com/kayjan/bigtree), [itertree](https://github.com/BR1py/itertree) and [littletree](https://github.com/lverweijen/littletree).
The following objects are `DownTreeLike`:
- All objects that support `obj.children`.
- Anything implementing `DownTree`.
- Recursive collections like lists, tuples, sets, dicts. This can be useful when dealing with json-data.
This can be tested using `isinstance`:
```python
isinstance(Path(r"C:\\Windows\System"), TreeLike) # True
isinstance(range(100), DownTreeLike) # True
isinstance(range(100), TreeLike) # False
isinstance(5, DownTreeLike) # False
isinstance("some text", DownTreeLike) # False (even though it might be considered a collection by python).
```
### Basic functions
On downtreelikes:
```python
children(node) # Children of node
label(node) # String representation of node (similar to str, but output excludes parent and children)
nid(node) # Address of node (similar to id, but supports delegates).
eqv(node1, node2) # Check if 2 nodes have the same identity (similar to is, but supports delegates)
```
Additionally, on treelikes:
```python
parent(node) # Parent of node or None if node is root of its own tree.
root(node) # Find root of this tree.
```
Examples:
```python
>>> from abstracttree import *
>>> children([1, 2, 3])
[1, 2, 3]
>>> children({"name": "Philip", "children": ["Pete", "Mariam"]})
[MappingItem(key="name", value="Philip"), MappingItem(key="children", value=["Pete", "Miriam"])]
>>> parent(Path(r"C:\\Windows\System"))
Path(r"C:\\Windows")
>>> label(Path(r"C:\\Windows\System"))
"System"
>>> eqv(Path(r"C:\\Windows\System"), Path(r"C:\\Windows\System"))
True
>>> eqv([1, 2, 3], [1, 2, 3])
False
```
### Iterators
On downtreelikes:
```python
nodes(tree) # Iterate through all nodes in tree (in no particular order).
descendants(node) # Children and grand-(grand-*)-children of node.
leaves(root) # Leaves reachable from root
```
If you want to iterate though the nodes in a specific order, use:
```python
preorder(node) # Nodes in preorder (root comes first).
postorder(node) # Nodes in postorder (root comes last).
levelorder(node) # Nodes near the root come before later nodes.
```
These will return tuples with (node, item). The item-object contains information about the depth of the node.
Additionally, on treelikes:
```python
ancestors(node) # Ancestors of node.
path(node) # Path from root to this node including this node.
siblings(node) # Siblings of node
```
### Adapters ###
To upgrade a `TreeLike` to a full `Tree` use `as_tree`.
```python
path_tree = as_tree(pathlib.Path("my_documents")) # Optionally pass `children`, `parent`, `label`.
# Iterate over all its descendants
for node in path_tree.descendants:
path_obj = node.value # Get back a Path-object from TreeAdapter
```
There is also `TreeAdapter` to help with classes that are very different.
### Exporting ###
Export to various formats
```python
print_tree(tree)
# If matplotlib is installed
plot_tree(tree)
# These may require graphviz or Pillow to be installed.
to_dot(tree)
to_mermaid(tree)
to_latex(tree)
to_reportlab(tree)
to_image(Path('.'), "filetree.png", how="dot")
to_image(DownTree, "tree_hierarchy.svg", how="mermaid")
to_pillow(tree).show()
```
Raw data
{
"_id": null,
"home_page": null,
"name": "abstracttree",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "tree, datastructure, hierarchy, taxonomy, newick, graphviz, mermaid",
"author": null,
"author_email": "lverweijen <lauwerund@gmail.com>",
"download_url": null,
"platform": null,
"description": "Trees are a common data structure and there are many different ways to implement them.\r\nThis package provides a common interface to access and operate on these objects.\r\n\r\n## Installing ##\r\n \r\nUse [pip](https://pip.pypa.io/en/stable/getting-started/) to install abstracttree:\r\n\r\n```sh\r\n$ pip install --upgrade abstracttree\r\n```\r\n\r\n## Usage ##\r\n\r\nYou can start by implementing the mixins below. Otherwise, a lot of trees are supported out of the box.\r\n\r\n### Mixins ###\r\n\r\n```mermaid\r\ngraph TD;\r\nTree[Tree];\r\nMutableTree[MutableTree];\r\nDownTree[DownTree];\r\nTree[Tree];\r\nMutableTree[MutableTree];\r\nMutableDownTree[MutableDownTree];\r\nMutableTree[MutableTree];\r\nBinaryDownTree[BinaryDownTree];\r\nBinaryTree[BinaryTree];\r\nTree-->MutableTree;\r\nDownTree-->Tree;\r\nDownTree-->MutableDownTree;\r\nMutableDownTree-->MutableTree;\r\nDownTree-->BinaryDownTree;\r\nBinaryDownTree-->BinaryTree;\r\nTree-->BinaryTree;\r\n```\r\n\r\n| ABC | Inherits from | Abstract Methods | Mixin Methods |\r\n|-------------------|---------------------------|---------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|\r\n| `DownTree` | | `children` | `nodes`, `nodes.preorder()`, `nodes.postorder()`, `nodes.levelorder()`, `descendants`, `leaves`, `levels`, `levels.zigzag()`, `is_leaf`, `transform()`, `nid` |\r\n| `Tree` | `DownTree` | `parent` | `root`, `is_root`, `ancestors`, `path`, `siblings` |\r\n| `MutableDownTree` | `DownTree` | `add_child()`, `remove_child()` | `add_children()` |\r\n| `MutableTree` | `MutableDownTree`, `Tree` | | `detach()` |\r\n| `BinaryDownTree` | `DownTree` | `left_child`, `right_child` | `children`, `nodes.inorder()`, `descendants.inorder()` |\r\n| `BinaryTree` | `BinaryDownTree`, `Tree` | | |\r\n\r\nFor example, to create a simple tree with children (but no parent):\r\n\r\n```python\r\nfrom abstracttree import DownTree, print_tree\r\n\r\nclass MyTree(DownTree):\r\n def __init__(self, value, children=()):\r\n self.value = value\r\n self._children = children\r\n \r\n def __str__(self):\r\n return \"MyTree \" + str(self.value)\r\n\r\n @property\r\n def children(self):\r\n return self._children\r\n\r\ntree = MyTree(1, children=[MyTree(2), MyTree(3)])\r\nprint_tree(tree)\r\n\r\n# This generates the following output:\r\n# MyTree 1\r\n# \u251c\u2500 MyTree 2\r\n# \u2514\u2500 MyTree 3\r\n```\r\n\r\n### Generics ##\r\n\r\nUnfortunately, not all trees inherit from the mixins above. Yet, some objects still have treelike behaviour.\r\nTherefore, AbstractTree provides support for a slightly weaker protocol.\r\n\r\nThe following objects are `TreeLike`:\r\n- All objects that support `obj.children` and `obj.parent`.\r\n- Builtins classes `pathlib.Path` and `zipfile.Path`.\r\n- Third party tree classes from [anytree](https://github.com/c0fec0de/anytree), [bigtree](https://github.com/kayjan/bigtree), [itertree](https://github.com/BR1py/itertree) and [littletree](https://github.com/lverweijen/littletree).\r\n\r\nThe following objects are `DownTreeLike`:\r\n- All objects that support `obj.children`.\r\n- Anything implementing `DownTree`.\r\n- Recursive collections like lists, tuples, sets, dicts. This can be useful when dealing with json-data.\r\n\r\nThis can be tested using `isinstance`:\r\n\r\n```python\r\nisinstance(Path(r\"C:\\\\Windows\\System\"), TreeLike) # True\r\nisinstance(range(100), DownTreeLike) # True\r\nisinstance(range(100), TreeLike) # False\r\nisinstance(5, DownTreeLike) # False\r\nisinstance(\"some text\", DownTreeLike) # False (even though it might be considered a collection by python).\r\n```\r\n\r\n### Basic functions\r\n\r\nOn downtreelikes:\r\n```python\r\nchildren(node) # Children of node\r\nlabel(node) # String representation of node (similar to str, but output excludes parent and children)\r\nnid(node) # Address of node (similar to id, but supports delegates).\r\neqv(node1, node2) # Check if 2 nodes have the same identity (similar to is, but supports delegates)\r\n```\r\n\r\nAdditionally, on treelikes:\r\n```python\r\nparent(node) # Parent of node or None if node is root of its own tree.\r\nroot(node) # Find root of this tree.\r\n```\r\n\r\nExamples:\r\n```python\r\n>>> from abstracttree import *\r\n>>> children([1, 2, 3])\r\n[1, 2, 3]\r\n>>> children({\"name\": \"Philip\", \"children\": [\"Pete\", \"Mariam\"]})\r\n[MappingItem(key=\"name\", value=\"Philip\"), MappingItem(key=\"children\", value=[\"Pete\", \"Miriam\"])]\r\n>>> parent(Path(r\"C:\\\\Windows\\System\"))\r\nPath(r\"C:\\\\Windows\")\r\n>>> label(Path(r\"C:\\\\Windows\\System\"))\r\n\"System\"\r\n>>> eqv(Path(r\"C:\\\\Windows\\System\"), Path(r\"C:\\\\Windows\\System\"))\r\nTrue\r\n>>> eqv([1, 2, 3], [1, 2, 3])\r\nFalse\r\n```\r\n\r\n### Iterators\r\n\r\nOn downtreelikes:\r\n```python\r\nnodes(tree) # Iterate through all nodes in tree (in no particular order).\r\ndescendants(node) # Children and grand-(grand-*)-children of node.\r\nleaves(root) # Leaves reachable from root\r\n```\r\n\r\nIf you want to iterate though the nodes in a specific order, use:\r\n```python\r\npreorder(node) # Nodes in preorder (root comes first).\r\npostorder(node) # Nodes in postorder (root comes last).\r\nlevelorder(node) # Nodes near the root come before later nodes.\r\n```\r\nThese will return tuples with (node, item). The item-object contains information about the depth of the node.\r\n\r\nAdditionally, on treelikes:\r\n```python\r\nancestors(node) # Ancestors of node.\r\npath(node) # Path from root to this node including this node.\r\nsiblings(node) # Siblings of node\r\n```\r\n\r\n### Adapters ###\r\n\r\nTo upgrade a `TreeLike` to a full `Tree` use `as_tree`.\r\n\r\n```python\r\npath_tree = as_tree(pathlib.Path(\"my_documents\")) # Optionally pass `children`, `parent`, `label`.\r\n\r\n# Iterate over all its descendants\r\nfor node in path_tree.descendants:\r\n path_obj = node.value # Get back a Path-object from TreeAdapter\r\n```\r\n\r\nThere is also `TreeAdapter` to help with classes that are very different.\r\n\r\n### Exporting ###\r\n\r\nExport to various formats\r\n```python\r\nprint_tree(tree)\r\n\r\n# If matplotlib is installed\r\nplot_tree(tree)\r\n\r\n# These may require graphviz or Pillow to be installed.\r\nto_dot(tree)\r\nto_mermaid(tree)\r\nto_latex(tree)\r\nto_reportlab(tree)\r\n\r\nto_image(Path('.'), \"filetree.png\", how=\"dot\")\r\nto_image(DownTree, \"tree_hierarchy.svg\", how=\"mermaid\")\r\nto_pillow(tree).show()\r\n```\r\n",
"bugtrack_url": null,
"license": null,
"summary": "Abstract base classes for tree data structures",
"version": "0.2.0",
"project_urls": {
"Changelog": "https://lverweijen.github.io/AbstractTree/CHANGES.html",
"Documentation": "https://lverweijen.github.io/AbstractTree/",
"Homepage": "https://github.com/lverweijen/abstracttree",
"Issues": "https://github.com/lverweijen/abstracttree/issues",
"Repository": "https://github.com/lverweijen/abstracttree"
},
"split_keywords": [
"tree",
" datastructure",
" hierarchy",
" taxonomy",
" newick",
" graphviz",
" mermaid"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "a68f392e1498a08e150ccdf481bc739e7e3758c0ef43b3d17baab2cf2d4ecbf2",
"md5": "d13bcbf69223d134365fdfe07f0bb893",
"sha256": "dc2343d5d312bdfe07624ebcaccc4ffff937147787f0837f3a8df2a88d96be23"
},
"downloads": -1,
"filename": "abstracttree-0.2.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "d13bcbf69223d134365fdfe07f0bb893",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 27758,
"upload_time": "2025-07-20T22:38:29",
"upload_time_iso_8601": "2025-07-20T22:38:29.827995Z",
"url": "https://files.pythonhosted.org/packages/a6/8f/392e1498a08e150ccdf481bc739e7e3758c0ef43b3d17baab2cf2d4ecbf2/abstracttree-0.2.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-20 22:38:29",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "lverweijen",
"github_project": "abstracttree",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "abstracttree"
}