カテゴリー

Python基礎

【python&セイウチ演算子】:=で代入式を短く

2021年12月25日

こんな人にオススメ

python3.8から導入されたセイウチ演算子:=(Walrus operator)ってどういう意味なの?

どんなときに便利になる?

ということで、今回はpython3.8以降で使うことができるセイウチ演算子:=(Walrus operator)について解説する。その名の由来は演算子の:=が横向きのセイウチの顔に似ているから。可愛い。

セイウチ演算子はこれでしかできない処理というわけではなく、長くなるコードを短くすることができるワザのようなもの。

個人的には慣れていないということもあると思うけど、使い所が難しいと感じた。ただ、使いこなせるとかなりコードを短く書けると思う。

python環境は以下。

  • Python 3.10.1
スポンサーリンク
スポンサーリンク

運営者のメガネと申します。TwitterInstagramも運営中。

自己紹介はこちらから、お問い合わせはこちらからお願いいたします。

運営者メガネ

作成したコード全文

下準備

import re

まずは下準備としてのimport関連。今回は標準ライブラリであるreだけ。reについてはセイウチ演算子の説明上は必要ない。最後に応用的な感じで使うだけ。

セイウチ演算子は定義と代入と使用が同時に行える

# valが1かどうかの判定
val = 1
if val == 1:
    print('True!')
    print(val)
# True!
# 1

そもそもセイウチ演算子は変数の定義と値の代入と変数の使用を同時に行える文法のこと。例えば上の例だと変数valを定義し、val1を代入している。

次にifval1か否かの判断をして、True、すなわちval=1ならprint文で出力するというもの。この場合、定義と代入、値の使用という2STEP必要。

一方でセイウチ演算子を使用すると値の定義と代入と使用が1行で可能になる。val := 1で定義と代入を、== 1ifの判定を行なっている。

# セイウチ演算子を使うと定義と判定を1行で行える
if (val := 1) == 1:
    print('True!')
    print(val)
# True!
# 1

このようにセイウチ演算子は代入までというステップと、その変数の使用を一気に行うことができる演算子。

いちいち変数を定義しなくてもいいので、ちょっとスッキリ書ける。

セイウチ演算子を使うときはかっこに気をつける

# カッコをつけないと先に1 == 1の判定が行われて、その結果がvalに代入される
if val := 1 == 1:
    print('True!')
    print(val)
# True!
# True

で、セイウチ演算子を使うときに気をつけないといけないことは、カッコを使わないと意味が変わるということ。

例えば上の例だとカッコを外して処理しているが、ifの中のprint(val)の結果が1ではなくTrueになっている。

これは計算の優先順位が1 == 1→val := Trueとなっているから。val1を代入してからifの判定をしたいなら(val := 1)とカッコを使う必要がある。

なお、この例は実質、以下と同じ意味になる。

# 意味合いとしては以下と同じ
if val := (1 == 1):
    print('True!')
    print(val)
# True!
# True

通常の代入文としては使用できないこともない

val := 1
#     val := 1
#         ^^
# SyntaxError: invalid syntax

セイウチ演算子は単独で使うことはできない。上のように単独使用すると文法エラーになる。

もちろんセイウチ演算子に必要な:を消したら通常の代入になるから使用可能。

# 当たり前だが、:をなくせば代入できる
val = 1
print(val)
# 1

ただ、式全体をカッコでくくることで、通常の代入文のように使用することが可能。しかし、この書き方だと誤解を招きやすかったりシンプルに回りくどい書き方なのでおすすめしない。

また、PEP572でも以下のように非推奨として書かれている。

Unparenthesized assignment expressions are prohibited at the top level of an expression statement. Example:

y := f(x) # INVALID

(y := f(x)) # Valid, though not recommended

# カッコでくくると代入できるが、誤解されやすいかも
(val := 1)
print(val)
# 1

tupleで使用することも可能

# tupleでも代入できる
(val := 1.1, val2 := 2.1)
print(val, val2)
# 1.1 2.1

セイウチ演算子はtupleでくくることで並べて使用することも可能。

以下のように各セイウチ演算子をバラバラでカッコでくくっても代入することができる。もちろん、通常の代入文ではこのような代入の仕方はできない。

# それぞれのセイウチ演算子をカッコでくくっても可能
(val := 1.2), (val2 := 2.2)
print(val, val2)
# 1.2 2.2

# 通常の代入文だとこれはできない
(val = 1.2), (val2 = 2.2)
#     (val = 1.2), (val2 = 2.2)
#      ^^^^^^^^^
# SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?

ただ、わざわざセイウチ演算子を使わなくても通常の代入式で賄える。

# セイウチ演算子を使わなくても代入可能
val, val2 = 10, 20
print(val, val2)
# 10 20

# 右辺がtupleでも可能
val, val2 = (100, 200)
print(val, val2)
# 100 200

複数変数への代入には注意が必要

val = 0
# valに10, val2に2が入ると期待
(val, val2 := 10, 2)
# 実際にはval2 := 10が適用され、valはすでに定義されたval = 0が使われる
print(val, val2)
# 0 10

複数変数に一括で代入しようとするときは注意が必要。上のように書いたらvalには10, val2には2が代入されそうだけど、実際にはval210が代入されてvalは変わらない。

なので新たに変数val3を初めに定義しようとすると、そんな変数ないよってことでエラーとなる。

# val3を定義したいときに使うとエラーになる
(val3, val2 := 10, 2)
#     (val3, val2 := 10, 2)
# NameError: name 'val3' is not defined. Did you mean: 'val'?

while文で使うときは注意

# 初期値0のaを1足しながら5未満まで出力
a = 0
while a < 5:
    print(a)
    a += 1
# 0
# 1
# 2
# 3
# 4

while文でもセイウチ演算子は使える。上の例だと初期値0の変数aを、a < 5の条件下で1増やしながら値の出力を行なっている。

aをループごとに増やしている部分をセイウチ演算子で置き換えると以下のようになる。一応、変数はbに変更した。

注意点はwhileでの評価の時点でセイウチ演算子が適用されるので、初期値b=0が適用されず、いきなりb += 1が適用され、1スタートとなる。

# 最初の時点で1足されるから、出力は1からになる
b = 0
while (b := b + 1) < 5:
    print(b)
# 1
# 2
# 3
# 4

formatやf-stringなど文字列で使う

# 通常の書き方
a = 10
b = 20
# print内の要素ごとにスペースが入るから「 , 」と[,」の間にもスペースが入る
print('a =', a, ', b =', b, ', a + b =', a + b)
# a = 10 , b = 20 , a + b = 30

# セイウチ演算子の書き方
print('a =', a := 10, ', b =', b := 20, ', a + b =', a + b)
# a = 10 , b = 20 , a + b = 30

セイウチ演算子はprint内でも使用可能。上の例だと予め定義しておいた変数をprintで使っているが、セイウチ演算子を使うとprintの中で定義しつつ出力することができる。

printの中で,を使って要素を使っているから、1行が長くなっているがこんな書き方もできる。

formatでも使用可能

# 通常の書き方
a = 10
b = 20
print("a = {a}, b = {b}, a + b = {c}".format(a=a, b=b, c=a + b))
# a = 10, b = 20, a + b = 30

# セイウチ演算子の書き方
print("a = {}, b = {}, a + b = {}".format(a := 10, b := 20, a + b))
# a = 10, b = 20, a + b = 30

いちいち,で区切るのが面倒だし見通しが悪いというのなら、.formatを使用する手もある。この場合も同じように.formatのカッコ内でセイウチ演算子を使えばいい。

文字列部分と代入部分で分かれるから、多少は見通しが良くなるだろう。

f-stringで使用するときは注意

# 通常の書き方
a = 10
b = 20
print(f"a = {a}, b = {b}, a + b = {a + b}")
# a = 10, b = 20, a + b = 30

# そのまま書くと書式設定の文字空けになってしまう
# 5や15は何文字空けるかの指定になるので、実際の値はすでに定義した値が使われる
print(f"a = {a := 5}, b = {b := 15}, a + b = {a + b}")
# a =    10, b =              20, a + b = 30

と言っても.formatを使うと、後ろに付け足す形になるから1行が長くなってしまう。ならf-string(f文字)を使えばいいんだけど、注意が必要。

f-stringの場合は文字列中の{}に直接変数を入れるんだけど、セイウチ演算子の:を使うとフォーマットされてしまって、無駄に空白を入れることになる。

さらに、セイウチ演算子で代入できていないということは、新規の変数を定義すると、定義されていないということでエラーとなる。

# :=を使っても文字空けに使われるから、代入できていない
print(f"c = {c := 5}, d = {d := 15}, c + d = {c + d}")
#     print(f"c = {c := 5}, b = {b := 15}, c + b = {c + b}")
# NameError: name 'c' is not defined

ならどうするかというと、f-stringとセイウチ演算子を両立させたい場合は、セイウチ演算子の部分をカッコでくくればいい。

# セイウチ演算子が使いたかったらカッコを使う
print(f"a = {(a := 30)}, b = {(b := 50)}, a + b = {a + b}")
# a = 30, b = 50, a + b = 80

reを使った応用例

# この文字列から数字だけを抽出したい
data = '今日は10000歩、昨日は9000歩のウォーキング'

最後に標準ライブラリreを使った応用例を紹介する。上の文字列から正規表現を使って数値部分だけを抽出する。

もし数値がなくて抽出するものがない、空白の状態なら何も出力せず、数値があるなら出力するというコードを書くと例えば以下のようになる。

# 通常の書き方
# re.findallで数値部分だけを抽出
result = re.findall(r'\\d+', data)
if result:  # dataに数値が入っていればTrue
    print(result)
# ['10000', '9000']

この場合は一旦resultという変数で数値部分を取り出し、それをifで要素数0の空のlistではないことを判定してから出力している。

一旦resultという変数を使うのをやめる場合、以下のように書くことができるが、この場合はprintの際にもre.findallで数値部分の抽出を行う必要があり、かなり冗長。

# ifに直接代入するならdainyuusurunara2回書かないといけない
if re.findall(r'\\d+', data):
    print(re.findall(r'\\d+', data))
# ['10000', '9000']

一旦、変数に置きたくもないしprintするときにもスッキリ書きたいならセイウチ演算子を使うとどっちの願いも叶う。

以下のようにifの判定時にセイウチ演算子を使用してあげると、resultを定義して代入しつつif判定可能。printの際には代入したresultを使用することができる。

# セイウチ演算子の書き方
# スッキリする
if (result := re.findall(r'\\d+', data)):
    print(result)
# ['10000', '9000']

ただ、セイウチ演算子を使うとifの判定がごちゃつくのがネック。どれも一長一短。

使い所が難しい

今回はpython3.8以降で実装されたセイウチ演算子について解説した。確かに短く書ける部分あるが、短くする代わりに1行が長くなる。

個人的には1行が長くなると処理がややこしくなって理解しにくいような気がするので、使い勝手が悪いようにも感じる。

ただ、ifで簡単な判定をしつつ、代入もしたいときには使えるかもしれない。難しい。

関連記事

【python&初級】のlistとかforとかifとかまとめ

続きを見る

【python&csv読み込み】pythonを使ってcsvを読み込み

続きを見る

【python3&zip関数】python3系にてzipは一回使ったら消える

続きを見る

【python&フィッティング】polyfitとcurve_fitでfitting

続きを見る

関連コンテンツ

スポンサーリンク

Amazonのお買い物で損したない人へ

1回のチャージ金額通常会員プライム会員
¥90,000〜2.0%2.5%
¥40,000〜1.5%2.0%
¥20,000〜1.0%1.5%
¥5,000〜0.5%1.0%

Amazonギフト券にチャージすることでお得にお買い物できる。通常のAmazon会員なら最大2.0%、プライム会員なら2.5%還元なのでバカにならない。

ゲットしたポイントは通常のAmazonでのお買い物に使えるからお得だ。一度チャージしてしまえば、好きなタイミングでお買いものできる。

なお、有効期限は10年だから安心だ。いつでも気軽にAmazonでお買い物できる。

Amazonチャージはここから出来るで

もっとお得なAmazon Prime会員はこちらから

30日間無料登録

執筆者も便利に使わせてもらってる

スポンサーリンク

  • この記事を書いた人

メガネ

独学でpythonを学び天文学系の大学院を修了。 ガジェット好きでMac×Android使い。色んなスマホやイヤホンを購入したいけどお金がなさすぎて困窮中。 元々、人見知りで根暗だったけど、人生楽しもうと思って良い方向に狂ったために今も人生めちゃくちゃ楽しい。 pythonとガジェットをメインにブログを書いていますので、興味を持たれましたらちょこちょこ訪問してくだされば幸いです🥰。 自己紹介→変わって楽しいの繰り返し

-Python基礎
-,