Python:导入子包或子模块


90

已经使用了平面软件包,我没想到嵌套软件包会遇到这个问题。这是…

目录布局

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...

内容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不希望这样:失败,并显示消息:“没有名为模块的模块”。但是……

# 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既可以处理子包,也可以处理子包,但是不需要处理全局配置就可以处理本地内容。
  • 我还尝试在中使用import 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.

当您将“ from。import module”写入“ /package/subpackage/__init__.py”时,情况如何?
Markus Unterwaditzer'9

您的“分解版本”似乎完全适合您想要的操作。如果您为attribute1和attribute2做一个单独的导入行(就像您“偏爱”的那样),您只是故意给自己做更多的工作。没有理由这样做。
BrenBarn'9年

抱歉,我没得到你想要的。您能以更清晰的方式重新表述您的问题吗?您到底想做什么?我的意思是,您想写什么不起作用的书?您希望它如何起作用?通过阅读,我认为您包括了Java或C的导入语义。最后一件事:您可以使模块“星号导入”安全地添加一个__all__变量,该变量包含星号导入时应导出的名称列表。编辑:好的,阅读BrenBarn的答案,我理解您的意思。
巴库里(Bakuriu)2012年

Answers:


68

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

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

第二行查找名为的程序包,package.subpackagemodule从该程序包中导入。该行对第三行无效。第三行仅查找一个名为的模块module,但未找到。它不会“重用”module您从上一行获得的对象。

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

目前尚不清楚您要实现的目标。如果只想导入特定的对象attribute1,则只需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两次,则可以手动创建对attribute1的本地引用:

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

您的评论与Ignacio Vazquez-Abrams的评论相同(我评论了他的信息)。最后,关于使用的问题module.attribute1是我的事,但我有办法避免到处都有前缀。因此,我必须在每个位置使用前缀,或者创建本地别名来重复该名称。不是我所期望的样式,但是如果没有办法(毕竟,我已经习惯了Ada,它需要与重命名声明类似的东西)。
Hibou57'9

@ Hibou57:我仍然不清楚您要在“第2版”中完成什么。您想做什么是不可能的?您想永远不要重新键入包/模块/属性名称的任何部分,但仍要导入模块及其属性?
BrenBarn'9年

我希望有一个本地包引用,就像可以有一个本地对象引用一样。似乎终于有了真正的本地模块引用,但是您不能从中引用。它是本地和全球的混合,有着有趣的味道(有些东西可以是本地的,有些则必须是全球化的,我不喜欢它,但是只要我现在更好地了解它的工作原理,我就可以了)。谢谢您的留言。
Hibou57'9

1
我不确定您是否仍然了解它的工作原理。还是无论如何您在2012年所做的。
Hejazzman

1
在经过6个月的裁员之后,每次我回到Python时,我都会来到这里。如果我每次访问该页面都可以投票!我要用这样一句话做一个巨大的海报:“没有办法通过导入导致该问题的软件包来“逐步”构建该模块的路径。
PatrickT

10

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

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

也:

from package.subpackage import module
print module.attribute1

sys.modules['name']直到现在我还不知道您的引用,这使我认为这是我所担心的(BrenBarn证实了):没有什么比Python中真正的子包更像了。sys.modules,顾名思义,它是全局的,如果所有对模块的引用都依赖于此,那么就没有什么像对模块的本地引用(可能是Python 3.x附带的)。
Hibou57'9

您对“引用”的使用存在歧义;import#2中的第一个生成package.subpackage.module绑定到的本地引用module
伊格纳西奥·巴斯克斯

是的,但这是我不能从;-)导入的“模块”
Hibou57'9

0

如果您要做的只是在全局名称空间中获取attribute1,那么版本3似乎还不错。为什么会出现过大的前缀?

在版本2中,代替

from module import attribute1

你可以做

attribute1 = module.attribute1

attribute1 = module.attribute1只是重复名称而没有附加值。我知道它有用,但是我不喜欢这种风格(这并不意味着我不喜欢您的回复)。
Hibou57'9

2
我想我像所有在这里发表评论的人一样,不理解您想做什么。在您给出的所有示例中,您似乎都希望以命名空间中子包中的符号结尾。您的非工作示例(示例2)想要通过从包中导入一个子模块,然后从该子模块中导入一个符号来实现。我不知道为什么要分两步而不是一步。也许更多地解释您理想的解决方案是什么以及为什么。
Thomas Vander Stichele 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.