Python模块名称相同(即,在包中重用标准模块名称)

21

假设我有一个包含模块的软件包:

SWS/
  __init.py__
  foo.py
  bar.py
  time.py

模块之间需要相互引用,但我的 time.py 模块与标准模块同名会导致问题。

例如,在我的 foo.py 模块中,同时需要我的 SWS.time 和标准的 python time 模块,我会遇到问题,因为解释器会先查找包内的 my time.py 模块,而不是标准的 time 模块。

有没有办法解决这个问题?重复使用模块名称是否应该避免?

对包哲学的任何解决方案和观点都会有所帮助。


2
我认为很明显,你不应该重复使用标准的Python模块名称。这只会带来麻烦。 - Cryptite
11
为什么这是显而易见的?(在这里我扮演魔鬼的辩手,表示质疑) - MikeWyatt
看看httplib / httplib2和urllib / urllib2。 这使得库的世界更加丑陋,但这比名称冲突和不确定行为更可取。 - Silas Ray
1
值得一提的是,Python 风格指南 建议在避免与关键字冲突时使用 class_ 而不是 class(后面加下划线)。在这里使用 import time_ as time 也许更合适。 - Casey Kuball
10
大约14年前,我创建了一个名为“wave”的模块。最近再次尝试时,它已经无法正常工作了。原来在这段时间内,已经添加了一个同名的标准模块。是的,我知道,我一定很老......(而且我不是预言家)。很明显:不要过早下结论。 - Jacques de Hooge
4个回答

19

重复使用标准函数/类/模块/包的名称从来都不是一个好主意。尽量避免它。但是,对于您的情况,有干净的解决方法。

你看到的行为,导入你的 SWS.time 而不是标准库的 time,是由于古老的 Python 版本(2.x)中 import 的语义。要修复它,请添加:

from __future__ import absolute_import

在文件的最顶端加入此行代码。这将把 import 的语义改变为 Python3.x 版本的方式,这种方式更加合理。在这种情况下,该语句为:

import 模块名
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

3
我认为,当模块名称出现在命名空间中,比如一个包时,这并不是个坏主意。这正是PEP328的精神所在。请参见我的下面回答。 - OozeMeister

13

这取决于您使用的Python版本。如果您的目标Python版本是2.4或更早(在2015年,我希望不是这样),那么是不好的做法,因为没有办法(除非使用黑客手段)区分这两个模块。

然而,在Python 2.5+中,我认为在包命名空间内重复使用标准库模块名称是完全可以的;实际上,这就是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

或者,它应该使用明确的相对导入,就像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
1
@Bakuriu,哎呀。我不知道PEP可以这样改变。我会更新我的答案。然而,我仍然坚持认为,拥有一个包装模块,其名称与顶级stdlib模块相同,不仅是允许的,而且是受鼓励的。 - OozeMeister

6

0

是的,确实没有好的解决办法。尽量不要将您的模块命名为标准包的名称。如果您真的想将模块命名为time,我建议使用_time.py代替。即使有一种方法可以做到这一点,这也会使您的代码难以阅读,并且在两个时间模块出现时容易混淆。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接