Python Story

Chapter #1 – Python Models

CPython, bytecode, dynamic typing, duck typing, GIL, memory

1.0 Prologue

Understanding how Python executes code, manages types, and handles memory makes every other chapter easier to reason about. This chapter covers the key conceptual models that distinguish Python from statically typed, compiled languages.

1.1 CPython and the Interpreter

The reference implementation of Python is CPython, written in C. When you run a .py file the interpreter:
  1. Parses source into an abstract syntax tree (AST).
  2. Compiles the AST to bytecode (stored in .pyc files inside __pycache__/).
  3. Executes the bytecode on the CPython virtual machine.
Other implementations exist — PyPy (JIT-compiled, faster for long-running code), Jython (JVM), MicroPython (embedded) — but CPython is the standard.

1.2 Dynamic Typing

Python is dynamically typed: types are associated with objects, not with variable names. A variable is simply a reference (a label) that can point at any object at any time. x = 42 # x refers to an int object x = "hello" # now x refers to a str object x = [1, 2, 3] # now x refers to a list object Type errors are caught at runtime, not at compile time. Optional type hints (PEP 484) let you annotate types for static analysis tools like mypy, but they have no effect at runtime.

1.3 Duck Typing

Python uses duck typing: if an object supports the required operations, it works — regardless of its declared type. "If it walks like a duck and quacks like a duck, it is a duck." def total_length(items): # works with any iterable whose elements have len() return sum(len(item) for item in items) total_length(["hello", "world"]) # 10 total_length(("ab", "cde")) # 5 Duck typing enables highly reusable code but shifts the responsibility for type correctness to the programmer (or to a type checker like mypy).

1.4 Everything Is an Object

In Python every value is an object: integers, strings, functions, classes, modules, and even None. Each object carries:
  • A type (accessed via type(obj))
  • An identity — unique id in memory (via id(obj))
  • A value
print(type(42)) # <class 'int'> print(type(print)) # <class 'builtin_function_or_method'> print(type(int)) # <class 'type'> — classes are objects too

1.5 The Global Interpreter Lock (GIL)

CPython uses a Global Interpreter Lock: a mutex that ensures only one thread executes Python bytecode at a time. This simplifies memory management but limits CPU-bound parallelism in multi-threaded code. Practical implications:
  • I/O-bound tasks benefit from threading because threads release the GIL while waiting for I/O.
  • CPU-bound tasks should use multiprocessing (separate processes, each with their own GIL) or C extensions that release the GIL.
  • Python 3.13 introduces an experimental free-threaded build that removes the GIL.

1.6 Memory Management

CPython manages memory with reference counting: each object stores a count of how many references point at it. When the count reaches zero the object is immediately deallocated. Reference cycles (A → B → A) can prevent counts from reaching zero. A supplemental cyclic garbage collector (the gc module) detects and collects cycles periodically. Small integer caching: CPython pre-allocates integers in [-5, 256], so a is b may be True for small ints even when created independently. Always use == for value equality.

1.7 Mutability

Python objects are either mutable or immutable:
Immutable Mutable
int, float, bool, complex list, dict, set
str, bytes, tuple bytearray, user-defined classes (usually)
frozenset  
Assignment never copies an object — it creates a new reference to the same object. Mutable objects must be explicitly copied (list.copy(), copy.deepcopy()) when independent copies are needed.

1.8 Epilogue

This chapter described how Python runs code, why types live on objects rather than variables, the trade-offs of the GIL, and the mutability model that governs data sharing. The next chapter surveys the full breadth of the language.

1.9 References

Python Data Model — python.org
CPython internals — Real Python
gc module — python.org
PEP 703 — Making the GIL Optional