Pythonの代入は式じゃなくって文で行います。そこから生まれる(?)ちょっとした不思議な挙動について皆さんも考えてみませんか?
以下のコードを実行すると、なんと出力されるだろうか?
a = 'a'
b = a, c = 'bc'
print(a, b, c) # ?
さて今回は素直にChatGPT、Gemini、Claudeの3つのLLMがこの問題を解けるかどうかを試してみました。今回は全員が不正解! なので、皆さんも正解できなかったところで問題ナッシングですよ。
では、3つのLLMはどんなところを間違えたのでしょうか。解説が終わった後にお話ししましょう。
どうもHPかわさきです。
今回もあちこちらこちらを放浪しながら、ネタを探していて、自分でも気が付いていなかったことを取り上げました。Pythonの代入が式じゃなくって文なことは分かっていたんですがね……そんな風になるとは思いもしませんでした。そういうわけでお楽しみいただければ幸いです。
問題文のコードを実行すると「b bc c」と出力されます。
問題文のコードはPythonの連鎖代入(=が複数ある代入文)とアンパック代入が組み合わされたことで分かりにくくなっています。代入が実際にどういう順序で行われるかというと、「b = 'bc'」が実行された後に、「a, c = 'bc'」が実行されるのです。
なんで? って思いますよね? え? 思わない?
というわけで解説です。その前に問題文のコードを再掲します。
a = 'a'
b = a, c = 'bc'
print(a, b, c) # ?
1行目はいいですよね。変数aに文字列'a'が代入されるだけです。
問題は2行目です。「意味分からん!」ってなりそうです。自分の頭の中にあるPythonインタプリターなら例外を出したくなるところです。C言語などに慣れた方は、これは「a, c = 'bc'」が実行されてから「b = a, c」が実行されるんだろ? と思うかもしれませんね。Pythonでは実はそうじゃないんです。
そうじゃないんですが、取りあえず「a, c = 'bc'」について説明しておきましょう。これはいわゆるアンパック代入です。というのは、文字列は反復可能オブジェクトだからです。これがタプルならすぐに分かるはず。
a, c = 'b', 'c' # 'b', 'c'は('b', 'c')のかっこを省略したもの
文字列もまたタプルやリストと同じく反復可能オブジェクトなので、それを1文字ずつに分割して、(文字数と同じ数の)変数にアンパック代入できます。そのため、変数aには'b'が、変数cには'c'が代入されるというわけです。
では、「a, c = 'bc'」の次に「b = a, c」が行われるとしたら、変数bの値は「('b', 'c')」というタプルになりそうです。が、実際には変数bの値は'bc'になります。
「ホントかよぉ」と思う方のために、実行結果も貼っておきましょう。
なぜでしょう。それはPythonでは「b = a, c = 'bc'」という代入文があったときには、「b = 'bc'」が実行された後に、「a, c = 'bc'」が実行されるからです。
試してみますか? ということで、以下のコードを書いてみました。
import dis
def foo():
b = a, c = 'bc'
dis.dis(foo)
「b = a, c = 'bc'」を本体とする関数をdis.dis関数で逆アセンブルしてバイトコードを表示してみましょう。
このバイトコードの振る舞いをざっくりと説明すると次のようになります。
このように先に変数bに'bc'が代入されてから、変数aとcへアンパック代入されるのです。Pythonの代入文は「最右辺の値が一度評価された後、ターゲット(左辺)に個別に代入される」と考えるのが適切なようです。つまり、「b = a, c = 'bc'」は次のようなコードだと考えるのがよいでしょう。
# b = a, c = 'bc'
b = 'bc'
a, c = 'bc'
3つのLLMの全てが今回の問題を正解できなかったのですが、どんな感じだったかをまとめましょう。
代入が右結合だと考えていたのが主な原因でしょう。
なお、混乱の度合いが一番ひどかったのはGeminiさんでした。
筆者としては、うまく3つのLLMを出し抜けて楽しかったです。
Copyright© Digital Advantage Corp. All Rights Reserved.