見出し画像

Python:リストの内包表記

「Python コードレシピ集」(黒住敬之著 技術評論社)をぱらぱらと見ていたら,レシピ062にリスト内包表記があった。載っている例は,リスト list1 の各要素を2倍した list2 を作るもので,

list1 = [1,2,3]
list2 = []
for val in list1:
   list2.append(val * 2)
print(list2)

を,内包表記を使うと

list1 = [1,2,3]
list2 = [val * 2 for val in list1]
print(list2)

とできる,という話。

これを読んで思い出したことがある。少し前に,Twitterでこんなやりとりをした。

画像4

Maehara さんのコードでは,0と1を反転するのに,まず 0 / 1 をブール値の代用として not で反転し,それを int で整数化している。筆者のコードでは,単に 1 から引くだけだ。 1 - y で,y が 0 なら 1,1 なら 0 になる。これは情報の授業でもやったもの。abs(y-1) という手もある。Cindyscriptなら絶対値記号が使えるので,|y-1| でよい。

最近思ったのは,それだったら,リストのコピーをするのに内包表記でいけるか,ということだった。その時にはうまくいかなかったが,もう一度やってみた。
リストのコピーは

list1 = [1,2,3]
list2 = list1

ではうまくいかない。リストはミュータブルだからだ。コピーしたつもりでも,list2 の要素を書き換えると,もとの list1 の要素も書き変わってしまう。

画像5

そこで,リストのコピーには,copy モジュールを import して,copy または deepcopy (2次元のとき)を使うのだが,そんなことをしなくても 内包表記でできないだろうか。

まず,1次元のリスト。

画像6

できた。
では,2次元では?

画像7

だめだ。list2 を変更したら,list1 まで書き変わっている。

では,Twitterでのやりとりのようにしたら?

画像8

できた。めでたしめでたし。

しかし,高校の授業ではリストの内包表記は教えない。面倒でも,for ループで append する。まず,理解できる生徒が少ないだろうし,共通テストでは絶対に出ないからだ。for ループを習熟する方がいい。
教材を作るのに,裏方として使うのはいいだろうな。1行で済むから。

ところで,「Python コードレシピ集」に載っている例だが,各要素を2倍するんだったら, 2 * list1 でいいか,と思うとそうはいかない。Python のリスト処理では,つぎのようになる。

画像1

ベクトルや行列の,スカラーをかけるときのようにしたければ,Numpy を使うことになる。

画像2

Cindyscriptでは,リストはそのまま線形代数で使える(Numpy の array と同じ)ので,次のようにできる。

画像3

Cindyscriptのリストに慣れていると,Pythonのリストは面倒でしょうがない。append (後方追加)はあっても prepend(前方追加)はないし,A = B でコピーできないし。ま,しょうがないか。

なんてことをやっているうちに,面白いことを見つけた。np.array はミュータブル? つまり,先程の例で,list2 = 2 * list1 で,各要素を2倍したリストを作った。じゃあ,list2 = list1 でコピーができる?

画像9

やっぱりだめだよね。
でもさ,こうすりゃできちゃうんだね。

画像10

2次元(Numpy では行列になる)でもできちゃう。

画像11

こういうのを Tips というべきか,裏技というべきか,好奇心を刺激することは確かだけど(プログラミングの楽しみの一つ) なんなんでしょね,Pythonって。