Python:当同名模块存在时,我该如何选择要导入的模块?

6
假设我现在在一个名为openid.py的文件中,执行以下操作:
from openid.consumer.discover import discover, DiscoveryFailure

我在pythonpath上安装了openid模块,但解释器似乎正在尝试使用我的openid.py文件。我该如何获取库的版本?

(当然,除了明显的“重命名您的文件”的答案之外,还有其他好的解决方法)。


7
修改你的文件名。 - SilentGhost
不重命名文件的原因是什么?与寻找解决方法相比,这似乎是一个小问题。 - Zoomulator
该文件在模块中具有别名的“类型”,因此语义上应称为openid。 openid是该类型的名称。 - Paul Tarjan
这些模块是否位于父模块“别名”中? - SilentGhost
是的,它们都是。但在“openid.py”文件内,我似乎无法访问“openid”库,这是主要的问题。 - Paul Tarjan
5个回答

9

这就是为什么绝对导入被选择作为新的默认行为。然而,在2.6中它们还不是默认值(也许在2.7中会是)。您可以通过从future中导入它们来获得它们的行为:

from __future__ import absolute_import

您可以在Nick提到的PEP或(我认为更容易理解的)文档“Python 2.5有什么新特性”中了解更多信息。


3

重命名。这就是名称空间背后的思想。你的openid可以是你顶级模块project中的一个子模块。你的email将与stdlib中的顶级模块email冲突。

因为你的openid不是通用的,它为你的项目提供了一个特殊情况。


4
实际上,我认为命名空间背后的思想并不是为了将其重命名为独特的名称;myproject.openid与openid是不同的。通过新的绝对导入,import openid 将始终获取系统范围内(例如stdlib)的模块,而相对路径下的 .openid 将获取当前模块中的openid子模块。 - c089
当然不是。如果他的模块是 project,那么他应该使用 project.openid。不清楚 OP 为什么要这样做,但如果他没有 project 模块,那么他应该将 openid 重命名为其他名称。 - SilentGhost
正确的写法是在包外使用 pkg.openid。但是在 "openid.py" 文件内部,我无法访问 "openid" 库。这是主要的问题。 - Paul Tarjan

2
我不会涉及重命名的争论,而是专注于向您展示如何做您想要的事情(无论它是否“对您有益”;-)。解决方案并不难...
只需设置__path__!以下是一个小演示:
$ mkdir /tmp/modules /tmp/packages
$ mkdir /tmp/packages/openid
$ echo 'print "Package!"' > /tmp/packages/openid/__init__.py
$ gvim /tmp/modules/openid.py
$ PYTHONPATH='/tmp/modules:/tmp/packages' python -c'import openid'
Module!
Package!

这里展示了一个模块 openid 成功导入同名的,即使该模块的路径在sys.path中排在前面,而且 在那个时候 sys.modules['openid'] 已经被设置。所有的"秘密"都在openid.py文件的简单代码中...

print "Module!"
__path__ = ['/tmp/packages']
import openid

当然,如果没有__path__的赋值,它只会输出Module!

当然,这也适用于导入包内的子模块。请执行以下操作:

$ echo 'print "Submod!"' > /tmp/packages/openid/submod.py

并将openid.py的最后一行更改为

from openid import submod

然后你会看到:

$ PYTHONPATH='/tmp/modules:/tmp/packages' python -c'import openid'
Module!
Package!
Submod!
$ 

关于设置 sys.modules ['openid']。您在另一条评论中也提到了这一点,但我不是很确定。为什么会这样呢?当前文件是否自动添加到sys.modules中?运行快速测试.py <“import sys; print'test' in sys.modules.keys()”时,结果似乎并非如此! - ThomasH
正如我回复另一条评论所说:作为主模块运行的一个文件会进入 sys.modules['__main__'] 而不是 __name__ 人为和惯例设置为 __main__。这就是为什么我在这里 import openid,以展示 __path__ 仍然有效。如果你只需要将 openid.py 作为主要运行文件,那么没有必要将其放在 sys.path 中。 - Alex Martelli
我认为 OP 的问题仅仅源于 '.' 在 sys.path 中排在前面,因此在查找包含 openid.py 脚本的目录时会搜索当前目录。但是,如果将 sys.path 中“有趣”的目录移到前面,就会先找到另一个“openid”(模块),一切都会正常。 - ThomasH
将_openid_包称为“模块”非常令人困惑(您和OP有时都这样做)—— openid.py是模块,带有文件__init__.py的目录openid是包。如果从未导入openid.py,则可以使用sys.path进行黑客攻击;但是,我的答案更普遍地适用(无论是导入openid.py还是作为主脚本运行),并且避免了对系统全局设置的干扰。 - Alex Martelli
谢谢,我知道了。我也会尽力改善我的措辞 :-). - ThomasH

1

您可以使用相对或绝对导入(取决于您的具体情况),这在PEP 328中有所涉及。当然,严肃地说,您不应该创建这样的命名冲突,而应该重命名您的文件。


即使文件的语义正确的名称是openid,也可以吗?比如它与电子邮件、网页、域名等类型的别名目录在一起? - Paul Tarjan
@Paul:即使如此。如果它在“类型别名目录”中,那么它真的应该是一个包,然后它就是pkg.openid,这样就不会有歧义了。 - Nick Bastin
正确的写法是在包外使用 pkg.openid。但是在 "openid.py" 文件内部,我无法访问 "openid" 库。这是主要的问题。 - Paul Tarjan
@Paul:当然可以。使用绝对导入(from future import absolute_import),然后执行 'import openid',它基本上会解析为(伪路径结构)/openid,而不是/pkg/openid。 - Nick Bastin

-1
你可以尝试打乱sys.path,在导入之前将有趣的目录移到前面。

如果sys.modules['openid']已经设置,这将无济于事,因为在OP的情况下,他正在openid.py中。 - Alex Martelli
@Alex 为什么会这样呢?当前文件是否自动添加到 sys.modules 中?快速测试:运行 test.py < "import sys; print sys.modules.keys()",结果表明情况并非如此! - ThomasH
被导入的模块会以其真实名称添加到sys.modules中;作为“主程序”运行的唯一文件将得到惯例名称__main__。但如果您将openid.py作为主模块运行,则首先无需将其添加到sys.path中! - Alex Martelli

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