现在让我们关注两件与性能无关的非常重要的事情:
可维护性
可扩展性
.
(val_dict[keys[0]],
val_dict[keys[1]],
val_dict[keys[2]],
val_dict[keys[3]]) = new_values
和
val_dict.update({keys[0]: new_values[0],
keys[1]: new_values[1],
keys[2]: new_values[2],
keys[3]: new_values[3]})
硬代码(维护噩梦)是指插入的元素数量,因此这些方法的可扩展性不太好。因此,我不会把它们包括在剩下的答案中。我并不是说这些都不好——它们只是不能很好地扩展,而且很难比较只适用于特定数量条目的函数的计时。
zip
(使用
itertools.izip
如果您使用的是python-2。x) :
def new1(val_dict, keys, new_values, length):
val_dict.update(zip(keys, new_values))
def new2(val_dict, keys, new_values, length):
for key, val in zip(keys, new_values):
val_dict[key] = val
哪种方法是解决这个问题的“最具pythonic”的方法(至少在我看来)。
我还更改了
new_values
因为在NumPy数组上迭代比将数组转换为列表然后在列表上迭代更糟糕,以防您对细节感兴趣,我在另一篇文章中详细阐述了这一部分
answer
.
让我们看看这些方法是如何执行的:
import numpy as np
def old_for(val_dict, keys, new_values, length):
for i in range(length):
val_dict[keys[i]] = new_values[i]
def old_update_comp(val_dict, keys, new_values, length):
val_dict.update({keys[i]: new_values[i] for i in range(length)})
def old_update_gen(val_dict, keys, new_values, length):
gen = ((keys[i], new_values[i]) for i in range(length))
val_dict.update(dict(gen))
def new1(val_dict, keys, new_values, length):
val_dict.update(zip(keys, new_values))
def new2(val_dict, keys, new_values, length):
for key, val in zip(keys, new_values):
val_dict[key] = val
val_dict = {'a': 1, 'b': 2, 'c': 3}
keys = ('b', 123, '89', 'c')
new_values = np.arange(10, 41, 10).tolist()
length = len(new_values)
%timeit old_for(val_dict, keys, new_values, length)
# 4.1 µs ± 183 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit old_update_comp(val_dict, keys, new_values, length)
# 9.56 µs ± 180 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit old_update_gen(val_dict, keys, new_values, length)
# 17 µs ± 332 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit new1(val_dict, keys, new_values, length)
# 5.92 µs ± 123 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit new2(val_dict, keys, new_values, length)
# 3.23 µs ± 84.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
val_dict = {'a': 1, 'b': 2, 'c': 3}
keys = range(1000)
new_values = range(1000)
length = len(new_values)
%timeit old_for(val_dict, keys, new_values, length)
# 1.08 ms ± 26 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit old_update_comp(val_dict, keys, new_values, length)
# 1.08 ms ± 13.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit old_update_gen(val_dict, keys, new_values, length)
# 1.44 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit new1(val_dict, keys, new_values, length)
# 242 µs ± 3.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit new2(val_dict, keys, new_values, length)
# 346 µs ± 8.24 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
因此,对于较大的输入,我的方法似乎比您的方法快得多(2-5倍)。
cdef
或
cpdef
功能,所以我只对其他方法进行了分类:
%load_ext cython
%%cython
cpdef new1_cy(dict val_dict, tuple keys, new_values, Py_ssize_t length):
val_dict.update(zip(keys, new_values.tolist()))
cpdef new2_cy(dict val_dict, tuple keys, new_values, Py_ssize_t length):
for key, val in zip(keys, new_values.tolist()):
val_dict[key] = val
cpdef new3_cy(dict val_dict, tuple keys, int[:] new_values, Py_ssize_t length):
cdef Py_ssize_t i
for i in range(length):
val_dict[keys[i]] = new_values[i]
这次我做了
keys
一
tuple
import numpy as np
val_dict = {'a': 1, 'b': 2, 'c': 3}
keys = tuple(range(4))
new_values = np.arange(4)
length = len(new_values)
%timeit new1(val_dict, keys, new_values, length)
# 7.88 µs ± 317 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit new2(val_dict, keys, new_values, length)
# 4.4 µs ± 140 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit new2_cy(val_dict, keys, new_values, length)
# 5.51 µs ± 56.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
val_dict = {'a': 1, 'b': 2, 'c': 3}
keys = tuple(range(1000))
new_values = np.arange(1000)
length = len(new_values)
%timeit new1_cy(val_dict, keys, new_values, length)
# 208 µs ± 9.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit new2_cy(val_dict, keys, new_values, length)
# 231 µs ± 13.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit new3_cy(val_dict, keys, new_values, length)
# 156 µs ± 4.13 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
因此,如果你有一个元组和一个numpy数组,你可以通过使用普通索引和memoryview的函数实现几乎2倍的加速
new3_cy
. 至少如果你有很多需要插入的键值对。
operator.itemgetter
这可能是最好的方法。