モジュール、パッケージと pyproject.toml
import システム、モジュールとパッケージの違い、__init__.py と __main__、そして pyproject.toml で依存関係・ツール設定・配布まで一箇所に整理します。
1部の最終章です。六つの章の間、一つのファイルの中でコードを書いてきました。本章は 複数のファイルに分け、それらをプロジェクトとしてまとめる方法 — import システム、モジュールとパッケージ、そしてそのすべてを定義する pyproject.toml です。
本章の pyproject.toml は本書全体で継続して拡張されていきます。第30章 型チェッカ設定と CI 統合 で [tool.ruff] / [tool.pyright] セクションを正式設定に育て、第32章 uv でライブラリ配布 で [build-system] / [project.scripts] セクションを使って最初のライブラリを PyPI に出します。本章はそのすべての作業の土台を敷きます。
モジュール — ファイル一つがモジュール #
Python では .py ファイル一つがそのままモジュール です。他の場所から import して使います。
def add(a: int, b: int) -> int:
return a + b
def multiply(a: int, b: int) -> int:
return a * b
PI = 3.141592import math_utils
print(math_utils.add(2, 3)) # 5
print(math_utils.PI) # 3.141592四つの import 形 #
# 1. モジュールまるごと
import math_utils
math_utils.add(1, 2)
# 2. 別名
import math_utils as mu
mu.add(1, 2)
# 3. 特定の名前だけ取り込む
from math_utils import add, PI
add(1, 2)
# 4. 別名 + 取り込み
from math_utils import add as plus
plus(1, 2)from m import * はほぼ使わない
#
from math_utils import *名前衝突、どこから来た名前か追跡不可、静的解析器の混乱。新規コードではほとんど使いません。例外はインタラクティブシェルで一時的に試す場合くらいです。
パッケージ — ディレクトリがパッケージ #
複数のモジュールをまとめたいときは ディレクトリ にします。
my_app/
├── __init__.py
├── auth.py
├── db.py
└── handlers/
├── __init__.py
├── user.py
└── post.pyこの構造で次のようになります。
from my_app import auth
from my_app.db import connect
from my_app.handlers.user import create_user__init__.py — 二つの役割
#
__init__.py は二つの仕事をします。
- このディレクトリがパッケージであることを示す (歴史的役割 — Python 3.3 以降はなくても namespace package として扱われる)
- パッケージが import されるときに実行されるコード — 公開 API を整理したり初期化したりする
"""my_app — 例題アプリケーション。"""
from my_app.auth import login, logout
from my_app.db import connect
__all__ = ["login", "logout", "connect"]
__version__ = "0.1.0"こうすれば呼び出し側は次のようになります。
from my_app import login, connect内部構造(auth モジュール、db モジュール) が露出しません。公開 API を一箇所で定義する パターンです。
__all__ は from my_app import * の際に取り込まれる名前のホワイトリストですが、実際には 公開 API を明示する文書 の役割としてより頻繁に使われます。
namespace package — __init__.py がない場合
#
3.3+ からは __init__.py のないディレクトリもパッケージになります(implicit namespace package) 。短所は次の通りです。
- 初期化コードを置く場所がない
- 一部ツールが認識しない場合がある (古いバージョンたち)
新しいパッケージを作るときは __init__.py を置く 方が安全です。空でも構いません (pass の一行も不要、空のファイル) 。
絶対 import vs 相対 import #
同じパッケージの中で別のモジュールを取り込むとき、二つの方法があります。
# 絶対 import
from my_app.db import connect
from my_app.auth import current_user
# 相対 import
from ..db import connect
from ..auth import current_user.. は「一段上」、. は「同じディレクトリ」です。
両方とも使えますが絶対 import を推奨 します (PEP 8) 。どこから取り込んでいるか一目で分かり、ファイルを移動したときに壊れる可能性も小さくなります。相対 import はパッケージ内部の短い距離でだけ使うくらいが無難です。
__main__ — モジュールを直接実行するとき
#
スクリプトとして実行したときだけ動くコードを置く場所です。
def main():
print("hello!")
if __name__ == "__main__":
main()このファイルは二つの方法で使えます。
$ uv run cli.py
hello! ← __name__ が '__main__'import cli
cli.main() # 明示的に呼び出せば動く
# import 時点では main() は呼ばれないif __name__ == "__main__": がないと import しただけで main が実行され、副作用が発生します。常にこのガードを置く のが模範です。
パッケージ単位の実行 — __main__.py
#
パッケージ自体を実行可能にしたいなら次の通りです。
my_cli/
├── __init__.py
├── __main__.py ← ここに main エントリポイント
└── core.pyfrom my_cli.core import run
if __name__ == "__main__":
run()こうすれば次のように実行できます。
$ uv run python -m my_cliCLI 道具を作るときによく使うパターンです。第33章 CLI 道具作り (Typer) で詳しく扱います。
標準ライブラリ — 同じ import システム #
標準ライブラリも同じように import します。別途インストールなしですぐ使えます。
import os # ファイルシステム
import sys # インタプリタ、argv
import json # JSON
import re # 正規表現
import datetime # 日付/時刻
import pathlib # パスオブジェクト
from collections import Counter, defaultdict
from itertools import chain, groupby
from functools import lru_cache, partial標準ライブラリは 公式ドキュメント がもっとも速い参考になります。
pyproject.toml — プロジェクトの単一の定義
#
第1章 で uv init で作ったあのファイルです。本章で詳しく見ていきます。
[project]
name = "my-app"
version = "0.1.0"
description = "例題アプリケーション"
readme = "README.md"
requires-python = ">=3.14"
authors = [
{ name = "Curtis", email = "you@example.com" },
]
license = { text = "MIT" }
dependencies = [
"httpx>=0.28",
"pydantic>=2.10",
]
[project.optional-dependencies]
docs = ["mkdocs>=1.6"]
[dependency-groups]
dev = [
"pytest>=8.0",
"ruff>=0.7",
"pyright>=1.1",
]
[project.scripts]
my-app = "my_app.cli:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"主要なセクションを一つずつ見ていきます。
[project] — PEP 621 メタデータ
#
名前、バージョン、説明、Python バージョン要求、作成者、ライセンス、ランタイム依存関係。
[project.optional-dependencies] — オプション依存関係
#
pip install 'my-app[docs]' のような形でユーザーが選んでインストールできるまとまりです。ライブラリを配布するときによく使います。
[dependency-groups] — 開発/テスト用
#
PEP 735 で標準化されたセクションです。配布には含まれないが開発時には必要な ツールをまとめます。uv が uv add --dev で自動的に追加します。
[project.scripts] — CLI エントリポイント
#
このセクションがあると、パッケージインストール時に コンソールコマンドが自動的に作られます。
[project.scripts]
my-app = "my_app.cli:main"インストール後は次の通りです。
$ my-app
# my_app.cli モジュールの main 関数が実行されるCLI 道具を作るなら本セクションが核心です。第33章 CLI 道具作り (Typer) で実際の CLI を作り、本セクションでエントリポイントをまとめます。
[build-system] — ビルドバックエンド
#
PyPI に上げるパッケージなら必要です。hatchling、setuptools、pdm-backend など複数の選択肢がありますが、新規プロジェクトは hatchling が軽量で無難です。
第32章 uv でライブラリ配布 で本セクションを正式に書き、PyPI へのリリース手順までつなげます。
pyproject.toml の中にツール設定も
#
リンター / フォーマッタ / 型チェッカも同じファイルに設定を置きます。
[tool.ruff]
line-length = 100
target-version = "py314"
[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "B"]
[tool.pyright]
typeCheckingMode = "strict"
pythonVersion = "3.14"
[tool.pytest.ini_options]
testpaths = ["tests"]setup.cfg、.flake8、mypy.ini、pytest.ini のような散らばっていたファイルが 一つにまとまります。 本章ではプレビュー程度だけ見て、正式設定は第30章 型チェッカ設定と CI 統合 で扱います。
uv の日常コマンド — もう一度 #
第1章 で見たものに加えて、よく使う流れを示します。
# 新しいプロジェクト
uv init my-app --python 3.14
# 依存関係
uv add httpx pydantic
uv add --dev pytest ruff pyright
uv remove old-package
# 同期 (別マシンで / 新たに受け取ったとき)
uv sync
# 実行
uv run python main.py
uv run pytest
uv run ruff check
uv run pyright
# 一時実行 (プロジェクトの依存関係ではないツール)
uvx ruff check . # 一度だけ回して環境を汚さない
# Python 自体のアップグレード
uv python install 3.14uvx は 隔離された環境で一度だけ実行 するコマンドです。pipx と同じ役割で、グローバルに道具をインストールしないので安全です。
1部のまとめ — ここまでで揃った道具 #
7 章を経てモダンPython の 基礎インフラ が揃いました。少なくとも次のようなコードは直接書き、読めるようになっています。
from collections.abc import Callable
type UserId = int
def find_users(
ids: list[UserId],
*,
transform: Callable[[UserId], str] = str,
skip_invalid: bool = True,
) -> dict[UserId, str]:
"""ID リストから変換されたユーザーマップを作る。"""
result: dict[UserId, str] = {}
for uid in ids:
try:
result[uid] = transform(uid)
except ValueError:
if not skip_invalid:
raise
return result型ヒント、| union、組み込みジェネリクス、keyword-only、Callable、例外処理、docstring — すべて 1 部で扱った道具です。
練習問題 #
- 新しいプロジェクト
mathkitを作り、パッケージ構造mathkit/{__init__.py, basic.py, advanced.py, __main__.py}を手で作ってください。basic.pyにadd、subtract、advanced.pyにfactorialを置き、__init__.pyで公開 API として露出します。uv run python -m mathkitでパッケージ単位の実行が動作するか確認します。 - 上のプロジェクトの
pyproject.tomlに[project.scripts]でmathkit = "mathkit.__main__:main"を追加してください。uv pip install -e .またはuv tool install --from . mathkitでインストール後、シェルからmathkitコマンドが動作するか確認します。 - 同じプロジェクトに
[dependency-groups]の dev グループでpytest、ruff、pyrightを追加した後、tests/test_basic.pyにadd(2, 3) == 5を検証する簡単なテストを書き、uv run pytestが通るか確認します。
一行まとめ: ファイル = モジュール、ディレクトリ = パッケージ。import は絶対パスを推奨。
if __name__ == "__main__":ガードは常に、パッケージ単位の実行は__main__.py。pyproject.toml一枚にメタデータ / 依存関係 / スクリプト / ツール設定がすべて集まる。uv の日常はinit / add / sync / run / uvx。
次の章 #
1 部が終わり 2 部 コードの構造化 が始まります。最初の章は 第8章 dataclass と __slots__ — データのまとまりクラスを短く安全に作る道具です。1 部の関数 / コレクションの道具では十分に解けなかった「形が決まったオブジェクト」の問題が dataclass で綺麗に解けます。