Skip to contentSkip to navigation
Module 06 — Functional PythonLesson 03Intermediate

Modules & Package Architecture

25 min study17 Deep Sections

Master modular architecture and namespace management in Python. Deep-dive into the Python import system, sys.modules caching, module introspection with dir(), vars(), and help(), package hierarchy with __init__.py, __all__ export control, circular import resolution, and the if __name__ == '__main__' execution guard.

Prerequisites & Prior Knowledge

`functions``scope-recursion`

Core Mastery Goals

  • •Understand how Python's import machinery resolves files, compiles bytecode (.pyc), and caches loaded modules in sys.modules
  • •Employ multiple import styles (absolute, selective, aliased) and understand the hazards of wildcard imports
  • •Inspect module attributes, docstrings, and symbol dictionaries using dir(), help(), and vars()
Section 02/Runtime Mechanics

Module Architecture & The Python Import Machinery

In Python, every single .py source file is an independent module. Modules encapsulate variables, functions, and classes within their own isolated global namespace, preventing name collisions across applications.

1sys.modules Cache

Python inspects sys.modules. If already loaded, it immediately returns the cached module in O(1) time.

2sys.path Search

If not cached, Python searches directories in sys.path: script directory, standard library, and site-packages.

3Bytecode .pyc

Source is compiled to CPython bytecode and cached inside __pycache__/*.pyc for fast restarts.

4Execution & Binding

Executes top-level code once, creates __dict__, inserts into sys.modules, and binds symbol in scope.

Demonstrating sys.modules caching and singleton module instance reuse.
import sys
import math

# Verify that math is recorded in sys.modules
print("Is math cached?", 'math' in sys.modules)
print("Cached object identity matches:", sys.modules['math'] is math)
print("Total loaded modules in runtime:", len(sys.modules))
Section 03/Syntax & Scope

Import Syntax Variations & Namespace Hygiene

Import StyleSyntax PatternNamespace ImpactProduction Recommendation
Direct Moduleimport mathBinds `math`. Access via `math.sqrt()`Recommended (clear provenance)
Specific Namesfrom math import sqrt, piBinds only `sqrt` and `pi` directly in local scopeExcellent for frequently called functions
Aliasedimport numpy as npBinds alias to module or functionStandard for ecosystem conventions (np, pd, plt)
Wildcard (Star)from math import *Dumps all public symbols into local scopeAnti-pattern! Pollutes scope and shadows variables
Python
import math
from math import ceil as round_up, floor as round_down

print("Square root of 81:", math.sqrt(81))
print("Pi constant:", round(math.pi, 4))
print("Ceiling with alias:", round_up(4.2))
print("Floor with alias:", round_down(6.9))
Section 04/Reflection Tools

Inspecting Modules: dir(), help(), and vars()

dir(module)

Returns an alphabetically sorted list of attribute and method names defined on the module object.

help(module)

Launches interactive or rendered documentation strings, signatures, and module docstrings.

vars(module)

Exposes module.__dict__, the actual hash table dictionary holding all live bindings.

Python
import math

# 1. dir() lists exported symbols and metadata
math_attrs = dir(math)
print("First 5 math attributes:", math_attrs[:5])
print("Is 'sqrt' present?", 'sqrt' in math_attrs)

# 2. vars() retrieves the module's namespace mapping
math_ns = vars(math)
print("math.__name__:", math_ns['__name__'])
print("math.pi via vars():", math_ns['pi'])
print("vars(math) is math.__dict__:", math_ns is math.__dict__)
Section 05/Batteries Included

Essential Standard Library: math & random with seed()

The standard library numerical modules provide rock-solid C-level math and pseudo-random numbers. Using random.seed() sets the internal Mersenne Twister state, making pseudo-random outputs 100% deterministic and repeatable for unit tests.

Security Invariant: Never use random for passwords, cryptographic keys, auth tokens, or session IDs! The Mersenne Twister PRNG can be reversed from 624 observed outputs. Always use Python's cryptographically secure secrets module instead.
Python
import random

# Seed ensures identical sequences across runs
random.seed(42)
print("Roll 1:", random.randint(1, 10))
print("Roll 2:", random.randint(1, 10))

options = ["rock", "paper", "scissors"]
print("Random choice:", random.choice(options))

# Reseeding with 42 reproduces the exact same output
random.seed(42)
print("Reseeded Roll 1:", random.randint(1, 10))
Section 06/Interactive Simulator

Interactive Import & Namespace Visualizer

Toggle import styles to see how symbols enter caller scope and identify namespace pollution hazards.

Caller Scope (globals() dictionary):1 Symbol Injected
"math": <module 'math' (built-in)>
Access: math.sqrt(25), math.pi, math.sin(0)
Section 07/Filesystem Architecture

Structuring Hierarchical Packages with __init__.py

Packages are directories of modules. Explore the file tree to see how __init__.py organizes clean sub-namespaces.

Package Filesystem
my_app/
utils/
my_app/__init__.pySource Inspection
# my_app/__init__.py
"""Top-level package initialization and public API exposure."""
from .core import Engine
from .models import UserAccount

__all__ = ["Engine", "UserAccount"]
__version__ = "1.0.0"
print("my_app package initialized!")
Section 08/API Contracts

Controlling Public API Surfaces with __all__

Inside a module or package, defining __all__ = ['sym1', 'sym2'] dictates the exact explicit list of symbols exported when another module executes from my_package import *. Internal helper functions and private variables (prefixed with _) are automatically excluded.

Python
# Example module: geometry.py
__all__ = ['calculate_circle_area', 'PI']

PI = 3.14159
_PRIVATE_CONSTANT = 42

def calculate_circle_area(radius: float) -> float:
    """Calculate the area of a circle given its radius."""
    return PI * (radius ** 2)

def _internal_helper():
    """Internal calculation routine not exported in __all__."""
    return True

print("Public exports:", __all__)
print("Area of radius 5:", round(calculate_circle_area(5), 2))
Section 09/CLI vs Library Pattern

The if __name__ == '__main__' Execution Guard

Toggle execution context to see how Python dynamically switches __name__ between script and module modes.

Test Execution Context:
# Inside script.py:
def calculate_area(w, h): return w * h
if __name__ == "__main__":
print("Executing standalone CLI run...")
print("Area:", calculate_area(10, 5))
Interpreter Variable Value:__name__ = "__main__"
Standard Output:
Executing standalone CLI run...
Area: 50

Because the script was executed as the root program, __name__ evaluates to '__main__', triggering the CLI test block.

Section 10/Architectural Deadlocks

Circular Imports & 3 Architectural Remedies

A circular import occurs when Module A imports Module B, and Module B simultaneously imports Module A. Because Python creates the module object in sys.modules before its top-level code has completed, one module will attempt to access attributes that do not yet exist, raising:ImportError: cannot import name ... from partially initialized module.

1. Extract Shared Leaf Module

Best practice. Move shared data structures, interfaces, or models into a third module (e.g. models.py) which both A and B import downstream.

2. Deferred In-Function Import

Move the import b inside the specific function that uses it. Resolves circularity because all files are already loaded when the function runs.

3. Import Module Object Directly

Write import b instead of from b import func. Access via b.func() at runtime once module initialization finishes.

Python
# Demonstrating deferred import pattern within a function
def process_report_data(data):
    # Deferred import prevents circular import deadlock at file loading time
    import json
    return json.dumps({"processed": len(data)})

print(process_report_data([10, 20, 30]))
Section 11/Hands-On Code

Tiered Code Walkthroughs

Level 1 · Foundation

Inspecting the Import Cache in sys.modules

Difficulty: Beginner

Because Python caches imported modules in sys.modules, subsequent import statements in other files simply reuse the existing module object without re-executing top-level code.

Because Python caches imported modules in sys.modules, subsequent import statements in other files simply reuse the existing module object without re-executing top-level code. Run cleanly to earn +1 XP
import sys
import math

# Verify that math is recorded in sys.modules
print("Is math cached?", 'math' in sys.modules)
print("Cached object identity matches:", sys.modules['math'] is math)
print("Total loaded modules in runtime:", len(sys.modules))
Run cleanly to earn +1 XP: Select Run (or edit code first to experiment)! Runs that end in an error earn no XP.
Level 2 · Applied

Importing with Aliases and Specific Symbols

Difficulty: Intermediate

Aliasing allows resolving name conflicts (e.g., if you already have a local function named ceil) while keeping code concise and expressive.

Aliasing allows resolving name conflicts (e.g., if you already have a local function named ceil) while keeping code concise and expressive. Run cleanly to earn +1 XP
import math
from math import ceil as round_up, floor as round_down

print("Square root of 81:", math.sqrt(81))
print("Pi constant:", round(math.pi, 4))
print("Ceiling with alias:", round_up(4.2))
print("Floor with alias:", round_down(6.9))
Run cleanly to earn +1 XP: Select Run (or edit code first to experiment)! Runs that end in an error earn no XP.
Level 3 · Architecture

Deferred In-Function Import to Break Circularity

Difficulty: Advanced

Importing inside the function body defers execution until call time, resolving circular dependency deadlocks between interconnected modules.

Importing inside the function body defers execution until call time, resolving circular dependency deadlocks between interconnected modules. Run cleanly to earn +1 XP
# Demonstrating deferred import pattern within a function
def process_report_data(data):
    # Deferred import prevents circular import deadlock at file loading time
    import json
    return json.dumps({"processed": len(data)})

print(process_report_data([10, 20, 30]))
Run cleanly to earn +1 XP: Select Run (or edit code first to experiment)! Runs that end in an error earn no XP.
Section 12/Defensive Architecture

Common Traps & Import Anti-Patterns

Common Trap

Syntax or Name Typos in Modules, Imports & Packages

Buggy Pattern
# Mistyped variable or missing closing parenthesis
result = modules__imports___packages_val
Why it fails: Beginners frequently encounter typos or forgotten definitions before using an identifier.
Correct Production Fix
# Declare and verify variables before execution
val = 10
print("Defined correctly:", val)
Expected: NameError: name is not defined
Subtle Trap

Unintended State Mutation or Scope Leakage

Buggy Pattern
# Modifying shared state or unexpected variable shadowing
def update_state(data=[]):
    data.append(1)
    return data
Why it fails: Default arguments in Python are evaluated once at function definition time, not at invocation time.
Correct Production Fix
def update_state(data=None):
    if data is None:
        data = []
    data.append(1)
    return data
Expected: Default mutable argument accumulates values across separate function calls.
Architecture Trap

Resource Leaks & High-Scale Performance Regressions

Buggy Pattern
# Opening resources in a tight loop without closing or bounding memory
files = [open(f"log_{i}.txt", "w") for i in range(5000)]
Why it fails: Failing to release system resources leads to process termination under heavy production workloads.
Correct Production Fix
for i in range(5):
    with open(f"log_{i}.txt", "w") as f:
        f.write("Safe")
Expected: OSError: [Errno 24] Too many open files (file descriptor exhaustion)
Section 13/System Design

Enterprise Scale: Microservice Package Boundaries

In large distributed systems, strict package boundaries isolate business logic, database connectors, and HTTP controllers. Modules never import across horizontal boundaries (e.g. controllers never directly import database drivers); they communicate through clean dependency-injected interfaces defined in package root __init__.py.

Standard Production Layout
project_root/
├── src/
└── service_name/
├── __init__.py # Exports public client & domain models
├── api/ # REST / GraphQL routes
├── domain/ # Pure business logic (no side effects)
└── storage/ # D1 / SQL database adapters
└── tests/ # Mirror unit and integration suites
Section 14/Industry Implementations

Real-World Architecture & Scale

Developer Productivity

🟢 Simple Script: Local Developer Automation Tool

Individual developers and IT administrators automate everyday repetitive workflows.

import math
print("Pi value:", math.pi)
print("Square root of 25:", math.sqrt(25))
Cloud Microservices

🟡 Scaled Service: Backend API Service Integration

SaaS platforms run backend microservices processing requests concurrently.

# Service integration layer
def handle_request(payload: dict) -> dict:
    return {"status": "success", "processed": True}
Distributed Systems

🔴 Enterprise Production: High-Throughput Cloud Architecture

Microservice codebases enforce strict package boundaries to isolate business logic, database connectors, and API controllers.

# High-resilience production pattern
import time

def process_stream(events: list) -> int:
    processed = 0
    for event in events:
        if event:
            processed += 1
    return processed
Section 15/Knowledge Verification

Comprehensive Self-Check Quiz Booster

Test your understanding across all 16 conceptual, syntax, and output-prediction questions.

Question 1 of 16 • concept (easy)
What does import math let you write?
Question 2 of 16 • concept (easy)
Why call random.seed(0) in a learning example?
Question 3 of 16 • concept (medium)
When does code inside if __name__ == "__main__": run?
Question 4 of 16 • predict-output (easy)
What does this print?
import math
print(math.sqrt(81))
Question 5 of 16 • predict-output (easy)
What does this print?
import math
print(math.ceil(2.1))
Question 6 of 16 • predict-output (medium)
What does this print?
from math import ceil as round_up
print(round_up(4.2))
Question 7 of 16 • spot-the-bug (medium)
Which line is the bug after writing import math?
Question 8 of 16 • syntax (medium)
Which statement imports ceil from math and renames it round_up?
Question 9 of 16 • predict-output (hard)
What does this print?
import random
random.seed(0)
print(random.randint(1, 10))
print(random.choice(['red', 'blue', 'green']))
Question 10 of 16 • concept (hard)
What is a package?
Question 11 of 16 • concept (medium)
What does dir(math) return when called on the imported math module?
Question 12 of 16 • concept (medium)
What is the primary role of sys.modules in Python's import system?
Question 13 of 16 • concept (hard)
What does vars(module) return when passed a module object?
Question 14 of 16 • concept (hard)
What is the purpose of defining __all__ in a module?
Question 15 of 16 • concept (hard)
What happens if Module A and Module B import each other directly at the top level (circular import)?
Question 16 of 16 • concept (medium)
Why is 'from module import *' considered an antipattern in production code?
Section 16/Hands-On Practice

Guided Practice Challenge

Official Lesson ChallengeTarget Output: 6

Import the math module. Use math.floor to round 6.9 down, then print the result.

Expected output: 6

Write your import and function call in the interactive editor.
# Write your code below
Section 17/Summary & Forward Path

Key Takeaways & Architectural Cheatsheet

A module is an isolated file namespace; sys.modules caches loaded modules to ensure single-execution semantics.
sys.path dictates the directory search order for importing modules and third-party packages.
dir(module) lists available attributes; vars(module) returns the underlying namespace dictionary (module.__dict__).
Avoid 'from module import *' wildcard imports; they pollute scopes and conceal variable provenance.
Packages are directories of modules; __init__.py orchestrates initialization and __all__ dictates public exports.
The if __name__ == '__main__': idiom prevents script execution logic from running when a module is imported.
Break circular imports by refactoring shared code into independent leaf modules or using deferred in-function imports.
Up Next in Module 06

Lesson 04: Decorators & Function Wrapping

Master higher-order decorators, closure scopes, functools.wraps metadata preservation, and parametrized decorators.

Continue to Decorators

Worked example

Splitting a bill fairly per head needs ceiling maths — import it from the standard library instead of reinventing it.

Example
import math

total = 1000
people = 3
per_head = math.ceil(total / people)
print(per_head)

How it works, step by step

  1. import math brings the module into scope under its name.
  2. 1000 / 3 is 333.33…; ceil rounds UP so the host is not short-changed.
  3. Standard library = batteries included, tested by thousands.

Your notes

Sign in to keep private notes alongside this lesson.

Sign in to take notes

Modules & Package Architecture practice

Sign-in required

Import the math module. Use math.floor to round 6.9 down, then print the result.

Expected output: 6

Coding Practice is Locked

Sign in to access the interactive Python editor, write and run code checks against automated tests, use hints, and earn XP.

Practice quiz

Check yourself

15 questions with explanations after submission, so you can correct course as you go.

Modules & Package Architecture quiz

Locked

You must be signed in to take this quiz. Sign in to test your understanding, track your answers, and earn XP.

Lesson incomplete

Not completed

Create an ID or sign in to save lesson completion across devices.

Create ID to save
Module lesson progress0/5 complete
Module cheat sheet