python-best-practices について
Python 開発で品質と一貫性を保つための指針を示すスキルです。PEP 8、型ヒント、docstring、例外処理、コード構成のパターンを扱います。
主な機能
- 01PEP 8 のスタイル指針
- 02型ヒントと現代的な構文
- 03NumPy スタイルの docstring
- 04例外処理とコンテキストマネージャー
- 05データクラス、列挙型、pathlib
- 06pytest によるテスト
活用シーン
- 01一貫したスタイルの Python コード作成
- 02関数やクラスへの型ヒントと文書の追加
- 03ファイルやモジュール構成のリファクタリング
- 04例外処理やファイル操作の改善
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ jefflester/ claude-skills-supercharged --skill python-best-practicesインストール後、Claude Code で/python-best-practicesを入力して読み込まれたか確認します。
詳細
Python のベストプラクティス
目的
このスキルでは、Python プロジェクト全体でコードの品質、保守性、一貫性を確保するための、Python 開発のベストプラクティスを説明します。
このスキルを使用するタイミング
次の場合に自動的に有効になります:
- Python ファイル(*.py)を扱うとき
- 「python」、「best practices」、「style guide」に言及したとき
- 型ヒントや docstring を追加するとき
- Python コードをリファクタリングするとき
スタイルガイドライン
PEP 8 への準拠
Python コードでは PEP 8 スタイルガイドに従います:
- インデント:インデントレベルごとに 4 つのスペース
- 行の長さ:コードは最大 79 文字、docstring とコメントは 72 文字
- 空行:トップレベルの定義間には 2 行、メソッド間には 1 行
- インポート:必ずファイルの先頭に配置し、標準ライブラリ、サードパーティ、ローカルの順にグループ化
- 命名規則:
- 関数、変数、モジュールには
snake_case - クラスには
PascalCase - 定数には
UPPER_SNAKE_CASE - 内部用・非公開には先頭にアンダースコアを付けた
_name
- 関数、変数、モジュールには
インポートの整理
インポートは必ず次の順序で整理します:
# 1. Standard library imports
import os
import sys
from pathlib import Path
# 2. Third-party imports
import requests
import numpy as np
# 3. Local application imports
from myapp.core import MyClass
from myapp.utils import helper_function
TYPE_CHECKING を使用して循環インポートを回避します:
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from myapp.other_module import OtherClass
def my_function(obj: "OtherClass") -> None:
"""Function that uses OtherClass only for type hints."""
pass
型ヒント
型ヒントを必ず使用する
型ヒントを使うとコードが明確になり、エラーを早期に検出できます:
def process_data(
items: list[str],
max_count: int | None = None,
verbose: bool = False
) -> dict[str, int]:
"""Process items and return counts.
Parameters
----------
items : list[str]
List of items to process
max_count : int | None, optional
Maximum items to process, by default None
verbose : bool, optional
Enable verbose output, by default False
Returns
-------
dict[str, int]
Dictionary mapping items to counts
"""
result: dict[str, int] = {}
for item in items[:max_count]:
result[item] = result.get(item, 0) + 1
if verbose:
print(f"Processed: {item}")
return result
最新の型構文(Python 3.10+)
Union の代わりに | を使った最新の共用体構文を使用します:
# Good (Python 3.10+)
def get_value(key: str) -> int | None:
pass
# Avoid (old style)
from typing import Union, Optional
def get_value(key: str) -> Optional[int]:
pass
ジェネリック型
組み込みのジェネリック型を使用します(Python 3.9+):
# Good (Python 3.9+)
def process_list(items: list[str]) -> dict[str, int]:
pass
# Avoid (old style)
from typing import List, Dict
def process_list(items: List[str]) -> Dict[str, int]:
pass
docstring
NumPy スタイルの docstring
一貫性を保つため、NumPy スタイルの docstring を使用します:
def calculate_statistics(
data: list[float],
include_median: bool = True
) -> dict[str, float]:
"""Calculate statistical measures for a dataset.
This function computes mean, standard deviation, and optionally
median for the provided dataset.
Parameters
----------
data : list[float]
List of numerical values to analyze
include_median : bool, optional
Whether to calculate median, by default True
Returns
-------
dict[str, float]
Dictionary containing:
- 'mean': arithmetic mean
- 'std': standard deviation
- 'median': median value (if include_median=True)
Raises
------
ValueError
If data is empty or contains non-numeric values
Examples
--------
>>> calculate_statistics([1.0, 2.0, 3.0, 4.0, 5.0])
{'mean': 3.0, 'std': 1.414, 'median': 3.0}
Notes
-----
Standard deviation uses Bessel's correction (ddof=1).
"""
if not data:
raise ValueError("Data cannot be empty")
# Implementation here
pass
クラスの docstring
class DataProcessor:
"""Process and transform data from various sources.
This class provides methods for loading, transforming, and
validating data from multiple input formats.
Parameters
----------
source_dir : Path
Directory containing source data files
cache_enabled : bool, optional
Enable result caching, by default True
Attributes
----------
source_dir : Path
Directory path for source files
cache : dict[str, Any]
Cache for processed results
Examples
--------
>>> processor = DataProcessor(Path("/data"))
>>> results = processor.process_files()
"""
def __init__(self, source_dir: Path, cache_enabled: bool = True):
"""Initialize the DataProcessor."""
self.source_dir = source_dir
self.cache: dict[str, Any] = {} if cache_enabled else None
エラー処理
具体的な例外型
裸の except ではなく、具体的な例外型を使用します:
# Good
try:
with open(file_path) as f:
data = f.read()
except FileNotFoundError:
logger.error(f"File not found: {file_path}")
raise
except PermissionError:
logger.error(f"Permission denied: {file_path}")
raise
# Avoid
try:
with open(file_path) as f:
data = f.read()
except: # Too broad!
pass
コンテキストマネージャー
リソースには必ずコンテキストマネージャーを使用します:
# Good
with open(file_path) as f:
content = f.read()
# Avoid
f = open(file_path)
content = f.read()
f.close() # Easy to forget!
カスタム例外
ドメイン固有のエラーにはカスタム例外を定義します:
class ValidationError(Exception):
"""Raised when data validation fails."""
pass
class DataProcessingError(Exception):
"""Raised when data processing encounters an error."""
def __init__(self, message: str, item_id: str):
super().__init__(message)
self.item_id = item_id
よく使われるパターン
データ構造には dataclass を使用する
シンプルなデータコンテナーには dataclasses を使用します:
from dataclasses import dataclass, field
@dataclass
class User:
"""User profile information."""
username: str
email: str
age: int
tags: list[str] = field(default_factory=list)
is_active: bool = True
def __post_init__(self):
"""Validate fields after initialization."""
if self.age < 0:
raise ValueError("Age cannot be negative")
固定された値の集合には Enum を使用する
固定された値の集合には Enum を使用します:
from enum import Enum, auto
class Status(Enum):
"""Processing status values."""
PENDING = auto()
PROCESSING = auto()
COMPLETED = auto()
FAILED = auto()
# Usage
current_status = Status.PENDING
if current_status == Status.COMPLETED:
print("Done!")
ファイル操作には pathlib を使用する
os.path の代わりに pathlib.Path を使用します:
from pathlib import Path
# Good
data_dir = Path("/data")
file_path = data_dir / "input.txt"
if file_path.exists():
content = file_path.read_text()
# Avoid
import os
data_dir = "/data"
file_path = os.path.join(data_dir, "input.txt")
if os.path.exists(file_path):
with open(file_path) as f:
content = f.read()
リスト内包表記
明確さとパフォーマンスのために内包表記を使用します:
# Good
squared = [x**2 for x in range(10) if x % 2 == 0]
# Avoid
squared = []
for x in range(10):
if x % 2 == 0:
squared.append(x**2)
コードの整理
モジュールの構成
モジュールを明確なセクションに分けて整理します:
"""Module for data processing utilities.
This module provides functions for loading, transforming, and
validating data from various sources.
"""
# Standard library imports
import os
import sys
from pathlib import Path
# Third-party imports
import requests
import pandas as pd
# Local imports
from myapp.core import BaseProcessor
from myapp.utils import validate_input
# Constants
MAX_RETRIES = 3
DEFAULT_TIMEOUT = 30
# Exceptions
class ProcessingError(Exception):
"""Raised when processing fails."""
pass
# Functions
def load_data(source: str) -> pd.DataFrame:
"""Load data from source."""
pass
# Classes
class DataProcessor(BaseProcessor):
"""Process and validate data."""
pass
# Module initialization
if __name__ == "__main__":
# CLI entry point
main()
マジックナンバーを避ける
マジックナンバーの代わりに名前付き定数を使用します:
# Good
MAX_RETRIES = 3
TIMEOUT_SECONDS = 30
def fetch_data(url: str) -> dict:
for attempt in range(MAX_RETRIES):
response = requests.get(url, timeout=TIMEOUT_SECONDS)
if response.status_code == 200:
return response.json()
# Avoid
def fetch_data(url: str) -> dict:
for attempt in range(3): # What is 3?
response = requests.get(url, timeout=30) # Why 30?
if response.status_code == 200:
return response.json()
テスト
テストには pytest を使用する
import pytest
from myapp.processor import DataProcessor
def test_process_valid_data():
"""Test processing with valid input."""
processor = DataProcessor()
result = processor.process([1, 2, 3])
assert result == [2, 4, 6]
def test_process_empty_data():
"""Test processing with empty input."""
processor = DataProcessor()
with pytest.raises(ValueError):
processor.process([])
@pytest.fixture
def sample_data():
"""Provide sample data for tests."""
return [1, 2, 3, 4, 5]
def test_with_fixture(sample_data):
"""Test using fixture."""
processor = DataProcessor()
result = processor.process(sample_data)
assert len(result) == len(sample_data)
要点
- PEP 8 のスタイルガイドラインに一貫して従う
- 関数シグネチャには必ず型ヒントを使用する
- すべての公開関数/クラスに NumPy スタイルの docstring を記述する
- 裸の
exceptではなく、具体的な例外型を使用する os.pathよりpathlib.Pathを優先する- 構造化データには dataclass と enum を使用する
- インポートを標準ライブラリ → サードパーティ → ローカルの順に整理する
- マジックナンバーを避け、名前付き定数を使用する
- pytest を使ってテストを記述する
- 最新の Python 構文を使用する(3.9+)
コメント
Markdown 対応