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

Python 是否可以使子包显示为实际的包,python,packages,python-import,Python,Packages,Python Import,开发人员试图使tensorflow目录显示为实际的包,而实际的包根实际上是tensorflow/python,我对此感到非常失望。通过使用表单的\uuuu init\uuuuu.py文件 from tensorflow.python import * del python 他们试图实现这一目标。这导致在使用软件包时出现一些不一致的行为(至少在我看来是这样),例如 import tensorflow.python # seems to work tensorflow

开发人员试图使
tensorflow
目录显示为实际的包,而实际的包根实际上是
tensorflow/python
,我对此感到非常失望。通过使用表单的
\uuuu init\uuuuu.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/\uuuu init\uuuu.py
将是

import package.api.code
是否可以创建
package/\uuuuu init\uuuuuu.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
中得到预期的结果,但我似乎无法找到使
import package.api
失败的方法


有人知道如何做到这一点吗?请允许我使用我忽略或应该考虑的案例来实现上述目标。

首先,我必须说我真的讨厌这种“阴暗”的技巧。它会对各种IDE智能感知产生不良影响,并使库结构不易理解。但还是

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

package/
    __init__.py
    api/
        __init__.py
        code.py
    code/
        __init__.py
在代码/\uuuu init\uuuu py中添加以下内容:

from package.api.code import *
在包/_uuinit_uuu.py中:

from package.code import *
import package.api
del package.api
然后,这一部分应按预期工作:

import package.code            # works as expected
package.code                   # works as expected
package.code.a                 # works as expected
from package import a          # works as expected
如果您进一步将其添加到包/\uuuu init\uuuuu.py中:

from package.code import *
import package.api
del package.api
您基本上断开了用户访问package.api的连接,但除此之外,他们仍然可以使用“from 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'

首先,我必须说,我讨厌这种“阴暗”的技术。它会对各种IDE智能感知产生不良影响,并使库结构不易理解。但还是

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

package/
    __init__.py
    api/
        __init__.py
        code.py
    code/
        __init__.py
在代码/\uuuu init\uuuu py中添加以下内容:

from package.api.code import *
在包/_uuinit_uuu.py中:

from package.code import *
import package.api
del package.api
然后,这一部分应按预期工作:

import package.code            # works as expected
package.code                   # works as expected
package.code.a                 # works as expected
from package import a          # works as expected
如果您进一步将其添加到包/\uuuu init\uuuuu.py中:

from package.code import *
import package.api
del package.api
您基本上断开了用户访问package.api的连接,但除此之外,他们仍然可以使用“from 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'

我设法写了一些几乎可以工作的东西(在
package/\uuu init\uuuu.py
):

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

在试图找到这个问题的解决方案时,我无意中发现,这实际上导致了使用更优雅的代码的相同行为:

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

我设法写了一些几乎有效的东西(在
package/\uuu init\uuuu.py
中):

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

在试图找到这个问题的解决方案时,我无意中发现,这实际上导致了使用更优雅的代码的相同行为:

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

您可以尝试:导入package.code作为code@PeterMajko你这是什么意思?如果
导入包,这应该正常工作。code
可以工作…如果使用“从y导入x作为z”,它工作得非常好:@PeterMajko我知道,我注意到tensorflow导入python的
确实可以工作(我相应地编辑了我的问题),但它应该工作吗?我假设
tensorflow/\uuuu init\uuuuuu.py
中的
delpython
的思想是使tensorflow包看起来像是
tensorflow/python
。在这种情况下,我不希望看到这种行为,他们有理由这样做。也许会问他们为什么采取这种方法?:)您可以尝试:导入package.code作为code@PeterMajko你这是什么意思?如果
导入包,这应该正常工作。code
可以工作…如果使用“从y导入x作为z”,它工作得非常好:@PeterMajko我知道,我注意到tensorflow导入python的
确实可以工作(我相应地编辑了我的问题),但它应该工作吗?我假设
tensorflow/\uuuu init\uuuuuu.py
中的
delpython
的思想是使tensorflow包看起来像是
tensorflow/python
。在这种情况下,我不希望看到这种行为,他们有理由这样做。也许会问他们为什么采取这种方法?:)实际上,我希望看到一致的行为: