我有一個列表a
,我想根據函數f
更改元素a[i: j]
。我能比天真的方式做得更好嗎?在for循環中修改列表元素
for index in range(i, j):
a[index] = f(a)
[通過更好地我的意思是更接近於map(f, a)
,或者說更快。]
我有一個列表a
,我想根據函數f
更改元素a[i: j]
。我能比天真的方式做得更好嗎?在for循環中修改列表元素
for index in range(i, j):
a[index] = f(a)
[通過更好地我的意思是更接近於map(f, a)
,或者說更快。]
您可以分配到切片:
a[i:j] = map(f, a[i:j])
@Randomblue你試過了嗎? – 2012-02-16 12:52:54
@Randomblue在片上閱讀。 http://docs.python.org/library/stdtypes.html#mutable-sequence-types「s [i:j] = t」從我到j的s片段被替換爲可迭代t的內容「 – hughdbrown 2012-02-16 13:26:59
使用列表理解...
a[i:j] = [f(ai) for ai in a[i:j]]
或map
相當...
a[i:j] = map(f, a[i:j])
這個時間有多好? – Randomblue 2012-02-16 01:46:06
你應該在你的用例上進行測試。它可能取決於i和j的值以及元素的類型。 – 2012-02-16 01:48:24
我不打算做定時練習,但我會告訴你什麼內部代碼的各種選項變成。您的代碼是naive
。切片爲l值的基於地圖的解決方案爲map_lvalue_slice
。用切片作爲l值的列表理解是list_comp_lvalue_slice
。相關列表理解一個解決方案使用一個元組,被稱爲tuple_lvalue_slice
:
>>> from dis import dis
>>>
>>> def naive(a, f, i, j):
... for index, ai in enumerate(a[i:j], start=i):
... a[index] = f(ai)
...
>>> def map_lvalue_slice(a, f, i, j):
... a[i:j] = map(f, a[i:j])
...
>>> def list_comp_lvalue_slice(a, f, i, j):
... a[i:j] = [f(ai) for ai in a[i:j]]
...
>>> def tuple_lvalue_slice(a, f, i, j):
... a[i:j] = tuple(f(ai) for ai in a[i:j])
...
>>> dis(naive)
2 0 SETUP_LOOP 55 (to 58)
3 LOAD_GLOBAL 0 (enumerate)
6 LOAD_FAST 0 (a)
9 LOAD_FAST 2 (i)
12 LOAD_FAST 3 (j)
15 SLICE+3
16 LOAD_CONST 1 ('start')
19 LOAD_FAST 2 (i)
22 CALL_FUNCTION 257
25 GET_ITER
>> 26 FOR_ITER 28 (to 57)
29 UNPACK_SEQUENCE 2
32 STORE_FAST 4 (index)
35 STORE_FAST 5 (ai)
3 38 LOAD_FAST 1 (f)
41 LOAD_FAST 5 (ai)
44 CALL_FUNCTION 1
47 LOAD_FAST 0 (a)
50 LOAD_FAST 4 (index)
53 STORE_SUBSCR
54 JUMP_ABSOLUTE 26
>> 57 POP_BLOCK
>> 58 LOAD_CONST 0 (None)
61 RETURN_VALUE
>>>
>>> dis(map_lvalue_slice)
2 0 LOAD_GLOBAL 0 (map)
3 LOAD_FAST 1 (f)
6 LOAD_FAST 0 (a)
9 LOAD_FAST 2 (i)
12 LOAD_FAST 3 (j)
15 SLICE+3
16 CALL_FUNCTION 2
19 LOAD_FAST 0 (a)
22 LOAD_FAST 2 (i)
25 LOAD_FAST 3 (j)
28 STORE_SLICE+3
29 LOAD_CONST 0 (None)
32 RETURN_VALUE
>>>
>>> dis(list_comp_lvalue_slice)
2 0 BUILD_LIST 0
3 LOAD_FAST 0 (a)
6 LOAD_FAST 2 (i)
9 LOAD_FAST 3 (j)
12 SLICE+3
13 GET_ITER
>> 14 FOR_ITER 18 (to 35)
17 STORE_FAST 4 (ai)
20 LOAD_FAST 1 (f)
23 LOAD_FAST 4 (ai)
26 CALL_FUNCTION 1
29 LIST_APPEND 2
32 JUMP_ABSOLUTE 14
>> 35 LOAD_FAST 0 (a)
38 LOAD_FAST 2 (i)
41 LOAD_FAST 3 (j)
44 STORE_SLICE+3
45 LOAD_CONST 0 (None)
48 RETURN_VALUE
>>>
>>> dis(tuple_lvalue_slice)
2 0 LOAD_GLOBAL 0 (tuple)
3 LOAD_CLOSURE 0 (f)
6 BUILD_TUPLE 1
9 LOAD_CONST 1 (<code object <genexpr> at 0xb748dc38, file "<stdin>", line 2>)
12 MAKE_CLOSURE 0
15 LOAD_FAST 0 (a)
18 LOAD_FAST 2 (i)
21 LOAD_FAST 3 (j)
24 SLICE+3
25 GET_ITER
26 CALL_FUNCTION 1
29 CALL_FUNCTION 1
32 LOAD_FAST 0 (a)
35 LOAD_FAST 2 (i)
38 LOAD_FAST 3 (j)
41 STORE_SLICE+3
42 LOAD_CONST 0 (None)
45 RETURN_VALUE
的解決方案,解決最快的C代碼,最好是在一個緊密的循環,是最可取的在我的意見,因爲他們很可能大多采用優化C代碼,而不是主要解釋說明。我更喜歡切片作爲你的代碼的l值解決方案,我可能會傾向於地圖解決方案,儘管我主要是一名列表理解傢伙。
而且,這裏是證明他們是等價代碼:
>>> i, j = 4, 8
>>> def f(ai):
... return -ai
...
>>> for fn in (naive, map_lvalue_slice, list_comp_lvalue_slice, tuple_lvalue_slice):
... a = range(10)
... fn(a, f, i, j)
... print "%-40s: %r" % (fn.__name__, a)
...
naive : [0, 1, 2, 3, -4, -5, -6, -7, 8, 9]
map_lvalue_slice : [0, 1, 2, 3, -4, -5, -6, -7, 8, 9]
list_comp_lvalue_slice : [0, 1, 2, 3, -4, -5, -6, -7, 8, 9]
tuple_lvalue_slice : [0, 1, 2, 3, -4, -5, -6, -7, 8, 9]
你可能更喜歡''以上range' xrange'(見http://stackoverflow.com/a/135114/589206)。 – hochl 2012-02-16 03:21:12
我認爲這更接近你的意圖:'對於索引,ai在枚舉(a [i:j],start = i): a [index] = f(ai)' – hughdbrown 2012-02-16 03:49:19