リストやタプルの要素をインデックスやスライスを使って取り出して、変数に代入することはよくありますが、そのコード、もしかしたらもっとカンタンに記述できるかもしれませんよ?
以下は5個の要素を持つリストの先頭要素を変数firstに、次の要素を変数secondに、末尾の要素を変数lastに、他の要素を変数middleに代入するPythonコードである。このコードではインデックスとスライスを用いて4つの代入文でこの処理を記述しているが、これを1つの代入文として記述せよ。
mylist = [0, 1, 2, 3, 4]
first = mylist[0]
second = mylist[1]
middle = mylist[2:-1]
last = mylist[-1]
print(first, second, middle, last)
新年明けましておめでとうございます。「へなちょこPythonistaかわさき」略して「HPかわさき」です。本年もどうぞよろしくお願いいたします。
おかげさまでシャドウバン状態からは復活できたようです。が、新年が始まってもまだ何も投稿していないのは、われながらいかがなものかと思っています(2025年1月9日時点)。少しずつではありますが、Pythonクイズ以外のネタもいろいろと投稿しながら、フォロワーさんを増やしていくのが今年一番の目標です。
なんかねー、会議では「アイコンをもっとかわいい感じの絵柄にした方がいいんじゃない?」とか言われることもあるのですが、これでも十分(キモ)かわいいと思いませんか? かわいくない。そうですか……(ガックリ)。生成AIもたいしたもんじゃないな(そうじゃない)。
以下に正解のコード例を示します。
mylist = [0, 1, 2, 3, 4]
first, second, *middle, last = mylist
print(first, second, middle, last)
正解例のコードでは、シーケンス(タプルやリストなどのオブジェクト)のアンパック代入によって、4つの変数への代入を1つの代入文にまとめています。
リストやタプルなどのシーケンスはコンテナとも呼ばれます。つまり、リストやタプルは何か別のオブジェクトを中に格納するオブジェクトということです。そして、コンテナから要素を取り出すことを「アンパック」と呼びます。
典型的には関数に引数としてリストの個々の要素をバラバラに渡したいときにアンパックを行います。以下の例を見てください。
mylist = [0, 1, 2]
print(mylist) # [0, 1, 2]
print(*mylist) # 0 1 2
2つのprint関数呼び出しのうち、最初のものはリストをアンパックせずに渡しています。次のものはリストをアンパックして渡しています。後者では変数名の前にアスタリスク「*」が付いている点に注目してください。こうすることで、mylistの要素がシーケンス(コンテナ)からアンパックされて(取り出されて)個別の値としてprint関数に渡されているのです(つまり、これは「print(0, 1, 2)」という呼び出しと一緒です)。
このようなシーケンス(コンテナ)の要素を個別に取り出すことをPythonではアンパックと呼んでいます。そして、代入文でも右辺にシーケンスを置くことで、その要素をアンパックして、左辺に置いた複数の変数に代入できます(複数の変数はカンマで区切る)。これを「アンパック代入」と呼ぶことがあります。このとき、関数へ引数を渡したときのようにアスタリスクを付ける必要はありません。
ただし、アンパック代入するには次のような条件があります。
この条件に従ってアンパック代入の例を紹介します。まずは簡単な方から見てみましょう。
mylist = [0, 1, 2]
a, b, c = mylist # OK:変数の個数とリストの要素数が等しい
a, b = mylist # ValueError:変数の個数とリストの要素数が等しくない
先ほどもいったように、mylistの前にアスタリスクがないことに、まずは注意してください。そして、1つ目の代入文ではリストの要素数と変数の個数が等しいのでこれはうまく代入できます。しかし、2つ目の代入文では変数の個数とリストの要素数が等しくないので例外が発生します。
次に左辺に置いた変数の一つにアスタリスクが付いている場合についてです。このときには、アスタリスクが付いた変数はリストとなって、0個以上の要素をそこに格納できるようになります。シーケンスの要素数が変数の個数よりも1個少ないときには、その変数は空のリストになり、シーケンスの要素数が変数の個数よりも2つ以上多いときには、複数の要素を含むリストになります。これも例を見てみましょう。
mylist = [0, 1]
first, second, *last = mylist # 変数の個数は3、リストの要素数は2
print(first, second, last) # 0 1 []
mylist = [0, 1, 2, 3]
first, second, *last = mylist # 変数の個数は3、リストの要素数は4
print(first, second, last) # 0 1 [2, 3]
mylist = [0, 1]
first, second, third, *last = mylist # ValueError
まず、変数lastの前にアスタリスクが付いていることに注目してください。この場合、lastはリストとなり(代入元がタプルや集合でもリストになります)、0個以上の要素を格納できるようになります。
そして、最初の代入文では変数は3個、リストの要素は2個です。この場合は、2個の要素が変数firstとsecondに代入され、lastは空のリストになります。空のリストが許されるので、この場合の条件は先ほども述べたように、シーケンスの要素数が「左辺に置いた変数の数−1」個以上あることとなるわけです。
次の代入文では変数は3個、リストの要素は4個です。この場合はリストの最初の2つの要素が変数firstとsecondに代入され、lastには残りの2つがリストとして代入されます。最後の代入文では、変数が4個、リストの要素が2個になっています。この場合、アスタリスクが付いた変数lastは空のリストにできますが、その前にあるthirdには代入する要素がありません。というわけで、ValueError例外が発生します。
アスタリスク付きの変数は代入文の左辺に0個か1個だけ置けます。複数置くことはできないことは覚えておきましょう。また、上の例では最後の変数にアスタリスクを付けていましたが、変数のリストのどこにでも置けます。
mylist = [0, 1, 2, 3]
first, *second, last = mylist # 2つ目の変数secondにアスタリスクを前置
print(first, second, last) # 0 [1, 2] 3
この例では、2つ目の変数secondにアスタリスクが付いています。そのため、リストの先頭要素が変数firstに、最後の要素がlastに代入され、残る2つの要素がリストとしてsecondに代入されます。
ここまでくれば、正解のコードの意味も分かるでしょう。最初の4行の代入文は以下のようなものでした。
mylist = [0, 1, 2, 3, 4]
first = mylist[0]
second = mylist[1]
middle = mylist[2:-1]
last = mylist[-1]
print(first, second, middle, last)
4つの代入文が意味しているのは以下の通りです。
3つの変数first、second、lastにはそれぞれ特定位置の値が1つだけ代入されますが、変数middleには可変個の要素がスライスとして代入されます。そのため、これを1つの代入文として記述すると正解例のようになるわけです。
mylist = [0, 1, 2, 3, 4]
first, second, *middle, last = mylist
print(first, second, middle, last)
これを図にすると以下のようになります。
というわけで、アンパック代入を使うことで4行あった代入文を1行にまとめられるのでした。
タプル(やシーケンス)のアンパックについては『Python入門』の『第19回 タプル』の『タプルのパックとアンパック』でも取り上げています。
正直、今回の説明とどちらが詳しいかは微妙なので、「ぜひとも目を通してくださいね」とも言いづらいのですが(ちょっとミッチリと書き過ぎちゃったかなと思っています)、記事の最後にはPython入門や解決!Pythonへのリンクを付けようというのが、筆者の心の鉄則なのでリンクさせてもらいます。
それでは皆さま、Pythonクイズを今年もよろしくお願いいたします。
Copyright© Digital Advantage Corp. All Rights Reserved.