初めてのPython実践試験学習 第26回「リスト・タプル・辞書から値を取り出すアンパック」

こんにちは、吉政創成 菱沼です。

今回もPythonエンジニア育成推進協会のPython 3 エンジニア認定実践試験の主教材「Python実践レシピ/技術評論社」を使って学習中です。

ここ数回ほど引数について学んできました。

今回からChapter3.4アンパックを学びます。書籍のP.60~62です。

アンパックとは何か

リストやタプル、辞書などの中身を取り出して、複数の変数に代入できるようにすることをアンパックというそうです。unpack=開梱する→箱から中身を取り出す、とイメージしておくと覚えやすいですね。

では具体的にどうやるのか。まずは書籍のサンプルコードを実行して、基本の動きをみてみます。

P.60

タプル

tp = (1, 2, 3)
a, b, c = tp
print(f'{a}, {b}, {c}')
1, 2, 3

# タプルは()が省略可能なためこのような書き方もできる
d, e, f = 4, 5, 6
print(f'{d}, {e}, {f}')
4, 5, 6

リスト

lt= [1, 2, 3]
a, b, c = lt
print(f'{a}, {b}, {c}')
1, 2, 3

辞書

my_dict = {'a': 1, 'b': 2, 'c': 3}
# 辞書のキーをアンパック
key1, key2, key3 = my_dict
print(f'{key1}, {key2}, {key3}')
a, b, c

# 辞書の値をアンパック
v1, v2, v3 = my_dict.values()
print(f'{v1}, {v2}, {v3}')
1, 2, 3

タプルやリスト、辞書から取り出した値を入れるための変数を「,(カンマ)」区切りで書くことで、それぞれの変数に各要素が代入されるようです。

また、この変数と要素の数が一致しないとエラーになります。それがこちら。

エラーになるとき

# 変数の数が一致しないとエラー①(書籍サンプル)
a, b = 1
Traceback (most recent call last):
  File "<pyshell#22>", line 1, in <module>
    a, b = 1
TypeError: cannot unpack non-iterable int object

# 変数の数が一致しないとエラー②(書籍サンプル)
a, b = 1, 2, 3
Traceback (most recent call last):
  File "<pyshell#24>", line 1, in <module>
    a, b = 1, 2, 3
ValueError: too many values to unpack (expected 2)

一応リストで試してみました。

lt= [1, 2, 3]
a, b = lt
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    a, b = lt
ValueError: too many values to unpack (expected 2)

要素が3つに対して、用意した変数が2つなのでエラーが出ています。

ネストしたタプルとリストのアンパックをやってみよう

入れ子になっている状態のタプルとリストもアンパックできるそうです。

まずは書籍にあるタプルのアンパックのサンプルコードを実行して確認してみます。

tp2 = (0, 1, (2, 3, 4))
a, b, c = tp2
print(f'{a}, {b}, {c}')
0, 1, (2, 3, 4)

a, b, (c, d, e) = tp2
print(f'{a}, {b}, {c}, {d}, {e}')
0, 1, 2, 3, 4

練習のために、今度はネストしたリストからのアンパックをやってみました。

isono = ["namihei", "fune", "sazae", "katsuo", "wakame", ["masuo", "tarao"]]
a, b, c, d, e, f = isono
print(f'{a}, {b}, {c}, {d}, {e}, {f}')
namihei, fune, sazae, katsuo, wakame, ['masuo', 'tarao']

中に入っているリストはバラされずにそのままリストとして取り出されています。

基本的にアンパックは1段階しか展開してくれないそうなので、もしこの中に入っている方のリストもアンパックしたいのであれば、代入先の変数もネストして書いてあげる必要があるそうです。

a, b, c, d, e, [f, g] = isono
print(f'{a}, {b}, {c}, {d}, {e}, {f}, {g}')
namihei, fune, sazae, katsuo, wakame, masuo, tarao

ちなみにこの [f, g] を、f, gと書くとエラーになってしまうので、ちゃんとリストで書いてあげないといけません。

a, b, c, d, e, f, g = isono
Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    a, b, c, d, e, f, g = isono
ValueError: not enough values to unpack (expected 7, got 6)

要素数がわからないときのアンパックはアスタリスクを使う

先ほど、変数と要素の数が一致していないとエラーになるという例を確認しました。

ただ、プログラムを実行している間、リストの要素数が変動して、事前にいくつあるのかがわからないケースがあるかと思います。

そういった時にどうするかというと、変数名の前に「*(アスタリスク)」を付けることで、要素をまとめて*が付いている変数に代入させることができるそうです。

*を付ける変数の位置はどこでも構わないそうですが1つにしか使えません。また、*がついている変数の前後に変数があったときには、先に前の変数にリストの最初の要素が入り、後ろの変数にはリストの最後の要素が入っていきます。残った分は*のついた変数に吸収される感じです。

ちょっとわかりにくいので、ここでサンプルコードを試してみます。

# 最後の変数にアスタリスク
tp = (0, 1, 2, 3, 4)
a, b, *c =tp
print(f'{a}, {b}, {c}')
0, 1, [2, 3, 4]

# 真ん中の変数にアスタリスク
a, *b, c =tp
print(f'{a}, {b}, {c}')
0, [1, 2, 3], 4

# 最初の変数にアスタリスク
*a, b, c =tp
print(f'{a}, {b}, {c}')
[0, 1, 2], 3, 4

# アスタリスクを2つの変数につけたらこうなる
*a, *b, c =tp
SyntaxError: multiple starred expressions in assignment

これはタプルの例ですが、リストもほぼ同じです。

辞書も * を使ったアンパックは可能ですが、キーが取り出されます。関数へ辞書の内容を渡したい場合は、通常 ** が使われるそうです。これに関しては次の項目で!

関数の引数としてリスト・タプルの要素を渡したい

では次に、タプルやリストの要素を関数の引数として渡したいときの方法です。

ここでもまた「*(アスタリスク)」を使用します。

では実際にサンプルコードを試してみます。

def sample_func(param1, param2, param3):

    print(f'{param1}, {param2}, {param3}')

    
args = [1, 2, 3]
sample_func(*args)
1, 2, 3

リスト名にアスタリスクをつけていますね。

辞書を展開して引数として渡してみよう

では次に辞書の展開です。

先ほどのリストとタプルの時にはアスタリスクは一つでしたが、辞書の場合は2つのアスタリスクを付けます。こちらがサンプルコード。

def display_user(name, age, email):
    print(f'{name}, {age}, {email}')

    
user= {'name': 'john', 'age': 30, 'email': 'john@example.com'}
display_user(**user)
john, 30, john@example.com

確かに辞書の値が取り出されています。

ちなみに、先ほどのアスタリスク1つのサンプルコードの時は位置引数で引き渡されますが、こちらのアスタリスク2つのほうはキーワード引数として引き渡されるそうです。関数の引数を入れ替えて一応確認してみます。

def display_user(email, name, age):
    print(f'{name}, {age}, {email}')

    
user= {'age': 30, 'name': 'john', 'email': 'john@example.com'}
display_user(**user)
john, 30, john@example.com

キーワードで割り当てられていますね。

よくある使われ方

辞書のキーと値の両方を一度に取り出したいときにアンパックはよく利用されるようです。

どういうケースなのか。サンプルコードを確認してみます。

———–引用———–

以下の例では、items()メソッドの結果をfor文に渡すことで、要素が2つのタプルから辞書のキーと値をそれぞれの変数に一度で受け取れます。

country_code = {
    'GBR': '英国',
    'TWN': '台湾',
    'JPN': '日本'

    }
for key, value in country_code.items():
    print(f'{key}:{value}')

    
GBR:英国
TWN:台湾
JPN:日本

———–

items()メソッドを使うと、辞書の各要素を (キー, 値) のタプルとして取り出してくれます。なので、例えば「’GBR’: ‘英国’」は items() で取り出されると (‘GBR’, ‘英国’) という2要素のタプルになります。

そして、タプルとしてセットで取り出されているので、for文で (‘GBR’, ‘英国’) のようなタプルがアンパックされ、キーが key に、値が value に代入されます。

では次のサンプルコードです。

———–引用———–

Pythonの組み込み関数enumerate()を使うと、for文でリストやタプルなどのイテラブルオブジェクトの要素と同時にインデックス番号を取得できます。これらの要素とインデックス番号はタプルで返されるため、アンパックを利用できます。

colorlist = ["red", "blue", "green"]
for i, color in enumerate(colorlist):
    print(f'{i}番目の色は{color}です')

    
0番目の色はredです
1番目の色はblueです
2番目の色はgreenです

———–

気を付ける点としては、enumerate()は(index, value)というタプルを順番に生成してくれます。変数に代入するときはうっかり順番が逆にならないよう注意しましょう、くらいなのかなと…。ちなみにインデックス番号は0スタートですが、1からのスタートにしたいのなら、enumerate(colorlist, 1)と第二引数に数え始めの数値を入れてあげればOKです。

それではきりが良いので今回はこちらで終了です。

お付き合いいただきありがとうございました。

実践試験について知りたい方は以下をご覧ください。

●Python3エンジニア認定実践試験

PAGE TOP