2013-08-28 6 views
9

ग्लोबल्स के रूप में चर या पाइथन के फ़ंक्शन के स्थानीय लोगों के रूप में eval() पर भिन्नता क्यों होती है?पाइथन के eval() का उपयोग करते समय स्थानीय और ग्लोबल्स के बीच क्या अंतर है?

described in the documenation के साथ, पायथन __builtins__ को ग्लोबल्स में कॉपी करेगा, अगर स्पष्ट रूप से नहीं दिया गया है। लेकिन कुछ और अंतर भी होना चाहिए जो मैं नहीं देख सकता।

निम्न उदाहरण फ़ंक्शन पर विचार करें। यह एक स्ट्रिंग code लेता है और फ़ंक्शन ऑब्जेक्ट देता है। बिल्टिन की अनुमति नहीं है (उदा। abs()), लेकिन math पैकेज से सभी फ़ंक्शन।

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) 

यह काम करता है के रूप में किसी भी स्थानीय या वैश्विक वस्तुओं का उपयोग नहीं की उम्मीद:

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

लेकिन यह math कार्यों का उपयोग कर काम नहीं करता:

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

यह निम्नलिखित अपवाद आउटपुट:

<string> in <lambda>(x) 
    NameError: global name 'cos' is not defined 

लेकिन जब वैश्विक रूप में एक ही मानचित्रण गुजर यह काम करता है:

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, {}) 

एक ही रूप में ऊपर के उदाहरण:

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

क्या विस्तार से यहाँ क्या होता है?

+0

प्रलेखन में यह कहा जाता है कि "ग्लोबल्स शब्दकोश मौजूद है और '__builtins__' की कमी है। मुझे लगता है कि इसका मतलब यह है कि कुंजी '__builtins__' मौजूद नहीं है, लेकिन आपके उदाहरण में, आप इसे किसी भी पर सेट नहीं करते हैं। मेरा सुझाव है कि आपको अनुमति दी जानी चाहिए ['__ बनाया गया'] = किसी भी डेल द्वारा दिए गए ['__ बनाया गया'] और इसे फिर से प्रयास करें। – jorispilot

+0

@jorispilot मेरा मुद्दा __builtins__ से जुड़ा नहीं है। मैंने अभी इसका उल्लेख किया है, क्योंकि eval द्वारा globals के इस विशेष आवरण है। जिस तरह से मैंने __builtins__ को सेट नहीं किया है, वह सही है, अगर आप 'abs' जैसे बिल्टिन फ़ंक्शंस को मना करना चाहते हैं। 'cos' एक निर्मित फ़ंक्शन नहीं है, यह 'गणित' मॉड्यूल का हिस्सा है। – lumbric

उत्तर

8

पायथन डिफ़ॉल्ट रूप से ग्लोबल्स के रूप में नाम दिखता है; केवल फ़ंक्शंस में असाइन किए गए नाम स्थानीय लोगों के रूप में देखे जाते हैं (इसलिए कोई भी नाम जो फ़ंक्शन के लिए पैरामीटर है या फ़ंक्शन में असाइन किया गया था)। एक वैश्विक नाम के रूप में

>>> 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 भार cos, केवल वैश्विक नाम स्थान में देख:

आप यह देख सकते हैं जब आप कोड वस्तुओं या कार्यों डिकंपाइल करने dis.dis() समारोह का उपयोग करें। LOAD_FAST ऑपोड इंडेक्स द्वारा नाम देखने के लिए वर्तमान नेमस्पेस (फ़ंक्शन लोकल) का उपयोग करता है (फ़ंक्शन स्थानीय नेमस्पेस अत्यधिक अनुकूलित और सी सरणी के रूप में संग्रहीत होते हैं)।

नाम देखने के लिए तीन और ऑपकोड हैं; LOAD_CONST (सच्चे स्थिरांक के लिए आरक्षित, जैसे None और अपरिवर्तनीय मानों के लिए शाब्दिक परिभाषाएं), LOAD_DEREF (बंद करने के संदर्भ में) और LOAD_NAME। उत्तरार्द्ध स्थानीय और ग्लोबल्स दोनों को देखता है और केवल तब उपयोग किया जाता है जब फ़ंक्शन कोड ऑब्जेक्ट को अनुकूलित नहीं किया जा सका, क्योंकि LOAD_NAME बहुत धीमा है।

तुम सच में चाहता था तो coslocals में देखा जाए, तो आप unoptimised होने के लिए कोड के लिए मजबूर करना होगा; इस केवल अजगर 2 में काम करता है, एक exec() कॉल (या exec बयान) जोड़कर:

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

अब LOAD_NAMEcos के लिए प्रयोग किया जाता है क्योंकि के लिए सभी अजगर जानता है, exec() कॉल एक स्थानीय रूप में उस नाम जोड़ा गया।

इस मामले में भी स्थानीय लोग LOAD_NAME देख रहे हैं, फ़ंक्शन के स्थानीय लोग होंगे, और स्थानीय लोग eval पर नहीं गए हैं, जो केवल माता-पिता के दायरे के लिए हैं।

+0

मुझे आपका जवाब वास्तव में समझ में नहीं आया। फ़ंक्शन पर विचार करें: 'def fn(): a = 1; स्थानीय लोगों में एक 'स्थानीय में' प्रिंट करें, 'ए' प्रिंट करें(); प्रिंट 'ए इन ग्लोबल्स?', ग्लोबल्स में 'ए' प्रिंट करें(); वापसी (लैम्ब्डा: ए)() '- यह क्यों काम करता है, लेकिन 'eval (' lambda: a ', {}, {' a ': 1})()'? – lumbric

+0

क्योंकि प्रत्येक दायरे में * अपने * स्थानीय हैं। 'eval' माता-पिता का दायरा है, 'लैम्ब्डा' का अपना गुंजाइश है, इसका अपना 'स्थानीय()'। –

+0

पहले कार्य में समान स्थिति, है ना? 'ए'' एफएन' के स्थानीय लोगों में है और फिर लैम्ब्डा फ़ंक्शन में उपयोग किया जाता है। दूसरे उदाहरण में, 'ए'' eval' के स्थानीय लोगों में है और फिर लैम्ब्डा फ़ंक्शन में उपयोग किया जाता है। – lumbric

संबंधित मुद्दे