Python 元组构造函数与列表组件

Python 元组构造函数与列表组件,python,list,tuples,Python,List,Tuples,就说我有一张单子 a = (3, 2, 9, 4) 我想在每个数字上加一个,然后存储结果,(之后我不需要再操纵结果),我的第一个想法是: [x + 1 for x in a] 但是关于: tuple(x + 1 for x in a) 元组应该更快,对吗?如果我不需要在之后更改结果,这段代码是否更有效?还有,它到底是如何工作的,tuple构造函数是否必须从生成器表达式中创建一个列表来提前知道大小?提前感谢您的解释 只需timeit(): 因此,元组构造函数变体似乎需要大约四倍的时间,我猜这

就说我有一张单子

a = (3, 2, 9, 4)
我想在每个数字上加一个,然后存储结果,(之后我不需要再操纵结果),我的第一个想法是:

[x + 1 for x in a]
但是关于:

tuple(x + 1 for x in a)
元组应该更快,对吗?如果我不需要在之后更改结果,这段代码是否更有效?还有,它到底是如何工作的,
tuple
构造函数是否必须从生成器表达式中创建一个列表来提前知道大小?提前感谢您的解释

只需
timeit()

因此,元组构造函数变体似乎需要大约四倍的时间,我猜这是因为列表理解是相当优化的(在cpython中)

但是让我们仔细看看:

In : f3 = lambda: list(x + 1 for x in a)

In : timeit.timeit(f3)
Out: 2.5421998500823975
因此,这与元组构造花费的时间大致相同,这表明性能损失在于生成器表达式开销。(我们可以排除列表/元组构造,请参见下面的编辑)

它的速度甚至是列表的两倍

In : inc = partial(operator.add,1)

In : f4 = lambda:map(inc, a)

In : timeit.timeit(f4)
Out: 1.2346529960632324
我认为这实际上可以归结为(cpython)实现细节,所以不要依赖于此。 无论如何,不要担心性能,它只是一个2-4的系数,使用最适合阅读的方法

如果您确实遇到了性能瓶颈,请在注意到它们后进行调查并优化。我敢打赌,列表操作中的因子4将是您遇到的最小问题

编辑: 有人提到“tuple”的查找成本可能会导致减速,但事实并非如此:

In : f5 = lambda: tuple([x + 1 for x in a])

In : timeit.timeit(f5)
Out: 0.7900090217590332
因此,我想真正是生成器表达式开销降低了速度。

该模块可以让您了解代码如何在内部执行

dis.dis(λa:[x+1表示a中的x])

  1           0 BUILD_LIST               0
              3 LOAD_FAST                0 (a)
              6 GET_ITER
        >>    7 FOR_ITER                16 (to 26)
             10 STORE_FAST               1 (x)
             13 LOAD_FAST                1 (x)
             16 LOAD_CONST               1 (1)
             19 BINARY_ADD
             20 LIST_APPEND              2
             23 JUMP_ABSOLUTE            7
        >>   26 RETURN_VALUE
  1           0 LOAD_GLOBAL              0 (tuple)
              3 LOAD_CONST               1 (<code object <genexpr> at 0x7f62e9eda930, file "<stdin>", line 1>)
              6 MAKE_FUNCTION            0
              9 LOAD_FAST                0 (a)
             12 GET_ITER
             13 CALL_FUNCTION            1
             16 CALL_FUNCTION            1
             19 RETURN_VALUE
…和
dis.dis(lambda:tuple(x+1表示a中的x))

  1           0 BUILD_LIST               0
              3 LOAD_FAST                0 (a)
              6 GET_ITER
        >>    7 FOR_ITER                16 (to 26)
             10 STORE_FAST               1 (x)
             13 LOAD_FAST                1 (x)
             16 LOAD_CONST               1 (1)
             19 BINARY_ADD
             20 LIST_APPEND              2
             23 JUMP_ABSOLUTE            7
        >>   26 RETURN_VALUE
  1           0 LOAD_GLOBAL              0 (tuple)
              3 LOAD_CONST               1 (<code object <genexpr> at 0x7f62e9eda930, file "<stdin>", line 1>)
              6 MAKE_FUNCTION            0
              9 LOAD_FAST                0 (a)
             12 GET_ITER
             13 CALL_FUNCTION            1
             16 CALL_FUNCTION            1
             19 RETURN_VALUE
1 0加载\u全局0(元组)
3加载常数1(<0x7f62e9eda930处的代码对象genexpr,文件“stdin”,第1行>)
6生成函数0
9加载速度0(a)
12获取ITER
13调用函数1
16调用函数1
19返回值

…但你可能无法从中推断出多少。如果您想知道哪个更快,请查看模块。

在大多数情况下,元组和列表之间的效率并不重要。如果您真的关心它,您可以使用
timeit
进行测试

元组和列表之间最重要的区别在于元组是不可变的,列表是可复用的。 这意味着您可以更改列表的值,但不能使用tuple。您可以散列元组,但不能使用列表进行散列。 比如说

k_tuple = ('a', 'b')
k_list = ['a', 'b']
d = {}
d[k_tuple] = 'c' # It is ok
d[k_list] = 'c' #It raise exception.

而且,当list作为函数的参数时,它是通过引用赋值的。当tuple作为函数的参数时,它是由值赋值的。

最好的方法是实际计时。结果可能会因许多因素而有所不同,包括您使用的是哪种python实现。我认为更可能是在名称“list”和“tuple”的全局命名空间查找中。[]是内置语法,不需要查找。