1. ホーム
  2. python

[解決済み] Pythonの__import__はなぜfromlistを要求するのですか?

2023-07-10 16:49:59

質問

Pythonでは、プログラム的にモジュールをインポートしたい場合、以下のようにします。

module = __import__('module_name')

サブモジュールをインポートしたい場合、単純に考えると

module = __import__('module_name.submodule')

もちろん、これはうまくいきません。 module_name になってしまいます。そうしなければなりません。

module = __import__('module_name.submodule', fromlist=['blah'])

なぜですか? の実際の値は fromlist の実際の値は、それが空でない限り、全く問題ではないようです。引数を要求しておいて、その値を無視することに何の意味があるのでしょうか?

Pythonのほとんどのものは正当な理由のために行われるようですが、どうしても、この動作が存在するための合理的な説明を思いつくことができません。

どのように解決するのですか?

実は __import__() の実装に起因しています。 import ステートメントを呼び出すからです。 __import__() . 基本的に、5つの微妙に異なる方法があります。 __import__() が呼び出すことができるのは import (2つの主要なカテゴリを持つ)。

import pkg
import pkg.mod
from pkg import mod, mod2
from pkg.mod import func, func2
from pkg.mod import submod

最初の の場合は import ステートメントは "left-most" モジュールオブジェクトを "left-most" の名前に割り当てる必要があります。 pkg . その後 import pkg.mod の後に pkg.mod.func() なぜなら import ステートメントがローカル名 pkg というモジュールオブジェクトがあり、これは mod 属性があります。そのため __import__() 関数が一番左のモジュールオブジェクトを返さなければならないので、それを pkg . これらの2つのimport文は、このように変換されます。

pkg = __import__('pkg')
pkg = __import__('pkg.mod')

3番目、4番目、5番目の場合は import 文はより多くの仕事をしなければなりません。それは、モジュール・オブジェクトから取得しなければならない(潜在的に)複数の名前に割り当てなければなりません。そのため __import__() 関数は1つのオブジェクトしか返せないので、モジュールオブジェクトからそれぞれの名前を取得させる本当の理由はありません(そして、それは実装をより複雑にするでしょう)。 だから、単純なアプローチは次のようになります(3番目のケースについて)。

tmp = __import__('pkg')
mod = tmp.mod
mod2 = tmp.mod2

しかし、この場合 pkg がパッケージで mod または mod2 はそのパッケージのモジュール で、まだインポートされていない であり、3番目と5番目のケースと同様です。は、その __import__() 関数が知る必要があるのは modmod2 という名前は import 文がアクセスできるようにしたい名前です。そうすることで、それらがモジュールであるかどうかを確認し、それらもインポートしようとします。ですから、呼び出しはより近いものになります。

tmp = __import__('pkg', fromlist=['mod', 'mod2'])
mod = tmp.mod
mod2 = tmp.mod2

というのは __import__() を読み込もうとすると pkg.modpkg.mod2 と同様に pkg (を使用することができます(ただし、もし mod または mod2 が存在しない場合は、エラーではなく __import__() 呼び出しのエラーではありません。エラーの発生は import ステートメントに任されています)。しかし、それはまだ4番目と5番目の例にとって正しいことではありません。なぜなら、もし呼び出しがそうであったなら。

tmp = __import__('pkg.mod', fromlist=['submod'])
submod = tmp.submod

では tmp は結局のところ pkg になってしまい、以前のように pkg.mod モジュールを取得したい場合は submod 属性を取得することができます。実装では、このように import 文が余計な仕事をするようにし、パッケージ名を . のように __import__() 関数がすでに行っているような、名前のトラバースを行いますが、これでは労力の一部が重複してしまうことになります。そこで、代わりに、実装では __import__() を返すようにしました。 一番右の モジュールの代わりに の代わりに という 場合のみ fromlistが渡され、空でない場合のみ。

(その import pkg as pfrom pkg import mod as m の構文は、どのローカル名に割り当てられるかを除いて、この話については何も変わりません。 __import__() 関数は as が使用されている場合、すべて import 文の実装のままです)。