【问题标题】:How to get second __init__.py execute for same namespace如何为同一命名空间执行第二个 __init__.py
【发布时间】:2014-08-16 08:51:48
【问题描述】:

这是我的目录结构

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

【问题讨论】:

  • 你有两个同名的包,这通常是不受欢迎的。为什么您的测试用例包必须同名?你能重命名第二个吗?
  • testcase 这个名字有误导性,让我改一下。
  • 由于其他原因,我无法将它们更改为不同的命名空间。它们是同一个开发团队的两个应用程序,但出于部署目的必须保持不同的层次结构。
  • Python 2 不支持开箱即用的命名空间包。 setuptools 确实添加了命名空间支持,但要复制这种行为需要大量的黑客攻击。当我有更多时间时,我可以总结一下setuptools 所做的事情,但您也可以直接使用它来打包您的项目并让setuptools 为您管理命名空间。跨度>

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


【解决方案1】:

当您编写import mynms.app2.foofrom mynms.app2.foo import foo 时,Python 会这样做:

  • 通过查找文件mynms/__init__.py 导入mynms,它在path1 中找到该文件
  • 通过查找文件mynms/app1/__init__.py(或mynms/app1.py)导入mynms.app1
  • 通过查找mynms/app1/foo.py(或mynms/app1/foo/__init__.py)导入mynms.app1.foo

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

  • import mynms - 它已经被导入,所以没有什么可做的。 (可以查看sys.modules['mynms']是否已经导入,如果没有,会引发KeyError。)
  • import mynms.app2 - 同样,这已经被导入了。
  • 通过读取文件mynms/app2/bar.py导入mynms.app2.bar,该文件位于path2

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

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

【讨论】:

    【解决方案2】:

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

    唯一的问题是包应该有正确的元信息,这样setuptools 可以在一个虚拟的单个包中加入几个子包。该信息通常传递给setup.py 中的setup() 函数,并在安装后位于您的site-packages 目录中。这意味着分发包的人无论如何都应该做点什么来适当地准备它们,而你几乎不能把它们放在PATH 中以使其工作。

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

    【讨论】:

      【解决方案3】:

      我同意其余的 cmets - 这是糟糕的代码结构,应该尽快重构。然而,如果你真的,真的,真的不能或不能,那么你可以通过使用标准库中的 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 这样做,而不是使用它。但这是一个很好的技巧。

      【讨论】:

        猜你喜欢
        • 2020-07-02
        • 2015-02-27
        • 2017-06-11
        • 1970-01-01
        • 2010-12-15
        • 1970-01-01
        • 1970-01-01
        • 2021-02-27
        • 1970-01-01
        相关资源
        最近更新 更多