PEP 8规定:

导入总是放在文件的顶部,就在任何模块注释和文档字符串之后,在模块全局变量和常量之前。

然而,如果我导入的类/方法/函数只在很少的情况下使用,那么在需要时进行导入肯定会更有效吗?

这不是:

class SomeClass(object):

    def not_often_called(self)
        from datetime import datetime
        self.datetime = datetime.now()

比这更有效率?

from datetime import datetime

class SomeClass(object):

    def not_often_called(self)
        self.datetime = datetime.now()

当前回答

我想提一下我的一个用例,与@John Millikin和@ v.k.提到的用例非常相似:

可选的进口

我使用Jupyter Notebook进行数据分析,我使用相同的IPython Notebook作为所有分析的模板。在某些情况下,我需要导入Tensorflow来做一些快速的模型运行,但有时我工作的地方,Tensorflow没有设置/导入很慢。在这些情况下,我将依赖于tensorflow的操作封装在一个helper函数中,在该函数中导入tensorflow,并将其绑定到一个按钮。

这样,我就可以“重新启动并运行全部”,而不必等待导入,也不必在导入失败时恢复其余的单元格。

其他回答

这就像许多其他优化一样——你牺牲了一些可读性来换取速度。正如John提到的,如果您已经完成了分析作业,并且发现这是一个非常有用的更改,并且您需要额外的速度,那么就去做吧。最好把所有其他的导入都放在一起:

from foo import bar
from baz import qux
# Note: datetime is imported in SomeClass below

模块导入非常快,但不是即时的。这意味着:

将导入放在模块的顶部是可以的,因为这是一个微不足道的成本,只需要支付一次。 将导入放在函数中会导致对该函数的调用花费更长的时间。

所以如果你关心效率,把进口放在最上面。只有在分析显示有帮助的情况下,才将它们移动到函数中(您进行了分析,以查看哪里可以最好地提高性能,对吗??)


我所见过的执行惰性导入的最佳理由是:

可选的库支持。如果您的代码有多个使用不同库的路径,如果没有安装可选库,请不要中断。 在插件的__init__.py中,该插件可能被导入,但实际上没有使用。例如Bazaar插件,它们使用bzrlib的惰性加载框架。

为了完成老谋子的回答和最初的问题:

当我们不得不处理循环依赖关系时,我们可以做一些“技巧”。假设我们正在处理模块a.py和b.py,它们分别包含x()和b.y()。然后:

我们可以移动模块底部的from导入之一。 我们可以将其中一个from导入移动到实际需要导入的函数或方法中(这并不总是可行的,因为您可能从多个地方使用它)。 我们可以把其中一个import改成import,就像import a

总结一下。如果您没有处理循环依赖关系,也没有使用某种技巧来避免它们,那么最好将所有导入放在顶部,因为原因已经在这个问题的其他答案中解释过了。请在做这些“技巧”时附上评论,这总是受欢迎的!:)

我采用了将所有导入放在使用它们的函数中,而不是放在模块的顶部的做法。

这样做的好处是能够更可靠地进行重构。当我将一个函数从一个模块移动到另一个模块时,我知道该函数将继续工作,并且保留所有遗留的测试。如果我将导入放在模块的顶部,当我移动一个函数时,我发现我最终要花费大量时间来完成新模块的导入并使其最小化。重构IDE可能会让这一点变得无关紧要。

正如在其他地方提到的那样,有一个速度惩罚。我在我的应用程序中测量了这一点,发现它对我的目的来说是微不足道的。

不需要搜索(例如grep)就能看到所有模块依赖关系也是很好的。然而,我关心模块依赖关系的原因通常是因为我正在安装、重构或移动由多个文件组成的整个系统,而不仅仅是单个模块。在这种情况下,我无论如何都要执行全局搜索,以确保具有系统级依赖关系。因此,我还没有找到全局导入来帮助我在实践中理解一个系统。

我通常把sys的导入放在if __name__=='__main__'检查中,然后将参数(如sys.argv[1:])传递给main()函数。这允许我在sys未被导入的上下文中使用main。

Curt提出了一个很好的观点:第二个版本更清晰,并且会在加载时失败,而不是在加载后失败,而且出乎意料。

通常我不担心加载模块的效率,因为它(a)非常快,(b)大多数只发生在启动时。

如果你不得不在意想不到的时候加载重量级模块,使用__import__函数动态加载它们可能更有意义,并确保捕获ImportError异常,并以合理的方式处理它们。