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,并将其绑定到一个按钮。

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

其他回答

这是一个只有程序员才能决定的权衡。

Case 1在需要时才导入datetime模块(并进行任何可能需要的初始化),从而节省了一些内存和启动时间。请注意,“仅在被调用时”导入也意味着“每次被调用时”导入,因此第一次调用之后的每个调用仍然会产生执行导入的额外开销。

情况2通过提前导入datetime来节省一些执行时间和延迟,这样在调用not_often_called()时就会更快地返回,而且也不会在每次调用时都产生导入的开销。

除了效率,如果import语句是…前面。将它们隐藏在代码中会使查找某个组件所依赖的模块变得更加困难。

就我个人而言,我通常遵循PEP,除了单元测试之类的东西,我不希望总是加载这些东西,因为我知道除了测试代码之外,它们不会被使用。

这是一个引人入胜的讨论。和许多人一样,我从未考虑过这个话题。我不得不在函数中导入,因为我想在我的一个库中使用Django ORM。在导入我的模型类之前,我不得不调用django.setup(),因为这是在文件的顶部,它被拖到完全非django库代码中,因为IoC注入器结构。

我稍微改了一下,最后把django.setup()放在了单例构造函数中,并把相关的导入放在了每个类方法的顶部。现在这运行得很好,但让我感到不安,因为导入不在顶部,而且我开始担心导入的额外时间。然后我来到这里,怀着极大的兴趣阅读了大家对此的看法。

我有很长的c++背景,现在使用Python/Cython。我对此的看法是,为什么不把导入放在函数中,除非它会导致一个概要瓶颈。这就像在你需要变量之前为它们声明空间一样。问题是我有数千行代码,所有的导入都在顶部!所以我想从现在开始,当我有时间的时候,我会在这里和那里改变奇怪的文件。

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

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

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

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

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

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

除了已经给出的优秀答案之外,值得注意的是导入的位置不仅仅是风格的问题。有时,模块具有需要首先导入或初始化的隐式依赖项,而顶层导入可能会导致违反所需的执行顺序。

这个问题经常出现在Apache Spark的Python API中,在导入任何pyspark包或模块之前,你需要初始化SparkContext。最好将pyspark导入放在保证SparkContext可用的范围内。