リストの内包表記はとても便利な書き方ですが、他の言語からやってくる人にとって
少し理解しづらいものだと感じました。
なのでこれさえ押さえとけば、簡単な内包表記は書けちゃうよって物をまとめました。
環境
Python 3.7.8
そもそもリストの内包表記とは
リストを生成する簡潔な手段です。【公式:リストの内包表記】
具体的にはこんな感じ。
sample = [1, 2, 3, 4]
new_sample = [ x * 2 for x in sample]
print(new_sample)
# [2, 4, 6, 8]
level 1 : 基本をおさえよう
リスト内包表記の基本はこれだ。
[ expression for item in iterable (if condition) ]
実にシンプル。
- iterableオブジェクトをfor文でループ
- expressionでitemデータを加工
- オプションでifによる条件の絞り込み
level 2 : まずはfor文を置き替えて
full_name = "Yamada Taro"
name_list = []
for c in full_name:
name_list.append(c)
print(full_name)
print(name_list)
# Yamada Taro
# ['Y', 'a', 'm', 'a', 'd', 'a', ' ', 'T', 'a', 'r', 'o']
上のfor文を内包表記で書き換えてみましょう。
full_name = "Yamada Taro"
characters = [char for char in full_name]
print(full_name)
print(characters)
# Yamada Taro
# ['Y', 'a', 'm', 'a', 'd', 'a', ' ', 'T', 'a', 'r', 'o']
2次元リストもitelableとして利用可能。
Matrix = [[7, 1, 5],
[22, 99, 0],
[33, 2, 57]]
row_min = [min(row) for row in Matrix]
# [1, 0, 2]
level 3 : optionのifでデータを絞ろう
if文を使えば、データを絞る事ができます。
SQLのWHERE句に該当するものです。
4文字より少なく、小文字のものだけに絞ります。
People = ["Taro", "Jiro", 'Wan', "Elepahnt", "Tomas", "ken", "sae"]
target = [name for name in People if len(name) < 4 and name.islower()]
print(target)
# ['ken', 'sae']
level 4 : expression でデータを加工しよう
expressionの所は、pythonの構文であれば何を書いても構いません。
nameを加工する例。
Genius = ["taro", "jiro", "yamada", "ito"]
target = [name.capitalize() for name in Genius]
print(target)
# ['Taro', 'Jiro', 'Yamada', 'Ito']
もちろん、expressionに if..else..
をいれる事も可能です。
People = ["Taro", "Hiroyuki", "ken", "youu"]
>>> answer = [name if name.startswith('y') else 'Not Genius' for name in People]
>>> print(answer)
# ['Not Genius', 'Not Genius', 'Not Genius', 'youu']
ここでの注意は、if...else...
は三項演算子であり、Level 1 で紹介した式の最後のif
とは異なるという事です。
[ expression for item in iterable (if condition) ]
この式でのifは抽出条件で、SQLのwhere句と同じものです。
else
は入れられません。
もしexpressionに if
を入れるなら、else
を必ずいれないといけません。
それは、三項演算子だからです。
a = 1
b = 2 if a >0
# SyntaxError: invalid syntax
b = 2 if a > 0 else -1
print(b)
# 2
level 5 : ネストされたfor文もリストに入れよう
ネストされたループはどうでしょう。
Genius = ["taro", "jiro", "yamada"]
L = []
for name in Genius:
for char in name:
L.append(char)
print(L)
# ['t', 'a', 'r', 'o', 'j', 'i', 'r', 'o', 'y', 'a', 'm', 'a', 'd', 'a']
内包表記にすると、こんな感じになります。
Genius = ["taro", "jiro", "yamada"]
L = [char for name in Genius for char in name]
print(L)
# ['t', 'a', 'r', 'o', 'j', 'i', 'r', 'o', 'y', 'a', 'm', 'a', 'd', 'a']
ループは2つより多くなると分かりづらくなるので、2つまでが良いと思います。
level 6 : map, filterを置き換えよう
mapやfilterはfunctionを引数にする事ができますが、
内包表記で書いたほうが分かりやすくなります。
mapは次のように置き換えられます。
L = list(map(func, iterable))
# 置き換え可能
L = [func(a) for a in iterable]
filterは次のように置き換えられます。
L = list(filter(condition_func, iterable))
# 置き換え可能
L = [a for a in iterable if condition]
実際に例を見てみましょう。
People = ["Kenny", "Tomas", "tom", "young"]
L = map(lambda a: a.lower(), People)
print(list(L))
# ['kenny', 'tomas', 'tom', 'young']
L = [a.lower() for a in People]
print(L)
# ['kenny', 'tomas', 'tom', 'young']
People = ["Kenny", "Tomas", "tom", "young"]
L1 = filter(lambda a: len(a) < 4, People)
print(list(L1))
# ['tom']
L2 = [a for a in People if len(a) < 4]
print(L2)
# ['tom']
level 7 : サイズが大きくなる場合は、generator式を使おう
大量の要素を含むものはジェネレーター式を検討しよう。
ジェネレーター式は実際必要になった時に生成されるので、メモリのコストを大幅に削減できます。
(でも、任意の要素にはアクセスできない。先頭から順番のアクセスのみ。)
# 100万の要素を生成
>>> large_list = [x for x in range(1_000_000)]
>>> large_list_g = (x for x in range(1_000_000))
>>> print(large_list.__sizeof__())
8697440
>>> print(large_list_g.__sizeof__())
96
>>>
level 8 : 複雑と感じるなら無理して使わない。
下記2つは同じリストを生成しますが、どちらが直感的に分かりやすいでしょうか。
list_2d = [
[0, 0, 0],
[1, 1, 1],
[2, 2, 2],
]
flat = [num ** 2 if num % 2 == 0 else num for row in list_2d for num in row]
print(flat)
# [0, 0, 0, 1, 1, 1, 4, 4, 4]
list_2d = [
[0, 0, 0],
[1, 1, 1],
[2, 2, 2],
]
flat = []
for row in list_2d:
for num in row:
flat.append(num ** 2 if num % 2 == 0 else num)
print(flat)
# [0, 0, 0, 1, 1, 1, 4, 4, 4]
私はforループ
の方が直感的で分かりやすいと思いますが、人によって違うと思います。
この例では単純な2重ループでしたが、条件文がついたり、expressionでデータを加工したり、forループが増えたりして、複雑だと感じたら内包表記を使わない方が良いのではないでしょうか。
さいごに
内包表記は簡潔にリストの記述をできますが、他者が逆に理解しづらくなってしまっては本末転倒なので、チーム内の様子を見ながら利用するのが良いのではないでしょうか。
level1 の式を押さえておくのはお勧めです。
以上