具有相同名称的 Python 模块(即,在包中重用标准模块名称)
Posted
技术标签:
【中文标题】具有相同名称的 Python 模块(即,在包中重用标准模块名称)【英文标题】:Python modules with identical names (i.e., reusing standard module names in packages) 【发布时间】:2012-05-17 02:32:00 【问题描述】:假设我有一个包含模块的包:
SWS/
__init.py__
foo.py
bar.py
time.py
并且模块需要引用彼此包含的函数。我的time.py
模块似乎遇到了问题,因为有一个同名的标准模块。
例如,如果我的 foo.py
模块同时需要我的 SWS.time
和标准 python time
模块,我会遇到麻烦,因为解释器会查看包内部并在之前找到我的 time.py
模块它遇到了标准的time
模块。
有没有办法解决这个问题?这是禁止的情况吗?不应该重复使用模块名称吗?
任何关于包装理念的解决方案和意见都会在这里有用。
【问题讨论】:
我认为很明显你不应该重用标准的 python 模块名称。这只是自找麻烦。 为什么很明显? 查看 httplib/httplib2 和 urllib/urllib2。它使图书馆的世界变得更丑陋,但这比命名冲突和不确定的行为更可取。 当您试图避免名称与关键字冲突时,可能还值得补充一点,python style guide 建议使用class_
而不是 class
(尾随下划线)。在这里可能合适,并使用import time_ as time
。
大约 14 年前,我制作了一个名为 wave 的模块。试了很久,还是不行。事实证明,同时添加了该名称的标准模块。是的,我知道,我一定是老了……(而且我不是明眼人)。很明显:不要太快判断。
【参考方案1】:
重用标准函数/类/模块/包的名称绝不是一个好主意。尽量避免它。但是,您的情况有一些干净的解决方法。
您看到的行为是导入 SWS.time
而不是 stdlib time
,这是由于古代 python 版本 (2.x) 中 import
的语义。要修复它,请添加:
from __future__ import absolute_import
在文件的最顶部。这会将import
的语义更改为python3.x 的语义,这更加明智。在这种情况下,声明:
import time
只会引用***模块。因此,解释器在包内执行该导入时不会考虑您的 SWS.time
模块,但它只会使用标准库之一。
如果一个模块在你的包需要导入SWS.time
你可以选择:
使用显式相对导入:
from . import time
使用绝对导入:
import SWS.time as time
所以,您的 foo.py
将类似于:
from __future__ import absolute_import
import time
from . import time as SWS_time
【讨论】:
我认为当模块名称存在于命名空间中时,就像一个包,这不是一个坏主意。这就是PEP328的精神。请参阅下面的答案。【参考方案2】:这取决于您使用的 Python 版本。如果您的目标 Python 版本是 2.4 或更早版本(2015 年,我当然希望不是),那么这是不好的做法,因为没有办法(没有黑客)区分这两个模块。
但是,在 Python 2.5+ 中,我认为在包命名空间中重用标准 lib 模块名称 非常好;其实就是the spirit of PEP328。
随着 Python 库的扩展,越来越多的现有包内部模块突然意外地隐藏了标准库模块。这是包内部的一个特别困难的问题,因为无法指定是哪个模块。为了解决歧义,建议 foo 始终是可从 sys.path 访问的模块或包。这称为绝对导入。
python-dev 社区选择绝对导入作为默认值,因为它们是更常见的用例,并且因为绝对导入可以提供相对(包内)导入的所有功能——尽管以重命名时的困难为代价在层次结构中较高的包块或将一个包移到另一个包中时。
因为这代表了语义上的变化,所以在 Python 2.5 和 2.6 中绝对导入将是可选的,通过使用
from __future__ import absolute_import
SWS.time
显然 不 与 time
相同,作为代码的读者,我希望 SWS.time
不仅使用 time
,而且将其扩展为某种方式。
所以,如果SWS.foo
需要导入SWS.time
,那么就应该使用绝对路径:
# in SWS.foo
# I would suggest renaming *within*
# modules that use SWS.time so that
# readers of your code aren't confused
# with which time module you're using
from SWS import time as sws_time
或者,它应该使用 explicit 相对导入,如 Bakuriu 的回答:
# in SWS.foo
from . import time as sws_time
如果您需要在SWS.time
模块中导入标准库time
模块,您首先需要导入未来的功能(仅适用于Python 2.5+;Python 3+ 默认情况下会这样做):
# inside of SWS.time
from __future__ import absolute_import
import time
time.sleep(28800) # time for bed
注意:from __future__ import absolute_imports
只会影响导入语句在未来功能被导入的模块中,并且不会影响任何其他模块(因为如果另一个模块依赖于相对导入,那将是有害的)。
【讨论】:
请注意,您对 PEP 8 的引用已过时。当前版本的 PEP 8 支持使用相对导入:引用:* 显式相对导入是绝对导入的可接受替代方案,尤其是在处理复杂的包布局时,使用绝对导入会不必要地冗长*跨度> @Bakuriu,真该死。我不知道 PEP 会这样改变。我会更新我的答案。但是,我仍然会坚持我的论点,即拥有一个与*** stdlib 模块名称相同的打包模块不仅是允许的,而且是鼓励的。【参考方案3】:正如其他人所说,这通常是个坏主意。
话虽如此,如果您正在寻找潜在的解决方法或更好地了解问题,我建议您阅读以下 SO 问题:
Importing from builtin library when module with same name exists
How to access a standard-library module in Python when there is a local module with the same name?
【讨论】:
【参考方案4】:是的,真的没有什么好办法。尽量不要将你的模块命名为标准包。如果您真的想调用您的模块time
,我建议您改用_time.py
。即使有办法做到这一点,当涉及到 2 个时间模块时,它也会使您的代码难以阅读和混淆。
【讨论】:
以上是关于具有相同名称的 Python 模块(即,在包中重用标准模块名称)的主要内容,如果未能解决你的问题,请参考以下文章