百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

如何在 Python 中使用“yield”?_pythonyield详解

itomcoil 2025-02-17 12:30 34 浏览

如果你是 Python 开发者,相信你一定知道 Python 中的 生成器。定义 Python 生成器的关键是使用“yield”关键字。Python 生成器普遍用于需要大集合的场景,提高代码的可读性以及多线程等其他特定场景。

你可能知道也可能不知道如何正确使用“yield”关键字。在本文中,我将从基本用法介绍 Python 生成器。后面的部分还将介绍一些更高级的使用模式。

1. 基础

让我们在本教程中编写一个示例。假设我们公司有一群人需要一个一个待命。所以,这将是一个轮换名单。

names = ["alice","bob","chris","jack","elio"]

现在,我们要从这个名称列表中定义一个 Python 生成器。代码如下。

def gen_roster(names):
    for name in names:
        yield name

请注意,我们需要使用yield关键字而不是return,因此我们可以使用此函数来制作生成器。

names = ["alice","bob","chris","jack","elio"]
def gen_roster(names):
    for name in names:
        yield name
roster = gen_roster(names)
print(type(roster))

运行输出:

现在,我们得到了rosterPython 生成器也是可迭代的,因此我们可以将其放入 for 循环中,一次性获取所有name

names = ["alice","bob","chris","jack","elio"]
def gen_roster(names):
    for name in names:
        yield name
roster = gen_roster(names)
for name in roster:
    print(name)

运行输出:

2. 使用__next__方法

好吧,在前面的示例中,在 for 循环中使用生成器并没有太大意义。使用生成器的好处是我们可以一次获得一个值。当我们处理大量集合时,这可能非常有用。也就是说,当我们创建一个生成器时,项目不会被读入内存。只有当我们试图获取下一个项目并点击yield关键字时,才会生成该项目。

因此,无论如何,重要的是获得生成器的“下一个”元素。在这种情况下,我们可以使用它的__next__()方法。

roster.__next__()

其实我们也可以按照下面的方式来做,更直观,更容易记忆。

next(roster)

你是否注意到当我们试图获取“下一个”列表元素“光标”被移动了?还记得我们在生成器的定义中定义了一个 for 循环吗?我们可以认为 for 循环将每次执行一次,直到到达关键字为止yield

这也意味着,我们的“光标”目前位于第三个名称。因此,如果我们尝试输出所有其余的项目,“alice”和“bob”将被跳过。

现在,“光标”在末尾。如果我们尝试获取下一个项目,则会抛出错误,因为没有更多列表元素。


这实际上不是我们想要的,因为name将用于决定谁在当前轮换中待命。换句话说,我们希望“光标”回到开头,这样所有的名字都会再次循环。

诀窍是简单的while True在 for 循环之上放置一个语句。因此,一旦所有名称都用完,for 循环将重新开始。

def gen_roster(names): 
    while True: 
        for name in names: 
            yield name

现在,假设我们想要一个有 12 个名字的名单。我们只有 5 名员工。因此,它们将被旋转。

names = ["alice","bob","chris","jack","elio"]
def gen_roster(names): 
    while True: 
        for name in names: 
            yield name
roster = gen_roster(names)

for i in range(12):
    print(next(roster))

如果我们不输入数字“12”,则生成器可以无限期地生成更多name。

向生成器发生一个值

生成器的一种高级用法是向生成器发送一个值。该值将成为当前yield 表达式的结果,该方法返回生成器生成的下一个值。

所以,不要指望生成器会返回我们刚刚发送的值,因为它会返回下一个。但是,我们可以使用它在生成器内部做一些事情。例如,我们可以通过向无限生成器发送某个值来停止它。

def gen_roster(names): 
    while names: 
        for name in names: 
            current_name = yield name 
            if current_name == 'stop': 
                names = None 
                break

如果从外部发送值“stop”,则生成器已定义为终止循环。因此,我们可以验证如下行为。

names = ["alice","bob","chris","jack","elio"]
def gen_roster(names): 
    while names: 
        for name in names: 
            current_name = yield name 
            if current_name == 'stop': 
                names = None 
                break
                
roster = gen_roster(names)
for i in range(10):
    if i == 3:
        roster.send('stop')
    print(next(roster))

运行输出:


在上面的代码中,我们要求程序循环 10 次。然而,对于第 4 轮,我们将值“stop”传递给生成器。结果,只输出了 3 个名字,并且生成器在第 4 轮就停止了,而不是循环了 10 次。

当我们想要在多线程编程场景中更改生成器的行为或规则时,send 方法将非常有用。

停止生成器—抛出异常并关闭

当出现异常问题时,我们可以使用该throw()方法在生成器暂停时引发异常。我们可以自定义错误类型。出于本文章中的演示目的,为了方便起见,我将简单地使用“TypeError”。

names = ["alice","bob","chris","jack","elio"]
def gen_roster(names): 
    while names: 
        for name in names: 
            current_name = yield name 
            if current_name == 'stop': 
                names = None 
                break
roster = gen_roster(names)
next_name = roster.throw(TypeError, 'Stop!')

如果没有出错,但我们仍然想终止生成器,我们可以使用close()生成器的方法。当我们有一个无限生成器并且我们想在某个点停止它时,这将非常有用

names = ["alice","bob","chris","jack","elio"]
def gen_roster(names): 
    while names: 
        for name in names: 
            current_name = yield name 
            if current_name == 'stop': 
                names = None 
                break
roster = gen_roster(names)
for i in range(10):
    if i == 3:
        roster.close()
    print(next(roster))

运行输出:

上面的代码在第 4 轮关闭了生成器“roster”,所以当下一个循环试图获取下一个值时,就会抛出异常

总结

在本文中,我介绍了 Python 中最重要的“Pythonic”概念之一生成器。关键字的使用yield是 Python 生成器的关键。

不仅介绍了基本用法,还介绍了一些高级用法,例如从外部向生成器发送值、引发异常和关闭生成器。希望它可以帮助你更多地了解 Python 生成器。

如果你发现我的任何文章对你有帮助或有用,麻烦点赞或者转发。 谢谢!

相关推荐

《Queendom》宣布冠军!女团MAMAMOO四人激动落泪

网易娱乐11月1日报道据台湾媒体报道,南韩女团竞争回归的生死斗《Queendom》昨(10/31)晚播出大决赛,并以直播方式进行,6组女团、女歌手皆演唱新歌,并加总前三轮的赛前赛、音源成绩与直播现场投...

正确复制、重写别人的代码,不算抄袭

我最近在一篇文章提到,工程师应该怎样避免使用大量的库、包以及其他依赖关系。我建议的另一种方案是,如果你没有达到重用第三方代码的阈值时,那么你就可以自己编写代码。在本文中,我将讨论一个在重用和从头开始编...

HTML DOM tr 对象_html event对象

tr对象tr对象代表了HTML表格的行。HTML文档中出现一个<tr>标签,就会创建一个tr对象。tr对象集合W3C:W3C标签。集合描述W3Ccells返回...

JS 打造动态表格_js如何动态改变表格内容

后台列表页最常见的需求:点击表头排序+一键全选。本文用原生js代码实现零依赖方案,涵盖DOM查询、排序算法、事件代理三大核心技能。效果速览一、核心思路事件入口:为每个<th>绑...

连肝7个晚上,总结了66条计算机网络的知识点

作者|哪吒来源|程序员小灰(ID:chengxuyuanxiaohui)计算机网络知识是面试常考的内容,在实际工作中经常涉及。最近,我总结了66条计算机网络相关的知识点。1、比较http0....

Vue 中 强制组件重新渲染的正确方法

作者:MichaelThiessen译者:前端小智来源:hackernoon有时候,依赖Vue响应方式来更新数据是不够的,相反,我们需要手动重新渲染组件来更新数据。或者,我们可能只想抛开当前的...

为什么100个前端只有1人能说清?浏览器重排/重绘深度解析

面试现场的"致命拷问""你的项目里做过哪些性能优化?能具体讲讲重排和重绘的区别吗?"作为面试官,我在秋招季连续面试过100多位前端候选人,这句提问几乎成了必考题。但令...

HTML DOM 介绍_dom4j html

HTMLDOM(文档对象模型)是一种基于文档的编程接口,它是HTML和XML文档的编程接口。它可以让开发人员通过JavaScript或其他脚本语言来访问和操作HTML和XML文档...

JavaScript 事件——“事件流和事件处理程序”的注意要点

事件流事件流描述的是从页面中接收事件的顺序。IE的事件流是事件冒泡流,而NetscapeCommunicator的事件流是事件捕获流。事件冒泡即事件开始时由最具体的元素接收,然后逐级向上传播到较为不...

探秘 Web 水印技术_水印制作网页

作者:fransli,腾讯PCG前端开发工程师Web水印技术在信息安全和版权保护等领域有着广泛的应用,对防止信息泄露或知识产品被侵犯有重要意义。水印根据可见性可分为可见水印和不可见水印(盲水印)...

国外顶流网红为流量拍摄性侵女学生?仅被封杀三月,回归仍爆火

曾经的油管之王,顶流网红DavidDobrik复出了。一切似乎都跟他因和成员灌酒性侵女学生被骂到退网之前一样:住在950万美元的豪宅,开着20万美元的阿斯顿马丁,每条视频都有数百万观看...人们仿佛...

JavaScript 内存泄漏排查方法_js内存泄漏及解决方法

一、概述本文主要介绍了如何通过Devtools的Memory内存工具排查JavaScript内存泄漏问题。先介绍了一些相关概念,说明了Memory内存工具的使用方式,然后介绍了堆快照的...

外贸独立站,网站优化的具体内容_外贸独立站,网站优化的具体内容有哪些

Wordpress网站优化,是通过优化代码、数据库、缓存、CSS/JS等内容,提升网站加载速度、交互性和稳定性。网站加载速度,是Google搜索引擎的第一权重,也是SEO优化的前提。1.优化渲染阻塞。...

这8个CSS工具可以提升编程速度_css用什么编译器

下面为大家推荐的这8个CSS工具,有提供函数的,有提供类的,有提取代码的,还有收集CSS的统计数据的……请花费两分钟的时间看完这篇文章,或许你会找到意外的惊喜,并且为你的编程之路打开了一扇新的大门。1...

vue的理解-vue源码 历史 简介 核心特性 和jquery区别 和 react对比

一、从历史说起Web是WorldWideWeb的简称,中文译为万维网我们可以将它规划成如下的几个时代来进行理解石器时代文明时代工业革命时代百花齐放时代石器时代石器时代指的就是我们的静态网页,可以欣...