(リスト外延表記: list extension)
extension_1 = []
for i in range(10):
extension_1.append(i)
extension_1
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list comprehension
基本構文
[ element for element in iterator]
僕は先に[i for i in]
だけ書いてから修飾することが多いです。
comprehension_1= [i for i in range(10)]
comprehension_1
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
%%timeit
extension_1 = []
for i in range(10000):
extension_1.append(i)
100 loops, best of 3: 3.37 ms per loop
%%timeit
comprehension_1= [i for i in range(10000)]
1000 loops, best of 3: 1.05 ms per loop
list内包表記はコードがすっきりするだけでなく速度面でも有利です
参考: Pythonの内包表記はなぜ速い?
遅い理由は大きく2つ
前者の影響はループの外に参照を追い出すことでも解消できます
%%timeit
extension_1_ = []
append=extension_1_.append
for i in range(10000):
append(i)
1000 loops, best of 3: 1.57 ms per loop
リスト内包表記は後置ifが使えます
extension_2 =[]
for i in range(10):
if i%2==0:
extension_2.append(i)
extension_2
[0, 2, 4, 6, 8]
comprehension_2 = [i for i in range(10) if i%2==0]
comprehension_2
[0, 2, 4, 6, 8]
%%timeit
extension_2 =[]
for i in range(10000):
if i%2==0:
extension_2.append(i)
100 loops, best of 3: 4.08 ms per loop
%%timeit
comprehension_2 = [i for i in range(10000) if i%2==0]
100 loops, best of 3: 3.15 ms per loop
%%timeit
extension_2_ =[]
append=extension_2_.append
for i in range(10000):
if i%2==0:
append(i)
100 loops, best of 3: 3.81 ms per loop
実はifが計算律速になるので、無理にリスト内包表記にする必要がなかったりします。
紛らわしいですが、else節を含む場合は条件演算子(他で言う三項演算子)を使うのでifの位置が変わります
(条件演算子はpython 2.5以降のみ対応です)
extension_3 =[]
for i in range(10):
if i%2==0:
extension_3.append(i)
else:
extension_3.append(str(i))
extension_3
[0, '1', 2, '3', 4, '5', 6, '7', 8, '9']
comprehension_3 = [ i if i%2==0 else str(i) for i in range(10)]
comprehension_3
[0, '1', 2, '3', 4, '5', 6, '7', 8, '9']
実際にはこちらと等価だと思えば理解しやすいかもしれません
extension_3_conditional =[]
for i in range(10):
extension_3_conditional.append(i) if i%2==0 else extension_3_conditional.append(str(i))
extension_3_conditional
[0, '1', 2, '3', 4, '5', 6, '7', 8, '9']
一応速度を測ってみます
%%timeit
extension_3 =[]
for i in range(10000):
if i%2==0:
extension_3.append(i)
else:
extension_3.append(str(i))
100 loops, best of 3: 12.4 ms per loop
%%timeit
extension_3_ =[]
append=extension_3_.append
for i in range(10000):
if i%2==0:
append(i)
else:
append(str(i))
100 loops, best of 3: 10.3 ms per loop
%%timeit
extension_3_conditional_ =[]
append=extension_3_conditional_.append
for i in range(10000):
append(i) if i%2==0 else append(str(i))
100 loops, best of 3: 9.04 ms per loop
%%timeit
comprehension_3 = [ i if i%2==0 else str(i) for i in range(10000)]
100 loops, best of 3: 8.6 ms per loop
あまり内包表記による高速化はありません
python2.7以降ではリスト以外の内包表記として、辞書内包やセット内包も使えます。
comprehension_dict = {str(i):i for i in range(10)}
print(comprehension_dict)
{'7': 7, '8': 8, '2': 2, '9': 9, '0': 0, '1': 1, '6': 6, '5': 5, '4': 4, '3': 3}
zipとかと相性いいです。
label = ["kinoko", "takenoko", "suginoko"]
feature = ["yama", "sato", "mura"]
{i:j for i,j in zip(label,feature)}
{'kinoko': 'yama', 'suginoko': 'mura', 'takenoko': 'sato'}
python2.6まではdictにtupleを渡してあげます。
comprehension_dict2 = dict((str(i),i) for i in range(10))
print(comprehension_dict2)
{'7': 7, '8': 8, '2': 2, '9': 9, '0': 0, '1': 1, '6': 6, '5': 5, '4': 4, '3': 3}
後置ifも使えます。
comprehension_dict2 = {str(i):i for i in range(10) if i%2==0}
print(comprehension_dict2)
{'8': 8, '6': 6, '2': 2, '4': 4, '0': 0}
条件演算子も使えます。
条件演算子なので、「:」の前後のkeyとvalueそれぞれに記載する必要があります。
comprehension_dict3 = {str(i) if i%2==0 else i : i if i%2==0 else str(i) for i in range(10)}
print(comprehension_dict3)
{'2': 2, 1: '1', 3: '3', 5: '5', '0': 0, 7: '7', 9: '9', '6': 6, '4': 4, '8': 8}
これは動かないです。(以前やりましたorz)
comprehension_dict4 = {str(i):i if i%2==0 else i:str(i) for i in range(10)}
File "<ipython-input-23-60c72c76aa53>", line 1 comprehension_dict4 = {str(i):i if i%2==0 else i:str(i) for i in range(10)} ^ SyntaxError: invalid syntax
セット内包表記にしたい時は : なしで {} で囲みます。
comprehension_set={i%5 for i in range(10)}
comprehension_set
{0, 1, 2, 3, 4}
構文から勘違いしやすいですが、()で囲んでもタプル内包表記になりません。
comprehension_gen=(i%5 for i in range(10))
comprehension_gen
<generator object <genexpr> at 0x7f3000219678>
for i in comprehension_gen:print(i)
0 1 2 3 4 0 1 2 3 4
()で囲うとジェネレータが発生します。むしろタプル内包表記より使います。
リストと違ってメモリ中に全要素を格納しないで、次の要素を順番に生成します。
内包表記を使わない書き方は下記ですが、一旦 ジェネレータを生成する関数を作らないと行けないので面倒です。
def gen_func():
for i in range(10):
yield i
extension_gen = gen_func()
extension_gen
<generator object gen_func at 0x7f3000166830>
タプル内包表記が必要なことはあまりないですが、もしどうしても必要ならリスト内包表記をtuple関数に渡すしかないです。
tuple([i for i in range(10)])
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
この節を読み終えた後はリーダブルコードを読んで浄化されることを推奨します。
for i in range(1,16):
if i%15==0:
print ("fizzbuzz")
elif i%3==0:
print ("fizz")
elif i%5==0:
print ("buzz")
else:
print(i)
1 2 fizz 4 buzz fizz 7 8 fizz buzz 11 fizz 13 14 fizzbuzz
["fizzbuzz" if i%15==0 else "fizz" if i%3==0 else "buzz"
if i%5==0 else i for i in range(1,16)]
[1, 2, 'fizz', 4, 'buzz', 'fizz', 7, 8, 'fizz', 'buzz', 11, 'fizz', 13, 14, 'fizzbuzz']
ネスト(多重配列)
outer_list=[]
for i in range(3):
innter_list=[]
for j in range(10):
innter_list.append(j)
outer_list.append(innter_list)
outer_list
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]
[[j for j in range(10)] for i in range(3)]
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]
フラッタン
この辺から可読性著しく悪くなります。
init=[[1,2,3],[4,5],[6,7]]
flatten=[]
for outer in init:
for inner in outer:
flatten.append(inner)
flatten
[1, 2, 3, 4, 5, 6, 7]
これと等価
[inner for outer in init for inner in outer]
[1, 2, 3, 4, 5, 6, 7]
基本的には左側のfor節から順番に読んでいって、最後にリストに入れるものが頭にくる
インデントするならこんな感じ
[
inner
for outer in init
for inner in outer
]
[1, 2, 3, 4, 5, 6, 7]
パタトクカシー
pato="パトカー"
taxi="タクシー"
rslt=[]
for i in zip(pato,taxi):
for j in i:
rslt.append("".join(j))
"".join(rslt)
'パタトクカシーー'
"".join(["".join(j) for i in zip(pato,taxi) for j in i])
'パタトクカシーー'
インデントすると
"".join(
["".join(j)
for i in zip(pato,taxi)
for j in i
]
)
'パタトクカシーー'
printとか入れてもできる
for i in zip(pato,taxi):
for j in i:
for k in j: print(k)
パ タ ト ク カ シ ー ー
[print(k) for i in zip(pato,taxi) for j in i for k in j]
パ タ ト ク カ シ ー ー
[None, None, None, None, None, None, None, None]
if 節との多重ループの合わせ技
import re
DIO=["U","無駄","RR","貧弱ゥ","Y"]
rslt=[]
for i in DIO:
if re.match("[URY]+",i):
for j in i:
rslt.append(j*2)
"".join(rslt)
'UURRRRYY'
"".join([j*3 for i in DIO if re.match("[URY]+",i) for j in i])
'UUURRRRRRYYY'
インデントするとこんな感じ
"".join(
[
j*4
for i in DIO
if re.match("[URY]+",i)
for j in i
]
)
'UUUURRRRRRRRYYYY'