Python在功能级别VS导入。模块级别

时间:2012-01-27 17:50:27

标签: python import scope

如果有疑问,我通常会将导入语句放在模块的顶部。通常,这会减少重复,这很好。但是,在只有一个函数(或类)需要导入的情况下,是否存在性能下降?

在调用函数时只会导入以下内容吗?

     def func():
         from task import test

如果是这样,我想这可能是一种轻微的效率。我还假设你可以为更快的垃圾收集和变量作用域获得一些附加点,因为导入的对象不会被添加到全局字典中。正如另一张海报很好地说明的那样:

  

这主要是由于查找变量。查找全局范围中的变量需要查找字典。相反,编译器静态地确定本地名称并通过索引引用它们,因此不需要查找字典。

这些公平的假设是我完全偏离基础吗?

由于

3 个答案:

答案 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)

如果不经常调用导入,我认为将导入放在定义中是有意义的。