day13---迭代器和生成器

1. 迭代器

  • 可迭代对象定义:内部含有__iter__方法的数据类型就是可迭代的,又叫做可迭代对象。

  • 迭代器定义:内部含有__next__方法的可迭代对象就叫做迭代器。

  • 过程:把迭代器当作一个容器,从容器中一个一个把值取出的过程就是迭代的过程

  • 迭代器是可迭代对象多一个__next__方法

  • 迭代器是可迭代对象的一部分

  • 获取迭代器:可迭代对象调__iter__()

  • 使用迭代器:迭代器__next__()

  • 迭代器一定是可迭代对象,但是可迭代不一定是迭代器。
  • 迭代器的特点:

    • 惰性运算
    • 从前到后一次去取值,过程不可逆,不可重复
    • 节省内存
  • 判断是否是可迭代对象:

lst = [1, 2, 3]
# 方法一:
print('__iter__' in dir(lst))
# 方法二:
from collections import Iterable
print(isinstance(lst, Iterable))
>>>
True
True
  • 判断是否是迭代器:

lst = [1, 2, 3]
lst_iter = lst.__iter__()
# 方法一:
print('__next__' in dir(lst_iter))
# 方法二:
from collections import Iterator
print(isinstance(lst_iter, Iterator))
>>>
True
True
  • for循环就是依赖的迭代器,还有range()、enumerate()、文件操作f()等也是迭代器

  • 使用迭代器取值,就不需要关心索引下表或者key的问题了

2. 生成器

  • 自己编写的、自定义的迭代器就叫做生成器

  • 生成器的本质就是迭代器

  • 生成器的所有优点跟迭代器一样

  • 生成器的实现方式:

    • 生成器函数
    • 生成器表达式
  • 生成器函数和普通函数的区别:

    • 生成器函数中含有yield关键字
    • 生成器函数调用的时候不会立即执行,而是返回一个生成器
    • 生成器函数中print也不会立即执行
    • yield关键字不会终止函数的运行

3. 生成器的应用

  • send可以给生成器传值

  • 在生成器函数中,从哪个yield开始接着执行,就把值传给那个yield

  • send不能用在第一个触发生成器

  • 生成器函数中有多少个yield,就必须要有多少个__next__+send

  • send和__next__的不同就是send可以传值,而__next__不能传值

  • g.__next__还可以写成next(g)

  • g.___next__还可以携程iter(g)

# 生成器监听文件输入的例子,类似于linux命令下的tail -f命令,用于动态查看文件内容
import time
def tail(filename):
        f = open(filename)
        f.seek(0, 2)
        while True:
            line = f.readline()
            if not line:
                time.sleep(0.1)
                continue
            yield line
g = tail('shell.txt')
for line in g:
        print(line)
>>>(此时一直处于死循环状态,文件如果有新增内容,终端会显示出来)
# 使用生成器计算平均值,类似于七日年化利率(利滚利的应用)
def averager():
        total = 0.0
        count = 0
        average = None
        while True:
            term = yield average
            total += term
            count += 1
            average = total/count
g = averager()
g.__next__()
print(g.send(20))
print(g.send(10))
print(g.send(30))
print(g.send(100))
print(g.send(240))
>>>
20.0
15.0
20.0
40.0
80.0
# 使用生成器的预激装饰器,实现平均值的计算
def wrapper(func):
        def inner():
            g = func()
            g.__next__()
            return g
        return inner
@wrapper
def averager():
        total = 0.0
        count = 0
        average = None
        while True:
            term = yield average
            total += term
            count += 1
            average = total/count
g = averager()
print(g.send(20))
print(g.send(10))
print(g.send(30))
print(g.send(100))
print(g.send(240))
>>>
20.0
15.0
20.0
40.0
80.0

4. 生成器进阶

生成器的触发执行方式有:next、send、for循环。其中next和send执行几次就取到生成器中的几个值,如果已经取到生成器中的最后一个值还接着运行,会报错;而for循环每次取一个值,直到取完为止,不会报错

# 使用生成器yield from分别取出A、B、C、D
def func():
        a = 'AB'
        b = 'CD'
        yield from a+b
g = func()
for i in g:
        print(i)
>>>
A
B
C
D
def func():
        yield from 'AB'
        yield from range(3)
print(list(func()))
>>>
['A', 'B', 0, 1, 2]
posted @ 2017-11-03 15:20  _岩哥  阅读(103)  评论(0)    收藏  举报