Basics

Python Modules and Packages: import, pip, venv and Libraries Explained Simply

Updated: · Skillaria.Top

A module in Python is a .py file, a package is a folder of modules, and a library is a set of packages that you import with the import statement. Third-party libraries are installed with pip (or uv) into a venv virtual environment — an isolated folder with its own interpreter and packages for one project, so the versions used by different projects do not conflict.

A module in Python is an ordinary file with code, and import is the way to take functions and classes from it. Modules are combined into packages, packages into libraries, and so that third-party libraries of different projects do not get in each other's way, they are installed into a venv virtual environment. Let us go through the whole chain: what happens during an import, how a package is structured, how pip and venv work in Python, and what lazy imports introduced in Python 3.15 are.

Module, package, library: what is the difference

TermWhat it isExample
ModuleA single .py file; the module name is the file name without the extensioncategories.py → import categories
PackageA folder with modules and an __init__.py fileexpenses/report.py → from expenses.report import build
LibraryA ready-made set of modules and packages for some taskrequests, pandas, the standard pathlib
Distribution (a PyPI package)An archive that pip installspip install requests

In conversation the words "library", "package" and "module" are often mixed up — that is normal. What matters is that technically import always loads a module or a package, while pip installs a distribution from PyPI, which contains packages inside it.

How import works in Python

There are several forms of import, and all of them appear in real code:

import math                    # the whole module, accessed as math.sqrt
from math import sqrt, pi      # individual names
import datetime as dt          # an alias
from collections import Counter as C

print(math.sqrt(16), sqrt(9), pi)
print(dt.date.today())

Aliases are standard for large libraries: import numpy as np, import pandas as pd. It is better not to use the from module import * construction: it clutters the namespace and hides where a name came from.

What import actually does

  1. It looks for the module in the sys.modules cache. If it is already loaded, it simply returns it.
  2. If not, it searches for the file in the list of folders sys.path: the folder of the running script, the standard library, the site-packages of the environment.
  3. It executes the found file as a whole, from top to bottom.
  4. It puts the module object into the cache and binds it to the name.

Two practical conclusions follow. A repeated import of the same module is almost free — the code is not executed a second time. And all the top-level code in a module, including print and heavy calculations, runs at import time.

import sys
for p in sys.path:
    print(p)
print("math" in sys.modules)

if __name__ == "__main__"

Since an import executes the file, there must be a way to tell "I was run as a script" from "I was imported". This is what the __name__ variable is for: when the file is run, it equals "__main__"; when imported, it equals the module name.

# categories.py
def guess(title: str) -> str:
    return "food" if "cafe" in title.lower() else "other"

if __name__ == "__main__":
    print(guess("Cafe near home"))   # runs only with python categories.py

For a package there is an alternative: a __main__.py file inside it and the launch python -m expenses.

Standard Python modules

Python comes "with batteries included": hundreds of built-in modules are available right away, without installation. The most popular ones:

ModuleWhat for
os, sys, pathlib, shutilSystem, paths, files
json, csv, pickleData formats
datetime, time, calendarDate and time
math, random, statistics, fractionsNumbers
re, stringWorking with text
collections, itertools, functoolsData structures and iterators
logging, argparse, subprocessLogs, command-line arguments, running programs
asyncio, threadingAsynchronous code and threads

The full list of standard library modules is stored in sys.stdlib_module_names (Python 3.10+). To see what a module contains, use dir(math) and help(math).

Your own package: project structure

When there are many modules, they are grouped into a package. A common layout is the src-layout:

expenses-project/
├── pyproject.toml
└── src/
    └── expenses/
        ├── __init__.py
        ├── cli.py
        ├── parsing.py
        ├── categories.py
        └── report.py

Inside a package, modules import each other either through the package name (from expenses.categories import guess) or relatively (from .categories import guess). A relative import works only inside a package, not in a file run directly as a script.

pip in Python: installing libraries

Third-party libraries live in the PyPI catalogue, and they are installed by the pip utility, which comes with Python. The most reliable way is to call it through the interpreter — that way the library lands in exactly the Python you run your code with.

python -m pip install requests          # install
python -m pip install "pandas>=2.3"     # with a version constraint
python -m pip install --upgrade requests
python -m pip uninstall requests        # remove
python -m pip list                      # list installed packages
python -m pip show requests             # version and dependencies
python -m pip freeze > requirements.txt
python -m pip install -r requirements.txt

On Windows the py launcher is often used instead of python. If pip is not found, the built-in module installs it: python -m ensurepip --upgrade.

Popular Python libraries

There are hundreds of thousands of third-party packages on PyPI, but most projects use the same short list. Start with it rather than searching for exotic options.

TaskLibraries
HTTP requests and APIsrequests, httpx
Data analysis and tablespandas, numpy
Chartsmatplotlib
Web servicesfastapi, django, flask
Testspytest
Imagespillow (imported as PIL)

Note that the name used for installation and the name used for import do not always match. You install with pip install pillow but write from PIL import Image; you install opencv-python but import cv2. If import fails right after installation, check under which name the package is imported in its documentation.

Installing libraries on Linux

On recent Debian and Ubuntu, pip install into the system Python ends with the externally-managed-environment error (PEP 668). This is a safeguard: the system Python belongs to the OS package manager, and pip must not break it. The right solution is not --break-system-packages but a virtual environment.

venv in Python: a virtual environment

A virtual environment is a separate folder with its own copy of the interpreter (a link to it) and its own site-packages. Libraries installed in it are visible only to this project. So one project can use Django 4 while a neighbouring project uses Django 5, and they do not conflict.

The venv module is built into Python and does not need to be installed separately (on Debian/Ubuntu the system package python3-venv is sometimes required).

# create an environment in the .venv folder
python -m venv .venv

# activate: Linux and macOS
source .venv/bin/activate
# activate: Windows PowerShell
.venv\Scripts\Activate.ps1

# now pip installs packages into .venv
python -m pip install requests
python -c "import sys; print(sys.prefix)"

# leave the environment
deactivate

After activation, (.venv) appears at the start of the terminal line. The environment folder is not committed to git (add .venv/ to .gitignore) and is not moved between machines — instead you record the list of dependencies and recreate the environment. If PowerShell blocks running the activation script, allow it for the current user: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.

uv and pyproject.toml

Modern projects describe their dependencies in pyproject.toml. The uv manager, which we use in the course, combines creating the environment and installing: uv add pandas downloads the package, writes it into pyproject.toml and pins exact versions in uv.lock. A colleague clones the project, runs uv sync — and gets an identical environment.

[project]
name = "expenses"
version = "0.1.0"
requires-python = ">=3.15"
dependencies = [
    "pandas>=2.3",
]

[project.scripts]
expenses = "expenses.cli:main"

Lazy imports in Python 3.15 (PEP 810)

The main novelty of 3.15 on this topic is the lazy keyword. Heavy libraries take a long time to load: in a measurement made for the course on the python:3.15-slim image, a regular import pandas took about 415 ms, while lazy import pandas took about 13 ms. For a CLI utility that is run just to show --help, the difference is huge.

lazy import pandas as pd
lazy from expenses.report import build_report

def main() -> None:
    ...
    df = pd.read_csv(path)   # the real loading of pandas happens here

Instead of the module, a proxy object (types.LazyImportType) is bound to the name, and the real import happens at the first access to the name. If that access never happens, no time is spent at all.

Limitations of lazy import

  • Allowed only at the top level of a module: inside a function, a class or a try/except it is a SyntaxError.
  • lazy from x import * is forbidden.
  • ModuleNotFoundError surfaces not at start-up but at the first use.
  • Side effects of a module (registering plugins and so on) shift in time.

To make all imports lazy at once, use the flag -X lazy_imports=all or the variable PYTHON_LAZY_IMPORTS=all; exceptions are set through sys.set_lazy_imports_filter(). For code that must also work on older versions there is the module variable __lazy_modules__ = ["pandas"]: 3.15 makes the listed imports lazy, while older versions simply perform a regular import. To see where start-up time goes, use python -X importtime -c "import pandas". Programs that are launched often and briefly benefit most; a long-running server gains almost nothing from laziness.

Common mistakes

  • ModuleNotFoundError: No module named 'requests' — the library was installed into a different Python, or the environment is not activated. Check with python -m pip show requests using the same interpreter.
  • Your own file is named json.py, random.py or csv.py — the script's folder comes first in sys.path, and it shadows the standard module. Python 3.13+ suggests "consider renaming" in the error. Rename the file and delete __pycache__.
  • Circular import: a.py imports b.py, which imports a.py. Move the shared code into a third module or import inside a function.
  • pip run inside the Python interpreter (>>> pip install) — you get a SyntaxError. pip commands are executed in the terminal.

Summary

A module is a file, a package is a folder of modules, and a library is a ready-made set of them; import looks for a module in the cache and along sys.path, and executes it once. Install third-party libraries with pip or uv only into a venv virtual environment — that way projects do not get in each other's way, and the environment is easy to reproduce. In our Python course this topic is practised hands-on: the expense tracker becomes a package with dependencies and starts quickly thanks to lazy imports.

FAQ

Do I need to create a venv for every project?

Yes, it is good practice: each project has its own library versions, and a separate environment prevents conflicts and keeps the system Python clean.

How is pip different from uv?

pip only installs packages, while uv also creates the environment, records dependencies in pyproject.toml and pins exact versions in a lock file; it is also noticeably faster.

How do I see the list of installed Python libraries?

Run python -m pip list in the right environment; pip freeze prints the same list in requirements.txt format.

Where are installed modules stored?

In the site-packages folder of the current environment; the path to a specific module is shown by the __file__ attribute, for example import requests; print(requests.__file__).

Can I move a venv folder to another computer?

No, the environment is tied to the machine's paths and interpreter; you move the list of dependencies and create the environment again.

Learn this topic on video

This topic is covered in depth with practice in the course “Python 3.15: от новичка до профи”.

Go to course