LoginSignup
1
1

More than 5 years have passed since last update.

python3x: iterables vs iterators

Last updated at Posted at 2016-04-13

授業で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 
  1. iterを呼びiteratorオブジェクト作成しthe_iteratorと呼ばれる変数に保存
  2. 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 selfreturn 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を残し且つ同じ結果を出せるようにしたいのだが現在試行錯誤中。。

参考にしたリンク

1
1
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
1
1