python生成器yield原理_Python generator生成器和yield表达式详解

前言

Python生成器(generator)并不是一个晦涩难懂的概念。相比于MetaClass和Closure等概念,其较为容易理解和掌握。但相对于程序结构:顺序、循环和分支而言其又不是特别的直观。无论学习任何的东西,概念都是非常重要的。正确树立并掌握一些基础的概念是灵活和合理运用的前提,本文将以一种通俗易懂的方式介绍一下generator和yield表达式。

1. Iterator与Iterable

首先明白两点:

Iterator(迭代器)是可迭代对象;

可迭代对象并不一定是Iterator;

比较常见的数据类型list、tuple、dict等都是可迭代的,属于collections.Iterable类型;

迭代器不仅可迭代还可以被内置函数next调用,属于collections.Iterator类型;

迭代器是特殊的可迭代对象,是可迭代对象的一个子集。

将要介绍的gererator(生成器)是types.GeneratorType类型,也是collections.Iterator类型。

也就是说生成器是迭代器,可被next调用,也可迭代。

三者的包含关系:(可迭代(迭代器(生成器)))

迭代器:可用next()函数访问的对象;

生成器:生成器表达式和生成器函数;

2. Python生成器

python有两种类型的生成器:生成器表达式和生成器函数。

由于生成器可迭代并且是iterator,因此可以通过for和next进行遍历。

2.1 生成器表达式

把列表生成式的[]改成()便得到生成器表达式。

>>> gen = (i + i for i in xrange(10))

>>> gen

at 0x0000000003A2DAB0>

>>> type(gen)

>>> isinstance(gen, types.GeneratorType) and isinstance(gen, collections.Iterator) and isinstance(gen, collections.Iterable)

True

>>>

2.2 生成器函数

python函数定义中有关键字yield,该函数便是一个生成器函数,函数调用返回的是一个generator.

def yield_func():

for i in xrange(3):

yield i

gen_func = yield_func()

for yield_val in gen_func:

print yield_val

生成器函数每次执行到yield便会返回,但与普通函数不同的是yield返回时会保留当前函数的执行状态,再次被调用时可以从中断的地方继续执行。

2.3 next与send

通过for和next可以遍历生成器,而send则可以用于向生成器函数发送消息。

def yield_func():

for i in xrange(1, 3):

x = yield i

print 'yield_func',x

gen_func = yield_func()

print 'iter result: %d' % next(gen_func)

print 'iter result: %d' % gen_func.send(100)

结果:

iter result: 1

yield_func 100

iter result: 2

简单分析一下执行过程:

line_no 5 调用生成器函数yield_func得到函数生成器gen_func;

line_no 6 使用next调用gen_func,此时才真正的开始执行yield_func定义的代码;

line_no 3 执行到yield i,函数yield_func暂停执行并返回当前i的值1.

line_no 6 next(gen_func)得到函数yield_func执行到yield i返回的值1,输出结果iter result: 1;

line_no 7 执行gen_func.send(100);

line_no 3 函数yield_func继续执行,并将调用者send的值100赋值给x;

line_no 4 输出调用者send接收到的值;

line_no 3 执行到yield i,函数yield_func暂停执行并返回当前i的值2.

line_no 7 执行gen_func.send(100)得到函数yield_func运行到yield i返回的值2,输出结果iter result: 2;

如果在上面代码后面再加一行:

print 'iter result: %d' % next(gen_func)

结果:

iter result: 1

yield_func 100

iter result: 2

yield_func None

File "G:\Cnblogs\Alpha Panda\Main.py", line 22, in

print 'iter result: %d' % next(gen_func)

StopIteration

yield_func只会产生2个yield,但是我们迭代调用了3次,会抛出异常StopIteration。

next和send均会触发生成器函数的执行,使用for遍历生成器函数时不要用send。原因后面解释。

2.4 生成器返回值

使用了yield的函数严格来讲已经不是一个函数,而是一个生成器。因此函数中yield和return是不能同时出现的。

SyntaxError: 'return' with argument inside generator

生成器只能通过yield将每次调用的结果返回给调用者。

2.5 可迭代对象转成迭代器

list、tuple、dict等可迭代但不是迭代器的对象可通过内置函数iter转化为iterator,便可以通过next进行遍历;

这样的好处是可以统一使用next遍历所有的可迭代对象;

tup = (1,2,3)

for ele in tup:

print ele + ele

上面的代码等价于:

tup_iterator = iter(tup)while True:

try:

ele = next(tup_iterator)

except StopIteration:

break

print ele + ele

for循环使用next遍历一个迭代器,混合使用send可能会导致混乱的遍历流程。

其实到这里生成器相关的概念基本已经介绍完成了,自己动手过一遍应该能弄明白了。为了更加深刻的体会生成器,下面我们在往前走一步。

3. range与xrange

在Python 2中这两个比较常用,看一下两者的区别:

range为一个内置函数,xrange是一个类;

前者返回一个list,后者返回一个可迭代对象;

后者遍历操作快于前者,且占用更少内存;

这里xrange有点类似于上面介绍的生成器表达式,虽然xrange返回的并不是生成器,但两者均返回并不包含全部结果可迭代对象。

3.1 自定义xrange的Iterator版本

作为一个iterator:

The iterator objects themselves are required to support the following two methods, which together form the iterator protocol:

iterator.__iter__()

Return the iterator object itself. This is required to allow both containers and iterators to be used with the for and in statements. This method corresponds to the tp_iter slot of the type structure for Python objects in the Python/C API.

iterator.next()

Return the next item from the container. If there are no further items, raise the StopIteration exception. This method corresponds to the tp_iternext slot of the type structure for Python objects in the Python/C API.

下面我们自定义class my_xrange:

class my_xrange(object):

def __init__(self, start, stop = None, step = 1):

""" 仅仅为了演示,假设start, stop 和 step 均为正整数 """

self._start = 0 if stop is None else start

self._stop = start if stop is None else stop

self._step = step

self._cur_val = self._start

def __iter__(self):

return self

def next(self):

if self._start <= self._cur_val < self._stop:

cur_val = self._cur_val

self._cur_val += self._step

return cur_val

raise StopIteration

测试结果:

import collections

myxrange = my_xrange(0, 10, 3)

res = []

for val in myxrange:

res.append(val)

print res == range(0, 10, 3)   # True

print isinstance(myxrange, collections.Iterator)  # Trueprint isinstance(myxrange, types.GeneratorType)  # False

3.2 使用函数生成器

下面使用函数生成器定义一个generator版的xrange。

def xrange_func(start, stop, step = 1):

""" 仅仅为了演示,假设start, stop 和 step 均为正整数 """

cur_val = start

while start <= cur_val and cur_val < stop:

yield cur_val

cur_val += step

isinstance(myxrange, collections.Iterator) and isinstance(myxrange, types.GeneratorType) is True

上面两个自定义xrange版本的例子,均说明生成器以及迭代器保留数列生成过程的状态,每次只计算一个值并返回。这样只要占用很少的内存即可表示一个很大的序列。

4. 应用

不管是迭代器还是生成器,对于有大量有规律的数据产生并需要遍历访问的情景均适用,占用内存少而且遍历的速度快。其中一个较为经典的应用为斐波那契数列(Fibonacci sequence)。

这里以os.walk遍历目录为例来说明yield的应用。如果我们需要遍历一个根目录下的所有文件并根据需要进行增删改查。可能会遇到下列的问题:

预先遍历且缓存结果,但是目录下文件可能很多,而且会动态改变;如果不缓存,多个地方可能会频繁的需要访问这一结果导致效率低下。

这时候可以使用yield定义一个生成器函数。

def get_all_dir_files(target_dir):

for root, dirs, files in os.walk(target_dir):

for file in files:

file_path = os.path.join(root, file)

yield os.path.realpath(file_path)

def file_factory(file):

""" do something """

target_dir = './'

all_files = get_all_dir_files(target_dir)

for file in all_files:

file_factory(file)

限于篇幅,就先介绍到这里,希望本文能让你对生成器有一个新的认识。

,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/297340.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java 操作POI 之复制sheet页

2019独角兽企业重金招聘Python工程师标准>>> 来点干货直接上代码&#xff0c;就不细说了 package com.qs.web.tools.core.excel; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.apache.poi.hssf.us…

Serverless 落地实践

.Net Core Serverless初体验什么是ServerlessServerless 是一个当今软件世界中比较新的话题。它并没有一个普遍公认的权威定义&#xff0c;每个人每个企业对它的解释可能都有不同&#xff0c;而 Serverless 正是在这种情况下不断发发展的。但是就算如此&#xff0c;有一些 Serv…

KOFLive Beta Daily-Scrum 9

组 员今天的工作进 度问 题明天的计划田 飞Work Item 38911&#xff1a;角色四照片拍摄 Work Item 38897:键盘输入人物四的图片已经导入&#xff0c;键盘三个连键检测完成还剩最后一个连键的检测Work Item 38911:拍摄人物角色五付 浩Work Item 37741:游戏主模块 Work Item 3889…

出这样的题,出题人的良心确定不会痛吗?

全世界只有3.14 % 的人关注了爆炸吧知识“那些年&#xff0c;让我们气到吐血的题目。”数学篇

mysql怎么查看索引情况_mysql 查看索引使用情况

mysql 查看索引使用情况这是以读为主的线上库rootread 02:28:07>show status like ‘Handler_read%’;———————–——-| Variable_name | Value |———————–——-| Handler_read_first | 0 || Handler_read_key | 0 || Handler_read_ne…

iTextSharp应用,生成pdf

using iTextSharp.text; using iTextSharp.text.pdf; using System.IO; public void PDF(string html) { Document dom new Document(); //创建文档 PdfWriter.GetInstance(dom, new FileStream(Server.MapPath("test") Guid.NewGuid() "…

python垃圾回收机制为什么标记能解决循环引用问题_Python 垃圾回收机制和如何解决循环引用...

引用计数&#xff1a;是一种垃圾收集机制&#xff0c;而且也是一种最直观&#xff0c;最简单的垃圾收集技术, 当一个对象的引用被创建或者复制时&#xff0c;对象的引用计数加 1&#xff1b;当一个对象的引用被销毁时&#xff0c;对象的引用计数减 1&#xff1b;当对象的引用计…

Dapr + .NET 实战(十-终篇)K8S运行Dapr

工作原理为了实现在k8s上安装Dapr&#xff0c;Dapr需要部署dapr-sidecar-injector、dapr-operator、dapr-placement和dapr-sentry服务。 dapr-operator: 管理组件(state stores, pub/subs, etc.)dapr-sidecar-injector: 将 Dapr 注入 annotated pods&#xff0c;并添加环境变量…

mvc control 请求两次问题

今天在做项目时&#xff0c;突然发现一个mvc 的control中action被执行了两次&#xff0c;最终发现是由于favicon.ico导致的。问题代码&#xff1a; <link rel"shortcut icon" href"Images/wangyi.ico" type"image/x-icon" />,最终改成了 &…

双系统卸载

xp卸载vista 1,xp我的电脑右键&#xff0c;属性&#xff0c;磁盘管理。 2&#xff0c;右击vista磁盘&#xff0c;格式化&#xff0c;设在格式化选项&#xff0c;确定。 3&#xff0c;在运行了输入msconfig,确定。 4&#xff0c;在系统配置实用程序对话框中切换到boot.ini选项卡…

为什么老师批改完试卷,分数下要画两条横线?今天终于知道了!

全世界只有3.14 % 的人关注了爆炸吧知识总有考试分数下画两条横线的习惯&#xff0c;原来是这样形成的&#xff01;为什么老师改卷之后会在分数下画两条横线呢&#xff1f;你要去问老师&#xff1f;别问了&#xff0c;可能连老师都不知道……来看看这位当了老师的网友怎么说的&…

mysql数据库已连接数据库_001. 【已解决】Java连接MYSQL 数据库的连接步骤

这篇文章主要以MySQL为例讲下Java如何连接到数据库的。当然&#xff0c;首先要安装有JDK(一般是JDK1.5.X)。然后安装MySQL&#xff0c;这些都比较简单&#xff0c;具体过程就不说了。配置好这两个环境后&#xff0c;下载JDBC驱动mysql-connector-java-5.0.5.zip(这个是最新版的…

Windows 11 小技巧- WSL开启Linux桌面应用

WSL 经过⼏年的发展&#xff0c;已经是⼀个Windows下不少开发⼈员做云原⽣应⽤的必备环境&#xff0c;你可以在上⾯写各种语⾔&#xff0c;各种后端框架&#xff0c;也可以完成容器和k8s的部署&#xff0c;在Windows11下更进⼀步&#xff0c;直接⽀持Linux桌⾯应⽤&#xff0c;…

lcd屏幕抖动_电视屏幕面板大科普!买电视之前必看!

BOE知识酷 ?显示技术|显示资讯| PPT|知识管理第856篇推文导读&#xff1a;经常看到什么LCD、LED、OLED、ULED、QLED、SLED、GLED……之类的名词&#xff0c;花里胡哨。别看那么多名词&#xff0c;但从显示技术角度看&#xff0c;现在市场上主流的电视机其实就两种&#xff1a;…

【转】Ubuntu 修改hosts

原文网址&#xff1a;http://l.14551.org/2009/12/2166 Ubuntu系统的Hosts只需修改/etc/hosts文件&#xff0c;在目录中还有一个hosts.conf文件&#xff0c;刚开始还以为只需要修改这个就可以了&#xff0c;结果发现是需要修改hosts。修改完之后要重启网络。具体过程如下&#…

一击即中的表白方式,学会了吗?

1 自己吃自己的&#xff0c;敢抢我吃的&#xff0c;吃我的小拳拳&#xff01;2 承受了太多哈哈哈3 狗狗在你不知道的时候会默默守护你4 眼前的方不是方。。5 一击即中的表白方式&#xff0c;学会了吗&#xff1f;&#xff1f; 图自甜星球日报社6 哦…了解了图自迷影心生 7 当90…

BIND的安装

ind建立步骤 1.解压bind安装文件tar zxvf bind-9.5.0.tar.gz2.进入bind安装目录cd bind-9.5.03.配置bind安装文件./configure --prefix/usr/local --enable-threads4.编译和安装bind文件make && make install5.建立主配置文件vi /etc/named.confoptions {directory &qu…

mysql2012用户名_SQL Server 登录名、服务器角色、用户名和数据库角色 --- 解释

1.一个数据库用户可以对应多个架构(架构是表容器)。架构里面包含的是数据库表。2.一个数据库角色有可能涉及多个架构。数据库角色对应的是权限。3.一个用户对应一个数据库角色。4.登录名与数据库用户在服务器级别是一对多的&#xff1b;在数据库级别是一对一的。服务器登录名&a…

有关转换问题

Convert.ChangeType 的运用 JOBEntity jobnew JOBEntity(); Type t job.GetType(); PropertyInfo[] info t.GetProperties(); foreach(PropertyInfo i in info){ //value 必须保证有效性&#xff0c;否则要对 PropertyType 进行 typeof 判断 if(i.PropertyType.IsGene…

Windows 11 正式版 ISO 镜像下载大全

Windows 11 ISO 镜像规格1.Windows 11 中文版文件大小&#xff1a;5.13 GB系统类型&#xff1a;64 位操作系统版本号&#xff1a;Windows 11 21H2发布日期&#xff1a;2021 年 10 月 5 日MD5: 68FB0CAD069CF82A9BA0996A6D3D112BSHA1: 9F602662FFAB0DF3B3A66FC6A42849BFFD3315A7…