2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Pythonらしいコードを書く(ループ編)

Posted at

はじめに

Pythonを学習するにあたって、せっかくなので、Pythonらしい書き方というものを調べてみました。
Pythonらしさは、Pythonの効率の良いコードと同意だからです。

参考にしたスライド
https://speakerdeck.com/pyconslides/transforming-code-into-beautiful-idiomatic-python-by-raymond-hettinger-1

ループ

インデックスを使った昔ながらのループから、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の forforEach なので、以下のように書くと良いです。
また、ループ中の変数については、リストを複数形に、リストの要素を単数形にすることで、何がループしているのかをわかりやすくしておくことがお作法です。

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
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?