【问题标题】:"Tuple comprehensions" and the star splat/unpack operator *“元组推导”和星形 splat/unpack 运算符 *
【发布时间】:2018-05-14 09:07:09
【问题描述】:

我刚刚看了问题Why is there no tuple comprehension in Python?

comments of the accepted answer 中,声明没有真正的“元组推导”。相反,我们当前的选择是使用生成器表达式并将生成的生成器对象传递给元组构造函数:

tuple(thing for thing in things)

或者,我们可以使用列表推导式创建一个列表,然后将该列表传递给元组构造函数:

tuple([thing for thing in things])

最后,与公认的答案相反,a more recent answer 表示元组推导确实是一件事(从 Python 3.5 开始),使用以下语法:

*(thing for thing in things),
  • 对我来说,第二个示例似乎也是首先创建生成器对象的示例。它是否正确?

  • 就幕后发生的事情而言,这些表达式之间有什么区别吗?在性能方面?我假设第一个和第三个可能有延迟问题,而第二个可能有内存问题(如链接的 cmets 中所述)。

  • 比较第一个和最后一个,哪个更pythonic?

更新:

正如所料,列表理解确实快得多。我不明白为什么第一个比第三个快。有什么想法吗?

>>> from timeit import timeit

>>> a = 'tuple(i for i in range(10000))'
>>> b = 'tuple([i for i in range(10000)])'
>>> c = '*(i for i in range(10000)),'

>>> print('A:', timeit(a, number=1000000))
>>> print('B:', timeit(b, number=1000000))
>>> print('C:', timeit(c, number=1000000))

A: 438.98362647295824
B: 271.7554752581845
C: 455.59842588083677

【问题讨论】:

  • 您询问性能。测试它们。在 ipython 中尝试%timeit。找出在您的特定机器上哪个更好。
  • 列表推导中的x for y in z 可能看起来像一个生成器,但它不是。内部运作不同。例如。 StopIterationx 部分引发的 StopIteration 将停止生成器,但会从列表理解中冒出。
  • 我会说两者都不是非常pythonic,因为元组通常用于表示静态已知的,可能是异构的项目集(例如,您可以对其进行解构),并具有与每个位置相关联的一些语义含义.列表更适合不确定的、同质的大量,其中像迭代这样的操作是有意义的。不过,这只是我的看法。
  • 虽然最后一个在技术上可以使用,但它是选项中最慢的,并且必须粘贴一个杂散的逗号只是为了让解释器能够理解它需要解包一个元组在我的不起眼的观点不是很“pythonic”。
  • 完成!我更新了@JohnZwinck 的问题。还有@schwobaseggl,我不确定我是否理解,我使用的是x for x in y 而不是x for y in z。关于这里提出的其他观点,我同意你们所有人。

标签: python python-3.x tuples list-comprehension generator-expression


【解决方案1】:

对我来说,第二个示例似乎也是生成器的示例 首先创建对象。这是正确的吗?

是的,你是对的,检查 CPython 字节码:

>>> import dis
>>> dis.dis("*(thing for thing in thing),")
  1           0 LOAD_CONST               0 (<code object <genexpr> at 0x7f56e9347ed0, file "<dis>", line 1>)
              2 LOAD_CONST               1 ('<genexpr>')
              4 MAKE_FUNCTION            0
              6 LOAD_NAME                0 (thing)
              8 GET_ITER
             10 CALL_FUNCTION            1
             12 BUILD_TUPLE_UNPACK       1
             14 POP_TOP
             16 LOAD_CONST               2 (None)
             18 RETURN_VALUE

这些表达在什么方面有什么区别吗? 在幕后进行?在性能方面?我假设第一个 第三个可能有延迟问题,而第二个可能有内存 问题(如链接的 cmets 中所述)。

我的时间显示第一个 1 稍微快一点,大概是因为通过BUILD_TUPLE_UNPACK 调用的解包比tuple() 调用更昂贵:

>>> from timeit import timeit
>>> def f1(): tuple(thing for thing in range(100000))
... 
>>> def f2(): *(thing for thing in range(100000)),
... 
>>> timeit(lambda: f1(), number=100)
0.5535585517063737
>>> timeit(lambda: f2(), number=100)
0.6043887557461858

比较第一个和最后一个,哪个更pythonic?

第一个对我来说似乎更具可读性,并且也适用于不同的 Python 版本。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-04
    • 1970-01-01
    • 2015-03-13
    • 2017-07-09
    • 1970-01-01
    • 2018-10-01
    • 1970-01-01
    • 2015-12-11
    相关资源
    最近更新 更多