Python导入编码风格
我发现了一种新的模式。 这种模式是众所周知的还是对此有何看法?
基本上,我很难清理源文件来确定哪些模块可以导入等,所以现在,而不是
import foo from bar.baz import quux def myFunction(): foo.this.that(quux)
我将所有导入移动到实际使用的函数中,如下所示:
def myFunction(): import foo from bar.baz import quux foo.this.that(quux)
这做了一些事情。 首先,我很less意外地污染了其他模块的内容。 我可以为模块设置__all__
variables,但是随着模块的发展,我不得不更新它,这对模块中实际存在的代码没有帮助。
其次,我很less在模块的顶部看到一大堆的import产品,其中一半或更多的import产品已经不再需要,因为我已经对其进行了重构。 最后,我发现这个模式很容易阅读,因为每个引用的名字就在函数体中。
(以前)这个问题的顶级答案是很好的格式,但绝对错误的performance。 让我来certificate一下
性能
顶部导入
import random def f(): L = [] for i in xrange(1000): L.append(random.random()) for i in xrange(1000): f() $ time python import.py real 0m0.721s user 0m0.412s sys 0m0.020s
在函数体中导入
def f(): import random L = [] for i in xrange(1000): L.append(random.random()) for i in xrange(1000): f() $ time python import2.py real 0m0.661s user 0m0.404s sys 0m0.008s
正如你所看到的,在模块中导入模块可能更有效率。 这样做的原因是简单的。 它将引用从全局引用移动到本地引用。 这意味着,至less对于CPython,编译器将发出LOAD_FAST
指令而不是LOAD_GLOBAL
指令。 正如其名称所暗示的,速度更快。 另一个回答者通过在循环的每个迭代中导入,人为地夸大了查找sys.modules
的性能。
通常情况下,最好在顶部导入,但是如果您正在访问模块,性能不是原因。 原因是人们可以更容易地跟踪模块的依赖,并且这样做与大多数Python世界的其他部分一致。
这确实有一些缺点。
testing
如果你想通过运行时修改来testing你的模块,可能会让它变得更加困难。 而不是做
import mymodule mymodule.othermodule = module_stub
你必须这样做
import othermodule othermodule.foo = foo_stub
这意味着你必须在全局上修补另一个模块,而不是只改变mymodule中的引用指向的地方。
依赖性跟踪
这使得你的模块依赖于什么模块是不明显的。 如果您使用许多第三方库或重新组织代码,这是特别恼人的。
我不得不维护一些使用导入内联的遗留代码,这使代码极难重构或重新打包。
性能注意事项
由于pythoncaching模块的方式,没有性能影响。 实际上,由于模块位于本地名称空间中,因此在函数中导入模块会有一些性能优势。
顶部导入
import random def f(): L = [] for i in xrange(1000): L.append(random.random()) for i in xrange(10000): f() $ time python test.py real 0m1.569s user 0m1.560s sys 0m0.010s
在函数体中导入
def f(): import random L = [] for i in xrange(1000): L.append(random.random()) for i in xrange(10000): f() $ time python test2.py real 0m1.385s user 0m1.380s sys 0m0.000s
这种方法的几个问题:
- 打开文件所依赖的模块并不明显。
- 它会混淆必须分析依赖项的程序,比如
py2exe
,py2app
等。 - 那么你在许多function中使用的模块呢? 你最终会导致大量的冗余导入,或者你必须在文件顶部和一些内部函数中有一些。
所以…首选的方法是把所有的import放在文件的顶部。 我发现如果我的import很难跟踪,通常意味着我有太多的代码,我最好把它分成两个或多个文件。
有些情况下,我发现函数内的导入是有用的:
- 为了处理循环依赖(如果你真的无法避免它们)
- 平台特定的代码
另外:在每个函数中放置导入实际上并不比在文件顶部慢得多。 每个模块第一次被加载它被放入sys.modules
,每个后续的导入只花费时间来查找模块,这是相当快(它不重新加载)。
另一个值得注意的事情是,函数内的from module import *
语法已经在Python 3.0中被移除了。
这里有一个简短的提到它在“Removed Syntax”
我build议你尽量避免from foo import bar
导入。 我只在内部使用它们,分解成模块是一个实现细节,不会有很多。
在所有其他地方,在导入包的地方,只需使用import foo
,然后用全名foo.bar
引用foo.bar
。 通过这种方式,您可以始终知道某个元素来自何处,而不必维护导入元素的列表(实际上,这将始终过时并导入不再使用的元素)。
如果foo
是一个很长的名字,你可以用import foo as f
来简化它,然后写f.bar
。 这比维护所有import产品还要方便和明确。
人们已经很好地解释了为什么要避免内联input,而不是真正的替代工作stream来解决你首先想要的原因。
我很难清理源文件来确定哪些模块导入可用,等等
要检查未使用的import,我使用pylint 。 它对Python代码进行静态(ish)分析,其中检查的(很多)是未使用的导入。 例如,下面的脚本
import urllib import urllib2 urllib.urlopen("http://stackoverflow.com")
将会生成以下消息:
example.py:2 [W0611] Unused import urllib2
至于检查可用的import,我通常依靠TextMate的(相当简单)完成 – 当您按Esc键时,它将与文档中的其他人完成当前单词。 如果我已经完成import urllib
, urll[Esc]
将展开到urllib
,如果不是,我跳转到文件的开始并添加导入。
从性能的angular度来看,你可以看到这样的情况: Python导入语句应该总是在模块的顶部?
一般来说,我只使用本地import来打破依赖周期。
你可能想看看在Python的维基导入语句的开销 。 简而言之:如果模块已经被加载(查看sys.modules
),你的代码将会运行sys.modules
慢。 如果你的模块还没有加载,而且foo
只会在需要的时候加载,可以是零次,那么整体性能会更好。
我相信在某些情况下,这是一个推荐的方法。 例如,在Google App Engine中,推荐使用延迟加载大模块,因为它将最小化实例化新的Python VM /解释器的预热成本。 看一下Google工程师的介绍。 但是请记住,这并不意味着你应该懒加载所有的模块。
安全实施
考虑一个所有Python代码都位于特权用户有权访问的文件夹中的环境。 为了避免将整个程序作为特权用户运行,您决定在执行过程中将权限下放给非特权用户。 只要您使用导入另一个模块的函数,程序就会抛出一个ImportError
因为非特权用户由于文件权限而无法导入模块。