見出し画像

文系でも分かる!Pythonプログラミング - nest / collection / 内包表記

← preview

next →

nest

classfication.

collection

>> collection ( コレクション )
= 集めること、収集、採集、回収。

list型, tuple型, dict型, set型...

これまでに複数の値を格納することができるオブジェクト
いくつか扱ってきました。

この性質を持つオブジェクトのことを
コレクション」と言います。


nest

[[(nest.)]]

>> nest ( ネスト )

= 入れ子(いれこ)。

巣。避難所、棲み家、休み場所、
巣窟(そうくつ)、温床、巣の中のもの。


コレクションには様々な値を格納できますよね。

その「様々な値」ってのには、

リスト
タプルといった
コレクション自体も含まれるわけです。

in_list = [{1,2,3}, ["あ","い","う"], ("a","b","c")]

例えばこんな感じで
リストの中にリストが入ってる事があります。

リストの中にタプルが入っていたり
辞書の値にリストが格納されていたりもします。


このようにコレクションに格納されたリストの事を
ネストされたリスト」「入れ子にされたのリスト

と呼びます。

※タプルの場合「ネストされたタプル」

また、データ群の中に更にデータ群がある構造
入れ子構造」と呼びます。

# 入れ子構造

city = [["Washington D.C.","NewYork"],["Tokyo","Osaka"],["London"]]

リストの事を「n次元のリスト」という
言葉で言い表す事もあります。

例えばこんな風に。

a = [12345]            # 1次元

b = [[123], [45]]        # 2次元

c = [[[12], [3]], [[45]]]  # 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]番号 
書くことになります。

間違いではありませんが、
さすがにデータ管理が面倒になります。
リストを変数に置き換えから格納するなどの
工夫が必要ですね。


内包表記

keep it simple...

これは僕が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]
  1. 空のリストを生成

  2. 2乗した i を 再び i に代入

  3. i が 2で割り切れた場合 

  4. を リストに追加する

という処理を行っているのですが...

実はこのスクリプト、
「内包表記」というものを使うと
たった一文で書けてしまうんですよ。


# 内包表記

print([i**2 for i in range(10if 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 = [for i in range(1,11if i % 2 == 0]

[iterable_object](取り出し元) の直後に
if文 
を書いていますね。

通常 for文や if文に必要な「 : (コロン) 」が
書かれていない事には注意しましょう。

あくまでもこれは
1つのlist型オブジェクトなのでね。


通常のif文がそうであったように
内包表記のif文も

既に定義済みの変数、
あるいはたった今取り出した値が代入された変数
( i )しか
使うことができません。

L = [i for i in range(1,11if 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)

# >>> ['佐藤', NoneNone, '山田', '山本', '佐々木', 
#      NoneNoneNone, '岡田', NoneNone, 
#      NoneNoneNoneNone, '山口', 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 条件式 ]

まずはこの形を意識して
様々なリストを作ってみましょう。


次回も内包表記について書きます。

この記事が気に入ったらサポートをしてみませんか?