首页 > 解决方案 > python命名空间与包:使包成为默认命名空间

问题描述

我有一个具有总体命名空间的项目,其中包含包。这是文件夹结构:

pypackage
├── pypackage             <-- Source code for use in this project.
|   |
│   ├── bin               <-- Module: Cli entry point into pyproject.
|   |   ├── __init__.py
|   |   └── pypackage.py
|   |
|   └── core              <-- Module: Core functionality.
|       ├── __init__.py
|       └── pypackage.py
|
├── tests             
├── README.md         
└── setup.py          

很简单。如果我想导入它,我使用:

from pypackage.core import pypackage

它很好用,因为我的 setup.py 看起来像这样:

from setuptools import setup, find_packages
...
NAME = 'pypackage'
setup(
    name=NAME,
    namespace_packages=[NAME],
    packages=[f'{NAME}.{p}' for p in find_packages(where=NAME)],
    entry_points={
        "console_scripts": [
            f'{NAME} = {NAME}.bin.{NAME}:cli',
        ]
    },
    ...
)

pypackage但是,当它曾经只是一个独立的 python 文件时,我有导入它的遗留代码。像这样:

import pypackage

那么我该如何制作它,以便我可以使用名称空间和子包保持相同的结构,但仍然以旧方式导入它?我该如何转这个:

from pypackage.core import pypackage

进入这个:

import pypackage

换句话说,当我导入外部项目时,如何给pypackage.core.pypackage模块起别名?pypackagepypackage

标签: pythonmodulenamespace-package

解决方案


您可以通过导入顶级包来在新包中添加“旧”名称。

作为全局变量导入的名称是包pypackage/__init__.py的属性pypackage。利用它来访问“旧”位置:

# add all public names from pypackage.core.pypackage to the top level for
# legacy package use
from .core.pypackage import *

现在任何使用的代码都import pypackage可以使用pypackage.foopypackage.bar如果实际上这些对象是在其中定义的pypackage.core.pypackage

现在,因为pypackage是一个setuptools命名空间,你有一个不同的问题;命名空间包可用于安装多个单独的发行版,因此顶级包必须为或仅包含最小__init__.py文件(使用空目录创建的命名空间包需要 Python 3.3)。

如果您是唯一使用此命名空间的发行版发布者,您可以在此处作弊,并在您的包中使用单个文件,该文件可以使用 pkg-util-style文件和我上面使用的附加导入,但是您不能使用其他分发包中的任何文件或要求它们都使用完全相同的内容。协调是这里的关键。__init__.pycore__init__.py__init__.py__init__.py

或者您将不得不使用不同的方法。保留pypackage为遗留包装模块,并重命名新的包格式以使用新的、不同的顶级名称,该名称可以与旧模块相邻。此时,您可以直接将遗留包作为额外的顶级模块包含在您的项目中。


推荐阅读