授業でiteratorとiterablesについて触れていたので忘れないようにちゃちゃっとまとめておく。細かい差異は多々あるが
you get an iterator from an iterable and an iterator must also be an iterable.
iterableからiteratorを取り出すということとiteratorはiterableとしての性質を持っていなければならない。
ということを頭の片隅にいれておくと脳内処理がスムーズに進むかもしれない。
目次
そもそもiterationとは
簡単に言うと箱のなかから指定した物を繰り返し取り出す操作に近い。下の例などがiterationに該当する。
box = [x for x in range(1,101,2)]
for y in box:
if cond(y):
return y
iteratorとは
An object representing a stream of data. Repeated calls to the iterator’s __next() method (or passing it to the built-in function next()) return successive items in the stream. When no more data are available a StopIteration exception is raised instead. At this point, the iterator object is exhausted and any further calls to its __next() method just raise StopIteration again.
nextとiterを併せ持つ
__next__
と__iter__
(returns self
)の2つのメソッドを持っているオブジェクト。要素を一回につき1個返し、一度限りiterationを通すことが出来る。
mutable object that tracks a position in a sequence advancing on each call to next
nextメソッドによる各呼び出しでiterationしながらシークエンス内の位置を追跡する可変オブジェクトan Iterator is an object with a (__next__)method.
(iterable)とはことなり`nextメソッドを持っている。言い換えればiterableとは異なり
next(iterator)`で要素を返すことが出来る。
the_iterator = iter(something_iterable)
try:
while True:
elem = the_iterator.__next__()
# do something
except StopIteration:
pass
- iterを呼びiteratorオブジェクト作成しthe_iteratorと呼ばれる変数に保存
- StopIterationが出るまで__next__()をiteratorにひたすら呼び続ける
具体的な例
class Naturals():
def __init__(self):
self.current = 0
def __next__(self):
result = self.current
self.current += 1
return result
def __iter__(self):
return self # must return self if a class implements both a next and an iter method.
>>> nats = Naturals()
>>> nats2 = iter(nats) # natsと同じ
>>> nats is nats2
True
iterableとは
普段使っているリスト、ストリング、タプルなどが該当する。
An object capable of returning its members one at a time. Examples of iterables include all sequence types (such as list, str, and tuple) and some non-sequence types like dict, file objects, and objects of any classes you define with an __iter() or __getitem() method.
forループの相棒
for loop
を使って箱から要素を取り出してくるが、iterableはその箱に相当する。
>>> for i in iterable:
#do something
教科書の定義をそのまま引用すると:
iterable represents a sequence and returns a new iterator on each call to
iter
iterableはシークエンスを表し、iter
メソッドを呼び出すたびに新しいiteratorを返す
といっても上記の作業をちょっとカッコよく表現しただけなので特に付け足すことはないです。SOFがより詳細に回答してくれていたので参考にする。
イメージとしてはiterable
自体がiterator
を持っている感じ。an iterator
obj.を an
iterable
から持ってきている。
for
ループはiterable
としか動作しない。iterableはiteratorを返す__iter__
メソッドを(クラス内で)定義されていなければならない。iteratorは__next__
メソッドが定義されていなければならない。よってiteratorもiterableとしてみなされる。
>>> s = 'cat' #iterable
>>> t = iter(s) #iterator
>>> t2 = iter(s) #iterator
>>> next(t)
'c'
>>> next(t)
'a'
>>> next(t)
't'
>> next(t)
Error raise StopIteration
>>> next(t2) # independent of other iterators
'c'
両者の違い
iterable | iterator | |
---|---|---|
__iter__ |
returns a new iterator | must return itself |
__next__ |
N/A | returns the next element or raise StopIteration
|
Analogy: an iterable is like a book (one can flip through the pages) and an iterator is a bookmark (saves the position and can locate the next page). Calling __iter__ on a book gives you a new bookmark independent of other bookmarks, but calling __iter__ on a bookmark gives you the bookmark itself, without changing its position at all.
iterableは本でiteratorはブックマークのようなもの。本に対して__iter__をかけると他のブックマークとは独立した新たなブックマークをその本に挟んでくれる。一方でブックマークに対して__iter__をかけるとその要素の位置関係を変えることなくそのブックマークそのものを返す。
>>> c = 'cat'
>>> next(c)
Error # cはiterableなのでnextを呼べない
>>>c = iter(c)
>>>next(c)
'c'
iterationクラスの中にあるiterをいじるとどうなるのか
>>> class OddNaturalsIterator():
... def __init__(self):
... self.current = 1
... def __next__(self):
... result = self.current
... self.current += 2
... return result
... def __iter__(self):
... return self
>>> odds = OddNaturalsIterator()
>>> odd_iter1 = iter(odds)
>>> odd_iter2 = iter(odds)
>>> next(odd_iter1) #1
>>> next(odd_iter1) #3
>>> next(odd_iter1) #5
>>> next(odd_iter2) #7
next(odd_iter2)
は新たにiteratorを作ってまた1から始まるのかと思っていた。これは__iter__
のreturn self
をreturn OddNaturalsIterator()
に変えれるとその通りの挙動になる。なぜならiteratorを定義するさいiter
メソッドを呼んでいるからである。
class OddNaturalsIterator():
def __init__(self):
self.current = 1
def __next__(self):
result = self.current
self.current += 2
return result
def __iter__(self):
return OddNaturalsIterator()
>>> odds = OddNaturalsIterator()
>>> odd_iter1 = iter(odds)
>>> odd_iter2 = iter(odds)
>>> next(odd_iter1) #1
>>> next(odd_iter1) #3
>>> next(odd_iter1) #5
>>> next(odd_iter1) #7
>>> next(odd_iter2) #1
>>> next(odd_iter2) #3
ただこれには一つ欠点がある。これだと毎回__iter__
がOddNaturalsIterator instance
を作ってしまうので効率的は良くない。なのでreturn self
を残し且つ同じ結果を出せるようにしたいのだが現在試行錯誤中。。