はじめに
データ処理用のプログラム開発時など、実装が複雑化してくると非常に長いメソッドチェーンの記載が必要となる場合があります。この際、特段の設定を行わずフォーマッタにかけると、改行によって逆に可読性が下がる場合があります。
OSS上でのRuffのメソッドチェーンに関する現行の議論を踏まえて、暫定版のRuffの設定を作成しました。
※今回の設定にはプレビュー版の項目が含まれています。今後の議論や仕様変更にも注視が必要です(2026年8月14日時点)。
コンセプト
メソッドチェーンを処理単位で縦に並べ、コードの可読性を高めることを目指します。
実装
Ruffの設定ファイルは次のとおりです。GitHubでも公開しています。
https://github.com/MySweetEden/polars-format
target-version = "py310"
line-length = 100
indent-width = 4
[lint]
select = ["I"]
[lint.isort]
# Ruff format accepts one or two blank lines after imports. The isort fix makes
# the preset deterministic by requiring two.
lines-after-imports = 2
[format]
quote-style = "double"
indent-style = "space"
line-ending = "lf"
skip-magic-trailing-comma = false
docstring-code-format = true
docstring-code-line-length = "dynamic"
# Required for Ruff's fluent-chain style (the value before the first dot is
# placed on its own line). Preview formatting may change between Ruff releases,
# so tests pin the Ruff version.
preview = true
検証
Polarsでのデータ処理プログラムについて、今回作成したruff.tomlを適用してフォーマットした結果、適用せずフォーマットした結果を載せています。
適用してフォーマットした結果の方が若干可読性が高いと信じています。
import polars as pl
result = (
df.filter(pl.col("status") == "active")
.with_columns((pl.col("price") * pl.col("quantity")).alias("total"))
.select("customer_id", "total")
)
summary = (
df.group_by("region")
.agg(
pl.col("total").sum().alias("revenue"),
pl.col("customer_id").n_unique().alias("customers"),
)
.sort("revenue", descending=True)
)
customer_orders = (
orders.join(customers, on="customer_id", how="left")
.filter(pl.col("total") > 0)
.select("order_id", "customer_id", "name", "total")
)
daily_revenue = (
orders.lazy()
.with_columns(
(pl.col("price") * pl.col("quantity")).alias("total"),
pl.col("ordered_at").dt.date().alias("order_date"),
)
.group_by("order_date")
.agg(pl.col("total").sum().alias("revenue"))
.sort("order_date")
.collect()
)
import polars as pl
result = (
df
.filter(pl.col("status") == "active")
.with_columns((pl.col("price") * pl.col("quantity")).alias("total"))
.select("customer_id", "total")
)
summary = (
df
.group_by("region")
.agg(
pl.col("total").sum().alias("revenue"),
pl.col("customer_id").n_unique().alias("customers"),
)
.sort("revenue", descending=True)
)
customer_orders = (
orders
.join(customers, on="customer_id", how="left")
.filter(pl.col("total") > 0)
.select("order_id", "customer_id", "name", "total")
)
daily_revenue = (
orders
.lazy()
.with_columns(
(pl.col("price") * pl.col("quantity")).alias("total"),
pl.col("ordered_at").dt.date().alias("order_date"),
)
.group_by("order_date")
.agg(pl.col("total").sum().alias("revenue"))
.sort("order_date")
.collect()
)
補足
Ruffにおけるメソッドチェーンのフォーマットについては、以下のIssueやPull Requestで議論されています(2026年8月14日時点)。
関連リンク:
- https://github.com/astral-sh/ruff/issues/8598
- https://github.com/astral-sh/ruff/issues/9577
- https://github.com/astral-sh/ruff/pull/21369
- https://github.com/astral-sh/ruff/discussions/22991
Issue #9577はPolarsのメソッドチェーンに関するもので、最終的には、より一般的なメソッドチェーンのフォーマットを扱うIssue #8598に統合されました。
その後、Pull Request #21369でfluent method-chain formattingが実装されました。一方で、pl.col(...)、pd.read_csv(...)、np.arange(...)のようなnamespace-style callまで分割される点については、現在も議論が続いています。