当前位置: 首页 > news >正文

python yield generator 详解

目录

generator基础
generator应用
generator基础应用  
generator高级应用
注意事项:
 

正文

  本文将由浅入深详细介绍yield以及generator,包括以下内容:什么generator,生成generator的方法,generator的特点,generator基础及高级应用场景,generator使用中的注意事项。本文不包括enhanced generator即pep342相关内容,这部分内容在之后的博文介绍。

generator基础
回到顶部
  在python的函数(function)定义中,只要出现了yield表达式(Yield expression),那么事实上定义的是一个generator function, 调用这个generator function返回值是一个generator。这根普通的函数调用有所区别,For example:

def gen_generator():yield 1def gen_value():return 1if __name__ == '__main__':ret = gen_generator()print ret, type(ret)    #<generator object gen_generator at 0x02645648> <type 'generator'>ret = gen_value()print ret, type(ret)    # 1 <type 'int'>

  从上面的代码可以看出,gen_generator函数返回的是一个generator实例,generator有以下特别:

遵循迭代器(iterator)协议,迭代器协议需要实现__iter__、next接口
能过多次进入、多次返回,能够暂停函数体中代码的执行
  下面看一下测试代码:


>>> def gen_example():...     print 'before any yield'...     yield 'first yield'...     print 'between yields'...     yield 'second yield'...     print 'no yield anymore'... >>> gen = gen_example()>>> gen.next()    # 第一次调用nextbefore any yield'first yield'>>> gen.next()    # 第二次调用nextbetween yields'second yield'>>> gen.next()    # 第三次调用nextno yield anymoreTraceback (most recent call last):File "<stdin>", line 1, in <module>StopIteratio


  调用gen example方法并没有输出任何内容,说明函数体的代码尚未开始执行。当调用generator的next方法,generator会执行到yield 表达式处,返回yield表达式的内容,然后暂停(挂起)在这个地方,所以第一次调用next打印第一句并返回“first yield”。 暂停意味着方法的局部变量,指针信息,运行环境都保存起来,直到下一次调用next方法恢复。第二次调用next之后就暂停在最后一个yield,再次调用next()方法,则会抛出StopIteration异常。 

  因为for语句能自动捕获StopIteration异常,所以generator(本质上是任何iterator)较为常用的方法是在循环中使用: 


1 def generator_example():
2     yield 1
3     yield 2
4 
5 if __name__ == '__main__':
6     for e in generator_example():
7         print e
8         # output 1 2

  generator function产生的generator与普通的function有什么区别呢

  (1)function每次都是从第一行开始运行,而generator从上一次yield开始的地方运行

  (2)function调用一次返回一个(一组)值,而generator可以多次返回

  (3)function可以被无数次重复调用,而一个generator实例在yield最后一个值 或者return之后就不能继续调用了

  在函数中使用Yield,然后调用该函数是生成generator的一种方式。另一种常见的方式是使用generator expression,For example:
  >>> gen = (x * x for x in xrange(5))
  >>> print gen
  <generator object <genexpr> at 0x02655710>
  

generator应用
回到顶部
generator基础应用  
  为什么使用generator呢,最重要的原因是可以按需生成并“返回”结果,而不是一次性产生所有的返回值,况且有时候根本就不知道“所有的返回值”。比如对于下面的代码  


1     RANGE_NUM = 100
2     for i in [x*x for x in range(RANGE_NUM)]: # 第一种方法:对列表进行迭代
3         # do sth for example
4         print i
5 
6     for i in (x*x for x in range(RANGE_NUM)): # 第二种方法:对generator进行迭代
7         # do sth for example
8         print i

  在上面的代码中,两个for语句输出是一样的,代码字面上看来也就是中括号与小括号的区别。但这点区别差异是很大的,第一种方法返回值是一个列表,第二个方法返回的是一个generator对象。随着RANGE_NUM的变大,第一种方法返回的列表也越大,占用的内存也越大;但是对于第二种方法没有任何区别。

  我们再来看一个可以“返回”无穷多次的例子:

def fib():
    a, b = 1, 1
    while True:
        yield a
        a, b = b, a+b 
这个generator拥有生成无数多“返回值”的能力,使用者可以自己决定什么时候停止迭代

generator高级应用
使用场景一:  

  Generator可用于产生数据流, generator并不立刻产生返回值,而是等到被需要的时候才会产生返回值,相当于一个主动拉取的过程(pull),比如现在有一个日志文件,每行产生一条记录,对于每一条记录,不同部门的人可能处理方式不同,但是我们可以提供一个公用的、按需生成的数据流。

 1 def gen_data_from_file(file_name):2     for line in file(file_name):3         yield line4 5 def gen_words(line):6     for word in (w for w in line.split() if w.strip()):7         yield word8 9 def count_words(file_name):
10     word_map = {}
11     for line in gen_data_from_file(file_name):
12         for word in gen_words(line):
13             if word not in word_map:
14                 word_map[word] = 0
15             word_map[word] += 1
16     return word_map
17 
18 def count_total_chars(file_name):
19     total = 0
20     for line in gen_data_from_file(file_name):
21         total += len(line)
22     return total
23     
24 if __name__ == '__main__':
25     print count_words('test.txt'), count_total_chars('test.txt')

   上面的例子来自08年的PyCon一个讲座。gen_words gen_data_from_file是数据生产者,而count_words count_total_chars是数据的消费者。可以看到,数据只有在需要的时候去拉取的,而不是提前准备好。另外gen_words中 (w for w in line.split() if w.strip()) 也是产生了一个generator

使用场景二:

  一些编程场景中,一件事情可能需要执行一部分逻辑,然后等待一段时间、或者等待某个异步的结果、或者等待某个状态,然后继续执行另一部分逻辑。比如微服务架构中,服务A执行了一段逻辑之后,去服务B请求一些数据,然后在服务A上继续执行。或者在游戏编程中,一个技能分成分多段,先执行一部分动作(效果),然后等待一段时间,然后再继续。对于这种需要等待、而又不希望阻塞的情况,我们一般使用回调(callback)的方式。下面举一个简单的例子:

1 def do(a):
2     print 'do', a
3     CallBackMgr.callback(5, lambda a = a: post_do(a))
4 
5 def post_do(a):
6     print 'post_do', a


  这里的CallBackMgr注册了一个5s后的时间,5s之后再调用lambda函数,可见一段逻辑被分裂到两个函数,而且还需要上下文的传递(如这里的参数a)。我们用yield来修改一下这个例子,yield返回值代表等待的时间。

1 @yield_dec
2 def do(a):
3     print 'do', a
4     yield 5
5     print 'post_do', a


  这里需要实现一个YieldManager, 通过yield_dec这个decrator将do这个generator注册到YieldManager,并在5s后调用next方法。Yield版本实现了和回调一样的功能,但是看起来要清晰许多。下面给出一个简单的实现以供参考:

   


# -*- coding:utf-8 -*-
import sys
# import Timer
import types
import timeclass YieldManager(object):def __init__(self, tick_delta = 0.01):self.generator_dict = {}# self._tick_timer = Timer.addRepeatTimer(tick_delta, lambda: self.tick())def tick(self):cur = time.time()for gene, t in self.generator_dict.items():if cur >= t:self._do_resume_genetator(gene,cur)def _do_resume_genetator(self,gene, cur ):try:self.on_generator_excute(gene, cur)except StopIteration,e:self.remove_generator(gene)except Exception, e:print 'unexcepet error', type(e)self.remove_generator(gene)def add_generator(self, gen, deadline):self.generator_dict[gen] = deadlinedef remove_generator(self, gene):del self.generator_dict[gene]def on_generator_excute(self, gen, cur_time = None):t = gen.next()cur_time = cur_time or time.time()self.add_generator(gen, t + cur_time)g_yield_mgr = YieldManager()def yield_dec(func):def _inner_func(*args, **kwargs):gen = func(*args, **kwargs)if type(gen) is types.GeneratorType:g_yield_mgr.on_generator_excute(gen)return genreturn _inner_func@yield_dec
def do(a):print 'do', ayield 2.5print 'post_do', ayield 3print 'post_do again', aif __name__ == '__main__':do(1)for i in range(1, 10):print 'simulate a timer, %s seconds passed' % itime.sleep(1)g_yield_mgr.tick()

注意事项:
回到顶部
(1)Yield是不能嵌套的!

 1 def visit(data):2     for elem in data:3         if isinstance(elem, tuple) or isinstance(elem, list):4             visit(elem) # here value retuened is generator5         else:6             yield elem7             8 if __name__ == '__main__':9     for e in visit([1, 2, (3, 4), 5]):
10         print e

  上面的代码访问嵌套序列里面的每一个元素,我们期望的输出是1 2 3 4 5,而实际输出是1  2  5 。为什么呢,如注释所示,visit是一个generator function,所以第4行返回的是generator object,而代码也没这个generator实例迭代。那么改改代码,对这个临时的generator 进行迭代就行了。

def visit(data):for elem in data:if isinstance(elem, tuple) or isinstance(elem, list):for e in visit(elem):yield eelse:yield elem或者在python3.3中 可以使用yield from,这个语法是在pep380加入的1 def visit(data):
2     for elem in data:
3         if isinstance(elem, tuple) or isinstance(elem, list):
4             yield from visit(elem)
5         else:
6             yield elem(2)generator function中使用return在python doc中,明确提到是可以使用return的,当generator执行到这里的时候抛出StopIteration异常。1 def gen_with_return(range_num):2     if range_num < 0:3         return4     else:5         for i in xrange(range_num):6             yield i7 8 if __name__ == '__main__':9     print list(gen_with_return(-1))
10     print list(gen_with_return(1))但是,generator function中的return是不能带任何返回值的1 def gen_with_return(range_num):
2     if range_num < 0:
3         return 0
4     else:
5         for i in xrange(range_num):
6             yield i


  上面的代码会报错:SyntaxError: 'return' with argument inside generator

References:

http://www.dabeaz.com/generators-uk/
https://www.python.org/dev/peps/pep-0380/
http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do
http://stackoverflow.com/questions/15809296/python-syntaxerror-return-with-argument-inside-generator

相关文章:

python yield generator 详解

目录 generator基础 generator应用 generator基础应用   generator高级应用 注意事项&#xff1a; 正文 本文将由浅入深详细介绍yield以及generator&#xff0c;包括以下内容&#xff1a;什么generator&#xff0c;生成generator的方法&#xff0c;generator的特点&#…...

MATLAB矩阵下标引用

在MATLAB中&#xff0c;普通的二维数组元素的数字索引分为双下标索引和单下标索引。双下标索引是通过一个二元数组对来对应元素在矩阵中的行列位置&#xff0c;例如A(2,3)表示矩阵A中第2行第3列的元素。单下标索引的方式是采用列元素优先的原则&#xff0c;对m行n列的矩阵按列排…...

syn洪水攻击原理是什么

在网络世界中&#xff0c;正常的网络访问就像一场有序的对话。当我们访问网站时&#xff0c;客户端与服务器要进行 TCP 三次握手来建立连接。首先&#xff0c;客户端向服务器发送一个 SYN 包&#xff0c;请求建立连接&#xff0c;这就如同向服务器打招呼说“我想连接”&#xf…...

前缀和(4)_除自身以外数组的乘积

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 前缀和(4)_除自身以外数组的乘积 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录…...

第二十一节:学习Redis缓存数据库的Hash操作(自学Spring boot 3.x的第五天)

这节记录下Redis的Hash操作。主要是opsForHash方式和boundHashOps方式。 boundHashOps和opsForHash都是Spring Data Redis中用于操作Redis哈希数据结构的方法&#xff0c;但它们在使用方式和场景上存在一些区别。 boundHashOps 使用方式&#xff1a; boundHashOps方法通过Redi…...

OpenCV视频I/O(1)视频采集类VideoCapture介绍

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 用于从视频文件、图像序列或摄像头捕获视频的类。 该类提供了用于从摄像头捕获视频或读取视频文件和图像序列的 C API。 以下是该类的使用方法&a…...

CVE-2024-46103

前言 CVE-2024-46103 SEMCMS的sql漏洞。 漏洞简介 SEMCMS v4.8中&#xff0c;SEMCMS_Images.php的search参数&#xff0c;以及SEMCMS_Products.php的search参数&#xff0c;存在sql注入漏洞。 &#xff08;这个之前就有两个sql的cve&#xff0c;这次属于是捡漏了&#x1f6…...

三,MyBatis-Plus 的各种查询的“超详细说明”,比如(等值查询,范围查询,模糊查询...)

三&#xff0c;MyBatis-Plus 的各种查询的“超详细说明”&#xff0c;比如(等值查询&#xff0c;范围查询&#xff0c;模糊查询…) 文章目录 三&#xff0c;MyBatis-Plus 的各种查询的“超详细说明”&#xff0c;比如(等值查询&#xff0c;范围查询&#xff0c;模糊查询...)1. …...

Linux 冯诺依曼体系结构与操作系统概念

目录 0.前言 1. 冯诺依曼体系结构概述 1.1 输入单元 1.2 中央处理单元&#xff08;CPU&#xff09; 1.3 输出单元 2. 冯诺依曼体系结构的关键特性 2.1 所有数据流向内存 2.2 数据流动示例&#xff1a;QQ聊天过程 3. 操作系统 3.1 概念 3.2 设计操作系统的目的 3.3 操作系统的“…...

UE4中 -skipbuild -nocompile 有什么区别

在项目开发中&#xff0c;我看到了在调用 Engine\\Build\\BatchFiles\\RunUAT.bat 相关的命令行中&#xff0c;有 -skipbuild、 -nocompile 两个很像的参数&#xff0c;于是想探究一下它们的区别与含义。 -skipbuild 参数 到底有没有 -skipbuild 这个参数&#xff1f;根据 http…...

k8s篇之数据挂载类型及区别

一、K8S集群数据挂载类型及区别 在 Kubernetes 中,数据挂载类型主要有以下几种,每种类型适用于不同的场景。以下是主要的挂载类型及其应用场景的详细说明: 1. emptyDir 描述:emptyDir 是一个空目录,其生命周期与 Pod 相同。 它在 Pod 创建时被创建,并在 Pod 删除时被清…...

LiveQing视频点播流媒体RTMP推流服务功能-支持电子放大拉框放大直播视频拉框放大录像视频流拉框放大电子放大

LiveQing视频点播流媒体RTMP推流服务功能-支持电子放大拉框放大直播视频拉框放大录像视频流拉框放大电子放大 1、鉴权直播2、视频点播3、RTMP推流视频直播和点播流媒体服务 1、鉴权直播 鉴权直播-》播放 &#xff0c;左键单击可以拉取矩形框&#xff0c;放大选中的范围&#x…...

fetch怎么使用

fetch 是一个现代、强大的、基于 Promise 的网络请求 API&#xff0c;用于在浏览器中发起网络请求&#xff08;如异步获取资源&#xff09;。它提供了一种更加简洁和灵活的方式来替代 XMLHttpRequest。下面是 fetch 的基本使用方法和一些示例。 基本语法 fetch(url, options)…...

回归预测 | Matlab基于SO-SVR蛇群算法优化支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于SO-SVR蛇群算法优化支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于SO-SVR蛇群算法优化支持向量机的数据多输入单输出回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab基于SO-SVR蛇群算法优化支持向量机的数据多…...

光耦知识分享:如何挑选合适的可控硅光耦型号

可控硅光耦是一种光电耦合器件&#xff0c;它结合了光敏元件&#xff08;通常是光敏二极管&#xff09;和可控硅器件&#xff08;如普通可控硅或三端可控硅&#xff09;的特性。它的工作原理是利用光信号控制可控硅的导通和截止&#xff0c;从而实现对电路的控制。 可控硅光耦…...

MySql Explain优化命令使用

MySql Explain优化命令使用 truncate table student // 自增id 从 0 开始 delete from student // 自增id 会保留 &#xff0c; 108 区别&#xff1a; 1&#xff1a;自增id 2&#xff1a;delete 可以恢复 truncate 无法恢复 前言 EXPLAIN 是一个用于获取 SQL 语句执行计划的…...

Android NestedScrollView+TabLayout+ViewPager+ 其它布局,ViewPager 不显示以及超出屏幕不显示问题

前言 此场景为 NestedScrollView 嵌套多个布局 &#xff0c;大致结构为 NestedScrollViewTabLayoutViewPagerfragment 其它View,如下图 &#xff0c; 一、ViewPager 设置高度才会显示内容问题 原因&#xff1a;NestedScrollView 计算高度先于 ViewPager 渲染前&#xff0c;所…...

Linux开机logo设置

本文介绍Linux开机logo设置。 常用的Linux开机logo设置工具有fbi(Linux Framebuffer Imageviewer)&#xff0c;plymouth等&#xff0c;本文针对fbi工具进行开机logo设置。 1.fbi工具安装 命令行下&#xff0c;输入&#xff1a; sudo apt-get install fbi -y 安装完毕后&am…...

webpack插件开发 模拟vue系统登录后,获取a标签下的文件

浏览器插件开发中&#xff0c;在webpack插件开发中&#xff0c;模拟Vue系统登录后获取a标签下的文件&#xff0c;可以通过监听某个登录事件&#xff0c;并在事件处理函数中修改Webpack的输出配置来实现。以下是一个简化的示例代码&#xff1a; // 假设有一个插件构造函数 Logi…...

大规模数据处理:分库分表与数据迁移最佳实践

什么是分库分表 分库分表是一种数据库架构优化策略&#xff0c;它将数据分散存储在多个数据库或表中&#xff0c;以此来提高系统的可扩展性和性能。 虽然分库分表能够提升系统的整体性能&#xff0c;但是也不要一上来就分库分表&#xff0c;如果系统在单表的情况下&#xff0…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用

在工业制造领域&#xff0c;无损检测&#xff08;NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统&#xff0c;以非接触式光学麦克风技术为核心&#xff0c;打破传统检测瓶颈&#xff0c;为半导体、航空航天、汽车制造等行业提供了高灵敏…...

tauri项目,如何在rust端读取电脑环境变量

如果想在前端通过调用来获取环境变量的值&#xff0c;可以通过标准的依赖&#xff1a; std::env::var(name).ok() 想在前端通过调用来获取&#xff0c;可以写一个command函数&#xff1a; #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...

uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)

UniApp 集成腾讯云 IM 富媒体消息全攻略&#xff08;地理位置/文件&#xff09; 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型&#xff0c;核心实现方式&#xff1a; 标准消息类型&#xff1a;直接使用 SDK 内置类型&#xff08;文件、图片等&#xff09;自…...

篇章二 论坛系统——系统设计

目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...

《Offer来了:Java面试核心知识点精讲》大纲

文章目录 一、《Offer来了:Java面试核心知识点精讲》的典型大纲框架Java基础并发编程JVM原理数据库与缓存分布式架构系统设计二、《Offer来了:Java面试核心知识点精讲(原理篇)》技术文章大纲核心主题:Java基础原理与面试高频考点Java虚拟机(JVM)原理Java并发编程原理Jav…...

数据库正常,但后端收不到数据原因及解决

从代码和日志来看&#xff0c;后端SQL查询确实返回了数据&#xff0c;但最终user对象却为null。这表明查询结果没有正确映射到User对象上。 在前后端分离&#xff0c;并且ai辅助开发的时候&#xff0c;很容易出现前后端变量名不一致情况&#xff0c;还不报错&#xff0c;只是单…...