【问题标题】:Unpacking generalizations解包概括
【发布时间】:2017-05-06 05:23:46
【问题描述】:

PEP 448 -- Additional Unpacking Generalizations 允许:

>>> LOL = [[1, 2], ['three']]
>>> [*LOL[0], *LOL[1]]
[1, 2, 'three']

好的!再见itertools.chain。反正从不喜欢你。

>>> [*L for L in LOL]
  File "<ipython-input-21-e86d2c09c33f>", line 1
    [*L for L in LOL]
    ^
SyntaxError: iterable unpacking cannot be used in comprehension

。为什么我们不能拥有美好的事物?

不幸的是,它们都存在语法错误:

[*l for l in lists]    # for l in lists: result.extend(l)
{*s for s in sets}     # for s in sets: result.update(s)
{**d for d in dicts}   # for d in dicts: result.update(d)
(*g for g in gens)     # for g in gens: yield from g

在推导式中解包似乎是显而易见的和 Python 式的,并且从简写“for-loop & append”到“for-loop & extend”有一个非常自然的扩展。

但由于他们费心添加该特殊错误消息,因此禁用它可能是有原因的。那么,这种语法有什么问题呢?

【问题讨论】:

  • 你添加第二个循环:[item for L in LOL for item in L] 可能是为了消除关于应该使用哪个循环的歧义。
  • 这还没有被排除在未来(最初的实现据我所知),寻找这方面的参考。
  • P.s 你的命名约定是可怕的
  • @wim [item for L in LOL for item in L[:3]]

标签: python python-3.x list-comprehension python-3.5 iterable-unpacking


【解决方案1】:

这是简要PEP 448 中解释的,它介绍了拆包概括:

此 PEP 的早期迭代允许在内部解包运算符 列表、集合和字典推导作为展平运算符 容器的迭代:

>>> ranges = [range(i) for i in range(5)]
>>> [*item for item in ranges]
[0, 0, 1, 0, 1, 2, 0, 1, 2, 3]

>>> {*item for item in ranges}
{0, 1, 2, 3}

这引起了人们对可读性和温和性的强烈担忧 支持。为了不损害争议较小的方面 PEP 的成员,这没有被提案的其余部分接受。

但是,这在未来可能会改变:

此 PEP 不包括列表、集合和字典推导式中的解包运算符,尽管未来的提议并未排除这一点。


PEP 提到“对可读性的强烈担忧”。我不知道整个故事,但是导致这个决定的详细讨论肯定可以在邮件列表中找到:

这是一个模棱两可的例子,如果在列表理解中允许解包概括:

[*t for t in [(1, 'a'), (2, 'b'), (3, 'c')]]

According to one of the core developers,如果结果是 [1, 'a', 2, 'b', 3, 'c'] 而不是 [(1, 'a'), (2, 'b'), (3, 'c')],这将是令人惊讶的。

由于没有正式的共识,所以不允许这些特殊情况比较简单。

【讨论】:

  • 我在 PEP 中看到了这一点,但“对可读性的强烈担忧”是什么?你能在你的回答中总结它们吗?
  • @wim 我不太了解细节,但我尝试添加一个示例。
  • 这就是我想要的,谢谢! Steven D'Aprano 的信息很奇怪 - for-loop + extend 的解包概括是显而易见且自然的。很难理解一个核心开发者怎么会认为它“非常奇怪”和“一百万年来从未”期望得到这样的结果。
【解决方案2】:

引用the Py-Dev mailing list thread in which this feature was accepted:

这样就可以理解了。 IIRC,在开发补丁的过程中,我们意识到 f(*x for x in xs)足够模糊,我们决定禁止它 - 请注意 f(x for x in xs) 已经有点特殊case 因为一个参数只能是一个“裸”的生成器表达式,如果它是唯一的参数。相同的推理不适用于(以那种形式)列表、集合和 dict 理解——而 f(x for x in xs)f((x for x in xs)) 含义相同,[x for x in xs][(x for x in xs)] 不同(这是一个列表一个元素,该元素是一个生成器 表达式)

(强调我的)

我还查看了 Python 问题跟踪器以了解此功能。我发现了一个问题,在实施时进行了讨论。帮助他们实现这一目标的消息序列以here 开头,其中很好地概述了 GvR 在msg234766 中介绍的歧义。

害怕链接腐烂,我在这里附上(格式化的)消息:

所以我觉得这里的测试函数应该是:

def f(*a, **k): print(list(a), list(k))

然后我们可以尝试这样的事情:

f(x for x in ['ab', 'cd'])

打印一个生成器对象,因为这被解释为一个生成器表达式的参数。

但现在让我们考虑一下:

f(*x for x in ['ab', 'cd'])

我个人认为这相当于:

f(*'ab', *'cd')

IOW:

 f('a', 'b', 'c', 'd')

PEP 没有明确说明在这里做什么。现在的问题是,我们应该将*x for x in ... 之类的东西解释为生成器表达式的扩展形式,还是*arg 的扩展形式?我不知何故认为后者更有用,也是更合乎逻辑的扩展。

我的理由是,PEP 支持 f(*a, *b) 之类的东西,将 f(*x for x in xs) 解释为对列表 xs 中的每个 x 执行 *x 是相当合乎逻辑的。

最后,如the Abstract section of the corresponding PEP 所述,此功能并未完全排除:

此 PEP 不包括列表、集合和字典解析中的解包运算符,尽管 这并未被排除在未来的提议中

所以,我们可能很快就会看到它(但肯定不是 3.6 :-),我希望我们能看到,它们看起来不错。

【讨论】:

  • 我认为这只是反对实现解包 函数调用 语法的论据。为什么不支持完全不模糊的[*x for x in xs]f(*x for x in xs) 仍然是语法错误?
猜你喜欢
  • 2016-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-09
  • 2019-03-03
  • 2017-05-11
  • 2019-12-20
相关资源
最近更新 更多