2011-01-10 6 views
7

Bu sözlüğe sahip:Tüm anahtarları bir dict'tan yerel değişkenler, daha iyi bir yaklaşım olarak nasıl yükleyebilirim?

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

Bunu elde etmenin en iyi yolu ne olurdu ?:

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

Bunu locals(). update (options) olarak çözüyorum, ama düşünüyordum da belki daha iyi bir çözüm var.

+1

iyi yolu ilk etapta böyle tehlikeli şeyler yapmak değil. Ve neyse ki, tek başına bir işlev geçerli ad alanı ile dalga geçmeyi başaramayacak (aslında, küresel bir ve belki de kötü yansıma korsanlığı kullanırsanız daha fazlası ile olabilir, ama bunu düşünmeyin!) – delnan

+0

@ delnan yerliler kullanarak anlıyorum() çirkin.Bak, bir config dosyasını ayrıştırdıktan sonra seçenekler sözlüğünü alıyorum, bu yüzden ayarlarda varsayılan değerlerin değerlerini yerlileri kullanarak nasıl değiştirebiliriz() ¿ –

+1

Daha iyi yapmayın. Çünkü bunu yapmak, değişkenlerinizi kullanıcı girdisi üzerine yazmanız anlamına gelir; bu, 'eval''ın yalnızca bir adım ötesindedir. Sadece sözlüğe devam et. – delnan

cevap

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 

Örnek:

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

güncelleme 2016-06 A önce extradict Python paketi, birlikte koymuştu birkaç hafta - bu pypi now geçerli olan özelliklerinden biri tam olarak ne boyunca bir şey yaparak istedi ediliyor verir MapGetter bağlam yöneticisidir:

.
from extradict import MapGetter 

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

Ve diğer normal "from .... import ....", bir sözlük veya eşleme nesnesinden (varsayılan bir dict dahil) yararlanır.

+0

Yerliler veya globals'ı değiştirmenin tehlikeli bir şey olduğunu biliyorum, ancak yönteminiz buna ihtiyacım var gibi çalışıyor. Muhtemelen gelecekte ayrıştırıcımın davranışını değiştirebilirim. –

+0

Bu, tehlikeli olan yerlileri veya globalleri değiştirmez - Python'un dinamizminin bir parçası ve bundan faydalanmak zorundayız. Tehlikeli olan, doğrulanmamış bir kaynaktan (normalde uzaktaki kullanıcı girdisi) veri toplayıp, onu çalışan programdaki "yaşayan" python nesnelerine dönüştürmektir. Yukarıda bahsettiğim güvenlik önlemlerini eklediğinizde, bu kodu kullanarak sorun olmamalıdır. – jsbueno

0

Yerel değişken adlarının listesi derlenmiş işlev nesnesinin statik bir parçası olduğu için çalışma yerlilerini çalışma zamanında değiştiremezsiniz.

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

Bu locals() orada globals() aynıdır çünkü küresel kapsamda çalışacak ve küresel değişkenler (işlev halk aksine) dinamik bir sözlükte, saklanır.

>>> locals() is globals() 
True 

Django ayarları modülündeki değerleri başka bir kaynaktan güncelliyorsunuz gibi görünüyor. Bunun mutlaka kötü olduğunu söyleyemem ama netlik açısından locals() yerine globals() kullanmalısınız. goldmab olarak

3

bir işlev içinde yerli çıkışı() çalışmaz değiştirerek kaydetti:

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 

Bu gerçekten ya çok temiz bir yol değildir, ancak iş yapacağını:

>>> 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'}} 

Bu arada, bu arada, kodunuzdaki her anahtarın bir değişken olarak iyi olması gerekir. Örneğin, sözlüğün bir anahtar olarak "DATABASE-USERNAME" içermesi durumunda, bunu bir değişkene atamaya çalışmak istisna teşkil eder. Dahası, bu, güvenilir olmayan bir kaynaktan gelen seçenekler sözlüğünü alırsanız, kod enjeksiyon saldırılarına karşı sizi savunmasız yapar. (Bir anahtar gibi bir şey söyleyebiliriz "ithal os; os.system ('sudo İÇİNadduser ScriptKiddie'); ..."

3

Ben ne istediğini basitçe olduğunu düşünüyorum:

globals().update(**options) 
İlgili konular