HYT MachineWorks

やったこととか思いついたことをメモしておくブログです。

forで、apendしたり、forにforしてネストが深くなる問題を回避する

forで、apendしたり、forにforをしてネストが深くなる問題とは

Pythonで色々処理をするときに、listを forでループさせることや、forでリストを作ったりすることがあると思います。いろいろ組み合わせたくてforにforを重ねてどんどんネストが深くなってしまったりすると思います。

それを回避する技とまではいかないけど方法をメモ

リスト内包表記

 まず、ありがちな、1~10の整数のリストを作るときに

a = []

for i in range(10):
a.append(i + 1)

print(a)

>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

ってやると思います。この場合、リスト内包表記を使うと

b = [i + 1 for i in range(10)]

print(b)

>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

という形で一行で表現出来てスッキリします。ここでは、イテレータから取り出したiに1を足して追加していますがそこに関数を適応したりもできるので

def func(x):
return x**10


c = [func(i + 1) for i in range(10)]

print(c)

>[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

こんな感じのことも出来ます。

また、中で条件を追加することも出来て偶数を抽出したくなった場合は、以下の様に書けます。

d = [i + 1 for i in range(10) if not (i + 1) % 2]

print(d)

>[2, 4, 6, 8, 10]

条件が後ろに来るのがなんか違和感がありますが、条件に合致するもののみを取り出すことが出来ます。

三項演算子

Pythonでは三項演算子が使えます。それは、通常if elseを用いて

if foo:
bar = 100
else:
bar = 0

のように書いていたものを

bar = 100 if foo else 0

という一つの式として表現できます。それと一つ前で説明した、リスト内包表記の中で関数を適応できるというものを組み合わせると

d = [i + 1 if not (i + 1) % 2 else 0 for i in range(10)]

print(d)

>[0, 2, 0, 4, 0, 6, 0, 8, 0, 10]

という感じで条件分岐をさせることができます。

同じ長さのリストをforで回したいとき

たとえば、下のe,fみたいな同じ長さのリストがあるとき

e = [1, 2, 3, 4, 5]
f = ["a", "b", "c", "d", "e"]

同時に回したい時は、zip関数を使って2つのリストを引数に入れて回すと同時に同じインデックスの値を取り出すことができて

e = [1, 2, 3, 4, 5]
f = ["a", "b", "c", "d", "e"]

for numeric, string in zip(e, f):
print(numeric, string)

>1 a
  2 b
  3 c
  4 d
  5 e

って感じで出来ます。forで回している以外のリストの値をenumerateを使ってスライスで取り出すみたいな事をしなくて済みます。ちなみに、2つ以上の引数でもOKです。

注意:同じ長さと前置きしてる様に違う場合短い方の回数しか回りません。注意してください。

forにforするを回避する

いよいよ、本題です。例えば、

g = [1, 2, 3, 4, 5, 6]
h = ["am", "pm"]

 という、2つのリストがあった場合に、gの値それぞれに、hの"am"と"pm"を付けたいとき

twelve_clock_list = []

for hour in hour_list:
for twelve in am_pm:
twelve_clock_list.append([hour, twelve])

print(twelve_clock_list)

 >[[1, 'am'], [1, 'pm'], [2, 'am'], [2, 'pm'], [3, 'am'], [3, 'pm'], [4, 'am'], [4, 'pm'], [5, 'am'], [5, 'pm'], [6, 'am'], [6, 'pm']]

って感じなんですが、大したことしてないのに二重になって嫌ですよね。

そんなあなたにおすすめなのが、itertoolsです。

10.1. itertools — 効率的なループ実行のためのイテレータ生成関数 — Python 3.6.1 ドキュメント

詳しい関数は、公式のドキュメントを見てもらうとして同じことがitertoolsを使うとこうなります。

from itertools import product

twelve_clock_list = [list(clock) for clock in product(hour_list, am_pm)]

print(twelve_clock_list)

>[[1, 'am'], [1, 'pm'], [2, 'am'], [2, 'pm'], [3, 'am'], [3, 'pm'], [4, 'am'], [4, 'pm'], [5, 'am'], [5, 'pm'], [6, 'am'], [6, 'pm']]

取り出すのをclockという一つで受け取っていますが実際は、hour_listとam_pmの値を組み合わせたタプルになっているのでlistでリストに変換しています。なのでlistを付けなければ

from itertools import product

twelve_clock_list = [clock for clock in product(hour_list, am_pm)]

print(twelve_clock_list)

>[(1, 'am'), (1, 'pm'), (2, 'am'), (2, 'pm'), (3, 'am'), (3, 'pm'), (4, 'am'), (4, 'pm'), (5, 'am'), (5, 'pm'), (6, 'am'), (6, 'pm')]

となります。そのため、タプルの値を直接受け取って

from itertools import product

for
hour, twelve in product(hour_list, am_pm):
print(hour, twelve)

>1 am
  1 pm
  2 am
  2 pm
  3 am
  3 pm
  4 am
  4 pm
  5 am
  5 pm
  6 am
  6 pm

ってなことも出来ます。

 

これで、大分スッキリ書けるのではないであろうか。