未来的 zip() 版本是否计划使用 list=False 参数(Python 3.10 strict=False 中的新增功能)?

Cla*_*dio -3 python-3.x

从 Python 版本 3.10 开始,内置函数zip()允许使用带有默认值的参数,该strict默认值False提供设置选项,以防迭代不具有相等的长度(有助于调试)。strict=TrueTypeError

list=False管道中是否还有一个选项,如果设置为list=True返回列表列表而不是元组列表?如果没有:有充分的理由不这样做吗?


来自评论:


听起来像你想要的map(list, zip(...))-凯尔伍德

这将是压缩项目整个长度的两倍,并且用 Python 编写的自己的 zip() 会减慢速度。另一种选择也[list(e) for e in zip(...)]有同样的缺点。我想要的是 zip 的 list=True 选项,想知道为什么它不存在?可能有一个我不知道的充分理由。首先打包元组,然后将其重新打包到列表中没有多大意义。元组比列表受到更多限制,减少了对结果可能的直接操作的数量。在我看来,更多的 Pythonic 是返回一个列表列表,这提供了更大的灵活性 - 这就是我想知道并提出这个问题的原因。

通常,您使用 zip 来迭代对,例如 zip(...) 中的 x, y

是的,但是 zip() 的另一个用途是转置列表数组的二维列表(例如在numpywith中提供.T),并希望结果是列表列表而不是元组列表,以便能够对数组元素进行操作。

Sha*_*ger 5

没有这样的计划。好处是微不足道的,并且会减慢所有使用zip速度,以启用 99% 以上的时间都不需要的用例。

\n

在(可能少于)1% 的情况下,可以使用 轻松实现map(list, zip(...)),其开销低得惊人,如下所示:

\n
    \n
  1. map仍然是懒惰的,因此您不会生成任何大型中间数据结构,并且它也是用 C 实现的,因此您不会在执行时进出字节码解释器层(每个新项目都涉及一次调用C 层完成所有工作以“原子地”生成下一个项目),
  2. \n
  3. 从已知大小tuple到的转换list非常便宜(list从现有的listtuple特殊情况创建或扩展 a ,以尽可能提高性能,因为这是常见的情况),并且
  4. \n
  5. 在 CPython 参考解释器上,它本身有一个优化,可以在请求下一个输出时zip识别它生成的何时未在其他地方引用,并将其用于下一个输出(因此实际上,您只生成一个真正的 new ,否则只生成新的s;对 s 的优化可能对s 没有用,因为如果您想要 a ,您可能打算修改它,因此即使您在下一个循环之前删除了对它的所有引用,它也无法可靠地重用)。由于这种优化(这也有利于类似 的情况) ,让它生成第一个的开销变得微不足道,直接生成 a 的增量收益不足以证明更改的合理性。tupletupletuplelisttuplelistlisttuplefor x, y in zip(it1, it2):list
  6. \n
\n

特别是第 3 项的优化是这样做的严格原因;为了保留 s 的优化, stuple的代码(迭代器必须生成下一个值的 C 等效项)将变得更加复杂,并且扩展一个被多次调用的廉价函数,即使是一点点也会对性能产生很大影响(如果没有别的,它至少引入了一个基于“模式”的附加测试和分支,并添加了在 C 层与 a (在 s 的现有代码之上)有效工作所需的专门代码。这可能不会看起来很多;它可能只会给模式中每个项目的开销增加一纳秒,但考虑到该方法被调用的频率,以及这将如何减慢常见的 ( ) 情况以支持不常见的情况 ( ),很难证明其合理性。ziptp_next__next__ziplisttupletupletuplelist

\n

根据记录,map(list, ...)对于 ing 两个序列的简单情况,包装的真正成本似乎是开销的大约 3 倍zip(来自 Linux x86-64 上的 CPython 3.10.5 的计时,使用 IPython 8.4.0%%timeit魔法来简化微基准):

\n
>>> %%timeit a = tuple(range(1000)); b = tuple(range(1000, 2000))\n... for tup in zip(a, b):\n...     pass\n15.7 \xce\xbcs \xc2\xb1 216 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 100,000 loops each)\n\n>>> %%timeit a = tuple(range(1000)); b = tuple(range(1000, 2000))\n... for lst in map(list, zip(a, b)):\n...     pass\n47 \xce\xbcs \xc2\xb1 1.09 \xce\xbcs per loop (mean \xc2\xb1 std. dev. of 7 runs, 10,000 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n

考虑到所有因素,这非常好,每个项目大约需要 30 ns 的开销来获取lists 而不是tuple。相比之下,仅用said做一件list有意义的事情(例如,调用lst.append(1)作为循环体)的成本会增加约 45 ns 的成本,因此基本上您用 said做的任何事情都会比获取slist的增量费用更高list首先。对于很少出现的事情来说,这没什么大不了的。如果支持list=True只给案例增加了 1 ns 的开销tuple,那么您需要证明这种list=True情况至少会发生在每 30 个案例中之一(实际上更像是十分之一左右的案例,因为zip运行速度会更慢)这种list情况是因为lists 需要两次分配,而不是一次,并且没有像s 那样针对小型固定大小lists的重复分配进行严格优化tuple),并且我向您保证该比率与现实世界代码中的比率相差甚远。

\n