内部“yield”的列表理解与生成器理解的区别

时间:2016-01-27 09:55:09

标签: python python-3.x list-comprehension

列表推导与内部yield的生成器理解有什么区别?两者都返回一个生成器对象(分别为listcompgenexpr),但在完全评估后,后者会添加看似多余的None s。

>>> list([(yield from a) for a in zip("abcde", itertools.cycle("12"))])
['a', '1', 'b', '2', 'c', '1', 'd', '2', 'e', '1']

>>> list(((yield from a) for a in zip("abcde", itertools.cycle("12"))))
['a', '1', None, 'b', '2', None, 'c', '1', None, 'd', '2', None, 'e', '1', None]

为什么?什么是科学解释?

3 个答案:

答案 0 :(得分:2)

TLDR:生成器表达式使用隐式yield,它从None表达式返回yield from

实际上两个事情在这里表现不同。你的列表理解实际上已被丢弃......

  • 再一次清晰

如果将表达式转换为等效函数,则最容易理解这一点。为清楚起见,让我们写出来:

listcomp = [<expr> for a in b]
def listfunc():
    result = []
    for a in b:
        result.append(<expr>)
    return result

gencomp = (<expr> for a in b)
def genfunc():
    for a in b:
        yield <expr>

要复制初始表达式,关键是将<expr>替换为(yield from a)。这是一个简单的文本替换:

def listfunc():
    result = []
    for a in b:
        result.append((yield from a))
    return result

def genfunc():
    for a in b:
        yield (yield from a)

使用b = ((1,), (2,)),我们会期望输出1, 2。实际上,两者都复制了各自表达/理解形式的输出。

正如elsewhere所述,yield (yield from a)应该让您怀疑。但是,result.append((yield from a))会让你感到畏缩......

  • 得出答案

让我们先看一下发电机。另一个重写使得显而易见的是:

def genfunc():
    for a in b:
        result = (yield from a)
        yield result

要使此有效,result必须具有值 - 即None。生成器 yield (yield from a)表达式,但结果如此。您只获得a的内容作为评估表达式的副作用。

  • 回到问题

如果您检查&#34;列表理解&#34;的类型,则不是list - 它是generator<listcomp>就是它的名字。是的,那不是月球,它是一个功能齐全的发电机。

还记得我们的转换是如何将yield from置于函数中的吗? Yepp,这就是你如何定义发电机! 这是我们的功能版本,这次加上print

def listfunc():
    result = []
    for a in b:
        result.append((yield from a))
        print(result[-1])
    print(result)
    return result

评估list(listfunc()) 打印 NoneNone[None, None]以及返回 [1, 2]。您的实际列表包含潜入发电机的None!然而,它被扔掉了,结果再次只是副作用。这就是实际发生的事情:

  • 在评估列表理解/ listfunc时创建生成器。
  • 喂它到list迭代它...
    • yield from a提供a 的值并返回 None
    • None存储在结果列表
  • 在迭代结束时......

    • return提升StopIteration,其值为[None, None]
    • list构造函数忽略此值并抛出值
  • 这个故事的道德

不要在理解中使用yield from。它没有按照你的想法做到。

答案 1 :(得分:1)

yield from表达式的值为None。您的第二个示例是生成器表达式的事实意味着它已经从迭代器隐式地产生,因此它还将产生yield from表达式的值。有关更详细的答案,请参阅this

答案 2 :(得分:0)

由于混淆和抛出SyntaxError: 'yield' inside list comprehension,两个示例在Python 3.8中均已弃用。参见buglog for 3.8 for the release notes