Python的禅:错误绝不应该默默传递.为什么zip工作方式如此?

chr*_*ris 8 python

我在我的代码中使用python的函数zip(主要用于创建如下所示的dicts)

dict(zip(list_a, list_b)) 
Run Code Online (Sandbox Code Playgroud)

我发现它确实很有用,但有时它让我感到沮丧,因为我最终得到的情况是list_a与list_b的长度不同.zip只是继续将两个列表拉到一起,直到它达到一个与较短列表长度相同的压缩列表,忽略了较长列表的其余部分.这似乎应该在大多数情况下被视为错误,根据python的zen,它永远不应该默默地传递.

鉴于这是一个完整的功能,我很好奇为什么它是这样设计的?如果您尝试将两个不同长度的列表压缩在一起,为什么不将其视为错误?

Aks*_*jan 12

原因1:历史原因

zip允许不等长的参数,因为它意味着map通过允许不等长度的参数来改进.这种行为是zip存在的原因.

这是你zip在它存在之前的表现:

>>> a = (1, 2, 3)
>>> b = (4, 5, 6)
>>> for i in map(None, a, b): print i
...
(1, 4)
(2, 5)
(3, 6)
>>> map(None, a, b)
[(1, 4), (2, 5), (3, 6)]
Run Code Online (Sandbox Code Playgroud)

这非常不直观,并且不支持不等长的列表.这是一个主要的设计问题,您可以在官方RFC zip第一次提出的日常工作中看到:

虽然map()习惯用法在Python中是常见的,但它有几个缺点:

  • 对于没有函数编程背景的程序员来说,这是不明显的.

  • 魔术None第一个论点的使用是不明显的.

  • 当列表长度不同时,它具有任意的,通常是无意的和不灵活的语义 - 较短的序列用以下内容填充None:

    >>> c = (4, 5, 6, 7)

    >>> map(None, a, c)

    [(1, 4), (2, 5), (3, 6), (None, 7)]

所以,不,这种行为不会被视为错误 - 这就是它首先设计的原因.


原因2:实际原因

因为它非常有用,所以明确指定并且根本不必将其视为错误.

通过允许不等长度,zip只要求其参数符合迭代器协议.这允许zip延长到发电机,元组,字典键和实现世界字面上什么__next__()和__iter__(),恰恰是因为它没有打听长度.

这很重要,因为发电机不支持len(),因此无法事先检查长度.添加一个长度检查,你应该打破zip生成器的工作能力.这是一个相当严重的劣势,你不同意吗?


原因3:菲亚特

Guido van Rossum想要这样:

可选填充.该PEP的早期版本提出了一个可选的pad关键字参数,当参数序列的长度不同时,将使用该参数.这与map(None,...)语义类似,只是用户可以指定pad对象.由于KISS原则,BDFL拒绝了这一点,因为它总是截断到最短的序列.如果真的有需要,以后添加会更容易.如果不需要,将来仍然无法删除它.

KISS胜过一切.


L_W*_*L_W 10

python 3.10zip()获得了一个新的可选strict标志。当它被设置并且遇到不等长度的列表时,它将引发一个ValueError. 这在PEP 618中有详细介绍,并在3.10 的变更日志中提到