pythonのモジュールシステムを深く理解する
5825 ワード
pythonのモジュールシステムを深く理解する
pythonエンジニアリングのコードは、モジュールとパッケージの形式で組織されています.要約すると、pythonのファイルは実行時にモジュールに対応し、init.pyを含むフォルダはパッケージに対応します.1つのモジュールの他のモジュールまたはパッケージ内のコンテンツへの参照はimportキーワードによって実現される.規模の大きいプロジェクト開発では,異なるモジュール,パケット間の参照関係,および検索パスがしばしば悩まされる.したがって,pythonモジュールの本質を認識し,python下位importの処理メカニズムを整理することは有意義である.
本文はpythonのモジュールシステムを4つの部分に分けて整理する.第1部ではpython moduleオブジェクトの具体的なデータ構造について説明します.第2部ではpythonのパッケージとモジュールについて説明します.第3部はimport文の下位論理を詳しく整理する.最後のセクションでは、pythonのimportメカニズムをめぐって、開発中の詳細について説明します.
PyModuleObjectオブジェクト
pyファイルに対応するモジュールでも、フォルダに対応するパッケージでも、pythonの最下位実装では、PyModuleObjectタイプのオブジェクトで記述されます.
PyModuleObjectオブジェクトの本体は辞書オブジェクトであることがわかります.ここから私たちは見ることができる.実はpython moduleはpython辞書です.より高いレベルで見ると、python moduleは主に様々なオブジェクト(変数、関数、クラス)を保存するためのネーミングスペースを提供しています.1つのmoduleオブジェクトが持つネーミングスペースに保存されているコンテンツはdir関数などの方法でアクセスできます
モジュールとパッケージ
pythonプロジェクトでは、実行時にメモリのモジュールオブジェクトがpyファイルに対応し、パッケージオブジェクトがpythonプロジェクトの1つに対応しています.init __. pyファイルのフォルダ.
モジュールオブジェクトでもパッケージオブジェクトでも、pythonの下部にPyModuleObjectオブジェクトを一括して保存します.
ご存じのように、pyファイルに対応するモジュールでは、最初のimportの過程で論理が実行されます.
これに対応して、1つのパケットは、最初のimportの過程で、パケット中__を実行します.init __.pyの論理.
importの内幕
python実装では、importメカニズムのコア実装がimport_にあります.module_レベル関数です.
import文実行環境の取得
import_module_level関数では、最初のコアの操作はimport文の実行環境を取得することです.この操作はget_parent関数で得られます.importの実行環境とは,現在のimport文が存在するモジュールが属するpackageである.
たとえば、test_というパッケージが現在あるとします.pkg、pyファイルtest_を含むmod.py.test_mod.pyにはimport文が含まれています.
仮想マシンがimport文を実行し、import_をトリガーするとmodule_level関数呼び出し後、get_parent操作が返す実行環境、すなわちtest_pkg.前述したようにtest_pkgはこのときもPyModuleObjectによって記述される.
下に続けてget_を探るparentは,この関数の大部分が現在のモジュールの__を解析することによって発見される.name __ 属性は上位packageの名前を取得し、グローバルsys.modulesはpackageオブジェクトにクエリーします.
通常test_mod.py対応モジュールその_name __ 属性の値は「test_pkg.test_mod」です.最後の「.」記号の位置によって、上位package名を取得できます.
特別な状況はtest_modが起動モジュール(python test_mod.pyを実行)として設定されている場合test_modモジュールの_name __ 属性値は「_main_」です.このとき、その上位実行環境はPy_に設定されるNone.
importパスチェーンに沿ってパッケージとモジュールを順次ロード
実行環境を取得したら、パッケージとモジュールのロードを正式に開始します.文import x.y.zの場合.
x->y->zはチェーンテーブルと見なすことができますimport_module_level関数では、チェーンテーブルの遍歴に似た操作が行われ、ノードごとにロード操作が実行されます.
load_nextのコア関数はimport_submodule関数
import_submodule関数では、
まず、ターゲット名に対応するPyModuleObjectオブジェクトがsysにロードするかどうかを検出する.modules辞書にあります.
もしsys.modulesにこのモジュールオブジェクトが見つからない場合は、このモジュールがグローバルで初めてロードされたことを示します.次に、次の操作を行います.現在受信されているpackageオブジェクトの__に従ってpath __ プロパティは、ターゲットパッケージまたはモジュールの検索パスを取得します.受信packageオブジェクトがPyNoneである場合(_name_=="_main_")などの場合)、パスはNULLです. pathを取得した後、python下位層はfind_を通過するmodule関数は、ターゲットパッケージまたはモジュールのファイルシステムハンドルを検索します.find_moduleのモジュール検索ポリシーは、まず受信path(すなわち親packageのパス)でターゲットを検索し、pathがNULLである場合、またはpathで検索に失敗した場合、sysの順に検索する.pathリストに表示されるパスの下で検索します. load_moduleは、モジュールまたはパッケージの実際のロード動作を実行します.load_Moduleは、ターゲットPyModuleObjectオブジェクトの予想されるタイプに応じて異なる操作を行います: add_submodule関数は、親PyModuleObjectにloadが入力したターゲットPyModuleObjectを挿入します.
pythonエンジニアリングのコードは、モジュールとパッケージの形式で組織されています.要約すると、pythonのファイルは実行時にモジュールに対応し、init.pyを含むフォルダはパッケージに対応します.1つのモジュールの他のモジュールまたはパッケージ内のコンテンツへの参照はimportキーワードによって実現される.規模の大きいプロジェクト開発では,異なるモジュール,パケット間の参照関係,および検索パスがしばしば悩まされる.したがって,pythonモジュールの本質を認識し,python下位importの処理メカニズムを整理することは有意義である.
本文はpythonのモジュールシステムを4つの部分に分けて整理する.第1部ではpython moduleオブジェクトの具体的なデータ構造について説明します.第2部ではpythonのパッケージとモジュールについて説明します.第3部はimport文の下位論理を詳しく整理する.最後のセクションでは、pythonのimportメカニズムをめぐって、開発中の詳細について説明します.
PyModuleObjectオブジェクト
pyファイルに対応するモジュールでも、フォルダに対応するパッケージでも、pythonの最下位実装では、PyModuleObjectタイプのオブジェクトで記述されます.
typedef struct {
PyObject_Head
PyObject *md_dict
}PyModuleObject;
PyModuleObjectオブジェクトの本体は辞書オブジェクトであることがわかります.ここから私たちは見ることができる.実はpython moduleはpython辞書です.より高いレベルで見ると、python moduleは主に様々なオブジェクト(変数、関数、クラス)を保存するためのネーミングスペースを提供しています.1つのmoduleオブジェクトが持つネーミングスペースに保存されているコンテンツはdir関数などの方法でアクセスできます
import xxx
dir(xxx)
モジュールとパッケージ
pythonプロジェクトでは、実行時にメモリのモジュールオブジェクトがpyファイルに対応し、パッケージオブジェクトがpythonプロジェクトの1つに対応しています.init __. pyファイルのフォルダ.
モジュールオブジェクトでもパッケージオブジェクトでも、pythonの下部にPyModuleObjectオブジェクトを一括して保存します.
>>>import os #
>>> import os.path #
>>> type(os)
>>> type(os.path)
ご存じのように、pyファイルに対応するモジュールでは、最初のimportの過程で論理が実行されます.
これに対応して、1つのパケットは、最初のimportの過程で、パケット中__を実行します.init __.pyの論理.
importの内幕
python実装では、importメカニズムのコア実装がimport_にあります.module_レベル関数です.
static PyObject *import_module_level(char *name, PyObject *globals, PyObject *locals,PyObject *fromlist, int level)
{
// import
parent = get_parent(globals, buf, &buflen, level);
...
// import ,
head = load_next(parent, level < 0 ? Py_None : parent, &name, buf,&buflen);
tail = head;
while (name) {
next = load_next(tail, tail, &name, buf, &buflen);
tail = next;
}
...
}
import文実行環境の取得
import_module_level関数では、最初のコアの操作はimport文の実行環境を取得することです.この操作はget_parent関数で得られます.importの実行環境とは,現在のimport文が存在するモジュールが属するpackageである.
たとえば、test_というパッケージが現在あるとします.pkg、pyファイルtest_を含むmod.py.test_mod.pyにはimport文が含まれています.
# test_mod.py
import x.y.z
仮想マシンがimport文を実行し、import_をトリガーするとmodule_level関数呼び出し後、get_parent操作が返す実行環境、すなわちtest_pkg.前述したようにtest_pkgはこのときもPyModuleObjectによって記述される.
下に続けてget_を探るparentは,この関数の大部分が現在のモジュールの__を解析することによって発見される.name __ 属性は上位packageの名前を取得し、グローバルsys.modulesはpackageオブジェクトにクエリーします.
通常test_mod.py対応モジュールその_name __ 属性の値は「test_pkg.test_mod」です.最後の「.」記号の位置によって、上位package名を取得できます.
特別な状況はtest_modが起動モジュール(python test_mod.pyを実行)として設定されている場合test_modモジュールの_name __ 属性値は「_main_」です.このとき、その上位実行環境はPy_に設定されるNone.
importパスチェーンに沿ってパッケージとモジュールを順次ロード
実行環境を取得したら、パッケージとモジュールのロードを正式に開始します.文import x.y.zの場合.
x->y->zはチェーンテーブルと見なすことができますimport_module_level関数では、チェーンテーブルの遍歴に似た操作が行われ、ノードごとにロード操作が実行されます.
// import ,
head = load_next(parent, level < 0 ? Py_None : parent, &name, buf,&buflen);
tail = head;
while (name) {
next = load_next(tail, tail, &name, buf, &buflen);
tail = next;
}
load_nextのコア関数はimport_submodule関数
static PyObject *import_submodule(PyObject *mod, char *subname, char *fullname){
// sys.modules
if ((m = PyDict_GetItemString(modules, fullname)) != NULL) {
Py_INCREF(m);
}
// mod.__path__
if (mod == Py_None)
path = NULL;
else {
path = PyObject_GetAttrString(mod, "__path__");
if (path == NULL) {
PyErr_Clear();
Py_INCREF(Py_None);
return Py_None;
}
}
//
fdp = find_module(fullname, subname, path, buf, MAXPATHLEN+1,
&fp, &loader);
//
m = load_module(fullname, fp, buf, fdp->type, loader);
Py_XDECREF(loader);
// package
if (!add_submodule(mod, m, fullname, subname, modules)) {
Py_XDECREF(m);
m = NULL;
}
}
return m;
}
import_submodule関数では、
まず、ターゲット名に対応するPyModuleObjectオブジェクトがsysにロードするかどうかを検出する.modules辞書にあります.
もしsys.modulesにこのモジュールオブジェクトが見つからない場合は、このモジュールがグローバルで初めてロードされたことを示します.次に、次の操作を行います.
load_module(){
switch(type){
case py_source:
// py
1.
2.
3. module
4. global module->m_dict
5. module
break
case py_complied:
// pyc,
1.
2. module
3. global module->m_dict
4. module
break
case c_extension:
// c
1.
2. initmodule , Py_InitModule api
break
case pkg:
// package
1.
2. __init__.py, load_module(__init__.py).
break
}
}