Modules in Python
Once a program grows to hundreds of lines, keeping all the code in one file gets awkward. Modules let you spread code across several files and pull in just the parts you need where you need them.
What is a module?
A module in Python is simply a file with a .py extension, containing Python code (functions, classes, variables) that can be imported and used in other programs.
Creating your own module and importing it
Creating a module in Python is simple: just write code in a file with a .py extension. Let's create mymath.py and place a few main-scripts next to it that show different ways to wire it in:
1# Import the whole module2import mymath3 4# All names are accessed via the mymath. prefix.5result = mymath.add(5, 3)6print(f"5 + 3 = {result}") # 5 + 3 = 87 8area = mymath.PI * 5 ** 29print(f"Area of a circle with radius 5: {area}")10# Area of a circle with radius 5: 78.5397511 On the left you'll find our module mymath.py and four variants of main_*.py, each showing a different import style. Switch between the tabs. Below is a short rundown of when each is useful.
Importing the whole module
The most direct way: import mymath (see main_basic.py). All names stay "inside" the module and are accessed via a prefix: mymath.add, mymath.PI. Handy when you need many functions from one module and want to keep the origin of each name visible.
Importing specific elements
from mymath import add, multiply (see main_specific.py). Pull in only what you need and use it without a prefix. Good for a couple of frequently-used functions; bad when, a hundred lines later, you forget where add came from.
Importing with renaming
import mymath as mm (see main_aliased.py). Useful if the module name is long (numpy as np, pandas as pd are the typical cases) or clashes with a local variable.
Importing all elements
from mymath import * (see main_star.py). Imports everything into the current namespace. In normal code this is usually a bad idea: you lose the source of names and can easily get conflicts. Fine in a REPL and sometimes in tests.
Where Python looks for modules
When you write import mymath, Python looks for a mymath.py file along the list of directories in sys.path. By default, this includes:
- The directory the script was started from (or the current directory in a REPL).
- Built-in modules (math, os, ...) that ship with Python.
- site-packages, the folder where pip install puts third-party packages.
The search stops at the first match. That means if a file sitting next to your script has the same name as a standard module, Python will pick yours instead of the system one. The classic trap: create random.py in your project and then wonder why random.randint doesn't work.
You can inspect sys.path like this:
Python 3.13import sys print(sys.path)
Special module variables
Python modules have several special variables.
The __name__ variable: module as a program vs. as a dependency
Inside Python, every module has a __name__ variable. When the module is imported, it holds the module's name ("mymath"). When the module is run directly (python mymath.py), Python puts the special value "__main__" in it. This lets you write a "do this only when run directly" block inside the module:
1"""Module with mathematical functions."""2 3PI = 3.141594 5def add(a, b):6 return a + b7 8def divide(a, b):9 if b == 0:10 raise ValueError("Cannot divide by zero")11 return a / b12 13# This block runs only on direct execution:14# python mymath.py15# It will not run when imported (import mymath) from another file.16if __name__ == "__main__":17 print(f"PI = {PI}")18 print(f"add(2, 3) = {add(2, 3)}")19 Run python mymath.py and you'll see the if-block's output. Run python main.py and that block will stay silent: the output will be just 8 from the call in main.py.
The __all__ variable: what ends up in from module import *
By default, from mymath import * imports every name that doesn't start with an underscore. If you want to pin down the module's public API, add an __all__ variable with a list of names.
1"""Module with mathematical functions."""2 3# Only these names will be picked up by from mymath import *4__all__ = ["PI", "add"]5 6PI = 3.141597_INTERNAL = "shouldn't escape"8 9def add(a, b):10 return a + b11 12def subtract(a, b):13 return a - b14 15def _round_helper(value):16 """Internal helper, not for public use."""17 return round(value, 2)18 Through from mymath import * only PI and add came through, exactly the names listed in __all__. The others (subtract, _INTERNAL, _round_helper) stay in the module and are only available via explicit import: from mymath import subtract.
Packages
When mymath.py grows (say, you have different groups of operations), you can split it across several files and bundle them into a package. A package in Python is a directory containing an __init__.py file. When you write import mathlib, Python sees that __init__.py and treats the directory as an importable package.
Let's turn our module into a package: spread the functions across topics, and assemble the public interface in __init__.py.
1"""mathlib: a package with mathematical functions."""2 3__version__ = "0.1"4 5# Re-export so users can write from mathlib import add6from mathlib.basic import PI, add, subtract7from mathlib.advanced import multiply, divide8 What changed:
- There are four files now, but from the outside this is still "one math module": the user writes from mathlib import add, just like before with mymath.
- mathlib/__init__.py controls what counts as the public API: the names listed via from .basic import ... and from .advanced import ... are accessible directly as mathlib.add.
- Inside a package, modules can refer to each other through relative imports: from . import basic (the same directory), from .. import other (the parent directory).
If the package grows further, you can split it into subpackages: for example, mathlib/stats/ with its own __init__.py and modules for statistical functions. The principle is the same, just one level deeper.
How to organise a module
A few practices that make a module easy to work with, both for you and for other readers.
One responsibility. Each module should own one specific concern: data_processing.py, auth.py, formatters.py. If a file already covers two unrelated topics, it's time to split.
Clear names. Descriptive but concise, in snake_case: user_interface.py, not ui.py and not UserInterface.py.
Order of contents inside the file:
- Docstring: a description of the module in triple quotes at the very top.
- Imports: the standard library first, then third-party packages, then your own modules.
- Constants: global values that don't change.
- Classes and functions: the main content.
- The if __name__ == "__main__": block: code that runs only on direct execution.
Explicit imports. Prefer from module import specific_thing over from module import *: it's clear what's actually used.
Private names with _. Prefix functions and variables that are "for internal use" with an underscore (_helper, _INTERNAL_CONST). It's a signal to others: "don't rely on this from the outside."
Understanding check
Let's check how well you've absorbed the topic of modules:
What will be printed when running main.py from the project below?
1import test_module2 3print(test_module.hello())4 