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()
我不太担心预先加载模块的效率。模块占用的内存不会很大(假设它足够模块化),启动成本可以忽略不计。
在大多数情况下,您希望在源文件的顶部加载模块。对于阅读代码的人来说,它可以更容易地区分哪个函数或对象来自哪个模块。
在代码的其他地方导入模块的一个很好的理由是,如果它在调试语句中使用。
例如:
do_something_with_x(x)
我可以用:
from pprint import pprint
pprint(x)
do_something_with_x(x)
当然,在代码的其他地方导入模块的另一个原因是,如果您需要动态导入它们。这是因为你几乎没有任何选择。
我不太担心预先加载模块的效率。模块占用的内存不会很大(假设它足够模块化),启动成本可以忽略不计。
我采用了将所有导入放在使用它们的函数中,而不是放在模块的顶部的做法。
这样做的好处是能够更可靠地进行重构。当我将一个函数从一个模块移动到另一个模块时,我知道该函数将继续工作,并且保留所有遗留的测试。如果我将导入放在模块的顶部,当我移动一个函数时,我发现我最终要花费大量时间来完成新模块的导入并使其最小化。重构IDE可能会让这一点变得无关紧要。
正如在其他地方提到的那样,有一个速度惩罚。我在我的应用程序中测量了这一点,发现它对我的目的来说是微不足道的。
不需要搜索(例如grep)就能看到所有模块依赖关系也是很好的。然而,我关心模块依赖关系的原因通常是因为我正在安装、重构或移动由多个文件组成的整个系统,而不仅仅是单个模块。在这种情况下,我无论如何都要执行全局搜索,以确保具有系统级依赖关系。因此,我还没有找到全局导入来帮助我在实践中理解一个系统。
我通常把sys的导入放在if __name__=='__main__'检查中,然后将参数(如sys.argv[1:])传递给main()函数。这允许我在sys未被导入的上下文中使用main。
Putting the import statement inside of a function can prevent circular dependencies.
For example, if you have 2 modules, X.py and Y.py, and they both need to import each other, this will cause a circular dependency when you import one of the modules causing an infinite loop. If you move the import statement in one of the modules then it won't try to import the other module till the function is called, and that module will already be imported, so no infinite loop. Read here for more - effbot.org/zone/import-confusion.htm
I do not aspire to provide complete answer, because others have already done this very well. I just want to mention one use case when I find especially useful to import modules inside functions. My application uses python packages and modules stored in certain location as plugins. During application startup, the application walks through all the modules in the location and imports them, then it looks inside the modules and if it finds some mounting points for the plugins (in my case it is a subclass of a certain base class having a unique ID) it registers them. The number of plugins is large (now dozens, but maybe hundreds in the future) and each of them is used quite rarely. Having imports of third party libraries at the top of my plugin modules was a bit penalty during application startup. Especially some thirdparty libraries are heavy to import (e.g. import of plotly even tries to connect to internet and download something which was adding about one second to startup). By optimizing imports (calling them only in the functions where they are used) in the plugins I managed to shrink the startup from 10 seconds to some 2 seconds. That is a big difference for my users.
所以我的答案是否定的,不要总是把导入放在模块的顶部。
我很惊讶没有看到重复负载检查的实际成本数字,尽管有很多很好的解释。
如果你在顶部导入,不管发生什么,你都要加载命中。这非常小,但通常是毫秒级,而不是纳秒级。
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
在函数中导入变量/局部作用域可以提高性能。这取决于函数中导入对象的使用情况。如果你多次循环并访问一个模块全局对象,将它导入为本地会有帮助。
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
可读性
除了启动性能外,本地化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版本号,因此在文件顶部导入会引入不必要的启动延迟。
虽然PEP鼓励在模块顶部导入,但在其他级别导入并不会出错。这表明进口应该在顶部,但也有例外。
在使用模块时加载模块是一种微优化。导入缓慢的代码可以在以后进行优化,如果这会产生相当大的差异的话。
不过,您可以在尽可能靠近顶部的位置引入标志,以便有条件地导入,允许用户使用配置导入所需的模块,同时仍然立即导入所有内容。
尽快导入意味着如果任何导入(或导入的导入)缺失或有语法错误,程序将失败。如果所有导入都发生在所有模块的顶部,则python分两步工作。编译。运行。
内置模块可以在任何导入它们的地方工作,因为它们设计得很好。您编写的模块应该是相同的。将导入移动到顶部或它们的第一次使用位置有助于确保没有副作用,并且代码正在注入依赖项。
无论您是否将导入放在顶部,当导入放在顶部时,代码都应该仍然可以工作。所以从立即导入开始,然后根据需要进行优化。
以下是对这个问题的最新答案总结
而且
相关的
的问题。
PEP 8
recommends putting imports at the top.
It's often more convenient to get
ImportErrors
when you first run your program
rather than when your program first calls your function.
Putting imports in the function scope
can help avoid issues with circular imports.
Putting imports in the function scope
helps keep maintain a clean module namespace,
so that it does not appear among tab-completion suggestions.
Start-up time:
imports in a function won't run until (if) that function is called.
Might get significant with heavy-weight libraries.
Even though import statements are super fast on subsequent runs,
they still incur a speed penalty
which can be significant if the function is trivial but frequently in use.
Imports under the __name__ == "__main__" guard seem very reasonable.
Refactoring
might be easier if the imports are located in the function
where they're used (facilitates moving it to another module).
It can also be argued that this is good for readability.
However, most would argue the contrary, i.e.
Imports at the top enhance readability,
since you can see all your dependencies at a glance.
It seems unclear if dynamic or conditional imports favour one style over another.