2.2 Module Construction and Execution
Key Takeaways
- Every Python module possesses a built-in `__name__` attribute set to `'__main__'` when executed directly as a script, or set to the module's filename string when imported.
- The standard boilerplate `if __name__ == '__main__':` ensures standalone execution code (such as CLI entry points or test suites) does not run when the file is imported as a library.
- Module metadata attributes (`__file__`, `__doc__`, `__package__`) allow runtime introspection into the physical file path, module docstrings, and parent package relationships.
- CPython compiles source `.py` files into platform-independent bytecode `.pyc` files stored in the `__pycache__` directory to accelerate startup time on subsequent runs.
- Top-level statements in a module execute sequentially from top to bottom during initialization; side effects in module global scope should be minimized.
Module Construction and Execution
Creating custom modules in Python requires no special compiler commands or declarations: any text file containing valid Python code and saved with a .py extension instantly functions as an importable module. However, understanding how the Python interpreter loads, executes, compiles, and exposes metadata about a module is essential for mastering Python architecture and passing the PCAP exam.
1. Custom Module Definition and Top-Level Execution
When Python imports a module, it does not merely register function and class definitions—it executes every top-level statement in the file sequentially from line 1 to the end.
Consider the following custom module:
# File: billing_engine.py
print("[billing_engine] Initializing billing engine...")
TAX_RATE = 0.0825
BASE_CURRENCY = "USD"
def calculate_total(subtotal):
return subtotal * (1 + TAX_RATE)
print("[billing_engine] Initialization complete. Tax rate is", TAX_RATE)
When another script imports billing_engine:
# File: app.py
print("[app] Starting application...")
import billing_engine
print("[app] Application running. Total:", billing_engine.calculate_total(100))
Console Output:
[app] Starting application...
[billing_engine] Initializing billing engine...
[billing_engine] Initialization complete. Tax rate is 0.0825
[app] Application running. Total: 108.25
Side Effects During Import
Any executable statement placed outside a def or class block (such as print(), opening network connections, creating database tables, or modifying global state) constitutes a side effect. Best practice dictates that top-level module code should strictly define constants, functions, and classes rather than performing heavy runtime actions.
2. The __name__ Variable and the Main Idiom
Python automatically creates and injects several special "dunder" (double underscore) variables into every module's namespace before executing its code. The most critical of these is __name__.
Direct Execution vs. Module Import
The value of __name__ depends entirely on how the Python file was invoked by the interpreter:
- Direct Script Execution: If the file is executed directly from the command line (e.g.,
python billing_engine.py), the interpreter sets:__name__ == '__main__' - Imported as a Module: If the file is loaded by another file via an
importstatement (e.g.,import billing_engine), the interpreter sets:__name__ == 'billing_engine'
The Standard Guard: if __name__ == '__main__':
By leveraging this distinction, Python developers can create dual-purpose files that act as both importable libraries and standalone executable scripts:
# File: temperature.py
"""Temperature conversion utility module."""
def c_to_f(celsius):
return (celsius * 9 / 5) + 32
def f_to_c(fahrenheit):
return (fahrenheit - 32) * 5 / 9
# Execution Guard / Test Harness / CLI Entry Point
if __name__ == '__main__':
print("--- Running Standalone Temperature Diagnostics ---")
test_c = 100
print(f"{test_c}°C = {c_to_f(test_c)}°F") # 100°C = 212.0°F
test_f = 32
print(f"{test_f}°F = {f_to_c(test_f)}°C") # 32°F = 0.0°C
- When executed directly via
python temperature.py,__name__evaluates to'__main__', so the diagnostic tests run. - When imported via
import temperatureinside another script,__name__evaluates to'temperature', so theifblock evaluates toFalseand the diagnostics are silently skipped.
3. Module Metadata and Introspection Attributes
Every Python module object exposes valuable metadata attributes that can be inspected at runtime:
| Attribute | Description | Example Value |
|---|---|---|
__name__ | Name of the module or '__main__' | 'temperature' or '__main__' |
__doc__ | The module-level docstring (or None if omitted) | 'Temperature conversion utility module.' |
__file__ | Absolute or relative filesystem path to the module file | '/app/utils/temperature.py' |
__package__ | Name of the package to which the module belongs | '' (top-level) or 'utils.converters' |
__cached__ | Path to the compiled .pyc bytecode file | '/app/utils/__pycache__/temperature.cpython-312.pyc' |
import temperature
print("Module Name:", temperature.__name__)
print("Module Doc:", temperature.__doc__)
print("Module File:", temperature.__file__)
print("Parent Package:", temperature.__package__)
Note for Built-ins: Built-in C modules (such as
sysorbuiltins) do not originate from Python source files on disk, so inspectingsys.__file__may raise anAttributeErroror returnNonedepending on the Python implementation.
4. Python Bytecode Compilation and __pycache__
Python is an interpreted language that uses an intermediate bytecode compilation step. When a module is imported for the first time, CPython compiles the human-readable source code (.py) into platform-independent bytecode instructions executed by the Python Virtual Machine (PVM).
Source Code (.py) ──▶ CPython Compiler ──▶ Bytecode (.pyc) ──▶ Python Virtual Machine (PVM)
The __pycache__ Directory and .pyc Naming
To avoid recompiling unchanged source files every time a program starts, Python caches the compiled bytecode inside an automatically generated subdirectory named __pycache__ located in the same directory as the source file.
The cached bytecode files follow a strict naming convention specified in PEP 3147: <module_name>.<cpython-version>.pyc
For example:
temperature.cpython-312.pyc(CPython version 3.12)temperature.cpython-311.pyc(CPython version 3.11)temperature.pypy39.pyc(PyPy implementation)
The Bytecode Invalidation Mechanism
Python stores two metadata markers in the header of each .pyc file:
- A timestamp reflecting the last modification time of the corresponding
.pysource file. - A source file size check (or hash-based verification).
When an import statement executes, Python compares the source file's current modification timestamp against the timestamp recorded inside the .pyc file:
- If the source file has not changed, Python skips compilation and directly loads the bytecode into the PVM.
- If the source file was modified, Python recompiles the source, writes a new
.pycfile into__pycache__, and executes the fresh bytecode.
PCAP Exam Point: Bytecode caching does not make code execution faster once running; it only speeds up module loading and startup time by skipping the lexing, parsing, and bytecode compilation phase.
A developer authors a module named string_utils.py containing:
What is output when another script executes # string_utils.py
print("Loaded string_utils")
def reverse_str(s):
return s[::-1]
if __name__ == '__main__':
print("Testing:", reverse_str("python"))
import string_utils?
What is the primary operational benefit of the .pyc files generated in the __pycache__ folder?
Where does Python obtain the string assigned to a module's built-in __doc__ attribute?
When a Python file engine.py is executed directly from the terminal via python engine.py, what is the exact value of the special variable __name__ during execution?