如何为同一名称空间执行第二次__init__.py执行

时间:2014-06-25 17:05:27

标签: python python-2.7 namespaces package python-import

这是我的目录结构

.
|-- path1
|   `-- mynms
|       |-- __init__.py
|       `-- app1
|           |-- __init__.py
|           `-- foo.py
|-- path2
|   `-- mynms
|       |-- __init__.py
|       `-- app2
|           |-- __init__.py
|           `-- bar.py
`-- user.py

文件内容:

$ cat user.py
#!/usr/bin/python

import sys
sys.path.append('path1')
sys.path.append('path2')

from mynms.app2.foo import foo
from mynms.app2.bar import bar

foo()
bar()

$ cat path1/mynms/__init__.py;echo ==============;cat path2/mynms/__init__.py
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

print "I am path1/mynms/__init__.py"
==============
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

print "I am path2/mynms/__init__.py"

$ cat path1/mynms/app1/foo.py; echo ============; cat path2/mynms/app2/bar.py
def foo():
  print "foo!"
============
def bar():
  print "bar!"

问题:当我运行user.py时,我只获取path1 / __ init__.py的输出,但不获取path2的输出。有没有办法解决这个问题?

$ ./user.py
I am path1/mynms/__init__.py        -----> Why is 'I am path2/mynms/__init__.py not printed?
foo!
bar!

3 个答案:

答案 0 :(得分:3)

当您编写import mynms.app2.foofrom mynms.app2.foo import foo时,Python会执行此操作:

  • 通过查找mynms
  • 中找到的文件mynms/__init__.py来导入path1 通过查找文件mynms.app1(或mynms/app1/__init__.py
  • 导入mynms/app1.py
  • 通过查找mynms.app1.foo(或mynms/app1/foo.py
  • 导入mynms/app1/foo/__init__.py

在下一个导入语句import mynms.app2.bar上,Python执行:

  • 导入mynms - 它已经导入,所以没有任何关系。 (您可以查看sys.modules['mynms']以查看它是否已导入。如果不是,则会引发KeyError。)
  • 导入mynms.app2 - 再次,这已经导入。
  • 通过阅读mynms.app2.bar
  • 中的mynms/app2/bar.py文件导入path2

Python没有(明智的)方法从两个不同的文件中导入mynms模块两次。

如果您有初始化代码,则需要将它们放在模块mynms.app1mynms.app2中,以便它们具有不同的名称。即文件mynms/app1/__init__.pymynms/app2/__init__.py

答案 1 :(得分:1)

正如Martijn Pieters在评论中已经提到的,Python 2不支持作为包集合分发的包。但是如果使用setuptools(很可能是你),你可以模仿这样的包。

唯一的问题是包应该具有正确的元信息,因此setuptools可以在虚拟单个包中连接多个子包。这些信息通常会传递到setup()中的setup.py函数,并在安装后存在于site-packages目录中。这意味着那些分发包装的人应该做出适当准备的东西,你很难将它们放在PATH中以使它们起作用。

Here是相应文档的链接。准备花一些时间,因为PEAK文档并不总是足以快速启动。

答案 2 :(得分:0)

我同意其余的评论 - 这是错误的代码结构,应该尽快重构。但是,如果你真的,真的,真的不能或者 ,那么你可以通过使用标准库中的imp模块来破坏导入内部:

https://docs.python.org/2/library/imp.html

例如,您可以通过执行以下操作导入不在普通python模块搜索路径中的文件:

/Users/me/test.py:

def run_foo():
  print "hi got here" 

和另一个脚本:

import imp
foo = imp.load_source("foo", "/Users/me/test.py")
foo.run_foo()

同样,你正在反对Python这样做,而不是用它。但这是一个很好的技巧。