2017-03-17 86 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