【问题标题】:How are generators and coroutines implemented in CPython?CPython 中的生成器和协程是如何实现的?
【发布时间】:2012-01-13 11:00:49
【问题描述】:

我读过在 CPython 中,解释器堆栈(为达到这一点而调用的 Python 函数列表)与 C 堆栈(在解释器自己的代码中调用的 C 函数列表)混合在一起。如果是这样,那么生成器和协程是如何实现的?他们如何记住他们的执行状态? CPython 是否将每个生成器/协程的堆栈复制到操作系统堆栈和从操作系统堆栈复制?还是 CPython 只是将生成器的最顶层堆栈帧保留在堆上,因为生成器只能从该最顶层帧产生?

【问题讨论】:

  • 一些现有的答案和 cmets 声称 Python 维护一个与 VM 的 C 堆栈完全分离的“程序堆栈”。这种说法是错误的。查看链接:en.wikipedia.org/wiki/Stackless_Python Stackless Python 存在但不是主流。理解就是问题是对的。
  • 近四年后,我意外地回答了自己,通过合着一章解释了生成器和协程是如何实现的:aosabook.org/en/500L/a-web-crawler-with-asyncio-coroutines.html
  • 很棒的文章,非常密集。
  • 不相关,但是...在不到 4 年的时间里,您是如何从询问生成器是如何实现的,到与 Guido 一起撰写有关此主题的书籍章节的? :)
  • 哈!实现和维护 Motor,我的 Tornado 和 asyncio 的 MongoDB 驱动程序,意味着我在过去几年中一直在使用和思考协程。我通过阅读 CPython 源代码(比我担心的更清晰)和 Tornado 的源代码来满足我的好奇心,然后在编写 asyncio 时,我也阅读了它。另外,我想在会议上发言,这进一步促使我研究协程和异步,以便我可以就这个主题发表演讲。

标签: python coroutine


【解决方案1】:

在运行的 Python 程序中,Python 的堆栈和 C 堆栈混合在一起的概念可能会产生误导。

Python 堆栈与解释器使用的实际 C 堆栈完全分开。 Python 堆栈上的数据结构实际上是完整的 Python“框架”对象(甚至可以自省并在运行时更改一些属性)。这个堆栈由 Python 虚拟机管理,它本身在 C 中运行,因此具有正常的 C 程序、机器级别、堆栈。

当使用生成器和迭代器时,解释器只是将相应的框架对象存储在 Python 程序堆栈之外的其他位置,并在生成器恢复执行时将其推回那里。这个“其他地方”是生成器对象本身。在生成器对象上调用方法“next”或“send”会导致这种情况发生。

【讨论】:

  • 我在阅读后查找了它,所以如果其他人有兴趣,这里是CPython implementation of generators。我建议先阅读这个答案,它有助于理解代码的作用。
【解决方案2】:

yield 指令将当前执行的上下文作为闭包,并将其转换为自己的活动对象。此对象有一个__iter__ 方法,该方法将在此 yield 语句之后继续。

所以调用栈变成了一个堆对象。

【讨论】:

  • 重要的是要澄清 C“硬件”堆栈和 Python 堆栈是完全不同的东西,因为这个问题混淆了两者。我的回答澄清了这一点。 (@Rudi - 你的回答很好,我留下评论,以便其他到达这里的人也检查那部分)
猜你喜欢
  • 2012-12-17
  • 2011-03-27
  • 2020-08-14
  • 1970-01-01
  • 1970-01-01
  • 2011-08-22
  • 2014-03-02
  • 2015-12-11
  • 1970-01-01
相关资源
最近更新 更多