147

PEP 8说:

  • 导入总是放在文件的顶部,就在任何模块注释和文档字符串之后,模块全局变量和常量之前。

有时,我违反了 PEP 8。有时我在函数中导入东西。作为一般规则,如果有一个只在单个函数中使用的导入,我会这样做。

有什么意见吗?

编辑(我觉得导入函数的原因可能是个好主意):

主要原因:它可以使代码更清晰。

  • 在查看函数代码时,我可能会问自己:“什么是函数/类 xxx?” (函数内部使用了xxx)。如果我在模块的顶部有我的所有导入,我必须去那里看看 xxx 是什么。这在使用from m import xxx. 看到m.xxx函数可能会告诉我更多。取决于是什么m:它是众所周知的顶级模块/包(import m)吗?或者它是一个子模块/包(from a.b.c import m)?
  • 在某些情况下,在使用 xxx 的位置附近拥有额外信息(“什么是 xxx?”)可以使函数更易于理解。
4

10 回答 10

108

从长远来看,我认为您会喜欢将大部分导入放在文件顶部,这样您就可以通过需要导入的内容一眼看出您的模块有多复杂。

如果我要向现有文件添加新代码,我通常会在需要的地方进行导入,然后如果代码保留,我会将导入行移动到文件顶部,从而使事情变得更加永久。

还有一点,我更喜欢ImportError在运行任何代码之前获得异常——作为健全性检查,所以这是在顶部导入的另一个原因。

pyChecker用来检查未使用的模块。

于 2009-06-22T02:05:00.620 回答
54

在这方面我有两次违反 PEP 8:

  • 循环导入:模块 A 导入模块 B,但模块 B 中的某些内容需要模块 A(尽管这通常表明我需要重构模块以消除循环依赖)
  • 插入 pdb 断点:import pdb; pdb.set_trace()这很方便 b/c 我不想放在import pdb我可能要调试的每个模块的顶部,并且在删除断点时很容易记住删除导入。

在这两种情况之外,将所有内容放在顶部是一个好主意。它使依赖关系更清晰。

于 2009-06-21T14:53:55.940 回答
21

以下是我们使用的四个导入用例

  1. import(和from x import yimport x as y在顶部

  2. 进口选择。在顶部。

    import settings
    if setting.something:
        import this as foo
    else:
        import that as foo
    
  3. 有条件的导入。与 JSON、XML 库等一起使用。在顶部。

    try:
        import this as foo
    except ImportError:
        import that as foo
    
  4. 动态导入。到目前为止,我们只有一个这样的例子。

    import settings
    module_stuff = {}
    module= __import__( settings.some_module, module_stuff )
    x = module_stuff['x']
    

    请注意,这种动态导入不会引入代码,而是引入用 Python 编写的复杂数据结构。它有点像腌制的数据,除了我们是手动腌制的。

    这也或多或少位于模块的顶部


以下是我们为使代码更清晰所做的工作:

  • 保持模块简短。

  • 如果我将所有导入都放在模块的顶部,我必须去那里看看以确定名称是什么。如果模块很短,那很容易做到。

  • 在某些情况下,在使用名称的位置附近提供额外信息可以使函数更易于理解。如果模块很短,那很容易做到。

于 2009-06-21T15:20:52.283 回答
10

要记住的一件事:不必要的导入可能会导致性能问题。所以如果这是一个经常被调用的函数,你最好把导入放在顶部。当然,这一种优化,所以如果有一个有效的案例表明在函数内部导入比在文件顶部导入更清晰,那么在大多数情况下,这胜过性能。

如果您正在使用 IronPython,我被告知最好导入内部函数(因为在 IronPython 中编译代码可能很慢)。因此,您也许可以找到一种方法来导入内部函数。但除此之外,我认为与传统作斗争是不值得的。

作为一般规则,如果有一个只在单个函数中使用的导入,我会这样做。

我想说的另一点是,这可能是一个潜在的维护问题。如果您添加一个使用以前仅由一个函数使用的模块的函数会发生什么?你会记得将导入添加到文件顶部吗?还是您要扫描每个函数以查找导入?

FWIW,在某些情况下,在函数内部导入是有意义的。例如,如果要在 cx_Oracle 中设置语言,则需要在导入之前_设置一个 NLS LANG 环境变量。因此,您可能会看到如下代码:

import os

oracle = None

def InitializeOracle(lang):
    global oracle
    os.environ['NLS_LANG'] = lang
    import cx_Oracle
    oracle = cx_Oracle
于 2009-06-21T15:08:40.280 回答
6

对于自我测试的模块,我之前已经打破了这条规则。也就是说,它们通常只用于支持,但我为它们定义了一个 main ,这样如果你自己运行它们,你就可以测试它们的功能。在那种情况下,我有时会导入getopt并且cmd只是在 main 中,因为我希望阅读代码的人清楚这些模块与模块的正常操作无关,并且仅包含在测试中。

于 2009-06-21T15:10:58.747 回答
6

Have a look at the alternative approach that's used in sqlalchemy: dependency injection:

@util.dependencies("sqlalchemy.orm.query")
def merge_result(query, *args):
    #...
    query.Query(...)

Notice how the imported library is declared in a decorator, and passed as an argument to the function!

This approach makes the code cleaner, and also works 4.5 times faster than an import statement!

Benchmark: https://gist.github.com/kolypto/589e84fbcfb6312532658df2fabdb796

于 2019-03-26T14:14:40.963 回答
5

来自关于两次加载模块的问题- 为什么不两者都加载?

脚本顶部的导入将指示函数中的依赖关系和另一个导入,使该函数更具原子性,同时似乎不会导致任何性能劣势,因为连续导入很便宜。

于 2017-09-27T20:43:04.540 回答
4

还有另一种(可能是“角落”)情况,它可能对import内部很少使用的功能有益:缩短启动时间。

我曾经在一个小型物联网服务器上运行一个相当复杂的程序,接收来自串行线路的命令并执行操作,可能是非常复杂的操作。

import语句放在文件顶部意味着在服务器启动之前处理所有导入;由于import列表包括jinja2,lxmlsignxml其他“重量级”(并且 SoC 不是很强大),这意味着在第一条指令实际执行前几分钟。

OTOH 将大多数导入功能放在我能够在几秒钟内让服务器在串行线路上“活着”的功能中。当然,当实际需要这些模块时,我不得不付出代价(注意:这也可以通过import在空闲时间产生一个后台任务来缓解)。

于 2020-07-14T15:01:10.883 回答
3

只要它是import而不是from x import *,你应该把它们放在顶部。它只向全局命名空间添加一个名称,并且您坚持使用 PEP 8。另外,如果您以后在其他地方需要它,您不必移动任何东西。

这没什么大不了的,但由于几乎没有区别,我建议按照 PEP 8 所说的去做。

于 2009-06-21T14:44:43.713 回答
2

在既是“普通”模块又可以执行的模块中(即有一个if __name__ == '__main__':-section),我通常导入仅在执行主要部分中的模块时使用的模块。

例子:

def really_useful_function(data):
    ...


def main():
    from pathlib import Path
    from argparse import ArgumentParser
    from dataloader import load_data_from_directory

    parser = ArgumentParser()
    parser.add_argument('directory')
    args = parser.parse_args()
    data = load_data_from_directory(Path(args.directory))
    print(really_useful_function(data)


if __name__ == '__main__':
    main()
于 2019-09-13T06:50:53.973 回答