Pythonでメソッドチェーンを改行して書く

Pythonのサードパーティライブラリの中には、pandasやNumPy、Pillow(PIL)のように、メソッドをつなげて順に処理(=メソッドチェーン)できるように設計されているものがあります。 なお、メソッドチェーンといってもメソッドの戻り値からそのままメソッドを呼ぶという処理を繰り返しているだけなので、特殊な文法というわけではない。 メソッドチェーンを使うと1行の文字数が長くなりがちだが、括弧を利用すると適宜改行できます。 ここではまずpandasを例に、

pandasにおけるメソッドチェーン 括弧内で改行 バックスラッシュを使う 全体を丸括弧で囲み改行

について説明し、その後で、

NumPyにおけるメソッドチェーン Pillow(PIL)におけるメソッドチェーン

について例を紹介します。 同じように括弧を利用して長い文字列をコード上で改行して書く方法もあります。

pandasにおけるメソッドチェーン

pandas.DataFrame, pandas.Seriesの多くのメソッドはpandas.DataFrame, pandas.Seriesを返すようになっており、メソッドをつなげて処理していくことができます。 メソッドチェーンを使わない場合、例えば以下のように書けます。 read_csv()でファイルを読み込み。

import pandas as pd

df = pd.read_csv('data/src/sample_pandas_normal.csv', index_col=0)

print(df)
#          age state  point
# name                     
# Alice     24    NY     64
# Bob       42    CA     92
# Charlie   18    CA     70
# Dave      68    TX     70
# Ellen     24    CA     88
# Frank     30    NY     57

このpandas.DataFrameに新たな列を追加し、不要な列を削除し、ソートし、先頭3行のみ抽出します。

df = df.assign(point_ratio=df['point'] / 100)
df = df.drop(columns='state')
df = df.sort_values('age')
df = df.head(3)

print(df)
#          age  point  point_ratio
# name                            
# Charlie   18     70         0.70
# Alice     24     64         0.64
# Ellen     24     88         0.88

同じ処理をメソッドをつなげて書くと以下のようになります。ここではあえて改行をしていません。

df_mc = pd.read_csv('data/src/sample_pandas_normal.csv', index_col=0).assign(point_ratio=df['point'] / 100).drop(columns='state').sort_values('age').head(3)

print(df_mc)
#          age  point  point_ratio
# name                            
# Charlie   18     70         0.70
# Alice     24     64         0.64
# Ellen     24     88         0.88

メソッドチェーンはシンプルに書けて便利ではあるが、よく理解していないメソッドをいきなり大量につなげると思わぬ結果になってしまうこともあります。慣れていない場合は無理せず一つずつメソッドを適用して結果を確認したほうが無難かもしれない。 また、エディタによっては2つ目以降のメソッドで補完が効かないなどのデメリットもあります。

括弧内で改行

Pythonでは括弧の中では自由に改行してよいので以下のように書けます。以降は結果の出力はすべて同じなので省略します。

df_mc_break = pd.read_csv(
    'data/src/sample_pandas_normal.csv',
    index_col=0
).assign(
    point_ratio=df['point'] / 100
).drop(
    columns='state'
).sort_values(
    'age'
).head(
    3
)

なお、自由に改行してよいといっても文字列リテラルの中で改行したりするとエラーになるので気をつけてください。

# df_mc_break = pd.read_csv(
#     'data/src/sample_
#     pandas_normal.csv',
#     index_col=0
# ).assign(
#     point_ratio=df['point'] / 100
# ).drop(
#     columns='state'
# ).sort_values(
#     'age'
# ).head(
#     3
# )
# SyntaxError: EOL while scanning string literal

もちろん改行しなくてもいいので、文字数が多いところのみ適当に改行するという形でも構いません。

dfdf_mc_break_mc = pd.read_csv(
    'data/src/sample_pandas_normal.csv', index_col=0
).assign(
    point_ratio=df['point'] / 100
).drop(columns='state').sort_values('age').head(3)

バックスラッシュを使う

Pythonにおいて、バックスラッシュ(\)は継続文字であり、行末におくとその後の改行が無視されて行が継続しているとみなされます。 これを利用すると以下のように書けます。

df_mc_break_backslash = pd.read_csv('data/src/sample_pandas_normal.csv', index_col=0) \
                        .assign(point_ratio=df['point'] / 100) \
                        .drop(columns='state') \
                        .sort_values('age') \
                        .head(3)

全体を丸括弧で囲み改行

括弧内では自由に改行できるといルールを利用して、全体を丸括弧()で囲むという方法もあります。

df_mc_break_parens = (
    pd.read_csv('data/src/sample_pandas_normal.csv', index_col=0)
    .assign(point_ratio=df['point'] / 100)
    .drop(columns='state')
    .sort_values('age')
    .head(3)
)

好みの問題ではあるが、余分な改行がないのでスッキリと見やすい。バックスラッシュを入力する手間もない。 この場合も改行するしないは自由なので、例えば以下のようにも書けます。

df_mc_break_parens = (pd.read_csv('data/src/sample_pandas_normal.csv', index_col=0)
                      .assign(point_ratio=df['point'] / 100)
                      .drop(columns='state')
                      .sort_values('age')
                      .head(3))

ドット(.)を行末に置いてもエラーにはならないが、メソッドチェーンであることが分かりにくくなるので避けたほうがよいだろう。

df_mc_break_parens = (
    pd.read_csv('data/src/sample_pandas_normal.csv', index_col=0).
    assign(point_ratio=df['point'] / 100).
    drop(columns='state').
    sort_values('age').
    head(3)
)

NumPyにおけるメソッドチェーン

NumPy配列ndarrayのメソッドの中にもndarrayを返すものがいくつかあります。 メソッドチェーンを使わない例を示します。

import numpy as np

a = np.arange(12)
a = a.reshape(3, 4)
a = a.clip(2, 9)

print(a)
# [[2 2 2 3]
#  [4 5 6 7]
#  [8 9 9 9]]

メソッドチェーンを使う例を示します。

a_mc = np.arange(12).reshape(3, 4).clip(2, 9)

print(a_mc)
# [[2 2 2 3]
#  [4 5 6 7]
#  [8 9 9 9]]

丸括弧で囲んで改行する場合です。

a_mc_break_parens = (
    np.arange(12)
    .reshape(3, 4)
    .clip(2, 9)
)

print(a_mc_break_parens)
# [[2 2 2 3]
#  [4 5 6 7]
#  [8 9 9 9]]

なお、NumPyではndarrayのメソッドではなくndarrayを引数とする関数として定義された処理も多い。pandasほどメソッドチェーンで何でもできるというわけでないので気をつけてください。

Pillow(PIL)におけるメソッドチェーン

画像処理ライブラリPillow(PIL)においては、画像を表すImage型のメソッドとして処理したImageを返すものがあります。 メソッドチェーンを使わない例を示します。画像ファイルを読み込んで様々な処理を行い、別名で保存している

from PIL import Image, ImageFilter

im = Image.open('data/src/lena_square.png')
im = im.convert('L')
im = im.rotate(90)
im = im.filter(ImageFilter.GaussianBlur())
im.save('data/temp/lena_square_pillow.jpg', quality=95)

メソッドチェーンを使う例を示します。

Image.open('data/src/lena_square.png').convert('L').rotate(90).filter(ImageFilter.GaussianBlur()).save('data/temp/lena_square_pillow.jpg', quality=95)

丸括弧で囲んで改行する場合です。

(
    Image.open('data/src/lena_square.png')
    .convert('L')
    .rotate(90)
    .filter(ImageFilter.GaussianBlur())
    .save('data/temp/lena_square_pillow.jpg', quality=95)
)

この例のように、読み込みから保存までまとめて行うと戻り値を変数に代入することなく完結するので、全体を丸括弧で囲むとちょっと奇妙に見えるかもしれない。お好みで。

Last Updated: 6/26/2019, 10:34:03 PM