目录
一、生成器与yield
1.1 如何得到自定义的迭代器
# 在函数内一旦存在yield关键字,调用函数并不会执行函数体代码,会返回一个生成器对象
def func():
print('第一次')
yield 1
print('第二次')
yield 2
print('第三次')
yield 3
print('第四次')
g=func()
# print(g) # <generator object func at 0x0000016B514AE9E0>
# 生成器内置有__iter__和__next__方法,所以生成器本身就是一个迭代器
# g.__iter__()
# g.__next__()
# 因而我们可以用next(生成器)触发生成器所对应函数的执行,然后遇到yield停下来,将yield后的值当做本次调用的结果返回
res1=g.__next__()
print(res1)
res2=g.__next__()
print(res2)
res3=g.__next__()
print(res3)
res4=g.__next__() # 第四次
# StopIteration
len('aaa') # 'aaa'.__len__()
next(g) # g.__next__()
iter(可迭代对象) # 可迭代对象.__iter__()
1.2 小案例分析
# # 应用案列
def my_range(start,stop,step=1):
# print('start...')
while start < stop:
yield start # 函数中出现了yield,调用时已不能执行函数体代码,实际上已变成一个生成器generator
start+=step
# print('end....')
g=my_range(0,3) # <generator object my_range at 0x000001891CC0D9E0>
print(next(g)) # start... 0 若不注释print,触发函数执行直到遇到yield则停止,将yield后的值返回,并在当前位置挂起函数
print(next(g)) # 1 再次调用next(g),函数从上次暂停的位置继续执行,直到重新遇到yield...
print(next(g)) # 2 重复上一步
print(next(g)) # 触发函数执行没有遇到yield则无值返回,即取值完毕抛出异常结束迭代StopIteration
# end...
## 既然生成器对象属于迭代器,那么必然可以使用for循环迭代,如下:
for n in my_range(1,3):
print(n) # 0、1、2
# 总结yield:
# 有了yield关键字,我们就有了一种自定义迭代器的实现方式。
# yield可以用于返回值,但不同于return,函数一旦遇到return就结束了,而yield可以保存函数的运行状态挂起函数,用来返回多次值。
二、yield表达式应用
在函数内可以采用表达式形式的yield
def dog(name):
print('道哥%s准备吃东西啦...' %name)
while True:
# x拿到的是yield接收到的值
x = yield # x = '肉包子'
print('道哥%s吃了 %s' %(name,x))
g=dog('qwert') # 得到生成器对象
# print(g) # <generator object dog at 0x00000296629F7190>
g.send(None) # 等同于next(g),若不传空值进行'初始化',则会报错:TypeError: can't send non-None value to a just-started generator
g.send(['一根骨头','aaa']) # 道哥qwert准备吃东西啦... 道哥qwert吃了 ['一根骨头', 'aaa']
g.send('肉包子') # 道哥qwert吃了 肉包子
g.send('一桶泔水') # 道哥qwert吃了 一桶泔水
g.close()
g.send('1111') # StopIteration 关闭之后无法传值
针对表达式形式的yield,生成器对象必须事先被初始化一次,让函数挂起在表达式x=yield的位置,等待调用g.send()方法为函数体传值,g.send(None)等同于next(g)。
# 我们可以编写装饰器来完成为所有表达式形式yield对应生成器的初始化操作,如下
def init(func):
def wrapper(*args,**kwargs):
g=func(*args,**kwargs)
next(g)
return g
return wrapper
@init
def dog(name):
print('道哥%s准备吃东西啦...' %name)
while True:
x = yield
print('道哥%s吃了 %s' %(name,x))
表达式形式的yield也可以用于返回多次值,即变量名=yield 值
的形式,如下:
def dog(name):
food_list=[]
print('道哥%s准备吃东西啦...' %name)
while True:
# x拿到的是yield接收到的值
x = yield food_list # x = '肉包子'
print('道哥%s吃了 %s' %(name,x))
food_list.append(x) # ['一根骨头','肉包子']
g=dog('qwert')
res=g.send(None) # next(g)
print(res) # 道哥qwert准备吃东西啦... []
res=g.send('一根骨头')
print(res) # 道哥qwert吃了 一根骨头 ['一根骨头']
res=g.send('肉包子')
print(res) # 道哥qwert吃了 肉包子 ['一根骨头', '肉包子']
def func():
print('start.....')
x=yield 1111 # x='xxxxx'
print('哈哈哈啊哈')
print('哈哈哈啊哈')
print('哈哈哈啊哈')
print('哈哈哈啊哈')
yield 22222
g=func()
res=next(g)
print(res) # start..... / 1111
res=g.send('xxxxx')
print(res) # 哈哈哈啊哈 / 哈哈哈啊哈 / 哈哈哈啊哈 / 哈哈哈啊哈 / 22222
三、三元表达式、列表生成式、生成器表达式
3.1 三元表达式
三元表达式是python为我们提供的一种简化代码的解决方案,语法如下:
res = 条件成立时返回的值 if 条件 else 条件不成立时返回的值
针对下述场景
def func(x,y):
if x > y:
return x
else:
return y
res=func(1,2)
print(res) # 2
用三元表达式可以一行解决
x=1
y=2
res=x if x > y else y
print(res) # 2
3.2 列表生成式
列表生成式是python为我们提供的一种简化代码的解决方案,用来快速生成列表。
# 1、列表生成式:
l = ['alex_nb', 'lxx_nb', 'wxx_nb', "xxq_nb", 'egon']
# 取出所有'nb'结尾的组成新的列表
new_l=[]
for name in l:
if name.endswith('nb'):
new_l.append(name)
print(new_l) # ['alex_nb', 'lxx_nb', 'wxx_nb', 'xxq_nb']
new_l=[name for name in l] # 快速生成新的列表,['alex_nb', 'lxx_nb', 'wxx_nb', 'xxq_nb', 'egon']
new_l=[name for name in l if name.endswith('nb')] # ['alex_nb', 'lxx_nb', 'wxx_nb', 'xxq_nb']
# 把所有小写字母全变成大写
new_l=[name.upper() for name in l]
print(new_l) # ['ALEX_NB', 'LXX_NB', 'WXX_NB', 'XXQ_NB', 'EGON']
# 把所有的名字去掉后缀_nb
new_l=[name.replace('_nb','') for name in l]
print(new_l) # ['alex', 'lxx', 'wxx', 'xxq', 'egon']
# 2、字典生成式
keys=['name','age','gender']
dic={key:None for key in keys}
print(dic) # {'name': None, 'age': None, 'gender': None}
items=[('name','egon'),('age',18),('gender','male')]
res={k:v for k,v in items if k != 'gender'}
print(res) # {'name': 'egon', 'age': 18}
# 3、集合生成式
keys=['name','age','gender']
set1={key for key in keys}
print(set1,type(set1)) # {'gender', 'age', 'name'} <class 'set'>
3.3 生成器表达式
创建一个生成器对象有两种方式,一种是调用带yield关键字的函数,另一种就是生成器表达式,与列表生成式的语法格式相同,只需要将[]换成(),即:
(expression for item in iterable if condition)
对比列表生成式返回的是一个列表,生成器表达式返回的是一个生成器对象。对比列表生成式,生成器表达式的优点自然是节省内存(一次只产生一个值在内存中)。
g=(i for i in range(6) if i > 3)
# !!!!!!!!!!!强调!!!!!!!!!!!!!!!
# 此刻g内部一个值也没有
print(g,type(g)) # <generator object <genexpr> at 0x000001F1CE13D9E0> <class 'generator'>
print(next(g)) # 4
print(next(g)) # 5
print(next(g)) # StopIteration
如果我们要读取一个大文件的字节数,应该基于生成器表达式的方式完成
with open('笔记.txt', mode='rt', encoding='utf-8') as f:
# 方式一:
res=0
for line in f:
res+=len(line)
print(res)
# 方式二:
res=sum([len(line) for line in f])
print(res)
# 方式三 :效率最高
res = sum((len(line) for line in f))
# 上述可以简写为如下形式
res = sum(len(line) for line in f)
print(res)
四、叠加多个装饰器(了解)
叠加多个装饰器的加载、运行分析:
def deco1(func1): # func1 = wrapper2的内存地址
def wrapper1(*args,**kwargs):
print('正在运行===>deco1.wrapper1')
res1=func1(*args,**kwargs)
return res1
return wrapper1
def deco2(func2): # func2 = wrapper3的内存地址
def wrapper2(*args,**kwargs):
print('正在运行===>deco2.wrapper2')
res2=func2(*args,**kwargs)
return res2
return wrapper2
def deco3(x):
def outter3(func3): # func3=被装饰对象index函数的内存地址
def wrapper3(*args,**kwargs):
print('正在运行===>deco3.outter3.wrapper3')
res3=func3(*args,**kwargs)
return res3
return wrapper3
return outter3
# 加载顺序自下而上(了解)
@deco1 # index=deco1(wrapper2的内存地址) ===> index=wrapper1的内存地址
@deco2 # index=deco2(wrapper3的内存地址) ===> index=wrapper2的内存地址
@deco3(111) # ===>@outter3===> index=outter3(index) ===> index=wrapper3的内存地址
def index(x,y):
print('from index %s:%s' %(x,y))
# 执行顺序自上而下的,即wraper1-》wrapper2-》wrapper3
index(1,2) # wrapper1(1,2)