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提到的,如果您已经完成了分析作业,并且发现这是一个非常有用的更改,并且您需要额外的速度,那么就去做吧。最好把所有其他的导入都放在一起:

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

其他回答

可读性

除了启动性能外,本地化import语句还需要考虑可读性。例如,在我目前的第一个python项目中使用python行号1283到1296:

listdata.append(['tk font version', font_version])
listdata.append(['Gtk version', str(Gtk.get_major_version())+"."+
                 str(Gtk.get_minor_version())+"."+
                 str(Gtk.get_micro_version())])

import xml.etree.ElementTree as ET

xmltree = ET.parse('/usr/share/gnome/gnome-version.xml')
xmlroot = xmltree.getroot()
result = []
for child in xmlroot:
    result.append(child.text)
listdata.append(['Gnome version', result[0]+"."+result[1]+"."+
                 result[2]+" "+result[3]])

如果import语句在文件的顶部,我将不得不向上滚动很长一段距离,或按Home键,以找出ET是什么。然后我将不得不返回到第1283行继续阅读代码。

实际上,即使import语句像许多人那样位于函数(或类)的顶部,也需要向上和向下分页。

很少会显示Gnome版本号,因此在文件顶部导入会引入不必要的启动延迟。

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

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

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

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

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

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

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

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

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

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

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


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

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

大多数情况下,这对于清晰和明智的做法是有用的,但并不总是如此。下面是模块导入可能存在于其他地方的两个例子。

首先,你可以有一个这样的单元测试模块:

if __name__ == '__main__':
    import foo
    aa = foo.xyz()         # initiate something for the test

其次,您可能需要在运行时有条件地导入一些不同的模块。

if [condition]:
    import foo as plugin_api
else:
    import bar as plugin_api
xx = plugin_api.Plugin()
[...]

在其他情况下,您可能会在代码的其他部分导入。