如果有疑问,我通常会将导入语句放在模块的顶部。通常,这会减少重复,这很好。但是,在只有一个函数(或类)需要导入的情况下,是否存在性能下降?
在调用函数时只会导入以下内容吗?
def func():
from task import test
如果是这样,我想这可能是一种轻微的效率。我还假设你可以为更快的垃圾收集和变量作用域获得一些附加点,因为导入的对象不会被添加到全局字典中。正如另一张海报很好地说明的那样:
这主要是由于查找变量。查找全局范围中的变量需要查找字典。相反,编译器静态地确定本地名称并通过索引引用它们,因此不需要查找字典。
这些公平的假设是我完全偏离基础吗?
由于
答案 0 :(得分:6)
仅在函数运行时导入函数中的导入。请记住,在Python中,所有语句在遇到它们时都会执行,而导入语句就像其他任何语句一样。导入模块时会导入顶级导入,因为它们是模块中的顶级语句。
您对名称查找的担忧是错误的:差异可以忽略不计,只有在分析显示问题时才应考虑。
我只将模块导入功能范围有两个原因:1)修复循环导入问题,可能通过重构以其他方式解决,或者2)如果模块是可选的,并且该函数未被使用我的很多用户。在2)的情况下,模块可以完全丢失,除非有人调用该函数,否则不会出现问题。
答案 1 :(得分:3)
让我们看看以下两个函数的字节码是什么样的:
def func1():
""" test imported each time function is run """
from task import test
test()
def func2():
""" test was imported at top of module """
test()
如下所示,func2()
使用全局导入的test
函数可以节省大量步骤。
>>> dis.dis(func1)
3 0 LOAD_CONST 1 (-1)
3 LOAD_CONST 2 (('test',))
6 IMPORT_NAME 0 (task)
9 IMPORT_FROM 1 (test)
12 STORE_FAST 0 (test)
15 POP_TOP
4 16 LOAD_FAST 0 (test)
19 CALL_FUNCTION 0
22 POP_TOP
23 LOAD_CONST 3 (None)
26 RETURN_VALUE
>>> dis.dis(func2)
3 0 LOAD_GLOBAL 0 (test)
3 CALL_FUNCTION 0
6 POP_TOP
7 LOAD_CONST 1 (None)
10 RETURN_VALUE
正如在德尔南的评论中指出的那样,预先考虑这一点可能是过早的优化。
对于位于全局命名空间中的test
,这不太可能导致任何查找性能问题。我认为你可以看到的最值得注意的方式是,如果test
存在哈希冲突,并且经常使用另一个名称,这会导致查找第二个名称需要更长时间。再次,过早优化以预先考虑这种罕见的情况。
答案 2 :(得分:0)
如果不经常调用导入,我认为将导入放在定义中是有意义的。