Pythonのimport完全ガイド!パッケージとモジュールの階層構造をスッキリ理解しよう

こんにちは。ゆうせいです。

Pythonを学び始めると、必ず出会うのが import という記述ですね。

便利なライブラリを読み込むときにも使いますし、自分で作った別のファイル(モジュール)を読み込むときにも使います。

でも、ファイルが一つや二つのうちは上手くいっていたのに、プロジェクトが大きくなってフォルダを分け始めた途端、「import が見つかりません(ModuleNotFoundError)」というエラーに出会って混乱した経験はありませんか?

「さっきまで動いていたのに、なぜ!?」

その悩み、とてもよく分かります。

その原因は、Pythonがファイルを探す「順序」と「ルール」、つまり「インポート階層」をまだ掴めていないからかもしれません。

今日は、Pythonがどのようにして import するファイルを見つけているのか、その「住所」の仕組みを、例え話を交えながら解き明かしていきましょう。


すべての基本、「モジュール」と「パッケージ」

まず、二つの大切な専門用語を押さえてしまいましょう。

専門用語解説:「モジュール(Module)」

モジュールとは、簡単に言えば「Pythonのファイル(.pyファイル)」そのもののことです。

機能ごとに処理をまとめた「道具箱」のようなイメージですね。

私たちは import 〇〇 と書くことで、その道具箱を使えるようにするわけです。

専門用語解説:「パッケージ(Package)」

パッケージとは、「モジュール(道具箱)を集めて入れたフォルダ」のことです。

ただし、ただのフォルダではありません。

Pythonに「やあ、このフォルダはただの入れ物じゃなくて、道具箱(モジュール)がたくさん入った『倉庫』だからね!」と教えてあげるための「目印」が必要になります。

その目印こそが、__init__.py(アンダースコア二つ、init、アンダースコア二つ、ドット、py)という名前の、空っぽのファイルです。

この __init__.py ファイルが置かれているフォルダだけが、Pythonの世界では「パッケージ(倉庫)」として認識されます。


Pythonはどうやってファイルを探す?「sys.path」の秘密

では、私たちが import my_tools と書いたとき、Pythonはどうやって my_tools.py という道具箱(モジュール)を探し出すのでしょうか?

実は、Pythonは「探すべき場所のリスト」をあらかじめ持っています。

そのリストのことを、専門用語で sys.path(シス・パス)と呼びます。

sys.path は、Pythonがモジュールを探しに行く「住所録」や「捜索リスト」のようなものだと考えてください。

Pythonは、このリストの上から順番に「〇〇というファイルはありますか?」と探しに行きます。

この sys.path には、主に以下の場所が登録されています。

  1. 今、実行しているスクリプトがあるフォルダ(カレントディレクトリ)これが一番大事です! だから、同じフォルダにあるファイル同士は、いつでも簡単に import できるんですね。
  2. Pythonをインストールしたときに最初から入っているライブラリの場所(標準ライブラリ)
  3. pipなどで後から追加したライブラリの場所(サードパーティライブラリ)

このリストの どこか に見つかれば import は成功し、リストの最後まで探しても見つからなければ「ModuleNotFoundError(モジュールが見つかりません)」となるわけです。


階層の核心!「絶対インポート」と「相対インポート」

さて、プロジェクトが大きくなり、パッケージ(倉庫)の中にさらにパッケージ(小倉庫)を作るような、複雑な階層構造になってきたとします。

my_project/
├── main.py
└── company/
    ├── __init__.py
    ├── staff.py
    └── products/
        ├── __init__.py
        └── tools.py

こんな構造を想像してみてください。

my_project がプロジェクトの入り口です。

company はパッケージ(倉庫)で、その中に staff.py(道具箱)があります。

さらに company の中には products という別のパッケージ(小倉庫)があり、その中に tools.py があります。

このとき、ファイルの指定方法(住所の書き方)には2種類あります。

1. 絶対インポート (Absolute Import)

これは、「プロジェクトの入り口(ルート)から見た、完全な住所」を指定する方法です。

上の例で、もし main.py が tools.py を読み込みたい場合、こう書きます。

# main.py の中
import company.products.tools

これは、「company という倉庫の中の、products という小倉庫の中の、tools という道具箱を持ってきて」という意味になります。

一番上の階層から書くので、住所が長くなりがちですが、誰がどこから読んでも「ああ、あの場所のことね」と分かる、最も確実で間違いのない方法です。

2. 相対インポート (Relative Import)

これは、「今自分がいる場所から見た、相対的な位置関係」で指定する方法です。

「隣の部屋」や「一つ上の階層」といった指定の仕方をします。

この方法では「ドット(.)」を使います。

  • . (ドット1個): 「今、自分と同じ階層(フォルダ)にいる」
  • .. (ドット2個): 「一つ上の階層(フォルダ)にいる」

例えば、company/products/tools.py の中から、すぐ隣(同じ階層)にある ではなく、一つ上の company 階層にある staff.py を読み込みたいとしましょう。

(tools.py から staff.py を見ると、staff.py は親の company フォルダにいますね)

# company/products/tools.py の中
from .. import staff

これは、「今いる products フォルダから一つ上がって(company フォルダに行って)、そこにある staff 道具箱を持ってきて」という意味になります。


どっちを使うべき? メリット・デメリット

では、この「絶対」と「相対」、どちらを使うのが良いのでしょうか?

絶対インポート

  • メリット:
    • 住所が明確で、どこから読んでも迷わない。
    • ファイルの位置を移動させたときも、プロジェクトの入り口からのパスが変わらなければ修正が不要。
  • デメリット:
    • 階層が深くなると、import a.b.c.d.e のように記述がとても長くなる。

相対インポート

  • メリット:
    • 記述が短くて済む。
    • パッケージ(倉庫)の名前を丸ごと変えたとき、パッケージ内部の import 文(相対パス)は修正しなくても動く。
  • デメリット:
    • 「今、自分はどこにいるんだっけ?」と、現在の位置を把握していないと混乱しやすい。
    • . がいくつも続くと(from ...module)、非常に読みにくくなる。
    • そのファイルを直接実行しようとすると、相対インポートはエラー(ImportError)を起こします。

まとめと今後の学習指針

Pythonのインポート階層、いかがでしたか?

  1. Pythonは sys.path という「住所録」に書かれた場所を探しに行く。
  2. __init__.py ファイルは、フォルダを「パッケージ(倉庫)」として認識させるための目印。
  3. 住所の書き方には、入口から全部書く「絶対インポート」と、今いる場所から指定する「相対インポート」がある。

初心者のうちは、まず「絶対インポート」だけを使うことを強くお勧めします!

それが一番混乱しない、安全な道です。

そして、なぜ import が失敗するのか分からなくなったときは、sys.path の中身を print してみて、「今Pythonがどこを探しに行っているのか」を実際に目で見て確認するクセをつけましょう。

さあ、これからはエラーを恐れずに、便利なモジュールやパッケージをどんどん活用してみてください!

セイ・コンサルティング・グループの新人エンジニア研修のメニューへのリンク

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。