Python: 导入子包或子模块

由于已经使用了平面软件包,我没有预料到在使用嵌套软件包时会遇到这样的问题。

目录布局

dir
|
+-- test.py
|
+-- package
|
+-- __init__.py
|
+-- subpackage
|
+-- __init__.py
|
+-- module.py

内容 Init.py

package/__init__.pypackage/subpackage/__init__.py都是空的。

module.py的内容

# file `package/subpackage/module.py`
attribute1 = "value 1"
attribute2 = "value 2"
attribute3 = "value 3"
# and as many more as you want...

Content of test.py (3 versions)

版本1

# file test.py
from package.subpackage.module import *
print attribute1 # OK

That's the bad and unsafe way of importing things (import all in a bulk), but it works.

版本2

# file test.py
import package.subpackage.module
from package.subpackage import module # Alternative
from module import attribute1

一种更安全的导入方法,逐项导入,但是它失败了,Python 不希望这样: 失败的消息是: “没有命名为模块的模块”。但是..。

# file test.py
import package.subpackage.module
from package.subpackage import module # Alternative
print module # Surprise here

这是个模块,但不是模块/-P8-O..

Version 3

# file test.py v3
from package.subpackage.module import attribute1
print attribute1 # OK

这个管用。因此,您要么被迫一直使用过度杀伤前缀,要么使用版本 # 1中的不安全方式,而 Python 不允许您使用安全方便的方式?更好的方法,哪一个是安全的,避免不必要的长前缀是唯一的 Python 拒绝?这是因为它喜欢 import *,还是因为它喜欢过长的前缀(这无助于实施这种做法) ?.

抱歉说了这么难听的话,但是我已经花了两天时间来处理这种愚蠢的行为了。除非我在某个地方完全错了,否则我会觉得 Python 的包和子包模型真的出了问题。

Notes

  • 我不想依赖于 sys.path,以避免全局副作用,也不想依赖于 *.pth文件,这只是另一种方式玩 sys.path与相同的全局效应。为了使解决方案干净,它必须是本地的。Python 可以处理子包,也可以不处理子包,但是它不需要使用全局配置来处理本地内容。
  • 我还尝试在 package/subpackage/__init__.py中使用导入,但它解决不了任何问题,它执行相同的操作,并且抱怨 subpackage不是一个已知的模块,而 print subpackage说它是一个模块(又是怪异的行为)。

也许我是完全错误的强硬(我更喜欢这个选项) ,但是这让我对 Python 感到非常失望。

除了我试过的三种方法,还有别的方法吗? 有什么我不知道的吗?

(sigh)

——编辑—— >% ——

到目前为止的结论(在人们的评论之后)

There is nothing like real sub‑package in Python, as all package references goes to a global dictionnary, only, which means there's no local dictionary, which implies there's is no way to manage local package reference.

你必须使用完全前缀或短前缀或别名,如:

完整的前缀版本

from package.subpackage.module import attribute1
# An repeat it again an again
# But after that, you can simply:
use_of (attribute1)

短前缀版本(但重复前缀)

from package.subpackage import module
# Short but then you have to do:
use_of (module.attribute1)
# and repeat the prefix at every use place

Or else, a variation of the above.

from package.subpackage import module as m
use_of (m.attribute1)
# `m` is a shorter prefix, but you could as well
# define a more meaningful name after the context

Factorized version

如果您不介意一次性导入多个实体,那么您可以:

from package.subpackage.module import attribute1, attribute2
# and etc.

不是我最喜欢的类型(我喜欢每个导入实体有一个导入语句) ,但可能是我个人喜欢的那个。

Update (2012-09-14):

最后,除了一个关于布局的注释之外,在实践中看起来还不错:

from package.subpackage.module import (


attribute1,
attribute2,
attribute3,
...)  # and etc.
172595 次浏览

# 2失败的原因是因为 sys.modules['module']不存在(import 例程有自己的作用域,不能看到 module本地名称) ,而且磁盘上没有 module模块或包。注意,可以用逗号分隔多个导入名称。

from package.subpackage.module import attribute1, attribute2, attribute3

另外:

from package.subpackage import module
print module.attribute1

如果您想要做的只是在全局名称空间中获取 Attribute1,那么版本3看起来不错。为什么是过度杀伤前缀?

在版本2中,而不是

from module import attribute1

你可以的

attribute1 = module.attribute1

您似乎误解了 import如何搜索模块。当使用 import 语句时,一直都是会搜索实际的模块路径(和/或 sys.modules) ; 由于以前的导入,它不会在本地名称空间中使用模块 物品。当你这样做:

import package.subpackage.module
from package.subpackage import module
from module import attribute1

The second line looks for a package called package.subpackage and imports module from that package. This line has no effect on the third line. The third line just looks for a module called module and doesn't find one. It doesn't "re-use" the object called module that you got from the line above.

换句话说,from someModule import ...并不意味着“来自我前面导入的名为 some Module 的模块...”,而是意味着“来自您在 sys.path 上找到的名为 some Module 的模块...”。没有办法通过导入导向模块的包来“增量地”构建模块的路径。导入时总是必须引用整个模块名称。

不清楚你想达到什么目的。如果您只想导入特定的对象属性1,只需执行 from package.subpackage.module import attribute1并使用它即可。一旦您从中导入了所需的名称,就不需要担心长 package.subpackage.module

如果 do希望以后访问模块以访问其他名称,那么可以执行 from package.subpackage import module,如您所见,然后可以执行 module.attribute1,以此类推。

如果你想要 both-,也就是说,如果你想要 attribute1直接访问 还有,你想要 module访问,只要做上面两个:

from package.subpackage import module
from package.subpackage.module import attribute1
attribute1 # works
module.someOtherAttribute # also works

如果您不喜欢输入 package.subpackage,甚至两次,您可以手动创建一个本地引用属性1:

from package.subpackage import module
attribute1 = module.attribute1
attribute1 # works
module.someOtherAttribute #also works