Python
Programming
Module vs Package
Coding
Software Development

What's the difference between a module and package in Python?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Python, both modules and packages are things you can import, which is why they are easy to confuse. The practical difference is that a module is usually one importable file, while a package is a directory-based namespace that groups related modules and sometimes subpackages.

A Module Is the Basic Import Unit

The simplest reusable Python code lives in a module. Most of the time, that means one .py file.

Example file named math_tools.py:

python
1PI = 3.14159
2
3
4def area_of_circle(radius: float) -> float:
5    return PI * radius * radius

You import it by the file name without the .py suffix:

python
import math_tools

print(math_tools.area_of_circle(2.5))

That file is a module because Python can load it as a named unit. A module can contain:

  • functions
  • classes
  • constants
  • top-level executable code

Python's standard library is full of modules such as math, json, and pathlib.

A Package Organizes Multiple Modules

Once a project grows beyond one file, a package becomes useful. A package is a directory that Python treats as an import namespace.

Example layout:

text
1shop/
2    __init__.py
3    pricing.py
4    inventory.py
5    reports/
6        __init__.py
7        daily.py

Here:

  • 'pricing.py is a module'
  • 'inventory.py is a module'
  • 'reports is a subpackage'
  • 'shop is a package'

That structure enables dotted imports:

python
from shop import pricing
from shop.inventory import get_item_count
from shop.reports.daily import build_report

The dotted path is the easiest visual signal that you are working with package structure rather than one standalone module.

What __init__.py Does

Traditionally, Python recognized a package by the presence of __init__.py. That file is still useful because it can define package-level behavior or re-export selected names.

Example shop/__init__.py:

python
from .pricing import area_discount
from .inventory import get_item_count

Now callers can write:

python
from shop import area_discount, get_item_count

That can make public package APIs cleaner, though it should be used deliberately. A package should not hide its structure so aggressively that the import surface becomes confusing.

Namespace Packages Add a Modern Twist

There is one complication: modern Python also supports namespace packages, which can exist without __init__.py in some setups. So the beginner rule:

"a package is a directory containing __init__.py"

is helpful, but not complete.

In everyday project code, regular packages with __init__.py are still the clearest model to teach and maintain. Namespace packages become more relevant when large systems split one logical namespace across multiple distributions.

A Package Is Also a Kind of Module

This is the subtle part. In Python's import system, packages are importable objects too. So the distinction is not:

  • module means importable
  • package means not importable

Instead, the distinction is:

  • a module is an importable code unit
  • a package is a module-like namespace that can contain other modules

That is why Python documentation sometimes speaks broadly about "modules" even when packages are involved. From the import system's perspective, packages participate in the module system.

Real-World Project Structure

Suppose a small project starts here:

text
reporting.py

At first, that is fine. Later, the code grows and becomes awkward to manage in one file. Refactoring into a package makes the structure clearer:

text
1reporting/
2    __init__.py
3    csv_export.py
4    pdf_export.py
5    validators.py

Now imports communicate organization:

python
from reporting.csv_export import write_csv
from reporting.validators import validate_row

Packages help when you need:

  • cleaner separation of responsibilities
  • fewer name collisions
  • a stable namespace for related code

They are an organizational tool, not just an import trick.

Do Not Confuse Import Packages with Installed Distributions

Python packaging terminology creates one more source of confusion. People often say "package" to mean something installable with pip. That is related, but not identical.

An installed distribution may provide:

  • one package
  • several packages
  • some top-level modules

So when discussing source code layout, it is safer to ask:

  • do you mean an importable package directory
  • or a distribution artifact installed by a packaging tool

Those are connected ideas, but they are not the same thing.

Common Pitfalls

One common mistake is calling every Python file a package. A file such as helpers.py is a module, not a package.

Another mistake is assuming packages always require __init__.py. That is usually true in ordinary projects, but namespace packages are a modern exception.

Developers also run into import-shadowing problems. A local module named json.py or random.py can accidentally hide the standard-library module of the same name.

Finally, some projects create deep package hierarchies too early. If a single file is enough, a simple module is often the better design.

Summary

  • A module is usually one importable Python file.
  • A package is an importable namespace that groups related modules and subpackages.
  • '__init__.py is still important for ordinary package structure and public package APIs.'
  • Namespace packages exist, but they are not the best first mental model for beginners.
  • Use modules for small units of code and packages when code needs structure under a shared namespace.

Course illustration
Course illustration

All Rights Reserved.