迭代器,生成器,可迭代对象

迭代器,生成器

可迭代对象

一个对象如果实现了 __iter__(self), 并且这个方法返回一个迭代器(Iterator),就是一个可迭代对象(Iterable)。

iter() 可以将一个可迭代对象转变成一个迭代器。

一个迭代器一定是可迭代对象,但是一个可迭代对象不一定是迭代器,譬如 list 是可迭代的,但不是迭代器。

from collections.abc import Iterator,Iterable

class it(object):

    def __iter__(self):
        return iter([1,2,3])

ge = it(1)
print(isinstance(ge,Iterable))
print(isinstance(ge,Iterator))

上面例子中的 [1, 2, 3] 是一个列表,它是一个可迭代对象。我们调用 iter([1, 2, 3]) 相当于调用了 [1, 2, 3].__iter__() ,它返回一个迭代器

迭代器

一个类,如果实现了两个方法,就是迭代器:

  1. 实现 __iter__(self) 方法,并返回一个迭代器对象 (实现了这个方法,就是一个可迭代对象
  2. 实现 __next__(self) 方法,返回下一次要迭代的值,如果没有值了,抛出 StopIteration 异常

迭代器能在你调用 next() 方法时返回容器中的下一个值。

举例:

from collections.abc import Iterator,Iterable

class it(object):
    def __init__(self, num):
        self.num = num

    def __iter__(self):
        return self    # 返回自身,因为这个类实现了 __next__ 方法,它本身就是一个迭代器

    def __next__(self):
        self.num += 1
        # 大于10,抛出异常
        if self.num > 10:
            raise StopIteration
        return self.num  # 返回下一次迭代的值

ge = it(1)
print(next(ge))
print(isinstance(ge,Iterable))
print(isinstance(ge,Iterator))

生成器

使用 yield 关键字,可以实现生成器, 而且可以直接使用 next() 来生成它的下一个值。

生成器是一种特殊的迭代器。

class generator(object):
    def __init__(self, num):
        self.num = num

    def __iter__(self):
        while self.num < 10:
            yield self.num
            self.num += 1


ge = generator(1)
print(type(ge))
print(isinstance(ge,Iterable))
print(isinstance(ge,Iterator))

当然,上面是一个生成器类,我们手动实现了 __iter__() 方法,并在方法中使用 yield。

一个更快捷创建生成器的方式是:生成器函数,即在函数使用 yield 关键字:

def generator():
    yield 1
    
g = generator()
print(g)

生成器的 next(), send(), yield from

next(generator) 很简单,就是不停的执行生成器,执行到下一个 yield 处,返回 yield 的数据

send(msg) 可以给生成器发送数据,进行数据交互,然后执行到下一个 yield 处,返回 yield 的数据。如果没有可以用来接收数据的变量,则和 next(generator) 效果一样。生成器没有启动时,想要使用 send() ,必须先发送一个 send(None) ,来启动生成器。因为此时还没有 yield 表达式 (类似赋值语句的等式:value = yield 3; 等号两边都有值)可以来接收值。

yield from <generator> 它可以让两个生成器交互。可以从当前生成器A,进入到另一个生成器B执行,在B中遇到 yield 后,会停在B的此处,下一次 sendnext 会从B生成器的这个位置继续执行,直到此生成器执行完毕后,再逐层回到之前的生成器A。说了这么多其实就是想说:yield from 不会阻塞执行,只是从一个生成器跳转到另一个生成器,yield 才会阻塞执行。

def inner():
    print('enter inner')
    value = yield 2  # 第二次截止到: yield 表达式的右边:yield 2;  # 第三次从yield表达式左边开始,给value赋值:value = send(3) 传递过来的值 3
    print('inner value:',value)
    yield 3  # 第三次返回   # 第四次开始发送数据,但是yield表达式左边没有值来接收,继续向下执行
    return 'inner_return'  # inner() 执行完毕,返回到上一层生成器(抛出异常,并将 return 值作为异常的value属性值)

def outer():
    print('outer entered')
    yield 1   # 第一次运行到这里停止 # 第二次发送"2", 但是yield 表达式左边没有变量来接收,所以send("2")等同于next(o)

    value = yield from inner()  # 进入另一个生成器 inner(); 第四次 inner() 返回值给 value(yield from 会自动处理异常,并将异常的value属性作为返回值)

    yield 4  # 第四次遇到 yield 停止   # 第五次从yield表达式左边赋值开始,但是左边没值,所以继续执行到下一个yield处
    print('back to outer')  
    print('value is :', value)  # 第五次结束,并抛出 StopIteration 异常

# o = outer()
# print(next(o))  # 1. 没啥好说的,往下走
# print(next(o))  # 2. 没啥好说的,往下走到 yield 2
# print(next(o))  # 3. 没发送数据,所以 yield 2 前面的 value 会是 None
# print(next(o))  # 4. inner() 执行结束,返回到调用这个函数的地方
# print(next(o))  # 没啥好说的,不停的往下走

# ==================================================
o = outer()
print(o.send(None))  # o.send(None) 等同于 next(o),第一次必须发送 None,先启动生成器,走到 yield 代码处
print(o.send('2'))  # 尽管发了一个数据:HH,但是第1次 yield 1 前面没有接收这个数据的变量,所以直接走向下一个 yield
print(o.send('3'))  # 上一次 yield 停止的地方:value = yield 2 有个变量可以接收值,所以给 value 赋值,然后走到下一个 yield 处
print(o.send('4'))
print(o.send('5'))

send()的用法:

  1. 如果生成器没有启动,第一次必须 send(None) 或者 next(生成器) 来启动它
  2. 后续 send(value) 时,如果 yield 停止的地方,yield 表达式左边有值(如:left_value = yield 2),则先给 yield表达式 左边的变量赋值(left_value = value);如果 yield 停止的地方,yield 表达式左边没有值(如:yield 2),则直接继续执行代码,此时 send(value) 等同于 next(生成器)

所以,send()的用法和 next(生成器)类似,只不过对于左边有值的 yield 表达式来说,send() 可以先给 yield 左边的变量赋值,然后继续执行代码直到遇到下一个yield停止。

for 循环

for 循环,可以循环迭代器,生成器,可迭代对象。它的原理,就是先执行要循环的对象的__iter__() 方法,获得一个迭代器,然后执行迭代器的 __next__() 方法,并且在循环完毕时,处理一下 StopIteration 异常。虽然我们写函数式的生成器时没有__iter__() 方法,但是它已经内置了这个方法。

譬如:

# 一个函数类型的生成器,依然内置了 __iter__()
def it():
    yield 1

    
ge = it()
print(dir(ge))
i = ge.__iter__()
print(dir(i))
print(next(i))
class it(object):
    def __iter__(self):
        return iter([1,2,3])

# ------- for --------
for i in it():
    print(i)

# ------- 原理 --------
i = it().__iter__()
while True:
    try:
        print(next(i))
    except StopIteration:
        break
posted @ 2021-10-14 10:01  wztshine  阅读(60)  评论(0编辑  收藏  举报