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]