是否可以使子包显示为实际包

时间:2017-11-16 15:31:03

标签: python packages python-import

我来到this question并对tensorflow开发人员如何使tensorflow目录显示为实际包而感到非常失望,而实际的包根实际上是{{1} }}。使用表单

tensorflow/python文件
__init__.py

他们试图实现这一目标。在使用包时,这会导致一些不一致的行为(至少在我看来),例如

from tensorflow.python import *
del python

现在,我想知道是否/如何可以避免大多数/所有这些问题以获得更一致的行为。通过不删除import tensorflow.python # seems to work tensorflow.python # AttributeError: no attribute 'python' from tensorflow.python import Session # works as expected tensorflow.python.Session # AttributeError: no attribute 'python' from tensorflow import python # works as expected tensorflow.nn # works as expected import tensorflow.nn # ImportError: no module 'tensorflow.nn' tensorflow.nn.tanh # works as expected from tensorflow.nn import tanh # ImportError: no module 'tensorflow.nn' 属性,可以轻松解决第一组不一致问题。但是,鉴于目标是使整个包看起来像是一个子包,这可能并不完全令人满意。

为了简单起见,让我们考虑以下包结构

python

其中package/ __init__.py api/ __init__.py code.py 看起来像

package/api/code.py

def a(): return 'alpha' def b(): return 'bravo' 将是

package/api/__init__.py

是否可以创建import package.api.code 以便以下工作

package/__init__.py

我相信最后四行代码应该通过添加到import package.api # ImportError: no module 'package.api' package.api # AttributeError: no attribute 'api' from package.api import code # ImportError: no module 'package.api' package.api.code # AttributeError: no attribute 'api' from package import api # ImportError: cannot import 'api' package.code # works as expected import package.code # works as above package.code.a # works as expected from package import a # correctly imports function a 来提供预期结果,但我似乎无法找到使sys.modules失败的方法。

有人会知道如何做到这一点吗?请随意指出我正在忽视或应考虑实现上述目标的用例。

2 个答案:

答案 0 :(得分:0)

首先 - 我必须说我真的很讨厌"阴暗"像这样的技术。它对各种IDE智能感知有不良影响,使得库结构不易理解。但还是......

如果您希望子模块code.py充当实际的子包,则需要创建一个虚拟模块:

package/
    __init__.py
    api/
        __init__.py
        code.py
    code/
        __init__.py

在code / __ init__py中添加:

from package.api.code import *

这在包/ __ init __。py:

from package.code import *

然后这部分应按预期工作:

import package.code            # works as expected
package.code                   # works as expected
package.code.a                 # works as expected
from package import a          # works as expected

如果你进一步将它添加到包/ __ init __。py:

import package.api
del package.api

您基本上断开了用户与访问package.api的连接,但没有别的,他们仍然可以使用&x 39;来自x import y'

来通过子包访问子模块
import package.api            # works
package.api.a()               # AttributeError: module 'package' has no attribute 'api'

import package.api.code       # works
package.api.code.a()          # AttributeError: module 'package' has no attribute 'api'

from package.api import code  # works
code.a()                      # works

from package import api       # works
api.code.a()                  # AttributeError: module 'package.api' has no attribute 'code'

答案 1 :(得分:0)

我设法写了一些几乎的作品(package/__init__.py):

import sys

from package.api import *

for key in sys.modules:
    parts = key.split('.')
    if len(parts) > 1 and parts.pop(0) == __name__:
        subkey = parts.pop(0)
        if subkey == 'api' and len(parts) == 0:
            sys.modules['.'.join([__name__, subkey])] = None
        elif subkey == 'api':
            m = sys.modules.pop(key)
            sys.modules['.'.join([__name__] + parts)] = m
del api        
del sys

导入错误表明它仍然非常糟糕,但除此之外,大多数示例都按照指定的工作 iff 包已经加载一次(即import package在运行我的问题中的语句之前已经调用过类似的东西。如果第一个语句是import package.api,那么就没有我想要的ImportError

为了找到这个问题的解决方案,我偶然发现了this answer,这实际上导致了更优雅代码的相同行为:

import sys

from package import api

# clean up this module
self = sys.modules.pop(__name__)
del self

# this module becomes hidden module
sys.modules[__name__] = api
sys.modules[api.__name__] = None

del api
del sys

然而,这仍然存在这样的问题:如果第一次导入类似于import package.api,则不会抛出ImportError