Python
tree data structure
implementation
programming tutorial
data structures

How can I implement a tree in Python?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

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.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.