2016-09-22 9 views
3

私はPythonの専門家ではないので、可変スコープのニュアンスを理解しようとしています。モジュールと変数スコープ

私が直面している問題を説明する簡単な例として、次の3つのファイルがあるとします。

最初のファイルはoutside_code.pyです。特定の制限のため、このファイルを変更することはできません。それはそのままでなければなりません。それは、ある時点で評価を実行するいくつかのコードを含んでいます(はい、私はevalが悪魔の産物であることを知っていますが、それは後日の議論です)。たとえば、次のコード行が含まれているとします。

def eval_string(x): 
    return eval(x) 

2番目のファイルは、ユーザー定義関数のセットです。 functions.pyとしましょう。これは、関数定義のいくつかの未知の番号が含まれ、例えば、のはそのfunctions.pyが一つの機能が含まれているとしましょう、以下に定義する:

def foo(x): 
    print("Your number is {}!".format(x)) 

は、今私は3番目のファイルを書き込み、のは、それを呼びましょうmain.py.これは、次のコードが含まれます。

import outside_code 
from functions import * 
outside_code.eval_string("foo(4)") 

私は*でfunctions.pyから関数定義のすべてをインポートするので、彼らは()functions.fooような何かをすることなく、main.pyでアクセスできる必要があります。 outside_code.pyもインポートするので、コア機能(評価版を含むコード)にアクセスできます。最後に、関数_files.pyで関数を呼び出し、functions.pyで定義された関数に関連する文字列を渡します。

簡略化した例では、「Your number is 4!」というコードを印刷します。しかし、 'foo'が定義されていないというエラーが表示されます。これは、outside_code.pyのコードがmain.pyにあるのと同じfoo関数にアクセスできないことを意味します。だから何とか私はfooにアクセスできるようにする必要があります。誰かが私に実際にfooの範囲が何であるか、そして私が実際にそれを使用したいスペースをカバーするためにどのように拡張できるかを教えてもらえますか?私の問題を解決する最善の方法は何ですか?

答えて

1

これらの名前をoutside_codeのスコープに追加する必要があります。 outside_codeは、通常のPythonモジュールである場合は、あなたが直接行うことができます。

import outside_code 
import functions 

for name in getattr(functions, '__all__', (n for n in vars(functions) if not n[0] == '_')): 
    setattr(outside_code, name, getattr(functions, name)) 

をこれは、すべての名前(あなたがfrom functions import *でインポートしたい)functions輸出を取り、内部eval()ようoutside_codeに対応するオブジェクトへの参照を追加しますoutside_code.eval_string()それらを見つけることができます。

あなたは、いわば、損傷を制限するためにeval_function()に渡す前に、表現から構文解析ツリーを生成して、抽出し、すべてのグローバル名を式からのみoutside_codeそれら名を追加するast.parse() functionを使用することができますが、この作業を行うためには、他のモジュールの名前空間を壊すことになります。

最初はeval()を使用した場合とほとんど変わりませんが、その別のモジュールでeval()に名前空間パラメータを指定することができない場合は、唯一の選択です。これは、がデフォルトでであるため、eval()は、名前空間として実行されるモジュールのグローバル名前空間を使用するためです。

ただし、実際にeval_string()関数がより多くのパラメータを受け入れる場合は、名前空間またはglobalsオプションを探します。それが存在する場合、この関数はおそらくより次のようになります。

def eval_string(x, namespace=None): 
    return eval(x, globals=namespace) 

た後、あなたがやるだけのことができます:vars(functions)はあなたfunctionsモジュールの名前空間を提供します

outside_code.eval_string('foo(4)', vars(functions)) 

+0

ありがとうございました。私はvars(関数)を理解しようとしています。私が見て、その中にあるものを見ると、functions.pyで定義された変数を含む辞書が得られますが、\ _ \ _ doc \ _ \ _、\ _ \ _builtins \ _ \ _、EOFErrorやSyntaxErrorのようなエラー、eval、hexなどの組み込み関数 余分なものがなくても、私がコード内で実際に定義したことをどうやって得ることができますか? –

+0

@ K.Mao:それはそのモジュールの*完全な名前空間*です。これらの名前はすべて、そのモジュールで実行される式で使用できます。 '_'で始まらない名前や' __all__'で定義されている名前に限定して、その辞書の新しいコピーを作ることができます。 –

+0

別のファイルの完全な名前空間を引数としてevalに渡すのに悪いことはありますか?私は間違いなく内蔵されている重要なものを上書きするか、それとも問題ありませんか? –

1

fooがmain.pyにインポートされました。そのスコープはそのファイル(もちろん当初定義されていたファイル)に制限されています。 outside_code.pyには存在しません。

実際のeval関数は、評価されたコードの名前空間に要素を追加できるように、ローカルとグローバルのdictsを受け入れます。しかし、もしあなたのeval_stringがまだそれらを通過していなければ何もできません。

1

関連文書:https://docs.python.org/3.5/library/functions.html#eval

evalはあなたが必要とする何をします値

eval('foo(4)', {'foo': foo}) 

に、オプションの辞書マッピンググローバル名を取ります。文字列 'foo'を関数オブジェクトfooにマッピングしています。

EDIT

は、あなたの質問を読み直す、それはあなたのために動作しません。このように見えます。私の唯一の他の思考は

eval_str('eval("foo(4)", {"foo": lambda x: print("Your number is {}!".format(x))})') 

を試してみることです。しかし、それは非常にハック解決策だとラムダに収まらない関数にうまくスケールしません。