web-dev-qa-db-ja.com

Python:スクリプトファミリー間で共通のコードを共有する

私はプロジェクト内でPythonスクリプトのファミリーを書いています。各スクリプトは、次のようにプロジェクトのサブディレクトリ内にあります。

projectroot
  |
  |- subproject1
  |    |
  |    |- script1.main.py
  |    `- script1.merger.py
  |
  |- subproject2
  |    |
  |    |- script2.main.py
  |    |- script2.matcher.py
  |    `- script2.merger.py
  |
  `- subproject3
       |
       |- script3.main.py
       |- script3.converter.py
       |- script3.matcher.py
       `- script3.merger.py

現在、いくつかのスクリプトはいくつかのコードを共有しています。共有コードはプロジェクト自体の一部と見なすのが最適であり、個別にコンパイルしてライブラリを作成したり、サイト全体のPYTHONPATHにドロップしたりするものではありません。そのコードは、projectrootディレクトリ自体や、projectrootというcommonの子ディレクトリ(おそらく)など、さまざまな場所に配置できます。

ただし、これまで考えてきたほとんどの方法では、サブプロジェクトから空の__init__.pyファイルを使用してパッケージを作成し、相対インポートを使用します(または、すべてのサブプロジェクトでsys.pathを冗長に操作します。さらに悪いことに、このスクリプトファミリを中心にパッケージ構造を構築すると、拒否された PEP-3122 からの次の警告に違反します。

注意!このPEPは拒否されました。 Guidoは、パッケージ内で実行中のスクリプトをアンチパターンと見なします。

パッケージ内のスクリプトがパターン化されていない場合、同じプロジェクトに共通のコードを保持するように設定するにはどうすればよいですか?または、モジュールとパッケージベースのシステムはここで受け入れられますか?最もクリーンなアプローチはどれですか? (FWIW「実際の」サブプロジェクトの兄弟であるユーティリティディレクトリを作成するのではなく、プロジェクトのルートディレクトリにshared.pycommon.pyなどのファイルを作成したいと思います。)

24
Ray Toal

プロジェクトのトップレベルに簡単な「ランチャー」スクリプトを配置し、各サブプロジェクトフォルダーをパッケージにすることをお勧めします。パッケージ内のモジュールは相互にインポートすることも、共通のコードをcommonパッケージに分解することもできます。

さまざまなmergerモジュールを共有バージョンにリファクタリングできると仮定すると、構造は次のようになります。

projectroot
  |- script1.py # launcher scripts, see below for example code
  |- script2.py
  |- script3.py
  |
  |- common
  |    |- __init__.py
  |    |- merger.py # from other packages, use from ..common import merger to get this
  |
  |- subproject1
  |    |- __init__.py # this can be empty
  |    |- script1_main.py
  |
  |- subproject2
  |    |- __init__.py
  |    |- script2_main.py
  |    |- script2_matcher.py
  |
  |- subproject3
       |- __init__.py
       |- script3_main.py
       |- script3_converter.py
       |- script3_matcher.py

ランチャースクリプトは非常に単純です。

from subproject1 import script1_main

if __name__ == "__main__":
    script1_main.main()

つまり、適切な「scriptN_main」モジュールをインポートして、その中で関数を実行するだけです。 mainモジュールはコンパイルされたバイトコードを.pycファイルにキャッシュできますが、スクリプトはキャッシュされないため、単純なスクリプトを使用すると、スクリプトの起動速度に若干の利点があります。

注:モジュールの名前を変更し、_文字を.文字に置き換えました。 Pythonは属性アクセスを示すことを期待しているため、識別子(モジュール名など)に.を含めることはできません。つまり、これらのモジュールをインポートすることはできません。(これはサンプルファイルのアーティファクトであり、実際のコードにあるものではないと思います。)

27
Blckknght

setuptools を使用して、両方のスクリプトライブラリとライブラリを配布してください。

例えば.

from setuptools import setup

setup(
    # other arguments here... (e.g. packages / package_dir)
    entry_points = {
        'console_scripts': [
            'script1 = subproject1.script1:main',
            'script2 = subproject2.script2:main',
        ],
    }
)

すべてのコードをライブラリとして記述でき、エントリポイントを持つために個別のモジュールを必要としない場合、これはあなたのためのツールです。スクリプトがある場合はそれでも問題ありませんが、参照できるmain関数が必要になります(上記の例を参照)

0
dnozay

私の好みは、サブプロジェクトをライブラリ/パッケージとして持つ、個別の「bin」または「scripts」ディレクトリです。

projectroot
  |
  |- scripts
  |
  |- lib
  |    |
  |    `- matcher.py
  |    `- merger.py
  |    `- subproject1
  |    `- subproject2
  |    `- subproject3

スクリプトであるという考えは、通常のパッケージとして必要なサブプロジェクトをそれぞれ参照できます。また、サブプロジェクトはインポートで相互に参照することもできます。

次に、サブプロジェクトパッケージを設定するメインスクリプトまたは共有スクリプトを作成することもできます。

0
Matt S