![見出し画像](https://assets.st-note.com/production/uploads/images/92620799/rectangle_large_type_2_db5c0e40af1875a71180899938501656.jpg?width=1200)
文系でも分かる!Pythonプログラミング - nest / collection / 内包表記
← preview
next →
nest
![](https://assets.st-note.com/production/uploads/images/92620810/picture_pc_782b2b554df35d12d2697e55658b1802.png?width=1200)
collection
>> collection ( コレクション )
= 集めること、収集、採集、回収。
list型, tuple型, dict型, set型...
これまでに複数の値を格納することができるオブジェクトを
いくつか扱ってきました。
この性質を持つオブジェクトのことを
「コレクション」と言います。
nest
![](https://assets.st-note.com/production/uploads/images/92621020/picture_pc_dcdb9c95c644251e3e450402d9f36143.png?width=1200)
>> nest ( ネスト )
= 入れ子(いれこ)。
巣。避難所、棲み家、休み場所、
巣窟(そうくつ)、温床、巣の中のもの。
コレクションには様々な値を格納できますよね。
その「様々な値」ってのには、
リストやタプルといった
コレクション自体も含まれるわけです。
in_list = [{1,2,3}, ["あ","い","う"], ("a","b","c")]
例えばこんな感じで
リストの中にリストが入ってる事があります。
リストの中にタプルが入っていたり
辞書の値にリストが格納されていたりもします。
このようにコレクションに格納されたリストの事を
「ネストされたリスト」「入れ子にされたのリスト」
と呼びます。
※タプルの場合「ネストされたタプル」
また、データ群の中に更にデータ群がある構造を
「入れ子構造」と呼びます。
# 入れ子構造
city = [["Washington D.C.","NewYork"],["Tokyo","Osaka"],["London"]]
リストの事を「n次元のリスト」という
言葉で言い表す事もあります。
例えばこんな風に。
a = [1, 2, 3, 4, 5] # 1次元
b = [[1, 2, 3], [4, 5]] # 2次元
c = [[[1, 2], [3]], [[4, 5]]] # 3次元
何次元のリストまで作れるか確かめるには
以下のコードを実行してみてください。
使用環境によって上限が異なります。
iPad Pro (第2世代) のPythonistaでは、
254次元が限界でした。
l = []
for i in range(1000):
l = [l]
print(l)
条件分岐の中(if~:)に別の条件分岐が
ネストされる事もあります。
# (省略)
#------------------------------#
if a > b:
if a - b >= 5:
print(a,b,True)
else:
print(a,b)
else:
if b - a >= 5:
print(a,b,True)
else:
print(a,b)
#------------------------------#
# >>> (省略)
たくさんネストしてもエラーにはなりません。
しかし値を取り出しにくくなったり
何がしたいのか分かりにくくなってしまいます。
これは非効率的です。
ネストのし過ぎにはくれぐれも注意しましょう。
ネストされたリストから値を取り出す
nested_list = [["A"],["B","C"]]
例えば上記の2次元リストから、
"C" だけ取り出したいと思ったとします。
今まで通り nested_list[1] と書いたら
['B', 'C'] というリストが
取り出されてしまいますよね。
print(nested_list[0])
# >>> ['A']
print(nested_list[1])
# >>> ['B', 'C']
しかし大丈夫です。
この取り出されたリスト ['B', 'C'] 中から、
更に "C" だけ取り出すことを
考えればいいのです。
nested_list = ["A",["B","C"]]
print(nested_list[1][1])
# >>> C
nested_list[1][1] のように
ネストされたリストの
[index]番号を続けて書きましょう。
すると、
[ネストされたリスト]内にある特定の値
だけを取り出すことができます。
例えば 5次元リスト になると
nested_list[1][2][3][4][5]
このように多くの [index]番号 を
書くことになります。
間違いではありませんが、
さすがにデータ管理が面倒になります。
リストを変数に置き換えから格納するなどの
工夫が必要ですね。
内包表記
![](https://assets.st-note.com/production/uploads/images/92620909/picture_pc_baca8f9ba4ec5214a589cc52571129a8.png?width=1200)
これは僕がPythonで最も好きな項目の1つです。
非常にスマートでカッコいい。
まずはこちらのサンプルコードをご覧下さい。
l = []
for i in range(1,11):
i = i**2
if i % 2 == 0:
l.append(i)
print(l)
# >>> [4, 16, 36, 64, 100]
空のリストを生成
2乗した i を 再び i に代入
i が 2で割り切れた場合 に
i を リストに追加する
という処理を行っているのですが...
実はこのスクリプト、
「内包表記」というものを使うと
たった一文で書けてしまうんですよ。
# 内包表記
print([i**2 for i in range(10) if i**2 % 2 == 0])
# >>> [0, 4, 16, 36, 64]
すごいですよね。
たった一文で同じ結果を得ることができました。
何が起こっているのか詳しく見ていきましょう。
L = []
for i in range(1,11):
L.append(i)
print(L)
L = [i for i in range(1,11)]
print(L)
この2つのスクリプトを実行すると
同じ結果が得られます。
リスト L には、
取り出し元 である range(1,11) から
取り出した値 i が追加されていますね。
これを内包表記で簡単に書くと、
L = [ 追加する値 for 取り出した値 in 取り出し元 ]
という書き方になります。
取り出し元 はもちろん [iterable_object] です。
どうせ取り出した値を全てリストに追加することは
分かりきってるのだから
リストの中にfor文を書いちゃえ〜というわけです。
複数のオブジェクトを1つの変数に
代入することが出来ない
というルールに変わりはありません。
ですから、L に代入するのは
複数のオブジェクトを格納した
list型オブジェクトでなければならない。
忘れずに [ブラケット] で括ってやりましょう。
追加する値 には
様々な処理を加えることができます。
例えば、
iを3乗したもの を
追加する値 とした場合は...
L = [ iを3乗したもの for 取り出した値 in 取り出し元 ]
という書き方になればいいので、
L = [i**3 for i in range(1,11)]
こうなります。
内包表記の条件分岐
内包表記では
条件文を追加することもできます。
L = [i for i in range(1,11) if i % 2 == 0]
[iterable_object](取り出し元) の直後に
if文 を書いていますね。
通常 for文や if文に必要な「 : (コロン) 」が
書かれていない事には注意しましょう。
あくまでもこれは
1つのlist型オブジェクトなのでね。
通常のif文がそうであったように
内包表記のif文も
既に定義済みの変数、
あるいはたった今取り出した値が代入された変数( i )しか
使うことができません。
L = [i for i in range(1,11) if a % 2 == 0]
# aは定義されていないのでエラーになる。
条件文が書けるなら
elif や else も追加できるか気になりますよね。
結論から言うと追加できます。
ただし、いくつか注意点があります。
「elif」は使えません。
代わりに「 else 取り出した値 if 条件式 」
という書き方をする必要があります。「else i if ~」を使ったら、
「else ~」を必ず最後に書かなければなりません。「if ~」 だけ書く場合は、
f o r 文 は 先 に 書 き ま す 。
「else i if ~ / else ~」も使う場合は、
f o r 文 は 後 で 書 き ま す 。
この3番目の注意点には特に気を付けましょう。
data = ["佐藤","鈴木","加藤","山田","山本","佐々木",
"木村","伊藤","武田","岡田","内藤","藤田",
"田中","高橋","藤木","高木","山口","綾小路"]
name = [d if "佐" in d
else d if "山" in d
else d if "岡" in d
else None
for d in data]
print(name)
# >>> ['佐藤', None, None, '山田', '山本', '佐々木',
# None, None, None, '岡田', None, None,
# None, None, None, None, '山口', None]
ちょっと複雑そうに見えますね。
"佐"、"山"、"岡" という文字を含んだ苗字はそのまま追加し、
それ以外は None に置き換えて追加しています。
[ 追加する値(処理) if 条件式
else 取り出した値 if 条件式
else 置き換える値
for 取り出した値 in 取り出し元 ]
このような書き方になっています。
else の後に
置き換える値 を書くのが
少し特殊ですね。
もし else の後に 条件文 を書くと
True か False に置き換わります。
でも、
置き換えた値( None )が邪魔だな
って思う事もありますよね。
その場合はちょっと手間が掛かりますが、
print([n for n in name if n is not None])
# >>> ['佐藤', '山田', '山本', '佐々木', '岡田', '山口']
このように
if 取り出した値 is not 置き換えた値
条件文を使ったリスト内包表記を書きましょう。
内包表記に慣れるのには
少し時間がかかるかもしれません。
[ 追加する値(処理) for 取り出した値 in 取り出し元 if 条件式 ]
まずはこの形を意識して
様々なリストを作ってみましょう。
次回も内包表記について書きます。