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

Ben*_*Ben 8 python import scope

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

在调用函数时,是否只导入以下内容?

     def func():
         from task import test
Run Code Online (Sandbox Code Playgroud)

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

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

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

谢谢

Ned*_*der 8

仅在函数运行时导入函数中的导入.请记住,在Python中,所有语句在遇到它们时都会执行,而导入语句就像其他任何语句一样.导入模块时会导入顶级导入,因为它们是模块中的顶级语句.

您对名称查找的担忧是错误的:差异可以忽略不计,只有在分析显示问题时才应考虑.

我只将模块导入函数作用域有两个原因:1)修复循环导入问题,可能通过重构以其他方式解决,或者2)如果模块是可选的,并且许多函数没有使用我的用户.在2)的情况下,模块可以完全丢失,除非有人调用该函数,否则不会出现问题.


And*_*ark 5

让我们看看以下两个函数的字节码是什么样的:

def func1():
    """ test imported each time function is run """
    from task import test
    test()

def func2():
    """ test was imported at top of module """
    test()
Run Code Online (Sandbox Code Playgroud)

如下所示,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
Run Code Online (Sandbox Code Playgroud)

正如 delnan 的评论中指出的那样,预先考虑到这一点可能是不成熟的优化。

至于test位于全局命名空间中,这不太可能导致任何查找性能问题。我认为您可以看到这一点的最值得注意的方式是,如果您经常使用的另一个名称存在哈希冲突test,这会导致第二个名称的查找时间更长。再次强调,过早的优化需要预先考虑这种罕见的情况。

  • 但请注意,它实际上不会多次导入模块 - 它只是在每次调用时检查是否必须导入。 (2认同)