初めてのfor文
多くのPython教材のfor文の章は、
n = 10
for i in range(n):
print(i)
と書くと「0〜9が出力できる!」というところからはじまる。
次に、「"Hello, World!"
を1文字ずつ出力するにはどうすればいいか?」という問題に直面すると、
hoge = "Hello, World!"
for i in range(len(hoge)):
print(hoge[i])
と書く人が多い気がする。
もし以前に「文字列はhoge[i]
と指定することでi
番目を取り出せる」ことを知っていると、「0〜9が出力できる!」の2つの知識が合わさって上のコードが完成する。
しかし、私は
hoge = "Hello, World!"
for c in hoge:
print(c)
の書き方を強く推したい!
なぜ?
正直、「見た目がすっきりするから」(逆にいうと、range(len(hoge))
は見た目がごちゃごちゃするから)でしかない。しかし、感じ方は人それぞれなのでもう少し説得力のある理由を無理やりひねり出してみた。
- 速い
- 変数名に意味が生じる
- 変数を分けられる
- コードに一貫性が生じる
速い
hoge = "Hello, World!"
としてjupyterで速度を測定すると、
# In[1]:
%%timeit
for i in range(len(hoge)):
hoge[i] # 出力はしない
# Out[1]:
# 976 ns ± 34.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
# In[2]:
%%timeit
for c in hoge:
c
# Out[2]:
# 294 ns ± 3.83 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
となり、3倍ほど速い。しかし、単位がnsなので実感は特にない。
おそらく、リストの要素の参照に時間がかかっていると思われる。
変数名に意味が生じる
for i in range(len(hoge)):
の場合、i
はhoge
がどんなものであろうとただのインデックスでしかない。しかし、for c in hoge:
と書くと、c
はhoge
の各要素が入ることになる。
例えば、hoge = schools
という2次元配列があり、その要素を全て出力するコードを以下の2通りで書いてみる。
# サンプル1
for i in range(len(schools)):
for j in range(len(schools[i])):
print(schools[i][j])
# サンプル2
for students in schools:
for student in students:
print(student)
サンプル1では、schools[i][j]
と言われても何が出てくるのか分からず、schools
の定義までさかのぼらなければならない。サンプル2では、schools
はstudents
という生徒名簿が入っていて、各学校の生徒student
が1人ずつ出力されるということをなんとなく予測できる。
変数を分けられる
hoge = [("H", "h"), ("E", "e"), ("L", "l"), ("L", "l"), ("O", "o")]
として、for文内で大文字と小文字を別々に使いたい場合、
for upper, lower in hoge:
print(upper)
print(lower)
のように、分けることができる。上の章と同じ理由で、hoge[i][0], hoge[i][1]
とするよりも可読性が上がる。
コードに一貫性が生じる
hoge
がもしイテレータだったとしたらTypeError: object of type 'generator' has no len()
のようなエラーが生じるため、lenは使えない。よって、for i in hoge:
と書かなければならない。ここで、イテレータではないlistやstrなどの場合もfor i in hoge:
と書くとコードに一貫性が生じる。
そもそも、Pythonのfor文はイテレータであってもイテレータでなくても、内部的にはiter(hoge)
でinの後ろに置いたイテラブルオブジェクトはイテレータ化され、next(hoge)
を繰り返すという動作は全く同じである。そのため、listやstrなどの場合もあたかもイテレータであるかのように実装する方が自然に感じる。