Python 迭代器和生成器
什么时候迭代器?
现在,我们已经获得了一个新线索,有一个叫做“可迭代的”概念。
首先,我们从报错来分析,好像之所以1234不可以for循环,是因为它不可迭代。那么如果“可迭代”,就应该可以被for循环了。
这个我们知道呀,字符串、列表、元组、字典、集合都可以被for循环,说明他们都是可迭代的。
迭代器 = 可迭代对象.__iner__() 或者iter(可迭代对象),自带一个__next__()方法
迭代器的优势:节省内存,效率高,每次只取一个值,而不需要所有的值都取出来之后再进行计算
from collections import Iterable
l = [1, 2, 3, 4]
t = (1, 2, 3, 4)
d = {1: 2, 3: 4}
s = {1, 2, 3, 4}
print(isinstance(l, Iterable))--->True
print(isinstance(t, Iterable))--->True
print(isinstance(d, Iterable))--->True
print(isinstance(s, Iterable))--->True
迭代器的特性:惰性计算,找他next要值才给,不要不给
生成器(generator):自己写的迭代器就是生成器,两种自己写生成器(迭代器)的机制,生成器函数、生成器表达式
生成器函数:
def cloth_g(num):
for i in range(num):
yield 'cloth%s'%i
g = cloth_g(10)
print(next(g))
print(next(g))
1.凡是带有yield关键字的函数,就是一个生成器函数
2.生成器函数的调用不会触发函数的执行,而会返回一个生成器(迭代器)
3.生成器函数的执行需要用next
生成器监听文件输入的例子:
def lister_file():
with open('tmp') as f1:
while True:
for line in f1:
if line.strip():
yield line.strip()
time.sleep(0.1)
g = lister_file()
for line in g:
print(line)
send,在执行next的过程中,传递一个参数给生成器函数的内部;
想生成器中传递值 有一个激活的过程 第一次必须要用next触发这个生成器
def func():
print(1111)
ret1 = yield 1
print(222,'ret1:',ret1)
ret2 = yield 2
print(3333,'ret2:',ret2)
yield 3
g = func()
print(next(g))
print(g.send('alex'))
print(g.send('金老板1'))
计算移动平均值:
def average():
avg = 0
day = 0
sum = 0
while True:
money = yield avg
sum += money
day += 1
avg = sum/day
g = average()
next(g)
print(g.send(200))
print(g.send(500))
print(g.send(800))
生成器函数: 是python程序员实现迭代器的一种手段
主要特征:在函数体中有yield关键字
调用生成器函数,不会执行这个函数中的代码,只是会获得一个生成器(迭代器)
只有从生成器中取值的时候,才会执行函数内部的代码,且每获取一个数据才得到这个数据的代码
获取数据的方式包括next send for循环(for i in g),数据类型的强制转换列表或者元组(l = list(g))
yield返回值的简单方法,如果本身就是一个可迭代的,且要把可迭代对象中的每个数据返回,可以使用yield from
def func():
yield from range(5)
g = func()
print(next(g))
print(next(g))
print(next(g)) 输入结果为0,1,2
在使用send的时候,在生成器创造出来之后需要进行预激活,这一步可以使用迭代器完成
def init(f):
def inner():
ret = f()
next(ret)#预激活
return ret
return inner
@init
def func():
yield from range(5)
g = func()
print(next(g)) #输出结果为1
生成器表达式:
取出10以内的每个数的平方,添加到列表内
常规操作:
l = []
for i in range(10):
l.append(i**2)
print(l)
列表推导式和生成器表达式
列表推导式:print([i**2 for i in rang(5)])
推导式的套路
之前我们已经学习了最简单的列表推导式和生成器表达式。但是除此之外,其实还有字典推导式、集合推导式等等。
下面是一个以列表推导式为例的推导式详细格式,同样适用于其他推导式。
variable = [out_exp_res for out_exp in input_list if out_exp == 2] out_exp_res: 列表生成元素表达式,可以是有返回值的函数。 for out_exp in input_list: 迭代input_list将out_exp传入out_exp_res表达式中。 if out_exp == 2: 根据条件过滤哪些值可以。
例一:30以内所有能被3整除的数 print([i for i in range(30) if i%3 == 0])--->输出结果[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
例二:30以内所有能被3整除的数的平方 print([i**2 for i in range(30) if i%3 == 0])--->输出结果[0, 9, 36, 81, 144, 225, 324, 441, 576, 729]
例三:找到嵌套列表中名字含有两个‘e’的所有名字print(j for i in names for j in i if count('e') == 2)
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
print(j for i in names for j in i if j.count('e') == 2)
练习题:
例1: 过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母
names = ['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe']
print([i,upper() for i in names if len(i)>3])
例2: 求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元祖列表
[(x,y) for x in range(5) if x%2==0 for y in range(5) if y %2==1]
例3: 求M中3,6,9组成的列表M = [[1,2,3],[4,5,6],[7,8,9]]
l = [row[2] for row in M] ---->列表推导式 print(l) --->[3, 6, 9]
g = (row[2] for row in M)---->生成器表达式 print(g)----><generator object <genexpr> at 0x0000000002411BF8>
for i in g:print(i)
生成器的重点在于,有没有执行,一个生成器从头到尾只能取值一次,在不找他要值的时候始终不执行
当他执行的时候,要以执行时候的所有变量值为准。