Python 3 - Modules
Python 3 - Modules and Packages
Section titled “Python 3 - Modules and Packages”Modules are a fundamental concept in Python for organizing code. They allow you to group related functions, classes, and variables into separate files, making your code more manageable, reusable, and understandable.
Essentially, a module is simply a Python file (.py) containing Python definitions and statements. The file name becomes the module name (without the .py extension).
Example: Creating a Simple Module
Section titled “Example: Creating a Simple Module”Let’s create a module named greetings.py with a function:
def say_hello(name): """Prints a greeting message.""" print(f"Hello, {name}!")
# You could also define variables or classes herePI = 3.14159The import Statement
Section titled “The import Statement”To use the code from a module in another Python file (or the interactive interpreter), you use the import statement.
import module_nameWhen Python encounters import greetings, it looks for greetings.py in a specific list of directories (the module search path). If found, Python executes the module’s code (if any top-level code exists) and makes its contents available through the module name.
import greetings # Import the greetings module
# Access functions/variables using dot notationgreetings.say_hello("Alice")print(f"The value of PI from greetings module is: {greetings.PI}")Running main_script.py produces:
Hello, Alice!The value of PI from greetings module is: 3.14159A module is loaded and executed only once per Python session, even if it’s imported multiple times. This prevents redundant initialization.
The from...import Statement
Section titled “The from...import Statement”Alternatively, you can import specific names (functions, classes, variables) directly into the current script’s namespace using from...import.
from module_name import name1, name2Example: Importing only the say_hello function:
from greetings import say_hello
# Now call the function directlysay_hello("Bob")
# PI is not imported directly, so this would cause an error:# print(PI)This makes the code slightly shorter but can lead to naming conflicts if the imported name already exists in your script.
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 is generally discouraged in production code. It imports potentially many names into your current namespace, making it unclear where a particular function or variable originated and increasing the risk of name collisions. Use it sparingly, perhaps only in interactive sessions for convenience.
Executing Modules as Scripts (if __name__ == "__main__":)
Section titled “Executing Modules as Scripts (if __name__ == "__main__":)”Every Python module has a built-in variable called __name__. When a module is run directly as the main script (e.g., python my_module.py), its __name__ is set to the string "__main__". When the module is imported by another script, its __name__ is set to the module’s actual name (e.g., "my_module").
This allows you to write code within a module that only executes when the module is run directly, typically used for tests, examples, or making the module runnable.
def say_hello(name): """Prints a greeting message.""" print(f"Hello, {name}!")
PI = 3.14159
# This block runs ONLY when greetings.py is executed directlyif __name__ == "__main__": print("Running greetings.py as the main script.") say_hello("Direct Run User") print(f"PI value is: {PI}")If you run python greetings.py, you’ll see the output from the if block. If you import greetings into another script, that block won’t execute automatically.
Locating Modules (Module Search Path)
Section titled “Locating Modules (Module Search Path)”When you import my_module, Python searches for my_module.py (or a package named my_module) in this order:
- The directory containing the input script (or the current directory if interactive).
- Directories listed in the
PYTHONPATHenvironment variable (if set). - Installation-dependent default paths (e.g.,
.../site-packageswithin your Python installation or virtual environment).
The complete search path is available as a list of strings in sys.path (you need to import sys first).
Best Practice: For managing project-specific dependencies, using virtual environments (like venv or conda) is highly recommended. Virtual environments create isolated Python setups where you can install packages without affecting your global Python installation or other projects. This largely replaces the need to manually manage PYTHONPATH for typical development.
Namespaces and Scoping (Briefly Revisited)
Section titled “Namespaces and Scoping (Briefly Revisited)”A namespace is a mapping from names to objects (like variables, functions). Modules create their own global namespace. When you access greetings.say_hello, you’re looking up say_hello in the namespace associated with the greetings module.
Function calls create local namespaces. Python follows the LEGB rule for finding names: Local -> Enclosing function locals -> Global (module-level) -> Built-in.
The global keyword is used inside a function to indicate that an assignment should modify a global variable instead of creating a new local one. The nonlocal keyword (used in nested functions) does similarly for names in enclosing function scopes.
#!/usr/bin/env python3
money = 2000 # Global variable
def add_money(): # To modify the *global* money, uncomment the next line: # global money money = 1 # Creates a *local* variable 'money' if 'global' isn't used money = money + 1 print("Inside function (local money):", money)
print("Before calling function (global money):", money)add_money()print("After calling function (global money):", money) # Unchanged if 'global' wasn't usedThe dir() Function
Section titled “The dir() Function”The built-in dir() function, when called with a module object, returns a sorted list of names defined within that module.
#!/usr/bin/env python3import math
content = dir(math)print(content)
# Try with our moduleimport greetingsprint("\nContents of greetings module:")print(dir(greetings))This is useful for exploring the contents of a module. It lists functions, classes, variables, and special attributes like __name__ and __file__.
Reloading Modules (importlib.reload)
Section titled “Reloading Modules (importlib.reload)”As mentioned, modules are typically loaded only once. If you modify a module’s source file while your main script is running (common during interactive development or long-running applications), the changes won’t be reflected automatically.
In Python 3, the reload() function was moved from built-ins to the importlib module. You can use it to re-execute a module’s code and update its namespace.
import importlibimport greetings
# ... Make changes to greetings.py ...
# Reload the moduleimportlib.reload(greetings)
# Now calls will use the updated code from greetings.pygreetings.say_hello("After Reload")Note: reload() is primarily intended for interactive debugging sessions. Relying on it in production code is often a sign of design issues. It’s generally better to restart the application or design for dynamic configuration loading.
Packages: Organizing Modules
Section titled “Packages: Organizing Modules”For larger projects, you might want to organize related modules into a directory hierarchy. This is where packages come in. A package is simply a directory containing Python modules and a special (often empty) file named __init__.py.
The __init__.py file signals to Python that the directory should be treated as a package. It can also contain initialization code for the package or define what symbols are exported when using from package import * (via the __all__ list).
Consider this structure:
my_app/ __init__.py main.py utils/ __init__.py strings.py math_ops.py models/ __init__.py user.pyHere, my_app, utils, and models are packages because they contain __init__.py. You can import modules using dot notation:
# In my_app/main.py
import my_app.utils.strings # Method 1: Full pathfrom my_app.utils import math_ops # Method 2: Import submodulefrom my_app.models.user import User # Method 3: Import specific class/function
# Using the importscleaned_text = my_app.utils.strings.clean(" some text ")result = math_ops.add(5, 3)new_user = User("admin")
# --- Relative Imports (within the package) ---# In my_app/utils/strings.py, you might import math_ops:# from . import math_ops # '.' means current package ('utils')# from .. import main # '..' means parent package ('my_app')Packages allow you to create well-structured, large applications by grouping related functionality and avoiding naming conflicts.