2017-03-17 88 views
0

我正在嘗試開發一個模塊,它將自動將我的參數轉換爲python對象並創建一個帶參數和對象的字典。如果模塊找到@ name @給出的參數名稱,則必須替換參數值。導入模塊中生成的嵌套lambda函數失敗

這裏的代碼和一個例子在最後。

#FILE convertor.py 
import types 

is_lambda = lambda value : isinstance(value, types.LambdaType) and value.__name__ == '<lambda>' 

def builder(tree, target, item, delimiter): 
    if delimiter not in item: 
     return item 
    result = '' 
    copir = item.split(delimiter) 
    for prefix,var in map(None,copir[::2],copir[1::2]): 
     if prefix is not None: result += prefix 
     if var is None: continue 
     lmdcheck = is_lambda(tree[var]) 
     if lmdcheck or delimiter is '@': 
      result += target+"[\""+var+"\"]" 
     elif delimiter is '$': 
      result += str(tree[var]) 
     else: 
      return None 
    return result 

def resolve_name(tree, target, item): 
    """ 
    Resolves links and string in RHS of parameters 
    """ 
    # Resolve links First 
    result = '' 
    bld = builder(tree, target, item , '@') 
    if bld is None: return None 
    result = bld 
    # And then Resolve strings 
    bld = builder(tree, target, result, '$') 
    if bld is None: return None 
    result = bld 
    return unicode(result) 

def generate(params, target, parent=None): 
    """ 
    @function generate generate recods in root initial directory 
    @param params  - list of (name, value) records 
    @param target  - string of variable name which will be generate (need for lambda(s)), 
    @param parent  - methodtree object which will be updated 
    """ 
    if parent is None: 
     try: 
      exec target+"= {}" 
     except BaseException as e: 
      self.error("Cannot create target object self.{}: {}".format(target,e),"generate") 
      return None 
    else: 
     try: 
      exec target+"= parent" 
     except BaseException as e: 
      self.error("Cannot create target object self.{}=parent: {}".format(target,e),"generate") 
      return None 
    try: 
     exec "tree = "+target 
    except BaseException as e: 
     error("Cannot create temporal variable tree for return target object:".format(e),"generate") 


    for name, value in params: 
     value = resolve_name(tree, target,value)  
     try: 
      exec "{}[\'{}\']={}".format(target,name,value) 
     except BaseException as e: 
      error("Cannot execute operation self.{}[\'{}\']={}: {}".format(target,name,value,e),"generate") 
      return None 
    return tree 

if __name__ == "__main__": 
    params=[ 
     ['parameter', '3'], 
     ['Varibale X','5'], 
     ['Pwered X','@Varibale [email protected]**@[email protected]'], 
     ['FunctionA','lambda x,p:x**p'], 
     ['FunctionB', 'lambda k:@[email protected](k,$parameter$)'] 
    ] 
    dic = generate(params,'dic') 
    for n in dic: 
     print n, dic[n], type(dic[n]) 
     if n == 'FunctionA': 
      print "FunctionA:", dic[n](2,3) 
     if n == 'FunctionB': 
      print "FunctionB:", dic[n](2) 

所以一切都很好地工作,如果我運行python convertor.py

$ python convertor.py 
FunctionA <function <lambda> at 0x7ff116246848> <type 'function'> 
FunctionA: 8 
Varibale X 5 <type 'int'> 
FunctionB <function <lambda> at 0x7ff1162468c0> <type 'function'> 
FunctionB: 8 
parameter 3 <type 'int'> 
Pwered X 125 <type 'int'> 

然而,當我輸入從我convertor.py產生,在嵌套lambda功能不起作用。

#File test.py 
from convertor import generate 
params=[ 
    ['FunctionA','lambda x,p:x**p'], 
    ['parameter', '3'], 
    ['FunctionB', 'lambda k:@[email protected](k,$parameter$)'] 
] 
dic = generate(params,'dic') 
for n in dic: 
    print n, dic[n], type(dic[n]) 
    if n == 'FunctionA': 
     print "FunctionA:", dic[n](2,3) 
    if n == 'FunctionB': 
     print "FunctionB:", dic[n](2) 


FunctionA <function <lambda> at 0x7fe037994848> <type 'function'> 
FunctionA: 8 
FunctionB <function <lambda> at 0x7fe0379948c0> <type 'function'> 
FunctionB: 
Traceback (most recent call last): 
    File "runner.py", line 14, in <module> 
    print "FunctionB:", dic[n](2) 
    File "<string>", line 1, in <lambda> 
NameError: global name 'dic' is not defined 

我在SO中找不到任何相似的主題,但它可能是重複的。

我可能明白爲什麼會發生這種情況。我只是想知道有沒有解決方案?

回答

2

Python的全局變量並非真正的全局變量:它們是基於每個模塊的。此外,函數還會記住它們被定義的模塊,並在那裏查找任何全局引用,而不是從它們被調用的模塊中查找。

因此,您在convertor.py的命名空間中生成的lambda表達式爲exec,並且將嘗試在那裏查找dic。一個可能的解決方案是將一個參數添加到generate(),指定要用於全局名稱的上下文:您的代碼生成語句看起來像exec <something> in context。您通常會爲此參數傳遞globals(),以使用調用模塊的全局變量。

+0

所以它會是'generate(.... context = globals())'並且在生成它的時候'exec ..在上下文中。這是對的嗎? – rth

+0

應該是這樣的。我沒有真正嘗試過你的代碼,並且實際上不太瞭解它,以確保這不會導致其他問題。 – jasonharper

+0

謝謝,它似乎工作。好答案! – rth