摘要

重点是两个协议,协议一__iter__和协议二__next__PEP 234

  • 可迭代对象(Iterable) 定义:实现了__iter__方法的对象 用途:iter函数调用__iter__方法,将对象转换为迭代器 备注:可迭代对象的意思是指其可以变成一个迭代器,且每次重新计数;
  • 迭代器(Iterator) 定义:实现了__iter__方法和__next__方法 用途:取数据 备注:迭代器也实现协议一,是为了让迭代器表现的像序列(在for循环中),其返回自身,即意味着不会重新计数。
  • 生成器(generator) 定义:列表生成器和使用yield定义生成器函数 用途:产生数据

Iterable

只要实现了__iter__方法的对象,即为Iterable。

Python常见可迭代对象

  • 集合或序列类型(list、tuple、set、dict、str)
  • 文件对象

注意

  1. 虽然实现了__iter__方法即为Iterable对象,但是若想在for循环中正常使用,就要保证__iter__方法正确返还Iterator对象。
  2. 即使不实现__iter__方法即不是可迭代对象,也可以在for循环中正常使用,需要对象实现__getitem__方法。

原因是,for循环中使用了iter函数来转换,iter函数优先调用对象的__iter__方法,否则调用__getitem__方法,两种方法均可以转换为迭代器对象,以下是iter()函数的Python等价实现:

def my_iter(iterable):
    if hasattr(iterable, '__iter__'):
        return iterable.__iter__()
    elif hasattr(iterable, '__getitem__'):
        index = 0
        while True:
            try:
                value = iterable.__getitem__(index)
                index += 1
                yield value
            except IndexError:
                break
    else:
        raise TypeError('Not iterable')

正确实现了__iter__方法的可迭代对象:

class IterObj:

    def __init__(self):
        self.a = [3, 5, 7, 11, 13, 17, 19]

    def __iter__(self):
        return iter(self.a)

实现了__getitem__方法的对象亦可以通过iter函数转换为生成器:

class IterObj:

    def __init__(self):
        self.a = [3, 5, 7, 11, 13, 17, 19]

    def __getitem__(self, i):
        return self.a[i]

it = IterObj()
print(isinstance(it, Iterable)) # false
print(isinstance(it, Iterator)) # false
print(isinstance(it, Generator)) false
print(hasattr(it, "__iter__")) # false
print(iter(it)) # <iterator object at 0x10b231278>

for i in it:
    print(i) # 将打印出3、5、7、11、13、17、19

Iterator

迭代器一定是可迭代对象,且额外实现了__next__方法

定义:

  • 实现__next__方法,要么返回下一个值,要么返回StopIteration 或其子类;
  • 实现__iter__方法,返回self
class IterObj:

    def __init__(self):
        self.a = [3, 5, 7, 11, 13, 17, 19]

        self.n = len(self.a)
        self.i = 0

    def __iter__(self):
        self.i = 0
        return self

    def __next__(self):
        while self.i < self.n:
            v = self.a[self.i]
            self.i += 1
            return v
        else:
            self.i = 0
            raise StopIteration()

Python中很多接口是通过调用next() 函数来交互的,如for 循环、map()等,所以对于Iterable对象,要通过内置的iter方法,将其转换为Iterator

IterableIterator 在用法上的区别在于,Iterable可以通过iter() 生成多个/多次不同的Iterator,每次重头计数。而一个Iterator 即使被iter()调用,返回的仍是自己,意味着计数不清零。

Generator

生成器既是可迭代对象,又是迭代器。 生成器有两种定义方式:

  • 列表生成器
  • 使用yield定义生成器函数

列表生成器:

    g = (x * 2 for x in range(10)) # 0~18的偶数生成器 
    print(isinstance(g, Iterable)) # true
    print(isinstance(g, Iterator)) # true
    print(isinstance(g, Generator)) # true
    print(hasattr(g, "__iter__")) # true
    print(hasattr(g, "__next__")) # true
    print(next(g)) # 0
    print(next(g)) # 2

生成器函数:

def gen():
    for i in range(10):
        yield i 

生成器的嵌套

假设有三个生成器A、B和C,试图通过A取B的数据,B取C的数据,C取最原始的数据,即实现三个生成器的嵌套,该如何操作?

可以通过yield from实现生成器的嵌套。

def generatorC():
    for i in range(5):
        yield i

def generatorB():
    yield from generatorC()

def generatorA():
    yield from generatorB()

# 使用生成器A来获取生成器C的输出
for num in generatorA():
    print(num)

通过yield from实现嵌套的两个生成器,分别是委托生成器和子生成器。其中委托生成器,只起一个桥梁作用,它建立的是一个双向通道,它并没有权利也没有办法,对子生成器yield回来的内容做拦截,而是将子生成器yield的数据传递给调用方。

如果希望委托生成器可以修改子生成器,即A可以修改B的数据,B可以修改C的数据,该如何操作?

此时不再适用yield from,而是通过yield

def generatorC():
    for i in range(5):
        yield i

def generatorB():
    for num in generatorC():
        num = do_something(num)
        yield num

def generatorA():
    for num in generatorB():
        num = do_something(num)
        yield num

# 使用生成器A来获取最终的输出
for num in generatorA():
    print(num)

<
Previous Post
装饰器
>
Next Post
错误、异常和警告