问题描述:

想要改进这篇文章?提供这个问题的详细答案,包括引文和解释为什么你的答案是正确的。没有足够细节的答案可能会被编辑或删除。

Python 中的 yield 关键字有什么用?它有什么作用?

例如,我试图理解这段代码1:

def _get_child_candidates(self, distance, min_dist, max_dist):if self._leftchild and distance - max_dist < self._median:yield self._leftchildif self._rightchild and distance + max_dist >= self._median:yield self._rightchild  

这是调用者:

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 时会发生什么?是否返回列表?单一元素?又叫了吗?后续调用何时停止?

  1. 这段代码由 Jochen Schulz (jrschulz) 编写,他为度量空间制作了一个很棒的 Python 库。这是完整源代码的链接:Module mspace。

解决方案1:

huntsbot.com – 高效赚钱,自由工作

要了解 yield 的作用,您必须了解 generators 是什么。在您了解生成器之前,您必须了解 iterables。

可迭代对象

创建列表时,您可以一一阅读其项目。一项一项地读取它的项目称为迭代:

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

mylist 是一个可迭代。当您使用列表推导时,您会创建一个列表,因此是一个可迭代的:

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
...    print(i)
0
1
4

您可以使用“for… in…”的所有内容都是可迭代的; lists、strings、文件…

这些可迭代对象很方便,因为您可以随心所欲地读取它们,但是您将所有值存储在内存中,当您有很多值时,这并不总是您想要的。

发电机

生成器是迭代器,一种只能迭代一次的可迭代对象。生成器不会将所有值存储在内存中,它们会即时生成值:

>>> 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 是一个与 return 类似的关键字,但该函数将返回一个生成器。

>>> def create_generator():
...    mylist = range(3)
...    for i in mylist:
...        yield i*i
...
>>> mygenerator = create_generator() # create a generator
>>> print(mygenerator) # mygenerator is an object!>>> for i in mygenerator:
...     print(i)
0
1
4

这是一个无用的示例,但是当您知道您的函数将返回大量值而您只需要读取一次时,它会很方便。

要掌握yield,你必须明白调用函数时,你写在函数体中的代码不会运行。函数只返回生成器对象,这有点棘手。

然后,您的代码将在每次 for 使用生成器时从中断处继续。

现在最困难的部分:

for 第一次调用从您的函数创建的生成器对象时,它将从头开始运行您的函数中的代码,直到它到达 yield,然后它将返回循环的第一个值。然后,每个后续调用将运行您在函数中编写的循环的另一次迭代并返回下一个值。这将一直持续到生成器被认为是空的,当函数运行时没有点击 yield 时会发生这种情况。这可能是因为循环已经结束,或者因为您不再满足 “if/else”。

你的代码解释

发电机:

# 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 the distance is ok, return the next childif 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 the distance is ok, return the next childif 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

呼叫者:

# 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 listnode = candidates.pop()# Get the distance between obj and the candidatedistance = node._get_dist(obj)# If distance is ok, then you can fill the resultif distance <= max_dist and distance >= min_dist:result.extend(node._values)# Add the children of the candidate in the candidate's 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 candidatecandidates.extend(node._get_child_candidates(distance, min_dist, max_dist))return result

此代码包含几个智能部分:

循环在一个列表上进行迭代,但在迭代循环时列表会扩展。这是一种遍历所有这些嵌套数据的简洁方法,即使它有点危险,因为您最终可能会陷入无限循环。在这种情况下,candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) 耗尽了生成器的所有值,但同时不断创建新的生成器对象,这些对象将产生与以前的值不同的值,因为它没有应用在同一个对象上节点。

extend() 方法是一个列表对象方法,它需要一个可迭代对象并将其值添加到列表中。

通常我们传递一个列表给它:

>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]

但是在您的代码中,它有一个生成器,这很好,因为:

您不需要读取两次值。您可能有很多孩子,并且您不希望他们都存储在内存中。

它之所以有效,是因为 Python 不关心方法的参数是否为列表。 Python 需要可迭代对象,因此它可以处理字符串、列表、元组和生成器!这被称为鸭子类型,这也是 Python 如此酷的原因之一。但这是另一个故事,另一个问题…

你可以在这里停下来,或者阅读一下以了解生成器的高级用法:

控制发电机耗尽

>>> 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 模块包含操作可迭代对象的特殊函数。曾经想复制一个生成器吗?链接两个发电机?使用单线对嵌套列表中的值进行分组? Map / Zip 不创建另一个列表?

然后只需 import itertools。

一个例子?让我们看看四马比赛的可能到达顺序:

>>> 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)]

了解迭代的内在机制

迭代是一个包含可迭代对象(实现 iter() 方法)和迭代器(实现 next() 方法)的过程。可迭代对象是您可以从中获取迭代器的任何对象。迭代器是允许您迭代可迭代对象的对象。

在这篇关于 how for loops work 的文章中有更多关于它的信息。

yield 并不像这个答案所暗示的那么神奇。当您在任何地方调用包含 yield 语句的函数时,您将获得一个生成器对象,但没有代码运行。然后,每次从生成器中提取对象时,Python 都会执行函数中的代码,直到遇到 yield 语句,然后暂停并传递对象。当您提取另一个对象时,Python 会在 yield 之后继续并继续,直到它到达另一个 yield(通常是同一个,但稍后迭代)。这一直持续到函数运行结束,此时生成器被视为耗尽。

“这些迭代很方便......但是你将所有值都存储在内存中,这并不总是你想要的”,要么是错误的,要么是令人困惑的。可迭代对象在调用可迭代对象上的 iter() 时返回一个迭代器,并且迭代器并不总是必须将其值存储在内存中,取决于 iter 方法的实现,它还可以按需生成序列中的值。

很高兴添加到这个 great 答案中,为什么 除了您使用 () 而不是 [] 之外,它是一样的,特别是 () 是什么(有可能与元组混淆)。

@MatthiasFripp“这会一直持续到函数运行结束”——或者遇到 return 语句。 (在包含 yield 的函数中允许使用 return,前提是它没有指定返回值。)

yield 语句暂停函数的执行并将一个值发送回调用者,但保留足够的状态以使函数能够从中断的地方恢复。恢复后,函数会在最后一次 yield 运行后立即继续执行。这允许它的代码随着时间的推移产生一系列值,而不是一次计算它们并将它们像列表一样发送回来。

解决方案2:

HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com

了解产量的捷径

当您看到带有 yield 语句的函数时,应用这个简单的技巧来了解会发生什么:

在函数的开头插入一行 result = [] 。用 result.append(expr) 替换每个 yield expr。在函数底部插入一行返回结果。是的 - 没有更多的收益声明!阅读并找出代码。将功能与原始定义进行比较。

这个技巧可能会让您了解函数背后的逻辑,但是 yield 实际发生的情况与基于列表的方法中发生的情况有很大不同。在许多情况下,yield 方法的内存效率也会更高,速度也更快。在其他情况下,这个技巧会让你陷入无限循环,即使原始函数工作得很好。请继续阅读以了解更多信息…

不要混淆你的迭代器、迭代器和生成器

一、迭代器协议——当你写

for x in mylist:...loop body...

Python 执行以下两个步骤:

获取 mylist 的迭代器:调用 iter(mylist) -> 这将返回一个带有 next() 方法(或 Python 3 中的 next() )的对象。 [这是大多数人忘记告诉你的步骤] 使用迭代器循环项目:继续对从步骤 1 返回的迭代器调用 next() 方法。将 next() 的返回值分配给 x 和循环身体被执行。如果在 next() 中引发了异常 StopIteration,则意味着迭代器中没有更多的值并且退出循环。

事实上,Python 在任何时候都执行上述两个步骤循环一个对象的内容 - 所以它可能是一个 for 循环,但它也可能是像 otherlist.extend(mylist) 这样的代码(其中 {2 } 是一个 Python 列表)。

这里的 mylist 是一个可迭代,因为它实现了迭代器协议。在用户定义的类中,您可以实现 iter() 方法以使您的类的实例可迭代。这个方法应该返回一个迭代器。迭代器是具有 next() 方法的对象。可以在同一个类上同时实现 iter() 和 next(),并让 iter() 返回 self。这适用于简单的情况,但不适用于您希望两个迭代器同时遍历同一个对象的情况。

这就是迭代器协议,许多对象都实现了这个协议:

内置列表、字典、元组、集合、文件。实现 iter() 的用户定义类。发电机。

请注意,for 循环不知道它正在处理什么样的对象 - 它只是遵循迭代器协议,并且很高兴在调用 next() 时获取一个接一个的项目。内置列表一一返回它们的项目,字典一一返回键,文件一一返回行,等等。生成器返回…好吧这就是 yield 的用武之地:

def f123():yield 1yield 2yield 3for item in f123():print item

如果您在 f123() 中有三个 return 语句,而不是 yield 语句,则只有第一个会被执行,并且函数将退出。但是 f123() 不是普通的函数。调用 f123() 时,它不返回 yield 语句中的任何值!它返回一个生成器对象。此外,该功能并没有真正退出 - 它进入暂停状态。当 for 循环尝试遍历生成器对象时,该函数在它先前返回的 yield 之后的下一行从暂停状态恢复,执行下一行代码,在本例中为 {1 } 语句,并将其作为下一项返回。这种情况一直发生,直到函数退出,此时生成器引发 StopIteration,循环退出。

所以生成器对象有点像一个适配器 - 一方面它展示了迭代器协议,通过公开 iter() 和 next() 方法来保持 for 循环的正常运行。然而,在另一端,它运行的函数刚好足以从中获取下一个值,并将其重新置于挂起模式。

为什么要使用生成器?

通常,您可以编写不使用生成器但实现相同逻辑的代码。一种选择是使用我之前提到的临时列表“技巧”。这并非在所有情况下都有效,例如,如果您有无限循环,或者当您的列表非常长时,它可能会低效使用内存。另一种方法是实现一个新的可迭代类SomethingIter,它将状态保存在实例成员中,并在它的next()(或Python 3 中的__next__())方法中执行下一个逻辑步骤。根据逻辑,next() 方法中的代码最终可能看起来非常复杂并且容易出现错误。在这里,生成器提供了一个干净且简单的解决方案。

“当你看到一个带有 yield 语句的函数时,应用这个简单的技巧来理解会发生什么” 这难道不是完全忽略了你可以send进入一个生成器这一事实,这是一个很大的部分发电机的意义?

“它可以是一个 for 循环,但也可以是 otherlist.extend(mylist) 之类的代码”->这是不正确的。 extend() 就地修改列表并且不返回可迭代对象。尝试循环 otherlist.extend(mylist) 将失败并返回 TypeError,因为 extend() 隐式返回 None,并且您不能循环 None。

@pedro您误解了那句话。这意味着 python 在执行 otherlist.extend(mylist) 时会在 mylist 上(而不是在 otherlist 上)执行上述两个步骤。

解决方案3:

保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com

这样想:

对于具有 next() 方法的对象,迭代器只是一个听起来很花哨的术语。所以一个 yielded 函数最终会是这样的:

原始版本:

def some_function():for i in xrange(4):yield ifor i in some_function():print i

这基本上是 Python 解释器对上述代码所做的事情:

class it:def __init__(self):# Start at -1 so that we get 0 when we add 1 below.self.count = -1# The __iter__ method will be called once by the 'for' loop.# The rest of the magic happens on the object returned by this method.# In this case it is the object itself.def __iter__(self):return self# The next method will be called repeatedly by the 'for' loop# until it raises StopIteration.def next(self):self.count += 1if self.count < 4:return self.countelse:# A StopIteration exception is raised# to signal that the iterator is done.# This is caught implicitly by the 'for' loop.raise StopIterationdef some_func():return it()for i in some_func():print i

为了更深入地了解幕后发生的事情,可以将 for 循环重写为:

iterator = some_func()
try:while 1:print iterator.next()
except StopIteration:pass

这更有意义还是让你更困惑?

“yield”关键字有什么作用?相关推荐

  1. “ yield”关键字有什么作用?

    Python中yield关键字的用途是什么? 它有什么作用? 例如,我试图理解这段代码1 : def _get_child_candidates(self, distance, min_dist, m ...

  2. yield关键字有什么作用

    所属网站分类: python基础 > 语句 作者:goodbody 链接: http://www.pythonheidong.com/blog/article/10/ 来源:python黑洞网  ...

  3. Python生成器实现及yield关键字

    Python生成器实现及yield关键字 我在另一篇文章中介绍了Python迭代器,https://blog.csdn.net/weixin_43790276/article/details/9034 ...

  4. 反编译使用yield关键字的方法

    我认为这是一个真命题:"没有用.NET Reflector反编译并阅读过代码的程序员不是专业的.NET程序员"..NET Reflector强大的地方就在于可以把IL代码反编译成可 ...

  5. C#中使用的yield关键字是什么?

    在" 如何仅显示IList <>的片段"问题中,答案之一具有以下代码片段: IEnumerable<object> FilteredList() {fore ...

  6. python function if yield_Python中的yield关键字

    Python中的yield关键字 这是stackoverflow上一个关于yield关键字的问题以及它被推荐次数最高的一个答案 问题: Python中的yield关键字是什么?它是用来做什么的? 例如 ...

  7. python生成器yield原理_生成器yield关键字详解

    鉴于yield关键字的原理大家理解的都不是很深刻,今天我们主要就这一课题进行探讨. 生成器可以用什么方式得到? 方法一: 利用推导式的方式得到生成器# 列表推导式 list1 = [i for i i ...

  8. Python 生成器 和 yield 关键字

    Python 中 yield 的作用:http://youchen.me/2017/02/10/Python-What-does-yield-do/# Python 生成器详解:http://codi ...

  9. python lambda表达式及用法_Python:lambda表达式和yield关键字理解与使用讲解

    一.lambda表达式 1.1.lambda表达式理解 lambda的主体是一个表达式,而不是一个代码块,仅仅能在lambda表达式中封装有限的逻辑进去.如果要通俗的理解lambda表达式,可以结合C ...

最新文章

  1. 在SaaS领域,单纯的免费策略根本行不通!
  2. QEMU支持的网络模式
  3. jdbc执行Statement接口的步骤
  4. Tomcat的安装和环境变量配置
  5. java中的标识符和关键字_浅谈java中的标识符、修饰符和关键字
  6. boost的chrono模块线程时钟的测试程序
  7. Java 代码精简之道 | 长文
  8. 【codeforces 239B】Easy Tape Programming
  9. 计算机视觉CV中RANSAC算法的学习笔记~
  10. 什么平台给了社区站长机会
  11. WMS系统仓库条码管理流程解析
  12. Python多字段排序之cmp_to_key详解
  13. 韩立刚计算机网络——第四章:网络层
  14. 共模和差模电感电路分析方法及思路
  15. 计算机表格排版,你必须要知道的excel排版技巧
  16. win11如何设置空间音效 windows11设置空间音效的步骤方法
  17. Windows - 强力删除文件
  18. 来世你还能和你的父母重逢吗?
  19. RK CPU调试技巧
  20. 机械革命无法使用U盘启动linux,机械革命bios设置,教您机械革命bios怎么设置u盘启动...

热门文章

  1. 旅游网站的设计与实现
  2. 解决cc2015到期后不能再次破解问题,及提供cutterman,Mark Man下载地址
  3. 计算机专业打游戏哪个笔记本好,玩游戏笔记本电脑配置推荐有什么_玩游戏用什么笔记本电脑好-系统城...
  4. ERP发展趋势(转)
  5. Baxter工作站建立及简单使用
  6. [RK3399][Android7.1] 调试笔记 --- RTC读取时间失败
  7. Caché程序员必须知道符号与缩写 第二章 ObjectScript中使用的缩写
  8. 获取图片某一点的rgb色值
  9. Auto.js多点找色兼容不同分辨率
  10. c++ unescape