概要(メソッドチェーンは「操作をつなげて一気に書く」設計)
メソッドチェーンは、複数のメソッド呼び出しをドットで連ねて、処理の流れをそのまま一行で表現する書き方です。ポイントは“次のメソッドを呼べる値を返す”設計にすること。具体的には「自分自身(self)を返す」か「同じ型の新しいインスタンスを返す」ことで、obj.method1().method2().method3()のような連鎖が可能になります。
基本のパターン(return selfで“流暢な”操作をつなぐ)
最小例(数値を加工するクラス)
class NumberOps:
def __init__(self, v: float):
self.v = v
def add(self, x: float) -> "NumberOps":
self.v += x
return self # 連鎖のためにselfを返す
def mul(self, x: float) -> "NumberOps":
self.v *= x
return self
def result(self) -> float:
return self.v
n = NumberOps(10).add(5).mul(2).result() # 10 + 5 → * 2 → 30
print(n)
Python各メソッドがselfを返すと、その場で同じオブジェクトに積み重ねていけます。副作用(内部状態の変更)が前提の“フルエントインターフェース”の基本形です。
不変(イミュータブル)設計でのメソッドチェーン(新しいインスタンスを返す)
副作用なしで安全につなぐ
class Text:
def __init__(self, s: str):
self._s = s
def strip(self) -> "Text":
return Text(self._s.strip()) # 新インスタンスで返す
def lower(self) -> "Text":
return Text(self._s.lower())
def replace(self, a: str, b: str) -> "Text":
return Text(self._s.replace(a, b))
def value(self) -> str:
return self._s
t = Text(" Hello World ").strip().lower().replace(" ", "_").value()
print(t) # hello_world
Python各メソッドが“新しい同型のインスタンス”を返す設計は、原本が書き換わらず、並行処理や再利用に強いのが利点です。
実務での使いどころ(前処理、クエリ構築、ビルダー)
データ前処理のパイプラインを一行で見通す
class Users:
def __init__(self, rows: list[dict]):
self._rows = rows
def filter_active(self) -> "Users":
return Users([r for r in self._rows if r.get("active")])
def normalize(self) -> "Users":
return Users([{"id": str(r.get("id", "")), "name": (r.get("name") or "unknown").strip()}
for r in self._rows])
def take(self, n: int) -> "Users":
return Users(self._rows[:n])
def to_list(self) -> list[dict]:
return self._rows
data = Users([{"id": 1, "name": " Taro ", "active": True},
{"id": 2, "name": None, "active": False}]) \
.filter_active().normalize().take(1).to_list()
print(data) # [{'id': '1', 'name': 'Taro'}]
Python工程が左から右へ視覚的に流れるため、複雑な前処理も読みやすく保てます。
クエリビルダー/ビルダーパターンで段階的に構築
class Query:
def __init__(self):
self._parts = []
def select(self, *cols) -> "Query":
self._parts.append(("SELECT", cols)); return self
def where(self, cond: str) -> "Query":
self._parts.append(("WHERE", cond)); return self
def order_by(self, key: str) -> "Query":
self._parts.append(("ORDER", key)); return self
def build(self) -> str:
# 実装例は簡略化
sel = ", ".join(self._parts[0][1])
where = self._parts[1][1]
order = self._parts[2][1]
return f"SELECT {sel} FROM users WHERE {where} ORDER BY {order}"
sql = Query().select("id", "name").where("active=1").order_by("name").build()
print(sql)
Python“作る→条件→並び”のような組み立てを、自然な順序で表現できます。
重要ポイントの深掘り(チェーン継続条件、例外、可読性)
何を返せばチェーンが続くかを設計で固定する
チェーンを続けるには「次のメソッドが存在する型」を返す必要があります。副作用ありならself、不変設計なら同型の新インスタンス。途中で最終結果(数値や文字列など)を返した時点でチェーンは終わるので、戻り値の型を明確に設計しましょう。
例外の扱いは“早期・明確”
チェーン途中の検証に失敗したら、ただちにValueErrorなどを送出します。エラーを握りつぶしてNoneを返すと、その後のメソッド呼び出しで「NoneTypeにメソッドがない」失敗が起きやすく、原因が見えにくくなります。
可読性を最優先に(長すぎる一行は分割)
3〜5段程度までなら読みやすさが上がりますが、10段を超えるチェーンは保守が難しくなります。適宜、改行・コメント・中間変数で区切り、意図を明確に保ちましょう。
result = (
Text(" Hello World ")
.strip()
.lower()
.replace(" ", "_")
.value()
)
Pythonよくある落とし穴(Noneを返してチェーン断絶、副作用の過多、順序依存の罠)
Noneを返すメソッドを混ぜるとチェーンが壊れる
Pythonの慣習で「更新系メソッドはNoneを返す」ことがあります(例:list.sort())。チェーン設計にするなら、更新後のselfを返すか“不変版の別名”を用意して区別しましょう。
副作用だらけでテストが難しくなる
チェーンの各段で外部I/Oや重い処理を行うと、失敗時の切り分けが困難になります。チェーンは変換・検証など“軽い処理”を中心にし、I/Oは明示メソッドに分離するのが安全です。
呼び出し順の意味が強すぎる
同じメソッドでも順序で結果が変わる設計は、利用者がミスしやすいです。できるだけ順序に依存しないようにするか、依存するならドキュメントで強調し、分岐を減らす工夫を入れましょう。
例題(テキストクレンザーとHTTPリクエストビルダー)
テキストクレンザー(不変チェーンで安全に整形)
class CleanText:
def __init__(self, s: str): self._s = s
def trim(self) -> "CleanText": return CleanText(self._s.strip())
def ascii(self) -> "CleanText": return CleanText(self._s.encode("ascii", "ignore").decode())
def to_snake(self) -> "CleanText": return CleanText(self._s.lower().replace(" ", "_"))
def value(self) -> str: return self._s
res = CleanText(" Café Latte ").trim().ascii().to_snake().value()
print(res) # cafe_latte
PythonHTTPリクエストビルダー(段階的に構築して最後に送る)
class RequestBuilder:
def __init__(self):
self._url = ""
self._params = {}
self._headers = {}
def url(self, u: str) -> "RequestBuilder":
self._url = u; return self
def param(self, k: str, v: str) -> "RequestBuilder":
self._params[k] = v; return self
def header(self, k: str, v: str) -> "RequestBuilder":
self._headers[k] = v; return self
def send(self) -> dict:
import requests
r = requests.get(self._url, params=self._params, headers=self._headers, timeout=5.0)
r.raise_for_status()
return r.json()
data = RequestBuilder().url("https://api.example.com/users").param("page", "1").header("Authorization", "Bearer TOKEN").send()
print(data)
Python“設定→送信”の流れがそのままチェーンに表現でき、読みやすく拡張しやすい形になります。
まとめ(メソッドチェーンは「返す型」をデザインするだけで劇的に効く)
メソッドチェーンは、各メソッドがselfか同型インスタンスを返す設計にすることで、処理の流れを直感的に表現できます。副作用ありのフルエントならreturn self、不変なら新インスタンスを返す。途中で結果型に切り替えた瞬間にチェーンは終わるため、戻り値の型を明確に。None返却の混入や過度な副作用、順序依存には注意し、必要なら改行や中間変数で可読性を担保する。これを徹底すれば、初心者でも“読みやすく、差し替えに強く、テストしやすい”設計を自然に書けます。
