【百尺竿头,更进一步学Python】Python进阶课程——生成器
学习了迭代器以后,今天我们来讲它的好兄弟生成器.在学习生成器之前,我们先对学过的迭代器进行一次巩固.
迭代器(巩固)
- 我们都知道利用迭代器,可以在每次迭代获取数据(通过next方法)时,按照特点的规律进行生成,但是我们在实现一个迭代器的时候,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next方法进行迭代使用。这种方式是我们提前将数据创建好了以后才进行的.
- 在Python中除了for遍历语法在in之后需要使用迭代器,其他的一些列表类型转换(list,tuple)等也需要使用迭代器
生成器
什么是生成器
- 生成器是一种特殊的迭代器
创建生成器
创建生成器有两种方式:
-
创建生成器,只需要把一个列表推导式的[]改成()即可
p = (i for i in range(1000)) print(p)
-
创建生成器,只需要在一个函数中使用yield语句,那么这个函数也不再叫函数,就叫生成器。yield在函数里面出现,这就是一个生成器模板.(我们运用以前学过的案例"水仙花数(完数)"来进行讲解)
def create_shui(startnum, endnum): while startnum < endnum: ge = startnum % 10 shi = (startnum % 100) // 10 bai = (startnum % 1000) // 100 qian = startnum // 1000 if startnum == ge ** 3 + shi ** 3 + bai ** 3 + qian ** 3: yield startnum startnum += 1 if __name__ == "__main__": ret = create_shui(1, 1000) print(ret) for item in ret: print(item)
总结:
- 只要在调用一个函数时发现函数中有yield,那么这就不是调用函数,而是创建了一个生成器对象
yield关键字
def create_shui(startnum, endnum):
print("开始...")
while startnum < endnum:
print("...1...")
yield startnum
print("...2...")
startnum += 1
print("...3...")
if __name__ == "__main__":
# ret本质是一个生成器对象,生成器的本质是一个迭代器
ret = create_shui(1, 5)
res = next(ret)
print(res)
# 执行的最后结果是:
# 开始...
# ...1...
# 1
从而我们可以得出yield关键字的本质.
yield的本质
- 将该函数标记为生成器,在调用有yield关键字的函数时,目标生成一个生成器对象。对这个生成器对象进行迭代时,每次的迭代都只执行到yield这块。而yield后面的数据是本次迭代的产物数据。只要不进行下一次迭代,代码流程一致监听在yield这块
- 保存当前的运行状态,然后暂停执行,也就是将生成器挂起
- yield关键字后面的表达式作为值返回
注意:
- python3中的生成器里面可以使用return返回最终运行的返回值,
- 而在Python2中生成器里面绝对不允许出现return的返回值(Python2中可以使用return但是return后面绝对不能有数据表达式)
生成器的唤醒
使用next()函数
- 我们可以对生成器使用next()函数让生成器开始迭代一次,也就是唤醒一次生成器
实例:
def create_shui(startnum, endnum):
print("开始...")
while startnum < endnum:
print("...1...")
yield startnum
print("...2...")
startnum += 1
print("...3...")
if __name__ == "__main__":
# ret本质是一个生成器对象,生成器的本质是一个迭代器
ret = create_shui(1, 5)
res = next(ret)
print(res)
# 再来一次
res = next(ret)
print(res)
# 再来一次
res = next(ret)
print(res)
# 执行到最后的结果是
# 开始...
# ...1...
# 1
# ...2...
# ...3...
# ...1...
# 2
# ...2...
# ...3...
# ...1...
# 3
使用send唤醒
- send一般不会放到第一次启动生成器,如果非要这样做,那么选择传递一个None
- send里面的参数会当做信息传递给yield当做yield的结果,然后通过一个变量可以接收这个结果
- send的结果是下一调用yield时,yield后面的值
实例:
def create_shui(startnum, endnum):
print("开始...")
while startnum < endnum:
print("...1...")
res = yield startnum
print(res)
print("...2...")
startnum += 1
print("...3...")
else:
return "结束..."
if __name__ == "__main__":
ret = create_shui(1, 5)
print(next(ret))
# send一般不会放到第一次启动生成器
# 如果非要这样做,那么选择传递一个None
# print(ret.send(None))
print(ret.send("123"))
print(ret.send("456"))
# 最后的执行结果是:
# 开始...
# ...1...
# 1
# 123
# ...2...
# ...3...
# ...1...
# 2
# 456
# ...2...
# ...3...
# ...1...
# 3
并发执行的模拟
import time
def upload():
while True:
time.sleep(0.5)
print("上传文件")
yield
def download():
while True:
time.sleep(1)
print("下载文件")
yield
def main():
ret1 = upload()
ret2 = download()
i = 1
while i <4:
next(ret2)
next(ret1)
i += 1
-
这个过程其实就是先让ret2先运行一会,当ret2中遇到了yield,让ret1运行
-
然后当ret1中遇到了yield再让ret2运行,这样就实现了/ret2/ret1/ret2/ret1/…交替运行,最终实现了所谓的并发