【问题标题】:Why can't you toggle a function generator's behavior by an argument?为什么不能通过参数切换函数生成器的行为?
【发布时间】:2017-07-21 07:20:21
【问题描述】:

考虑这两个函数:

def foo():
    x = 0
    while True:
        yield x
        x += 1

def wrap_foo(limit=10, gen=True):
    fg = foo()
    count = 0
    if gen:
        while count < limit:
            yield next(fg)
            count += 1
    else:
        return [next(fg) for _ in range(limit)]=

foo() 是一个生成器,wrap_foo() 只是限制了生成的数据量。我正在尝试让包装器充当带有gen=True 的生成器,或者充当使用kwarg gen=False 将所有生成的数据直接放入内存的常规函数​​。

常规的生成器行为如我所料:

In [1352]: [_ for _ in wrap_foo(gen=True)]
Out[1352]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

但是,对于 gen=False,不会生成任何内容。

In [1351]: [num for num in wrap_foo(gen=False)]
Out[1351]: []

似乎 Python 根据 yield 语句的存在将函数预分类为生成器(如果 yield 被注释掉,后一个示例可以完美运行)。

这是为什么?我想了解这里的机制。我正在运行 3.6

【问题讨论】:

  • 如果您的def 正文中有yield,则该函数将始终是生成器。然后,return 将充当隐式 StopIteration,而不是典型的 return 值。如果您想将整个内容加载到内存中,只需使用list(wrap_foo(10))。您为什么要以其他方式进行操作?
  • 我就是这么想的。我只是在我的交互式 shell 中的某个时刻变得懒惰并尝试添加一个 kwarg,这样我就可以直接获取生成的数据,而不是总是调用 [_ for _ in ... 然后我很好奇为什么我不能这样做。
  • 但是您不需要调用[_ for _ in ...],您已经将该逻辑抽象到生成器中,因此只需使用list实现它即可。
  • 非常正确。我只是在戏剧化;-)

标签: python python-3.x generator


【解决方案1】:

似乎 Python 将函数预先分类为基于生成器 关于 yield 语句的存在(后一个例子完美地工作 如果产量被注释掉)。

这是为什么?

因为 Python 不能等到函数实际执行 yield 来决定它是否是生成器。首先,生成器被定义为在第一个next 之前不执行任何的代码。其次,如果生成器碰巧没有生成任何元素,它可能永远不会真正到达它的任何yield 语句。

【讨论】:

    【解决方案2】:

    似乎 Python 根据 yield 语句的存在将函数预先分类为生成器

    是的,这正是发生的事情。 wrap_foo 在函数定义时被确定为生成器。您可以考虑使用生成器表达式:

    def wrap_foo(limit=10, gen=True):
        fg = foo()
        if gen:
            return (next(fg) for _ in range(limit))
        else:
            return [next(fg) for _ in range(limit)]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-06-03
      • 2015-09-04
      • 2015-12-07
      • 1970-01-01
      • 2019-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多