【问题标题】:What is the fastest and pythonic way to generate a list [(0,0), (0,1), (0,2)...(0,100)]? [closed]生成列表[(0,0),(0,1),(0,2)......(0,100)]的最快和pythonic方法是什么? [关闭]
【发布时间】:2020-06-22 08:37:10
【问题描述】:

生成列表 [(0,0), (0,1), (0,2)...(0,100)] 的最快和 Pythonic 方法是什么?

我知道有一个像 l = [(0, x) for x in range(101)] 这样的语句,但它有一个 for 循环,所以我认为这不是制作这样一个列表的最快方法。

那么制作这种列表的最 Pythonic 方式是什么?

【问题讨论】:

  • 列表推导是最pythonic的方式
  • 我觉得你的代码已经够pythonic了。
  • 这不是for 循环;列表推导仅与 for 循环共享 for 关键字。
  • 谢谢你帮了大忙。我认为我不必为我的代码而苦苦挣扎,除非它不会造成任何性能问题。
  • list(zip(itertools.repeat(0), range(101))) 可能更快,但可读性肯定较差。

标签: python performance loops iteration


【解决方案1】:

如果您想要更快的解决方案,您可以使用itertools.repeat

from itertools import repeat:

list(zip(repeat(0), range(101)))

基准:

%timeit [(0, x) for x in range(101)]
# 3.64 µs ± 19.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit list(zip(repeat(0), range(101)))
# 2.81 µs ± 35.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

另一个 Pythonic 解决方案:

list((0, x) for x in range(101))

【讨论】:

  • 它更快,但pythonic方式更具可读性。谢谢你的回答。
  • repeat(0) 就足够了,不用重复长度。 zip 在其最短参数(在本例中为 range(101))用完后停止迭代。
  • @chepner 感谢您的建议
  • 几乎没有理由考虑 list + 生成器表达式而不是列表理解。它不再可读,而且速度更慢。
  • 一个非常小的差异我会说 btw 列表理解和列表 + 生成器表达式,并且使用 list + 生成器表达式更具可读性,但这可能值得商榷
【解决方案2】:

如果你没有做 profiling 来证明性能有问题,那么解决方案应该是你已经提到的 listcomp:

l = [(0, x) for x in range(101)]

它可读且足够快(在大多数机器上需要 3-4 μs,这意味着您每秒可以执行超过 25 万次),所以不要费心去看除非您确实需要,否则可以提供更快的解决方案。

如果分析确实以某种方式显示这是最热门的代码,并且它实际上导致了影响性能的瓶颈,那么您可以使用微优化的废话,例如:

from itertools import repeat, count  # At top of file

l = [*zip(repeat(0, 101), count())]  # At point of use

它的运行时间大约是 listcomp 的 ⅔。但这充其量只是事半功倍。 如果您知道需要多次生成此精确的list,而性能确实很重要,那么解决方案是完全停止生成list。使用可读的 listcomp 制作模板,而不是按需复制该模板:

TEMPLATE_LIST = [(0, x) for x in range(101)]  # At top of file (global scope)

l = [*TEMPLATE_LIST]  # Slightly faster on 3.8.0, but generalized unpacking less widely known
l = TEMPLATE_LIST[:]  # Slightly slower, but shallow copy idiom well-known

或者如果您想要防止对模板进行就地修改list

MAKE_TEMPLATE_LIST = [(0, x) for x in range(101)].copy  # At top of file (global scope)

l = MAKE_TEMPLATE_LIST()  # At point of use

模板list 的廉价浅拷贝要快得多,而且您的所有值都是不可变的(tuples of ints),因此您不需要更昂贵的深拷贝。即使是微优化的废话解决方案(我的机器上TEMPLATE_LIST[:] 约为220-230 ns,[*TEMPLATE_LIST]MAKE_TEMPLATE_LIST() 都约为190-200 ns)所需的时间也不到10%,并且它的所有部分都很明显(MAKE_TEMPLATE_LIST 不太明显)。主要缺点是:

  1. TEMPLATE_LIST 的定义与它的使用位置是分开的,因此维护人员必须进行交叉引用(某些 IDE 至少会提供悬停提示)
  2. TEMPLATE_LIST 可修改; MAKE_TEMPLATE_LIST 更难被意外修改,但仍然可以被替换。在任何一种情况下,这都是代码纪律问题,而不是对抗的问题(“我们都是成年人”)
  3. 永久内存使用(与 list 这么小的无关;如果您无法承受 6 KB 的内存成本,那么 Python 可能不适合您)

TL;DR:除非您 100% 确定需要速度,否则请使用 listcomp。如果您确定,复制现有的 list 会比从头开始重建更快。

【讨论】:

  • 感谢您的回答。除非我真的需要,否则我不会费心寻找更快的解决方案。
  • 而且 TEMPLATE_LIST 实现的性能提升非常显着。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-31
  • 1970-01-01
  • 2020-12-18
  • 2011-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多