yield关键字详解与三种用法

本篇文章比较硬核, 适合有一定Python基础的读者阅读, 如果您对Python还不甚了解可以先关注我哦, 我会持续更新Python技术文章

yield详解

yield与return相同每次调用都会返回一个值, 不同的是return返回值后会直接结束函数执行, 而yeild则是返回值后冻结函数执行, 下次调用此函数会从冻结处开始执行

(冻结就是保存函数状态, 所有的局部变量与执行状态都会被保留)

yield语法

send参数 yield 返回值

语法分为三个部分, 除yield关键字外其它两个部分全部可空

第一部分: yield表达式返回值 (send的参数), 从此处开始解除冻结

第二部分: yield关键字

第三部分: 返回值, 返回后从此处开始冻结

执行到yield时yield会把右边的表达式当做本次函数的返回值返回给调用者, 然后函数进入冻结状态, 再次调用被冻结函数时yield解除冻结, 如果本次调用使用的是send方法则yield会把调用send函数时传递的参数当做yield表达式的返回值

调用yield函数的四种方式 (执行迭代的四种方式)

首先先来了解几个概念

可迭代对象: 实现了 __iter__ 方法的对象就是可迭代对象 (生成迭代器的对象)

迭代器: __iter__ 方法返回的, 同时实现了 __iter__ 方法与 __next__ 方法的对象就是迭代器 (虽然只实现了 __next__ 方法就可以迭代, 但是它不是迭代器也不是可迭代对象也不是生成器…整个一三无产品, 所以最好不要使用这种语法)

生成器: 函数内使用了yield关键字, 调用这个函数返回的对象就是生成器, 生成器是一种特殊的迭代器 (元组推导式也可以创建生成器)

迭代器耗尽: 迭代器主动抛出了StopIteration异常, 就表示迭代器耗尽, Python会自动处理这个异常

(对可迭代对象调用iter内置函数将会创建一个迭代器)

(对迭代器执行next内置函数或调用send方法将执行一次迭代)

(生成器内置实现了 __iter__ 方法与 __next__ 方法)

1. next(生成器对象) – 内置函数, 执行一次迭代 (恢复冻结的生成器对象)

2. 生成器对象.send(yield表达式返回值) – 生成器方法, 传递参数并执行一次迭代 (恢复冻结的生成器对象) (必须先调用一次next全局函数后才能调用)

3. for循环 – 自动调用调用next全局函数进行迭代, 直到迭代器耗尽

4. list、tuple、str、set等序列对象会自动next全局函数进行迭代, 直到迭代器耗尽

1. 生成器

什么是生成器?

生成器是一种特殊的迭代器, 可以理解为生成元素的算法, 每次调用都会生成一个元素, 特点是不会占用很大的内存空间, 而是在需要时动态生成

使用yield实现range函数功能

# 使用yield实现range函数功能

def my_range(start, end=None, step=1):

if end is None:

start, end = 0, start

# 生成元素的算法

while start < end:

yield start

start += step

# 函数结束自动抛出StopIteration异常, 表示迭代器耗尽

# 使用list自动迭代

print(list(my_range(10)))

# 使用for循环迭代

for i in my_range(10):

print(i)

# 创建生成器对象

gen = my_range(10)

print(next(gen))  # 使用next内置函数执行一次迭代

print(gen.send(None))  # 使用send方法执行一次迭代

使用send方法改变生成器行为

# 定义一个无限生成器

def infinite():

i = 0

while True:

set_i = yield i  # 使用set_i变量接收send传递的参数

if set_i is not None:  # 没有send参数则yield表达式返回None

i = set_i

else:

i += 1

# 函数结束自动抛出StopIteration异常, 表示迭代器耗尽

# 创建生成器对象

gen = infinite()

print(next(gen))  # 执行一次迭代  打印0

print(next(gen))  # 执行一次迭代  打印1

print(gen.send(10))  # 传递参数并执行一次迭代  打印10

print(next(gen))  # 执行一次迭代  打印11

2. 协程

什么是协程?

协程就是利用yield冻结特性来使多个函数交替执行, 使之呈现出并发执行效果 (多个函数同时执行)

(协程可以看做轻量级的线程, 优点是没有多线程的开销, 适用于IO密集型程序)

(这是最原始的协程实现方式, 实际开发中一般不会使用这种方式)

协程实现

# 函数1

def func_1():

while True:

print(‘--func1--‘)  # 执行一部分代码

yield  # 冻结, 等待下一次迭代唤醒

# 函数2

def func_2():

while True:

print(‘--func2--‘)  # 执行一部分代码

yield  # 冻结, 等待下一次迭代唤醒

# 创建生成器对象

func1 = func_1()

# 创建生成器对象

func2 = func_2()

# 循环执行

while True:

next(func1)  # 函数1解除冻结, 执行一段代码冻结后返回

next(func2)  # 函数2解除冻结, 执行一段代码冻结后返回

# 两个生成器交替冻结-解除冻结就完成了并发

# 效果就是单线程程序可以呈现出多线程效果

3. 上下文管理器

什么是上下文管理器?

上下文管理器保证了代码无论是否出现异常, 资源都会被正确回收

上文: 资源创建

下文: 资源释放 (无论是否出现异常都会被执行)

管理器: 上下文调度

上下文管理器可以通过实现 __enter__ 方法与 __exit__ 方法实现, 也可以使用函数+装饰器+yield实现, 本篇文章讲的就是函数+装饰器+yield的实现方式

上文由with语句开始时自动执行, 返回结果使用as关键字接收

下文由with语句结束时自动执行 (无论是否出现异常都会被执行)

函数+装饰器+yield 实现上下文管理器

# 导入上下文管理器装饰器

from contextlib import contextmanager

# 实现上下文管理器的文件对象

@contextmanager  # 必须要使用contextmanager装饰器进行装饰

def m_file(filename, mode=‘r‘):

# 上文 -- 创建外汇返佣打开文件 -- with开始时自动执行

f = open(filename, mode)

yield f  # 返回上文对象 -- 对应__enter__方法 -- 返回值使用as关键字接收

# 下文 -- 关闭文件对象 -- with语句结束时自动执行

f.close()  # 对应__exit__方法

# 使用with打开上下文管理器

with m_file(‘test.txt‘, ‘w‘) as f:

f.write(‘test‘)

原文链接:https://blog.csdn.net/weixin_44221705/article/details/103572127

原文地址:https://www.cnblogs.com/benming/p/12059424.html

时间: 2024-10-04 18:40:43

yield关键字详解与三种用法的相关文章

Windows下图文详解PHP三种运行方式(php_mod、cgi、fastcgi)

PHP能不能成功的在Apache服务器上运行,就看我们如何去配置PHP的运行方式.PHP运行目前为止主要有三种方式: a.以模块加载的方式运行,初学者可能不容易理解,其实就是将PHP集成到Apache服务器,以同一个进程运行. b.以CGI的方式运行,CGI英文叫做公共网关接口,就是Apache在遇到PHP脚本的时候会将PHP程序提交给CGI应用程序(php-cgi.exe)解释,解释之后的结果返回给Apache,然后再返回给相应的请求用户. c.以FastCGI的方式运行.这种形式是CGI的加

文件和目录详解(九)---三种文件时间以及utime函数详解

文件有三种时间,分别是: 文件数据的最后访问时间------read函数会修改 文件数据的最后修改时间------write函数会修改 i 节点状态的最后更改时间----unlink函数会修改 要区分文件数据的修改时间和 i 节点状态更改时间,因为文件数据和 i 节点信息是分开存储的,有些系统调用只会影响 i 节点中的信息,而不会影响文件的数据部分,比如 unlink 函数. 使用 utime 函数可以修改一个文件的三种时间. ==================================

MySQL集群搭建详解(三种结点分离)

本文将搭建一个最简化的MySQL Cluster系统,配置方法中的所有命令都是以root账户运行.这个MySQL Cluster包含一个管理结点.两个数据结点.两个SQL结点,这五个结点会分别安装在五个虚拟机上,虚拟机的名称和IP如下所示: 管理结点 mysql-mgm 192.168.124.141 数据结点 1 mysql-ndbd-1 192.168.124.142 数据结点 2 mysql-ndbd-2 192.168.124.143 SQL 结点1 mysql-sql-1 192.16

详解django三种文件下载方式

推荐使用FileResponse,从源码中可以看出FileResponse是StreamingHttpResponse的子类,内部使用迭代器进行数据流传输. 在实际的项目中很多时候需要用到下载功能,如导excel.pdf或者文件下载,当然你可以使用web服务自己搭建可以用于下载的资源服务器, 如nginx,这里我们主要介绍django中的文件下载. 实现方式:a标签+响应头信息(当然你可以选择form实现) <div class="col-md-4"><a href=

详解Apache三种工作模式及目录属性

Apache工作模式介绍 1.Apache作为现今web服务器用的最广泛也是最稳定的开源服务器软件 2.其工作模式有许多种,源码包安装httpd时可查看httpd-mpm.conf文件,该文件位于extra/conf目录中 3.目前主要有两种模式: event模式:一个进程中包含多个线程 prefork模式:一个进程中包含一个线程 worker模式:一个进程中包含多个线程 event工作模式介绍 1.event是Apache最新的工作模式,它和worker模式很像,不同的是在于它解决了keep-

.NET(c#)new关键字的三种用法

前几天去家公司面试,有一道这样的题:写出c#中new关键字的三种用法,思前想后挖空心思也只想出了两种用法,回来查了下msdn,还真是有第三种用法:用于在泛型声明中约束可能用作类型参数的参数的类型,这是在Framework 2.0 中定义泛行时才会使用到的,自己对c# 2.0 中的支持还只是粗通皮毛,怪不得累死so many脑细胞也没能想不出这第三种来! 三种用法如下: 在 C# 中,new 关键字可用作运算符.修饰符或约束. 1)new 运算符:用于创建对象和调用构造函数.这种大家都比较熟悉,没

this和super关键字的常见三种用法

super和this的含义 super :代表父类的存储空间标识(可以理解为父亲的引用). this :代表当前对象的引用(谁调用就代表谁). super关键字的三种用法: 在子类的成员方法中,访问父类的成员变量格式:super.成员变量名: 在子类的成员方法中,访问父类的成员方法格式:super.方法变量名: 在子类的构造方法中,访问父类的构造方法格式:super(): this关键字的三种用法: 在本类成员方法中,访问本类的成员变量格式:this.变量名 在本类成员方法中,访问本类的另一个成

Android高效率编码-第三方SDK详解系列(三)——JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送

Android高效率编码-第三方SDK详解系列(三)--JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送 很久没有更新第三方SDK这个系列了,所以更新一下这几天工作中使用到的推送,写这个系列真的很要命,你要去把他们的API文档大致的翻阅一遍,而且各种功能都实现一遍,解决各种bug各种坑,不得不说,极光推送真坑,大家使用还是要慎重,我们看一下极光推送的官网 https://www.jpush.cn/common/ 推送比较使用,很多软件有需要,所以在这个点拿出来多讲讲,我们本节

java关键字详解

Java关键字及其作用 目录 Java关键字及其作用--- 1 一.     关键字总览:2 二.     详细解释--- 3 1.访问控制--- 3 1)私有的-- 3      private 2)受保护的-- 3      protected 3)公共的-- 3      public 2.类.方法和变量修饰符--- 3 1)声明抽象-- 3      abstract 2)类-- 4      class 3)继承.扩展-- 4      extends 4)最终.不可改变-- 4