代码之家  ›  专栏  ›  技术社区  ›  codeape

导入内部函数是pythonic吗?

  •  94
  • codeape  · 技术社区  · 17 年前

    PEP 8 说:

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

    有什么意见吗?

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

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

    • 当查看函数的代码时,我可能会问自己:“什么是函数/类xxx?”(函数中使用了xxx)。如果我把所有的导入都放在模块的顶部,我必须去那里看看,以确定xxx是什么。使用时,这是一个更大的问题 from m import xxx . 看到 m.xxx m is:它是著名的顶级模块/包吗( import m )? 或者它是一个子模块/包( from a.b.c import m )?
    • 在某些情况下,在使用xxx的位置附近有额外的信息(“xxx是什么?”)可以使函数更容易理解。
    7 回复  |  直到 17 年前
        1
  •  102
  •   bariod Александр Степаненко    8 年前

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

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

    ImportError 在运行任何代码之前出现异常——作为一种健全性检查,这是在顶部导入的另一个原因。

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

        2
  •  51
  •   Rick Copeland    17 年前

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

        3
  •  20
  •   S.Lott    17 年前

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

    1. import (及 from x import y import 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编写的数据结构。这有点像是一段经过处理的数据

      这也是,或多或少,在一个模块的顶部


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

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

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

        4
  •  9
  •   Jason Baker    17 年前

    要记住一件事:不必要的导入可能会导致性能问题。因此,如果这是一个经常调用的函数,那么最好将导入放在顶部。当然是这个

    如果您正在使用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
    
        5
  •  6
  •   Dan Lew    17 年前

    我以前打破过这条规则,用于自我测试的模块。也就是说,它们通常只是用于支持,但我为它们定义了一个main,这样,如果您自己运行它们,就可以测试它们的功能。在这种情况下,我有时会导入 getopt cmd 主要是因为我想让阅读代码的人清楚地知道,这些模块与模块的正常操作无关,只是为了测试。

        6
  •  5
  •   IljaBek    8 年前

    来自于关于 loading the module twice -为什么不能两者兼而有之?

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

        7
  •  4
  •   kolypto    7 年前

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

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

    注意导入的库是如何在装饰器中声明和传递的 作为函数的参数 !

    快4.5倍 import 陈述

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

        8
  •  4
  •   ZioByte    6 年前

    还有另一个(可能是“角落”)案例,它可能有益于 import

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

    放置 文件顶部的语句意味着 全部的 在服务器启动前处理的导入;自从 进口 jinja2 , lxml , signxml 和其他“重量级”(SoC不是很强大)这意味着 分钟 在第一条指令实际执行之前。

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

        9
  •  3
  •   Javier    17 年前

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

    这没什么大不了的,但既然几乎没有区别,我建议按照PEP8的说法去做。

        10
  •  2
  •   codeape    6 年前

    在属于“正常”模块且可执行的模块中(即具有 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()