本文翻译自:In practice, what are the main uses for the new “yield from” syntax in Python 3.3?

I'm having a hard time wrapping my brain around PEP 380 . 我很难缠住PEP 380 。

  1. What are the situations where "yield from" is useful? 在什么情况下“从中获得收益”有用?
  2. What is the classic use case? 什么是经典用例?
  3. Why is it compared to micro-threads? 为什么与微线程相比?

[ update ] [更新]

Now I understand the cause of my difficulties. 现在,我了解了造成困难的原因。 I've used generators, but never really used coroutines (introduced by PEP-342 ). 我曾经使用过生成器,但从未真正使用过协程(由PEP-342引入)。 Despite some similarities, generators and coroutines are basically two different concepts. 尽管有一些相似之处,但生成器和协程基本上是两个不同的概念。 Understanding coroutines (not only generators) is the key to understanding the new syntax. 了解协程(不仅是生成器)是了解新语法的关键。

IMHO coroutines are the most obscure Python feature , most books make it look useless and uninteresting. 恕我直言, 协程是最晦涩的Python功能 ,大多数书籍使它看起来毫无用处且无趣。

Thanks for the great answers, but special thanks to agf and his comment linking to David Beazley presentations . 感谢您做出的出色回答,特别感谢agf及其与David Beazley演讲相关的评论。 David rocks. 大卫·罗克。


#1楼

参考:https://stackoom.com/question/ejjC/实际上-Python-中新的-yield-from-语法的主要用途是什么


#2楼

Let's get one thing out of the way first. 让我们先解决一件事。 The explanation that yield from g is equivalent to for v in g: yield v does not even begin to do justice to what yield from is all about. yield from g等同for v in g: yield v的解释for v in g: yield v 甚至还没有开始对所yield from的全部yield from 公平 Because, let's face it, if all yield from does is expand the for loop, then it does not warrant adding yield from to the language and preclude a whole bunch of new features from being implemented in Python 2.x. 因为,让我们面对现实,如果yield from所有yield from都是扩大for循环,那么它就不能保证在语言中增加yield from也就无法在Python 2.x中实现一大堆新功能。

What yield from does is it establishes a transparent bidirectional connection between the caller and the sub-generator : 什么yield from所做的就是建立主叫方和副发电机之间的透明双向连接

  • The connection is "transparent" in the sense that it will propagate everything correctly too, not just the elements being generated (eg exceptions are propagated). 从某种意义上说,该连接是“透明的”,它也将正确地传播所有内容,而不仅仅是所生成的元素(例如,传播异常)。

  • The connection is "bidirectional" in the sense that data can be both sent from and to a generator. 数据可以生成器发送生成器的意义上说,该连接是“双向的”。

( If we were talking about TCP, yield from g might mean "now temporarily disconnect my client's socket and reconnect it to this other server socket". ) 如果我们正在谈论TCP,则yield from g可能意味着“现在暂时断开客户端的套接字,然后将其重新连接到另一个服务器套接字”。

BTW, if you are not sure what sending data to a generator even means, you need to drop everything and read about coroutines first—they're very useful (contrast them with subroutines ), but unfortunately lesser-known in Python. 顺便说一句,如果您不确定向生成器发送数据意味着什么,则需要删除所有内容并首先了解协程 -它们非常有用(将它们与子例程进行对比),但是不幸的是在Python中鲜为人知。 Dave Beazley's Curious Course on Couroutines is an excellent start. 戴夫·比兹利(Dave Beazley)的“关于古皮的好奇课程”是一个很好的起点。 Read slides 24-33 for a quick primer. 阅读幻灯片24-33以获得快速入门。

Reading data from a generator using yield from 使用以下命令从生成器读取数据

def reader():"""A generator that fakes a read from a file, socket, etc."""for i in range(4):yield '<< %s' % idef reader_wrapper(g):# Manually iterate over data produced by readerfor v in g:yield vwrap = reader_wrapper(reader())
for i in wrap:print(i)# Result
<< 0
<< 1
<< 2
<< 3

Instead of manually iterating over reader() , we can just yield from it. 代替手动遍历reader() ,我们可以从中yield from

def reader_wrapper(g):yield from g

That works, and we eliminated one line of code. 那行得通,我们消除了一行代码。 And probably the intent is a little bit clearer (or not). 意图可能会更清晰(或不太清楚)。 But nothing life changing. 但是生活没有改变。

Sending data to a generator (coroutine) using yield from - Part 1 使用第1部分中的收益将数据发送到生成器(协程)

Now let's do something more interesting. 现在,让我们做一些更有趣的事情。 Let's create a coroutine called writer that accepts data sent to it and writes to a socket, fd, etc. 让我们创建一个名为writer的协程,该协程接受发送给它的数据并写入套接字,fd等。

def writer():"""A coroutine that writes data *sent* to it to fd, socket, etc."""while True:w = (yield)print('>> ', w)

Now the question is, how should the wrapper function handle sending data to the writer, so that any data that is sent to the wrapper is transparently sent to the writer() ? 现在的问题是,包装器函数应该如何处理将数据发送到writer,以便将发送到包装器的任何数据透明地发送到writer()

def writer_wrapper(coro):# TBDpassw = writer()
wrap = writer_wrapper(w)
wrap.send(None)  # "prime" the coroutine
for i in range(4):wrap.send(i)# Expected result
>>  0
>>  1
>>  2
>>  3

The wrapper needs to accept the data that is sent to it (obviously) and should also handle the StopIteration when the for loop is exhausted. 包装器需要(显然) 接受发送给它的数据,并且在for循环用完时还应该处理StopIteration Evidently just doing for x in coro: yield x won't do. 显然,只是for x in coro: yield x执行for x in coro: yield x不会。 Here is a version that works. 这是一个有效的版本。

def writer_wrapper(coro):coro.send(None)  # prime the corowhile True:try:x = (yield)  # Capture the value that's sentcoro.send(x)  # and pass it to the writerexcept StopIteration:pass

Or, we could do this. 或者,我们可以这样做。

def writer_wrapper(coro):yield from coro

That saves 6 lines of code, make it much much more readable and it just works. 这样可以节省6行代码,使其更具可读性,并且可以正常工作。 Magic! 魔法!

Sending data to a generator yield from - Part 2 - Exception handling 从第2部分-异常处理将数据发送到生成器收益

Let's make it more complicated. 让我们使其更加复杂。 What if our writer needs to handle exceptions? 如果我们的作者需要处理异常怎么办? Let's say the writer handles a SpamException and it prints *** if it encounters one. 假设writer处理SpamException ,如果遇到SpamException则输出***

class SpamException(Exception):passdef writer():while True:try:w = (yield)except SpamException:print('***')else:print('>> ', w)

What if we don't change writer_wrapper ? 如果我们不更改writer_wrapper怎么办? Does it work? 它行得通吗? Let's try 我们试试吧

# writer_wrapper same as abovew = writer()
wrap = writer_wrapper(w)
wrap.send(None)  # "prime" the coroutine
for i in [0, 1, 2, 'spam', 4]:if i == 'spam':wrap.throw(SpamException)else:wrap.send(i)# Expected Result
>>  0
>>  1
>>  2
***
>>  4# Actual Result
>>  0
>>  1
>>  2
Traceback (most recent call last):... redacted ...File ... in writer_wrapperx = (yield)
__main__.SpamException

Um, it's not working because x = (yield) just raises the exception and everything comes to a crashing halt. 嗯,这行不通,因为x = (yield)只会引发异常,并且一切都会崩溃。 Let's make it work, but manually handling exceptions and sending them or throwing them into the sub-generator ( writer ) 让它正常工作,但是手动处理异常并将其发送或将其抛出到子生成器( writer )中

def writer_wrapper(coro):"""Works. Manually catches exceptions and throws them"""coro.send(None)  # prime the corowhile True:try:try:x = (yield)except Exception as e:   # This catches the SpamExceptioncoro.throw(e)else:coro.send(x)except StopIteration:pass

This works. 这可行。

# Result
>>  0
>>  1
>>  2
***
>>  4

But so does this! 但是,这也是!

def writer_wrapper(coro):yield from coro

The yield from transparently handles sending the values or throwing values into the sub-generator. yield fromyield from透明地处理将值发送或将值扔到子生成器中。

This still does not cover all the corner cases though. 但是,这仍然不能涵盖所有极端情况。 What happens if the outer generator is closed? 如果外部发电机关闭,会发生什么? What about the case when the sub-generator returns a value (yes, in Python 3.3+, generators can return values), how should the return value be propagated? 如果子生成器返回一个值(是的,在Python 3.3+中,生成器可以返回值),该如何处理? That yield from transparently handles all the corner cases is really impressive . 透明地处理所有极端情况的yield from确实令人印象深刻 。 yield from just magically works and handles all those cases. yield from神奇的yield from并处理所有这些情况。

I personally feel yield from is a poor keyword choice because it does not make the two-way nature apparent. 我个人认为yield from是一个糟糕的关键字选择,因为它不会使双向性变得显而易见。 There were other keywords proposed (like delegate but were rejected because adding a new keyword to the language is much more difficult than combining existing ones. 还提出了其他关键字(例如delegate但遭到拒绝,因为向语言添加新关键字比合并现有关键字要困难得多。

In summary, it's best to think of yield from as a transparent two way channel between the caller and the sub-generator. 总之,最好将yield from视为调用方和子生成方之间的transparent two way channel

References: 参考文献:

  1. PEP 380 - Syntax for delegating to a sub-generator (Ewing) [v3.3, 2009-02-13] PEP 380-委派给子发电机的语法(Ewing)[v3.3,2009-02-13]
  2. PEP 342 - Coroutines via Enhanced Generators (GvR, Eby) [v2.5, 2005-05-10] PEP 342-通过增强型生成器进行协同程序(GvR,Eby)[v2.5,2005-05-10]

#3楼

A short example will help you understand one of yield from 's use case: get value from another generator 一个简短的示例将帮助您了解用例的一种yield from :从另一个生成器获取价值

def flatten(sequence):"""flatten a multi level list or something>>> list(flatten([1, [2], 3]))[1, 2, 3]>>> list(flatten([1, [2], [3, [4]]]))[1, 2, 3, 4]"""for element in sequence:if hasattr(element, '__iter__'):yield from flatten(element)else:yield elementprint(list(flatten([1, [2], [3, [4]]])))

#4楼

In applied usage for the Asynchronous IO coroutine , yield from has a similar behavior as await in a coroutine function . 在异步IO协程的应用用法中, yield from行为与协程函数中的 await行为类似。 Both of which is used to suspend the execution of coroutine. 两者都用于中止协程的执行。

  • yield from is used by the generator-based coroutine . 基于生成器的协程使用的yield from

  • await is used for async def coroutine. await用于async def协程。 (since Python 3.5+) (自Python 3.5+开始)

For Asyncio, if there's no need to support an older Python version (ie >3.5), async def / await is the recommended syntax to define a coroutine. 对于Asyncio,如果不需要支持较旧的Python版本(即> 3.5),则建议使用async def / await定义协程的语法。 Thus yield from is no longer needed in a coroutine. 因此,协程中不再需要yield from

But in general outside of asyncio, yield from <sub-generator> has still some other usage in iterating the sub-generator as mentioned in the earlier answer. 但是,通常在异步之外, yield from <sub-generator>在迭代sub-generator时还有其他用法,如先前答案中所述。


#5楼

This code defines a function fixed_sum_digits returning a generator enumerating all six digits numbers such that the sum of digits is 20. 该代码定义了一个fixed_sum_digits函数,该函数返回一个生成器,该生成器枚举所有六个数字的数字,以使数字的总和为20。

def iter_fun(sum, deepness, myString, Total):if deepness == 0:if sum == Total:yield myStringelse:  for i in range(min(10, Total - sum + 1)):yield from iter_fun(sum + i,deepness - 1,myString + str(i),Total)def fixed_sum_digits(digits, Tot):return iter_fun(0,digits,"",Tot)

Try to write it without yield from . 尝试编写时不要yield from If you find an effective way to do it let me know. 如果您找到有效的方法,请告诉我。

I think that for cases like this one: visiting trees, yield from makes the code simpler and cleaner. 我认为对于这种情况:访问树, yield from会使代码更简单,更清晰。


#6楼

What are the situations where "yield from" is useful? 在什么情况下“从中获得收益”有用?

Every situation where you have a loop like this: 您遇到这样的循环的每种情况:

for x in subgenerator:yield x

As the PEP describes, this is a rather naive attempt at using the subgenerator, it's missing several aspects, especially the proper handling of the .throw() / .send() / .close() mechanisms introduced by PEP 342 . 正如PEP所描述的,这是使用子生成器的相当幼稚的尝试,它缺少几个方面,尤其是对PEP 342引入的.throw() / .send() / .close()机制的正确处理。 To do this properly, rather complicated code is necessary. 要正确执行此操作,需要相当复杂的代码。

What is the classic use case? 什么是经典用例?

Consider that you want to extract information from a recursive data structure. 考虑您要从递归数据结构中提取信息。 Let's say we want to get all leaf nodes in a tree: 假设我们要获取树中的所有叶节点:

def traverse_tree(node):if not node.children:yield nodefor child in node.children:yield from traverse_tree(child)

Even more important is the fact that until the yield from , there was no simple method of refactoring the generator code. 更重要的是,直到yield from为止,没有重构生成器代码的简单方法。 Suppose you have a (senseless) generator like this: 假设您有一个(无意义的)生成器,如下所示:

def get_list_values(lst):for item in lst:yield int(item)for item in lst:yield str(item)for item in lst:yield float(item)

Now you decide to factor out these loops into separate generators. 现在,您决定将这些循环分解为单独的生成器。 Without yield from , this is ugly, up to the point where you will think twice whether you actually want to do it. 没有yield from ,这是很丑陋的,以至于您会三思而后行是否真的要这样做。 With yield from , it's actually nice to look at: 有了的yield from ,实际上可以很高兴地看到:

def get_list_values(lst):for sub in [get_list_values_as_int, get_list_values_as_str, get_list_values_as_float]:yield from sub(lst)

Why is it compared to micro-threads? 为什么与微线程相比?

I think what this section in the PEP is talking about is that every generator does have its own isolated execution context. 我认为PEP的这一部分所谈论的是,每个生成器确实都有其自己的隔离执行上下文。 Together with the fact that execution is switched between the generator-iterator and the caller using yield and __next__() , respectively, this is similar to threads, where the operating system switches the executing thread from time to time, along with the execution context (stack, registers, ...). 再加上分别使用yield__next__()在生成器-迭代器和调用者之间切换执行的事实,这类似于线程,其中操作系统不时切换执行线程以及执行上下文(堆栈,寄存器...)。

The effect of this is also comparable: Both the generator-iterator and the caller progress in their execution state at the same time, their executions are interleaved. 其效果也相当:生成器迭代器和调用者都同时在其执行状态下进行,它们的执行交织在一起。 For example, if the generator does some kind of computation and the caller prints out the results, you'll see the results as soon as they're available. 例如,如果生成器进行某种计算,并且调用方将结果打印出来,则结果可用时,您将立即看到它们。 This is a form of concurrency. 这是一种并发形式。

That analogy isn't anything specific to yield from , though - it's rather a general property of generators in Python. 但是,这种类比并不是特定于yield from -而是Python中生成器的一般属性。

实际上,Python 3.3中新的“ yield from”语法的主要用途是什么?相关推荐

  1. python3语法错误python_[大数据]Python 3.x中使用print函数出现语法错误(SyntaxError: invalid syntax)的原因 - 码姐姐找文...

    在安装了最新版本的Python 3.x版本之后, 去参考别人的代码(基于Python 2.x写的教程),去利用print函数,打印输出内容时,结果却遇到print函数的语法错误: SyntaxErro ...

  2. Python笔记 · Airflow中的DAG与With语法

    在<Python笔记 · With语法糖>这篇文章中我们提到: 在Airflow中通过With构建DAG时,不必显示地将Operator添加到DAG中,只要是在With语句块内声明的Ope ...

  3. mysql新加一列_mysql如何增加表中新的列?

    mysql中可以使用"ALTER TABLE"语句来增加表中新的列,语法格式"ALTER TABLE 表名 ADD 新字段名 数据类型 [约束条件];":默认是 ...

  4. python在子类中添加新的属性_python - 如何创建类属性?

    python - 如何创建类属性? 在python中,我可以使用@classmethod装饰器向类添加方法. 是否有类似的装饰器向类中添加属性? 我可以更好地展示我在说什么. class Exampl ...

  5. 您应该知道Python 3.10中的新特性!

    Hello,大家好,我是Alex,欢迎来到每周博客! 这篇博客来给大家介绍一下Python 3.10的新特性. Python 3.10版本带来了一些很棒的新功能和改进. 结构模式匹配 结构模式匹配可以 ...

  6. Python 3.10 中的 6 个新特性,你体验了吗?

    作者 | Sara A. Metwalli 译者 | 有数可据 出品 | CSDN(ID:CSDNnews) 新的 Python 版本推出了有趣的新功能. Python 是当今最流行的编程语言之一.它 ...

  7. python能调用身份证读卡器吗_用Python在Linux下调用新中新DKQ-A16D读卡器,读二代证数据...

    1.背景 最近在研究二代证读卡器,手头上的设备是新中新DKQ-A16D,在官网(https://www.onecardok.com.cn/download)逛了一圈,发现Win下的示例,浏览器插件很多 ...

  8. Python 3.11 中的最佳新特性和修正

    更快的解释器,更易懂的错误,更强大的类型提示,以及其他一系列的加速和调整,现在都可以试用了. Python 每年都会发布新版本,上半年是功能锁定的测试版,年底是最终版本. Python 3.11 的特 ...

  9. (6)Python爬虫——爬取中新网新闻

    工欲善其事,必先利其器.我们要进行数据挖掘,就必须先获取数据,获取数据的方法有很多种,其中一种就是爬虫.下面我们利用Python2.7,写一个爬虫,专门爬取中新网http://www.chinanew ...

最新文章

  1. 《用Python进行自然语言处理》第 1 章 语言处理与 Python
  2. Spring Boot 2.x基础教程:Spring Data JPA的多数据源配置
  3. static in c language
  4. 你的食物变质没?用AI算法来检测一下吧
  5. 【Spring 工厂】注入详解 — Set注入(JDK内置类型,用户自定义类型)、构造注入(重载)
  6. MFC窗口最小化到托盘
  7. 文件上传------c#
  8. 《移动平台开发实践》第1周作业
  9. 【Linux 内核】Linux 内核源码结构 ( 下载 Linux 内核源码 | 使用 VSCode 阅读 Linux 内核源码 )
  10. 数据结构视频教程 -《小甲鱼全套教程之C C++数据结构系列教程》
  11. Hbase构建二级索引的一些解决方案
  12. 注册gitlab-runner
  13. 监控摄像机镜头角度和距离计算表
  14. 泰坦尼克号数据_泰坦尼克号数据可视化分析
  15. 02.Rocky8安装KVM
  16. java如何输出省略号_关于java:此方法签名中的省略号(…)是什么?
  17. idea修改css,js样式浏览器没更新问题
  18. 使用UltraISO软碟通制作Win10PE启动U盘
  19. 分布式消息队列kafka
  20. vue3+ts+vite 路由详解

热门文章

  1. android setContentView()
  2. 安卓高手之路 图形系统(2)----------------基于Binder的架构思路)
  3. Android Stadio 所有的窗口都没有了
  4. android第一次启动超级慢
  5. Android 10.0 系统启动之SystemServer进程-[Android取经之路]
  6. Android之解决在scrollview中嵌套ListView切换界面时scrollview整体向下滑动
  7. ( Android-源代码分享)
  8. swift_025(Swift 的自动引用计数(ARC)
  9. uniapp原生子窗体(弹出层为例子)
  10. table标签修改tr,td标签的行距