概要(staticmethodは「クラスに属するけれど、状態に依存しない純粋な関数」)
@staticmethodは、self(インスタンス)やcls(クラス)に触れない“補助関数”をクラスの中にまとめるための仕組みです。見た目はメソッドでも、内部ではただの関数に近い扱いで、インスタンスを作らずクラスから直接呼べます。重要なのは「インスタンスの状態やクラス設定に依存しない処理だけを置く」「classmethodとの線引きを明確にする」ことです。
基本構文と呼び方(selfもclsも受け取らない)
最小の定義と呼び出し
class StringUtil:
@staticmethod
def slugify(s: str) -> str:
return s.lower().replace(" ", "-")
# インスタンス化不要で呼べる
print(StringUtil.slugify("Hello World")) # "hello-world"
# インスタンスから呼んでもOK(意味は同じ)
u = StringUtil()
print(u.slugify("Hi There")) # "hi-there"
Python- 第1引数にself/clsを取りません。純粋に渡された引数だけで処理します。
- クラスからもインスタンスからも呼べますが、どちらでも動作は同じです。
classmethodとの違い(どちらを使うかの判断)
- staticmethod: クラス文脈もインスタンス文脈も不要。完全に独立した処理。
- classmethod: cls(クラス)を使って代替コンストラクタや共有設定の操作など“クラス単位の挙動”を作る。
設計の指針(いつstaticmethodにするか)
状態に触れないユーティリティを「関連するクラスの中へ」
クラスに論理的に属する処理(整形、バリデーション、フォーマットなど)を、そのクラスの中へ整理し、可読性と発見性を高めます。
class Price:
def __init__(self, amount: float, currency: str = "JPY"):
self.amount = amount
self.currency = currency
@staticmethod
def validate_amount(v: float) -> None:
if v < 0:
raise ValueError("金額は0以上")
@staticmethod
def format(amount: float, currency: str) -> str:
return f"{amount:.2f} {currency}"
Python「クラス文脈が必要になったらclassmethodへ」
将来、継承で系統別の動作(デフォルト値の切り替え、工場生成など)が必要になりそうなら、最初からclassmethodにしておくと拡張が楽です。staticではクラス属性の上書きに追随できません。
Web / APIでの実例(バリデーション・整形・署名計算)
入力バリデーションや整形を静的関数へ
class Params:
@staticmethod
def ensure_id(uid: str) -> str:
if not uid or not uid.strip():
raise ValueError("idは必須")
return uid.strip()
@staticmethod
def qs(params: dict[str, str]) -> str:
return "&".join(f"{k}={v}" for k, v in params.items())
Python- インスタンス状態に触れない“ユーティリティ”は静的にまとめると再利用が簡単。
署名生成やハッシュ計算(セキュアな補助関数)
import hmac, hashlib
class Signer:
@staticmethod
def sign(secret: bytes, message: bytes) -> str:
return hmac.new(secret, message, hashlib.sha256).hexdigest()
Python- クラスやインスタンスの状態に依存せず、純粋計算に徹する関数はstaticが最適。
APIレスポンスの軽い変換
class ResponseUtil:
@staticmethod
def pick_user(data: dict) -> dict:
return {"id": str(data.get("id", "")), "name": data.get("name") or "unknown"}
Python- 変換ロジックを“見つけやすい場所”へ集約し、UI層・サービス層から共有。
深掘り(可読性・テスト容易性・継承との相性)
可読性と発見性(関連機能をまとまりとして見せる)
モジュールのグローバル関数に散らばるより、「このクラスの周辺処理はここにある」とわかるのが利点です。IDEの補完でも見つけやすく、保守性を高めます。
テストが簡単(依存が少ない純粋関数)
状態に依存せず副作用が小さいため、単体テストが容易です。入力と出力だけをアサートすればよく、モックも最小で済みます。
継承したときの振る舞い
staticはclsに依らないため、系統で切り替えたい設計には向きません。継承後に“系統別デフォルト”を反映したいならclassmethodへ置く(またはインスタンスメソッドにして属性参照する)のが適切です。
ありがちな落とし穴と回避(誤用・責務過多・隠れ依存)
インスタンス状態に触れるのにstaticを選ぶ
selfを使わずに外からグローバル参照してしまう“隠れ依存”は危険です。状態が必要ならインスタンスメソッドにし、clsが必要ならclassmethodにします。
なんでもstaticで詰め込み、巨大クラス化
バリデーション・整形・計算を一つのクラスに詰め込みすぎると責務が膨張します。役割で分け、モジュール関数に出す選択肢も持ちましょう。
将来の拡張を阻害
「今は固定値で足りる」からstaticにしたが、後で系統別の設定が必要になることがあります。拡張可能性が見えているときはclassmethodへ寄せるのが安全です。
例題(APIクライアント+静的ユーティリティの組み合わせ)
クライアントは状態を持ち、ユーティリティはstaticで共有
import requests, hmac, hashlib
class Util:
@staticmethod
def mask(token: str) -> str:
return token[:4] + "..." + token[-4:] if token and len(token) > 8 else "***"
@staticmethod
def sign(secret: bytes, message: bytes) -> str:
return hmac.new(secret, message, hashlib.sha256).hexdigest()
@staticmethod
def normalize_user(data: dict) -> dict:
return {"id": str(data.get("id", "")), "name": data.get("name") or "unknown"}
class ApiClient:
def __init__(self, base_url: str, api_key: str, timeout: float = 5.0):
if not base_url.startswith("https://"):
raise ValueError("HTTPSのみ許可")
self.base_url = base_url
self.timeout = timeout
self.session = requests.Session()
self.session.headers.update({"Authorization": f"Bearer {api_key}"})
def get_user(self, uid: str) -> dict:
r = self.session.get(f"{self.base_url}/users/{uid}", timeout=self.timeout)
r.raise_for_status()
return Util.normalize_user(r.json())
Python- 状態のあるクライアントと、状態に依存しないユーティリティを分けると、見通しとテストが良くなります。
まとめ(「状態ゼロの補助」はstaticmethod。状態や文脈が要るなら他へ)
@staticmethodは、クラスに論理的に属する“状態に依存しない処理”をまとめるための道具です。self/clsを使わない純粋関数だけを置き、バリデーション・整形・ハッシュ計算などを整理する。クラス文脈が要るならclassmethod、個体の状態が要るならインスタンスメソッドへ。責務を過不足なく分ければ、初心者でも「読みやすく、テストしやすく、拡張しやすい」OOP設計が自然に書けます。
