【问题标题】:How does a generator function work internally?生成器功能如何在内部工作?
【发布时间】:2018-01-25 04:57:42
【问题描述】:

下面是一个生成器函数。

def f():
   x=1
   while 1:
      y = yield x
      x += y

这个生成器函数 (f) 是否在内部实现,如下所示?

class f(collections.Iterable):
   def __init__(self):
      self.x = 1
   def __iter__(self):
      return iter(self)
   def __next__(self):
      return self.x
   def send(self, y):
      self.x += y
      return self.next()

编辑:

This 是我问题的答案。

【问题讨论】:

标签: python generator python-internals


【解决方案1】:

在内部,生成器的工作方式与常规函数调用大致相同。在引擎盖下,运行的生成器和运行的函数大多使用相同的机器。

当您调用函数或生成器时,会创建一个堆栈帧。它具有局部变量(包括传递给函数的参数)、指向活动操作码的代码指针,以及用于挂起的 try-blocks、with-blocks 或循环的堆栈。

在常规函数中,执行立即开始。当遇到return 时,会保留最终结果并释放堆栈帧及其引用的所有内容。

generator 函数中,堆栈帧被包装在generator-iterator 对象中并立即返回。生成器函数中的代码仅在被next(g)g.send(v) 调用时运行。遇到yield 时暂停执行。

考虑生成器的一种方式是,它们就像可以使用yield 暂停并使用g.next() 恢复的函数。堆栈帧保持活动状态,因此恢复正在运行的生成器比每次调用都必须构建一个新帧的新函数调用便宜。

【讨论】:

  • yield如何在内部暂停/挂起函数?
  • @overexchange 生成器和函数的执行涉及运行当前操作码和更新代码指针。暂停只是意味着停止这样做。恢复意味着继续这样做。跟我一起数,1、2、3,现在说点别的,继续数4、5、6,……你只需要知道最后的数。同样,stackframe 保持函数的状态,您可以随时恢复停止更新并恢复更新。
  • 异步的构建块是关于能够暂停/恢复的任务。对于服务器端编程,通常任务在执行 IO 时暂停。当你说暂停就是停止时,我们不是因为在等待 IO(比如 http get 请求)而暂停吗?
  • @overexchange 我认为您认为这比实际情况更复杂。 next(g) 的代码基本上可以归结为 PyEval_EvalFrameEx(gen->gi_frame, exc)。就是这样,它只是执行框架的当前状态(请参阅Objects/genobject.c,这在 Python 2.7 中是清晰而简单的)。 yield 只是从该调用返回。相反,一个函数调用运行PyEval_EvalCodeEx(...),它创建一个新框架并执行它,如上所示。见Objects/funcobject.c
猜你喜欢
  • 2017-10-07
  • 2020-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-12
  • 2014-10-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多