5. インポートシステム¶
ある 1 つの module にある Python コードから他のモジュールを インポート することで、そこにあるコードへアクセスできるようになります。 import 文はインポート機構を動かす最も一般的な方法ですが、それが唯一の方法ではありません。 importlib.import_module() や組み込みの __import__() といった関数を使っても、インポート機構を動かすことができます。
import 文は 2 つの処理を連続して行っています; ある名前のモジュールを探し、その検索結果をローカルスコープの名前に束縛します。 import 文の検索処理は、適切な引数で __import__() 関数を呼び出すこととして定義されています。 __import__() の戻り値は import 文の名前束縛処理の実行で使われます。名前束縛処理の厳密な詳細は import 文を参照してください。
__import__() を直接呼び出すとモジュールの検索のみが行われ、見つかった場合、モジュールの作成処理が行われます。親パッケージのインポートや (sys.modules を含む) 様々なキャッシュの更新などの副作用は起きるかもしれませんが、 import 文のみが名前束縛処理を行います。
import 文が実行されるときには、標準の組み込み関数 __import__() が呼ばれます。インポートシステムを呼び出すその他の (importlib.import_module() 関数のような) メカニズムは、__import__() の呼び出しをバイパスして独自のインポート・セマンティクスを実装している可能性があります。
モジュールが初めてインポートされるとき、 Python はそのモジュールを検索し、見付かった場合、モジュールオブジェクトを作成し、初期化します [1] 。その名前のモジュールが見付からなかった場合、 ModuleNotFoundError が送出されます。 Python には、インポート機構が実行されたときに名前からモジュールを検索する様々な戦略が実装されています。これらの戦略は、これ以降の節で解説される様々なフックを使って、修正したり拡張したりできます。
バージョン 3.3 で変更: インポートシステムが PEP 302 の第 2 フェーズの完全な実装へ更新されました。もはや暗黙的なインポート機構はありません - インポート機構全体は sys.meta_path を通して公開されています。加えて、ネイティブの名前空間パッケージのサポートは実装されています (PEP 420 を参照) 。
5.1. importlib¶
importlib モジュールはインポート機構とやり取りするための便利な API を提供します。例えば importlib.import_module() は、インポート機構を実行するための組み込みの __import__() よりもシンプルで推奨される API を提供します。より詳細なことは importlib ライブラリのドキュメントを参照してください。
5.2. パッケージ¶
Python にはモジュールオブジェクトの種類は 1 種類しかなく、 Python 、 C 、それ以外のもののどれで実装されているかに関係なく、すべてのモジュールはこの種類になります。モジュールの組織化を助け、名前階層を提供するために、 Python には パッケージ という概念があります。
パッケージはファイルシステムのディレクトリ、モジュールはディレクトリにあるファイルと考えることができますが、パッケージやモジュールはファイルシステムから生まれる必要はないので、この比喩を額面通りに受け取ってはいけません。この文書の目的のために、ディレクトリとファイルという便利な比喩を使うことにします。ファイルシステムのディレクトリのように、パッケージは階層構造を成し、通常のモジュールだけでなく、サブパッケージを含むこともあります。
すべてのパッケージはモジュールですが、すべてのモジュールがパッケージとは限らないことを心に留めておくのが重要です。もしくは他の言い方をすると、パッケージは単なる特別な種類のモジュールであると言えます。特に、__path__ 属性を持つ任意のモジュールはパッケージと見なされます。
すべてのモジュールは名前を持ちます。Python の属性アクセスの文法と同様に、サブパッケージの名前は親パッケージ名とドット記号で区切られます。したがって、email という名前のパッケージや、それが含む email.mime という名前のサブパッケージ、さらにそれに含まれる email.mime.text と言う名前のモジュールを考えることができます。
5.2.1. 通常のパッケージ¶
Python では、 通常のパッケージ と 名前空間パッケージ の 2 種類のパッケージが定義されています。通常のパッケージは Python 3.2 以前から存在する伝統的なパッケージです。典型的な通常のパッケージは __init__.py ファイルを含むディレクトリとして実装されます。通常のパッケージがインポートされたとき、この __init__.py ファイルが暗黙的に実行され、それで定義しているオブジェクトがパッケージ名前空間にある名前に束縛されます。 __init__.py ファイルは、他のモジュールに書ける Python コードと同じものを含むことができ、モジュールがインポートされたときに Python はモジュールに属性を追加したりします。
例えば、以下のようなファイルシステム配置は、3 つのサブパッケージを持つ最上位の parent パッケージを定義します:
parent/
__init__.py
one/
__init__.py
two/
__init__.py
three/
__init__.py
parent.one をインポートすると暗黙的に parent/__init__.py と parent/one/__init__.py が実行されます。その後に parent.two もしくは parent.three をインポートすると、それぞれ parent/two/__init__.py や parent/three/__init__.py が実行されます。
A subdirectory inside a regular package that does not contain an
__init__.py file is treated as an implicit
namespace package (a "namespace
subpackage") rooted in that parent. See PEP 420 for the underlying
specification.
5.2.2. 名前空間パッケージ¶
名前空間パッケージはさまざまな ポーション を組み合わせたもので、それぞれのポーションが親パッケージにサブパッケージを提供します。 ポーションはファイルシステム上の別々の場所にあることもあります。 zip ファイル内、ネットワーク上など、インポート時に Python が検索する場所であれば、どこにあることも可能です。 名前空間パッケージはファイルシステム上のオブジェクトに直接対応するとは限らず、具体的な実体を持たない仮想モジュールの場合もあります。
名前空間パッケージは、 __path__ 属性に普通のリストは使いません。その代わりに独自の iterable 型を使っていて、ポーションの親パッケージのパス (もしくは最上位パッケージのための sys.path) が変わった場合、そのパッケージでの次のインポートの際に、新たに自動でパッケージポーションを検索します。
名前空間パッケージには parent/__init__.py ファイルはありません。それどころか、異なるポーションがそれぞれ提供する複数の parent ディレクトリがインポート検索の際に見つかることもあります。したがって parent/one は物理的に parent/two の隣りにあるとは限りません。その場合、そのパッケージかサブパッケージのうち 1 つがインポートされたとき、Python は最上位の parent パッケージのための名前空間パッケージを作成します。
Namespace packages may also be nested inside a regular package. When the
import system searches a regular package's __path__ and encounters a
subdirectory that does not contain an __init__.py file, that
subdirectory becomes a portion contributing to a namespace
subpackage of the enclosing regular package.
名前空間パッケージの仕様については PEP 420 も参照してください。
5.3. 検索¶
検索を始めるためには、 Python はインポートされるモジュール (もしくはパッケージですが、ここでの議論の目的においてはささいな違いです) の 完全修飾 名を必要とします。この名前は、 import 文の様々な引数や importlib.import_module() および __import__() 関数のパラメータから得られます。
この名前はインポート検索の様々なフェーズで使われ、これは例えば foo.bar.baz のようなドットで区切られたサブモジュールへのパスだったりします。この場合、 Python は最初に foo を、次に foo.bar 、そして最後に foo.bar.baz をインポートしようとします。中間のいずれかのインポートに失敗した場合は、 ModuleNotFoundError が送出されます。
5.3.1. モジュールキャッシュ¶
インポート検索で最初に調べる場所は sys.modules です。このマッピングは、中間のパスを含む、これまでにインポートされたすべてのモジュールのキャッシュを提供します。なので foo.bar.baz がインポート済みの場合、 sys.modules は foo 、 foo.bar 、 foo.bar.baz のエントリーを含みます。それぞれのキーはその値として対応するモジュールオブジェクトを持ちます。
インポートではモジュール名は sys.modules から探され、存在した場合は、対応する値がインポートされるべきモジュールであり、この処理は完了します。しかし値が None だった場合、 ModuleNotFoundError が送出されます。モジュール名が見付からなかった場合は、 Python はモジュールの検索を続けます。
sys.modules は書き込み可能です。キーの削除は対応するモジュールを破壊しない (他のモジュールがそのモジュールへの参照を持っている) かもしれませんが、指定されたモジュールのキャッシュされたエントリーを無効にし、それが次にインポートされたとき Python にそのモジュールを改めて検索させることになります。キーを None に対応付けることもできますが、次にそのモジュールがインポートされるときに ModuleNotFoundError となってしまいます。
たとえモジュールオブジェクトへの参照を保持しておいて、 sys.modules にキャッシュされたエントリーを無効にし、その指定したモジュールを再インポートしたとしても、 2 つのモジュールオブジェクトは同じでは ない ことに注意してください。それとは対照的に、 importlib.reload() は 同じ モジュールオブジェクトを再利用し、モジュールのコードを再実行することで単にモジュールの内容を再初期化するだけです。
5.3.2. ファインダーとローダー¶
sys.modules に指定されたモジュールが見つからなかった場合は、 Python のインポートプロトコルが起動され、モジュールを見つけロードします。このプロトコルは 2 つの概念的なオブジェクト、 ファインダー と ローダー から成ります。ファインダーの仕事は、知っている戦略を使って指定されたモジュールを見つけられるかどうか判断することです。両方のインターフェースを実装しているオブジェクトは インポーター と呼ばれます - インポーターは要求されたモジュールがロードできると分かったとき、自分自身を返します。
Python にはデフォルトのファインダーとインポーターがいくつかあります。 1 つ目のものは組み込みモジュールの見つけ方を知っていて、 2 つ目のものは凍結されたモジュール (訳注: freeze ツールで処理されたモジュールのこと。プログラミング FAQ の「どうしたら Python スクリプトからスタンドアロンバイナリを作れますか?」の項目を参照) の見つけ方を知っています。 3 つ目のものは インポートパス からモジュールを探します。 インポートパス はファイルシステムのパスや zip ファイルの位置を示すリストです。このリストは、 URL で特定できるもののような、位置を示すことのできる任意のリソースの検索にまで拡張することもできます。
インポート機構は拡張可能なので、モジュール検索の範囲とスコープを拡張するために新しいファインダーを付け加えることができます。
ファインダーは実際にはモジュールをロードしません。指定されたモジュールが見つかった場合、ファインダーは module spec (モジュール仕様)、すなわちモジュールのインポート関連の情報をカプセル化したものを返します。モジュールのロード時にインポート機構はそれを利用します。
次の節では、インポート機構を拡張するための新しいファインダーやローダーの作成と登録を含め、ファインダーとローダーのプロトコルについてより詳しく解説します。
バージョン 3.4 で変更: Python の以前のバージョンでは、ファインダーは直接 ローダー を返していましたが、現在はローダーを 含む モジュール仕様を返します。ローダーはインポート中はまだ使われていますが、責任は減りました。
5.3.3. インポートフック¶
インポート機構は拡張可能なように設計されています; その主となる仕組みは インポートフック です。インポートフックには 2 種類あります: メタフック と インポートパスフック です。
メタフックはインポート処理の最初、 sys.modules キャッシュの検索以外のインポート処理より前に呼び出されます。これにより、 sys.path の処理や凍結されたモジュールや組み込みのモジュールでさえも、メタフックで上書きすることができます。メタフックは以下で解説するように、 sys.meta_path に新しいファインダーオブジェクトを追加することで登録されます。
インポートパスフックは、 sys.path (もしくは package.__path__) の処理の一部として、対応するパス要素を取り扱うところで呼び出されます。インポートパスフックは以下で解説するように、新しい呼び出し可能オブジェクトを sys.path_hooks に追加することで登録されます。
5.3.4. メタパス¶
指定されたモジュールが sys.modules に見つからない場合、Python は次にメタパス・ファインダーのリスト sys.meta_path を検索します。
各ファインダーに順番に問い合わせ、指定されたモジュールを扱えるかを調べます。
メタパス・ファインダーには find_spec() メソッドが必要です。
このメソッドは、名前、インポートパス、および省略可能な対象モジュールという三つの引数を取ります。
指定されたモジュールを扱えるかどうかの判断方法は、各メタパス・ファインダーが自由に決められます。
meta path finder が指定されたモジュールの扱い方を知っている場合は、ファインダは spec オブジェクトを返します。指定されたモジュールを扱えない場合は None を返します。 sys.meta_path に対する処理が spec を返さずにリストの末尾に到達してしまった場合は、 ModuleNotFoundError を送出します。その他の送出された例外はそのまま呼び出し元に伝播され、インポート処理を異常終了させます。
メタパス・ファインダーの find_spec() メソッドは、二つまたは三つの引数で呼び出されます。
最初の引数は、インポートするモジュールの完全修飾名 (例えば foo.bar.baz) です。
二番目の引数は、モジュールの検索に使うパスエントリです。
最上位のモジュールでは None ですが、サブモジュールやサブパッケージでは親パッケージの __path__ 属性の値になります。
必要な __path__ 属性にアクセスできない場合は ModuleNotFoundError が送出されます。
三番目の引数は、後でロードの対象となる既存のモジュールオブジェクトです。
インポートシステムが対象モジュールを渡すのは、リロード時だけです。
メタパスは、1 回のインポート要求で複数回走査される可能性があります。例えば、関係するモジュールがどれもまだキャッシュされていないとしたときに foo.bar.baz をインポートすると、最初は各メタパス・ファインダー (mpf) に対して mpf.find_spec("foo", None, None) を呼び出して、最上位のインポート処理を行います。foo がインポートされた後に、mpf.find_spec("foo.bar", foo.__path__, None) を呼び出していく 2 回目のメタパスの走査が行われ、foo.bar がインポートされます。foo.bar のインポートまで行われたら、最後の走査で mpf.find_spec("foo.bar.baz", foo.bar.__path__, None) を呼び出していきます。
あるメタパス・ファインダーは最上位のインポートのみサポートしています。これらのインポーターは、2 つ目の引数に None 以外のものが渡されたとき、常に None を返します。
Python のデフォルトの sys.meta_path には、三つのメタパス・ファインダーがあります。
組み込みモジュール用、凍結されたモジュール用、および インポートパス からモジュールをインポートするためのもの (つまり パスベース・ファインダー) です。
バージョン 3.4 で変更: メタパス・ファインダーの find_spec() メソッドが find_module() に代わり、後者は非推奨になりました。
従来のメソッドは変更せずに使えますが、インポート機構が呼び出すのは、ファインダーに find_spec() が実装されていない場合だけです。
バージョン 3.10 で変更: インポートシステムが find_module() を使うと ImportWarning が発生するようになりました。
バージョン 3.12 で変更: find_module() は削除されました。
代わりに find_spec() を使ってください。
5.4. ロード¶
モジュール仕様が見つかった場合、インポート機構はモジュールをロードする時にそれ (およびそれに含まれるローダー) を使います。これは、インポートのロード部分で起こることの近似です:
module = None
if spec.loader is not None and hasattr(spec.loader, 'create_module'):
# It is assumed 'exec_module' will also be defined on the loader.
module = spec.loader.create_module(spec)
if module is None:
module = ModuleType(spec.name)
# The import-related module attributes get set here:
_init_module_attrs(spec, module)
if spec.loader is None:
# unsupported
raise ImportError
if spec.origin is None and spec.submodule_search_locations is not None:
# namespace package
sys.modules[spec.name] = module
elif not hasattr(spec.loader, 'exec_module'):
module = spec.loader.load_module(spec.name)
else:
sys.modules[spec.name] = module
try:
spec.loader.exec_module(module)
except BaseException:
try:
del sys.modules[spec.name]
except KeyError:
pass
raise
return sys.modules[spec.name]
以下の詳細に注意してください:
sys.modulesの中に与えられた名前を持つ既存のモジュールオブジェクトがあるなら、 import は既にそれを返しているでしょう。モジュールは、ローダーがモジュールコードを実行する前に
sys.modulesに存在しています。 モジュールコードが (直接的または間接的に) 自分自身をインポートする可能性があるので、これは重要です; モジュールをsys.modulesに追加することで、最悪のケースでは無限の再帰が、そして最良のケースでは複数回のロードが、前もって防止されます。ロード処理に失敗した場合、その失敗したモジュールは -- そして、そのモジュールだけが --
sys.modulesから取り除かれます。sys.modulesキャッシュに既に含まれていたすべてのモジュールと、副作用としてロードに成功したすべてのモジュールは、常にキャッシュに残されます。これはリロードとは対照的で、リロードの場合は失敗したモジュールもsys.modulesに残されます。後のセクション で要約されるように、モジュールが作られてから実行されるまでの間にインポート機構はインポート関連のモジュール属性を設定します (上記擬似コード例の "_init_module_attrs")。
モジュール実行はモジュールの名前空間が構築されるロードの重要な瞬間です。実行はローダーに完全に委任され、ローダーは何をどのように構築するかを決定することになります。
ロードの間に作成されて exec_module() に渡されたモジュールは、インポートの終わりに返されるものとは異なるかもしれません [2]。
バージョン 3.4 で変更: インポートシステムはローダーの定型的な責任を引き継ぎました。これらは以前は importlib.abc.Loader.load_module() メソッドによって実行されました。
5.4.1. ローダー¶
モジュールローダーは、ロードの重要な機能であるモジュール実行機能を提供します。インポート機構は、実行しようとするモジュールオブジェクトを単一の引数として importlib.abc.Loader.exec_module() メソッドを呼び出します。 importlib.abc.Loader.exec_module() から返された任意の値は無視されます。
ローダーは以下の仕様を満たしていなければいけません:
モジュールが (組み込みモジュールや動的に読み込まれる拡張モジュールではなくて) Python モジュールだった場合、ローダーはモジュールのグローバル名前空間 (
module.__dict__) で、モジュールのコードを実行すべきです。ローダーがモジュールを実行できない場合は
ImportErrorを送出すべきです。 ただし、exec_module()の実行中にそれ以外の例外が送出された場合も、その例外はそのまま伝播します。
多くの場合、ファインダーとローダーは同じオブジェクトで構いません。
その場合、find_spec() メソッドは、ローダーに self を設定したモジュール仕様を返すだけです。
モジュールローダーは、 create_module() メソッドを実装することでロード中にモジュールオブジェクトを作成することを選択できます。このメソッドは、モジュール仕様を引数に取って、ロード中に使う新しいモジュールオブジェクトを返します。 create_module() はモジュールオブジェクトに属性を設定する必要はありません。もしこのメソッドが None を返すなら、インポート機構は新しいモジュールを自身で作成します。
Added in version 3.4: ローダーの create_module() メソッド。
バージョン 3.4 で変更: load_module() メソッドは exec_module() によって置き換えられ、インポート機構がロードのすべての定型的な処理を引き受けました。
既存のローダーとの互換性のため、もしローダーに load_module() メソッドが存在し、かつローダーが exec_module() を実装していなければ、インポート機構はローダーの load_module() メソッドを使います。しかし、 load_module() は deprecated であり、ローダーは代わりに exec_module() を実装すべきです。
load_module() メソッドは、モジュールを実行することに加えて上記で説明されたすべての定型的なロード機能を実施しなければなりません。同じ制約が適用されます。以下は追加の明確化です:
sys.modulesに与えられた名前のモジュールが存在している場合、ローダーはその既存のモジュールを使わなければいけません。 (そうしないとimportlib.reload()は正しく動かないでしょう。) 指定されたモジュールがsys.modulesに存在しない場合、ローダーは新しいモジュールオブジェクトを作成し、sys.modulesに追加しなければいけません。無限の再帰または複数回のロードを防止するために、ローダーがモジュールコードを実行する前にモジュールは
sys.modulesに存在しなければなりません (must)。ロード処理に失敗した場合、ローダーは
sys.modulesに追加したモジュールを取り除かなければいけませんが、それはロードに失敗したモジュール のみ を、そのモジュールがローダー自身に明示的にロードされた場合に限り、除去しなければなりません。
バージョン 3.5 で変更: exec_module() が定義されていて create_module() が定義されていない場合、 DeprecationWarning が送出されるようになりました。
バージョン 3.6 で変更: exec_module() が定義されていて create_module() が定義されていない場合、 ImportError が送出されるようになりました。
バージョン 3.10 で変更: load_module() を使用すると ImportWarning が発生します。
5.4.2. サブモジュール¶
サブモジュールをロードするのにどのようなメカニズム (例えば、 importlib API 、 import または import-from ステートメント、またはビルトイン関数の __import__) が使われた場合でも、バインディングはサブモジュールオブジェクトを親モジュールの名前空間に配置します。例えば、もしパッケージ spam がサブモジュール foo を持っていた場合、 spam.foo をインポートした後は spam は値がサブモジュールに束縛された属性 foo を持ちます。以下のディレクトリ構造を持っているとしましょう:
spam/
__init__.py
foo.py
そして spam/__init__.py は以下のようになっているとします:
from .foo import Foo
このとき、以下を実行することにより spam モジュールの中に foo と Foo に束縛された名前が置かれます:
>>> import spam
>>> spam.foo
<module 'spam.foo' from '/tmp/imports/spam/foo.py'>
>>> spam.Foo
<class 'spam.foo.Foo'>
Python の慣れ親しんだ名前束縛ルールからするとこれは驚きかもしれませんが、それは実際インポートシステムの基本的な機能です。不変に保たなければならないのは (上記のインポートの後などで) sys.modules['spam'] と sys.modules['spam.foo'] が存在する場合、後者が前者の foo 属性として存在しなければならないということです。
5.4.3. モジュール仕様¶
インポート機構は、インポートの間 (特にロードの前) に、個々のモジュールについてのさまざまな情報を扱います。情報のほとんどはすべてのモジュールで共通です。モジュール仕様の目的は、このインポート関連の情報をモジュールの単位でカプセル化することです。
インポートの際にモジュール仕様を使うことは、インポートシステムコンポーネント間、例えばモジュール仕様を作成するファインダーとそれを実行するローダーの間で状態を転送することを可能にします。最も重要なのは、それによってインポート機構がロードの定型的な作業を実行できるようになるということです。これに対して、モジュール仕様なしではローダがその責任を担っていました。
モジュール仕様は module.__spec__ で公開されます。
インタープリタの起動時に初期化されるモジュール でも、同様に __spec__ を適切に設定します。
例外は __main__ で、このモジュールの __spec__ は 場合によって None に設定されます。
モジュール仕様の内容については ModuleSpec を参照してください。
Added in version 3.4.
5.4.4. モジュールの __path__ 属性¶
__path__ 属性は、パッケージのサブモジュールを探す場所を列挙した文字列の シーケンス (空でも構いません) である必要があります。
定義上、__path__ 属性を持つモジュールは パッケージ です。
パッケージの __path__ 属性は、そのサブパッケージをインポートするときに使われます。
インポート機構では sys.path とほぼ同じ働きをし、インポート時にモジュールを探す場所のリストを提供します。
ただし通常、__path__ に指定する場所は sys.path よりもはるかに限定されています。
sys.path に適用される規則は、パッケージの __path__ にも適用されます。
パッケージの __path__ を走査するときには、後述する sys.path_hooks が参照されます。
パッケージの __init__.py ファイルでは、パッケージの __path__ 属性を設定したり変更したりできます。
PEP 420 より前は、通常この方法で名前空間パッケージを実装していました。
PEP 420 の採用により、名前空間パッケージには __path__ を操作するだけの __init__.py ファイルを用意する必要がなくなりました。
インポート機構が、名前空間パッケージの __path__ を自動的に正しく設定します。
5.4.5. モジュールの repr¶
デフォルトでは、すべてのモジュールは利用可能な repr を持っています。ただしこれは、これまでに説明した属性の設定内容に依存しており、モジュール仕様によってモジュールオブジェクトの repr をより明示的に制御することができます。
もしモジュールが仕様 (__spec__) を持っていれば、インポート機構はそこから repr を生成しようとします。もしそれが失敗するか、または仕様が存在しなければ、インポートシステムはモジュールで入手可能なあらゆる情報を使ってデフォルトの repr を構築します。それは module.__name__, module.__file__, module.__loader__ を (足りない情報についてはデフォルト値を使って補いながら) repr への入力として使おうと試みます。
これが使われている正確な規則です:
モジュールが
__spec__属性を持っていれば、仕様に含まれる情報が repr を生成するために使われます。 "name", "loader", "origin", "has_location" 属性が参照されます。モジュールに
__file__属性がある場合は、モジュールの repr の一部として使われます。モジュールに
__file__はないが__loader__があり、その値がNoneではない場合は、ローダーの repr がモジュールの repr の一部として使われます。そうでなければ、単にモジュールの
__name__を repr の中で使います。
バージョン 3.12 で変更: Python 3.4 から非推奨だった module_repr() の使用は Python 3.12 で廃止されました。
モジュールの repr() 結果を決める際に呼び出されることはなくなりました。
5.4.6. キャッシュされたバイトコードの無効化¶
Python は .pyc ファイルからキャッシュ済みのバイトコードをロードする前に、そのキャッシュがソースの .py ファイルに対して最新かどうかを確認します。
デフォルトでは、キャッシュファイルを書き込む際にソースの最終更新タイムスタンプとサイズを記録することで、この確認を可能にしています。
実行時には、インポートシステムがキャッシュファイルに記録されたメタデータとソースのメタデータを照合して、キャッシュファイルが有効かどうかを判断します。
Python は、ソースファイルのメタデータの代わりに内容のハッシュ値を記録する「ハッシュベース」のキャッシュファイルもサポートしています。
ハッシュベースの .pyc ファイルには、チェックありとチェックなしの二種類があります。
チェックありの場合、Python はソースファイルのハッシュ値を計算し、キャッシュファイル内のハッシュ値と比較して、キャッシュファイルが有効かどうかを確認します。
無効と判断された場合はキャッシュを再生成し、新しいチェックありのハッシュベースのキャッシュファイルを書き込みます。
チェックなしの場合、Python は .pyc ファイルが存在すれば有効とみなします。
ハッシュベースの .pyc ファイルを検証する動作は、--check-hash-based-pycs フラグで変更できます。
バージョン 3.7 で変更: ハッシュベースの .pyc ファイルを追加しました。
以前の Python は、タイムスタンプに基づくバイトコードキャッシュの無効化だけをサポートしていました。
5.5. パスベース・ファインダー¶
上で触れた通り、 Python にはいくつかのデフォルトのメタパス・ファインダーが備わっています。そのうちの 1 つは パスベース・ファインダー (PathFinder) と呼ばれ、 パスエントリ のリストである インポートパス を検索します。それぞれのパスエントリは、モジュールを探す場所を指しています。
パスベース・ファインダー自体は何かのインポート方法を知っているわけではありません。その代わりに、個々のパスエントリを走査し、それぞれに特定の種類のパスの扱いを知っているパスエントリ・ファインダーを関連付けます。
デフォルトのパスエントリ・ファインダーは、ファイルシステム上のモジュールを見つけるためのすべてのセマンティクスを実装しています。それは Python ソースコード (.py ファイル) 、Python バイトコード (.pyc ファイル) 、共有ライブラリ (例えば .so ファイル) などの特別なファイルタイプを処理します。標準ライブラリの zipimport モジュールによってサポートされる場合は、デフォルトのパスエントリ・ファインダーは (共有ライブラリ以外の) すべてのファイルタイプの zip ファイルからのロードも扱います。
パスエントリはファイルシステム上の場所に限定される必要はありません。URL やデータベースクエリやその他文字列で指定できる場所を参照することも可能です。
パスベース・ファインダーは、検索できるパスエントリの種類を拡張・カスタマイズするための追加のフックやプロトコルを提供します。 例えば、ネットワーク上の URL をパスエントリとしてサポートするには、HTTP の処理を実装してウェブ上のモジュールを探すフックを書けます。 このフック (呼び出し可能オブジェクト) は、後述するプロトコルをサポートした パスエントリ・ファインダー を返します。 そのファインダーを使って、ウェブからモジュールをロードするローダーを取得します。
警告の言葉: この節と前の節の両方で ファインダー という言葉が、 メタパス・ファインダー と パスエントリ・ファインダー という用語で区別されて使われています。これら 2 種類のファインダーは非常に似ており、似たプロトコルをサポートし、インポート処理で同じように機能しますが、微妙に異なっているのを心に留めておくのは重要です。特に、メタパス・ファインダーはインポート処理の開始時、 sys.meta_path の走査が動くときに動作します。
それとは対照的に、パスエントリ・ファインダーはある意味でパスベース・ファインダーの実装詳細であり、実際 sys.meta_path からパスベース・ファインダーが取り除かれた場合、パスエントリ・ファインダーの実装は何も実行されないでしょう。
5.5.1. パスエントリ・ファインダー¶
パスベース・ファインダー には、文字列 パスエントリ で指定された場所の Python モジュールや Python パッケージを見つけ、ロードする責任があります。ほとんどのパスエントリはファイルシステム上の場所を指定していますが、そこに制限される必要はありません。
メタパス・ファインダーとして、 パスベース・ファインダー には前に解説した find_spec() プロトコルが実装されていますが、これに加えて インポートパス からモジュールを見つけ、ロードする方法をカスタマイズするために使えるフックを提供しています。
パスベース・ファインダー は sys.path 、 sys.path_hooks 、 sys.path_importer_cache という 3 つの変数を使います。さらにパッケージオブジェクトの __path__ 属性も使います。これらによって、インポート処理をカスタマイズする方法が提供されます。
sys.path は、モジュールやパッケージの検索場所を示す文字列のリストです。
環境変数 PYTHONPATH と、インストール環境や実装に固有のデフォルト値から初期化されます。
sys.path の各エントリには、ファイルシステム上のディレクトリ、zip ファイルのほか、URL やデータベースクエリなど、モジュールを検索する別の「場所」を指定することもできます (site モジュールを参照)。
sys.path に含めるのは文字列だけにしてください。
それ以外の型の値は無視されます。
パスベース・ファインダー は メタパス・ファインダー なので、インポート機構は、前で解説したパスベース・ファインダーの find_spec() メソッドを呼び出すことで インポートパス の検索を始めます。 path 引数が find_spec() に渡されたときは、それは走査するパス文字列のリスト - 典型的にはそのパッケージの中でインポートしているパッケージの __path__ 属性になります。 path 引数が None だった場合、それは最上位のインポートであることを示していて、 sys.path が使われます。
パスベース・ファインダーは検索パスの各エントリを順に調べ、それぞれに適した パスエントリ・ファインダー (PathEntryFinder) を探します。
この処理には時間がかかる場合があります (例えば、検索のための stat() 呼び出しにオーバーヘッドが生じます)。
そのため、パスベース・ファインダーはパスエントリとパスエントリ・ファインダーの対応をキャッシュします。
このキャッシュは sys.path_importer_cache に保存されます (名前に反して、保存するオブジェクトは インポーター に限らず、実際にはファインダーです)。
これにより、特定の パスエントリ に対応する パスエントリ・ファインダー を探す、コストの高い処理を一度だけにできます。
ユーザーコードは sys.path_importer_cache のエントリを自由に削除して、パスベース・ファインダーに再検索させることができます。
パスエントリがキャッシュにない場合、パスベース・ファインダーは sys.path_hooks にある呼び出し可能オブジェクトを順に調べます。
リスト内の各 パスエントリフック は、検索対象のパスエントリを一つの引数として呼び出されます。
フックは、そのパスエントリを扱える パスエントリ・ファインダー を返すか、ImportError を送出します。
パスベース・ファインダーは、ImportError を、そのフックでは対象の パスエントリ に対応する パスエントリ・ファインダー が見つからないことを示すものとして扱います。
この例外は無視され、インポートパス の走査が続きます。
フックは文字列または bytes オブジェクトを受け取れるようにしてください。
bytes オブジェクトのエンコーディングはフックが決めます (ファイルシステムのエンコーディング、UTF-8、またはそれ以外でも構いません)。
引数をデコードできない場合は ImportError を送出すべきです。
sys.path_hooks を辿る処理が パスエントリ・ファインダー を何も返さずに終わった場合、パスベース・ファインダーの find_spec() メソッドは、 sys.path_importer_cache に (このパスエントリに対するファインダーが存在しないことを示すために) None を保存し、 メタパス・ファインダー はモジュールが見つからなかったことを伝えるために None を返します。
sys.path_hooks にある パスエントリフック のいずれかが パスエントリ・ファインダー を 返した 場合、後述するプロトコルでそのファインダーにモジュール仕様を要求します。
得られたモジュール仕様を使って、モジュールをロードします。
The current working directory -- denoted by an empty string -- is handled
slightly differently from other entries on sys.path. First, if the
current working directory is found to not exist, no value is stored in
sys.path_importer_cache. Second, the value for the current working
directory is looked up fresh for each module lookup. Third, the path used for
sys.path_importer_cache and returned by
importlib.machinery.PathFinder.find_spec() will be the actual current
working directory and not the empty string.
5.5.2. パスエントリ・ファインダー・プロトコル¶
モジュールと初期化されたパッケージのインポートをサポートするため、および名前空間パッケージのポーションとして提供するために、パスエントリ・ファインダーは find_spec() メソッドを実装しなければいけません。
find_spec() は二つの引数を取ります。
インポートするモジュールの完全修飾名と、省略可能な対象モジュールです。
find_spec() は、必要な情報をすべて設定したモジュール仕様を返します。
この仕様には、一つの例外を除き、必ず loader が設定されています。
モジュール仕様が名前空間の ポーション を表すことをインポート機構に伝えるには、パスエントリ・ファインダーが submodule_search_locations にそのポーションを含むリストを設定します。
バージョン 3.4 で変更: find_spec() が find_loader() と find_module() に代わり、後者二つは非推奨になりました。
ただし、find_spec() が定義されていない場合は引き続き使われます。
古いパスエントリ・ファインダーの中には、 find_spec() の代わりにこれら 2 つの deperecated なメソッドのうちのいずれかを実装しているものがあるかもしれません。これらのメソッドは後方互換性のためにまだ考慮されています。しかし、パスエントリ・ファインダーに find_spec() が実装されていれば、古いメソッドは無視されます。
find_loader() は、インポートするモジュールの完全修飾名を一つの引数として取ります。
find_loader() は二要素のタプルを返します。
最初の要素はローダー、二番目の要素は名前空間の ポーション です。
他のインポート機構の実装に対する後方互換性のために、多くのパスエントリ・ファインダーは、メタパス・ファインダーがサポートするのと同じ伝統的な find_module() メソッドもサポートしています。しかし、パスエントリ・ファインダーの find_module() メソッドは、決して path 引数では呼び出されません (このメソッドは、パスフックの最初の呼び出しから適切なパス情報を記録する動作が期待されています)。
パスエントリ・ファインダーの find_module() メソッドは deprecated です。なぜなら、その方法ではパスエントリ・ファインダーが名前空間パッケージに対してポーションを提供することができないからです。もし find_loader() と find_module() の両方がパスエントリ・ファインダーに存在したら、インポートシステムは常に find_module() よりも find_loader() を優先して呼び出します。
バージョン 3.10 で変更: インポートシステムによる find_module() と find_loader() の呼び出しで、ImportWarning が発生するようになりました。
バージョン 3.12 で変更: find_module() と find_loader() は削除されました。
5.6. 標準のインポートシステムを置き換える¶
インポートシステム全体を置き換えるための最も信頼性のある仕組みは、 sys.meta_path のデフォルトの内容を削除し、全部をカスタムのメタパスフックで置き換えるものです。
If it is acceptable to only alter the behaviour of import statements
without affecting other APIs that access the import system, then replacing
the builtin __import__() function may be sufficient.
標準のインポートシステム全体を無効にせず、一部のモジュールのインポートだけを防ぐには、メタパスの先頭に近い位置にあるフックで処理します。
find_spec() から None を返す代わりに、直接 ModuleNotFoundError を送出すれば十分です。
None はメタパスの検索を続けることを意味しますが、例外を送出すると検索は直ちに終了します。
5.7. パッケージの相対インポート¶
相対インポートでは先頭にドットを付けます。 ドット一つは、現在のパッケージを起点とする相対インポートを表します。 ドットが二つ以上の場合は、最初のドットを除くドット一つにつき一階層ずつ、現在のパッケージの上位を起点にします。 例えば、次のようなパッケージ構成があるとします。
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
subpackage1/moduleX.py と subpackage1/__init__.py のどちらでも、次の相対インポートは有効です。
from .moduleY import spam
from .moduleY import spam as ham
from . import moduleY
from ..subpackage1 import moduleY
from ..subpackage2.moduleZ import eggs
from ..moduleA import foo
絶対インポートには import <> と from <> import <> のどちらの構文も使えますが、相対インポートに使えるのは後者だけです。
この理由を、次の文で考えてみます。
import XXX.YYY.ZZZ
この文を実行すると、XXX.YYY.ZZZ を式として使えるようになる必要があります。
一方、.moduleY は有効な式ではありません。
5.8. __main__ に対する特別な考慮¶
__main__ モジュールは、 Python のインポートシステムに関連する特別なケースです。 他の場所 で言及されているように、 __main__ モジュールは sys や builtins などと同様にインタプリタースタートアップで直接初期化されます。しかし、前者 2 つのモジュールと違って、 __main__ は厳密にはビルトインのモジュールとしての資格を持っていません。これは、 __main__ が初期化される方法がインタプリタが起動されるときのフラグやその他のオプションに依存するためです。
5.8.1. __main__.__spec__¶
__main__ がどのように初期化されるかに依存して、 __main__.__spec__ は適切に設定されることもあれば None になることもあります。
Python が -m オプションを付けて実行された場合には、 __spec__ は対応するモジュールまたはパッケージのモジュール仕様に設定されます。また、ディレクトリや zip ファイル、または他の sys.path エントリを実行する処理の一部として __main__ モジュールがロードされる場合にも __spec__ が生成 (populate) されます。
それ以外のケース では、 __main__.__spec__ は None に設定されます。これは、 __main__ を生成 (populate) するために使われたコードがインポート可能なモジュールと直接一致していないためです:
対話プロンプト
-cオプションstdin から起動された場合
ソースファイルやバイトコードファイルから直接起動された場合
最後のケースでは、たとえ技術的にはファイルがモジュールとして直接インポートできた としても __main__.__spec__ は常に None になることに注意してください。もし __main__ において有効なモジュールメタデータが必要なら -m スイッチを使ってください。
__main__ がインポート可能なモジュールと一致し、 __main__.__spec__ がそれに応じて設定されていたとしても、それでもなお、この 2 つのモジュールは別物とみなされることに注意してください。これは、 if __name__ == "__main__": チェックによって保証されるブロックは、 __main__ 名前空間を生成 (populate) するためにモジュールが使用される時にだけ実行され、通常のインポート時には実行されない、という事実に起因しています。
5.9. 参考資料¶
Python の初期の頃からすると、インポート機構は目覚ましい発展を遂げました。 一部細かいところがドキュメントが書かれたときから変わってはいますが、最初期の パッケージの仕様 はまだ読むことができます。
オリジナルの sys.meta_path の仕様は PEP 302 で、その後継となる拡張が PEP 420 です。
PEP 420 は Python 3.3 に 名前空間パッケージ を導入しました。
また、PEP 420 は find_module() の代替として find_loader() プロトコルも導入しました。
PEP 366 は、メインモジュールでの明示的な相対インポートのために追加した __package__ 属性の解説をしています。
PEP 328 は絶対インポート、明示的な相対インポート、および、当初 __name__ で提案し、後に PEP 366 が __package__ で定めた仕様を導入しました。
PEP 338 はモジュールをスクリプトとして実行するときの仕様を定めています。
PEP 451 は、モジュール仕様オブジェクトにおけるモジュール毎のインポート状態のカプセル化を追加しています。また、ローダーの定型的な責任のほとんどをインポート機構に肩代わりさせています。これらの変更により、インポートシステムのいくつかの API が deprecate され、またファインダーとローダーには新しいメソッドが追加されました。
脚注