关于python:导入内部函数是pythonic吗?

Is it pythonic to import inside functions?

PEP 8表示:

  • Imports are always put at the top of the file, just after any module
    comments and docstrings, and before module globals and constants.

在发生阻塞时,我违反了PEP8。有时我会在函数中导入一些东西。作为一般规则,如果有一个导入只在一个函数中使用,我会这样做。

有什么意见吗?

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

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

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


从长远来看,我认为您会很感激将大部分导入内容放在文件的顶部,这样您就可以一目了然地知道模块需要导入的内容有多复杂。

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

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

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


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

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

除了这两种情况外,最好把所有东西都放在最上面。它使依赖关系更加清晰。


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

  • 顶部为import(和from x import yimport x as y)

  • 导入选项。在顶部。

    1
    2
    3
    4
    5
    import settings
    if setting.something:
        import this as foo
    else:
        import that as foo
  • 条件导入。用于JSON、XML库等。在顶部。

    1
    2
    3
    4
    try:
        import this as foo
    except ImportError:
        import that as foo
  • 动态导入。到目前为止,我们只有一个例子。

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

    注意,这个动态导入不会带来代码,但会带来复杂的用python编写的数据结构。这有点像腌渍的数据除了我们用手腌制它。

    这或多或少也在模块的顶部。

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

    • 保持模块短。

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

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


    需要记住的一点是:不必要的导入可能会导致性能问题。因此,如果这是一个经常调用的函数,那么最好将导入放在顶部。当然,这是一种优化,因此,如果有一个有效的案例需要说明,在函数内部导入比在文件顶部导入更为清晰,那么在大多数情况下,这就胜过了性能。

    如果您正在执行Ironpython,我被告知最好在函数内部导入(因为在Ironpython中编译代码可能很慢)。因此,您可以获得一种方法来导入内部函数。但除此之外,我认为这不值得和传统抗争。

    As a general rule, I do this if there is an import that is only used within a single function.

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

    fwiw,在某些情况下导入函数是有意义的。例如,如果要在cx-oracle中设置语言,则需要在导入前设置一个nls_lang环境变量。因此,您可以看到这样的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import os

    oracle = None

    def InitializeOracle(lang):
        global oracle
        os.environ['NLS_LANG'] = lang
        import cx_Oracle
        oracle = cx_Oracle


    对于自我测试的模块,我以前已经打破了这个规则。也就是说,它们通常只是用于支持,但我为它们定义了一个主函数,这样,如果您自己运行它们,就可以测试它们的功能。在这种情况下,我有时会直接导入getoptcmd,因为我想让阅读代码的人清楚地知道,这些模块与模块的正常运行没有任何关系,只包含在测试中。


    这是关于两次加载模块的问题——为什么不都加载呢?

    脚本顶部的导入将指示依赖项和函数中的另一个导入,使该函数更原子化,但似乎不会造成任何性能缺点,因为连续导入成本较低。


    只要是import而不是from x import *,你就应该把它们放在最上面。它只在全局名称空间中添加一个名称,而您坚持使用PEP8。另外,如果你以后需要它在其他地方,你不必移动任何东西。

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


    请看一下SQLAlchemy中使用的替代方法:依赖注入:

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

    请注意,导入的库是如何在decorator中声明的,并作为参数传递给函数的!

    这种方法使代码更干净,而且比import语句快4.5倍!

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