はじめに
Pythonを学習するにあたって、せっかくなので、Pythonらしい書き方というものを調べてみました。
Pythonらしさは、Pythonの効率の良いコードと同意だからです。
ループ
インデックスを使った昔ながらのループから、Pythonらしい文法でループを表現します。
整数のループ
Javaなどの基本的なfor文 for (i=0; i<6; i++)
はPythonにはありません。
代わりに数値の配列を使います。
for i in [0, 1, 2, 3, 4, 5]:
print(i ** 2)
整数の範囲なら range
を使うのがPython風なので、置き換えます。
Python3の range
はメモリ確保しない動作をしますが、それ以前は値が大きい場合にメモリを消費してしまうので、 xrange
がおすすめです。
for i in range(6):
print(i ** 2)
コレクションのループ
最近ではコレクションを使った簡単なループがあるのであまりやりませんが、プログラムを学習しはじめたばかりだと、ついインデックスを使いたくなってしまいます。
colors = ['red', 'blue', 'yellow', 'green', 'pink']
for i in range(len(colors)):
print(colors[i])
これは理解しやすいのですが、どうしても複雑になってしまって読みにくいコードになってしまいます。
Pythonの for
は forEach
なので、以下のように書くと良いです。
また、ループ中の変数については、リストを複数形に、リストの要素を単数形にすることで、何がループしているのかをわかりやすくしておくことがお作法です。
colors = ['red', 'blue', 'yellow', 'green', 'pink']
for color in colors:
print(color)
逆順にリストをループする
逆順にするには、リストを逆順にする処理 reversed
を加えることで実施します。
なにがおきているかわかりやすいです。
colors = ['red', 'blue', 'yellow', 'green', 'pink']
for color in reversed(colors):
print(color)
要素といっしょに要素番号(インデックス)を取得する
これは結構必要なパターンで、要素と同時にインデックスがとれるものです。
順序を表示したいときは少なくありません。
colors = ['red', 'blue', 'yellow', 'green', 'pink']
for i, item in enumerate(colors):
print(i, '-->', item)
2つのリストを同時にループする
少ない方のリスト分ループします。
昔ながらなの方法だとインデックスを使う方法がわかりやすいかと思います。
colors = ['red', 'blue', 'green', 'yellow', 'pink']
names = ['aka', 'ao', 'midori']
n = min(len(colors), len(names))
for i in range(n):
print(names[i], '-->', colors[i])
Pythonではzip
を使って簡単に表現できます。
colors = ['red', 'blue', 'green', 'yellow', 'pink']
names = ['aka', 'ao', 'midori']
for name, color in zip(names, colors):
print(name, ' => ', color)
zip
は扱うリストのサイズが大きいとメモリを大きく確保するという問題もあります。
python3
では改善されているようですが、それ以前のバージョンの場合はitertools
モジュールにあるizip
に置き換えると良いようです。
ソートする
要素をソートしてループします。逆順も可能です。
colors = ['red', 'blue', 'green', 'yellow', 'pink']
# 昇順
for color in sorted(colors):
print(color)
# 逆順
for color in sorted(colors, reverse=True):
print(color)
ソート順をもっと複雑に
ソート順をもっと複雑にしたい場合は関数を割り当てることも可能です。
戻り値の大きさでソート順が決まります。
colors = ['red', 'blue', 'green', 'yellow', 'pink']
def compare_length(c1):
return len(c1)
print(sorted(colors, key=compare_length))
長さ順なら関数を定義するような面倒なことをせず、直接len
を指定することも可能です。
また、key
にはラムダ式を指定することもできます。
print(sorted(colors, key=len))
print(sorted(colors, key=lambda x:len(x)))
番兵(Sentinel)
番兵とは、リスト処理方法の一つです。
リストの最後に特定の値(番兵)を追加することで、構造をシンプルにする手法です。
たとえばファイルの内容をすべてリストに詰め込むとき、読み込んだブロックが空であるかをチェックし、空の場合はファイルの最後尾と判断するようなロジックがあります。
これを理解しやすい方法で書くとこんな感じになります。
from functools import partial
blocks = []
with open('mydata.db', 'rb') as f:
while True:
block = f.read(32)
if block == '':
break
blocks.append(block)
番兵はPython
ではこんな風に書けます。
from functools import partial
blocks = []
with open('mydata.db', 'rb') as f:
for block in iter(partial(f.read, 64), b''):
blocks.append(block)
ループの終了条件
検索系の関数の場合に、検索条件に合致しない場合に「Not Found」を返すといったように、リストの最後まで一致するものがないときに例外値を返したいときがあります。
これまでは以下のようなコードを書いていました。
def find(seq, target):
found = False
for i, value in enumerate(seq):
if value == tgt:
found = True
break
if not found:
return -1
return i
found
フラグは、target
が見つかった時にTrue
になる変数ですが、このようなフラグ変数が入ると、コードは読みづらくなります。
このような場合にPython
は、for
文の中でelse
を使うことでフラグを使わない構文が作成できます。
エレガントです。
def find(seq, target):
for i, value in enumerate(seq):
if value == tgt:
break
else:
return -1
return i