28

プロジェクト内で一連の 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.pyまたはなどのファイルを配置することをお勧めします。)common.py

4

3 に答える 3

29

単純な「ランチャー」スクリプトをプロジェクトの最上位に置き、各サブプロジェクト フォルダーをパッケージにすることをお勧めします。パッケージ内のモジュールは、相互にインポートすることも、共通コードを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 は属性アクセスを示すと想定しているため、識別子 (モジュール名など) に a を含めることはできません。つまり、これらのモジュールはインポートできませんでした。(これはサンプル ファイルのみのアーティファクトであり、実際のコードにあるものではないと推測しています。)

于 2013-08-13T06:05:05.557 に答える
1

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

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

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

サブプロジェクト パッケージをセットアップするメイン スクリプトまたは共有スクリプトを使用することもできます。

于 2013-08-06T18:04:10.007 に答える
0

私は最近、Python 3.9 で動作するように見えるこの手法を発見しました。これは Blckknght の回答と大差ありませんが、サブプロジェクトごとにスクリプトを実行する必要がなくなりprojectrootます。

projectroot
  |
  |- common
  |    |
  |    `- merger.py
  |
  |- subproject1
  |    |
  |    `- __main__.py
  |
  |- subproject2
  |    |
  |    |- __main__.py
  |    `- matcher.py

projectrootディレクトリから、で実行します

python -m subproject1
python -m subproject2

事実上、subproject1およびsubproject2を「アプリケーション バンドル」として扱っています。

subproject1 と subproject2 の両方がimport common.merger、インポート パスをハッキングするなどの特別な手段なしで直接実行できるようです。

重要な場合とそうでない場合がある 1 つの不具合があります。各サブプロジェクト内では、インポート ルート ディレクトリがprojectroot存在するため、プロジェクト自体内で絶対インポートまたは明示的な相対インポートを使用する必要があります。

import .matcher

また

import subproject2.matcher

だがしかし

import matcher # ModuleNotFoundError: No module named 'matcher'

-mもう 1 つの欠点は、アプリケーションを実行するためにおそらくわかりにくいフラグが必要になることです。

于 2021-07-01T09:18:58.900 に答える