Chapter 06無料公開

【アンパック】要素をスマートに取り出す

ごめ茶🍵
ごめ茶🍵
2020.10.17に更新

Pythonにはアンパックという概念があります。
unpackという英単語には「解いて中身を出す、包みから取り出す」という意味があります。

リストやタプルに各要素が入っている状態のことを「要素がパッケージされている(包まれている)状態」と考え、その1つ1つの要素を取り出して変数などに代入することをアンパックといいます。

>>> nums = [1, 2, 3]

# リストにパックされている各要素(1, 2, 3)がアンパックされて変数a, b, c に代入される
>>> a, b, c = nums
>>> a
1
>>> b
2
>>> c
3

# ↓冗長な書き方
a = nums[0]
b = nums[1]
c = nums[2]

上記は、アンパック処理の中でも最も基本となる例です。
複数の変数名をカンマ区切りにすることで、リストの各要素がアンパックされて1つずつ変数に代入されます。

この時、変数の個数とリストの要素数は同じ数である必要があります

# 3つの変数に5つの要素を代入しようとするとエラー
>>> a, b, c = [1, 2, 3, 4, 5]
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 3) → 「アンパックするには要素が多すぎる(要素の数は3つの想定)」

可変長のアンパック

リストの長さが可変の時(要素の数が定まっていない時)はアスタリスクを使うことで変数代入ができます。

>>> a, b, *c = [1, 2, 3, 4, 5]
>>> a
1
>>> b
2
>>> c
[3, 4, 5]

上記例の場合、1つ目と2つ目の要素はそれぞれa, bに代入されて残りの要素がリストとしてcに代入されます。

最初や真ん中の変数にもアスタリスクをつけることができます。

>>> a, *b, c = [1, 2, 3, 4, 5]
>>> a
1
>>> b
[2, 3, 4]
>>> c
5

可変長変数に対応する値がない時、変数の中身は空のリストになります。

>>> a, b, *c = [1, 2]
>>> a
1
>>> b
2
>>> c
[]

ここまでの例では右辺にリストを使っていましたが、タプルでも同様の挙動になります。

>>> a, b, *c = (1, 2, 3, 4, 5)
>>> a
1
>>> b
2
>>> c
[3, 4, 5]

さらに、タプルの括弧は省略可能なので以下のように書いた方がよりシンプルになります。

>>> a, b, *c = 1, 2, 3, 4, 5
>>> a
1
>>> b
2
>>> c
[3, 4, 5]

関数の引数にアンパックした値を渡す

以下のように、関数の引数としてリストを渡す際にアスタリスクをつけるとアンパックされます。

names = ["加藤", "羽生", "藤井"]

def print_names(first, second, third):
    print("1番目: ", first)
    print("2番目: ", second)
    print("3番目: ", third)

# namesにアスタリスクをつけて要素をアンパックする
print_names(*names)

# 出力結果
1番目:  加藤
2番目:  羽生
3番目:  藤井

上記例で、もしもアスタリスクを付けなかった場合は、3つの引数をとる関数(print_names関数)に1つの値(namesリスト)渡すことになるのでエラーとなります。

names = ["加藤", "羽生", "藤井"]

def print_names(first, second, third):
    print("1番目: ", first)
    print("2番目: ", second)
    print("3番目: ", third)

# アスタリスクを付けなかった場合
print_names(names)

# 出力結果
    print_names(names)
TypeError: print_names() missing 2 required positional arguments: 'second' and 'third'

辞書を展開(アンパック)して引数に渡すこともできます。辞書を展開する場合は、アスタリスクを2つ付けます。これで、キー名と引数名が対応して値が渡されるようになります。

profile = {
    "name": "藤井",
    "age": 18,
    "birthplace": "愛知県"
}

def print_profile(birthplace, age, name):
    print("出身地: ", birthplace)
    print("年齢: ", age)
    print("名前: ", name)
   
# アスタリスクを2つ付けて辞書を展開
print_profile(**profile)

# 出力結果
出身地:  愛知県
年齢:  18
名前:  藤井