Q1パッケージ として認識されるために、フォルダの中に必ず置くべきファイルはどれですか?
__init__.py と相対インポート — パッケージで複数ファイルを束ねる
Python のパッケージを作る __init__.py の役割と、絶対インポート / 相対インポートの使い分けを基礎から学びます。
アプリケーションが大きくなると、関連するモジュールを 1 つのフォルダにまとめて パッケージ として扱いたくなります。本記事では、パッケージの作り方と __init__.py の役割、from .module import ... のような 相対インポート を整理します。
パッケージとは __init__.py 入りのフォルダ
パッケージ とは、__init__.py という特別なファイルを含んだ フォルダ のことです。Python はこのファイルを見つけると、そのフォルダ全体を 1 つのパッケージとして扱い、外部から import フォルダ名 で読み込めるようにします。__init__.py は パッケージが import されたときに最初に実行される 場所で、ここで公開する関数やクラスを定義することが多いです。
- __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__.py で from .calculation import add, multiply と書けば、外側のファイルからは from my_package import add, multiply と短く書けます。
__init__.py で引き上げて公開すると、main.py からはファイル名 calculation を意識せずに from my_package import add と短く書ける。公開 API を __init__.py に集める意義
利用者が触るのは __init__.py が公開した関数・クラスだけ、と決めておくと、内部のファイル分割を後から自由に変えても 利用側のコードを書き換えずに済みます。これは外側に対して カプセル化 を効かせる、パッケージ設計の基本パターンです。
from と import — 2 つの書き方
import 文には 2 つの書き方 があります — from パッケージ import 名前 と import パッケージ です。どちらも対象を読み込む点は同じですが、呼び出すときの書き方 が変わります。
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 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) |
よく使うのは from パッケージ import 名前 の形で、関数名を短く呼べるため main 側のコードが読みやすくなります。ただし、異なるパッケージが同じ名前の関数を持っている ときは import パッケージ の形でモジュール名を残しておくと、どちらの関数を呼んでいるかが一目で分かります。
- add — 名前そのものが main 側に入る
- 呼び方:
add(1, 2)でそのまま使える - 短く書ける反面、
addがどのパッケージ由来か一目で分からない
- my_pkg — パッケージ名だけが main 側に入る
- 呼び方:
my_pkg.calc.add(1, 2)と完全パスで書く - 長くなる代わりに、
my_pkg.calc.addで由来が明示される
from パッケージ import 名前 は 名前そのもの を main 側のスコープに入れる (= 短く呼べる)。import パッケージ は パッケージ名のみ が入り、関数を呼ぶときは完全パスでドットをつなぐ (= どこから来たか追いやすい)。絶対インポートと相対インポート
パッケージ内のモジュール同士でお互いを参照したい、というケースを考えます。たとえば utility/validator.py から utility/helper.py を読み込みたいときの書き方は 2 通り あります — 絶対インポート と 相対インポート です。違いを理解するには、まず プロジェクトのルート が何を指すかを押さえる必要があります。
- main.py ← 最初に実行されるファイル
- config.py ← アプリ全体の設定値
- __init__.py ←
import my_appで最初に実行されるファイル
- __init__.py
- validator.py
- helper.py
main.py が置いてあるフォルダ。絶対インポート from my_app.utility import validator の my_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 config | 1 階層上のフォルダにあるファイルを指定 |
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
- config.py ←
from ..config import ...の対象
- validator.py ←
from .helper import log_messageを書く側 - helper.py ←
log_message(...)を提供する側
from .helper import log_message の . は「このフォルダ (= utility/)」を指す。config.py は 1 階層上の my_app/ にあるので、validator から見ると ..config (.. = 1 階層上) で参照できる。実行ファイルから相対インポートはできない
python main.py のように 直接実行 されるファイルからは相対インポートを書けません(ImportError になります)。相対インポートは「自分が どこかのパッケージの一員 として読み込まれている」前提で動く仕組みのためです。実行ファイル側からは、必ず絶対インポート(from my_app.utility import validate_user)で書きましょう。
実プロジェクト風の構成
ここまでのルールで、実際のアプリでよく見るフォルダ構成が組み立てられます。たとえば、設定・データベース・ユーティリティの 3 つのパッケージに分けた構成は次のようになります。
- main.py — 実行ファイル(直接 python で起動する側)
- config.py — アプリ全体の設定値
- __init__.py — my_app パッケージの公開 API
- __init__.py
- connection.py / models.py
- __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 と 相対インポート で書く、という役割分担になります。
ここからは応用 — つまずいたら戻ってきて OK
次の演習は 2 つのパッケージを並行で組み立てる集大成課題 です。__init__.py の再エクスポートが手に馴染んでいて、相対インポート / 絶対インポートの違いがあやしくない 状態で進むのが理想です。詰まったら直前の validator.py 演習や、上の 「実プロジェクト風の構成」 の図に戻って整理してから挑戦してください。
複数フォルダ構成にチャレンジ
ここまでのルールを使って、2 つのパッケージを並行で組み立てる 演習に挑戦してみましょう。商品カタログを扱う catalog/ パッケージと、請求書を整形する billing/ パッケージを別々のフォルダで管理し、main.py から両方を呼び出して 1 つの注文処理を完成させます。役割をフォルダごとに分けると、後で「商品データだけ差し替えたい」「請求書の書式だけ変えたい」となっても影響範囲を そのフォルダ内に閉じ込められる のが利点です。
- main.py — 実行ファイル (両パッケージを使ってオーダー処理)
- __init__.py —
from .products import get_priceで再エクスポート - products.py —
get_price(name)を実装する
- __init__.py —
from .invoice import format_invoiceで再エクスポート - invoice.py —
format_invoice(name, qty, unit_price)を実装する
catalog/ には商品データ (products.py)、billing/ には請求書整形 (invoice.py) を置く。各 __init__.py が公開 API を再エクスポートし、main.py からは from catalog import get_price と from billing import format_invoice の 2 つの絶対インポートで両方を呼び出せる。__init__.py が .products / .invoice を 相対インポート で再エクスポート。ここから先は補足 — 実務では稀
__all__ を扱う以下のセクションは情報目的の補足です。from package import * を実際に使う機会は実務でほとんどなく、本筋の理解には影響しません。「明示的に名前を書く」 import の使い方さえ押さえていれば、ここはサッと読んで進めても OK です。
__all__ で from パッケージ import * を制御する
from my_package import * のように アスタリスクで全部読み込む 書き方をされたとき、何を公開するかは __init__.py 内の __all__ で制御できます。__all__ = ["add"] と書いておくと、* インポートでは add だけが取り込まれ、他の関数は取り込まれません。
__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__ は「誰かが * を使った場合の保険」と捉えておけば十分です。
この記事では、__init__.py で複数モジュールを 1 つの パッケージ として束ねる書き方、from パッケージ import 名前 と import パッケージ の 2 通りの書き方の違い、絶対インポートと相対インポート の使い分け、そして実プロジェクトに近いフォルダ構成を確認しました。
理解度チェック
まずは1問ずつ答えてみましょう。
Q2my_app/utility/validator.py から、同じ utility フォルダ内の helper.py を読み込みたいとき、相対インポートとして正しい書き方はどれですか?
Q3実行ファイル(python main.py で直接起動するファイル)の中で相対インポート(from . import xxx)を書くと、何が起きますか?