在函数内部导入是符合 Python 风格的吗?

2025-04-16 08:56:00
admin
原创
16
摘要:问题描述:PEP 8说:导入总是放在文件的顶部,紧接着任何模块注释和文档字符串之后,以及模块全局变量和常量之前。有时候,我会违反 PEP 8。有时我会在函数内部导入一些东西。一般来说,如果导入的内容只在一个函数中使用,我就会这样做。有什么意见吗?编辑(我觉得导入函数是个好主意的原因):主要原因:可以使代码更加...

问题描述:

PEP 8说:

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

有时候,我会违反 PEP 8。有时我会在函数内部导入一些东西。一般来说,如果导入的内容只在一个函数中使用,我就会这样做。

有什么意见吗?

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

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

  • 在查看函数代码时,我可能会问自己:“xxx 是什么函数/类?”(xxx 在函数内部使用)。如果我所有的导入都在模块顶部,我必须去那里查看才能确定 xxx 是什么。这在使用 时尤其成问题。在函数中from m import xxx查看可能会告诉我更多信息。这取决于是什么:它是一个众所周知的顶级模块/包()吗?还是一个子模块/包()?m.xxx`mimport mfrom a.b.c import m`

  • 在某些情况下,在使用 xxx 的位置附近添加额外的信息(“xxx 是什么?”)可以使该功能更容易理解。


解决方案 1:

从长远来看,我认为您会喜欢将大多数导入放在文件顶部,这样您就可以一眼看出模块需要导入的内容有多复杂。

如果我要向现有文件添加新代码,我通常会在需要的地方进行导入,然后如果代码保留,我会通过将导入行移动到文件顶部来使其更加永久。

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

您可以使用 linter 来检查未使用的模块。

解决方案 2:

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

  • 循环导入:模块 A 导入模块 B,但模块 B 中的某些内容需要模块 A(尽管这通常表明我需要重构模块以消除循环依赖)

  • 插入 pdb 断点:import pdb; pdb.set_trace()这很方便,因为我不想把它放在import pdb我可能想要调试的每个模块的顶部,并且当我删除断点时很容易记住删除导入。

除了这两种情况之外,最好把所有内容都放在顶部。这样可以使依赖关系更清晰。

解决方案 3:

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

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

  2. 导入选项。位于顶部。

import settings
if setting.something:
    import this as foo
else:
    import that as foo
  1. 条件导入。与 JSON、XML 库等一起使用。位于顶部。

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

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

请注意,这种动态导入不会引入代码,而是引入用 Python 编写的复杂数据结构。它有点像腌制过的数据,只不过是我们手动腌制的。

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


为了使代码更清晰,我们做了以下工作:

  • 保持模块简短。

  • 如果我把所有导入的代码都放在模块顶部,我就必须去那里查看它的名称。如果模块很短,这很容易做到。

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

解决方案 4:

需要注意的是:不必要的导入会导致性能问题。所以,如果这是一个会被频繁调用的函数,最好将导入放在文件顶部。当然,这一种优化,所以如果有充分的理由证明在函数内部导入比在文件顶部导入更清晰,那么在大多数情况下,这样做会更有利于提高性能。

如果你正在使用 IronPython,我听说最好在函数内部导入(因为在 IronPython 中编译代码可能很慢)。因此,你或许可以找到一种方法来导入函数内部。但除此之外,我认为不值得为了迎合惯例而这样做。

一般来说,如果导入仅在单个函数中使用,我就会这样做。

我想指出的另一点是,这可能是一个潜在的维护问题。如果你添加一个函数,它使用的模块之前只有一个函数使用,会发生什么?你会记得在文件顶部添加导入吗?还是要扫描每个函数来查找导入?

顺便说一下,在某些情况下,在函数内部导入是有意义的。例如,如果您想在 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:

对于自测试模块,我以前就违反过这条规则。也就是说,它们通常只是用来支持,但我为它们定义了一个主函数,这样当你单独运行它们时,就可以测试它们的功能。在这种情况下,我有时会导入getoptcmd直接运行主函数,因为我希望阅读代码的人清楚地知道,这些模块与模块的正常运行无关,只是为了测试而引入的。

解决方案 6:

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

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

解决方案 7:

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

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

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

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

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

解决方案 8:

还有另一种(可能是“极端”)情况,其中import内部很少使用的功能可能会有益:缩短启动时间。

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

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

另一方面,通过将大多数导入放在函数中,我能够在几秒钟内让服务器在串行线路上“活跃”。当然,当真正需要这些模块时,我不得不付出代价(注:也可以通过import在空闲时间运行后台任务来缓解这种情况)。

解决方案 9:

只要是import且不是from x import *,就应该把它们放在最上面。这样只会向全局命名空间添加一个名称,并且遵循 PEP 8 规范。另外,如果以后在其他地方需要它,也无需移动任何内容。

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

解决方案 10:

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

例子:

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()
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用