在一些優化,我在Python 2.7的代碼工作,我偶然發現了以下現象:通過預先分配和將`my_list [x] = y`填充爲`__setitem__`的語法糖來填充python列表。
>>> from timeit import timeit
>>> def fill_by_appending():
... my_list = []
... for x in xrange(1000000):
... my_list.append(x)
... return my_list
...
>>> def fill_preallocated_1():
... my_list = [0]*1000000
... for x in xrange(1000000):
... my_list[x] = x
... return my_list
...
>>> def fill_preallocated_2():
... my_list = [0]*1000000
... for x in xrange(1000000):
... my_list.__setitem__(x,x)
... return my_list
...
>>> def fill_by_comprehension():
... my_list = [x for x in xrange(1000000)]
... return my_list
...
>>> assert fill_by_appending() == fill_preallocated_1() == fill_preallocated_2() == fill_by_comprehension()
>>> timeit("fill_by_appending()", setup="from __main__ import fill_by_appending", number=100)
5.877948999404907
>>> timeit("fill_preallocated_1()", setup="from __main__ import fill_preallocated_1", number=100)
3.964423894882202
>>> timeit("fill_preallocated_2()", setup="from __main__ import fill_preallocated_2", number=100)
12.38241720199585
>>> timeit("fill_by_comprehension()", setup="from __main__ import fill_by_comprehension", number=100)
2.742932081222534
,這並不奇怪,我認爲預分配是不是追加更快,或者說理解是比什麼都更快,但爲什麼使用__setitem__
比使用[]
慢三倍?
起初,我有一個理論,使用my_list[x] = x
或者只是重新分配存儲在my_list[x]
新對象的地址的引用,或者也許是,解釋甚至注意到,兩人都是同一類型的,並使用一個重載的賦值運算符,而setitem
調用實際複製內存在,但有些實驗證明我錯了:
>>> class MyList(list):
... def __setitem__(self, x,y):
... super(MyList, self).__setitem__(x,y**2)
...
>>> ml = MyList([1,2,3,4,5])
>>> ml[2]=10
>>> ml
[1, 2, 100, 4, 5]
是否有人知道什麼是引擎蓋下回事?
我猜'__setitem__'是一個函數調用,當'[X] ='有解釋特殊的意義。爲什麼'[]'快於'list()'來創建新列表的原因相同。 – Igonato
注意:對於這個特定的情況,真正的答案是「只要調用'範圍(1000000)'」(或者在Py3上,'list(range(1000000))'如果你確實需要'list')假設你正在考慮稍微複雜的填充模式。 – ShadowRanger
@ShadowRanger略微:) – gmoss