順番に読み進めながら学べます

__init__.py と相対インポート — パッケージで複数ファイルを束ねる

__init__.pyでパッケージを束ね公開APIを絞る書き方、from ... importimportの違い、絶対インポートと相対インポートの棲み分けを実例で学べます。

アプリケーションが大きくなると、関連するモジュールを 1 つのフォルダにまとめてパッケージとして扱いたくなります。本記事では、パッケージの作り方と__init__.pyの役割、from .module import ...のような相対インポートを整理します。

パッケージとは __init__.py 入りのフォルダ

パッケージとは、__init__.pyという特別なファイルを含んだフォルダのことです。Python はこのファイルを見つけると、そのフォルダ全体を 1 つのパッケージとして扱い、外部からimport フォルダ名で読み込めるようにします。__init__.pyパッケージが import されたときに最初に実行される場所で、ここで公開する関数やクラスを定義することが多いです。

パッケージの基本構造
my_package/ (パッケージ)
  • __init__.py — パッケージが import されたときに最初に実行されるファイル
  • calculation.py — モジュール(add / multiply)
  • string_utils.py — モジュール(format_name など)
my_package/フォルダに__init__.pyが入っているのでパッケージとして認識される。中のcalculation.pyなどのモジュールは__init__.pyが橋渡しして外に公開する。

__init__.py空ファイルでも構いません。空でも、フォルダにそのファイルがあるだけで Python はパッケージとして認識します。

__init__.py を介さず直接 import する

__init__.py空ファイルでも、パッケージ内のモジュールは直接 import すれば呼び出せます。from パッケージ名.モジュール名 import 関数名のように、ファイル位置をドットでつなぐ書き方です。

# my_package/__init__.py  ← 中身は空でも OK

# my_package/calculation.py
def add(a, b):
    return a + b


# main.py から呼ぶ
from my_package.calculation import add   # ← ファイル名 calculation まで明示する
print(add(1, 2))   # 3

このパターンでは、利用者がどのファイルに何があるかを知っている必要があります。後でファイル分割を変えると(例: calculation.py を 2 つに分けるなど)、利用側のコードも書き直さないといけません。次のセクションで紹介する__init__.pyでの再エクスポートは、これを「パッケージ名直下から短い名前で呼べる」形に整える仕組みです。

__init__.py で公開する関数を集める

__init__.pyの中でfrom .モジュール名 import 関数名と書いておくと、外部からはパッケージ名直下にその関数があるかのように見えるようになります。たとえば__init__.pyfrom .calculation import add, multiplyと書けば、外側のファイルからはfrom my_package import add, multiplyと短く書けます。

__init__.py で再エクスポートして import を短くする
main.py__init__.pyfrom .calculationimport add, multiplycalculation.pydef add()def multiply()from my_packageimport addadd(1, 2)が呼べる経由引き上げmy_package/
calculation.py の add を__init__.pyで引き上げて公開すると、main.py からはファイル名 calculation を意識せずにfrom my_package import addと短く書ける。

左に my_package/ フォルダが添付されています(📂 ファイルから calculation.py と __init__.py の中身を確認できます)。

① from my_package import add, multiply で 2 つの関数を読み込んでください。

② add(10, 20) と multiply(3, 4) の結果を print() で表示してください。

Python エディタ

コードを実行してください

公開 API を __init__.py に集める意義

利用者が触るのは __init__.py が公開した関数・クラスだけ、と決めておくと、内部のファイル分割を後から自由に変えても利用側のコードを書き換えずに済みます。これは外側に対してカプセル化を効かせる、パッケージ設計の基本パターンです。

from と import — 2 つの書き方

import文には2 つの書き方があります — from パッケージ import 名前import パッケージです。どちらも対象を読み込む点は同じですが、呼び出すときの書き方が変わります。

from と import — スコープに入る名前が変わる
from my_pkg.calcimport addスコープに入る:add呼び方:add(1, 2)importmy_pkg.calcスコープに入る:my_pkg呼び方:my_pkg.calc.add(1, 2)
from パッケージ import 名前名前そのものをスコープに取り込むのでそのまま呼べる。import パッケージパッケージ名のみが入り、関数呼び出しは完全パスで書く必要がある。
# パターン 1: from ... import ...
from my_package.calculation import add
print(add(1, 2))   # add がそのまま使える


# パターン 2: import ...
import my_package.calculation
print(my_package.calculation.add(1, 2))   # 完全パスで呼ぶ必要がある


# パターン 2 + as エイリアス(長い名前を短くしたい)
import my_package.calculation as calc
print(calc.add(1, 2))
書き方スコープに入る名前呼び出し方
from my_package.calculation import addaddadd(1, 2)
import my_package.calculationmy_packagemy_package.calculation.add(1, 2)
import my_package.calculation as calccalccalc.add(1, 2)

よく使うのはfrom パッケージ import 名前の形で、関数名を短く呼べるため main 側のコードが読みやすくなります。ただし、異なるパッケージが同じ名前の関数を持っているときはimport パッケージの形でモジュール名を残しておくと、どちらの関数を呼んでいるかが一目で分かります。

from と import — main 側のスコープに何が入るか
from my_pkg.calc import add
  • add — 名前そのものが main 側に入る
  • 呼び方: add(1, 2)でそのまま使える
  • 短く書ける反面、addがどのパッケージ由来か一目で分からない
import my_pkg.calc
  • my_pkg — パッケージ名だけが main 側に入る
  • 呼び方: my_pkg.calc.add(1, 2)と完全パスで書く
  • 長くなる代わりに、my_pkg.calc.addで由来が明示される
from パッケージ import 名前名前そのものを main 側のスコープに入れる (= 短く呼べる)。import パッケージパッケージ名のみが入り、関数を呼ぶときは完全パスでドットをつなぐ (= どこから来たか追いやすい)。

添付の mathlib/ パッケージで 2 通りの書き方を試します。calculator.py には triple(n) が定義されています(📂 で確認)。

from ... import ... のパターンで triple を取り込み、triple(7) の結果を print() してください。

② 続いて、import ... as ... のパターンで mathlib.calculator を calc という別名でも取り込み、calc.triple(7) も print() してください。

Python エディタ

コードを実行してください

絶対インポートと相対インポート

パッケージ内のモジュール同士でお互いを参照したい、というケースを考えます。たとえばutility/validator.pyからutility/helper.pyを読み込みたいときの書き方は2 通りあります — 絶対インポート相対インポートです。違いを理解するには、まずプロジェクトのルートが何を指すかを押さえる必要があります。

プロジェクトのルートとは
project/ ← ここがルート (main.py が置いてあるフォルダ)
  • main.py ← 最初に実行されるファイル
  • config.py ← アプリ全体の設定値
my_app/
  • __init__.pyimport my_appで最初に実行されるファイル
utility/
  • __init__.py
  • validator.py
  • helper.py
プロジェクトのルート = 最初に実行するmain.pyが置いてあるフォルダ。絶対インポートfrom my_app.utility import validatormy_appは、このルート直下にある同名フォルダを指す。

プロジェクトのルートとは、python main.pyのように最初に実行する main.py が置いてあるフォルダのことです。絶対インポートfrom my_app.utility import validatorを書くと、Python はこのルート直下からmy_appフォルダを探し、その中のutility/validator.pyをたどります。フォルダ構成をドットでつないだものが、絶対インポートのパスになります。

種類書き方意味
絶対インポートfrom my_app.utility import helperプロジェクトのルートからの完全パスで指定
相対インポートfrom . import helper今いるファイルからの相対位置で指定
相対インポート(親)from .. import config1 階層上のフォルダにあるファイルを指定
validator.py から helper.py を読む書き方
絶対インポートfrom my_app.utilityimport helperルートからの完全パス相対インポートfrom . importhelper今のフォルダを基準
同じパッケージ内の隣のモジュールを読むときは、絶対パスでも相対パスでも書ける。from . import.は「自分と同じフォルダ」を表す。

実行ファイル(main.py)から外部のパッケージを読み込むときは絶対インポートを使い、パッケージ内部のモジュール同士の参照には相対インポートを使う、という棲み分けが一般的です。相対インポートだと、後でパッケージ名(フォルダ名)を変えたときにも内部のコードを書き換えずに済みます。

# my_app/utility/validator.py
# 同じパッケージ内の helper.py を相対インポート
from .helper import log_message


def validate_user(user):
    if user.name and user.email:
        log_message("検証 OK")
        return True
    log_message("問題が発生しました")
    return False


# my_app/utility/helper.py
def log_message(message):
    print("[LOG]", message)


# 別パッケージにある config.py を読みたいときは、
# 1 つ上に上がる .. を使う:
# from ..config import get_config
上のコードのフォルダ構成
my_app/
  • config.pyfrom ..config import ...の対象
utility/
  • validator.pyfrom .helper import log_messageを書く側
  • helper.pylog_message(...)を提供する側
validator.py と helper.py は同じ utility/ フォルダの中に並んでいる。だから validator.py のfrom .helper import log_message.は「このフォルダ (= utility/)」を指す。config.py1 階層上の my_app/にあるので、validator から見ると..config (.. = 1 階層上) で参照できる。

実行ファイルから相対インポートはできない

python main.pyのように直接実行されるファイルからは相対インポートを書けません(ImportError になります)。相対インポートは「自分がどこかのパッケージの一員として読み込まれている」前提で動く仕組みのためです。実行ファイル側からは、必ず絶対インポート(from my_app.utility import validate_user)で書きましょう。

添付のshop/パッケージで絶対インポートと相対インポートの組み合わせを試します。📂 を開くとshop/__init__.py は空で、Cartto_yenもパッケージ直下から見えていません。

① まず main.py をそのまま実行してください — ImportErrorになります(Cartto_yenがどこにも公開されていないため)。

② 次に 📂 から shop/__init__.py を開いて、from .cart import Cartfrom .formatter import to_yen相対インポート (.は今のパッケージ = shop) で書いて保存してください。

③ main.py に Cart の利用コード — Cart() を作って apple 100円・orange 200円 を追加 → 合計を to_yen() で整形して print() — を書いて再実行してください。main 側は絶対インポート、shop 内部は相対インポートという棲み分けです。

Python エディタ

コードを実行してください

実プロジェクト風の構成

ここまでのルールで、実際のアプリでよく見るフォルダ構成が組み立てられます。たとえば、設定・データベース・ユーティリティの 3 つのパッケージに分けた構成は次のようになります。

実プロジェクトに近いフォルダ構成
project/ (プロジェクトルート)
  • main.py — 実行ファイル(直接 python で起動する側)
  • config.py — アプリ全体の設定値
my_app/
  • __init__.py — my_app パッケージの公開 API
database/
  • __init__.py
  • connection.py / models.py
utility/
  • __init__.py
  • validator.py / helper.py
実行ファイルmain.pyからmy_app/パッケージ全体を読み込み、内部のモジュール同士は相対インポートで結ぶ。各サブフォルダにも__init__.pyを置いて、それぞれの公開 API をまとめる。

main.py からはfrom my_app.utility import validate_userのように絶対インポートで公開関数を呼び、utility/の内側にある validator.py から helper.py を呼ぶときはfrom .helper import log_message相対インポートで書く、という役割分担になります。

絶対と相対の境界
main.pyvalidator.pyhelper.py絶対相対my_app/utility/
main.py から utility パッケージへは絶対(ルートからのパス)、utility 内部の validator → helper は相対(自分基準のパス)で結ぶ。境界を意識すると後でフォルダ名を変えても影響範囲が狭い。

my_app/utility/ パッケージの validator.py を自分で実装して、main.py から呼び出します(📂 ファイルから validator.py を開いて編集 → Cmd+S または保存)。helper.py は完成済みです。

① validator.py のvalidate_user(email)を実装してください:

- __init__.pyでfrom .helper import log_message相対インポートで同じフォルダの helper.py を読み込む

- email に@.が両方含まれていればlog_message("検証 OK: " + email)を呼び True を返す

- そうでなければlog_message("問題が発生しました: " + email)を呼び False を返す

② main.py でfrom my_app.utility import validate_user絶対インポートで読み込み、validate_user("taro@example.com")の結果を print() してください。

Python エディタ

コードを実行してください

ここからは応用 — つまずいたら戻ってきて OK

次の演習は2 つのパッケージを並行で組み立てる集大成課題です。__init__.py の再エクスポートが手に馴染んでいて、相対インポート / 絶対インポートの違いがあやしくない状態で進むのが理想です。詰まったら直前のvalidator.py演習や、上の「実プロジェクト風の構成」の図に戻って整理してから挑戦してください。

複数フォルダ構成にチャレンジ

ここまでのルールを使って、2 つのパッケージを並行で組み立てる演習に挑戦してみましょう。商品カタログを扱うcatalog/パッケージと、請求書を整形するbilling/パッケージを別々のフォルダで管理し、main.py から両方を呼び出して 1 つの注文処理を完成させます。役割をフォルダごとに分けると、後で「商品データだけ差し替えたい」「請求書の書式だけ変えたい」となっても影響範囲をそのフォルダ内に閉じ込められるのが利点です。

プロジェクトのフォルダ構成 (catalog と billing の 2 パッケージ)
project/ ← プロジェクトのルート
  • main.py — 実行ファイル (両パッケージを使ってオーダー処理)
catalog/
  • __init__.pyfrom .products import get_priceで再エクスポート
  • products.pyget_price(name)を実装する
billing/
  • __init__.pyfrom .invoice import format_invoiceで再エクスポート
  • invoice.pyformat_invoice(name, qty, unit_price)を実装する
catalog/には商品データ (products.py)、billing/には請求書整形 (invoice.py) を置く。各__init__.pyが公開 API を再エクスポートし、main.py からはfrom catalog import get_pricefrom billing import format_invoiceの 2 つの絶対インポートで両方を呼び出せる。
main.py が 2 つのパッケージを束ねる流れ
catalogget_price()main.py両方を組み合わせるbillingformat_invoice()from catalog import get_pricefrom billing import ...
main.py から catalog を絶対インポートで読み込んで単価を引き、billing を絶対インポートで読み込んで請求書を整形。各パッケージ内部では__init__.py.products / .invoice相対インポートで再エクスポート。

添付のcatalog/billing/の 2 つのパッケージを__init__.py も含めて自分で完成させ、main.py から両方を絶対インポートして注文処理を組み立てます (📂 から各ファイルを開いて編集 → Cmd+S または保存)。

① catalog/products.py にget_price(name)を実装 — "apple" → 100、"orange" → 150、それ以外 → 0 を返す (dict + dict.get(name, 0)が便利)。

② catalog/__init__.py にfrom .products import get_priceを書いて、外側からfrom catalog import get_priceで呼べるようにする (相対インポート)。

③ billing/invoice.py にformat_invoice(name, qty, unit_price)を実装 — qty * unit_priceで合計を計算し、"apple x 3 = ¥300"のような文字列を返す。

④ billing/__init__.py にfrom .invoice import format_invoiceを書く。

⑤ main.py でfrom catalog import get_pricefrom billing import format_invoiceを絶対インポートし、商品"apple"を 3 個注文するとして catalog から単価を引いて → billing で整形 → print() してください。

Python エディタ

コードを実行してください

ここから先は補足 — 実務では稀

__all__ を扱う以下のセクションは情報目的の補足です。from package import *を実際に使う機会は実務でほとんどなく、本筋の理解には影響しません。「明示的に名前を書く」 import の使い方さえ押さえていれば、ここはサッと読んで進めても OK です。

__all__ で from パッケージ import * を制御する

from my_package import *のようにアスタリスクで全部読み込む書き方をされたとき、何を公開するかは__init__.py内の__all__で制御できます。__all__ = ["add"]と書いておくと、*インポートではaddだけが取り込まれ、他の関数は取り込まれません。

__all__ で公開する名前を絞る
__init__.pyfrom .calc importadd, multiply__all__ = ["add"]from pkgimport *add→ 取り込まれるmultiply→ 取り込まれない(NameError)
__all__ = ["add"]を __init__.py に書くと、from パッケージ import *で取り込まれるのは add のみ。multiply は除外される(明示的に名前を指定すれば取り込める)。
# my_package/__init__.py
from .calculation import add, multiply

__all__ = ["add"]   # * では add しか公開しない

# 利用側
# from my_package import *
# add(1, 2)        # OK
# multiply(1, 2)   # NameError(* では取り込まれていない)

実務では * インポートは避ける

from my_package import *何が読み込まれたか一目で分からないため、実務ではほとんど使いません。from my_package import add, multiplyのように明示的に名前を書くのが基本です。__all__は「誰かが*を使った場合の保険」と捉えておけば十分です。

添付のbundle/パッケージで __all__ の挙動を確認します。📂 から bundle/__init__.py を見ると、add / multiplyの両方を取り込みつつ__all__ = ["add"]で公開を絞っています。

① main.py でfrom bundle import *を実行し、add(2, 3)を print() してください。

② 続けてmultiply(2, 3)を呼び出すコードを try/except で囲み、NameError になることを確認してください (この演習は CPython 互換の Pyodide ランタイムを使用します — 初回のみロードに 5〜15 秒かかります)。

Python エディタ

コードを実行してください

この記事では、__init__.pyで複数モジュールを 1 つのパッケージとして束ねる書き方、from パッケージ import 名前import パッケージの 2 通りの書き方の違い、絶対インポートと相対インポートの使い分け、そして実プロジェクトに近いフォルダ構成を確認しました。

QUIZ

理解度チェック

まずは1問ずつ答えてみましょう。

Q1パッケージとして認識されるために、フォルダの中に必ず置くべきファイルはどれですか?

Q2my_app/utility/validator.pyから、同じ utility フォルダ内のhelper.pyを読み込みたいとき、相対インポートとして正しい書き方はどれですか?

Q3実行ファイルpython main.pyで直接起動するファイル)の中で相対インポート(from . import xxx)を書くと、何が起きますか?