19

効率のために、Python がそのオブジェクトのヒープ (および名前空間のシステムですが、多かれ少なかれ明確です) でどのように機能するかを理解しようとしています。したがって、基本的に、オブジェクトがいつヒープにロードされるか、そこにいくつあるか、それらがどれくらい生きているかなどを理解しようとしています.

そして私の質問は、パッケージを操作してそこから何かをインポートするときです:

from pypackage import pymodule

どのオブジェクトがメモリ(Python インタープリターのオブジェクト ヒープ) にロードされますか? そしてより一般的に:何が起こるのですか?:)

上記の例は次のようなことをしていると思います:パッケージのオブジェクトがpypackageメモリに作成され(パッケージに関する情報が含まれていますが、それほど多くはありません)、モジュールpymoduleがメモリにロードされ、その参照がローカル名前空間に作成されました. ここで重要なことはpypackage、明示的に記述されていない限り(または他のオブジェクト) の他のモジュールがメモリ内に作成されていないことです (モジュール自体、またはパッケージの初期化のトリックとフックのどこかで、私がよく知らない) . 最後に記憶に残る唯一の大きなことはpymodule(つまり、モジュールがインポートされたときに作成されたすべてのオブジェクト)。そうですか?誰かがこの問題を少し明確にしてくれれば幸いです。多分あなたはそれについてのいくつかの有用な記事をアドバイスできますか? (ドキュメンテーションはより具体的なものをカバーしています)

モジュールのインポートに関する同じ質問に対して、次のことがわかりました。

Python がモジュールをインポートするとき、まずモジュール レジストリ (sys.modules) をチェックして、モジュールが既にインポートされているかどうかを確認します。その場合、Python は既存のモジュール オブジェクトをそのまま使用します。

それ以外の場合、Python は次のようにします。

  • 新しい空のモジュール オブジェクトを作成します (これは基本的に辞書です)。
  • そのモジュール オブジェクトを sys.modules ディクショナリに挿入します。
  • モジュール コード オブジェクトを読み込みます (必要に応じて、最初にモジュールをコンパイルします)。
  • 新しいモジュールの名前空間でモジュール コード オブジェクトを実行します。コードによって割り当てられたすべての変数は、モジュール オブジェクトを介して使用できます。

また、パッケージについても同様の説明をいただければ幸いです。

ちなみに、パッケージではモジュール名がsys.modules奇妙なことに追加されます:

>>> import sys
>>> from pypacket import pymodule
>>> "pymodule" in sys.modules.keys()
False
>>> "pypacket" in sys.modules.keys()
True

また、同じ問題に関する実践的な質問もあります。

さまざまなプロセスやプログラムで使用される可能性のある一連のツールを構築するとき。そして、それらをモジュールに入れました。そこで宣言された関数を 1 つだけ使用したい場合でも、完全なモジュールをロードするしかありません。私が見たように、小さなモジュールを作成してパッケージに入れることで、この問題を軽減することができます (モジュールの 1 つだけをインポートしたときにパッケージがすべてのモジュールをロードしない場合)。

そのようなライブラリを Python で作成するより良い方法はありますか? (モジュール内に依存関係がない単なる関数で。)C拡張で可能ですか?

PS 長い質問で申し訳ありません。

4

2 に答える 2

12

ここにいくつかの異なる質問があります。。。

パッケージのインポートについて

パッケージをインポートする場合、手順の順序はモジュールをインポートする場合と同じです。唯一の違いは、パッケージのコード(つまり、「モジュールコードオブジェクト」を作成するコード)がパッケージのコードであるということ__init__.pyです。

そうです、パッケージのサブモジュールは、__init__.py明示的にロードされない限りロードされません。そうすると、もちろんパッケージから他のモジュールをインポートしない限り、ロードされるfrom package import moduleだけです。module

sys.modulesパッケージからロードされたモジュールの名前

パッケージからモジュールをインポートする場合、追加される名前はsys.modules「修飾名」であり、モジュール名と、インポート元のパッケージのドットで区切られた名前を指定します。したがって、そうするとfrom package.subpackage import mod、に追加されるのsys.modules"package.subpackage.mod"です。

モジュールの一部のみをインポートする

通常、1つの関数だけでなく、モジュール全体をインポートする必要があることは大きな問題ではありません。あなたはそれが「痛みを伴う」と言いますが、実際にはほとんどありません。

あなたが言うように、関数に外部依存関係がない場合、それらは純粋なPythonであり、それらのロードにはそれほど時間はかかりません。通常、モジュールのインポートに時間がかかる場合は、他のモジュールをロードするためです。つまり、外部依存関係があり、すべてをロードする必要があります。

モジュールにモジュールのインポート時に発生する高価な操作があるが(つまり、それらはグローバルなモジュールレベルのコードであり、関数内ではない)、モジュール内のすべての関数の使用に必須ではない場合は、必要に応じて、モジュールを再設計して、そのロードを後で実行するまで延期します。つまり、モジュールが次のようなことを行う場合:

def simpleFunction():
    pass

# open files, read huge amounts of data, do slow stuff here

あなたはそれをに変更することができます

def simpleFunction():
    pass

def loadData():
    # open files, read huge amounts of data, do slow stuff here

someModule.loadData()次に、「データをロードしたいときに電話する」と人々に伝えます。または、あなたが提案したように、モジュールの高価な部分をパッケージ内の独自の個別のモジュールに入れることができます。

モジュールがすでに十分に大きく、より小さなモジュールに合理的に分割できる場合を除いて、モジュールのインポートがパフォーマンスに大きな影響を与えることはありませんでした。それぞれが1つの関数を含む大量の小さなモジュールを作成しても、それらすべてのファイルを追跡する必要があるというメンテナンスの問題を除いて、何も得られない可能性があります。これがあなたに違いをもたらす特定の状況が実際にありますか?

また、最後のポイントに関しては、私が知る限り、純粋なPythonモジュールの場合と同じオールオアナッシングロード戦略がC拡張モジュールに適用されます。明らかに、Pythonモジュールの場合と同様に、物事をより小さな拡張モジュールに分割することはできますがfrom someExtensionModule import someFunction、その拡張モジュールの一部としてパッケージ化された残りのコードも実行せずに実行することはできません。

于 2013-03-07T19:47:16.553 に答える
2

モジュールのインポート時に発生するおおよその手順は次のとおりです。

  1. Python はモジュールを見つけようとし、sys.modules見つかった場合は何もしません。パッケージは完全な名前でキー付けされているため、がpymoduleから欠落している間、そこに存在します (そして として取得できます。sys.modulespypacket.pymodulesys.modules["pypacket.pymodule"]

  2. Python は、モジュールを実装するファイルを見つけます。モジュールがパッケージの一部である場合は、x.y構文によって決定され、 and (またはさらにサブパッケージ)xの両方を含むという名前のディレクトリが検索されます。配置された一番下のファイルは、ファイル、ファイル、または / ファイルのいずれかになります。モジュールに適合するファイルが見つからない場合は、が発生します。__init__.pyy.py.py.pyc.so.pydImportError

  3. 空のモジュール オブジェクトが作成され、モジュール内のコードがモジュールの実行__dict__名前空間として実行されます。1

  4. モジュール オブジェクトは に配置sys.modulesされ、インポーターの名前空間に挿入されます。

ステップ 3 は、「オブジェクトがメモリに読み込まれる」ポイントです。問題のオブジェクトはモジュール オブジェクトであり、その .xml に含まれる名前空間の内容です__dict__def通常、この dict には、各モジュールに通常含まれるすべての、class、およびその他のトップレベルのステートメントを実行することの副作用として作成されたトップレベルの関数とクラスが含まれます。

上記は のデフォルトの実装のみを記述していることに注意してくださいimport組み込みのオーバーライドや__import__import hooksの実装など、インポートの動作をカスタマイズする方法はいくつかあります。


1モジュール ファイルが.pyソース ファイルの場合、最初にメモリにコンパイルされ、コンパイルの結果のコード オブジェクトが実行されます。の場合.pyc、コード オブジェクトはファイルの内容を逆シリアル化することによって取得されます。モジュールが共有ライブラリ.soまたは.pyd共有ライブラリの場合、オペレーティング システムの共有ライブラリ ロード機能を使用してロードされinit<module>、モジュールを初期化するために C 関数が呼び出されます。

于 2013-03-07T19:42:01.930 に答える