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)
)

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

シェア

関連カテゴリー

Python pandas NumPy Pillow

Pythonで正規化・標準化(リスト、NumPy配列、pandas.DataFrame) pandas参考書『Pythonによるデータ分析入門』の注意点 『Pythonデータサイエンスハンドブック』は良書(NumPy, pandasほか) Python, OpenCV, Pillow(PIL)で画像サイズ(幅、高さ)を取得 pandas.DataFrame, SeriesとNumPy配列ndarrayを相互に変換 pandasからNumPyの関数などを使う方法(pd.np) pandas参考書『Python for Data Analysis, 2nd Edition』 Pythonでの画像処理、Pillow, NumPy, OpenCVの違いと使い分け Pythonのリストと配列とnumpy.ndarrayの違いと使い分け 『Python Data Science Handbook』(英語の無料オンライン版あり) pandasで欠損値NaNを除外(削除)・置換(穴埋め)・抽出 pandasで条件に応じて値を代入(where, mask) pandas.DataFrameをGroupByでグルーピングし統計量を算出 pandas.DataFrameの行を条件で抽出するquery pandasでカテゴリ変数をダミー変数に変換(get_dummies)

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