How to Remove a Node and Its Subtree in treelib: A Complete Guide
Use tree.remove_node(node_id) to delete a node and all its descendants in caesar0301/treelib, which returns the count of removed nodes and raises NodeIDAbsentError if the identifier does not exist.
The treelib library provides a pure Python implementation for managing tree data structures. When working with hierarchical data in the caesar0301/treelib repository, you often need to prune entire branches by removing a parent node along with all its children. This guide explains the internal mechanics and practical usage of the subtree removal methods.
Understanding the remove_node Method
The primary method for deleting a node and its subtree is remove_node, implemented in treelib/tree.py (lines 1602‑1628). This method performs a complete teardown of the target branch while maintaining tree integrity.
How It Works Internally
When you call tree.remove_node(identifier), the method executes the following steps according to the source code:
- Validation: Checks that the identifier exists in the internal
_nodesdictionary. If not, it raisesNodeIDAbsentError(lines 1606‑1608). - Subtree Collection: Uses
Tree.expand_tree(identifier)to gather the target node identifier plus every descendant identifier in the subtree. - Pointer Cleanup: Clears the backward pointer (
_bpointer) of each removed node and disconnects children from their parents. - Node Deletion: Removes all collected identifiers from the internal
_nodesdictionary. - Parent Update: Updates the parent node's forward pointer (
_fpointer) to remove references to the deleted branch.
Return Value and Error Handling
The method returns an integer representing the total number of nodes removed, including the root of the deleted branch. If you attempt to remove a non-existent node, the library raises NodeIDAbsentError with a descriptive message indicating the missing identifier.
Removing a Node and Its Subtree: Code Examples
Basic Node Removal
The following example demonstrates removing a single node and its subtree, adapted from examples/getting_started.py (lines 51‑55):
from treelib import Tree
tree = Tree()
tree.create_node("Root", "root")
tree.create_node("Bob", "bob", parent="root")
tree.create_node("Alice", "alice", parent="root")
# Before removal
tree.show()
# └── Root
# ├── Bob
# └── Alice
removed_count = tree.remove_node("bob")
print(f"Removed {removed_count} node(s)")
# After removal
tree.show()
# └── Root
# └── Alice
Removing a Deep Branch
When removing a node with multiple levels of descendants, remove_node automatically calculates the total count:
# Create a deeper hierarchy
tree.create_node("Grandpa", "grandpa")
tree.create_node("Dad", "dad", parent="grandpa")
tree.create_node("Mom", "mom", parent="grandpa")
tree.create_node("Child1", "c1", parent="dad")
tree.create_node("Child2", "c2", parent="dad")
tree.create_node("Sibling", "sib", parent="mom")
# Remove the entire "dad" branch (dad + c1 + c2)
count = tree.remove_node("dad")
print(f"Deleted {count} nodes") # Output: Deleted 3 nodes
Handling Non-Existent Nodes
Always wrap removal operations in try-except blocks to handle missing identifiers gracefully:
from treelib.exceptions import NodeIDAbsentError
try:
tree.remove_node("nonexistent")
except NodeIDAbsentError as exc:
print(exc) # Output: Node 'nonexistent' is not in the tree
Alternative: Using remove_subtree to Detach Branches
If you need to preserve the removed branch for re-insertion elsewhere rather than permanently deleting it, use remove_subtree. Implemented in treelib/tree.py (lines 1629‑1650), this method detaches the node and its descendants into a new Tree instance:
# Detach "mom" and her descendants into a separate tree
subtree = tree.remove_subtree("mom")
print(f"Subtree contains {subtree.size()} nodes")
# The original tree no longer contains "mom" or "sib"
# The subtree variable now holds an independent Tree object
Unlike remove_node, which returns an integer count, remove_subtree returns a fully functional Tree object containing the detached hierarchy. This is useful for moving branches between trees or archiving deleted sections.
Summary
- Use
tree.remove_node(identifier)to permanently delete a node and all its descendants incaesar0301/treelib. - The method returns the count of removed nodes and automatically updates parent-child relationships in the internal
_nodesdictionary. - Implementation resides in
treelib/tree.py(lines 1602‑1628), utilizingexpand_treeto collect subtree identifiers. - Use
remove_subtreeinstead if you need to detach a branch into a newTreeobject rather than deleting it. - Always handle
NodeIDAbsentErrorwhen removing nodes that might not exist.
Frequently Asked Questions
What happens to child nodes when I remove a parent node?
When you call remove_node on a parent, the method automatically removes all descendants in the subtree. According to the implementation in treelib/tree.py, the method uses expand_tree to collect every node identifier in the target branch, then deletes them all from the internal _nodes dictionary while clearing their backward pointers.
How do I check if a node exists before removing it?
You can verify existence using the tree.contains(identifier) method or by checking the internal _nodes dictionary, but the most Pythonic approach is to catch the NodeIDAbsentError exception. The remove_node method explicitly raises this error (defined in treelib/exceptions.py) when the identifier is not found, allowing you to handle missing nodes gracefully without pre-checking.
Can I recover a deleted subtree?
Once you execute remove_node, the nodes are permanently deleted from the tree's internal storage. However, if you need to preserve the branch for later use, use remove_subtree instead. This method, located at lines 1629‑1650 in treelib/tree.py, returns a new Tree instance containing the detached nodes, allowing you to re-insert them elsewhere or archive them before permanent deletion.
Does remove_node update the tree structure automatically?
Yes, the method handles all structural updates internally. After removing the target nodes from the _nodes dictionary, it updates the parent node's forward pointer (_fpointer) to remove references to the deleted children. This ensures the tree remains consistent without requiring manual pointer management or re-balancing operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →