导入内部函数是pythonic吗?

时间:2009-06-21 14:41:00

标签: python conventions

PEP 8说:

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

在发生时,我违反了PEP 8.有时我会在函数中导入内容。作为一般规则,如果导入仅在单个函数中使用,则执行此操作。

有什么意见吗?

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

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

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

10 个答案:

答案 0 :(得分:68)

从长远来看,我认为你会很高兴你的大部分导入都位于文件的顶部,这样你就可以一眼就看出你的模块有多复杂,需要导入它。

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

还有一点,我希望在运行任何代码之前获得ImportError异常 - 作为完整性检查,这是在顶部导入的另一个原因。

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

答案 1 :(得分:36)

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

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

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

答案 2 :(得分:18)

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

  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编写的数据结构。这有点像腌制的数据 除了我们手工腌制。

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


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

    • 保持模块简短。

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

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

答案 3 :(得分:7)

要记住一件事:不必要的进口会导致性能问题。因此,如果这是一个经常调用的函数,那么最好只将导入放在顶部。当然这个一个优化,所以如果有一个有效的案例要求在函数内部导入比在文件顶部导入更明确,那么在大多数情况下这会超过性能。

如果你正在做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

答案 4 :(得分:6)

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

答案 5 :(得分:3)

来自loading the module twice的问题 - 为什么不同时出现?

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

答案 6 :(得分:2)

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

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

答案 7 :(得分:2)

还有另一种(可能是“拐角”)情况,在很少使用的函数中使用import可能会有所帮助:缩短启动时间。

我曾经在小型IoT服务器上运行一个相当复杂的程序,从串口线接受命令并执行操作(可能是非常复杂的操作),就碰壁了。

在文件顶部放置import语句是为了在服务器启动之前处理所有所有导入;由于import列表中包含jinja2lxmlsignxml和其他“重量级”(而SoC并不是很强大),因此这意味着分钟第一条指令实际上已执行。

OTOH将大多数导入置于函数中,我能够在几秒钟内使服务器在串行线上“运行”。当然,当实际需要这些模块时,我必须付出代价(注意:这也可以通过在空闲时间执行import秒的后台任务来减轻)。

答案 8 :(得分:1)

看看sqlalchemy中使用的另一种方法:依赖注入:

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

请注意如何在装饰器中声明导入的库,并将作为参数传递给函数

这种方法可以使代码更干净,并且比import语句还快 4.5倍

基准:https://gist.github.com/kolypto/589e84fbcfb6312532658df2fabdb796

答案 9 :(得分:0)

在既是“正常”模块又可以执行(即具有if __name__ == '__main__':部分)的模块中,我通常会导入仅在执行主体部分中的模块时使用的模块。

示例:

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()