将python导入放在函数中是否存在性能成本?

时间:2015-08-22 00:48:19

标签: python

我构建了非常复杂的python应用程序,通常使用Django。为了简化应用程序间接口,我有时会使用从模型中抽象出来的service.py模块。

由于这些'聚合功能',它们经常以循环导入结束,通过将import语句放在服务函数中可以轻松消除。

与通常移动的进口尽可能接近其使用点是否存在显着的性能或内存成本?例如,如果我只在文件的一个函数中使用特定的导入名称,那么将导入放在该特定函数中而不是在传统位置的文件顶部似乎很自然。

这个问题与this question略有不同,因为每次导入都在函数名称空间中。

3 个答案:

答案 0 :(得分:11)

如果您担心的话,导入模块的点不会导致性能下降。模块是单例,每次遇到import语句时都不会import。但是,如何进行导入以及后续属性查找 会产生影响。

例如,如果您import math,那么每次您需要使用sin(...)功能时,您必须执行math.sin(...),这通常比执行from math import sin更慢并且直接使用sin(...),因为系统不必继续查找模块中的函数名称。

此查找惩罚适用于使用点.访问的任何内容,并且在循环中尤其明显。因此,建议在性能关键循环/部分中获取可能需要经常使用/调用的内容的本地引用。

例如,在关键循环之前使用原始import math示例,您可以执行以下操作:

# ... within some function
sin = math.sin
for i in range(0, REALLY_BIG_NUMBER):
    x = sin(i)   # faster than:  x = math.sin(x)
    # ...

这是一个简单的例子,但请注意,您可以使用其他对象(例如列表,词典等)的方法执行类似的操作。

我可能更关心你提到的循环进口。如果您的目的是"修复"通过将进口报表移入更多" local"地点(例如在特定功能或代码块等内)您可能需要解决更深层次的问题。

就个人而言,我通常会将导入保留在模块的顶部。由于没有充分理由而偏离该模式可能会使您的代码更难以通过,因为模块的依赖性不会立即明显(即,在整个代码中散布的import语句而不是在一个地方)。

它可能也会导致循环依赖性问题,您似乎更难以调试并且更容易陷入困境。毕竟,如果上面没有列出该模块,有人可能会高兴地认为您的模块A不依赖于模块B,然后在import A B时添加A { {1}}已经隐藏在某个黑暗角落里的import B

基准样本

这是使用查找符号的基准:

>>> timeit('for i in range(0, 10000): x = math.sin(i)', setup='import math', number=50000)
89.7203312900001

另一个基准使用查找符号:

>>> timeit('for i in range(0, 10000): x = sin(i)', setup='from math import sin', number=50000)
78.27029322999988

这里有10秒以上的差异。

请注意,您的收益取决于程序花费多长时间运行此代码 - 即。一个性能关键部分,而不是零星的函数调用。

答案 1 :(得分:4)

请参阅this问题。

基本上,每当您导入模块时,如果已导入模块,它将使用缓存值。

这意味着性能将在第一次加载模块时被触发,但是一旦加载模块,它将缓存未来调用的值。

答案 2 :(得分:0)

正如雷所说,导入特定功能(稍快) 1.62852311134 for sin() 1.89815092087 for math.sin() 使用以下代码

from time import time
sin=math.sin
t1=time()
for i in xrange(10000000):
x=sin(i)
t2=time()
for i in xrange(10000000):
    z=math.sin(i)
t3=time()
print (t2-t1)
print (t3-t2)