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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 迭代器和生成器 楔子 假如我现在有一个列表l=['a','b','c','d','e'],我想取列表中的内容,有几...
    go以恒阅读 778评论 1 4
  • PYTHON-进阶-ITERTOOLS模块小结转自wklken:http://wklken.me/posts/20...
    C_Y_阅读 1,082评论 0 2
  • 包(lib)、模块(module) 在Python中,存在包和模块两个常见概念。 模块:编写Python代码的py...
    清清子衿木子水心阅读 3,845评论 0 27
  • 接口定义 Iterable只是返回了Iterator接口的一个实例,这里很是奇怪,为什么不把两个接口合二为一,直接...
    IT_Matters阅读 14,354评论 4 13
  • 三个月后,李齐言很快地适应了山中生活,唐唐的日子也比以往好过不少,李齐言虽然破阵无方,但好歹是个剑客,欺负一下山中...
    执书阅读 515评论 0 0