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 

这种方法的几个问题:

  • 打开文件所依赖的模块并不明显。
  • 它会混淆必须分析依赖项的程序,比如py2exepy2app等。
  • 那么你在许多function中使用的模块呢? 你最终会导致大量的冗余导入,或者你必须在文件顶部和一些内部函数中有一些。

所以…首选的方法是把所有的import放在文件的顶部。 我发现如果我的import很难跟踪,通常意味着我有太多的代码,我最好把它分成两个或多个文件。

有些情况下,我发现函数内的导入是有用的:

  • 为了处理循环依赖(如果你真的无法避免它们)
  • 平台特定的代码

另外:在每个函数中放置导入实际上并不比在文件顶部慢得多。 每个模块第一次被加载它被放入sys.modules ,每个后续的导入只花费时间来查找模块,这是相当快(它不重新加载)。

另一个值得注意的事情是,函数内的from module import *语法已经在Python 3.0中被移除了。

这里有一个简短的提到它在“Removed Syntax”

http://docs.python.org/3.0/whatsnew/3.0.html

我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 urlliburll[Esc]将展开到urllib ,如果不是,我跳转到文件的开始并添加导入。

从性能的angular度来看,你可以看到这样的情况: Python导入语句应该总是在模块的顶部?

一般来说,我只使用本地import来打破依赖周期。

你可能想看看在Python的维基导入语句的开销 。 简而言之:如果模块已经被加载(查看sys.modules ),你的代码将会运行sys.modules慢。 如果你的模块还没有加载,而且foo只会在需要的时候加载,可以是零次,那么整体性能会更好。

我相信在某些情况下,这是一个推荐的方法。 例如,在Google App Engine中,推荐使用延迟加载大模块,因为它将最小化实例化新的Python VM /解释器的预热成本。 看一下Google工程师的介绍。 但是请记住,这并不意味着你应该懒加载所有的模块。

安全实施

考虑一个所有Python代码都位于特权用户有权访问的文件夹中的环境。 为了避免将整个程序作为特权用户运行,您决定在执行过程中将权限下放给非特权用户。 只要您使用导入另一个模块的函数,程序就会抛出一个ImportError因为非特权用户由于文件权限而无法导入模块。