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.
Last updated: August 2026

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:

  1. Direct Script Execution: If the file is executed directly from the command line (e.g., python billing_engine.py), the interpreter sets: __name__ == '__main__'
  2. Imported as a Module: If the file is loaded by another file via an import statement (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 temperature inside another script, __name__ evaluates to 'temperature', so the if block evaluates to False and 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:

AttributeDescriptionExample 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 sys or builtins) do not originate from Python source files on disk, so inspecting sys.__file__ may raise an AttributeError or return None depending 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:

  1. A timestamp reflecting the last modification time of the corresponding .py source file.
  2. 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 .pyc file 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.

Loading diagram...
Module Execution Flow and Bytecode Caching Lifecycle
Test Your Knowledge

A developer authors a module named string_utils.py containing:

# string_utils.py
print("Loaded string_utils")

def reverse_str(s):
    return s[::-1]

if __name__ == '__main__':
    print("Testing:", reverse_str("python"))
What is output when another script executes import string_utils?

A
B
C
D
Test Your Knowledge

What is the primary operational benefit of the .pyc files generated in the __pycache__ folder?

A
B
C
D
Test Your Knowledge

Where does Python obtain the string assigned to a module's built-in __doc__ attribute?

A
B
C
D
Test Your Knowledge

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?

A
B
C
D