类/函数定义中的导入语句 - 这是个好主意吗?

Posted

技术标签:

【中文标题】类/函数定义中的导入语句 - 这是个好主意吗?【英文标题】:Import statement inside class/function definition - is it a good idea? 【发布时间】:2011-07-12 20:31:18 【问题描述】:

我创建了一个名为 util 的模块,它提供了我在 Python 中经常使用的类和函数。 其中一些需要导入的功能。在类/函数定义中导入需要的东西有什么优缺点?是否比模块文件开头的import 更好?这是个好主意吗?

【问题讨论】:

Good or bad practice in Python: import in the middle of a file 的可能重复项 Is it pythonic to import inside functions?的可能重复 【参考方案1】:

每个导入放在文件顶部是最常见的样式。 PEP 8 推荐它,这是开始它的一个很好的理由。但这不是一时兴起,它具有优势(尽管还不足以使其他一切都成为犯罪)。它允许一目了然地找到所有导入,而不是查看整个文件。它还确保在执行任何其他代码(可能取决于某些导入)之前导入所有内容。 NameErrors 通常很容易解决,但它们可能很烦人。

通过将模块保持在较小的范围内可以避免(显着)命名空间污染,因为您添加的只是实际模块 (no, import * doesn't count and probably shouldn't be used anyway)。在函数内部,您会在每次调用时再次导入(并不是真正有害,因为所有内容都导入一次,但不需要)。

【讨论】:

我认为最好的理由是:如果你导入文件,你想在导入时检查你是否有所有正确的包,而不是在你将使用某个功能的时候。当然可能是你不需要所有的包(我想这是缺点)【参考方案2】:

PEP8,Python 风格指南指出:

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

当然,这不是硬性规定,进口可以去任何你想去的地方。但是将它们放在顶部是最好的方法。您当然可以在函数或类中导入。

但请注意,您不能这样做:

def foo():
    from os import *

因为:

SyntaxWarning: import * only allowed at module level

【讨论】:

当我将 import 语句放在模块的开头,然后在不同的文件中使用这个模块并在其中调用 dir(util) 时,我看到的不仅是我在 @ 中定义的函数/类的名称987654325@ 还有imports 在那里制作的。好吃吗? 是的,因为import 执行加载的源文件中的所有语句。所以让我们假设有一个模块X 导入Y。现在当我们在另一个模块中导入X 时,Y 也被导入了。 我只是认为它违反了封装,因为任何人都可以访问实现(获取有关我使用的模块的信息)。 这个答案信息量不大——它没有说明为什么,这才是最重要的。 假设我编写了一个包含 3 个类的模块,但其中只有一个需要 os.path.exists()。因此,根据 PEP8,我的模块必须执行 from os import path,即使在整个模块中除了一行之外没有其他任何东西使用它?【参考方案3】:

就像飞羊的回答一样,我同意其他人是对的,但是我在开发代码时将导入放在其他地方,例如__init__() 例程和函数调用中。在我的类或函数经过测试并证明可以使用其中的导入后,我通常会按照 PEP8 指南为它提供自己的模块,并使用导入。我这样做是因为有时我在重构代码或删除带有坏主意的旧代码后忘记删除导入。通过将导入保留在正在开发的类或函数中,如果我想将其复制到其他地方或将其提升到自己的模块中,我指定了它的依赖项......

【讨论】:

【参考方案4】:

我认为最好的做法是(根据一些 PEP),将 import 语句放在模块的开头。您可以将 import 语句添加到 __init__.py 文件中,该文件会将这些模块导入到包内的所有模块中。

所以...这当然是您可以按照自己的方式做的事情,但不鼓励这样做,实际上是不必要的。

【讨论】:

【参考方案5】:

虽然其他答案大多是正确的,但 python 允许这样做是有原因的。

导入不需要的多余内容是不明智的。所以,如果你想例如将 XML 解析为元素树,但不想使用缓慢的内置 XML 解析器,如果 lxml 可用,则需要在需要调用解析器时检查这一点。

而不是一开始就记住 lxml 的可用性,我宁愿 try 导入和使用 lxmlexcept 它不存在,在这种情况下我会回退到内置的 xml 模块.

【讨论】:

【参考方案6】:

如果需要解决诸如避免循环导入或试图减少模块的初始化时间等问题,请仅将导入移动到本地范围内,例如在函数定义中。如果根据程序的执行方式,许多导入是不必要的,则此技术特别有用。如果模块仅在该函数中使用,您可能还希望将导入移动到该函数中。请注意,第一次加载模块可能会很昂贵,因为模块需要一次初始化,但多次加载模块实际上是免费的,只需要几次字典查找。即使模块名称超出范围,该模块也可能在 sys.modules 中可用。

https://docs.python.org/3/faq/programming.html#what-are-the-best-practices-for-using-import-in-a-module

【讨论】:

以上是关于类/函数定义中的导入语句 - 这是个好主意吗?的主要内容,如果未能解决你的问题,请参考以下文章

在 debounce 函数中使用 requestAnimationFrame 是个好主意吗?

导入内部函数是pythonic吗?

使用带有 fallthrough 的开关来处理 Javascript 中的默认参数是个好主意吗?

为程序使用不同的 Python 脚本是个好主意吗? [关闭]

软删除是个好主意吗? [复制]

命名空间中的函数重载(与具有静态成员的类相比)是一个坏主意吗?