SAIG の佐藤尭彰です。最近は業務で Python ばっかり書いています。
今回は Python連載 の第4回目で、Python の中でも「なんとなく」で扱われがちなイテレータについてです。
イテレータとは
あるコンテナの中の要素に1つずつアクセスできるオブジェクト。
もう少し 公式 から引用すると、
(
iter()
) 関数は、コンテナの中の要素に1つずつアクセスする__next__()
メソッドが定義されているイテレータオブジェクトを返します。
つまり、コンテナの中身を1つずつ返す __next__()
メソッドを持つ (ようなオブジェクトを返す __iter__()
関数を持つ) ことがイテレータの本質です。
list
などのシーケンスと異なり、実態として中身が存在する必要がありません。これを実装するための1手段が ジェネレータ や ジェネレータ式 であり、返すべき値はこれらを呼び出すたびに都度計算してよいのです。このイテレータの性質から、イテレータを使えるところ (= iterable
を要求されるところ) にシーケンスでなくイテレータを渡すとメモリや実行時間を削減できます。
一方でイテレータは実際に値が帰ってくるまでは中身が確定しません。確定させるためには list
もしくは tuple
などにキャストする必要があります。
print(map(lambda x: x**2, range(5))) |
▲ ざっくり printf デバッグをするときに忘れがちな list()
組み込みのイテレータ
組み込み = import なしに使える、標準のもの
よく見るもの
ここは使いこなしているユーザが多いのではないでしょうか。
map(func, iterable)
: 第2引数に第1引数を作用させたものを返すfilter(func, iterable)
: 第2引数に第1引数を作用させた結果が真値となる要素のみを返すenumerate(iterable)
: インデックスと中身のタプルを返すzip(iter1, iter2, ...)
: それぞれの iterable の i 番目からなるタプルを返す- 長さがまちまちなときは最短なもので止まる
あまり見ないけど便利なもの
本題その1です。
filter(None, iterable)
- 第1引数に
None
を渡すと、iterable内の要素自体が真値を返すような要素のみを返します - つまり「
None
,False
,0
(と等価なもの),''
,[]
, etc. 」を除くことができます。0が消えることを除いては かなり使い勝手がよく、無為なif
文でインデントを1つ掘るよりも見通しの良いコードを書くことができます
- 第1引数に
# やりがちな例 |
reversed(seq)
- いわゆるリバースイテレータを返します。逆順にしたコピーを返す
[::-1]
よりも軽くて便利なことが多いです - 一方で、(事実上)引数
seq
はlist
かtuple
である必要があります
- いわゆるリバースイテレータを返します。逆順にしたコピーを返す
iter(callable, sentinel)
: 2引数版iter
- sentinel と一致するまで callable を叩いた返り値を返します
- 文字通りの番兵がいるようなテキストデータ・バイナリデータをパースするときに役に立つ(かも)
itertools
本題その2です。
import することでいろいろなイテレータが作れます。どこで使うんだと言うのもありますが、大体はいつか使える関数です。
主な無限イテレータ
count(start[, step=1])
: stop がない無限range
cycle(iterable)
:cycle('ABCD') --> A B C D A B C D A B C D ...
- だいたい
zip
などの 一番短いやつに揃えて止まる 系ジェネレータを止めたくないときに使います
- だいたい
主な(普通の)イテレータ
accumulate(p[, func])
- 累積和。
np.cumsum(v)
- このほか第2引数 func は幅広い二項演算を取ることができるため
np.cumprod
にしたり累積max
したり色々できます
- 累積和。
chain.from_iterable(iterable)
- 2重のネストに限定された
np.ravel
です- ネストされていない要素が混じっていたり、3重ネスト以上を平坦化したい場合はおとなしく
collections.abc.Iterable
かどうかを判定するしかないようです
- ネストされていない要素が混じっていたり、3重ネスト以上を平坦化したい場合はおとなしく
- 2重のネストに限定された
from itertools import chain |
▲ こんな感じで組むと flatten
できる
groupby(iterable[, key])
- 前から見ていって
key
が一致するような要素集合を (key
,要素集合
) の形で返します - iterable がソート済ならばだいたい
df.groupby
ですが、ソートされていないとkey
が変わるたびにブロックを返すので敢えてそれを利用する使い道もあります- タイムスタンプ順にそろえておいて、同じ人の連続ログをひとまとめにして見たい、など
- C++ の
uniq
と同じような動作です
- 前から見ていって
islice(iterable[, start], stop[, step])
iterable[start:stop:step]
reversed
同様、コピーを作らないのでメモリに優しいです
takewhile(pred, seq)
pred
が偽になったら終了するイテレータ- リスト内包でbreakしたくなったらこれを思い出して下さい
おわりに
標準ライブラリを上手に使って快適な Python ライフを。
次回は10月7日、小橋昌明さんの pandasの内部で何が起きているか です。