Python 3.8以降では、代入式という式が使える。代入式では「変数 := 式」のように「:=」演算子を使って、式の値を変数に代入する。式なので、これを実行すると代入された値がその式の値となる。
if文の条件に書けるのは式だ。つまり、それを評価することで真偽のいずれかの値を求めることができる要素しか、if文の条件には書けない。そして、代入文は変数に値を代入する「文」であり、式ではない。そのため、代入文をif文の条件に書くことはできない。代入式を使えば、if文の条件に何かの値を代入しながら、その値を基に処理を分岐できる。
簡単な例を以下に示す。この例では文字列の長さを基に表示するメッセージを変更しようというものだ。
s = 'foobarbaz'
if len(s) >= 5:
print(f'文字列{s}の長さは{len(s)}で、5以上です')
else:
print(f'文字列{s}の長さは{len(s)}で、5未満です')
ここではlen関数を使って何度も文字列の長さを調べていることに注目しよう。関数を呼び出した場合、その処理が終わって値が返されるまでにどのくらいの時間がかかるかは分からない(この場合のlen関数ならそれほどの時間がかからないのは分かってはいるが)。そのため、同じ結果を返す関数を何度も呼び出すよりはその結果を変数に保存しておくのが一般的だ。このことを考えると上のコードは次のようになる。
s = 'foobarbaz'
length = len(s)
if length >= 5:
print(f'文字列{s}の長さは{length}で、5以上です')
else:
print(f'文字列{s}の長さは{length}で、5未満です')
変数lengthにlen関数の呼び出し結果を代入することで、同じ関数を何度も呼び出す必要がなくなった。だが、コードが1行だけとはいえ長くなった。代入式を使うと1行増えた代入文をif文の条件に書ける。
s = 'foobarbaz'
if (length := len(s)) >= 5:
print(f'文字列{s}の長さは{length}で、5以上です')
else:
print(f'文字列{s}の長さは{length}で、5未満です')
if文の条件に指定した値を、その後のブロックでも何度も使いたいといった場合には、代入式を使うことで(わずかではあるが)コードを簡潔に記述できるだろう。
実行結果を以下に示す。
ただし、「(length := len(s))」とlen関数の戻り値を変数lengthに代入する部分をかっこ「()」で囲んでいる点には注意しよう。実は「:=」演算子は「>=」演算子よりも優先順位が低い。つまり、「if length := len(s) >= 5:」のように書くと、変数lengthには「len(s) >= 5」の結果、つまりTrueかFalseのどちらかが代入されてしまう。
このかっこを省略して実行すると、次のようになる。
「長さがTrueで」とおかしなことになっている点に注目されたい。
Python 3.10ではmatch文という構文が追加された。これは「構造的パターンマッチ」と呼ばれる処理をPythonで実現するものだ。だが、条件によって、処理を分岐するという観点からはif〜elif〜else文の兄弟といえなくもない。ただし、その考え方はif文とはかなり異なる。
ここではmatch文について簡単に紹介するにとどめる。詳細は「Python 3.10の新機能:『構造的パターンマッチ』とは」などを参照されたい。
match文の構文を以下に示す。
match 調べたい値:
case パターン1:
pass # 条件の値がパターン1にマッチした場合に行う処理
case パターン2:
pass # 条件の値がパターン2にマッチした場合に行う処理
# 省略
case パターンn:
pass # 条件の値がパターンnにマッチした場合に行う処理
case _:
pass # 全てのパターンにマッチしなかった場合に行う処理
「match」に続けて、調べたい値を記述する。ここには変数や式、関数呼び出しなどを置ける。caseブロックにはパターンを記述する。「調べたい値」が各caseブロックに書いたパターンにマッチすれば、その節にコードが実行される。マッチしなければ、調べたい値が次のパターンにマッチするかを試す。全てのパターンにマッチしなければ、match文では何かの処理が行われることはない。ただし、最後にある「case _:」ブロックの「_」というパターンは「ワイルドカードパターン」と呼ばれ、常にマッチが成功する。if文におけるelse節と考えてもよい。
例として、変数の値が偶数か奇数かで処理を振り分けることを考えてみよう。既に見たようにif文ならこれは次のように書ける。
number = 7
if number % 2 == 0:
print('even')
else:
print('odd')
match文で同じように書いてみよう。
number = 7
match number:
case number % 2 == 0:
print('even')
case _:
print('odd')
実はこのコードはエラーとなる。
match文のcaseブロックにはこのような複雑な式は書けない。ではどうすればよいかというと、調べたい値の方で余りを求めて、それを0と比較すればよい。
number = 7
match number % 2:
case 0:
print('even')
case _:
print('odd')
実行結果を以下に示す。
caseブロックにはさまざまなパターンを書けるが、「case 0:」のように値をそのまま記述するものを「リテラルパターン」と呼ぶ。リテラルパターンでは「調べたい値 == リテラル値」となるときにマッチが成功して、そのブロックのコードが実行される。
今度はFizzBuzz問題をmatch文を使って記述してみよう。if文を使った場合は次のようなコードになることは既にお分かりだろう。
number = 27
if number % 15 == 0:
print('FizzBuzz')
elif number % 3 == 0:
print('Fizz')
elif number % 5 == 0:
print('Buzz')
else:
print(number)
その一方で、上のmatch文の例からif節やelif節で書いている「number % 15 == 0」などをmatch文のcaseブロックには書けないことも分かっている。ではどうすればよいかというと、次のようなコードが考えられる。
number = 27
match number % 3, number % 5:
case 0, 0:
print('FizzBuzz')
case 0, _:
print('Fizz')
case _, 0:
print('Buzz')
case _:
print(number)
matchに続けて調べたい値として「number % 3」「number % 5」の2つをカンマで並べ、caseブロックでは3で割った余りと5で割った余りの組がどうなっているかをやはりカンマ区切りで並べて、2つの条件がマッチするかどうかを調べていると考えられる。ここで使用している「_」はやはり全てにマッチするという意味だ。
例えば変数numberの値が27であれば、3で割った余りは0、5で割った余りは2となる。つまり、余りの組は「0, 2」である。これは一番上のパターンである「0, 0」とはマッチしないが、全てにマッチすることを意味する「_」を使った「0, _」にはマッチする。よって、このパターンにマッチすれば3の倍数であることが分かる。5の倍数のときも同様だ。そして、一番上の「0, 0」というパターンにマッチするのは15の倍数だけである。
調べたい値を、対応するブロックで使えるよう、変数に代入するようなパターンも書ける。先ほどのコードを少し変えてみよう。
number = 27
match number % 3, number % 5:
case 0, 0:
print('FizzBuzz, rests are 0 and 0')
case 0, n:
print(f'Fizz, rests are 0 and {n}')
case n, 0:
print(f'Buzz, rests are {n} and 0')
case n, m:
print(f'{number}, rests are {n} and {m}')
「_」ではなく「n」「m」と変数を並べているのとf文字列にそれらの値を埋め込んでいるのが、上のコードとは違うところだ。先ほどと同じく変数numberの値が27とすると、余りの組は「0, 2」である。これは「case 0, n:」とマッチするが、マッチしたときに変数nには対応する値である「2」が代入されるのだ(このようなパタンーンを「キャプチャーパターン」と呼ぶ)。そして、対応するブロックではこれを5で割った余りとして画面に出力するようにしている。
27を5で割った余りである2がちゃんと表示されていることに注目されたい。
このようにif文とは少し頭の使い方が違ってくるかもしれないが、調べたい値とパターンがうまくマッチするような書き方をすることで便利に使えるはずだ。
今回は制御構造とは何かについて簡単に見た後に、Pythonのif文とif文に書く条件について見た。次回は、繰り返し処理を行うための制御構造であるfor文とwhile文を取り上げる。
「Python入門」
Copyright© Digital Advantage Corp. All Rights Reserved.