Introduction
In earlier lessons, you learned how to install Python and run your first scripts. In this lesson, you’ll look inside the black box and see how Python actually runs your code. You’ll learn what the interpreter does, how Python turns your .py files into something it can execute, what bytecode and .pyc files are, and how interactive mode differs from running full scripts. You’ll also see how Python handles errors along the way.
Understanding this execution model will help you reason about performance, debugging, and why some errors appear only at runtime.
The Python Interpreter: From Source Code to Running Program
When you run a Python file, the interpreter doesn’t execute your text directly. It goes through several steps:
- Read your source code (
.pyfile) - Parse it into a syntax tree
- Compile it to bytecode
- Execute the bytecode in the Python Virtual Machine (PVM)
Let’s walk through these at a high level.
1. Parsing: Understanding Your Code Structure
Parsing is where Python checks if your program is syntactically valid and builds an internal representation of it.
- If your code is malformed (missing colons, bad indentation, etc.), Python raises a
SyntaxErrorat this stage. - Parsing happens both when running a script and when typing in the interactive prompt.
2. Compilation: Source Code → Bytecode
If parsing succeeds, Python compiles the syntax tree into bytecode:
- Bytecode is a low-level, platform-independent set of instructions.
- It is not machine code, but a format that the Python Virtual Machine understands.
You can see the bytecode for a simple function using the dis module:
import dis
def add(a, b):
return a + b
dis.dis(add)
# output (simplified and may differ slightly by version)
4 0 LOAD_FAST 0 (a)
2 LOAD_FAST 1 (b)
4 BINARY_ADD
6 RETURN_VALUE
Here, LOAD_FAST, BINARY_ADD, and RETURN_VALUE are bytecode instructions the VM will execute.
Bytecode and .pyc Files
To avoid recompiling the same file every time, Python caches compiled bytecode in .pyc files:
- Stored in a
__pycache__directory next to your source. - Named with version information, e.g.
main.cpython-311.pyc. - Automatically created and managed by Python — you don’t need to handle them manually.
How .pyc Fits into Execution
When you run a module:
- Python checks if a valid
.pycfile already exists. - If yes and it matches the current source (based on timestamps), Python loads the bytecode directly.
- If no (or outdated), Python recompiles the
.pyfile, executes it, and updates/creates the.pycfile.
This is why the first run of a large program may be slightly slower than subsequent runs.
High-Level Execution Flow in the Python VM
Once bytecode is ready (from a script or interactive input), the Python Virtual Machine:
- Loads the bytecode into a new frame (execution context)
- Initializes the module’s global namespace (variables, functions, imports)
- Executes bytecode instructions sequentially
- Manages function calls, loops, and control flow by jumping between bytecode instructions
Example:
# flow_example.py
def greet(name):
message = f"Hello, {name}!"
return message
user = "Python"
print(greet(user))
# output
Hello, Python!
Under the hood:
- The module code runs first: defines
greet, setsuser, callsprint(greet(user)). - Calling
greetcreates a new frame; its bytecode runs untilreturn. - Control returns to the caller, and
printoutputs the returned string.
Script Execution vs Interactive Mode (REPL)
Python can execute code in two main ways:
- Interactive mode – using the REPL (Read–Eval–Print Loop), e.g. the
pythonprompt - Script mode – running a
.pyfile (Above explanation)
What is the REPL?
The REPL repeatedly performs four steps for each line you enter:
- Read: take your input line (or block)
- Eval: parse and compile it, then execute it
- Print: display the result (if any)
- Loop: wait for the next input
Example (imagine this typed into the Python prompt):
>>> x = 10
>>> x + 5
15
>>> print(x * 2)
20
# output
15
20
The REPL is great for:
- Quick experiments
- Testing small pieces of logic
- Exploring libraries interactively
Script Execution
In script mode, Python:
- Reads the entire file
- Parses and compiles it (possibly using
.pyccache) - Executes top-to-bottom in a new module namespace
Example script:
# script_example.py
x = 10
def multiply_by_two(n):
return n * 2
result = multiply_by_two(x)
print("Result:", result)
If you run python script_example.py:
# output
Result: 20
You typically use scripts (or modules) for:
- Reusable programs
- Automation tasks
- Larger projects with multiple files
In practice, many developers prototype in the REPL and then move stable code into scripts.
Summary / Key Takeaways
In this lesson How Python Executes Code: Interpreter and Execution Model, you have learned:
- Python uses a multi-step execution pipeline: reading your
.pyfile, parsing it, compiling to bytecode, then executing that bytecode in the Python Virtual Machine. - Parsing happens before any code runs, and syntax problems at this stage produce
SyntaxErrorexceptions that prevent execution from starting. - Bytecode is a platform-independent instruction format, generated from your source once and then executed by the Python VM rather than running your text directly.
.pycfiles cache compiled bytecode in the__pycache__directory so that Python can skip recompilation when the source has not changed.- Interactive mode (REPL) and script execution use the same core machinery, but the REPL runs code line-by-line for quick experiments while scripts run entire modules for reusable programs.
- The Python VM executes bytecode within frames and namespaces, handling function calls, control flow, and variable scopes as it steps through instructions.
