Packages: folders of modules
A folder with an __init__.py is a package. Dotted imports reach the modules inside it, modules in the package import each other with relative imports, and __init__.py decides what the package itself offers.
- Import from a package with
import shop.taxandfrom shop.pricing import price_with_tax - Use relative imports (
from .tax import rate_for) between modules of the same package - Re-export names in
__init__.py, and recognise the error from a relative import outside a package
Ten modules in one folder get hard to find. A package groups related modules in a folder: shop/pricing.py, shop/tax.py, plus shop/__init__.py, which marks the folder as a package and runs when the package is first imported.
From outside, dotted names reach inside: import shop.tax (then shop.tax.rate_for(...)) or from shop.pricing import price_with_tax. Inside the package, modules import their neighbours with relative imports: from .tax import rate_for means "the tax module in my own package", so the package keeps working if the folder is renamed or moved.
Run each example to see what it prints. Running examples doesn't count toward progress.
from pathlib import Path import importlib Path("shop").mkdir(exist_ok=True) Path("shop/__init__.py").write_text('"""Runtime Market shop helpers."""\n', encoding="utf-8") Path("shop/tax.py").write_text('''from decimal import Decimal RATES = {"NY": Decimal("0.08875"), "FL": Decimal("0.07"), "OR": Decimal("0")} def rate_for(state): return RATES.get(state, Decimal("0.05")) ''', encoding="utf-8") Path("shop/pricing.py").write_text('''from decimal import Decimal, ROUND_HALF_UP from .tax import rate_for def price_with_tax(price_text, state): total = Decimal(price_text) * (1 + rate_for(state)) return total.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) ''', encoding="utf-8") importlib.invalidate_caches() from shop.pricing import price_with_tax import shop.tax print(price_with_tax("47.99", "NY"), price_with_tax("47.99", "OR")) print(shop.tax.rate_for("FL"))
from .tax import rate_for
__init__.py can also choose what the package offers at the top level. With from .pricing import price_with_tax inside it, users can write from shop import price_with_tax without knowing which module the function lives in, and you can reorganise the modules later without breaking them.
from pathlib import Path import importlib Path("catalog").mkdir(exist_ok=True) Path("catalog/__init__.py").write_text('from .lookup import find_product\n', encoding="utf-8") Path("catalog/lookup.py").write_text('''PRODUCTS = {"HOM-52782": "Deluxe Pillow", "GRO-63674": "Pro Honey"} def find_product(sku): return PRODUCTS.get(sku, "unknown product") ''', encoding="utf-8") importlib.invalidate_caches() from catalog import find_product import catalog print(find_product("HOM-52782"), "|", catalog.find_product("XXX-00000")) print(catalog.lookup.find_product("GRO-63674"))
When to use it: once a project has more than a handful of modules, group them by responsibility (shop.pricing, shop.tax, shop.export). Mistakes to avoid: relative imports in the script you run, forgetting __init__.py (it isn't strictly required in modern Python, but without it the folder is a looser "namespace package" and many tools expect it), and circular imports, where two modules import each other at the top.
- A folder with
__init__.pyis a package;__init__.pyruns on the package's first import. - From outside:
import shop.taxorfrom shop.pricing import name. Inside:from .tax import name. - Relative imports only work inside a package, never in the program you run.
- Next: one file that is both a module and a program.