2.

Python モジュール (import) 完全ガイド(パッケージ / __init__.py / 相対 import)

編集
この記事の要点
  • モジュール = 1 つの .py ファイル。import math でロード
  • パッケージ = __init__.py を持つディレクトリ(Python 3.3+ は暗黙名前空間パッケージで不要)
  • from datetime import datetime / 別名 import numpy as np でスコープ短縮
  • if __name__ == "__main__": でスクリプト実行時とライブラリ import 時を分岐
  • 相対 import from .util import x はパッケージ内専用。スクリプト直接実行では使えない

import の基本

# 1. モジュール全体を import
import math
print(math.pi)             # 3.141592...

# 2. 特定の名前を import
from datetime import datetime, timedelta
now = datetime.now()
tomorrow = now + timedelta(days=1)

# 3. すべて import(非推奨)
from math import *         # ★ 名前空間汚染。避けるべき

# 4. 別名(エイリアス)
import numpy as np
import pandas as pd
from collections import defaultdict as dd

モジュールとパッケージ

概念実体
モジュール (Module)単一の .py ファイルmath.py
パッケージ (Package)__init__.py 付きディレクトリrequests/
サブパッケージパッケージ内のパッケージrequests/auth/
名前空間パッケージ__init__.py 不要(Python 3.3+)分散したディレクトリ

典型的なパッケージ構造

myproject/
├── __init__.py            # パッケージ初期化(空でも可)
├── main.py
├── utils/
│   ├── __init__.py
│   ├── strings.py
│   └── dates.py
└── models/
    ├── __init__.py
    ├── user.py
    └── order.py
# main.py から各モジュールを呼ぶ
from myproject.utils.strings import slugify
from myproject.models.user import User
from myproject.models import order   # サブモジュール

u = User("taro")
o = order.Order(u)
print(slugify("Hello World"))   # hello-world

__init__.py の役割

# myproject/utils/__init__.py
"""utils パッケージの公開 API を集約"""

from .strings import slugify, escape_html
from .dates import format_date, parse_iso

__all__ = ["slugify", "escape_html", "format_date", "parse_iso"]
# from myproject.utils import * で出てくる名前を制限

これにより外部からは from myproject.utils import slugify という短い形で書けるようになります。

暗黙名前空間パッケージ (PEP 420, Python 3.3+)

# __init__.py が無くてもパッケージとして認識される
myns/
├── plugin1/
│   └── feature.py
└── plugin2/
    └── feature.py

# 異なる場所にあるディレクトリを 1 つのパッケージとして扱える
# プラグインシステム等に便利

__name__ == "__main__" イディオム

# utils.py
def greet(name):
    return f"Hello, {name}!"

if __name__ == "__main__":
    # このファイルを直接 python utils.py で実行したときだけ走る
    # import utils したときは走らない
    print(greet("World"))

# 動作:
# $ python utils.py        → "Hello, World!" 出力
# >>> import utils         → 何も出力しない

相対 import と絶対 import

# myproject/models/user.py から utils.strings を参照する場合

# 絶対 import(推奨)
from myproject.utils.strings import slugify

# 相対 import (.., . で階層を表す)
from ..utils.strings import slugify    # 2 階層上
from .order import Order               # 同じ models パッケージ内

# 相対 import の制約:
# - パッケージ内でしか使えない
# - python myproject/models/user.py のような直接実行では動かない
#   → python -m myproject.models.user で実行する

sys.path とモジュール検索順

import sys
print(sys.path)
# ['', '/usr/lib/python311.zip', '/usr/lib/python3.11', ...]
# 先頭が空文字 = カレントディレクトリ

# import 時は sys.path を順に探す
# 1. 組み込みモジュール (sys, builtins)
# 2. sys.path の各エントリ
# 3. 見つからなければ ModuleNotFoundError

# 動的に追加(推奨されないが緊急時に)
sys.path.insert(0, "/path/to/my/libs")
import my_lib

モジュールのキャッシュとリロード

import sys
import mymodule

# import したモジュールは sys.modules にキャッシュされる
print(sys.modules['mymodule'])

# 同じプロセス内で import しても 2 回目以降は実行されない
# 編集を反映したい場合(開発中など)
import importlib
importlib.reload(mymodule)

# キャッシュから削除して再 import
del sys.modules['mymodule']
import mymodule

標準ライブラリ主要モジュール

カテゴリモジュール用途
テキストre / string / textwrap正規表現 / 文字列定数 / 整形
数値math / random / statistics / decimal数学関数 / 乱数 / 統計 / 十進演算
日付datetime / time / calendar / zoneinfo日時 / Unix 時間 / タイムゾーン (3.9+)
データ構造collections / heapq / bisect / queuedefaultdict / deque / ヒープ / キュー
関数functools / itertools / operatorlru_cache / 組合せ / 演算子
ファイルos / pathlib / shutil / globパス操作 / コピー / パターン検索
I/Oio / json / csv / pickleストリーム / 各種フォーマット
並行threading / multiprocessing / asyncio / concurrent.futuresスレッド / プロセス / 非同期
HTTPurllib / http標準 HTTP クライアント(実用は requests)
テストunittest / doctestテスト基盤(実用は pytest)

サードパーティパッケージのインストール

# pip でインストール
pip install requests pandas

# 仮想環境を作って分離(推奨)
python -m venv .venv
source .venv/bin/activate         # Mac/Linux
.venv\Scripts\activate            # Windows
pip install -r requirements.txt

# pyproject.toml / Poetry を使う場合
poetry add requests
poetry install

モジュールから他の属性を取得

import math

# 公開属性一覧
print(dir(math))
# ['__doc__', '__name__', ..., 'pi', 'sqrt', 'sin', ...]

# ドキュメント
help(math.sqrt)
print(math.sqrt.__doc__)

# 属性が存在するか
print(hasattr(math, 'pi'))   # True

# 動的取得
fn = getattr(math, 'sqrt')
print(fn(16))                # 4.0

FAQ

Q: from x import * はなぜダメ?
A: 名前空間が汚染され、どの関数がどこから来たか不明になります。__all__ 制限があっても可読性が落ちるため避けるべき。

Q: 循環 import が起きた
A: A が B を、B が A を import する状態。回避策は (1) 関数内で遅延 import、(2) 共通部分を 3 つ目のモジュールに分離、(3) 設計見直し(依存方向を一方向に)。

Q: スクリプトをパッケージ内で実行したい
A: python -m myproject.models.user を使えば相対 import も動きます。python myproject/models/user.py はパッケージ認識されない。

編集
Post Share
子ページ
  1. math
同階層のページ
  1. 組み込み関数
  2. モジュール

最近更新/作成されたページ