iterable和iterator

事后注: <流畅的python>14章对可迭代对象与迭代器做了区分, 并给出了最佳实践, 最好去看书, 我这里只是很早以前的一个总结, 没有错误但是思路并不清晰.

我们常用的迭代器是个对象,网上常说,它所属的类需要满足以下条件:
1. __iter__返回本身
2. __next__返回下一个元素,如果没有后续元素,则抛出StopIteration异常

一直不是很理解这是为啥,直到我做了一些实验,证明了上面说法是正确的。


首先明确几个概念:python中除了Iterator以外还有个Iterable的概念。对某个实例o的循环则有for e in o:e=next(o)2种方法。

首先给出判断IteratorIterable的代码判断方法:

"""
本代码基于python3.7.0, 后面的代码类似,不再说明
"""

from collections.abc import Iterator, Iterable

a = ...

if isinstance(a, Iterator):
    print('a is Iterator')

if isinstance(a, Iterable):
    print('a is Iterable')
  1. 如果某个类未定义__iter__, 但是定义了__getitem__, 则可以对它的实例做for循环,循环做的是从0开始依次调用__getitem__方法。该实例既不是Iterator, 也不是Iterable.

  2. 如果某个类定义了__iter__, 则可以对它的实例做for循环, 循环做的是先调用__iter__获得返回的对象,再不断调用该返回对象的__next__方法,直到raise StopIteration. 该实例是Iterable.

  3. 如果某个类定义了__next__, 它的实例o可以用next(o)来不断迭代,直至 raise StopIteration. 该实例既不是Iterator, 也不是Iterable.

  4. 如果某个类既定义了__iter__, 又定义了__next__, 它的实例既是Iterator, 也是Iterable.

  5. 如果某个类定义了__iter__, 又定义了__getitem__, 则对该类的实例做for循环时,__iter__的逻辑会遮盖__getitem__的逻辑.

因此,根据2、4两点,最合理的迭代器(Iterator)满足了以下2个特点:
1. __iter__返回self
2. __next__返回下一个元素,如果没有后续元素,则抛出StopIteration异常

这就和开头讲到的定义一致了, 正如python之禅所说的:

There should be one-- and preferably only one --obvious way to do it.
尽量找一种,最好只有一种明显的解决方案。

其实,管它是Iterator还是Iterable, 我们关注的是对象的行为,能够顺畅的进行for或者next迭代,才是最重要的。


顺便补充一下: 生成器generator也支持for或者next迭代。
生成器函数( generator function)是满足以下条件的函数:

  1. 无须return
  2. yield返回下一个元素
  3. 当函数体退出时,迭代完成

由生成器函数调用得到的就是生成器(generator).

我的实验代码如下:

from collections.abc import Iterator, Iterable
import time


class A:
    """"""

    def __init__(self, name='old'):
        self.name = name
        self.start = 1
        self.end = 10

    def __next__(self):
        print(f'call {self.name}.__next__')
        start = self.start
        self.start += 1
        if start <= self.end:
            return start
        raise StopIteration

    def __iter__(self):
        print(f'call {self.name}.__iter__')
        # return A('new')
        return self

    def __getitem__(self, item):
        print(f'call __getitem__({item})')
        time.sleep(1)
        return 5


a = A()
for i in a:
    print(i)

while True:
    try:
        print(next(a))
    except StopIteration:
        break

if isinstance(a, Iterator):
    print('a is Iterator')

if isinstance(a, Iterable):
    print('a is Iterable')

print('*' * 100)
i1 = a.__iter__()
i2 = i1.__iter__()
for x in i1:
    print(x)
print('*' * 100)
for x in i2:
    print(x)
print('*' * 100)
for x in a:
    print(x)

print('+' * 100)


class B:
    def __init__(self):
        self.num = 0

    def __getitem__(self, item: int):
        if item < 10:
            self.num += 1
            return self.num
        raise StopIteration


b = B()
for z in b:
    print(z)
it = iter(b)  # iter(b)保留了b的状态
if isinstance(it, Iterator):
    print('it is Iterator')
if isinstance(it, Iterable):
    print('it is Iterable')
for z in it:
    print(z)
print('it2')
it2 = iter(b)
for z in it2:
    print(z)

l = [1, 2, 3]
it = iter(l)
g = (i for i in range(10))

def consumer(x):
    if x > 10:
        x = yield 5

c = consumer(3)

if isinstance(l, abc.Iterable):
    print('l is iterable')
if isinstance(l, abc.Iterator):
    print('l is Iterator')

if isinstance(it, abc.Iterable):
    print('it is iterable')
if isinstance(it, abc.Iterator):
    print('it is Iterator')

if isinstance(g, abc.Iterable):
    print('g is iterable')
if isinstance(g, abc.Iterator):
    print('g is Iterator')

if isinstance(c, abc.Iterable):
    print('c is iterable')
if isinstance(c, abc.Generator):
    print('c is Generator')
if isinstance(c, abc.Iterator):
    print('c is Iterator')

最后几行输出是

......
l is iterable
it is iterable
it is Iterator
g is iterable
g is Iterator
c is iterable
c is Generator
c is Iterator

你可能感兴趣的:(iterable和iterator)