生成器Generator
生成器函数
一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束
def func():
print('hello')
yield 1
g = func() #调用生成器函数
print(g) #g是一个generator生成器
结果:
<generator object func at 0x000001F22C0D0EB8>
#调用就不会执行这个函数,而是返回一个生成器
print(dir(g)) #g是一个迭代器,所以所有的生成器都是迭代器
a = g.__next__()
print(a)
结果:
hello
1
事例二:next本质就是调用__next__
def genrator_fun1(): #1 #5
a = 1 #6
print('现在定义了a变量') #7
yield a #8 #10
b = 2 #11
print('现在又定义了b变量') #12
yield b #13
g1 = genrator_fun1() #2
#打印g1可以发现g1就是一个生成器
print('g1 : ',g1) #3
print(next(g1)) #4
print(next(g1)) #9
结果:
g1 : <generator object genrator_fun1 at 0x00000195435B0EB8>
现在定义了a变量
1
现在又定义了b变量
2
yield关键字的特点: 可以记录当前函数中执行的位置,下一次继续执行
next和yield是一对搭档 : next开始函数的执行 yield停止函数的执行
生成器函数最大的特点:
调用之后不执行,需要next来触发这个函数继续向下执行
一个函数中可以有多个yield
def func():
yield 1 #停止符,也是一个记录符(到这里num1就执行完了返回结果给num1)
print(123) #接着上次的代码开始执行
yield 2 #遇到yield又停止了,返回结果给num2
for i in range(2):
print(i)
yield 3
g = func()
num1 = g.__next__()
print(num1)
num2 = g.__next__() #num1函数执行完后就开始执行num2
print(num2)
num3 = g.__next__()
print(num3)
结果
1
123
2
0
1
3
事例二
def f():
print(111)
yield 1
print(222)
yield 3
print(333)
yield 5
f1 = f()
for i in f1:
print('*'*i)
# num1 = next(f1)
# num2 = next(f1)
# num3 = next(f1)
# print(num1,num2,num3)
结果
111
*
222
***
333
*****
执行效率对比,明显yield一次取10个数比下边这个一次生成所有数据,然后再从所有的数据中判断取值效率要高的多
def func1():
for i in range(10000000):
yield (i+1)%10
g = func1()
for i in range(10):
print(g.__next__())
----------------------------
def func2():
new_l = []
for i in range(10000000):
new_l.append((i+1)%10)
return new_l
ret = func2()
count = 0
for i in ret:
if count < 10:
print(i)
else:
break
count+= 1
iter_ret = ret.__iter__()
for i in range(10):
iter_ret.__next__()
#做2000001件衣服,前两次每次输出1件,后边一次输出80件
def get_cloth():
for i in range(1,2000001):
yield '第%s件衣服'%i
g = get_cloth()
print(g.__next__()) #这是我已经执行过的第1件衣服
print(g.__next__()) #这是我已经执行过的第2件衣服
for i in range(80):
print(g.__next__()) #下面我开始一次要80件就从3件开始执行82结束
# 监听文件的输入,对于文件中随时输入的内容进行自动化分析/自动化展示
def get_line(f):
f = open('f',encoding='utf-8')
while True:
line = f.readline().strip()
if not line:
continue
yield line
line_g = get_line(file)
for line in line_g:
print(line.split(','))
总结
可迭代对象:
拥有__iter__方法
特点:惰性运算
例如:range(),str,list,tuple,dict,set
迭代器Iterator:
拥有__iter__方法和__next__方法
例如:iter(range()),iter(str),iter(list),iter(tuple),iter(dict),iter(set),reversed(list_o),map(func,list_o),filter(func,list_o),file_o
生成器Generator:
本质:迭代器,所以拥有__iter__方法和__next__方法
特点:惰性运算,开发者自定义
使用生成器的优点:
1.延迟计算,一次返回一个结果。不会一次生成所有的结果,这对于大数据量处理,将会非常有用
2.提高代码可读性
send 获取下一个值的效果和next基本一致
只是在获取下一个值的时候,给上一yield的位置传递一个数据
send使用的注意事项
第一次使用生成器的时候,是用next获取下一个值
最后一个yield不能接受外部的值
def f():
print(111)
# smell = yield 1 #ret把000传递给yield位置上即smell=000
# print(smell)
yield 1 #ret的yield返回值1
print(222)
smell2 = yield 2
print(smell2) #这个是最后一个yield不接受外部的参数所以没有返回值
yield 3
g = f()
print(g.__next__())
ret = g.send('000') #传一个值到生成器里
print(ret)
ret2 = g.send('ccc')
print(ret2)
结果
111
1
222
2
ccc
3
如果启用上边的两行结果
111
1
000
1
222
2
yield from生成器函数调用从中取值?
def func():
l1 = [1,2,3]
s2 = 'abc'
l1.extend(s2)
print(l1)
for i in l1:
yield i
g = func()
for i in g:
print(i)
结果:
[1, 2, 3, 'a', 'b', 'c']
1
2
3
a
b
c
事例二
def func():
l1 = [1,2,3]
s2 = 'abc'
yield from l1 #from循环的意思,相当于for
yield from s2
g = func()
for i in g:
print(i)
执行结果
1
2
3
a
b
c
事例三
def func():
l1 = [1,2,3]
s2 = 'abc'
for a in l1:
yield a
for j in s2:
yield j
g = func()
for i in g:
print(i)
事例四
def gen2():
yield from 'AB'
yield from range(3)
print(list(gen2()))
等同
def gen1():
for c in 'AB':
yield c
for i in range(3):
yield i
print(list(gen1()))
float 浮点数
1.2
2.3333344
2.56789*10**2 = 256.789
为什么要叫浮点数 : 因为小数点是浮动的
浮点数能表示的数学上的数字: 有理数(有限小数 无限循环小数)
浮点数的小数点是不准确的: 小数的小数位是转换成二进制存储
如果你创建的变量本身带小数点,那么这个变量的数据类型直接就是浮点数
所有的除法(除了//)得到的都是小数
浮点数如果太长请转换成字符串存储
a = '1.71264864975975073507505'
除法
python2.x 整数除以整数就会取整(向下取整),有一个数浮点数,就按照浮点数计算
python3.x 所有的除(除了//)的结果都是小数
生成器函数中
send/__next__ 生成器函数之外用的
yield/yield from 生成器函数之内用的
next+send == yield
如果函数中的yield要接收参数,那么应该使用send传值
如果函数中的yield不需要接收参数,那么应该使用next即可
生成器和迭代器是一样的,内部的值都只能取一次
从生成器中取值的方式也和迭代器是一样的:
for
next/send
数据类型的强制转换(list)
面试题
例题一
def demo():
for i in range(4):
yield i
g=demo()
g1=(i for i in g)
g2=(i for i in g1)
print(list(g1))
print(list(g2)) # 生成器中的数据只能取一次,去完就没有了
结果
[0, 1, 2, 3]
[]
例题二
def demo():
for i in range(3):
yield i
g=demo() #g=0,1,2
g1=(i for i in g)
g2=(i for i in g1)
print(g2.__next__()) #0 #谁先取值就给谁
print(g2.__next__()) #1
print(g1.__next__()) #2,因为g2把前两个数给打印出来了,所以只剩下2,就给了g1
执行结果
0
1
2
列表推导式和生成器表达式
sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以可以直接这样计算一系列值的和
生成器表达式:几乎不占内存
print(sum(x ** 2 for x in range(10000000)))
#而不用多此一举的先构造一个列表:内存占用大,机器容易卡死
#print(sum([x ** 2 for x in range(10000000]))
列表推导式
应用场景:当已经有了一个列表,从这个列表中的每一个元素都需要做某个操作,并且需要将操作的结果放在一个新的列表中,适合使用列表推导式
新的列表 = [每一个元素要做的操作 for 列表中的每一个元素 in 列表]
循环/列表转换成列表推导式
例题一
l = [1,2,3,4,5,6]
new_l = []
for i in l:
new_l.append(i*i) #[1,4,9,16,25,36]
print(new_l)
#列表推导式
new_l2 = [i*i for i in l]
print(new_l2)
等同
l = [1,2,3,4,5,6]
print([i*i for i in l])
例题二
new_lst = []
for i in range(2,20):
new_lst.append(i//3)
print(new_lst)
列表推导式
lst = [i//3 for i in range(2,20)]
print(lst)
例题三
#请计算0-100内能被3整除的所有数字
new_lst = []
for i in range(0,10):
if i%3 == 0:
new_lst.append(i)
print(new_lst)
列表推导式
print([i for i in range(0,10) if i%3 == 0])
生成器表达式(的结果是生成器):
处理比较简单的逻辑
并且能够以更少的代码来节省时间空间
def func():
for i in range(101):
if i%3 == 0:
yield i
g1 = func()
print(g1.__next__())
生成器表达式
g = (i for i in range(0,101) if i%3 == 0)
print(g.__next__())
print(g.__next__())
#列表推导式
egg_list=['鸡蛋%s' %i for i in range(10)]
print(egg_list)
生成器表达式
egg_list=('鸡蛋%s' %i for i in range(10))
print(egg_list)
print(next(egg_list))
# 计算移动平均值的例子(没懂)
#def cal_avg():
# sum_n = 0
# count = 0
# while True:
# if count:
# num = yield sum_n/count # 7/1 num = 9 16/2 = 8 num = 8 24/4 = 8
# else:
# num = yield 0 # num = 7
# sum_n += num # sum_n = 7 + 9 = 16 +8 = 24 +10
# count += 1 # count = 1 + 1 = 2 + 1 = 3 + 1
## 7,9,8,10
#g = cal_avg()
#g.__next__() # 0
#avg1 = g.send(7)
#print(avg1)
#avg2 = g.send(9)
#print(avg2)
#avg3 = g.send(8)
#avg3 = g.send(10)
#def add(n,i):
# return n+i
#def test():
# for i in range(4):
# yield i
#g=test() #g= 0,1,2,3
#for n in [1,10]: #n=1,10
# g=(add(n,i) for i in g) #g= 0,1,2,3所以i=0,1,2,3
#print(list(g)) #因为add=n+i,所以循环第一次就等于0+10,第二次就等于1+10
执行结果
[20, 21, 22, 23]
python迭代器-生成器-列表推导式-生成器表达式-day11-12
猜你喜欢
转载自www.cnblogs.com/linuxcbb/p/9910606.html
今日推荐
周排行