在编写一些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不会改变任何东西。
答案 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
根据反汇编的比较,一些会使后者变慢的事情:
list
必须从全局命名空间加载。
append
。
您支付两笔通话费用而不是一笔费用。
简短的回答是Python字节代码具有非常简洁有效的方式来存储像['foo']
这样的短列表并进行二进制操作。