python3x: iterables vs iterators

授業でiteratorとiterablesについて触れていたので忘れないようにちゃちゃっとまとめておく。細かい差異は多々あるが

>_you get an iterator from an iterable and an iterator must also be an iterable._
_iterableからiteratorを取り出すということとiteratorはiterableとしての性質を持っていなければならない。_

ということを頭の片隅にいれておくと脳内処理がスムーズに進むかもしれない。

#目次
- <a href="http://qiita.com/weedslayer/items/5581cbd1276e0e80a855#%E3%81%9D%E3%82%82%E3%81%9D%E3%82%82iteration%E3%81%A8%E3%81%AF">そもそもiterationとは</a>
- <a href="http://qiita.com/weedslayer/items/5581cbd1276e0e80a855#iterator%E3%81%A8%E3%81%AF">iteratorとは</a>
- <a href="http://qiita.com/weedslayer/items/5581cbd1276e0e80a855#iterable%E3%81%A8%E3%81%AF">iterableとは</a>
- <a href="http://qiita.com/weedslayer/items/5581cbd1276e0e80a855#%E4%B8%A1%E8%80%85%E3%81%AE%E9%81%95%E3%81%84">両者の違い</a>
- <a href="http://qiita.com/weedslayer/items/5581cbd1276e0e80a855#iteration%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E4%B8%AD%E3%81%AB%E3%81%82%E3%82%8Biter%E3%82%92%E3%81%84%E3%81%98%E3%82%8B%E3%81%A8%E3%81%A9%E3%81%86%E3%81%AA%E3%82%8B%E3%81%AE%E3%81%8B">iterationクラスの中にあるiterをいじるとどうなるのか</a>

#そもそも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 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`を残し且つ同じ結果を出せるようにしたいのだが現在試行錯誤中。。

#参考にしたリンク

- [Python 3.6.1 Documentation](https://docs.python.org/3/glossary.html#term-iterable)
- [What exactly are Python's iterator, iterable, and iteration protocols?](http://stackoverflow.com/questions/9884132/what-exactly-are-pythons-iterator-iterable-and-iteration-protocols)
- [Iterators and Generators](http://cs61a.org/lab/lab11/)