Python 高效包装器

Posted

技术标签:

【中文标题】Python 高效包装器【英文标题】:Python Efficient Wrappers 【发布时间】:2013-02-21 23:16:17 【问题描述】:

Python 函数调用相对 expensive. 但我一直遇到我希望能够以不同方式调用函数的情况,最简单的方法似乎是使用不同的调用围绕函数创建一个轻量级包装器.

是否有比调用函数的方式更 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

所以开销似乎很明显。

【问题讨论】:

如果你只想添加参数,你可以使用functools.partial(),它使用C来避免堆栈push-pop。 【参考方案1】:

我认为,总的来说,最好的办法是写出最清晰的代码,不要过分担心效率。我认为在这种情况下,我会按照您已经完成的方式对其进行编码,而不必担心。

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

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/

【讨论】:

谢谢。仅仅忽略效率最终可能是最好的方法。在这个简单的例子中,重写很容易,但在更复杂的情况下变得不太实用,尤其是在以后可能更改代码时。

以上是关于Python 高效包装器的主要内容,如果未能解决你的问题,请参考以下文章

包装器类型

Python:C++ 的包装器需要访问 .dll 文件

Python C 包装器内存泄漏

试图理解 Python 包装器

python包装函数在装饰器中接受参数

python Python中的Bluetoothctl包装器