So far each program has lived in one file. Real projects split code across many files so that each part has a clear job, can be tested on its own and can be reused. In Python, every .py file is a module, and a folder of modules is a package. The import statement connects them.
This unit explains what import really does: where Python looks for a module, why a module's code runs only once, and how a file with an unlucky name can break a library. It ends with a project layout that many Python projects use.
You will be able to:
- Import modules in each common form and explain what a module object and its namespace are
- Guard script only code with
if __name__ == "__main__": - Explain how Python finds a module through
sys.path, and diagnose a file that shadows a library - Build a package, use absolute and relative imports, and run a module with
python -m - Use the module cache,
importlib.reloadand__all__correctly, and untangle a circular import - Describe the
src/project layout and why it helps
Note. The examples in this unit write their module files into the current folder with
pathliband then callsys.path.insert(0, "."). When you runpython main.py, Python already puts the script's folder first onsys.path; the extra line makes the examples work however they are started, including withpython -I, which leaves that folder out. Give this unit a new, empty folder of its own, so that the files the examples create do not mix with your other practice files.
A module is a file
A module is a file of Python code. Its name is the file name without .py. Importing it runs the file's code once and gives you a module object whose attributes are the names the file defined. Each module has its own global namespace, the mapping from names to objects, so geometry.area and shapes.area can never clash.
math appears in the list because geometry imported it: an import binds a name in the importing module's namespace. The first import also stores the compiled bytecode in a __pycache__ folder next to the file, so later runs start faster (see Introduction to Python).
Import forms
| Statement | Binds | Use it as |
|---|---|---|
import math | math | math.sqrt(16) |
import numpy as np | np | np.array(...), a conventional short alias |
from math import sqrt, pi | sqrt, pi | sqrt(16) |
from math import factorial as fact | fact | fact(5) |
import os.path | os (with os.path loaded) | os.path.splitext(...) |
from math import * | every public name | avoid; see __all__ below |
PEP 8 asks for imports at the top of the file, one module per import line, grouped as standard library, then third party, then your own modules. import module keeps the origin visible at every use (math.sqrt); from module import name is shorter when you use a name often.
Scripts and modules: if __name__ == "__main__":
Every module has a __name__ attribute. When a file is imported, __name__ is the module's name. When the same file is run as the program, Python sets __name__ to "__main__". Testing for that lets one file act both as an importable module and as a script. The example also runs the file as a script with subprocess.run, which starts another program and collects its output (A tour of the standard library covers it properly):
Imported, the file defines greet and skips the guarded block. Run with python greetings.py (here through subprocess), it executes the block too. Put a script's work in a main() function and call it from the guard. Tests can then import the module without starting the program, and multiprocessing needs the guard whenever its workers reimport your main module, which they do with the spawn and forkserver start methods. Since Python 3.14 those are the defaults everywhere: spawn on Windows and macOS, forkserver on Linux (see Concurrency).
How Python finds a module
When you write import name, Python first checks the module cache, the dict sys.modules. If the module isn't there, it asks a series of finders: first for built in and frozen modules compiled into the interpreter (such as sys), then for a file or folder called name in each directory on sys.path, in order. The first match wins.
sys.path is a list of folders, normally built from:
- the folder of the script you ran (for
python -mor the REPL, the current folder); - the folders in the
PYTHONPATHenvironment variable, if it is set; - the standard library folders;
site-packages, wherepipinstalls third party packages (inside your virtual environment if one is active).
Your output will differ: it lists your own installation's folders, then the path of the standard library's json package. The -P option and the PYTHONSAFEPATH variable (both since 3.11) stop Python from putting the script's folder or the current folder first.
The shadowing pitfall
Because the script's folder comes first on sys.path, a file in your project with the same name as a library hides that library. A practice file called random.py, json.py, email.py or pandas.py next to your script is the classic case: the next import random anywhere in that program loads your file instead.
The block runs python dice.py in a child process and shortens the full path in the message. Its last two lines delete both files again. If you try this by hand, delete your random.py afterwards: while it sits in a folder, every program there that imports random gets your file instead. Since Python 3.13 the error message suggests the rename itself. A file that imports a third party library with its own name, such as a pandas.py containing import pandas as pd, imports itself, and the message ends with (consider renaming '...pandas.py' if it has the same name as a library you intended to import).
Pitfall. When a well known library suddenly "has no attribute" that the documentation promises, print
module.__file__. If it points into your project, rename your file. Never name your own modules after the standard library (sys.stdlib_module_nameslists them) or after packages you install.
Packages
A package is a folder of modules that you import with dotted names, such as shop.tax. A regular package contains a file called __init__.py. That file runs when the package is first imported; it may be empty, or it may import names from the submodules to give the package a tidy interface.
Absolute and relative imports
Inside a package, a module can import its neighbours in two ways:
- An absolute import names the full path from the top level package:
from shop.tax import add_vat. PEP 8 recommends this form. - A relative import starts with dots:
from .tax import add_vatmeans "from thetaxmodule in my own package", andfrom ..utils import helpergoes one package up. Relative imports work only inside packages.
Importing shop.pricing first imported the package shop (running __init__.py), then shop.pricing, which pulled in shop.tax. The second import of shop printed nothing: it came from the cache.
Running a module with python -m
A module that uses relative imports can't be run as a file path, because Python then treats it as a lone script outside any package. Run it with python -m and its dotted name from the project folder instead: Python imports the package first, sets __name__ to "__main__", and puts the current folder on sys.path.
You already use this form: python -m pip and python -m venv run modules from installed packages. If a package contains a __main__.py file, python -m package runs it.
Regular and namespace packages
A folder without __init__.py can still be imported. It becomes a namespace package (PEP 420), which may be spread over several folders on sys.path; Python merges them into one package. This lets separately installed distributions add modules to the same package name.
| Regular package | Namespace package | |
|---|---|---|
| Marker | has __init__.py | no __init__.py |
| Location | exactly one folder | one or more folders, merged |
| Package level code | __init__.py runs on first import | none |
__file__ | path of __init__.py | None |
| Typical use | your own application or library | plugin systems split across distributions |
For your own code, use regular packages. A folder that lacks __init__.py by mistake still imports, as a namespace package, but some tools then treat it differently; for example, setuptools' find_packages() skips it.
Modules are cached
Python runs a module's code once per process. Later imports, from any module, find it in sys.modules and return the same object. That makes a module a natural singleton: module level state is shared by everyone who imports it.
loading config appears once, although two modules imported config, and a change made through one importer is visible to the other.
Pitfall. The file you run is the module
__main__. If another module imports that file by its own name, the cache has no entry under that name, so the file runs a second time as a separate module with its own copies of every global. Keep the code you run as a script separate from the modules it imports.
importlib.reload
importlib.reload(module) runs the module's current source again inside the same module object. It is handy in the REPL or a notebook after you edit a file, but it has limits: names copied earlier with from module import name keep their old values, and objects created from old classes keep the old classes.
In a program, restart the process rather than reloading modules.
__all__ and star imports
from module import * binds every public name of the module, meaning every name that doesn't start with an underscore. If the module defines a list called __all__, only the names in that list are imported. __all__ doesn't hide anything else: module.name still reaches every attribute.
Avoid from module import * in your code. Readers can't tell where a name came from, linters can't spot undefined names, and imported names silently replace your own or built in ones:
os exports a low level os.open, which replaced the built in open.
Circular imports
A circular import happens when module A imports module B while B, directly or indirectly, imports A. When B asks for a name from A, A has only partly run, so the name may not exist yet:
The traceback ends with ImportError: cannot import name 'DEFAULT_STATUS' from partially initialized module 'orders' (most likely due to a circular import), followed by the file's path. Python had registered orders in sys.modules and started running it; its first line imported customers, which asked orders for a name not yet defined.
Ways out, best first:
- Move the shared code into a third module that both import. This is almost always the right fix, because a cycle usually means two modules share a concept that deserves its own home.
- Import the module instead of the name (
import orders) and look uporders.DEFAULT_STATUSinside the function, when it is called rather than at import time. - Move the import into the function that needs it. This works, but hides the dependency.
Laying out a project: the src/ layout
Once a project becomes an installable package, many projects use the src/ layout: the package lives in a src folder, with tests and configuration beside it.
Because src/ isn't on sys.path by default, you can't import inventory by accident from the project folder. You install the project into your virtual environment instead, usually in editable mode so that code changes take effect without reinstalling, and your tests then run against the package as users will get it:
pyproject.toml describes the project and how to build it; Packaging and tooling covers it, and Testing with pytest covers the tests/ folder. The simpler "flat" layout, with inventory/ directly in the project folder, also works, but it lets tests silently import the local copy instead of the installed one.
Check yourself
- What is a module, and what is its
__name__when it is imported? When it is run as a script? - In what order does Python look for a module when you write
import name? - You create
json.pynext to your script, andimport requestsnow fails inside the library. Why? - What is the difference between a regular package and a namespace package?
- Why does
python shop/pricing.pyfail onfrom .tax import add_vatwhilepython -m shop.pricingworks? - Two modules both
import config. How many times doesconfig.pyrun, and what follows from that? - What does
__all__control, and what doesn't it control? - What is the usual best fix for a circular import?
Suggested answers
- A file of Python code, loaded as a module object. Imported,
__name__is the module's name (for example"geometry"or"shop.tax"); run as the program, it is"__main__". - The module cache
sys.modulesfirst; then built in and frozen modules; then each folder onsys.pathin order: the script's folder (or the current folder),PYTHONPATH, the standard library andsite-packages. - Your folder comes first on
sys.path, so anyimport json, including the one insiderequests, loads your file instead of the standard library module. Rename the file. - A regular package has an
__init__.py, lives in one folder and can run package level code. A namespace package has no__init__.pyand may be merged from several folders; its__file__isNone. - Run as a file path, the module is a lone script with no parent package, so a relative import has nothing to be relative to.
python -mimports it asshop.pricing, as part of the package. - Once per process. Both importers share one module object, so module level state, such as a settings dict, is shared too.
- It lists the names that
from module import *binds. It doesn't hide anything frommodule.nameaccess or from explicitfrom module import name. - Move the shared code into a third module that both modules import.
Exercises
These exercises are optional practice. Pick the ones that help you.
- Script or module. From a block, write
textstats.pycontainingword_count(text), which returns the number of whitespace separated words, and a guardedmainblock that prints5 wordsfor the sentence"the quick brown fox jumps". Importing the module must print nothing. Check both: import it and testword_count, then run it withsubprocessand compare its output. - A tidy package. Create a package
unitswith a moduletemperature.pythat definesto_celsius(f),to_fahrenheit(c)and a private helper_round1(x)that rounds to one decimal place. Makeunits/__init__.pyreexport the two public functions and set__all__. Thenfrom units import *must bind exactlyto_celsiusandto_fahrenheit, andto_celsius(212)must give100.0. - Find the shadows. Write
shadowing_files(folder)that returns the sorted names of.pyfiles directly insidefolderwhose name, without.py, is a standard library module name (usesys.stdlib_module_names, available since 3.10). For a folder containingrandom.py,json.py,app.pyandnotes.txt, it returns['json.py', 'random.py']. - Plugin registry. Write
registry.pywith a dictPLUGINSand a decoratorregister(name)that stores the decorated function undername. Writecsv_plugin.pyandupper_plugin.py, each registering one text processing function when imported. Importing a plugin module twice must not run its registration twice. Check that the registry ends up with exactly two entries,csvandupper, and that callingPLUGINS["upper"]("hi")gives'HI'.
Solutions
1. Script or module.
The block's only output is the final line, which shows that importing textstats printed nothing.
2. A tidy package.
The star import runs inside a small, otherwise empty module, star_check, so dir() there lists exactly the names it created.
3. Find the shadows.
4. Plugin registry.
If the second import csv_plugin ran the module again, register("csv") would raise ValueError. It doesn't, because the module is taken from sys.modules, and all three modules share the one registry module and its PLUGINS dict.