48

複数のファイルをモジュール__init__.pyに整理する方法はありますか?

理由: モジュールは名前空間のレイヤーが少ないため、パッケージよりも使いやすいです。

通常、これはパッケージを作成します。問題はパッケージにあります。「パッケージをインポート」すると、空の名前空間が得られます。次に、ユーザーは "from thepackage import *" (眉をひそめる) を使用するか、何が含まれているかを正確に把握して、使用可能な名前空間に手動でプルする必要があります。

私が望むのは、ユーザーが「パッケージをインポート」して、このようにきれいな名前空間を作成し、プロジェクトに関連する関数とクラスを公開して使用することです。

current_module
\
  doit_tools/
  \
   - (class) _hidden_resource_pool
   - (class) JobInfo
   - (class) CachedLookup
   - (class) ThreadedWorker
   - (Fn) util_a
   - (Fn) util_b
   - (Fn) gather_stuff
   - (Fn) analyze_stuff

メンテナの仕事は、異なるファイルで同じ名前を定義しないようにすることです。これは、プロジェクトが私のように小さい場合は簡単なはずです。

from doit_stuff import JobInfoまた、クラスを含むモジュールではなく、クラスを取得できるようにするとよいでしょう。

すべてのコードが 1 つの巨大なファイルにある場合、これは簡単ですが、物事が大きくなり始めたときに整理するのが好きです。私がディスクに持っているものは、次のようなものです。

place_in_my_python_path/
  doit_tools/
    __init__.py
    JobInfo.py
      - class JobInfo:
    NetworkAccessors.py
      - class _hidden_resource_pool:
      - class CachedLookup:
      - class ThreadedWorker:
    utility_functions.py
      - def util_a()
      - def util_b()
    data_functions.py
      - def gather_stuff()
      - def analyze_stuff()

ファイルが巨大でナビゲートできないように、それらを分離するだけです。それらはすべて関連していますが、誰か (おそらく私) がすべてをインポートせずにクラスを自分で使用したい場合があります。

さまざまなスレッドでいくつかの提案を読みました。これを行う方法について見つけることができる各提案で何が起こるかを次に示します。

使用しない場合、__init__.py Python は sys.path からフォルダーに降りないため、何もインポートできません。

空白__init__.pyを使用すると、import doit_tools何も入っていない空の名前空間になります。ファイルがインポートされないため、使用が難しくなります。

にサブモジュールをリストすると__all__、(眉をひそめた?)from thing import *構文を使用できますが、すべてのクラスが再び不要な名前空間バリアの背後にあります。from x import *ユーザーは、(1)の代わりにを使用する必要があることを認識しimport x、(2) 線幅スタイルの制約に合理的に従うことができるまで、クラスを手動で再シャッフルする必要があります。

ステートメントを追加するfrom thatfile import X__init__.pyと、より近くなりますが、名前空間の競合 (?) と、そこに入れたくなかったもののための追加の名前空間があります。以下の例では、次のことがわかります。

  1. クラス JobInfo は、名前が同じであるため、JobInfo という名前のモジュール オブジェクトを上書きしました。JobInfo の型が であるため、どういうわけか Python はこれを理解でき<class 'doit_tools.JobInfo.JobInfo'>ます。(doit_tools.JobInfo はクラスですが、doit_tools.JobInfo.JobInfo は同じクラスです...これは絡み合っており、非常に悪いように見えますが、何も壊れていないようです。)
  2. 各ファイル名は doit_tools ネームスペースに入り込むため、誰かがモジュールの内容を見ている場合、より混乱を招きます。新しい名前空間を定義するのではなく、 doit_tools.utility_functions.py にいくつかのコードを保持させたい。

.

current_module
\
  doit_tools/
  \
   - (module) JobInfo
      \
       - (class) JobInfo
   - (class) JobInfo
   - (module) NetworkAccessors
      \
       - (class) CachedLookup
       - (class) ThreadedWorker
   - (class) CachedLookup
   - (class) ThreadedWorker
   - (module) utility_functions
      \
       - (Fn) util_a
       - (Fn) util_b
   - (Fn) util_a
   - (Fn) util_b
   - (module) data_functions
      \
       - (Fn) gather_stuff
       - (Fn) analyze_stuff
   - (Fn) gather_stuff
   - (Fn) analyze_stuff

また、データ抽象化クラスだけをインポートすると、「from doit_tools import JobInfo」を実行すると、予想とは異なる結果が得られます。

current_namespace
\
 JobInfo (module)
  \
   -JobInfo (class)

instead of:

current_namespace
\
 - JobInfo (class)

では、これは Python コードを整理するための間違った方法なのでしょうか? そうでない場合、関連するコードを分割してモジュールのような方法で収集する正しい方法は何ですか?

おそらく最良のシナリオは、'from doit_tools import JobInfo' を実行すると、パッケージを使用している人にとって少し混乱することでしょうか?

おそらく、コードを使用する人々が次のことを行うように、「api」と呼ばれる python ファイルですか?:

import doit_tools.api
from doit_tools.api import JobInfo

============================================

コメントへの応答の例:

Pythonパスにあるフォルダー「foo」内の次のパッケージコンテンツを取得します。

foo/__init__.py

__all__ = ['doit','dataholder','getSomeStuff','hold_more_data','SpecialCase']
from another_class import doit
from another_class import dataholder
from descriptive_name import getSomeStuff
from descriptive_name import hold_more_data
from specialcase import SpecialCase

foo/specialcase.py

class SpecialCase:
    pass

foo/more.py

def getSomeStuff():
    pass

class hold_more_data(object):
    pass

foo/stuff.py

def doit():
    print "I'm a function."

class dataholder(object):
    pass

これを行う:

>>> import foo
>>> for thing in dir(foo): print thing
... 
SpecialCase
__builtins__
__doc__
__file__
__name__
__package__
__path__
another_class
dataholder
descriptive_name
doit
getSomeStuff
hold_more_data
specialcase

another_classまたdescriptive_name、名前空間の下に doit() などの余分なコピーがあります。

Data.py という名前のファイル内に Data という名前のクラスがある場合、「from Data import Data」を実行すると、名前空間の競合が発生します。これは、Data がモジュール Data 内にある現在の名前空間のクラスであるためです。現在の名前空間。(しかし、Pythonはこれを処理できるようです。)

4

4 に答える 4

17

あなたはそれを行うことができますが、それは本当に良い考えではなく、Pythonモジュール/パッケージが動作するはずの方法と戦っています. 適切な名前をインポート__init__.pyすることで、パッケージの名前空間でアクセスできるようになります。モジュール名を削除すると、それらにアクセスできなくなります。(なぜそれらを削除する必要があるのか​​ については、この質問を参照してください)。したがって、次のようなものを使用して、必要なものに近づけることができます(__init__.py):

from another_class import doit
from another_class import dataholder
from descriptive_name import getSomeStuff
from descriptive_name import hold_more_data
del another_class, descriptive_name
__all__ = ['doit', 'dataholder', 'getSomeStuff', 'hold_more_data']

ただし、これにより、以降の への試行が中断されimport package.another_classます。一般に、をそのモジュールへのインポート可能な参照としてアクセス可能にしpackage.moduleないと、 から何もインポートできません(ただし、 を使用すると をブロックできます)。package.module__all__from package import module

より一般的には、クラス/関数ごとにコードを分割することで、Python パッケージ/モジュール システムに反対しています。通常、Python モジュールには、ユニットとしてインポートしたいものを含める必要があります。便宜上、サブモジュール コンポーネントを最上位のパッケージ名前空間に直接インポートすることは珍しくありませんが、逆に --- サブモジュールを非表示にして、最上位のパッケージ名前空間を介してのみその内容へのアクセスを許可しようとすると --- リードすることになります問題に。さらに、モジュールのパッケージ名前空間を「クレンジング」しようとしても何も得られません。これらのモジュールはパッケージ名前空間にあるはずです。それが彼らの所属です。

于 2012-09-22T03:02:00.013 に答える
4

たとえば、次のように定義__all__ = ['names', 'that', 'are', 'public']します。__init__.py

__all__ = ['foo']

from ._subpackage import foo

実際の例: numpy/__init__.py.


Python パッケージがどのように機能するかについて、いくつかの誤解があります。

を使用しないと__init__.py、Python が sys.path からフォルダーに降りないため、何もインポートできません。

__init__.pyディレクトリを Python パッケージを含むものとしてマークするには、Python 3.3 より古い Python バージョンのファイルが必要です。

空白を使用すると、__init__.pydoit_tools をインポートすると、何も含まれていない空の名前空間になります。ファイルがインポートされないため、使用が難しくなります。

インポートを妨げません:

from doit_tools import your_module

期待どおりに動作します。

にサブモジュールをリストすると__all__、(眉をひそめた?)from thing import *構文を使用できますが、すべてのクラスが不要な名前空間バリアの背後にあります。from x import *ユーザーは、(1)の代わりにを使用する必要があることを知っている必要がありますimport x。(2) 線幅スタイルの制約に合理的に従うことができるまで、手動でクラスを再シャッフルする必要があります。

(1) ユーザー (ほとんどの場合) は、インタラクティブな Python シェルの外で使用しないでください。from your_package import *

()(2)長いインポート行を分割するために使用できます:

from package import (function1, Class1, Class2, ..snip many other names..,
                     ClassN)

from thatfile import Xステートメントをに追加すると__init__.py、より近くなりますが、名前空間の競合 (?) と、そこに入れたくなかったもののための追加の名前空間があります。

名前空間の競合 (同じ名前の異なるオブジェクト) を解決するのはあなた次第です。名前は任意のオブジェクトを参照できます: 整数、文字列、パッケージ、モジュール、クラス、関数など他のすべての場合における名前バインディングの使用に関して。

名前を非公開としてマークするには、名前の前_package/_nonpublic_module.py.

于 2012-09-22T02:59:10.280 に答える
-2

Python は Java ではありません。モジュール ファイル名は、クラス名と同じである必要はありません。実際、Python はモジュール ファイル名にすべて小文字を使用することを推奨しています。

また、「from math import sqrt」は、数学ではなく名前空間にのみ sqrt を追加します。

于 2012-09-22T04:23:38.130 に答える