为什么列出`my_list + ['foo']`比`new_list = list(my_list)更快; Python中的new_list.append('foo')`

时间:2014-09-01 18:02:40

标签: python performance list

在编写一些Python代码时,我偶然发现了让我感到惊讶的事情。我正在考虑两种复制列表的方法,然后在副本中添加一个元素:

# I thought this would be clean-looking but slow since it creates an extra one element list, ['foo']
mylist = range(4)
newlist_0 = mylist + ['foo']
print newlist_0 # [0, 1, 2, 3, 'foo']

# I thought this would be faster
newlist_1 = list(mylist)
newlist_1.append('foo')
print newlist_1 # [0, 1, 2, 3, 'foo']

令人惊讶的是,第一种方式不仅看起来很好而且速度更快。我跑了:

import timeit
for stmt in ['newlist_0 = mylist + ["foo"]', 'newlist_1 = list(mylist); newlist_1.append("foo")']:
    print "For statement {:50} timeit results are {}".format(stmt, timeit.repeat(setup='mylist = range(4)', stmt=stmt))

得到了这个输出:

For statement newlist_0 = mylist + ["foo"]                       timeit results are [0.29012012481689453, 0.3021109104156494, 0.32175779342651367]
For statement newlist_1 = list(mylist); newlist_1.append("foo")  timeit results are [0.39945101737976074, 0.39692091941833496, 0.38529205322265625]

我偶然发现this question讨论list(lst)复制列表比lst[:]慢的事实,但切换到使用[:]复制mylist不会改变任何东西。

1 个答案:

答案 0 :(得分:2)

让我们来看一下反汇编。你的第一个方法在Python 2.7字节码中是这样的:

          0 LOAD_FAST                0 (mylist)
          3 LOAD_CONST               1 ('foo')
          6 BUILD_LIST               1
          9 BINARY_ADD          

第二种方法如下:

          0 LOAD_GLOBAL              0 (list)
          3 LOAD_FAST                0 (mylist)
          6 CALL_FUNCTION            1
          9 STORE_FAST               1 (newlist_1)

         12 LOAD_FAST                1 (newlist_1)
         15 LOAD_ATTR                1 (append)
         18 LOAD_CONST               1 ('foo')
         21 CALL_FUNCTION            1

根据反汇编的比较,一些会使后者变慢的事情:

  1. list必须从全局命名空间加载。

  2. 必须从列表对象加载
  3. append

  4. 您支付两笔通话费用而不是一笔费用。

  5. 简短的回答是Python字节代码具有非常简洁有效的方式来存储像['foo']这样的短列表并进行二进制操作。