115

已经使用了平面包,我没想到会遇到嵌套包的问题。这里是……</p>

目录布局

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

init .py的内容

package/__init__.py和都是package/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...

test.py(3个版本)的内容

版本 1

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

这是导入东西的糟糕和不安全的方式(批量导入),但它有效。

版本 2

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

一种更安全的逐项导入方式,但它失败了,Python 不希望这样:失败并显示消息:“没有名为模块的模块”。不过……</p>

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

……说<module 'package.subpackage.module' from '...'>。所以这是一个模块,但那不是一个模块 /-P 8-O ...呃

版本 3

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

这个有效。所以你要么被迫一直使用 overkill 前缀,要么使用版本 #1 中的不安全方式,并且 Python 不允许使用安全方便的方式?更好的方法,即安全且避免不必要的长前缀是 Python 唯一拒绝的方法?这是因为它喜欢import *还是因为它喜欢过长的前缀(这无助于实施这种做法)?

抱歉,我用了两天的时间来解决这种愚蠢的行为。除非我在某个地方完全错了,否则这会让我感觉 Python 的包和子包模型中确实存在某些问题。

笔记

  • 我不想依赖sys.path, 来避免全局副作用,也不想依赖*.pth文件,这只是sys.path使用相同全局效果的另一种方式。为了使解决方案干净,它必须是本地的。要么 Python 能够处理子包,要么不能,但它不应该需要使用全局配置来处理本地内容。
  • 我也尝试过在中使用导入package/subpackage/__init__.py,但它什么也没解决,它做同样的事情,并且抱怨subpackage不是一个已知的模块,而print subpackage说它是一个模块(再次奇怪的行为)。

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

除了我尝试的三种方法之外,还有其他已知的方法吗?有什么我不知道的?

(叹)

----- %< ----- 编辑 ----- >% -----

到目前为止的结论(经过人们的评论)

在 Python 中没有什么像真正的子包,因为所有包引用都转到一个全局字典,只有,这意味着没有本地字典,这意味着没有办法管理本地包引用。

您必须使用完整前缀或短前缀或别名。如:

完整前缀版本

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

或者,上述的变体。

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

分解版本

如果您不介意一次批量导入多个实体,您可以:

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

不是我最喜欢的口味(我更喜欢每个进口实体有一个进口声明),但可能是我个人喜欢的。

更新(2012-09-14):

最终在实践中似乎没问题,除了对布局的评论。而不是上述,我使用:

from package.subpackage.module import (

    attribute1, 
    attribute2,
    attribute3,
    ...)  # and etc.
4

3 回答 3

95

您似乎误解了如何import搜索模块。当您使用 import 语句时,它总是搜索实际的模块路径(和/或sys.modules);它不使用由于先前导入而存在的本地命名空间中的模块对象。当你这样做时:

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

第二行查找一个名为package.subpackage并从该包导入module的包。该行对第三行没有影响。第三行只是寻找一个名为的模块module,但没有找到。它不会“重用”module您从上面的行中获得的调用对象。

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

目前尚不清楚您要达到的目标。如果您只想导入特定的对象属性 1,只需执行from package.subpackage.module import attribute1并完成它。package.subpackage.module一旦您从中导入了您想要的名称,您就无需担心冗长的问题。

如果您确实希望以后可以访问该模块以访问其他名称,那么您可以这样做from package.subpackage import module,并且正如您所见,您可以随心所欲地做module.attribute1等等。

如果你想要两者——也就是说,如果你想要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
于 2012-09-01T17:09:37.363 回答
11

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

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

还:

from package.subpackage import module
print module.attribute1
于 2012-09-01T16:50:33.937 回答
0

如果您要做的只是在全局命名空间中获取 attribute1,那么版本 3 似乎还不错。为什么它是矫枉过正的前缀?

在版本 2 中,而不是

from module import attribute1

你可以做

attribute1 = module.attribute1
于 2012-09-01T16:54:27.033 回答