017 生成器与yield的步步分析,看不懂,你吃翔

一 生成器与yield

若函数体包含yield关键字,再调用函数的时候,并不会执行函数体代码,得到的返回值即 生成器表达式

def test(start, stop, step=1):
    print("start...")
    while start < stop:
        yield start
        start += step
    print("end...")


g = test(0, 3)
print(g)  # <generator object test at 0x000002BD360943C0>
# 生成器内置有__iter__和__next__方法,所以生成器本身就是一个迭代器
print(g.__iter__())  # <generator object test at 0x000001A65FFD43C0>
print(g.__next__())  # 0 触发函数执行直到遇到yield则停止,将yield后的值返回,并在当前位置挂起函数
print(g.__next__())  # 1 再次调用next(g),函数从上次暂停的位置继续执行,直到重新遇到yield...
print(next(g))  # 2 再次调用next(g),函数从上次暂停的位置继续执行,直到重新遇到yield...
print(g.__next__())  # 这一行报错,触发函数执行没有遇到yield则无值返回,即取值完毕抛出异常结束迭代

# 调用一次g.__next__()等价于调用了一次next(g),会在遇到一个yield的时候,停止下来,然后将前面的内容打印出来。
# 下一次调用的时候,会在停留的位置继续向后执行,直到遇到下一个yield的时候再停下来,然后将遇到这个yield之前的内容打印出来

既然生成器对象属于迭代器,那么必然可以使用for循环迭代,如下:

>>> for i in countdown(3):
...     print(i)
... 
countdown start
3
2
1
Done!

 
 

二 yield表达式应用

有了yield关键字,我们就有了一种自定义迭代器的实现方式。yield可以用于返回值,但不同于return,函数一旦遇到return就结束了,而yield可以保存函数的运行状态挂起函数,用来返回多次值。那yield究竟干啥的:
首先吐槽下:网上资源出来的这种,这尼玛你们能看得懂吗,这说的啥哟…
无恶意,只是吐槽下
无恶意,只是吐槽下,接下来是我自己的理解,如有错误,评论区见(ps:不过你小心点,我来自祖安,23333)

前面章节函数的基本使用中说过return,那return是干什么的呢,
return就是函数体中的代码运行到return就会终止停下来,不管后面有多少代码,都不会去执行,然后返回return 后面接的东西,这个东西可以是数字,可以是字母,可以是函数等等,没有就是代表None。
所以我们可以先把yield看成是一个“return”,程序运行到这一行也会停下来,比如说

def func():
    print(11111)
    yield 1
    print(22222)
    yield 2
    print(33333)


g = func()  # <generator object func at 0x015B5F08>
res1 = next(g)
res2 = next(g)
print(res1)
print(res2)
"""
执行结果:
11111
22222
1
2
思路:
1 函数名+括号代表函数开始执行了,所以func()程序执行之后,因为在函数中有个yield,所以函数并不会真正的执行,所以在func()之后,得到了一个生成器g
2 直到有next(g)的代码的出现,才会真的执行,而next(g)的底层实现就是调用了生成器g的内置方法__next__()
3 程序执行next(g)之后,就会运行函数func内体代码,直到遇到yield才会停下来,并将遇到yield之前的代码执行完毕,这个例子中就是打印11111
4 如果还有next(g),那么则会在停下来的位置,继续往后执行,直到遇到第二个yield才会停下来,并将遇到第一个yield和第二个yield之间的代码执行完毕,
    这个例子中就是22222,第二个yield之后的代码不会运行,print(33333)自然也不会打印
5 yield会将紧跟在yield后面的参数作为,g.__next__()的返回值
"""

那有人就会有疑问了,既然yield会将紧跟在yield后面的参数作为next(g)的返回值,那么yield自身的返回值是什么,看下面代码:

def func():
    print(111)
    result1 = yield 1
    print("result1: ", result1)
    print(222)
    result2 = yield 2
    print(333)


g = func()
res1 = next(g)
print("res1: ", res1)
"""
执行结果:
111
res1:  1

分析:
咦???为什么print("result1: ", result1)这一行没有执行,上面说了,遇到yield会停下来,直到下一个next(g)的出现,才会在停下来的位置,继续往后执行,直到遇到第二个yield才会停下来,并将遇到第一个yield和第二个yield之间的代码执行完毕。
"""

再接着看:

def func():
    print(111)
    result1 = yield 1
    print("result1: ", result1)
    print(222)
    result2 = yield 2
    print(333)


g = func()
res1 = next(g)
res2 = next(g)
print("res1: ", res1)
print("res2: ", res2)
"""
执行结果:
111
result1:  None
222
res1:  1
res2:  2

分析:
field的返回值是None
"""

那如何让其返回值不为None呢,就要用到send方法了

def func():
    print(111)
    result1 = yield 1
    print("result1: ", result1)
    print(222)
    result2 = yield 2
    print(333)


g = func()
next(g)
res1 = g.send("a")
print("res1: ", res1)
"""
执行结果:
111
result1:  a
222
res1:  2

分析:
send方法前要有个next(g),不然会报错,TypeError: can't send non-None value to a just-started generator,这是报错信息。
那么这个next(g)可以了解为"初始化",让函数在result1=yield 1这一行停下来,等待g.send()为其传参。所以当我们send("a")时,就会为yield 1自身的返回值传入a。
眼尖的炮友们,基友们,女孩子的话就叫女朋友们,就会发现,print("res1: ", res1)这一行的执行结果是res1:  2,而不是1,这是因为,你调用send()方法的时候,给其传参的同时,也在其内部运行了一次__next__()方法
"""

 
 

三 三元表达式,列表生成式,生成器表达式

3.1 三元表达式

三元表达式是python为我们提供的一种简化代码的解决方案,语法如下

res = 条件成立时返回的值 if 条件 else 条件不成立时返回的值

例如:

def max2(x, y):
	if x > y:
		return x
	else:
		return y
	
res = max2(1, 2)
# 上述代码用三元表达式可以一行解决
x = 1
y = 2
res = x if x > y else y  # 三元表达式

3.2 列表生成式

列表生成式是python为我们提供的一种简化代码的解决方案,用来快速生成列表,

egg_list = []
for i in range(10):
	egg_list.append("鸡蛋%s" %i)
# 用列表生成式一行就可以解决
egg_list = ["鸡蛋%s" %i for i in range(10)]

3.3 生成器表达式

创建一个生成器对象有两种方式,一种是调用带yield关键字的函数,另一种就是生成器表达式,与列表生成式的语法格式相同,只需要将[]换成()
对比列表生成式返回的是一个列表,生成器表达式返回的是一个生成器对象

>>> [x*x for x in range(3)]
[0, 1, 4]
>>> g = (x*x for x in range(3))
>>> g
<generator object <genexpr> at 0x000001D97509BCF0>
>>> g.__next__()
0
>>>

如果我们要读取一个大文件的字节数,应该基于生成器表达式的方式完成

with open('db.txt','rb') as f:
    nums=(len(line) for line in f)
    total_size=sum(nums) # 依次执行next(nums),然后累加到一起得到结果=
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值