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()

当前回答

在函数中导入变量/局部作用域可以提高性能。这取决于函数中导入对象的使用情况。如果你多次循环并访问一个模块全局对象,将它导入为本地会有帮助。

test.py

X=10
Y=11
Z=12
def add(i):
  i = i + 10

runlocal.py

from test import add, X, Y, Z

    def callme():
      x=X
      y=Y
      z=Z
      ladd=add 
      for i  in range(100000000):
        ladd(i)
        x+y+z

    callme()

run.py

from test import add, X, Y, Z

def callme():
  for i in range(100000000):
    add(i)
    X+Y+Z

callme()

在Linux上的时间显示了一个小的增益

/usr/bin/time -f "\t%E real,\t%U user,\t%S sys" python run.py 
    0:17.80 real,   17.77 user, 0.01 sys
/tmp/test$ /usr/bin/time -f "\t%E real,\t%U user,\t%S sys" python runlocal.py 
    0:14.23 real,   14.22 user, 0.01 sys

真实的是挂钟。用户是程序中的时间。Sys是系统调用的时间。

https://docs.python.org/3.5/reference/executionmodel.html#resolution-of-names

其他回答

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

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

我很惊讶没有看到重复负载检查的实际成本数字,尽管有很多很好的解释。

如果你在顶部导入,不管发生什么,你都要加载命中。这非常小,但通常是毫秒级,而不是纳秒级。

If you import within a function(s), then you only take the hit for loading if and when one of those functions is first called. As many have pointed out, if that doesn't happen at all, you save the load time. But if the function(s) get called a lot, you take a repeated though much smaller hit (for checking that it has been loaded; not for actually re-loading). On the other hand, as @aaronasterling pointed out you also save a little because importing within a function lets the function use slightly-faster local variable lookups to identify the name later (http://stackoverflow.com/questions/477096/python-import-coding-style/4789963#4789963).

下面是一个简单测试的结果,该测试从函数内部导入了一些内容。报告的时间(在2.3 GHz Intel Core i7上的Python 2.7.14中)如下所示(第2个调用比后面的调用多似乎是一致的,尽管我不知道为什么)。

 0 foo:   14429.0924 µs
 1 foo:      63.8962 µs
 2 foo:      10.0136 µs
 3 foo:       7.1526 µs
 4 foo:       7.8678 µs
 0 bar:       9.0599 µs
 1 bar:       6.9141 µs
 2 bar:       7.1526 µs
 3 bar:       7.8678 µs
 4 bar:       7.1526 µs

代码:

from __future__ import print_function
from time import time

def foo():
    import collections
    import re
    import string
    import math
    import subprocess
    return

def bar():
    import collections
    import re
    import string
    import math
    import subprocess
    return

t0 = time()
for i in xrange(5):
    foo()
    t1 = time()
    print("    %2d foo: %12.4f \xC2\xB5s" % (i, (t1-t0)*1E6))
    t0 = t1
for i in xrange(5):
    bar()
    t1 = time()
    print("    %2d bar: %12.4f \xC2\xB5s" % (i, (t1-t0)*1E6))
    t0 = t1

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

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

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

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

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

在函数中导入变量/局部作用域可以提高性能。这取决于函数中导入对象的使用情况。如果你多次循环并访问一个模块全局对象,将它导入为本地会有帮助。

test.py

X=10
Y=11
Z=12
def add(i):
  i = i + 10

runlocal.py

from test import add, X, Y, Z

    def callme():
      x=X
      y=Y
      z=Z
      ladd=add 
      for i  in range(100000000):
        ladd(i)
        x+y+z

    callme()

run.py

from test import add, X, Y, Z

def callme():
  for i in range(100000000):
    add(i)
    X+Y+Z

callme()

在Linux上的时间显示了一个小的增益

/usr/bin/time -f "\t%E real,\t%U user,\t%S sys" python run.py 
    0:17.80 real,   17.77 user, 0.01 sys
/tmp/test$ /usr/bin/time -f "\t%E real,\t%U user,\t%S sys" python runlocal.py 
    0:14.23 real,   14.22 user, 0.01 sys

真实的是挂钟。用户是程序中的时间。Sys是系统调用的时间。

https://docs.python.org/3.5/reference/executionmodel.html#resolution-of-names

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

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

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

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

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