python について
python は、現代的な Python 3.12 以降のプロジェクト向けコーディング規約です。型付け、モジュール構成、階層型アーキテクチャの一貫性を求める開発者を対象としています。
主な機能
- 01ドメイン中心のモジュール構成
- 02階層型依存関係と薄いエントリーポイント
- 03型注釈と現代的な構文
- 04命名とインポートの規約
- 05例外とログの指針
活用シーン
- 01新しい Python アプリの構成
- 02再利用可能なビジネスロジックサービスの定義
- 03関数シグネチャと型注釈のレビュー
- 04循環インポートとモジュール間結合の削減
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ imankulov/ skills --skill pythonインストール後、Claude Code で/pythonを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/data-structures.mddocstrings.mdlogical-grouping.mdmodule-organization.mdparallel-symmetry.mdurl-parsing.md詳細
Python コーディング規約
型安全性、明確な関数シグネチャ、一貫したモジュール構成、階層化アーキテクチャを重視した、モダンな Python(3.12 以降)のための独自の規約です。
アプリケーションアーキテクチャ
スクリーミングアーキテクチャ
モジュールにはドメインの名前を付けます。たとえば、billing/、authentication/、annual_reports/ のようにします。そうすれば、ディレクトリ一覧を見ただけで、アプリがどのフレームワークを使っているかではなく、何をするものかが分かります。ディレクトリでドメインを表し、その下のレイヤーはディレクトリ内のファイルに配置します(billing/services.py、billing/api.py など)。トップレベルに models/ や views/ ディレクトリを作ると、この構造が逆転し、ツリー全体にドメインが分散してしまいます。
モジュールはフラットに保ちます。auth/、users/、billing/ は兄弟ディレクトリとし、users/auth/ や users/billing/ のようにネストさせません。ネストするとドメインが1階層下に埋もれ、トップレベルの一覧を見ても、アプリが何をするものかすぐに分からなくなります。
レイヤー
依存関係は下向きに流れます。各レイヤーは自分より下のレイヤーからインポートし、その逆は行いません。
+-------------------------------------------------------+
| api.py, tasks.py, admin.py, ... (entry points) |
+-------------------------------------------------------+
| imports
v
+-------------------------------------------------------+
| services.py (business logic) |
+-------------------------------------------------------+
| imports
v
+-------------------------------------------------------+
| models.py, types.py, const.py (data layer) |
+-------------------------------------------------------+
エントリーポイント(api.py、tasks.py、admin.py、cli.py など)は、サービス関数を呼び出す薄いラッパーです。リクエストの解析、スケジューリング、出力の整形を行いますが、ビジネスロジックは含めません。エントリーポイント同士はインポートしません。これらはサービスに依存する同列の要素です。相互にインポートすると、循環依存と密結合が生じます。
services.py にはすべてのビジネスロジックを配置します。サービス関数は、API エンドポイント、バックグラウンドタスク、CLI コマンド、管理操作など、どのエントリーポイントからでも再利用できます。いずれも同じサービス関数を呼び出せます。
サービスは構造化された入力を受け取り、構造化された出力を返します。処理に必要であれば、渡されたパスのファイルを読み込む、データベースにクエリを実行する、サードパーティ API を呼び出すなど、必要な I/O を行ってかまいません。ただし、ユーザーに接する境界には触れません。プロンプトの表示や引数の解析、生の文字列から型付き値への変換、HTTP リクエストや他のフレームワークオブジェクトへのアクセス、表示用の結果の整形は行いません。これらはすべてラッパー側の役割です。
データレイヤー(models.py、types.py、const.py)では、ビジネスロジックを含まないデータ構造を定義します。const.py には内部インポートを含めてはいけません。どこからでも安全にインポートできます。
インポートはすべてモジュールの先頭に記述します。循環依存を解消する場合に限り、関数内でインポートを遅延させます。これが頻繁に必要になる場合は、モジュール構成のリファクタリングが必要な可能性があります。
エントリーポイントの命名規約
サービス関数には基本の名前を付けます。それをラップするエントリーポイントには、レイヤーを示すサフィックスを付けます。これにより、エイリアスを使わずにどこからでも from myapp.services import create_user と記述できます。
| レイヤー | サフィックス | 例 |
|---|---|---|
| サービス(ビジネスロジック) | (なし) | create_user() |
| API エンドポイント | _api | create_user_api() |
| CLI コマンド | _cli | create_user_cli() |
| バックグラウンドタスク | _task | create_user_task() |
関数
- 型に情報がある場合は、引数と戻り値に型注釈を付けます。正直に示せる型が
Anyしかない場合(省略可能なライブラリや動的にインポートされるライブラリをラップするもの、汎用デコレーター、**kwargsのパススルーなど)は、型注釈を付けません。Anyは、読者、型チェッカー、IDE の役に立たず、ノイズになるだけです。 - docstring に型情報は記載しません。型はシグネチャに記載します。どの程度記述するかについては、Docstrings and comments を参照してください。
- 引数が 3 つを超える関数では、キーワード専用引数(
*)を使います。 - 内部ヘルパー関数にはアンダースコアを先頭に付け、docstring は 1 行にします。
- 呼び出し元が 1 つだけのヘルパーは、それ自体にテストする価値のあるロジック(述語、マッパー、変換)がある場合を除き、インライン化します。こうした処理に名前を付けると、テストでは呼び出し元に必要なものをすべて準備する代わりに、手作業で用意した入力を使ってテストできます。それ以外の場合、1 つの関数本体に別の名前を付けても意味はありません。
- ファイルの構成は、新聞のように上から下へ(Robert C. Martin の Clean Code)とします。公開関数を先に置き、ヘルパーをその下に置き、呼び出し元が 1 つの
_helperは呼び出し元のすぐ下に置きます。Pydantic モデルは上部近くにまとめ、使用する前に定義します。
以下の拡張 docstring 形式は、3 つすべてのセクションが必要となった関数の例です。ほとんどの関数は 1 行の概要で十分です。
def process_user_data(*, user_id: int, name: str, email: str, age: int) -> UserData:
"""Process and validate user data.
Args:
user_id: Unique identifier for the user
name: User's full name
email: User's email address
age: User's age in years
Returns:
Processed user data with validation status
Raises:
ValueError: If the email is malformed or the age is negative
"""
...
def _format_name(first: str, last: str) -> str:
"""Combine first and last name into full name."""
return f"{first} {last}".strip()
例外
サービス関数は、例外を送出して失敗を通知します。意図的に送出するすべての例外を、docstring の Raises: セクションに列挙します。この契約を読んだエントリーポイントが、何を捕捉し、どのように変換するかを判断します。
まずは組み込み例外を使います。入力不正には ValueError、不変条件の破綻には RuntimeError を使います。アプリ独自の例外が必要な場合は、myapp/exceptions.py に 1 つの例外階層を定義します。これはアプリの __init__.py の隣、例外を送出するモジュールより 1 つ上の階層に置き、MyAppError を基底クラスとして、その下に MyAppAuthenticationError などを配置します。モジュールごとに独自の exceptions.py を設けることはせず、ほとんどの場合、個別の失敗条件に独自のクラスを作る必要はありません。呼び出し元が捕捉するのは、末端のクラスよりも基底クラスであることがはるかに多いためです。
先頭のアンダースコア
先頭のアンダースコア 1 つは、「内部用です。このモジュールの外から呼び出したりインポートしたりしないでください」という意味です。外部で使われると実際に問題が起きる、次の 2 つの場合に限って使います。
- 内部関数とメソッド。
_helperは、公開 API が適用する検証を省略したり、周囲の関数の不変条件を保持したり、いつでも形を変えたりする可能性があります。 - 遅延初期化に使う可変のシングルトン状態(
_queue、_started、_patched、_server_url)。外部コードが変更すると、モジュールの不変条件が破られます。
別のモジュールからインポートする必要があるなら、それは内部用ではありません。アンダースコアを外してください。from foo import _bar は偽りです。関数を真に内部用として保つか、共有モジュールに移してプレフィックスを削除してください。ヘルパーを共有の utils.py に切り出す場合は、アンダースコアを外します。以前のモジュールでは内部用でしたが、新しいモジュールではそうではないためです。
モジュールレベルの定数やクラスには、プレフィックスを付けないでください。UPPER_CASE で「定数」であることはすでに伝わります。クラスの非公開性は、クラスの配置場所によって示します(再エクスポートせず、呼び出し元の隣に置きます)。_MAX_RETRIES や _RequestTracer ではなく、MAX_RETRIES や RequestTracer を使います。
ダンダー(__init__、__enter__、…)は Python のプロトコルであり、この規約の対象ではありません。
インポートの整理
自分のモジュールは実際の名前でインポートします。エイリアスを付けて内部コードの出所を分かりにくくしてはいけません。サービス関数と、それをラップするエントリーポイントの名前が衝突する場合は、import ... as ではなくレイヤーのサフィックスを付けます。エイリアスは、サードパーティーのインポート同士が実際に衝突する場合と、慣例的な短縮名(import pandas as pd)に限って使います。
共有ユーティリティはユーティリティモジュールに置きます。別のモジュール内で最初に作成したヘルパーを 2 つ目のモジュールでも使う必要が生じたら、共有の場所(utils.py、helpers.py)に移します。module_a が汎用ヘルパーを先に定義したというだけの理由で、module_b が module_a に依存する構成にしてはいけません。
型ヒント(PEP 604 および 695)
モダンなユニオン構文(list[str]、dict[str, int] | None)を使用してください。基本型に typing から List、Dict、Optional、Union をインポートしないでください。
ジェネリックは PEP 695 の型パラメーターで宣言してください。TypeVar と Generic は使用せず、class Codec[T]:、def first[T](items: list[T]) -> T: のように記述します。
命名
同じルールが、変数、関数、クラス、パッケージ、モジュールのファイル名すべてに適用されます。
単語を省略せずに書いてください。 doc より document、req_id より request_id、cfg より configuration を優先してください。母音を省いたり単語を短縮したりすれば、数回分のキー入力は減りますが、後から読む人は毎回意味を推測することになります。例外は、元の語より短縮形のほうがよく知られている場合です(id、url、json、csv、http)。ただし、少しでも迷う場合は、これらも省略せずに書いてください。
単語は必ず区切ってください。 変数、関数、モジュールのファイル名には snake_case、クラスには CamelCase を使用してください。境界を設けずに単語を連結しないでください。parsepayload ではなく parse_payload、Retrypolicy ではなく RetryPolicy、syncstate.py ではなく sync_state.py とします。
CamelCase では略語も一語として扱い、先頭の文字だけを大文字にしてください。JSONParser ではなく、JsonParser、ApiClient、DbConnection、HttpResponse、SqlQuery とします。
ロギング
プロジェクトの指示でロギング規約が定められている場合は、それに従ってください。定められていない場合は、標準ライブラリの logging モジュールを使用し、値は文字列に埋め込まず、遅延評価される %s 引数として渡してください。f-string ではなく、logger.warning("Vector search failed: %s", exc) のように記述します。
参考資料
特定のトピックに関する詳しいパターンが必要な場合は、以下を適宜参照してください。
- データ構造 — Pydantic と dict の使い分け、列挙型、インターフェース、TypedDict
- Docstring とコメント — どの程度記述するか、セクションを設けるべきタイミング
- モジュールの構成 — types.py、const.py の配置ルール
- 論理的なグループ化 — フィールド、定数、ステップを関心事ごとにまとめる方法
- 並列処理の対称性 — 同じ入力を処理する 2 つのシステムにおける命名と構造
- URL の解析 — URL 操作にはすべて furl を使用
検証
Python コードを記述した後は、参考資料に記載されている、特に見落としやすい 2 つのルールを確認してください。
- フィールド、定数、処理ステップが関心事ごとにグループ化され、どこでも同じ順序になっている
- 同じ入力を並列処理するシステム間で、プレフィックス、ガード、データ構造が共通している