![]() |
1
104
(先前的) top-voted answer 这个问题的格式很好,但在性能方面绝对错误。让我示范一下 性能最高进口
导入函数体
如你所见,它可以
更多
在函数中有效地导入模块。原因很简单。它将引用从全局引用移动到本地引用。这意味着,至少对于cpython,编译器将发出
一般来说,最好在顶部导入,但性能是 不 如果您经常访问模块的原因。原因是可以更容易地跟踪模块所依赖的内容,并且这样做与python世界的大多数其他部分是一致的。 |
![]() |
2
53
这确实有一些缺点。 测试如果您希望通过运行时修改来测试模块,那么这可能会使测试变得更加困难。而不是做
你必须这么做
这意味着您必须全局地修补othermodule,而不是仅仅更改mymodule中的引用所指向的内容。 依赖项跟踪这使得您的模块所依赖的模块不明显。如果您使用许多第三方库或正在重新组织代码,这尤其令人恼火。 我不得不维护一些遗留代码,在所有地方都使用内联导入,这使得代码极难重构或重新打包。 业绩说明由于python缓存模块的方式,性能没有受到影响。事实上,由于模块位于本地命名空间中,因此在函数中导入模块对性能有轻微的好处。 最高进口
导入函数体
|
![]() |
3
19
这种方法有几个问题:
所以…首选方法是将所有导入放在文件的顶部。我发现,如果很难跟踪我的导入,通常意味着我有太多的代码,最好将其拆分为两个或更多文件。 有些情况下我 有 在函数内部找到了有用的导入:
另外:将导入放到每个函数中实际上是
不
明显比文件顶部慢。第一次加载每个模块时
|
![]() |
4
10
另一个值得注意的是
在“删除的语法”下面有一个简短的介绍: |
![]() |
5
4
我建议你尽量避免
在所有其他地方,在导入包的地方,只需使用
如果
|
![]() |
6
3
人们已经很好地解释了为什么要避免内联导入,但并没有真正的替代工作流来解决您首先需要它们的原因。
检查我使用的未使用的导入 pylint . 它对python代码进行静态(ish)分析,它检查的(许多)事情之一是未使用的导入。例如,下面的脚本..
…将生成以下消息:
至于检查可用的导入,我通常依赖textmate(相当简单)的完成-当您按esc键时,它将与文档中的其他单词一起完成当前单词。如果我做了
|
![]() |
7
2
从性能的角度来看,您可以看到: Should Python import statements always be at the top of a module? 一般来说,我只使用本地导入来中断依赖循环。 |
![]() |
8
2
你可能想看看进口
statement overhead
在python wiki中。简而言之:如果模块已经加载(请看
|
![]() |
9
2
我相信在某些情况下,这是一种推荐的方法。例如,在google app engine中,建议延迟加载大模块,因为这将最小化实例化新python vms/解释器的预热成本。看一看 Google Engineer's 描述这一点的陈述。但是请记住 不 意味着你应该延迟加载所有模块。 |
![]() |
10
0
两种变体都有其用途。不过,在大多数情况下,最好在函数外部导入,而不是在函数内部导入。 性能有好几个答案都提到了这一点,但在我看来,它们都缺乏一个完整的讨论。 第一次在python解释器中导入模块时,无论是在顶层还是在函数内部,都会很慢。这很慢,因为python(我关注的是cpython,对于其他python实现可能有所不同)执行了多个步骤:
后续导入不必执行所有这些操作,因为python可以简单地从
可能模块中的函数实际上并不经常使用,但它取决于
但是
几乎慢了两倍。 意识到这点很重要 aaronasterling "cheated" a bit in the answer . 他说在函数中进行导入实际上使函数更快。在某种程度上这是真的。这是因为python查找名称的方式:
因此,与其检查本地作用域,然后检查全局作用域,不如检查本地作用域,因为模块的名称在本地作用域中可用。这实际上使它更快!但这是一种叫做
"Loop-invariant code motion"
. 这基本上意味着,通过在循环(或重复调用)之前将其存储在变量中,可以减少在循环(或重复调用)中执行的操作的开销。所以不是
虽然您可以清楚地看到重复查找全局
这也可以通过避免循环内的函数查找而达到极端:
再次快得多,但是导入和变量之间几乎没有区别。 可选依赖项
有时模块级导入实际上可能是个问题。例如,如果您不想添加另一个安装时依赖项,但该模块对某些
额外的
功能。决定依赖项是否应该是可选的不应该很容易,因为它会影响用户(如果他们得到一个意外的
但这同样可以通过两种方式来实现:
或:
这可以通过提供替代实现或定制用户看到的异常(或消息)来进行更多的定制,但这是主要的要点。 如果希望为可选依赖项提供另一种“解决方案”,顶级方法可能会更好一些,但是通常人们使用in函数导入。主要是因为它导致一个更干净的堆栈跟踪和更短。 循环进口函数内导入对于避免循环导入导致的导入非常有用。在许多情况下,循环导入是“坏”包结构的标志,但如果绝对无法避免循环导入,则通过将导致循环的导入放入实际使用它的函数中来解决“循环”(从而解决问题)。 别再说了如果实际将所有导入放在函数中而不是模块作用域中,则会引入冗余,因为函数很可能需要相同的导入。这有几个缺点:
其他想法:
大多数ide已经有了一个未使用导入的检查器,所以可能只需单击几下就可以删除它们。即使不使用IDE,也可以偶尔使用静态代码检查器脚本并手动修复它。另一个答案提到了pylint,但也有其他答案(例如pyflakes)。
这就是为什么你通常使用
另外,如果您认为您污染了模块名称空间太多,那么您可能应该考虑将模块拆分为子模块,但是这只对几十个导入有意义。
如果您想减少名称空间污染,还有一点(非常重要)要提,那就是避免
最后,您可以始终使用别名,以避免使用“public”导入污染命名空间,方法是:
总结
|
![]() |
11
-1
安全实现
考虑这样一个环境:所有python代码都位于一个只有特权用户才能访问的文件夹中。为了避免以特权用户的身份运行整个程序,您决定在执行期间将特权删除给非特权用户。一旦使用导入另一个模块的函数,程序将抛出
|