2015年11月21日

itertools.islice

itertools.islice はイテレータオブジェクトを作成する関数です。その使い方と使い所について紹介します。


以下は整数のリストと、前後の要素の差を計算するソースコードです。

# 整数のリスト
>>> data = [1, 4, 2, 8, 5, 7]

# 差を計算
>>> for i in range(1, len(data)):
...     print(data[i] - data[i-1])

上のループ文の書き方は、Pythonicさがあまり感じられませんね。[]に index値を入れて走査するのは Python 的ではありません。

Python らしいループ文は以下のようになります。

>>> for i, j in zip(data[1:], data):
...     print(i - j)

zip とスライスを使い、かなりPythonicになりました。
しかし、この書き方には問題があります。スライスはコンテナの複製を作成します(いわゆるディープコピー)。このため、 data のサイズが大きい場合には、data[1:] が大きなメモリ負荷となる可能性があります。

data を複製するのではなく data からイテレータオブジェクトを作成すれば、この問題は解消されます。イテレータオブジェクトを利用する方法は以下のようになります。

>>> n = iter(data)
>>> n.__next__()
>>> for i, j in zip(n, data):
...     print(i - j)

メモリ負荷の問題は解消しましたが、ループ文のための準備が2行あり、あまりCoolな書き方ではありませんね。ここでは省略していますが、安全のためには data が空リストだった場合の処理を追加する必要があります。その処理も追加すれば、ますますCoolではなくなります。


ここで、ようやく itertools.islice の出番です。ここまで述べてきた問題を全て解決するのが、itertools.islice です。
その使い方は以下のようになります。

>>> from itertools import islice

>>> for i, j in zip(islice(data, 1, None), data):
...     print(i - j)

[]でのリストへのアクセスがなくなり、余計なメモリ負荷が無く、ループ文自体を1行で書ける。まさにCoolでPythonicな書き方だと思います。

0 件のコメント:

コメントを投稿