导入内部函数是pythonic吗?

cod*_*ape 112 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的额外信息("什么是xxx?")可以使函数更容易理解.

小智 77

从长远来看,我认为你会很高兴你的大部分导入都位于文件顶部,这样你就可以一目了然地看出你的模块需要导入的复杂程度.

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

另外一点,我更喜欢ImportError在运行任何代码之前获得异常 - 作为完整性检查,这是在顶部导入的另一个原因.

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

  • 感谢您提供 ImportError 提示,我从来没有想到过这一点! (3认同)

Ric*_*and 42

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

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

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

  • 我同意它使整个模块的依赖关系更加清晰.但我相信它可以使代码在功能级别上不那么明确,以便在顶部导入所有内容.当你查看函数的代码时,你可能会问自己:"什么是函数/类xxx?" (函数内部使用xxx).你必须查看文件的最顶层才能看到xxx的来源.从m import xxx使用时,这是一个更大的问题.看到m.xxx告诉你更多 - 至少是否毫无疑问m是什么. (7认同)

S.L*_*ott 19

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

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

  2. 选择进口.在顶部.

    import settings
    if setting.something:
        import this as foo
    else:
        import that as foo
    
    Run Code Online (Sandbox Code Playgroud)
  3. 有条件导入.与JSON,XML库等一起使用.在顶部.

    try:
        import this as foo
    except ImportError:
        import that as foo
    
    Run Code Online (Sandbox Code Playgroud)
  4. 动态导入.到目前为止,我们只有一个例子.

    import settings
    module_stuff = {}
    module= __import__( settings.some_module, module_stuff )
    x = module_stuff['x']
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,此动态导入不会引入代码,但会引入用Python编写的复杂数据结构.它有点像腌制的数据,除非我们手工腌制.

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


以下是我们如何使代码更清晰:

  • 保持模块简短.

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

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


Jas*_*ker 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
Run Code Online (Sandbox Code Playgroud)

  • 我同意你的维护问题.重构代码可能有点问题.如果我添加第二个函数,该函数使用以前仅由一个函数使用的模块 - 我将导入移动到顶部,或者我通过在第二个函数中导​​入模块来破坏我自己的一般规则. (2认同)
  • 我认为性能论证也可以反过来.导入模块可能非常耗时.在像超级计算机那样的分布式文件系统上,导入像numpy这样的大模块可能需要几秒钟.如果一个模块只需要一个很少使用的功能,那么导入该功能将大大加快常见情况. (2认同)

Dan*_*Lew 6

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


Ilj*_*Bek 5

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

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


kol*_*pto 5

看看 sqlalchemy 中使用的替代方法:依赖注入:

@util.dependencies("sqlalchemy.orm.query")
def merge_result(query, *args):
    #...
    query.Query(...)
Run Code Online (Sandbox Code Playgroud)

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

这种方法使代码更加简洁,并且比语句运行速度快 4.5 倍import

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


Zio*_*yte 5

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

我曾经遇到过这个问题,在小型物联网服务器上运行一个相当复杂的程序,该程序从串行线路接受命令并执行操作,可能是非常复杂的操作。

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

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