【问题标题】:Good or bad practice in Python: import in the middle of a file [duplicate]Python中的好习惯或坏习惯:在文件中间导入[重复]
【发布时间】:2010-11-14 09:21:12
【问题描述】:

假设我有一个比较长的模块,但只需要一个外部模块或方法一次。

在模块中间导入该方法或模块是否认为可以?

或者imports 应该只在模块的第一部分。

例子:

import string, pythis, pythat
...
...
...
...
def func():
     blah
     blah 
     blah
     from pysomething import foo
     foo()
     etc
     etc 
     etc
...
...
...

请证明您的回答并添加指向PEPs 或相关来源的链接

【问题讨论】:

    标签: python python-import


    【解决方案1】:

    PEP 8权威声明:

    导入总是放在顶部 文件,就在任何模块之后 cmets 和 docstrings,以及模块全局变量和常量之前。

    PEP 8 应该是任何“内部”风格指南的基础,因为它总结了核心 Python 团队发现的最有效的风格,总体而言(当然,与任何其他语言一样,也有个人异议,但共识和 BDFL 就 PEP 8 达成一致。

    【讨论】:

    • Alex:这是“最佳实践”,但并不总是可以避免的。请参阅我上面给出的示例。
    • @jkp,S.Lott 评论了您的答案,并说明了为什么在该示例中可以避免。我能想到的唯一示例是使用动态生成的模块名称,因此 __import__,而不是普通导入——在 那些 的情况下,在您知道模块名称之前,您无法导入重新导入,但你没有在上面使用普通的import。
    • 我经常沉迷的例外是在“if name == 'main':”条件下,模块需要导入sys或 argparse 以执行 i/o iff 它作为脚本调用,否则没有任何理由导入这些模块。
    • 成熟项目与PEP相矛盾的那一刻怎么称呼?请参阅docs.djangoproject.com/en/1.8/topics/signals/… 上的“这段代码应该在哪里”
    • 当然有很多例外,因为PEP8不是神给的。我能想到的第一个是避免循环导入。当然,任何人都可以通过一些努力使任何东西符合任何规则。我的意思是,这些准则是用来提供帮助还是用来服务的。
    【解决方案2】:

    在 2001 年的 Python 邮件列表上有关于这个话题的详细讨论:

    https://mail.python.org/pipermail/python-list/2001-July/071567.html

    以下是该线程中讨论的一些原因。来自 Peter Hansen,以下是文件顶部不要全部导入的三个原因:

    在函数中导入的可能原因:

    1. 可读性:如果只需要导入一个 功能,这不太可能改变, 只放在那里可能会更清晰。

    2. 启动时间:如果您没有外部导入 的函数定义,它不会执行 当您的模块第一次被另一个导入时,但是 仅当调用其中一个函数时。这 延迟导入的开销(或避免它 如果函数可能永远不会被调用)。

    3. 总有一个原因比那些 到现在为止我们一直在考虑。

    范罗苏姆加入了第四个:

    1. 开销:如果模块导入了很多模块, 而且很有可能实际上只有少数人会 使用。这类似于“启动时间” 原因,但更进一步。如果一个脚本 使用您的模块仅使用一小部分 功能它可以节省相当多的时间,尤其是 如果可以避免的进口也进口很多 模块。

    提供了五分之一,因为本地进口是避免循环进口问题的一种方式。

    请随意阅读该主题以进行完整讨论。

    【讨论】:

    【解决方案3】:

    其他人都已经提到了 PEP,但也要注意不要在关键代码中间有 import 语句。至少在 Python 2.6 下,当函数有 import 语句时,还需要更多的字节码指令。

    >>> def f():
        from time import time
        print time()
    
    >>> dis.dis(f)
      2           0 LOAD_CONST               1 (-1)
                  3 LOAD_CONST               2 (('time',))
                  6 IMPORT_NAME              0 (time)
                  9 IMPORT_FROM              0 (time)
                 12 STORE_FAST               0 (time)
                 15 POP_TOP             
    
      3          16 LOAD_FAST                0 (time)
                 19 CALL_FUNCTION            0
                 22 PRINT_ITEM          
                 23 PRINT_NEWLINE       
                 24 LOAD_CONST               0 (None)
                 27 RETURN_VALUE
    
    >>> def g():
        print time()
    
    >>> dis.dis(g)
      2           0 LOAD_GLOBAL              0 (time)
                  3 CALL_FUNCTION            0
                  6 PRINT_ITEM          
                  7 PRINT_NEWLINE       
                  8 LOAD_CONST               0 (None)
                 11 RETURN_VALUE  
    

    【讨论】:

    • 谢谢你,我一直在寻找“内部真正发生的事情”之类的答案。
    【解决方案4】:

    如果导入的模块不经常使用且导入成本高,则中间导入即可。

    否则,听从 Alex Martelli 的建议是否明智。

    【讨论】:

      【解决方案5】:

      这通常被认为是不好的做法,但有时它是不可避免的(比如当您必须避免循环导入时)。

      必要时的一个例子:我使用Waf 来构建我们所有的代码。系统分为工具,每个工具都在自己的模块中实现。每个工具模块都可以实现detect() 方法来检测是否存在先决条件。其中之一的示例可能会执行以下操作:

      def detect(self):
          import foobar
      

      如果一切正常,则该工具可用。然后稍后在同一模块中可能需要foobar 模块,因此您必须在函数级别范围内再次导入它。显然,如果它是在模块级别导入的,事情就会彻底崩溃。

      【讨论】:

      • def 中的导入当然是可以避免的。我们使用try: import x; except ImportError 逻辑块将所有导入保持在前面。
      • 我特别 +1 了关于循环导入的部分,尽管我同意其他人关于 PEP8 的回答。当然,循环导入可以而且应该被修复,但这可能是毫无价值的努力。因此,为了为您的时间带来价值,如果 A) 避免内联导入所需的更改对某些实时服务具有很高的风险,并且|或 B) 修复循环导入对最终用户(包括从长远来看重新:维护)。也许,还有 C)因为它需要太长时间并且价值可疑。
      • @S.Lott 那么如何处理导入异常?
      【解决方案6】:

      在文件开头将所有导入组合在一起被认为是“良好的形式”。

      模块可以导入其他模块。将所有导入语句放在模块(或脚本,就此而言)的开头是习惯但不是必需的。导入的模块名称放在导入模块的全局符号表中。

      从这里:http://docs.python.org/tutorial/modules.html

      【讨论】:

        【解决方案7】:

        95% 的情况下,您应该将所有导入文件放在文件的顶部。您可能想要进行函数本地导入的一种情况是,如果您必须这样做以避免循环导入。说 foo.py 导入 bar.py,bar.py 中的一个函数需要从 foo.py 中导入一些东西。如果您将所有导入都放在顶部,则在导入依赖于尚未编译的信息的文件时可能会遇到意外问题。在这种情况下,拥有一个函数本地导入可以让您的代码推迟导入另一个模块,直到其代码完全编译,然后您调用相关函数。

        但是,您的用例似乎更多的是要明确 foo() 的来源。在这种情况下,我更喜欢以下两件事之一:

        首先,而不是

        from prerequisite import foo
        

        直接导入先决条件,稍后将其称为prerequisite.foo。增加的冗长通过提高代码透明度得到了回报。

        或者,(或结合上述)如果您的导入和使用它的地方之间的距离真的很长,那么可能是您的模块太大了。需要一个没有其他用途的导入可能表明您的代码可以被重构为更易于管理的块。

        【讨论】:

        • 循环可以通过分解来修复。有条件的导入充其量是一种笨拙的解决方法。我会说这是 99.7% 的时间,而 0.3% 是“直到你可以分解以修复循环”。
        【解决方案8】:

        PEP8:

        导入总是放在顶部 文件,就在任何模块之后 cmets 和 docstrings,以及模块全局变量和常量之前。

        有范围内的导入是不错的做法。这样导入仅适用于您使用它的功能。

        我认为,如果导入在块的顶部组合在一起,或者如果您希望它全局位于文件的顶部,我认为代码会更具可读性。

        【讨论】:

          【解决方案9】:

          好吧,我认为在文件开头将所有导入组合在一起是一个好习惯,因为如果想知道加载了哪些库,每个人都知道在哪里查找

          【讨论】:

            猜你喜欢
            • 2011-11-04
            • 2010-10-16
            • 2018-05-23
            • 1970-01-01
            • 1970-01-01
            • 2013-07-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多