从 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),并希望结果是列表列表而不是元组列表,以便能够对数组元素进行操作。
没有这样的计划。好处是微不足道的,并且会减慢所有使用zip速度,以启用 99% 以上的时间都不需要的用例。
在(可能少于)1% 的情况下,可以使用 轻松实现map(list, zip(...)),其开销低得惊人,如下所示:
map仍然是懒惰的,因此您不会生成任何大型中间数据结构,并且它也是用 C 实现的,因此您不会在执行时进出字节码解释器层(每个新项目都涉及一次调用C 层完成所有工作以“原子地”生成下一个项目),tuple到的转换list非常便宜(list从现有的list或tuple特殊情况创建或扩展 a ,以尽可能提高性能,因为这是常见的情况),并且zip识别它生成的何时未在其他地方引用,并将其重用于下一个输出(因此实际上,您只生成一个真正的 new ,否则只生成新的s;对 s 的优化可能对s 没有用,因为如果您想要 a ,您可能打算修改它,因此即使您在下一个循环之前删除了对它的所有引用,它也无法可靠地重用)。由于这种优化(这也有利于类似 的情况) ,让它生成第一个的开销变得微不足道,直接生成 a 的增量收益不足以证明更改的合理性。tupletupletuplelisttuplelistlisttuplefor x, y in zip(it1, it2):list特别是第 3 项的优化是不这样做的严格原因;为了保留 s 的优化, stuple的代码(迭代器必须生成下一个值的 C 等效项)将变得更加复杂,并且扩展一个被多次调用的廉价函数,即使是一点点也会对性能产生很大影响(如果没有别的,它至少引入了一个基于“模式”的附加测试和分支,并添加了在 C 层与 a (在 s 的现有代码之上)有效工作所需的专门代码。这可能不会看起来很多;它可能只会给模式中每个项目的开销增加一纳秒,但考虑到该方法被调用的频率,以及这将如何减慢常见的 ( ) 情况以支持不常见的情况 ( ),很难证明其合理性。ziptp_next__next__ziplisttupletupletuplelist
根据记录,map(list, ...)对于 ing 两个序列的简单情况,包装的真正成本似乎是开销的大约 3 倍zip(来自 Linux x86-64 上的 CPython 3.10.5 的计时,使用 IPython 8.4.0%%timeit魔法来简化微基准):
>>> %%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)\nRun 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),并且我向您保证该比率与现实世界代码中的比率相差甚远。
| 归档时间: |
|
| 查看次数: |
74 次 |
| 最近记录: |