Python Modules
Python Modules and Packages
Section titled “Python Modules and Packages”Modules are a fundamental way to organize Python code. They allow you to group related functions, classes, and variables into separate files, promoting code reusability, maintainability, and logical structure. A module is essentially a Python file (.py) containing Python definitions and statements.
Think of modules as toolboxes. Instead of having all your tools scattered everywhere, you group related tools (like wrenches or screwdrivers) into specific boxes. When you need a specific tool, you grab the right toolbox (import the module) and then pick the tool you need.
Example: Creating a Simple Module
Section titled “Example: Creating a Simple Module”Let’s create a module named helpers.py:
"""A simple module with helper functions."""
PI = 3.14159
def greet(name): """Greets the person with the given name.""" print(f"Hello, {name}!")
class Calculator: """A basic calculator class.""" def add(self, x, y): return x + yThe import Statement
Section titled “The import Statement”To use the definitions from a module in another Python file (e.g., main.py), you use the import statement. The basic syntax is:
import module_name# Or import multiple modulesimport module1, module2# Or preferred style (one import per line)import module_oneimport module_twoWhen Python encounters import helpers, it searches for helpers.py (or other module types) in its search path. Once found, it executes the module’s code (if not already imported) and makes its definitions available via the module name prefix.
Example (main.py in the same directory as helpers.py):
import helpers
helpers.greet("Alice")print(f"The value of PI is approximately: {helpers.PI}")
calc = helpers.Calculator()result = calc.add(5, 3)print(f"5 + 3 = {result}")Running python main.py produces:
Hello, Alice!The value of PI is approximately: 3.141595 + 3 = 8A module is loaded and executed only once, the first time it is imported during a program’s execution. Subsequent imports of the same module simply retrieve the already loaded module object.
The from...import Statement
Section titled “The from...import Statement”This statement allows you to import specific names (functions, classes, variables) directly into the current script’s namespace, avoiding the need for the module prefix.
from module_name import name1# Or import multiple namesfrom module_name import name1, name2# Or import and renamefrom module_name import name1 as alias_nameExample:
from helpers import greet, PIfrom helpers import Calculator as Calc # Renaming
greet("Bob")print(f"PI: {PI}")
my_calc = Calc()print(f"10 + 5 = {my_calc.add(10, 5)}")This avoids prefixing with helpers. but introduces the imported names directly into the current scope. Be mindful of potential name collisions.
The from...import * Statement
Section titled “The from...import * Statement”You can import all public names (those not starting with an underscore _) from a module using:
from module_name import *Warning: This practice is generally discouraged in production code. It imports potentially many names into the current namespace, making it unclear where a specific name originated and increasing the risk of name conflicts. It’s acceptable in interactive sessions or sometimes within specific module patterns (__init__.py).
Module Search Path
Section titled “Module Search Path”When you import my_module, Python searches for it in this order:
- The directory containing the input script (or the current directory if interactive).
- Directories listed in the
PYTHONPATHenvironment variable (if set). The format is platform-dependent (e.g., colon-separated on Unix/macOS, semicolon-separated on Windows). - Installation-dependent default paths (usually within the Python installation’s
site-packagesdirectory).
The search path is accessible programmatically via sys.path, which is a list of strings.
import sysprint(sys.path)The PYTHONPATH Variable
Section titled “The PYTHONPATH Variable”PYTHONPATH is an environment variable you can set to add extra directories where Python will look for modules. Its syntax mirrors the system’s PATH variable.
Example (Linux/macOS):
export PYTHONPATH="/home/user/my_python_libs:/another/path"Example (Windows Command Prompt):
set PYTHONPATH=C:\Users\user\my_python_libs;D:\other_pathWhile PYTHONPATH can be useful, managing dependencies with virtual environments and standard package installation (pip) is the preferred modern approach.
Namespaces and Scope
Section titled “Namespaces and Scope”A namespace is a mapping from names to objects (like a dictionary). Python uses namespaces to avoid name collisions.
Python follows the LEGB rule for scope resolution: Local -> Enclosing function locals -> Global -> Built-in.
To modify a global variable from within a function, you must use the global keyword. To modify a variable in an enclosing function’s scope (in nested functions), use the nonlocal keyword (Python 3+).
x = 100 # Global
def outer_func(): y = 20 # Enclosing local def inner_func(): nonlocal y # Modify enclosing scope variable global x # Declare intent to modify global variable z = 5 # Local y += 5 x += 1 print(f"Inner: x={x}, y={y}, z={z}") inner_func() print(f"Outer: x={x}, y={y}")
outer_func()print(f"Global: x={x}")The dir() Function
Section titled “The dir() Function”The built-in dir() function, when called without arguments, returns a list of names in the current local scope. When called with an object (like a module) as an argument, it returns a sorted list of names defined by that object (its attributes, methods, etc.).
import math
print(dir(math)) # List names in the math module
my_var = 10def my_func(): passprint(dir()) # List names in the current (global) scopeOutput might include special names like __name__ (module’s name) and __file__ (path to the module file).
The globals() and locals() Functions
Section titled “The globals() and locals() Functions”These built-in functions return dictionaries representing the global and local namespaces, respectively.
Reloading Modules (importlib.reload)
Section titled “Reloading Modules (importlib.reload)”Modules are typically loaded only once. If you modify a module’s source file while your main program is running (common during interactive development or long-running applications), the changes won’t be reflected automatically. To force a reload of an already imported module, use the importlib.reload() function (note: reload was a built-in in Python 2, but moved to importlib in Python 3).
import importlibimport helpers
# ... code using helpers ...
# Assume helpers.py was modifiedimportlib.reload(helpers)
# Now use the updated version of helpershelpers.greet("Updated User")Use reload with caution, as it can lead to inconsistencies if not managed carefully, especially with complex dependencies or stateful objects.
Packages in Python
Section titled “Packages in Python”Packages provide a way to structure Python’s module namespace by using “dotted module names”. A package is essentially a directory containing Python modules and potentially other sub-packages. To be recognized as a regular package (prior to Python 3.3, and still common practice), the directory must contain a file named __init__.py (which can be empty).
Example Structure:
sound/ __init__.py formats/ __init__.py wavread.py aiffread.py ... effects/ __init__.py echo.py surround.py ... filters/ __init__.py equalizer.py ...You can import modules from packages using dot notation:
import sound.effects.echosound.effects.echo.apply_echo(...)
from sound.formats import wavreadwavread.read_wavfile(...)
from sound.filters.equalizer import Equalizereq = Equalizer()The __init__.py File
Section titled “The __init__.py File”The __init__.py file serves several purposes:
Example (sound/effects/__init__.py):
print("Initializing sound effects package...")
# Make echo and surround available directly via 'from sound.effects import ...'from . import echofrom . import surround
# Control 'from sound.effects import *'__all__ = ["echo", "surround"]With this __init__.py, you could do:
import sound.effects
sound.effects.echo.apply_echo(...) # Access via sub-moduleNote: Python 3.3 introduced Implicit Namespace Packages (PEP 420), which allow creating packages without an __init__.py file, primarily useful for splitting a single large package across multiple directories.