そぬばこ

備忘録とか、多分そんな感じ。

Python のセイウチ演算子 (Walrus operator) ":=" と仲良くする

はじめに

セイウチ演算子 (Walrus operator) は Python 3.8 で登場した代入式を利用するための演算子です。

なんとなく最近使うことが多いので、自分のユースケースを軽くまとめます。

セイウチ演算子のイメージ

C言語 を利用したことがある人ならば、C99 での for 文のカウント変数の宣言のようなものだと思ってください。

これを

int i = 0;
for (i = 0; i < 10; i++) {
    ;
}

こうする話です。

for (int i = 0; i < 10; i++) {
    ;
}

Python のセイウチ演算子

詳しくは What's New In Python 3.8 の説明通りです。 記事執筆が2022年なので、既にリリースから3年もの月日が経過していますね。

例えば if 文内で評価した式を再利用している際に、評価対象を出力するための関数等の呼び出しの重複を防いだりすることが出来ます。

これを

if len(list_object) > 10:
    print(len(list_object))

こうする話です。

if (n := len(list_object)) > 10:
    print(n)

3.7 以前では n への代入を if 文の前に書けば重複の呼び出しは回避できますが、セイウチ演算子 := を使って if 文内に書くことができます。

なお、> 手前の () を外すと、先に > が評価されてしまい、 n には真理値が代入、 if は n を評価するため if 文としての処理は変わりませんが n の値が変わるため、気をつけなければなりません。

個人的ユースケース

What's New In Python 3.8 でのユースケースで事足りていますが、個人的に便利に使っているユースケースを書き残しています。

正規表現

これは、公式の例にもあるものです。 マッチオブジェクトを if 文の中で利用するため、一番使っているかもしれません。

if match := re.search(r"regex_pattern", text):
    # 正規表現にマッチした際の処理
    result_span = match.span()

環境変数の呼び出し

os.environ から値を取り出す際に dict.get() を利用する際に使えます。 直接環境変数os.environ["ENVVAR"] で取り出すと対応する環境変数に値が無い場合に KeyError になりますが、無い場合を許したい処理に工夫の余地があるのです。 dict.get() は key に対応する値が無い場合、デフォルトで None を返す*1仕様になっており、これを利用した if 文でセイウチ演算子が活躍します。

if envvar := os.environ.get("ENVVAR"):
    # 環境変数がある場合の処理
    print(envvar)

スライスによる取り出し

Python のスライスをリストのようなオブジェクトに与えた際、完全にインデックス外を指す場合に空のリストを返す性質があります。

a = [1, 2, 3]

print(a[0:2])  # [1, 2]
print(a[1:4])  # [2, 3] : 取れるところまで取る
print(a[3:4])  # [] : 空

Python はリストが空かどうかのチェックを if 文で書く際、リスト長の長さを取って判定するのではなく、そのままリストを評価させて空であれば False となることを利用してそのまま if list_object: とすることは有名です *2 が、スライスを利用した場合に空のリストを返すことを利用して、そのままセイウチ演算子を使う処理を書くことがあります。

if sliced_list := list_object[slice_object]:
    # スライスで取れた場合の処理
    print(sliced_list)

おわりに

他にも while 文や内包表記で活躍するセイウチ演算子ですが、非常に便利な反面、コードの可読性を損なう書き方になる場合も多くあります。 下手な乱用には気をつけなければなりません (自戒)。

*1:実際は default 引数に設定された値を返すが、その初期値が None

*2:PEP 8の Style Guide を参照