はじめに
pythonの基礎として、内包表記について記載していきます。
内包表記とは?
内包表記とは、リストや辞書などのイテラブルオブジェクトのループ処理を簡単に記述できる方法です。
- リスト内包表記
- セット内包表記
- 辞書内包
- ジェネレータ式
内容としてはリスト内包表記の割合が大きいです。
リスト内包表記
リスト内包表記は以下のように書きます。
[式 for 変数名 in イテラブルオブジェクト]
リストやタプルなどのイテラブルオブジェクトの各要素に対して式が評価され、その結果を要素とする新たなリストが返されます。
例えば
>>> [i*2 for i in [1, 2, 3, 4, 5]]
[2, 4, 6, 8, 10]
# 当然変数に入れられるし、range()も使える
>>> test = [i*2 for i in range(5)]
>>> print(test)
[0, 2, 4, 6, 8]
# for文で記述するとこんな感じ
>>> test = []
>>> for i in range(5):
... test.append(i*2)
...
>>> print(test)
[0, 2, 4, 6, 8]
if文を使ったリスト内包表記
以下のようにすることが可能です。
[式 for 変数名 in イテラブルオブジェクト if 条件式]
条件式がTrueとなるイテラブルオブジェクトの要素のみ式が評価され、その結果を要素とするリストが返されます。
以下では、1から10までの2で割り切れる要素に対し、2倍する処理が行われ、その結果がリストに追加されています。
>>> gusu_2 = [x*2 for x in range(1,11) if x % 2 == 0]
>>> print(gusu_2)
[4, 8, 12, 16, 20]
以下では2で割り切れない要素(奇数)を、リストとして返しています。
>>> kisu = [i for i in range(10) if i % 2 == 1]
>>> print(kisu)
[1, 3, 5, 7, 9]
# for文で書くとこうなる
>>> kisu_list = []
>>> for i in range(10):
... if i % 2 == 1:
... kisu_list.append(i)
...
>>> print(kisu_list)
[1, 3, 5, 7, 9]
書く順番を変えて、if-elseで3の倍数で馬鹿になることもできます。
>>> ["baka" if x % 3 == 0 else x for x in range(1,10)]
[1, 2, 'baka', 4, 5, 'baka', 7, 8, 'baka']
三項演算子については長くなりそうなのでまた別で投稿します。
ネストしたリスト内包表記
複数のイテラブルオブジェクトを組み合わせることもできます。
[式 for 変数名1 in イテラブルオブジェクト1 for 変数名2 in イテラブルオブジェクト2 for 変数名3 in イテラブルオブジェクト3]
例えば
>>> [x for inner_list in [[1, 3], [5], [7, 9]] for x in inner_list]
[1, 3, 5, 7, 9]
matrix = [["A","B","C"], ["D","E","F"], ["G","H","I"]]
>>> flat = [x for row in matrix for x in row]
>>>
>>> print(flat)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
# for文だとこうなる
>>> flat = []
>>> for row in matrix:
... for x in row:
... flat.append(x)
...
>>> print(flat)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
複数の変数を使うことも可能。
>>> cells = [(row, col) for row in range(3) for col in range(2)]
>>> print(cells)
[(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]
# 条件分岐を追加することもできる
>>> cells = [(row, col) for row in range(3) for col in range(2) if col == row]
>>> print(cells)
[(0, 0), (1, 1)]
# 多重ループ:独立した2つのリストを2重ループとして回す
[x + y for x in range(3) for y in [100, 200, 300]]
zip()で同時2つのリストを扱う
zip()を使うことでも2つのリストを同時に扱うことができます。
例えば
>>> list1 = [1, 2, 3]
>>> list2 = [4, 5, 6]
>>> new_list = [x * y for x, y in zip(list1, list2)]
>>> print(new_list)
[4, 10, 18]
>>> [x * y for x, y in zip([1,2,3], [4,5,6])]
[4, 10, 18]
セット内包表記
リストの代わりにセットを生成する内包表記です。セットの場合は[]の代わりに{}で囲むだけで記述できます。
>>> set1 = {x*2 for x in range(1,11)}
>>> print(set1)
{2, 4, 6, 8, 10, 12, 14, 16, 18, 20}
辞書内包表記
リストの代わりに辞書を生成する内包表記です。辞書の場合は要素の値を記述する部分で「key:value」といったようにコロンを使い、セットと同様に{}で囲みます。
>>> my_info = [("age",33),("weight",78),("height",175)]
>>> {k:v for k,v in my_info}
{'age': 33, 'weight': 78, 'height': 175}
要素を定義する部分が「k:v」となっています。keyとvalueです。
以下のようにすれば、各値の二乗したものが辞書として得られます。
>>> test = {x:x**2 for x in range(1,11)}
>>> print(test)
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
ジェネレータ式
リスト内包表記を丸括弧()にすると、リストではなくジェネレータを返します。
これをジェネレータ式(generator expression)といいます。戻り値はリストではありません。
メリットとしては省メモリなことです。リスト内包表記を使うと最初に全要素を含むリストが生成されますが、ジェネレータ式を使えばループが繰り返される度に、都度一つずつ生成されるので、メモリの使用量を抑えることができます。
>>> gene = (i*2 for i in range(5))
>>> print(gene)
<generator object <genexpr> at 0x10c04e468>
>>> print(type(gene))
<class 'generator'>
for文を回すことで中身を出力することができます。
>>> for i in gene:
... print(i)
...
0
2
4
6
8
参考
[Python] 部屋とYシャツとイテレータとジェネレータと私