Python
tree data structure
implementation
programming tutorial
data structures

How can I implement a tree in Python?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Python does not have one built-in tree type because “tree” can mean several different structures: a general n-ary tree, a binary tree, a search tree, or an immutable parse tree. For most everyday code, the cleanest implementation is a node object that stores a value and a list of children.

A Simple General-Purpose Tree Node

A tree node usually needs three things:

  • the node’s own value
  • a reference to its parent, if upward navigation matters
  • a collection of child nodes

A dataclass makes this compact and readable.

python
1from dataclasses import dataclass, field
2from typing import Any, List, Optional
3
4
5@dataclass
6class Node:
7    value: Any
8    parent: Optional["Node"] = None
9    children: List["Node"] = field(default_factory=list)
10
11    def add_child(self, value: Any) -> "Node":
12        child = Node(value=value, parent=self)
13        self.children.append(child)
14        return child
15
16
17root = Node("root")
18a = root.add_child("A")
19b = root.add_child("B")
20a.add_child("A1")
21a.add_child("A2")
22b.add_child("B1")

This representation is flexible enough for menus, file-like hierarchies, organization charts, and many recursive algorithms.

Traversing the Tree

Once you have nodes, traversal is usually the next task. A depth-first traversal is the natural first example.

python
1def walk_depth_first(node: Node):
2    yield node
3    for child in node.children:
4        yield from walk_depth_first(child)
5
6
7for node in walk_depth_first(root):
8    print(node.value)

This recursion is simple and expressive. If your tree can become very deep, an explicit stack may be safer than recursion to avoid hitting Python’s recursion limit.

Searching the Tree

A tree implementation becomes much more useful when it includes a basic search helper.

python
1def find_first(node: Node, target: str) -> Node | None:
2    if node.value == target:
3        return node
4
5    for child in node.children:
6        found = find_first(child, target)
7        if found is not None:
8            return found
9
10    return None
11
12
13match = find_first(root, "A2")
14print(match.value if match else "not found")

This is enough for many practical cases. If lookups need to be fast and frequent, you may also maintain a side dictionary from identifier to node.

When a Binary Tree Is More Appropriate

If each node should have at most two children and left-versus-right ordering matters, use explicit left and right attributes instead of a generic children list.

python
1from dataclasses import dataclass
2from typing import Optional
3
4
5@dataclass
6class BinaryNode:
7    value: int
8    left: Optional["BinaryNode"] = None
9    right: Optional["BinaryNode"] = None

That shape is more appropriate for binary search trees, heaps, and expression trees. The implementation should reflect the algorithmic rules you actually need instead of forcing every tree into one generic form.

Trees as Nested Dictionaries or Lists

For lightweight or read-only data, you may not need a custom class at all. Nested dictionaries or lists are sometimes enough.

python
1filesystem = {
2    "name": "root",
3    "children": [
4        {"name": "docs", "children": []},
5        {"name": "src", "children": [{"name": "main.py", "children": []}]},
6    ],
7}

That approach is easy to serialize to JSON, but it becomes awkward when you want methods, parent references, or mutation helpers. Object-based trees are usually more maintainable once behavior matters.

Design Questions That Matter Early

Before choosing an implementation, decide:

  • do nodes need parent references
  • is child order meaningful
  • should the tree be mutable or immutable
  • are values unique identifiers or arbitrary payloads
  • how deep can recursion go

Those answers influence whether a dataclass node, a binary structure, or a JSON-friendly nested container is the right fit.

Common Pitfalls

The biggest pitfall is building a tree class before deciding what kind of tree the problem really needs. A general n-ary node is flexible, but it may be the wrong fit for search-tree logic.

Another mistake is using a mutable default list in the constructor. Dataclasses should use field(default_factory=list) so each node gets its own children list.

Developers also forget about cycles. A tree should not let a node become its own descendant. If you add reparenting logic later, you should guard against that explicitly.

Finally, be careful with deep recursion. Recursive traversal is elegant, but extremely deep trees may require an iterative implementation.

Summary

  • In Python, a tree is usually best implemented as a node object with a value and child references.
  • A dataclass with children and optional parent fields is a strong general-purpose starting point.
  • Add traversal and search helpers early so the structure is actually usable.
  • Use binary-node fields only when the algorithm really depends on left and right children.
  • Choose the representation that matches your use case rather than assuming there is one universal tree implementation.

Course illustration
Course illustration

All Rights Reserved.