【问题标题】:Use of yield with a dict comprehension将 yield 与 dict 理解一起使用
【发布时间】:2012-09-03 16:42:09
【问题描述】:

作为一个人为的例子:

myset = set(['a', 'b', 'c', 'd'])
mydict = {item: (yield ''.join([item, 's'])) for item in myset}

list(mydict) 给出:

['as', 'cs', 'bs', 'ds', {'a': None, 'b': None, 'c': None, 'd': None}]

这里发生了什么? yield 是做什么的?无论yield 后面是什么表达式,这种行为是否一致?

注意:我知道mydict = {item: ''.join([item, 's']) for item in myset} 会给出字典{'a': 'as', 'b': 'bs', 'c': 'cs', 'd': 'ds'},这似乎是我在这里尝试做的。

【问题讨论】:

  • 哇。我会因为 SyntaxError 而赔钱。
  • @DSM -- 我也是。我猜 dict 理解是作为生成器表达式的包装器实现的。 (毕竟{k:v for k,v in ...}等价于dict((k,v) for k,v in ...)
  • 我认为这个故事的寓意是:“不要在家里尝试这个”;-)。但非常有趣的问题 (+1)。
  • 在 python3 中,list(mydict) 只是 ['as', 'cs', 'bs', 'ds']

标签: python generator yield


【解决方案1】:

我认为yield 正在将您出色的字典理解转化为生成器表达式。因此,当您在生成器上进行迭代时,yield 正在“生成”看起来像 asbs ... 的元素,但语句 yield ... 返回 None。所以,在一天结束的时候,你会得到一个看起来像 {'a': None, 'b': None, ...} 的字典。

让我感到困惑的部分是为什么字典实际上是在最后产生的。我猜这种行为实际上并没有被标准很好地定义,但我可能错了。

有趣的是,如果你用列表推导式尝试这个,python 会抱怨:

>>> a = [(yield i) for i in myset]
  File "<stdin>", line 1
SyntaxError: 'yield' outside function

但它是Ok in a generator(显然)。

【讨论】:

  • FWIW pypy 没有给出字典:list(mydict) 产生 ['as', 'cs', 'bs', 'ds'],如果你告诉我表达式是有效的,我猜它会返回.
  • 所以要么 pypy 有问题,要么没有定义行为(我的钱在第二个)。
  • @DSM -- pypydict( (item,(yield (item+'s'))) for item in 'abdc') 返回什么?
  • {'a': None, 'c': None, 'b': None, 'd': None}.
  • @DSM -- 谢谢。不知道我的想法是什么,但是,pypy 和 Cpython 同意这一点很有趣(而且并不奇怪)。
【解决方案2】:

我找到了! ^_^

平时生活中的表情

print {item: (yield ''.join([item, 's'])) for item in myset} 

这样评估:

def d(myset):
    result = {}
    for item in myset:
        result[item] = (''.join([item, 's']))
    yield result

print d(myset).next()

为什么是yield result 而不是return result?我认为有必要像这样支持嵌套列表理解*:

print {i: f.lower() for i in nums for f in fruit}  # yes, it's works

那么,会是这样的代码吗?

def d(myset):
    result = {}
    for item in myset:
        result[item] = (yield ''.join([item, 's']))
    yield result

>>> print list(d(myset))
['as', 'cs', 'bs', 'ds', {'a': None, 'b': None, 'c': None, 'd': None}]

首先将返回''.join([item, 's']) 的所有值,最后一个将返回字典resultyield 表达式的值为None,因此result 中的值也是None

* 对评估嵌套列表推导的更正确解释:

print {i: f.lower() for i in nums for f in fruit}

# eval like this:

result = {}
for i, f in product(nums, fruit): # product from itertools
    key, value = (i, f.lower())
    result[key] = value
print result

【讨论】:

    【解决方案3】:

    我认为你的代码必须执行这个相似性:

    def d(myset):
        for item in myset:
            yield item, (yield ''.join([item, 's']))
    
    d(myset)
    

    首先,评估yield ''.join([item, 's'](并返回'as'、'cs'等)。 yield 表达式的值为 None,因为被发送回生成器。然后 eval yield item, None,返回元组 ('a', None), ('b', None)。

    所以,我有:

    >>> list(d(myset))
    ['as', ('a', None), 'cs', ('c', None), 'bs', ('b', None), 'ds', ('d', None)]
    

    接下来会发生什么,我不明白。

    【讨论】:

      【解决方案4】:

      首先,yield 返回什么?在这种情况下答案是None,因为yield 返回传递给next() 的参数,在这种情况下什么都不是(list 不传递任何东西给next)。

      现在这是你的答案:

      >>> myset = set(['a', 'b', 'c', 'd'])
      >>> mydict = {item: (yield ''.join([item, 's'])) for item in myset}
      >>> mydict
      <generator object <dictcomp> at 0x0222BB20>
      

      dict 理解变成了一个生成器,因为你在函数体上下文中使用了yield!这意味着整个事物在传递到 list 之前不会被评估。

      这就是发生的事情:

      1. list 致电next(mydict)
      2. Yield 将 ''.join([item, 's']) 返回到 list 并冻结理解。
      3. list 致电next(mydict)
      4. 理解恢复并将yield (None) 的结果分配给字典中的item,并开始新的理解迭代。
      5. 回到 1。

      最后,实际的生成器对象返回主体中的临时对象,即dict。我不知道为什么会发生这种情况,而且可能也没有记录在案的行为。

      【讨论】:

        猜你喜欢
        • 2013-07-11
        • 2012-12-25
        • 1970-01-01
        • 1970-01-01
        • 2021-07-11
        • 2014-01-25
        • 1970-01-01
        • 2014-12-20
        • 2013-04-06
        相关资源
        最近更新 更多