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

时间:2016-09-22 00:30:01

标签: python

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

dict(zip(list_a, list_b)) 

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

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

3 个答案:

答案 0 :(得分: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)]

这非常不直观,并且不支持不等长的列表。这是一个主要的设计问题,您可以在the official RFC proposing zip for the first time中看到这一天:

  

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

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

  •   
  • 使用魔术None第一个参数是不明显的。

  •   
  • 它有任意的,经常是无意识的,不灵活的语义   列表的长度不同 - 较短的序列是填充的   与None

         

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

         

    >>> map(None, a, c)

         

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

  •   

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

原因2:实际原因

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

通过允许不等长度,zip仅要求其参数符合iterator protocol。这允许zip扩展到生成器,元组,字典键以及世界上实现__next__()__iter__()的任何内容,正是因为它没有查询长度。

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

原因3:菲亚特

Guido van Rossum想要这样:

  

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

KISS胜过一切。

答案 1 :(得分:0)

根据我的经验,你有两个恰好具有相同长度的列表的唯一原因是因为它们都是由相同的源构建的,例如它们是map相同的底层源,它们是在同一个循环中构建的,等等。在这些情况下,我不是单独创建它们然后压缩它们,我通常只创建一个预压缩的元组列表。大多数时候我实际使用zip,其中一个迭代是无限的,在这些情况下我很高兴它能让我。

答案 2 :(得分:0)

使用python 3.10 zip()获得新的可选strict标志。设置它并且遇到长度不等的列表时,它将引发ValueErrorPEP 618中对此进行了详细说明,并在changelog of 3.10

中进行了提及