Modules, Packages, pip and Virtual Environments
Splitting code into modules and packages, importing it, and installing third-party packages in an isolated environment.
What a module is
A module is a file of Python code that
other code can import. Any file ending in .py is a
module; its name is the file name without the extension, so
prices.py is the module prices. The
functions, classes and variables defined at the top level of that
file become the module's contents.
Modules solve two problems. They let you split a growing program into files that each do one job, and they let you reuse code: a function written once in a module can be imported by any number of programs. Python itself ships with hundreds of modules, the standard library, and the rest of this page uses them for examples.
For example, a small module for a shop could look like this:
# prices.py
VAT_RATE = 0.21
def with_vat(amount):
"""Return the amount including VAT, rounded to cents."""
return round(amount * (1 + VAT_RATE), 2)
A second file in the same folder can then use it with
import prices and call
prices.with_vat(10.0).
Importing
The import statement loads a module and gives your code a name to reach it by. The first time a module is imported, Python runs its code from top to bottom and keeps the resulting module object in memory. Importing it again, from any file, reuses that object instead of running the code a second time. By convention, all imports go at the top of a file.
import module
The plain form binds the module itself to a name. You then reach its contents with a dot, which makes it obvious where each name comes from.
import math
print(math.sqrt(16))
print(math.pi)
print(math.floor(7.8))
4.0 3.141592653589793 7
import module as alias
as gives the module a different name in your file. This
is useful for long names or for well-known community abbreviations
such as import numpy as np.
import statistics as stats
temperatures = [18.5, 21.0, 19.5]
print(stats.mean(temperatures))
print(stats.median(temperatures))
19.666666666666668 19.5
from module import name
from … import … copies selected names from the module
into your file, so you use them without the module prefix. The
module name itself is not defined afterward.
from math import pi, sqrt
radius = 2
print(round(pi * radius ** 2, 2))
print(sqrt(81))
12.57 9.0
You can rename here too: from math import sqrt as root.
Why to avoid from module import *
from math import * imports every public name in the
module at once. It saves typing, but it hides where names come from,
and it can silently replace names you already have. After the import
below, pow is no longer the built-in function but
math.pow, which always returns a float:
print(pow(2, 3))
from math import *
print(pow(2, 3))
8 8.0
Import the names you need explicitly, or import the module and use the dot. Linters and the PEP 8 style guide both discourage wildcard imports.
How Python finds modules
When you write import prices, Python first checks
whether the module is already loaded. If not, it searches the
folders in the list sys.path, in order, and uses the
first match. That list starts with:
- The folder that contains the script you ran (or the current folder, in the REPL).
-
The folders in the
PYTHONPATHenvironment variable, if it is set. - The standard library.
-
The
site-packagesfolder where third-party packages are installed.
You can print the list to see it on your own machine:
import sys
for folder in sys.path:
print(folder)
Some modules, such as sys (and, in many builds,
math), are built into the interpreter itself and are
found before this search even starts.
Shadowing a standard module
Because the script's own folder comes first, a file of yours with
the same name as a standard module wins. Suppose you are learning
about random numbers and save your experiment as
random.py:
# random.py (a bad file name)
import random
print(random.randint(1, 6))
Running python3 random.py fails. The line
import random finds your file instead of the
standard module, so random has no randint:
AttributeError: partially initialized module 'random' has no attribute 'randint' (most likely due to a circular import)
Python 3.13 and later add a hint to this message, suggesting you
rename the file. The same problem can break other modules that
import random behind the scenes, such as
statistics.
Running versus importing: __name__
Python automatically sets a variable
__name__ in every
module. It holds the module's name. There is one exception: in the
file you run directly, Python sets __name__ to the
string "__main__".
import math
print(math.__name__)
print(__name__)
math __main__
That makes it possible for a file to be both a reusable module and a
runnable program. Put the code that should only run when the file is
started directly under an if test:
def celsius_to_fahrenheit(celsius):
return celsius * 9 / 5 + 32
def main():
for celsius in (0, 20, 100):
print(celsius, "°C =", celsius_to_fahrenheit(celsius), "°F")
if __name__ == "__main__":
main()
0 °C = 32.0 °F 20 °C = 68.0 °F 100 °C = 212.0 °F
Run this file as python3 convert.py and it prints the
table. Another file can import convert to use
celsius_to_fahrenheit() without the table being
printed, because in that case __name__ is
"convert".
Packages
A package is a folder of modules that is imported as one unit. It lets you group related modules and give them a shared name, the same way folders group files. A package can contain subpackages, which are simply folders inside it.
shop/
├── main.py
└── billing/
├── __init__.py
├── invoices.py
└── taxes.py
From main.py, the modules in billing are
reached with dotted names:
# main.py
import billing.taxes
from billing import invoices
from billing.taxes import with_vat
print(billing.taxes.with_vat(10.0))
print(with_vat(25.0))
The __init__.py file
The file __init__.py marks a folder as a regular
package. It runs once, when the package is first imported, and it is
often empty. It can also import the most useful names from the
package's modules, so users can write
from billing import with_vat instead of reaching into
billing.taxes.
The standard library and third-party packages
The modules you have used so far — math,
statistics, sys — belong to the
standard library: they are
installed together with Python, so every Python installation has
them.
Everything else is a third-party package, written
by someone outside the core Python team. Well-known examples are
requests (HTTP), pandas (data tables) and
flask (web applications). These packages are published
on PyPI, the Python Package Index at
pypi.org: the official public repository of Python packages, with
hundreds of thousands of projects. Before you can import one, you
have to install it.
Installing packages with pip
pip is Python's standard package installer. It downloads packages from PyPI, together with the packages they depend on, and installs them where Python can import them. The common commands:
python3 -m pip install requests # install the latest version
python3 -m pip install "requests==2.32.3" # install an exact version
python3 -m pip install --upgrade requests # upgrade to the latest version
python3 -m pip uninstall requests # remove a package
python3 -m pip list # show installed packages
python3 -m pip show requests # details of one package
On Windows, replace python3 with py (or
python).
Why python3 -m pip
Many systems also have a plain pip command, but a
computer often has more than one Python installed, and
pip may belong to a different one than
python3. python3 -m pip runs pip as a
module of exactly the interpreter you name, so packages always land
where that interpreter looks for them. Inside an activated virtual
environment (below), pip and
python -m pip are the same thing.
Freezing requirements
pip freeze prints every installed package with its
exact version. Saved to a file named requirements.txt,
that list lets anyone recreate the same set of packages:
python3 -m pip freeze > requirements.txt # save the list
python3 -m pip install -r requirements.txt # install everything in it
A requirements file is plain text, one package per line:
certifi==2024.8.30 charset-normalizer==3.4.0 idna==3.10 requests==2.32.3 urllib3==2.2.3
Commit requirements.txt to version control; it is how
other people, and servers, install what your project needs.
Virtual environments
By default, pip installs packages for the whole Python installation.
That causes trouble quickly: project A needs version 1 of a package,
project B needs version 2, and only one can be installed. A
virtual environment
solves this. It is a folder with its own Python executable and its
own site-packages, so each project gets a separate,
isolated set of packages that cannot conflict with other projects or
with the system.
Creating and activating one
The venv module in the standard library creates a
virtual environment. Run it once, in the project folder. The name
.venv is a common convention. On Debian and Ubuntu,
install the venv module first:
sudo apt install python3-venv.
python3 -m venv .venv
Then activate it in each new terminal session. Activation
puts the environment's python and pip
first on your path, and most shells show its name in the prompt.
# macOS and Linux
source .venv/bin/activate
# Windows (PowerShell)
.venv\Scripts\Activate.ps1
# Windows (Command Prompt)
.venv\Scripts\activate.bat
While it is active, python and pip refer
to the environment, and everything you install stays inside
.venv. When you are done, leave it with:
deactivate
A typical start of a new project therefore looks like this (macOS or Linux):
python3 -m venv .venv
source .venv/bin/activate
python -m pip install requests
python -m pip freeze > requirements.txt
The externally-managed-environment error
On many recent Linux distributions (such as Debian 12 and Ubuntu
23.04 and later), and with Homebrew's Python on macOS,
pip install outside a virtual environment stops with an
error:
error: externally-managed-environment × This environment is externally managed
This is deliberate. The system's Python is managed by the operating
system's package manager, and system tools depend on it; packages
installed with pip could overwrite or break those. The fix is
exactly what this page recommends anyway: create a virtual
environment and install there. For Python packages the system
provides, you can also use the operating system's package manager
(for example apt install python3-requests).