35

名前空間パッケージのトピックは、初心者にとっては少し混乱するように思われます。また、以前のバージョンの Python でいくつかの異なる方法で実装されていたり、StackOverflow に関する多くの Q&A が古くなっていることは役に立ちません。での解決策を探してい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

質問:

#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_modulesnamespace_modules=['org.common']namespace_modules=['org', 'common']

#3

どういうわけかこれを別の方法で実装するだけで、上記のすべてを忘れることができますか? おそらく、より単純な、またはより「pythonic」なものでしょうか?

4

2 に答える 2

14

パーティーに遅れましたが、仲間の旅行者が Python の名前空間パスをたどるのを助けることは決して悪いことではありません!

#1:

では、__init__.pyこれらのうちどれを使用する必要がありますか?:

ここにリストされているように、名前空間パッケージを実行する方法は 3 つあります。

  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は、名前空間ディレクトリに no を指定します。

#2:

setup.py では、namespace_modules パラメーターを追加する必要がありますか? その場合、namespace_modules=['org.common'] または namespace_modules=['org', 'common'] を使用しますか?

名前空間パッケージの選択がネイティブ スタイルでない場合は、はいnamespace_packagessetup().

#3:

どういうわけかこれを別の方法で実装するだけで、上記のすべてを忘れることができますか? おそらく、より単純な、またはより「pythonic」なものでしょうか?

Python の複雑なトピックにたどり着いたので、自分が何をしているのか、何を望んでいるのかを理解しているようで、Python Namespace パッケージを作成することがその方法であることがわかりました。これは、問題を解決するための Pythonic な方法と見なされます。


あなたの質問に加えて、ここに私が発見したいくつかのことがあります:

私はPEP420Python Packaging guideを読み、名前空間パッケージを理解するのに多くの時間を費やしました。ここここここ、および SO のこのスレッドいくつかの回答を読みました。

しかし、私の問題は、パッケージを作成した後でした。すべての手順とサンプル コードでsetuptools.setup(package=[])関数内のパッケージが明示的にリストされているため、私のコードは失敗しました。サブパッケージ/ディレクトリが含まれていませんでした。さらに深く掘り下げると、 setuptools にはfind_namespace_package()サブパッケージの追加にも役立つ機能があることがわかりました

編集

へのリンクfind_namespace_packages()(setuptools以降のバージョン40.1.0): https://setuptools.readthedocs.io/en/latest/setuptools.html#find-namespace-packages

編集 (2019 年 8 月 9 日):

答えを完成させるために、例を挙げて再構成しましょう。

次の解決策は、暗黙的な名前空間パッケージをサポートする Python 3.3+ を想定しています。

Python バージョン以降のソリューションを探しているので、3.5提供されているコード サンプルを取り上げて、さらに詳しく説明しましょう。

次のように仮定します。

名前空間/Python パッケージ名:org

配布パッケージ: org_clientorg_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/

更新されたピップ リストには、次のように表示されます。

(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_clientorディレクトリがないことがわかりますorg_common。これらは名前空間パッケージとして解釈されます。

(venv) $ cd venv/lib/python3.5/site-packages/org/
(venv) $ ls -l
client/
common/
...
于 2019-07-24T17:00:27.637 に答える