python function if yield_成为Python大牛必须要掌握的高端语法——yield!
这是stackoverflow上一个关于python中yield用法的帖子,这里翻译自投票最高的一个回答,原文链接:
问题
Python中yield关键字的用途是什么?它有什么作用?
例如,我试图理解以下代码:
def _get_child_candidates(self, distance, min_dist, max_dist):
if self._leftchild and distance - max_dist < self._median:
yield self._leftchild
if self._rightchild and distance + max_dist >= self._median:
yield self._rightchild
这是调用者(caller):
result, candidates = [], [self]
while candidates:
node = candidates.pop()
distance = node._get_dist(obj)
if distance <= max_dist and distance >= min_dist:
result.extend(node._values)
candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result
当调用方法_get_child_candidates时会发生什么?返回了一个列表(list)?还是返回了一个元素?然后被重复调用了吗?调用何时结束?
代码来自 Jochen Schulz (jrschulz), who made a great Python library for metric spaces.
回答
要想理解yield的作用,你必须了解什么是生成器(generators),在这之前,我们先来看可迭代对象(iterables)。
可迭代对象 (iterables)
当你创建了一个列表,你可以遍历这个列表读取它的每一个元素,逐个读取列表元素称为迭代(iteration)。
>>> mylist = [1, 2, 3]
>>> for i in mylist:
... print(i)
1
2
3
mylist就是一个可迭代对象(iterable)。当你使用列表生成式(list comprehension)创建一个列表(list),即创建了一个可迭代对象。
>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
... print(i)
0
1
4
可以使用for... in...的所有对象都是可迭代对象:列表(lists)、字符串、文件...
这些可迭代对象使用很方便,因为你可以根据需要如你所愿的读取其中的元素。但是,当你有大量数据时把所有值都存储在内存中,这样往往不是你想要的( but you store all the values in memory and this is not always what you want when you have a lot of values.)。
生成器 (Generators)
生成器是迭代器(iterators),但是只能迭代一次,生成器不会将所有值存储在内存中,而是实时的生成这些值:
>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
... print(i)
0
1
4
看上去除了用()替换了原来的[]外,它们没什么不同。但是,你不可以再次使用for i in mygenerator ,因为生成器只能被迭代一次:计算出0,然后并不保存结果和状态继续计算出1,最后计算出4,逐一生成。
yield
yield 是一个类似 return 的关键字,不同的是这个函数将返回一个生成器。
>>> def createGenerator():
... mylist = range(3)
... for i in mylist:
... yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
>>> for i in mygenerator:
... print(i)
0
1
4
这个例子没有什么实际作用。但是当你知道你的函数将返回大量你只需要读取一次的值时,使用生成器是一个有效的做法。
要掌握 yeild,你必须要知道当你调用这个函数时,你在函数体中编写的代码并没有立马执行。
该函数仅仅返回一个生成器对象,这有点棘手 :-)
然后,你的代码将从for循环每次使用生成器停止的位置继续执行。
现在到了关键部分:
for第一次调用从函数创建的生成器对象,函数将从头开始执行直到遇到yeild,然后返回yield后的值作为第一次迭代的返回值。接下来每次调用都会再次执行你在函数中定义的循环,并返回(return)下一个值,直到没有值可以返回(return)。
当循环结束,或者不满足if/else条件,导致函数运行但不会执行(not hit)yeild,此时生成器被认为是空的。
问题代码的解释 (Your code explained)
生成器 (Generator):
# Here you create the method of the node object that will return the generator
def _get_child_candidates(self, distance, min_dist, max_dist):
# Here is the code that will be called each time you use the generator object:
# If there is still a child of the node object on its left
# AND if distance is ok, return the next child
if self._leftchild and distance - max_dist < self._median:
yield self._leftchild
# If there is still a child of the node object on its right
# AND if distance is ok, return the next child
if self._rightchild and distance + max_dist >= self._median:
yield self._rightchild
# If the function arrives here, the generator will be considered empty
# there is no more than two values: the left and the right children
调用者 (Caller):
# Create an empty list and a list with the current object reference
result, candidates = list(), [self]
# Loop on candidates (they contain only one element at the beginning)
while candidates:
# Get the last candidate and remove it from the list
node = candidates.pop()
# Get the distance between obj and the candidate
distance = node._get_dist(obj)
# If distance is ok, then you can fill the result
if distance <= max_dist and distance >= min_dist:
result.extend(node._values)
# Add the children of the candidate in the candidates list
# so the loop will keep running until it will have looked
# at all the children of the children of the children, etc. of the candidate
candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result
这段代码包含几个高明的部分:这个循环对列表进行迭代,但是迭代中列表还在不断扩展 :-) 这是一种遍历嵌套数据的简明方法,即使这样有些危险,因为你可能会陷入死循环中。在这个例子中,candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))穷尽了生成器产生的所有值,但while不断的创建新的生成器对象加入到列表,因为每个对象作用在不同节点上,所以每个生成器都将生成不同的值。
extend()是一个列表(list)对象的方法,作用于可迭代对象(iterable),并将其值添加到列表里。
通常,通常我们将列表作为参数传递给它:
>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]
但是在你的代码里它接收到的是一个生成器(generator),这很好,因为:你不必重复读取这些值
你可以有很多子对象,但不需要将它们都存储在内存里。
它很有效,因为Python不关心一个方法的参数是否是列表,Python只希望他是一个可迭代对象,所以这个参数可以是列表,元组,字符串和生成器!这就是所谓的duck typing ,这也是Python为何如此酷的原因之一,但这已经是另外一个问题了......
你可以在这里停下,来看一些生成器的高级用法:
控制生成器的穷尽 (Controlling a generator exhaustion)
>>> class Bank(): # Let's create a bank, building ATMs
... crisis = False
... def create_atm(self):
... while not self.crisis:
... yield "$100"
>>> hsbc = Bank() # When everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # Crisis is coming, no more money!
>>> print(corner_street_atm.next())
>>> wall_street_atm = hsbc.create_atm() # It's even true for new ATMs
>>> print(wall_street_atm.next())
>>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())
>>> brand_new_atm = hsbc.create_atm() # Build a new one to get back in business
>>> for cash in brand_new_atm:
... print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...
注意,对于Python 3,请使用 print(corner_street_atm.__next__()) 或者 print(next(corner_street_atm))
这在很多场景都非常有用,例如控制资源的获取。
Itertools,你最好的朋友 (Itertools, your best friend)
itertools模块包含很多处理可迭代对象的特殊方法。曾经想要复制一个生成器吗?连接两个生成器?用一行代码将嵌套列表中的值进行分组?不创建另一个列表进行Map/Zip?
只需要import itertools
需要一个例子?让我们来看看4匹马赛跑到达终点先后顺序的所有可能情况:
>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
>>> 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)]
了解迭代的内部机制 (Understanding the inner mechanisms of iteration)
迭代是一个实现可迭代对象(实现的是 __iter__() 方法)和迭代器(实现的是 __next__() 方法)的过程。你可以获取一个迭代器的任何对象都是可迭代对象,迭代器可以让你迭代遍历一个可迭代对象(Iterators are objects that let you iterate on iterables.) .
python function if yield_成为Python大牛必须要掌握的高端语法——yield!相关推荐
- python大牛_成为Python大牛必须要掌握的高端语法——yield
成为Python大牛必须要掌握的高端语法--yield 1. 什么是yield 在介绍yield语法之前,首先要向大家说明Python中的迭代(iteration).可迭代(iterable).迭代器 ...
- 成为Python大牛必须要掌握的高端语法(附链接代码)
来源:机器学习算法与Python实战 本文约3000字,建议阅读5分钟 必要掌握的高端语法助你成为Python大牛. 这是stackoverflow上一个关于python中yield用法的帖子,这里翻 ...
- node函数 python_成为Python大牛必须要掌握的高端语法(附链接代码)
来源:机器学习算法与Python实战 本文约3000字,建议阅读5分钟 必要掌握的高端语法助你成为Python大牛. 这是stackoverflow上一个关于python中yield用法的帖子,这里翻 ...
- Python必须要掌握的高端语法
关注上方"深度学习技术前沿",选择"星标公众号", 资源干货,第一时间送达! 作者:grisse 链接: https://segmentfault.com ...
- Python精讲Numpy基础,大牛笔记详细解释
https://www.toutiao.com/a6664936105076326920/ 总认为Numpy是渣渣,直到深入接触以后才知道功能这么强大.堪比Matlab啊.果然是人生苦短,我用Pyth ...
- could not export python function call python_value. Remove calls to Python functions before export
could not export python function call <python_value>. Remove calls to Python functions before ...
- could not export python function call Remove calls to Python functions before export
could not export python function call <python_value>. Remove calls to Python functions before ...
- python function terminated_calibre 打不开也转不了 并且出现错误 mobi转docx
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 'verbose': 2} InputFormatPlugin: MOBI Input running on C:\Users\ADMINI~1\AppD ...
- python社招面试_百度大牛总结十条Python面试题陷阱,看看你是否会中招
每年的3-4月份是跳槽的高峰期,无论是应聘Python web开发,爬虫工程师,或是数据分析,还是自动化运维,都涉及到一些基础的知识!我挑了一些Python的基础面试题,看看你能不能的答上来,也许面试 ...
最新文章
- linux ext4增加大小,如何修改 ext4 文件系统的大小
- 设计模式实例(Lua)笔记之五(Bridge模式)
- Java多线程-synchronized关键字
- go语言学习(2)rune以及strings
- istqb证书含金量_“性能测试” 领域含金量最高的资格认证:LoadRunner ASP
- 给vmstat加上时间戳
- 如何判断是不是真正的物理隔离网络光端机
- c cuda 指定gpu_GPU并行编程:熟练使用CUDA C语言
- MySQL命令行导出数据库
- SQL:如何用一个sql统计出全校男生个数、女生个数以及总人数
- PHP5.3, PHP5.4, PHP5.5新特性
- 荣耀50 Pro+配置参数曝光:AMOLED高刷屏+骁龙888旗舰芯片
- 马斯克获评最鼓舞人心科技领导者,马云排名第5
- python生产教程_python入门教程12-09 (python语法入门之生产者消费者模型)
- mysql5.6开启binlog日志
- 分析易语言多久可以学会呢.易语言好学吗
- C语言谭浩强试题,c语言试题谭浩强Word版
- Goonie企业竞争情报系统评为选中国优秀网络应用软件奖
- 0xC0000005: 读取位置 xxx时发生访问冲突
- QQ游戏-大家来找茬 外挂
热门文章
- PLC与常用设备的连接
- 【联合仿真】Adams六关节机械臂与Matlab/Simulink的联合仿真(下)
- 梅森旋转产生随机数c语言实现,梅森旋转法产生随机数
- mysql8.0日期类型_mysql8.0.19基础数据类型详解
- python判断英文字母_python判断字符串是否包含字母
- Oracle的权限角色及用户
- PX4 FMU [7] rgbled [转载]
- Centos7 设置静态IP后重启网络服务出错
- CXF WebService 教程
- 数据结构C语言版之线性表