簡體   English   中英

Python3 中的 Python 命名空間包

[英]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_mod1org_common_config ,所以我只需要在打包和__init__.py文件上執行設置,我相信。

問題:

#1

使用__init__.py ,我應該使用哪些(如果有)?:

 from pkgutil import extend_path __path__ = extend_path(__path__, __name__)

或者:

 import pkg_resources pkg_resources.declare_namespace(__name__)

#2

使用setup.py ,我是否還需要添加namespace_modules參數,如果是,我會使用namespace_modules=['org.common']還是namespace_modules=['org', 'common']

#3

我可以通過以某種方式以不同方式實現這一點而放棄上述所有內容嗎? 也許更簡單或更“pythonic”?

聚會遲到了,但在 Python 中幫助其他旅行者沿着命名空間路徑走從來沒有壞處!

#1:

使用__init__.py ,我應該使用哪些(如果有)?:

它取決於,有三種方法可以做到的命名空間的包作為上市在這里

  1. 使用本機命名空間包。 這種類型的命名空間包在 PEP 420 中定義,並且在 Python 3.3 及更高版本中可用。 如果您的命名空間中的包只需要支持 Python 3 並通過 pip 安裝,則建議使用此方法。

  2. 使用 pkgutil 風格的命名空間包。 對於需要支持 Python 2 和 3 並通過 pip 和 python setup.py install 安裝的新軟件包,建議使用此方法。

  3. 使用 pkg_resources 風格的命名空間包。 如果您需要與已經使用此方法的軟件包兼容,或者您​​的軟件包需要是 zip 安全的,則建議使用此方法。

如果您使用 #2 ( pkgutil-style ) 或 #3 ( pkg_resources-style ),那么您將必須對__init__.py文件使用相應的樣式。 如果您使用本機命名空間,則命名空間目錄中沒有__init__.py

#2:

使用setup.py,我是否還需要添加namespace_modules 參數,如果是,我是使用namespace_modules=['org.common'] 還是namespace_modules=['org', 'common']?

如果您選擇的命名空間包不是本機風格,那么是的,您將需要在setup() namespace_packages

#3:

我可以通過以某種方式以不同方式實現這一點而放棄上述所有內容嗎? 也許更簡單或更“pythonic”?

由於您最終在 Python 中遇到了一個復雜的主題,因此您似乎知道自己在做什么,想要什么,並確定創建 Python 命名空間包是實現此目的的方法。 這將被認為是解決問題的 Pythonic 方式。


除了您的問題之外,我還發現了以下幾點:

我閱讀了PEP420Python 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_mod1org_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.clientorg.common符號。

要了解原因,您可以瀏覽此處(假設在 venv 中):

(venv) $ cd venv/lib/python3.5/site-packages/
(venv) $ ls -l | grep org

您將看到沒有org_clientorg_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']

  • 不! 只有name=packages= 但是,請注意與目錄結構相比的packages= arg 格式。
  • 這是package= arg 的格式: 命名空間包的 setup.py
  • 對應的目錄結構如下: example_package/a/<子包>

我可以通過以某種方式以不同方式實現這一點而放棄上述所有內容嗎? 也許更簡單或更“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.pystuptools.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

然后這就是我實際上是如何理解這一點的: https : //github.com/pypa/sample-namespace-packages/tree/master/native

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM