Python高效包装器



Python函数调用相对昂贵。但是我一直遇到这样的情况,我希望能够以不同的方式调用函数,最简单的方法似乎是用不同的调用在函数周围创建一个轻包装。

是否有更python和/或更有效的方法来启用多个方法调用函数?


一个完全人为的,过于简单的例子来说明我的问题:

from math import sqrt
from collections import namedtuple
Point = namedtuple('Point', 'x y')
def distFunc(x1, y1, x2, y2):
    return sqrt((x1-x2)**2 + (y1-y2)**2)
def pointDistFunc(p1, p2):
    return distFunc(p1.x, p1.y, p2.x, p2.y)

是否有更好的方法来写pointDistFunc?

事实上,这一次它:

p1 = Point(1, 1)
p2 = Point(100, 100)
if __name__ == '__main__':
    import timeit
    print(timeit.timeit("distFunc(1, 1, 100, 100)", setup="from __main__ import distFunc"))
    print(timeit.timeit('pointDistFunc(p1, p2)', setup= 'from __main__ import pointDistFunc, p1, p2'))

给:

0.392938508373
0.977704155415

所以开销看起来很明显

我认为,总的来说,最好的事情是编写最清晰的代码,不要过于担心效率。我想在这种情况下,我会按照你已经做过的方式编码,而不用担心它。

但是如果你知道一些代码将被大量调用,并且你希望它尽可能快,那么你可以通过重写来加快速度。在您的简单示例中,您可以通过重写包装器使其只执行计算来提高速度:

def pointDistFunc(p1, p2):
    return sqrt((p1.x-p2.x)**2 + (p1.y-p2.y)**2)

理想情况下,你应该有一些单元测试来检查

pointDistFunc(p1, p2) == distFunc(p1.x, p1.y, p2.x, p2.y)

这样,如果您最终更改了distFunc(),但忘记更改pointDistFunc(),测试将失败,您将被提醒。

您提到的指导原则并不是要阻止您编写包装器;它更多的是建议如何重写热点,包括列表:

def gen_point_dist_from_lst(lst, p2):
    return (sqrt((p1.x-p2.x)**2 + (p1.y-p2.y)**2) for p1 in lst)

如果列表有1000个点,那么与直接的生成器表达式

相比,上面的代码节省了2000个函数调用
(pointDistFunc(p1, p2) for p1 in lst)

关键是在你尝试这些技巧之前,首先要有一个问题。如果您的程序已经运行得足够快,也许您不需要优化任何东西。如果你需要你的代码更快,你可以试试这些技巧。

注:如果您可以将PyPy用于您正在做的事情,那么它应该可以消除函数调用带来的开销。PyPy有一个即时编译器,可以为您优化程序中的热点。

http://speed.pypy.org/

最新更新