为什么要优化这个?您是否编写了工作、测试过的代码,然后检查了您的算法profiled 您的代码并发现优化它会产生效果?您是否在发现自己花费时间的深层内部循环中这样做?如果没有,请不要打扰。
您只有通过计时才能知道哪个对您最有效。要以有用的方式计时,您必须将其专门用于您的实际用例。例如,列表推导式中的函数调用与内联表达式中的函数调用之间存在明显的性能差异;目前尚不清楚您是否真的想要前者,或者您是否将其简化为使您的案例相似。
您说最终得到一个 numpy 数组还是一个 list 并不重要,但如果您正在进行这种微优化,它确实很重要,因为当您之后使用它们时,它们的性能会有所不同。解决这个问题可能会很棘手,所以希望它会证明整个问题还为时过早。
-
为了清晰、易读等,最好简单地使用正确的工具来完成工作。我很难在这些事情之间做出决定。
- 如果我需要 numpy 数组,我会使用它们。我将使用它们来存储大型同构数组或多维数据。我经常使用它们,但很少在我认为我想使用列表的地方使用它们。
- 如果我使用这些,我会尽我所能编写我的函数已经矢量化,这样我就不必使用
numpy.vectorize。例如,下面的 times_five 可以用于没有修饰的 numpy 数组。
- 如果我没有理由使用 numpy,也就是说,如果我没有解决数值数学问题或使用特殊的 numpy 功能或存储多维数组或其他什么...
- 如果我有一个已经存在的函数,我会使用
map。这就是它的用途。
- 如果我有一个适合小表达式的操作并且我不需要函数,我会使用列表推导式。
- 如果我只想对所有情况进行操作,但实际上不需要存储结果,我会使用普通的 for 循环。
- 在许多情况下,我实际上会使用
map 并列出推导式的惰性等效项:itertools.imap 和生成器表达式。在某些情况下,这些可以将内存使用量减少 n 倍,并且有时可以避免执行不必要的操作。
如果事实证明这就是性能问题所在,那么正确处理这类事情是很棘手的。 非常人们经常为他们的实际问题计时错误的玩具箱。更糟糕的是,非常普通的人会根据它制定愚蠢的通用规则。
考虑以下情况(timeme.py 贴在下面)
python -m timeit "from timeme import x, times_five; from numpy import vectorize" "vectorize(times_five)(x)"
1000 loops, best of 3: 924 usec per loop
python -m timeit "from timeme import x, times_five" "[times_five(item) for item in x]"
1000 loops, best of 3: 510 usec per loop
python -m timeit "from timeme import x, times_five" "map(times_five, x)"
1000 loops, best of 3: 484 usec per loop
一个天真的观察者会得出结论,地图是这些选项中表现最好的,但答案仍然是“视情况而定”。考虑使用您正在使用的工具的优势:列表推导式让您避免定义简单的函数;如果您做正确的事情,numpy 可以让您在 C 中对事物进行矢量化。
python -m timeit "from timeme import x, times_five" "[item + item + item + item + item for item in x]"
1000 loops, best of 3: 285 usec per loop
python -m timeit "import numpy; x = numpy.arange(1000)" "x + x + x + x + x"
10000 loops, best of 3: 39.5 usec per loop
但这还不是全部——还有更多。考虑算法更改的力量。它可以更具戏剧性。
python -m timeit "from timeme import x, times_five" "[5 * item for item in x]"
10000 loops, best of 3: 147 usec per loop
python -m timeit "import numpy; x = numpy.arange(1000)" "5 * x"
100000 loops, best of 3: 16.6 usec per loop
有时,算法更改可能会更有效。随着数字越来越大,这将越来越有效。
python -m timeit "from timeme import square, x" "map(square, x)"
10 loops, best of 3: 41.8 msec per loop
python -m timeit "from timeme import good_square, x" "map(good_square, x)"
1000 loops, best of 3: 370 usec per loop
即使是现在,这一切也可能对您的实际问题影响不大。如果你能正确使用 numpy,它看起来很棒,但它有其局限性:这些 numpy 示例都没有使用数组中的实际 Python 对象。这使必须做的事情变得复杂;甚至很多。如果我们确实可以使用 C 数据类型呢?这些不如 Python 对象健壮。它们不可为空。整数溢出。你必须做一些额外的工作来检索它们。它们是静态类型的。有时这些事情被证明是问题,甚至是意想不到的问题。
所以你去:一个明确的答案。 “这取决于。”
# timeme.py
x = xrange(1000)
def times_five(a):
return a + a + a + a + a
def square(a):
if a == 0:
return 0
value = a
for i in xrange(a - 1):
value += a
return value
def good_square(a):
return a ** 2