使用此main.py
时
from mymodule import myfunc
GENERALPARAM = "6878623"
for i in xrange(10000):
myfunc(i)
而这个mymodule.py
:
def myfunc(a):
# very fast computation here
if condition(a):
log(a, GENERALPARAM)
如何使该GENERALPARAM
在mymodule.py
的myfunc
中可用?通过使用全局变量(如何?
当然,我可以将其作为参数传递:
#main.py
for i in xrange(10000):
myfunc(i, GENERALPARAM)
#mymodule.py
def myfunc(a, GENERALPARAM):
...
但我想避免将相同的变量作为参数传递数百万次(对于非常短的计算)。
TL;DR:没有干净的方法可以做到这一点,实际上有很好的理由。此外,很有可能将其作为参数传递,即使不是更快,也会同样快(参数是函数的本地参数,本地查找比全局查找更快)。
更详细的答案:
"很好的理由"是关于可维护性和健壮性。仅依赖于其输入的函数易于理解和测试,并且具有可预测的行为。依赖于模块级全局的函数已经更难理解和测试,并且变得难以预测。依赖于主脚本中定义的全局的函数(是的,这可以通过黑客的方式完成,我不会在这里展示这种方式)不仅完全不可测试和不可预测,而且还要求导入模块确实定义了这个全局,这充其量是脆弱的(实际上使模块在不同的上下文中无法使用)。关于"全局是邪恶的"话题已经有很多废话了,所以我不会进一步解释......
现在 wrt/OP 动机,即
我想避免通过数百万次(很短的时间) 计算)与参数相同的变量。
我认为OP在这里主要关注的是表演......
在 Python 中,将参数传递给函数的成本相当低 - 没有复制任何东西,实际传递的是(引用)对象本身(参见 Ned Batcheler 关于 Python 名称和值的参考文章以获取更多详细信息)。 此外,参数是函数的局部变量,因此函数执行期间的名称查找是最快的 - 比解析模块全局更快,也比解析模块全局上的属性快得多。因此,干净而明显的解决方案(将 objec 作为参数传递)可能与黑客解决方案一样快,如果不是更快的话。
事实上,我对这两种解决方案都做了一个快速粗略的基准测试 - 唉,没有timeit模块,它会产生更准确的结果,但黑客的本质使得不可能在这里使用timeit(是的,黑客永远不会让你的生活更轻松) - 同时使用Python 2.7.6和Python 3.4.3,平均差异(在主循环中使用500000次迭代)非常小,实际上小于平均值同一代码版本的两个调用之间的可变性。我怀疑与函数中具有多次查找变量的实际代码相比,由于本地查找与全局查找成本相比,干净的解决方案往往会变得更快。
我用于基准测试的代码:
清洁解决方案:
# main2.py
import datetime
start = datetime.datetime.now()
from lib2 import myfunc
GENERALPARAM = ["foo", "bar"]
if __name__ == "__main__":
for i in range(500000):
myfunc(i, GENERALPARAM)
done = datetime.datetime.now()
print("done in %s" % (done - start))
# lib2.py
def myfunc(i, p):
return "{} - {}".format(i, p)
黑客解决方案:
# main.py
import datetime
start = datetime.datetime.now()
from lib import myfunc
GENERALPARAM = ["foo", "bar"]
if __name__ == "__main__":
for i in range(500000):
myfunc(i)
done = datetime.datetime.now()
print("done in %s" % (done - start))
# lib.py
import __main__ as main # this is the hack. DONT DO THIS AT HOME
def myfunc(i):
return "{} - {}".format(i, main.GENERALPARAM)