内包表記とは「イテラブルオブジェクトを元にして、新たなイテラブルオブジェクトを作る」構文です。
今回は内包表記を使って、様々なイテラブルオブジェクトを作成していきたいと思います。
基本的な内包表記の書き方 (for文との比較)
まずは通常のfor文で値が入ったリストを作成してみます。
lst = []
for i in range(5):
lst.append(i*2)
print(lst) #[0, 2, 4, 6, 8]
最初に空のリストを作成し、そこにappend
メソッドで要素を追加していく形。これがfor文を使った通常のリスト作成方法です。
続いて内包表記を用いた方法。
lst = [i*2 for i in range(5)]
print(lst) #[0, 2, 4, 6, 8]
リストを作成しているのは1行目のみです。内包表記を使うことで、同じ処理でも非常に簡潔に記述できているのが分かります。
注意しておきたいのは記述する順番です。内包表記の場合式 for 変数 in イテラブル
と、新しいオブジェクトに入る要素を作る式を最初に記述します。
if文を含んだ内包表記
内包表記にはif文を含めることができます。
kingdom = ["信","王賁","蒙恬","蒙武","王翦","桓騎","秦王"]
lst = [i for i in kingdom if i.startswith("王")]
print(lst) #['王賁', '王翦']
このコードでは漫画「キングダム」のキャラクターの中から、「王」で始まるキャラだけを選別し、新たなリストを作成しています。
通常のfor文と比較してみると、
kingdom = ["信","王賁","蒙恬","蒙武","王翦","桓騎","秦王"]
lst = []
for i in kingdom:
if i.startswith("王"):
lst.append(i)
print(lst)
for文の中に記述されたif文が、内包表記の場合はfor文の後ろに来ていることが分かります。実際の処理と表記の順番が異なる内包表記は、変数iの動きを把握することが重要です。
if-elseを含んだ内包表記 – 三項演算子
ifだけでなくelseも内包表記に含めたい場合、先ほどとは順番が変わってしまうのでこれもチェックしておきましょう。
kingdom = ["信","王賁","蒙恬","蒙武","王翦","桓騎","秦王"]
lst = [i if i.startswith("王") else "その他" for i in kingdom]
print(lst)
#['その他', '王賁', 'その他', 'その他', '王翦', 'その他', 'その他']
if-else文を含む場合、内包表記の中では三項演算子という記述方法を用いて表現します。if-elseを丸ごとfor文の前に出すということには注意です。
if-elif-elseを含める
同じように三項演算子を使って「if-elif-else」と同じ処理を内包表記に含めることができます。
lst = [i if i.startswith("王") else i if i.startswith("蒙") else ("その他") for i in kingdom]
print(lst)
#['その他', '王賁', '蒙恬', '蒙武', '王翦', 'その他', 'その他']
このコードの中に「elif」はありません。三項演算子ではelifの代わりにelse 式 if
を重ね「そうでなくて、もしこの後のifがTrueなら」という文法でelifを表現しています。
ただ処理が若干複雑化してくると、内包表記ではすぐに可読性が著しく低下してしまいます。1行で記述することにこだわらず、改行するか通常のfor文を使用したほうがより読みやすいコードになるでしょう。
lst = [i if i.startswith("王")
else i if i.startswith("蒙")
else ("その他")
for i in kingdom]
print(lst)
セット/タプル内包表記
これまではリストを作る「リスト内包表記」を例としてお話してきました。ではセットやタプルを作るにはどうすればよいでしょうか?
まずはセットです。
numbers = [1,2,3,1,2,3,1,2,3]
s = {n*2 for n in numbers}
print(s) #{2, 4, 6}
リスト内包表記との違いは、内包表記を囲む括弧が波括弧{}になっただけです。これで最終的な出力はセットになります。重複した値が除外されていることが確認できるはずです。
ではタプルはどうでしょう?
numbers = [1,2,3,1,2,3,1,2,3]
t = (n*2 for n in numbers)
print(t) #<generator object <genexpr> at 0x7fac57b36c80>
タプルは括弧を丸括弧()にすればいいかと思いきや、このコードの出力はジェネレータオブジェクトです。タプルを作るのであればこの内包表記の頭にtuple()
を付け加え、結果をタプルに変換する必要があります。
numbers = [1,2,3,1,2,3,1,2,3]
t = tuple(n*2 for n in numbers)
print(t) #(2, 4, 6, 2, 4, 6, 2, 4, 6)
辞書内包表記
続いて辞書を作ります。辞書は通常の場合、zip関数を使用してループを回すのが簡単です。まずはfor文を見てみます。
gafa = ["google","amazon","facebook","apple"]
product = ["検索","通販","SNS","iPhone"]
dic = {}
for i,j in zip(gafa,product):
dic[i] = j
print(dic)
#{'google': '検索', 'amazon': '通販', 'facebook': 'SNS', 'apple': 'iPhone'}
ではこれと同じものを内包表記で作成してみましょう。
dic = {i:j for i,j in zip(gafa,product)}
これで上の例と全く同じ辞書を作成できます。やはり辞書の場合でもfor文のヘッダ部分は変わりません。最終的な値となる式の部分には「i : j」とキー:バリューを両方記述しましょう。
またキーにインデックス番号を割り当てる場合などは、zip関数ではなくenumerate関数を使ってもかまいません。
dic = {i:j for i,j in enumerate(gafa,1)}
print(dic)
#{1: 'google', 2: 'amazon', 3: 'facebook', 4: 'apple'}
enumerate関数は第一引数にイテラブルオブジェクト、第二引数に開始番号を渡すことで、通し番号を別に出力することができます。(第二引数は省略可能です。省略した場合、番号は0から始まります)