首页 > 编程语言 >Python 代码中的 yield 到底是什么?

Python 代码中的 yield 到底是什么?

时间:2024-07-28 19:39:26浏览次数:13  
标签:dist 迭代 Python 代码 生成器 yield 列表

在Python编程中,有一个强大而神秘的关键字,那就是yield。初学者常常被它搞得晕头转向,而高级开发者则借助它实现高效的代码。到底yield是什么?它又是如何在Python代码中发挥作用的呢?让我们一起来揭开它的面纱。

Python里的一个非常重要但也颇具迷惑性的关键词——yield

什么是yield?为什么我们需要在Python中使用它?

 

 

来,让我们一起来拆解一下,看看yield到底是个啥。

 

 

迭代与可迭代对象

 


要搞明白yield,咱们先得弄清楚什么是可迭代对象(iterables)。

所谓可迭代对象,简单来说,就是你可以逐个读取其元素的对象,比如列表、字符串、文件等等。举个例子,当你创建一个列表时,你可以用for循环一个个地读取它的元素:

mylist = [1, 2, 3]
for i in mylist:
print(i)

 

输出会是:

1
2
3

 

这里的mylist就是一个可迭代对象。你还可以用列表推导式(list comprehension)来创建一个列表,它同样也是可迭代的:​​​​​​​

mylist = [x*x for x in range(3)]
for i in mylist:
print(i)

 

输出是:​​​​​​​

0
1
4

 

凡是你可以用for... in...来操作的东西,都是可迭代对象,包括列表、字符串、文件等等。

可迭代对象非常方便,因为你可以任意多次地读取它们的值,但前提是你得把所有值都存储在内存里。这就带来了一个问题:当数据量很大时,这种方式显然不太合适。

 

生成器

 


生成器(generators)是迭代器的一种,你只能遍历它们一次。生成器不像列表那样把所有的值都存储在内存里,而是即用即生成。来看看生成器的例子:​​​​​​​

mygenerator = (x*x for x in range(3))
for i in mygenerator:
print(i)

 

输出和列表推导式一样:​​​​​​​

0
1
4

 

但注意了,生成器只能使用一次,因为它们会“边用边忘”:计算0后忘记0,计算1后忘记1,最后计算4后结束。再用同一个生成器对象做for循环就没有结果了。

 

 

 

 

yield关键词

 


说到yield,这是个类似于return的关键词,但它返回的不是一个值,而是一个生成器。看看这个例子:​​​​​​​

def create_generator():
mylist = range(3)
for i in mylist:
yield i*i

mygenerator = create_generator() # 创建一个生成器
print(mygenerator) # mygenerator 是一个生成器对象!

 

输出是:

<generator object create_generator at 0xb7555c34>

 

通过for循环遍历这个生成器:​​​​​​​

for i in mygenerator:
print(i)

 

输出:​​​​​​​

0
1
4

 

这个例子看起来简单,但它在处理大量数据时特别有用,因为生成器只在需要时生成值,而不是一次性生成所有值然后存储在内存中。

 

 

深入理解yield

 


为了彻底掌握yield,我们需要理解当调用生成器函数时,函数体内的代码并不会立即执行。函数返回的是一个生成器对象,然后你的代码会在每次调用for循环时从上次中断的地方继续执行,直到遇到下一个yield。

 

第一次调用for循环时,生成器对象会从头开始运行函数中的代码,直到遇到yield,然后返回循环中的第一个值。随后的每次调用都会执行函数中循环的下一次迭代,直到生成器不再有值返回。这可能是因为循环结束了,或者条件不再满足。

 

来看看一个实际的例子:​

1 def _get_child_candidates(self, distance, min_dist, max_dist):
2     if self._leftchild and distance - max_dist < self._median:
3         yield self._leftchild
4     if self._rightchild and distance + max_dist >= self._median:
5         yield self._rightchild

 

这里的代码在每次使用生成器对象时都会被调用:

 

如果节点对象还有左子节点并且距离合适,返回下一个子节点。

如果节点对象还有右子节点并且距离合适,返回下一个子节点。

如果没有更多子节点,生成器会被认为是空的。

 

调用这个生成器的方法如下:​​​​​​​

 

1 result, candidates = list(), [self]
2 while candidates:
3     node = candidates.pop()
4     distance = node._get_dist(obj)
5     if distance <= max_dist and distance >= min_dist:
6         result.extend(node._values)
7     candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
8 
9 return result

 

这里的代码有几个巧妙之处:

 

  • 循环遍历一个列表,而列表在循环过程中会扩展。这样可以方便地遍历所有嵌套的数据,虽然有些危险,因为可能会陷入无限循环。在这个例子中,candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))用尽生成器的所有值,但while循环不断创建新的生成器对象,因为它们作用在不同的节点上会产生不同的值。
  • extend()方法是列表对象的方法,它期望一个可迭代对象,并将其值添加到列表中。通常我们传递一个列表给它,但在代码中,它接收一个生成器,这是个好主意,因为:
  • 你不需要读取值两次。
  • 你可能有很多子节点,不想全部存储在内存中。

 

这段代码展示了Python为何如此酷:它不在乎方法的参数是列表还是其他可迭代对象。这种特性叫鸭子类型(duck typing),也是Python灵活性的一个体现。

 

高级用法

 


再来看一个更高级的用法——控制生成器的耗尽:​​​​​​​

 

 1 class Bank():
 2     crisis = False
 3     def create_atm(self):
 4         while not self.crisis:
 5             yield "$100"
 6 
 7 hsbc = Bank()
 8 corner_street_atm = hsbc.create_atm()
 9 print(next(corner_street_atm)) # 输出 $100
10 print(next(corner_street_atm)) # 输出 $100
11 print([next(corner_street_atm) for _ in range(5)]) # 输出 ['$100', '$100', '$100', '$100', '$100']
12 
13 hsbc.crisis = True
14 print(next(corner_street_atm)) # 输出 StopIteration

 

这里我们模拟了一个ATM机,在银行没有危机时,你可以不断取钱,但一旦危机来了,ATM机就会停止工作,即使是新的ATM机也不能再取钱了。

 

itertools模块

 


最后,给大家介绍一个非常有用的模块——itertools。这个模块包含了很多操作可迭代对象的特殊函数。如果你曾经希望复制一个生成器、连接两个生成器、用一行代码将值分组到嵌套列表中,或者在不创建另一个列表的情况下使用map和zip,那么就应该导入itertools。

举个例子,我们看看四匹马比赛的可能到达顺序:​​​​​​​

import itertools

horses = [1, 2, 3, 4]
races = itertools.permutations(horses)
print(list(itertools.permutations(horses)))

 

输出:

[(1, 2, 3, 4), (1, 2, 4, 3), (1, 3, 2, 4), (1, 3, 4, 2), (1, 4, 2, 3), (1, 4, 3, 2), (2, 1, 3, 4), (2, 1, 4, 3), (2, 3, 1, 4), (2, 3, 4, 1), (2, 4, 1, 3), (2, 4, 3, 1), (3, 1, 2, 4), (3, 1, 4, 2), (3, 2, 1, 4), (3, 2, 4, 1), (3, 4, 1, 2), (3, 4, 2, 1), (4, 1, 2, 3), (4, 1, 3, 2), (4, 2, 1, 3), (4, 2, 3, 1), (4, 3, 1, 2), (4, 3, 2, 1)]

 

itertools模块简直是Python程序员的好伙伴,可以让你在处理迭代对象时如虎添翼。

 

总结

 


yield是Python中一个强大的工具,它可以帮助你以一种高效的方式处理大量数据。理解yield的工作原理对于掌握Python编程至关重要。

在大数据时代,处理海量数据已成为常态。生成器作为一种高效的数据处理方式,因其优越的内存管理能力,受到了越来越多开发者的青睐。无论是日志处理、数据流分析,还是实时数据处理,生成器都展现了不可替代的价值。

通过对yield的详解,我们不仅理解了它的基本概念和用法,还认识到它在高效数据处理中的重要性。掌握yield,将为你的Python编程之旅增添一把利器。

 

标签:dist,迭代,Python,代码,生成器,yield,列表
From: https://www.cnblogs.com/zhuuque23/p/18328757

相关文章

  • 【Python】Django学习1
     按黑马程序员的美多商场作方向:https://www.bilibili.com/video/BV1nf4y1k7G3一、应用创建、注册处理、配置Pycharm 创建Django项目: 自应用注册处理: 二、应用数据初始化第一步:创建元数据初始化py脚本pythonmanage.pymakemigrations初始化的脚本会放在......
  • 【代码随想录训练营第42期 Day10打卡 LeetCode 232.用栈实现队列 225. 用队列实现栈 2
    目录一、做题心得二、题目与题解题目一:232.用栈实现队列题目链接题解题目二:225.用队列实现栈题目链接题解题目三:20.有效的括号题目链接题解题目四:1047.删除字符串中的所有相邻重复项 题目链接题解三、小结一、做题心得今天是代码随想录训练营打卡的第1......
  • Python爬虫入门01:在Chrome浏览器轻松抓包
    文章目录爬虫基本概念爬虫定义爬虫工作原理爬虫流程爬虫类型爬虫面临的挑战使用Chrome浏览器抓包查看网页HTML代码查看HTTP请求请求头(RequestHeader)服务器响应抓包的意义爬虫基本概念爬虫定义爬虫(WebCrawler或Spider)是一种自动浏览互联网的程序,它按照一定的......
  • Python科研武器库 - 字符串操作 - 字符串分隔 split()、rsplit()
    使用场景:在科研中,数据样本的命名方式往往包含着对应的信息,例如,一张人脸图片34_anger_1.png,文件名中包含着该图片中主体在数据集中的ID、人脸表情种类、以及该图片是同类型第几张图片。使用python批量处理数据时,从文件名中获取对应信息也是很关键的一步,然后才能基于这些信息进......
  • 小白必看的cmd简单代码!(图片看不到的可复制 粘贴到Typroa进行观看)
    打卡cmd的方法直接window加r输入cmd在下方菜单找到window标志,打开输入命令提示符更高级的cmd权限使用:右键命令提示符,点击"以管理员身份运行"一些简单的dos命令(均需英文输入法)(回车步骤省略)1.盘符切换:打开cmd后输入想要切换的磁盘再加上:即可![](C:\Users\直実\Pictures......
  • R语言随机森林RandomForest、逻辑回归Logisitc预测心脏病数据和可视化分析|附代码数据
    全文链接:http://tecdat.cn/?p=22596最近我们被客户要求撰写关于预测心脏病的研究报告,包括一些图形和统计输出。本报告是对心脏研究的机器学习/数据科学调查分析。更具体地说,我们的目标是在心脏研究的数据集上建立一些预测模型,并建立探索性和建模方法。但什么是心脏研究?研究大纲......
  • R语言分布滞后非线性模型(DLNM)研究发病率,死亡率和空气污染示例|附代码数据
    全文下载链接:http://tecdat.cn/?p=21317最近我们被客户要求撰写关于分布滞后非线性模型(DLNM)的研究报告,包括一些图形和统计输出。本文提供了运行分布滞后非线性模型的示例,同时描述了预测变量和结果之间的非线性和滞后效应,这种相互关系被定义为暴露-滞后-反应关联数据集包含1987-......
  • Python用GARCH、离散随机波动率模型DSV模拟和估计股
    原文链接:http://tecdat.cn/?p=25165 原文出处:拓端数据部落公众号这篇文章介绍了一类离散随机波动率模型,并介绍了一些特殊情况,包括GARCH和ARCH模型。本文展示了如何模拟这些过程以及参数估计。本文为这些实验编写的Python代码在文章末尾引用。离散随机波动率模型是一个......
  • Python - Plotly Express - x 轴 xticks
    我有一个代码,我想用标签作为月份和年份的名称绘制x轴。但目前它正在x轴上绘制所有时间戳,如-代码片段是:fig=px.scatter(data_frame,x=data_frame.timestamp,y=data_frame.err_codes_integer,hover_name=data_frame.col3,......
  • 8:Python字符串的魔法属性
    test="alex"v=test.capitalize()#首字母的大写print(v)test1="alEx"v1=test1.casefold()#所有字母变小写print(v1)test2="alEx"v2=test2.center(20,"*")#设置宽度,并将内容居中,20代指总长度,*代指空白位置填充且只能填一个字符print(v2)test3="alexralexr"v3=te......