Q1Which file must you place in a folder for it to be recognized as a package?
__init__.py and Relative Imports — Bundling Files into a Package
Learn the role of __init__.py for building Python packages and when to reach for absolute vs. relative imports.
The previous article covered single-file modules and how import works. Once an application grows, you'll want to bundle related modules under one folder as a package. This article walks through how to import packages.
A Package Is a Folder Containing __init__.py
A package is a folder that contains a special file called __init__.py. When Python finds it, the whole folder becomes one package and other code can pull it in with import folder_name. __init__.py is the first thing that runs when the package is imported, and it's a common place to declare which functions and classes the package exposes.
- __init__.py — the file that runs first when the package is imported
- calculation.py — a module (add / multiply)
- string_utils.py — a module (format_name, etc.)
my_package/ folder contains __init__.py, so Python treats the folder as a package. The internal modules like calculation.py get exposed externally through __init__.py.__init__.py can be empty. Even an empty file is enough — Python only needs the file's presence to recognize the folder as a package.
Importing Without Going Through __init__.py
Even with an empty __init__.py, you can still import a module directly from the package. The form is from package_name.module_name import function_name — connect the file's location with dots.
# my_package/__init__.py <- empty is fine
# my_package/calculation.py
def add(a, b):
return a + b
# In main.py
from my_package.calculation import add # spell out the file name `calculation`
print(add(1, 2)) # 3
The downside: callers need to know which file holds what. If you reorganize the package later (say, split calculation.py into two), every caller has to change too. The next section's pattern of re-exporting through __init__.py fixes that — it lets callers refer to package-name-direct, short names.
Collecting the Public API in __init__.py
Write from .module_name import function_name inside __init__.py and the function appears as if it lives directly under the package. For example, from .calculation import add, multiply inside __init__.py lets callers write from my_package import add, multiply — short and clean.
from my_package import add without thinking about which file (calculation) contains it.Why Collect the Public API in __init__.py
If callers only ever touch what __init__.py exposes, you can reshuffle internal files later without touching caller code. That's classic encapsulation at the package level — the basic pattern of package design.
from vs. import — Two Ways to Write It
There are two forms of import: from package import name and import package. Both pull in the target, but how you call afterwards is different.
from package import name brings the name itself into scope so you can use it directly. import package only brings the package name into scope; calls have to use the full dotted path.# Form 1: from ... import ...
from my_package.calculation import add
print(add(1, 2)) # add is directly usable
# Form 2: import ...
import my_package.calculation
print(my_package.calculation.add(1, 2)) # call with the full dotted path
# Form 2 + as alias (shortens long names)
import my_package.calculation as calc
print(calc.add(1, 2))
| Form | Name in scope | How to call |
|---|---|---|
| from my_package.calculation import add | add | add(1, 2) |
| import my_package.calculation | my_package | my_package.calculation.add(1, 2) |
| import my_package.calculation as calc | calc | calc.add(1, 2) |
The most common form is from package import name — the function name is short and main-side code reads cleanly. But when two different packages export the same function name and you need both, the import package form keeps the module name visible so it's obvious which one you're calling.
- add — the name itself lands in main's scope
- Call:
add(1, 2)works directly - Short to write, but it's not obvious which package
addcame from
- my_pkg — only the package name lands in main's scope
- Call:
my_pkg.calc.add(1, 2)with the full dotted path - Longer to write, but
my_pkg.calc.addmakes the origin obvious
from package import name brings the name itself into main's scope (= short calls). import package brings only the package name, so calls use the full dotted path (= the origin is explicit).Absolute vs. Relative Imports
What if modules within a package need to refer to each other? Say utility/validator.py wants to import from utility/helper.py — there are two ways to write it: absolute import and relative import. To choose between them, first you need to be clear on what the project root means.
- main.py ← the entry point you run first
- config.py ← app-wide settings
- __init__.py ← runs when
import my_apphappens
- __init__.py
- validator.py
- helper.py
main.py (the entry point) sits. The my_app in the absolute import from my_app.utility import validator refers to the same-named folder directly under that root.The project root is the folder that holds the entry point you run with python main.py. When you write the absolute import from my_app.utility import validator, Python looks for my_app directly under that root, then drills into utility/validator.py. The dotted path mirrors the folder structure — that's all an absolute import is.
| Type | Form | What it means |
|---|---|---|
| Absolute import | from my_app.utility import helper | Full path from the project root |
| Relative import | from . import helper | Relative to the file you're in |
| Relative import (parent) | from .. import config | Targets a file one folder up |
. in from . import means "the same folder I'm in".The usual split: the entry point (main.py) uses absolute imports to reach into packages, and modules inside a package use relative imports to refer to each other. With relative imports, renaming a package folder later doesn't force changes to the internal code.
# my_app/utility/validator.py
# Relative import for helper.py in the same package
from .helper import log_message
def validate_user(user):
if user.name and user.email:
log_message("OK")
return True
log_message("problem detected")
return False
# my_app/utility/helper.py
def log_message(message):
print("[LOG]", message)
# To reach config.py in another package,
# go up one with ..:
# from ..config import get_config
- config.py ← target of
from ..config import ...
- validator.py ← writes
from .helper import log_message - helper.py ← provides
log_message(...)
from .helper import log_message in validator.py uses . — "this folder (= utility/)". config.py is one level up in my_app/, so from validator's view you'd reach it as ..config (.. = one level up).Entry Points Can't Use Relative Imports
A file you run directly with python main.py can't use relative imports — you'll hit ImportError. Relative imports only work when the file is loaded as a member of some package. From the entry point, always use absolute imports (e.g., from my_app.utility import validate_user).
A Real-Project-Style Layout
With these rules, you can put together folder structures that match real apps. Here's a layout split into three packages — settings, database, and utility:
- main.py — entry point (the file you run with python)
- config.py — app-wide settings
- __init__.py — public API of the my_app package
- __init__.py
- connection.py / models.py
- __init__.py
- validator.py / helper.py
main.py pulls in the whole my_app/ package; modules inside use relative imports to reach each other. Each subfolder has its own __init__.py collecting that subpackage's public API.From main.py you call out with absolute imports like from my_app.utility import validate_user. Inside utility/, validator.py reaches helper.py with from .helper import log_message — a relative import. That's the canonical division of labor.
From here on it's advanced — feel free to circle back if you get stuck
The next exercise is the capstone — building two packages in parallel. You'll have the smoothest time if the __init__.py re-export pattern feels natural and the absolute / relative import distinction is solid for you. If you get stuck, jump back to the previous validator.py exercise or the "A Real-Project-Style Layout" diagram above to reset before tackling this.
A Multi-Folder Challenge
Time to try building two packages in parallel with everything you've learned. You'll manage a product catalog/ package and a billing/ package in separate folders, then orchestrate them from main.py to complete one order. Splitting responsibilities across folders means changes to product data only touch catalog/, and changes to invoice formatting only touch billing/ — the blast radius stays inside one folder.
- main.py — entry point (uses both packages to handle an order)
- __init__.py — re-exports
from .products import get_price - products.py — implements
get_price(name)
- __init__.py — re-exports
from .invoice import format_invoice - invoice.py — implements
format_invoice(name, qty, unit_price)
catalog/ holds product data (products.py); billing/ holds invoice formatting (invoice.py). Each __init__.py re-exports the public API, so main.py can call out with two absolute imports: from catalog import get_price and from billing import format_invoice.__init__.py to re-export .products / .invoice.From here on is bonus material — rare in real code
The section below covers __all__ and is informational only. Real-world code almost never uses from package import *, so this part doesn't affect the main thread. As long as you've got the explicit-name import form down, feel free to skim through this section.
Controlling from package import * with __all__
When someone writes from my_package import * to pull everything in with a star, you can control what's exposed via __all__ in __init__.py. With __all__ = ["add"], the * import only picks up add — nothing else.
__all__ = ["add"] in __init__.py, only add comes through from package import *. multiply is left out (you can still grab it explicitly).# my_package/__init__.py
from .calculation import add, multiply
__all__ = ["add"] # only add is exposed via *
# Caller side
# from my_package import *
# add(1, 2) # OK
# multiply(1, 2) # NameError (not pulled in by *)
In Practice, Avoid * Imports
from my_package import * is hard to read — you can't tell at a glance what came in. Real-world code almost always uses explicit names like from my_package import add, multiply. Treat __all__ as a *safety net for the rare case someone does use ``**.
This article covered using __init__.py to bundle several modules into one package, the difference between from package import name and import package, picking between absolute and relative imports, and a folder layout close to a real project.
Knowledge Check
Answer each question one by one.
Q2From my_app/utility/validator.py, which is the correct relative import to load helper.py in the same utility folder?
Q3What happens if you write a relative import (from . import xxx) inside an entry point (a file you run directly with python main.py)?