内含`yield`的列表理解和生成器理解之间的区别 [英] Difference between list comprehension and generator comprehension with `yield` inside

查看:71
本文介绍了内含`yield`的列表理解和生成器理解之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

内部包含yield的列表推导和生成器推导之间有什么区别?两者都返回一个生成器对象(分别为listcompgenexpr),但是在充分评估后,后者会添加似乎相当多余的None s.

What is the difference between list comprehensions and generator comprehensions with yield inside? Both return a generator object (listcomp and genexpr respectively), but upon full evaluation the latter adds what seem to be rather superfluous Nones.

>>> 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]

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

How come? What is the scientific explanation?

推荐答案

TLDR:生成器表达式使用隐式yield,该隐式yieldyield from表达式返回None.

TLDR: A generator expression uses an implicit yield, which returns None from the yield from expression.

这里实际上有行为不同.您的列表理解实际上已被丢弃...

There are actually two things behaving differently here. Your list comprehension is actually thrown away...

  • 再次清楚

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

Understanding this is easiest if you transform the expressions to equivalent functions. For clarity, let's write this out:

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).这是一个简单的文本替换:

To replicate the initial expressions, the key is to replace <expr> with (yield from a). This is a simple textual replacement:

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.确实,两者都复制了各自表达/理解形式的输出.

With b = ((1,), (2,)), we would expect the output 1, 2. Indeed, both replicate the output of their respective expression/comprehension forms.

在其他地方所述,yield (yield from a)应该使您感到怀疑.但是,result.append((yield from a))应该会让您感到畏缩...

As explained elsewhere, yield (yield from a) should make you suspicious. However, result.append((yield from a)) should make you cringe...

  • 提供答案

让我们先看一下生成器.另一次重写使显而易见的事情发生了:

Let's look at the generator first. Another rewrite makes it obvious what is going on:

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

要使其有效,result必须具有一个值-即None.生成器不会 yield (yield from a)表达式,但是会生成结果.您只能得到a的内容,这是评估表达式的副作用.

For this to be valid, result must have a value - namely None. The generator does not yield the (yield from a) expression, but its result. You only get the content of a as a side effect of evaluating the expression.

  • 返回问题

如果您检查列表理解"的类型,则不是list-是generator. <listcomp>只是它的名字.是的,那不是月亮,而是功能齐全的发生器.

If you check the type of your "list comprehension", it is not list - it is generator. <listcomp> is just its name. Yes, that's not a Moon, that's a fully functional generator.

还记得我们的转换如何将yield from放入函数中吗?是的,这就是您定义生成器的方式! 这是我们的函数版本,这次是在上面撒上print:

Remember how our transformation put a yield from inside a function? Yepp, that is how you define a generator! Here is our function version, this time with print sprinkled on it:

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!但是,它被扔掉了,结果再次只是副作用.这是实际发生的情况:

Evaluating list(listfunc()) prints None, None, and [None, None] and returns [1, 2]. Your actual list contains those None that sneaked into the generator as well! However, it is thrown away and the result is again just a side effect. This is what actually happens:

  • 通过评估列表理解力/listfunc,将创建​​一个生成器.
  • 将其馈送到list对其进行迭代...
    • yield from a提供a 的值并返回 None
    • None存储在结果列表中
    • A generator is created upon evaluating the list comprehension/listfunc.
    • Feeding it to list iterates over it...
      • yield from a provides the values of a and returns None
      • None is stored in the result list

      在迭代结束时...

      • return引发StopIteration且值为[None, None]
      • list构造函数将忽略此值并将其丢弃
      • return raises StopIteration with a value of [None, None]
      • The list constructor ignores this and throws the value away

      这个故事的寓意

      请勿在理解内使用yield from.它没有按照您的想象做.

      Don't use yield from inside of comprehensions. It does not do what you think it does.

      这篇关于内含`yield`的列表理解和生成器理解之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆