2011-01-10 4 views
7

:。dictのすべてのキーをローカル変数としてロードするにはどうすればよいですか?この辞書を与える

>>> options = {'DATABASES': {'default': {'ENGINE': 'django.db.backends.sqlite3'}}} 

この?:

>>> foo(options) 
>>> print DATABASES 
{'default': {'ENGINE': 'django.db.backends.sqlite3'}} 

を取得するための最良の方法だろう私は、地元の人々()更新(オプション)として、これを解決するのですが、私は考えていました、多分もっと良い解決策があるかもしれません。

+1

最良の方法は、まず第一に、このような危険なことを行うことではありません。そして、幸いなことに、関数だけでは現在の名前空間で欺くことができません(実際には、グローバルなものと、おそらくもっと悪い反射のハッカーを使用しているかもしれませんが、考えてみてください)。 – delnan

+0

@デルナン私は地元の人()が醜い使用を理解しています。設定ファイルを解析した後、オプション辞書を取得するので、どのように設定のデフォルト値の値をlocals()を使用して置き換えることができますか?¿ –

+1

あなたはうまくいけません。なぜなら、そうすることは変数を変数 '' eval'の上の1ステップだけであるユーザ入力で上書きすることを意味するからです。辞書に入ってください。 – delnan

答えて

7
import inspect 

allowed_vars = set(["min_", "max_", "path", ...]) 
def update_globals(dic): 
    caller_frame = inspect.currentframe(1) 
    globals = caller_frame.f_globals 
    # here, you _could_ simply do globals.update(dic) 
    # but it is evil 
    for key, value in dic.items(): 
     #here you should carefully verify each key, and value for not 
     #not dangerous pairs, with stuff like: 
     #if key not in allowed_vars: 
     # sys.stderr.write("Warning: invalid variable in configuration update\n") 
     #  continue 
     #if type(value) not in (string, int, float): 
     # #(issue error) 
     # continue 
     globals[key] = value 

例:

>>> a 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
NameError: name 'a' is not defined 
>>> update_globals({"a": 5}) 
>>> a 
5 

更新2016から06 A数週間前に私はextradict Pythonパッケージをまとめました。それはpypi nowで利用できます。その機能の1つはコンテキストマネージャです。これは何かを実行して の質問を正確に行うことができます:

from extradict import MapGetter 

def myfunc(): 
    options = {'DATABASES': {'default': {'ENGINE': 'django.db.backends.sqlite3'}}} 
    with MapGetter(options) as options: 
     from options import DATABASES 
... 

他の通常の "from .... import ...."の使用法ではなく、辞書またはマッピングオブジェクト(デフォルトのdictを含む)からのものです。

+0

地元やグローバルを変更するのは危険ですが、私の必要に応じてあなたの方法がうまくいくように思えます。おそらく将来私は設定用のパーサの動作を変更するでしょう –

+0

それは危険なローカルやグローバルを変更していない - それはPythonのダイナミズムの一部であり、私たちはそれを利用しなければなりません。危険なのは、検証されていないソース(通常はリモートユーザの入力)からデータを取り出し、それを実行中のプログラムの "生きた" Pythonオブジェクトに変換することです。私が上で触れたセーフガードを追加すれば、このコードを使っても問題はありません。 – jsbueno

0

ローカル変数名のリストはコンパイルされた関数オブジェクトの静的な部分であるため、実行時に関数ローカルを変更することはできません。

>>> def a(): b = 5 
... 
>>> a.func_code.co_varnames 
('b',) 

これはlocals()globals()と同じであり、グローバル変数(関数地元とは異なり)動的である辞書に格納されているという理由だけで、グローバルスコープで動作します。

別のソースからDjango設定モジュールの値を更新しているようです。私はこれが必ずしも悪いとは言いませんが、わかりやすくするためにlocals()の代わりにglobals()を使用してください。

3

goldmabが指摘したように、関数内)(地元の出力を変更すると、動作しません。

SyntaxError: invalid syntax 
>>> def foo(): 
...  locals().update({'a': 1}) 
...  print a 
... 
>>> foo() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 3, in foo 
NameError: global name 'a' is not defined 

これはどちらか本当に非常にクリーンな方法ではありませんが、それは仕事をするだろう。

>>> def foo(): 
...  options = {'DATABASES': {'default': {'ENGINE': 'django.db.backends.sqlite3'}}} 
...  for k, v in options.items(): 
...   exec('%s = v' % k) 
...  print DATABASES 
... 
>>> foo() 
{'default': {'ENGINE': 'django.db.backends.sqlite3'}} 

ところで、あなたのディクテーションの各キーは変数としてokである必要があります。たとえば、辞書に 'DATABASE-USERNAME'がキーとして含まれていた場合、その変数に変数を代入しようとすると例外が発生します。さらに、信頼できないソースからオプション辞書を入手すると、コードインジェクション攻撃に対して脆弱になります。 (キーは、何かのように、言うことができる "輸入OSを、os.system( 'sudoのはadduserスクリプトキディ'); ..."

3

私が何をしたいことは、単純だと思う:

globals().update(**options) 
関連する問題