Data Structures & Memory Model

Lists, dicts, algorithmic complexity basics, copying, and Python's memory model.

Python objects are reference counted (plus cyclic GC). Know shallow vs deep copy when mutating nested structures.

Core concepts

  1. Big-O intuition: List append O(1) amortized; membership in set O(1) average. — Python wiki: Time complexity
  2. copy vs deepcopy: list.copy() is shallow; copy.deepcopy for nested graphs. — Python docs: copy
  3. dict insertion order: Guaranteed 3.7+ — useful for deterministic serialization. — Python docs: dict
  4. dataclasses / pydantic: Structured data for APIs and agent tool schemas. — Python docs: dataclasses
  5. slots: Optional memory optimization for many instances. — Python docs: slots
python
from dataclasses import dataclass

@dataclass
class ToolCall:
    name: str
    arguments: dict

call = ToolCall("search", {"q": "python asyncio"})

Concept videos

YouTube lessons for these topics. On a large screen they open in the reading panel next to the roadmap, same as the article links on each core concept.

Explanation

Python hides memory until it does not — shared references and accidental mutation cause bugs that look like magic.

Big-O intuition

Big-O describes growth rate: doubling input size — how much slower? List append is cheap; scanning entire list for membership is expensive. Sets and dicts use hashes for fast lookups. Pick structures for the operations you repeat in hot paths.

copy vs deepcopy

Shallow copy duplicates the outer container but shares inner objects — like photocopying a folder but not the papers inside. deepcopy recursively clones nested graphs — slower but safe when mutating nested configs or API payloads.

dict insertion order

Modern dicts remember insertion order, so JSON serialization can be stable — useful for tests, caches, and signed payloads. Do not rely on order across Python versions older than 3.7.

dataclasses / pydantic

Dataclasses generate boilerplate (__init__, __repr__) for plain data carriers. Pydantic adds validation — ideal for HTTP bodies and LLM tool arguments where shape matters as much as syntax.

slots

By default instances store attributes in a dict — flexible but heavier. __slots__ fixes allowed attributes and saves memory when you create millions of small objects (simulations, parsers). Trade flexibility for footprint.

What Is It?

Python objects live on the heap; variables bind names to objects.

Reference counting frees most objects immediately; a cyclic garbage collector handles reference cycles.

Algorithmic complexity (O(n), O(n log n)) guides choices between list scans and set lookups.

Why Should I Care?

Accidental O(n²) loops appear in ETL scripts and API aggregations under load.

Mutating shared nested structures causes bugs that pytest catches only if you test copies.

Real World Analogy

Reference counting is like a library checkout counter — when nobody has borrowed a book (object), the library can shelve it away (free memory).

Visual Mental Model

text
object <-- ref -- variable
  refcount hits 0 -> dealloc
  cycles need GC collector

Beginner Explanation

Lists and dicts are references. Copy carefully. Big-O basics explain why nested loops on large datasets hurt.

Smallest Possible Example

python
import copy
a = {"items": [1]}
b = copy.deepcopy(a)
b["items"].append(2)
print(len(a["items"]))  # 1

Common Mistakes

❌ Using a list for membership tests in a hot loop.

List scan is O(n). Use a set when you only need presence checks.

❌ Assuming assignment copies data.

Assignment copies references. Use copy.copy or deepcopy when needed.

❌ Creating huge intermediate lists with eager comprehensions on streaming data.

Generators iterate lazily and save memory.

Real Project Example

Redis and PostgreSQL backends still see Python services that bottleneck on in-memory loops.

pandas users fight copy/view semantics — same reference ideas at DataFrame scale.

Build This Mini Project

Profile two versions of deduplicating 10k strings: list membership vs set. Log timings with time.perf_counter.

Interview Question

What is the difference between shallow and deep copy in Python?

copy.copy duplicates the container but not nested objects; copy.deepcopy recursively clones nested structures so mutations in the copy do not affect the original.

Key Takeaways

  • Names refer to objects.
  • Pick the right structure for the operation.
  • Know big-O for interviews and perf.
  • Prefer generators for large streams.

Resources

The 60-second story

A warehouse. Some shelves you can walk straight to. Some you search by opening every box. Big-O is not a math flex. It is “what happens to the walk when the warehouse gets a thousand times bigger.”

Appending to a list is usually tossing a box at the end of a row. Membership in a list is walking the row. Membership in a set is checking a numbered locker: average walk stays short. A dict is the same locker idea with a label on the item inside. Since Python 3.7, dicts remember the order you put labels in, so a walk down the lockers is stable. That matters when you serialize and want the same file tomorrow.

Copying is where people get robbed. list.copy() photographs the row of boxes but not the stuff inside boxes that are themselves shelves. Change an inner shelf and both “copies” see it. copy.deepcopy hires someone to duplicate the inner shelves too. It is slower because it is actually copying.

Dataclasses are a form with fields: name, price, quantity. You stop passing mystery tuples that mean (price, name) on Tuesday and (name, price) on Wednesday. Pydantic is that form with a bouncer who rejects a quantity of "lots". Agent tools love those forms because the model has to fill a shape, not an essay.

__slots__ is bolting the shelf to a fixed set of hooks so each box does not carry a full spare notebook of attributes. You care when you have millions of small boxes, not when you have twelve.

Map the warehouse.

  • Big-O is how the walk grows.
  • A list scan is checking every box.
  • A set or dict lookup is a locker number.
  • A shallow copy is a photo of the row.
  • A deep copy duplicates inner shelves.
  • A dataclass is a printed form.
  • __slots__ is a fixed set of hooks.
python
from dataclasses import dataclass

@dataclass
class Item:
    name: str
    qty: int

These structures exist because “a list of anything” falls over the moment you ask “have I seen this id?” in a loop. The locker is the engineering reason. Insertion-ordered dicts exist so you can stop sorting just to keep output stable.

Mental model: lockers versus walking the aisle. Use a set or dict when the question is “is it here?” Use a list when the question is “what is the sequence?” Copy like you mean it when the boxes contain shelves.

If you can tell someone why in on a list gets slower as the warehouse grows, and why a shallow copy still shares the inner shelf, this page stuck.

Threads in the same domain as this chapter — go deeper on iHateReading without leaving the roadmap.

Vector search using vector embeddings cover

Vector search using vector embeddings

Embeddings, similarity search, and RAG-shaped memory for apps.

Python AI agent SDKs in 2026: OpenAI vs Pydantic-AI vs smolagents vs Strands cover

Python AI agent SDKs in 2026: OpenAI vs Pydantic-AI vs smolagents vs Strands

Same three-tool agent in four frameworks — pick by execution model and testing needs.