[英]Python Namespace Packages in Python3
命名空間包的主題對於初學者來說似乎有點令人困惑,而且 Python 的先前版本以幾種不同的方式實現它或者 StackOverflow 上的許多問答都過時了,這無濟於事。 我正在尋找Python 3.5
或更高版本的解決方案。
我正在將一堆 Python 代碼重構為模塊和子模塊,並努力使這些項目中的每一個都設置為在位於同一命名空間中時彼此獨立運行。
我們最終將使用內部 PyPi 服務器,將這些包提供給我們的內部網絡,並且不想將它們與外部(公共)PyPi 包混淆。
示例:我有 2 個模塊,我希望能夠執行以下操作:
from org.client.client1 import mod1
from org.common import config
反射的模塊將這樣分開:
存儲庫 1:
org_client_client1_mod1/
setup.py
mod1/
__init__.py
somefile.py
存儲庫 2:
org_common_config/
setup.py
config/
__init__.py
someotherfile.py
我的 Git 存儲庫已經設置為org_client_client1_mod1
和org_common_config
,所以我只需要在打包和__init__.py
文件上執行設置,我相信。
使用
__init__.py
,我應該使用哪些(如果有)?:from pkgutil import extend_path __path__ = extend_path(__path__, __name__)
或者:
import pkg_resources pkg_resources.declare_namespace(__name__)
使用
setup.py
,我是否還需要添加namespace_modules
參數,如果是,我會使用namespace_modules=['org.common']
還是namespace_modules=['org', 'common']
?
我可以通過以某種方式以不同方式實現這一點而放棄上述所有內容嗎? 也許更簡單或更“pythonic”?
聚會遲到了,但在 Python 中幫助其他旅行者沿着命名空間路徑走從來沒有壞處!
使用
__init__.py
,我應該使用哪些(如果有)?:
它取決於,有三種方法可以做到的命名空間的包作為上市在這里:
使用本機命名空間包。 這種類型的命名空間包在 PEP 420 中定義,並且在 Python 3.3 及更高版本中可用。 如果您的命名空間中的包只需要支持 Python 3 並通過 pip 安裝,則建議使用此方法。
使用 pkgutil 風格的命名空間包。 對於需要支持 Python 2 和 3 並通過 pip 和 python setup.py install 安裝的新軟件包,建議使用此方法。
使用 pkg_resources 風格的命名空間包。 如果您需要與已經使用此方法的軟件包兼容,或者您的軟件包需要是 zip 安全的,則建議使用此方法。
如果您使用 #2 ( pkgutil-style
) 或 #3 ( pkg_resources-style
),那么您將必須對__init__.py
文件使用相應的樣式。 如果您使用本機命名空間,則命名空間目錄中沒有__init__.py
。
使用setup.py,我是否還需要添加namespace_modules 參數,如果是,我是使用namespace_modules=['org.common'] 還是namespace_modules=['org', 'common']?
如果您選擇的命名空間包不是本機風格,那么是的,您將需要在setup()
namespace_packages
。
我可以通過以某種方式以不同方式實現這一點而放棄上述所有內容嗎? 也許更簡單或更“pythonic”?
由於您最終在 Python 中遇到了一個復雜的主題,因此您似乎知道自己在做什么,想要什么,並確定創建 Python 命名空間包是實現此目的的方法。 這將被認為是解決問題的 Pythonic 方式。
除了您的問題之外,我還發現了以下幾點:
我閱讀了PEP420 , Python Packaging guide並花了很多時間來理解命名空間包,並且我大致了解它是如何工作的。 我在這里、 這里、 這里和 SO 上的這個線程閱讀了幾個答案 - 這里的示例和 Rob 共享的 Git 鏈接。
然而,我的問題是在我創建了我的包之后。 由於所有說明和示例代碼都在setuptools.setup(package=[])
函數中明確列出了包,我的代碼失敗了。 我的子包/目錄不包括在內。 深入挖掘,我發現 setuptools 有一個find_namespace_package()
函數,它也有助於添加子包
編輯:
鏈接到find_namespace_packages()
( setuptools
版本大於40.1.0
): https : 40.1.0
編輯(08/09/2019):
為了完成答案,讓我也重組一個例子。
以下解決方案假設 Python 3.3+ 支持隱式命名空間包
由於您正在尋找適用於 Python 3.5
或更高版本的解決方案,讓我們使用提供的代碼示例並進一步詳細說明。
讓我們假設以下情況:
命名空間/Python 包名稱: org
分發包: org_client
, org_common
蟒蛇: 3.3+
設置工具: 40.1.0
為您執行以下操作
from org.client.client1 import mod1
from org.common import config
並保持您的頂級目錄相同,即。 org_client_client1_mod1
和org_common_config
,您可以將結構更改為以下內容
存儲庫 1:
org_client_client1_mod1/
setup.py
org/
client/
client1/
__init__.py
submod1/
__init__.py
mod1/
__init__.py
somefile.py
file1.py
更新了setup.py
from setuptools import find_namespace_packages, setup
setup(
name="org_client",
...
packages=find_namespace_packages(), # Follows similar lookup as find_packages()
...
)
存儲庫 2:
org_common_config/
setup.py
org/
common/
__init__.py
config/
__init__.py
someotherfile.py
更新setup.py
:
from setuptools import find_namespace_packages, setup
setup(
name="org_common",
...
packages=find_namespace_packages(), # Follows similar lookup as find_packages()
...
)
安裝(使用pip
):
(venv) $ pip3 install org_common_config/
(venv) $ pip3 install org_client_client1_mod1/
更新的 pip 列表將顯示以下內容:
(venv) $ pip3 list
...
org_client
org_common
...
但它們不可導入,導入時您必須遵循org.client
和org.common
符號。
要了解原因,您可以瀏覽此處(假設在 venv 中):
(venv) $ cd venv/lib/python3.5/site-packages/
(venv) $ ls -l | grep org
您將看到沒有org_client
或org_common
目錄,它們被解釋為命名空間包。
(venv) $ cd venv/lib/python3.5/site-packages/org/
(venv) $ ls -l
client/
common/
...
這是一個棘手的話題。 所有-
's, _
's 和__init__.py
's 無處不在並不能完全讓我們輕松。
首先,我來回答你的問題:
對於
__init__.py
,我應該使用哪些(如果有)?
__init__.py
可以完全為空,它只需要在正確的位置。 即(雙關語)它們應該在任何包含 python 代碼的子包中(不包括setup.py
。)遵循這些規則,你應該沒問題。使用 setup.py,我是否還需要添加
namespace_modules
參數,如果是,我會使用namespace_modules=['org.common']
還是namespace_modules=['org', 'common']
?
我可以通過以某種方式以不同方式實現這一點而放棄上述所有內容嗎? 也許更簡單或更“pythonic”?
我將在本答案的其余部分以本機格式重新實現您的命名空間包:
我會把我能找到的所有有用的文檔放在帖子的底部。
K 所以我假設你想要本機命名空間包。 首先讓我們看一下您的 2 個存儲庫的當前結構:
org_client_client1_mod1/
setup.py
mod1/
__init__.py
somefile.py
&
org_common_config/
setup.py
config/
__init__.py
someotherfile.py
這^太簡單了!!!
我的大腦不夠靈活,無法知道我們是否可以使用命名空間包深入 3 層,但是要執行您想要的操作,我很確定您會執行以下操作:
org-client/
setup.py
org/
client/
client1/
__init__.py
mod1/
__init__.py
somefile.py
&
org-common-but-also-note-this-name-doesnt-matter/
setup.py
org/
common/
__init__.py
config/
__init__.py
someotherfile.py
基本上,關鍵是在每個setup.py
為stuptools.setup()
指定正確的name=
& packages=
args。
這些將是:
name='org_client',
...
packages=['org.client']
&
name='org_common'
...
packages['org.common']
分別。
然后只需使用pip install .
安裝每一個pip install .
在每個頂級目錄中。
安裝第一個會讓你訪問somefile.py
模塊,安裝第二個會讓你訪問someotherfile.py
。 它也不會因為您嘗試在同一環境中安裝 2 個名為org
包而感到困惑。
K 所以文檔中最有用的部分: https : //packaging.python.org/guides/packaging-namespace-packages/#packaging-namespace-packages
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.