Packages

A package is a directory of modules organized under a common name, letting related code be grouped hierarchically instead of living as one flat pile of files.

A single module works for small programs, but real projects need many related files organized together. A package is Python's answer: a directory that groups modules (and other packages) under one importable name, giving code a hierarchy — mypackage.subpackage.module — instead of a flat namespace.

What Is a Package

A package is a directory containing Python modules, importable as a single unit using dotted names. The directory's name becomes the top-level package name, and each .py file inside it becomes a submodule reachable by dotted path.

This structure creates a package named 'shapes' with two submodules: shapes.circle and shapes.square.

shapes/
  __init__.py
  circle.py
  square.py
import shapes.circle
from shapes import square

print(shapes.circle.area(2))
print(square.area(3))

The Role of __init__.py

Historically, __init__.py (even empty) is what marked a directory as a regular package. Its code runs once, the first time the package or any of its submodules is imported, and its namespace becomes the package's own namespace — a natural place to define what the package exposes at its top level.

This lets callers write from shapes import circle_area directly, instead of reaching through shapes.circle.area — a common pattern for presenting a clean public API from a package.

# shapes/__init__.py
from .circle import area as circle_area
from .square import area as square_area

__all__ = ['circle_area', 'square_area']

Namespace Packages (No __init__.py Required)

Since Python 3.3, a directory without an __init__.py can still act as a package — called a namespace package. Python language behavior allows this: it exists mainly to let a single logical package be split across multiple directories (e.g. by separate distributions), which regular packages can't do. For an ordinary single-directory package, including __init__.py explicitly remains the clearer, more common choice.

Namespace packages have subtler import rules than regular packages (they can't hold package-level code the way __init__.py can). Most everyday projects should use a regular package with __init__.py unless there's a specific reason not to.

Subpackages: Nesting Packages

A package can contain another package (a subpackage), building arbitrarily deep hierarchies. Each level needs its own __init__.py (for regular packages) and is reached with further dots.

graphics/
  __init__.py
  shapes/
    __init__.py
    circle.py
    square.py
  colors/
    __init__.py
    palette.py
from graphics.shapes import circle
from graphics.colors.palette import RED

Absolute Imports Inside a Package

An absolute import spells out the full dotted path from the top-level package (or from a location on sys.path), regardless of which file does the importing. This is the default, most explicit style, and works the same whether the importing code lives inside or outside the package.

# graphics/shapes/square.py
from graphics.colors.palette import RED   # absolute import

def describe():
    return f'a square in {RED}'

Relative Imports Inside a Package

A relative import references a sibling or ancestor module using leading dots instead of the full path: one dot means 'this package', two dots means 'the parent package', and so on. Relative imports only work inside a package (a module run directly as a script cannot use them) and are common for referring to nearby siblings without hardcoding the package's full name.

Relative imports express 'where I am relative to this file'; absolute imports express 'the full name from the top'. Many style guides prefer absolute imports for clarity except within tightly coupled subpackages.

# graphics/shapes/square.py
from . import circle         # sibling module in the same package (shapes)
from ..colors import palette  # module in a sibling package (colors)

Practical Package Layout

A typical small project groups its package under a src/ or project-root directory, with a top-level __init__.py exposing the public API and submodules organized by responsibility (models, utilities, I/O, etc.). Tests and scripts usually live outside the importable package itself.

myapp/
  __init__.py
  core.py
  utils/
    __init__.py
    strings.py
    numbers.py
tests/
  test_core.py

Edge Cases

Running a file directly with python graphics/shapes/square.py breaks any relative imports in it, because the file is then treated as the top-level __main__ script, not part of the graphics.shapes package — Python has no package context to resolve the dots against. Use python -m graphics.shapes.square instead to run it as part of the package.

python graphics/shapes/square.py
# ImportError: attempted relative import with no known parent package

python -m graphics.shapes.square
# works: runs the module inside its package context

Key Takeaways

A package is a directory of modules importable as a dotted hierarchy.

__init__.py marks a regular package and defines what the package exposes at its own level.

Namespace packages (no __init__.py) exist mainly to split one logical package across multiple directories.

Absolute imports spell the full dotted path; relative imports use leading dots and only work inside a package.

Running a package's file directly as a script breaks relative imports; use python -m package.module instead.

python mypackage/submodule.py # file uses relative imports python -m mypackage.submodule # run as part of the package

Running a file directly makes it __main__ with no package context, so any 'from . import sibling' fails; -m runs it inside its package correctly.

__init__.py runs the moment the package (or any submodule) is first imported; expensive work there slows down every import and surprises anyone who just wanted one submodule.

What turns a plain directory into a Python package?

Traditionally, an __init__.py file inside it (a regular package); since Python 3.3, a directory without __init__.py can also work as a namespace package.

When does a package's __init__.py code run?

Once, the first time the package itself or any of its submodules is imported.

What is the difference between an absolute and a relative import inside a package?

An absolute import spells the full dotted path from the top-level package; a relative import uses leading dots to reference siblings or ancestors relative to the current file's location in the package.

Why does python mypackage/module.py break relative imports in that file?

Running a file directly makes it the __main__ script with no package context, so Python cannot resolve the leading-dot relative import against a parent package.

What is a namespace package, and why does it exist?

A package without __init__.py, allowed since Python 3.3, that lets one logical package be split across multiple separate directories or distributions — something a regular package can't do.

What does __all__ typically control inside a package's __init__.py?

The list of names exported when a from package import * is performed, and conventionally the list of names considered the package's public API.

Sketch a package layout named store with two submodules, inventory.py and orders.py, and show the absolute import a test file outside the package would use to reach both.

The store/ directory with __init__.py is a regular package; inventory.py and orders.py are its submodules, reachable from outside via from store import inventory, orders using an absolute import.

What happens when this is run directly with python pkg/a.py, and why?

It raises ImportError: attempted relative import with no known parent package. Running a.py directly makes it __main__ with no package context, so from . import b has no parent package to resolve against. It would work with python -m pkg.a instead.

What is the practical difference in effect between from pkg import module and from pkg.subpkg import module?

The first imports a submodule directly under the pkg package; the second reaches one level deeper, into a subpackage of pkg — the dotted path simply mirrors the directory nesting on disk.

# pkg/a.py
from . import b
def use():
    return b.value

# pkg/b.py
value = 42

# run: python pkg/a.py