Pythonでインポートされたクラスのスコープは何ですか?
質問
曖昧なタイトルですみません。誰かが提案を持っている場合は、ぜひ教えてください。また、より適切なタグでタグ付けし直してください。
問題
インポートされたクラスのインスタンスがインポーターのスコープ (グローバル、ローカル) 内のものを表示できるようにしたいと考えています。ここで働いている正確なメカニズムはわからないので、言葉で説明するよりも断片を使ったほうがうまく説明できます。
## File 1
def f1(): print "go f1!"
class C1(object):
def do_eval(self,x): # maybe this should be do_evil, given what happens
print "evaling"
eval(x)
eval(x,globals(),locals())
次に、反復セッションからこのコードを実行すると、たくさんのコードが表示されます。 NameErrors
## interactive
class C2(object):
def do_eval(self,x): # maybe this should be do_evil, given what happens
print "evaling"
eval(x)
eval(x,globals(),locals())
def f2():
print "go f2!"
from file1 import C1
import file1
C1().do_eval('file1.f1()')
C1().do_eval('f1()')
C1().do_eval('f2()')
file1.C1().do_eval('file1.f1()')
file1.C1().do_eval('f1()')
file1.C1().do_eval('f2()')
C2().do_eval('f2()')
C2().do_eval('file1.f1()')
C2().do_eval('f1()')
この種のタスクに一般的なイディオムやパターンはありますか?私は完全に間違った木に吠えているのでしょうか?
解決
この例では、関数をオブジェクトとしてメソッドに単純に渡すことができます。 C1
:
>>> class C1(object):
>>> def eval(self, x):
>>> x()
>>>
>>> def f2(): print "go f2"
>>> c = C1()
>>> c.eval(f2)
go f2
Python では、関数やクラスを他のメソッドに渡し、そこで呼び出したり作成したりできます。
実際にコード文字列を評価したい場合は、Thomas がすでに述べたように、環境を指定する必要があります。
上記のモジュールを少し変更しました。
## File 1
def f1(): print "go f1!"
class C1(object):
def do_eval(self, x, e_globals = globals(), e_locals = locals()):
eval(x, e_globals, e_locals)
次に、対話型インタープリターで次のようにします。
>>> def f2():
>>> print "go f2!"
>>> from file1 import * # 1
>>> C1().do_eval("f2()") # 2
NameError: name 'f2' is not defined
>>> C1().do_eval("f2()", globals(), locals()) #3
go f2!
>>> C1().do_eval("f1()", globals(), locals()) #4
go f1!
いくつかの注釈
- ここでは、すべてのオブジェクトを次から挿入します。
file1
このモジュールの名前空間に f2
の名前空間にありませんfile1
, したがって、次の結果が得られます。NameError
- これで環境を明示的に渡し、コードを評価できるようになりました。
f1
インポートしたため、このモジュールの名前空間にあります
編集:の環境を明示的に渡す方法に関するコードサンプルを追加しました。 eval
.
他のヒント
関数は、メソッドやクラス本体と同様に、常に定義されているスコープ内で実行されます。これらは別のスコープで実行されることはありません。インポートは単なる代入ステートメントであり、Python のすべては参照であるため、関数、クラス、モジュールはどこにインポートされるかさえ知りません。
次の 2 つのことができます。使用してほしい「環境」を明示的に渡すか、スタックハッカリーを使用して呼び出し元の名前空間にアクセスします。前者は後者ほど実装に依存せず脆弱ではないため、後者よりもはるかに好まれます。
同様のことを試みる string.Template クラスを見てみるとよいでしょう。