2013-08-28 8 views
9

Değişkenler globals olarak veya Python'un işlevi eval() işlevine yerel olarak iletilirse neden bir fark yaratır? Python's eval()? Kullanırken yerliler ve globals arasındaki fark nedir?

Ayrıca, described in the documenation olarak Python, açıkça verilmediği takdirde, __builtins__ numaralı telefonu globals'a kopyalayacaktır. Fakat göremediğim başka bir farklılık da olmalı. Aşağıdaki örnek işlevi göz önünde bulundurun. Bir dize code alır ve bir işlev nesnesi döndürür. Yerleşiklere izin verilmez (ör. abs()), ancak tüm işlevler math paketinden.

def make_fn(code): 
    import math 
    ALLOWED_LOCALS = {v:getattr(math, v) 
     for v in filter(lambda x: not x.startswith('_'), dir(math)) 
    } 
    return eval('lambda x: %s' % code, {'__builtins__': None}, ALLOWED_LOCALS) 

Çalışıyor herhangi bir yerel veya global nesneleri kullanarak değil beklendiği gibi:

fn = make_fn('x + 3') 
    fn(5) # outputs 8 

Ama math işlevleri kullanarak çalışmaz:

fn = make_fn('cos(x)') 
    fn(5) 

Bu şu istisna verir:

<string> in <lambda>(x) 
    NameError: global name 'cos' is not defined 
0 globaller aynı eşleme geçerkenAma çalışır: Yukarıdaki gibi

def make_fn(code): 
    import math 
    ALLOWED = {v:getattr(math, v) 
     for v in filter(lambda x: not x.startswith('_'), dir(math)) 
    } 
    ALLOWED['__builtins__'] = None 
    return eval('lambda x: %s' % code, ALLOWED, {}) 

aynı örnek: detaylı olarak burada ne olur

fn = make_fn('cos(x)') 
    fn(5) # outputs 0.28366218546322625 

?

+0

Dokümantasyonda, "globals sözlüğü var ve" __builtins__ "bulunmuyor. Bu, "__builtins__" anahtarının mevcut olmadığı anlamına gelir, ancak örneğinizde, bunu None olarak ayarladığınız anlamına gelir. ÖNERİLEN [‘__ builtins__’] = None DEĞİŞTİRMELİ [‘__ builtins__’] ile değiştirilmemesini öneririm ve tekrar deneyin. – jorispilot

+0

@jorispilot Sorunum, __builtins__'ye bağlı değil. Daha önce değindim, çünkü bu globals'ın özel bir kasası var. __builtins__ öğesini None olarak ayarladığım yol, 'abs' gibi yerleşik işlevleri yasaklamak istiyorsanız, doğru değildir. "çünkü" yerleşik bir işlev değildir, matematik modülünün bir parçasıdır. – lumbric

cevap

8

Python adlarını varsayılan olarak globals olarak arar; yalnızca işlevlerine atanan isimler yerel olarak aranır (böylece işlevin bir parametresi olan veya işleve atanmış olan herhangi bir ad). Kod nesne ve işlevleri derleme için dis.dis() işlevini kullandığınızda

yazarak görebilirsiniz:

>>> import dis 
>>> def func(x): 
...  return cos(x) 
... 
>>> dis.dis(func) 
    2   0 LOAD_GLOBAL    0 (cos) 
       3 LOAD_FAST    0 (x) 
       6 CALL_FUNCTION   1 
       9 RETURN_VALUE   

LOAD_GLOBAL yükler cos global isim olarak, sadece globaller ad bakıyor. LOAD_FAST opcode, adlara dizinle bakmak için geçerli ad alanını (işlev yerelleri) kullanır (işlev yerel ad alanları, bir C dizisi olarak son derece optimize edilir ve depolanır).

İsimleri aramak için üç tane daha opco var; LOAD_CONST (None ve değişmez değerler için tamsayı tanımları gibi), LOAD_DEREF (bir bağlantıya başvuru) ve LOAD_NAME. İkincisi hem yerel hem de globals'a bakar ve sadece bir fonksiyon kodu nesnesi optimize edilemediğinde kullanılır, çünkü LOAD_NAME çok daha yavaştır.Eğer gerçektenlocals buradan takip edilecek cos istedi Eğer

, sen iyileştirilmemiş olmak için kodu zorlamak olurdu; Bir exec() çağrı (veya exec deyimi) ekleyerek Python 2'de bu sadece eserler:

>>> def unoptimized(x): 
...  exec('pass') 
...  return cos(x) 
... 
>>> dis.dis(unoptimized) 
    2   0 LOAD_CONST    1 ('pass') 
       3 LOAD_CONST    0 (None) 
       6 DUP_TOP    
       7 EXEC_STMT   

    3   8 LOAD_NAME    0 (cos) 
      11 LOAD_FAST    0 (x) 
      14 CALL_FUNCTION   1 
      17 RETURN_VALUE   

Şimdi LOAD_NAMEcos için kullanılan tüm Python bilir çünkü exec() çağrı yerel olarak bu ismi ekledi.

Bu durumda bile, LOAD_NAME yerlileri içine bakar, işlevin kendisinin yerlileri olacaktır ve yerliler yalnızca üst kapsam için olan eval'a iletilir.

+0

Cevabınızı gerçekten anlamadım. Fonksiyonu düşünün: def fn(): a = 1; yazdırma 'yerlilerde?', 'A' yerlilerinde(); , "globals'da", "a" da globals() yazdırır; dönüş (lambda: a)() '- neden bu işe yarar, ama" eval ('lambda: a', {}, {'a': 1})() '? – lumbric

+0

Her kapsamın kendi * kendi * yerlileri vardır. "eval" ana kapsamdır, "lambda" kendi kapsamını, kendi "yerlilerini()" alır. –

+0

İlk işlevdekiyle aynı durum, değil mi? 'a',' fn'nün yerlileri arasındadır ve daha sonra lambda işlevinde kullanılır. İkinci örnekte, “a”, “eval” ın yerlileri arasındadır ve daha sonra lambda işlevinde kullanılır. – lumbric