2012-06-20 160 views
11

给定一个字符串,如字符串创建lambda函数** **正确

"2*(i+j) <= 100" 

我要生成相应的lambda函数,

fn = lambda i,j: 2*(i+j) <= 100 
  • 我能做到这一点与eval,但我正在寻求一个不太邪恶的方法。

  • 我发现

    import ast 
    f = ast.Lambda('i,j', '2*(i+j) <= 100') 
    

    但我还没有想出如何执行的结果!我想自动拉出参数列表('i','j') - 现在,我只是使用re.findall('\ w +'),但我会爱上以便能够正确使用cos等现有功能,而不是将它们作为“关键字”隐藏起来。


我看着Is there a Python library for handling complicated mathematical sets (constructed using mathematical set-builder notation)?并试图找出如何最好地解析集合建构式符号到lambda表达式喂到约束解算器。

我基本上希望能够识别变量的ast.literal_eval。

理想情况下,给出i >= 20我想找回((lambda x: x >= 20), ['i']),然后我可以直接给constraint喂食。

+0

你试图解决什么问题?你在制作一个gp读取 - 解释循环吗? – starbolin

回答

2

如果你的输入来自可信源,则的eval()是最容易的,最清晰,最可靠的方式。

如果你的输入是不可信,那么它需要是消毒

一个合理的方法是使用正则表达式。确保字符串中没有函数调用,属性查找或双下划线。

或者,更复杂的方法是走AST分析树来确定是否有任何令人反感的调用。

第三种方法是行走AST分析树并直接执行它。这可以让你完全控制接听电话。 ast.literal_eval函数采用这种方法。也许你从源头开始,为你想要支持的任何操作做一些扩展:

def literal_eval(node_or_string): 
    """ 
    Safely evaluate an expression node or a string containing a Python 
    expression. The string or node provided may only consist of the following 
    Python literal structures: strings, numbers, tuples, lists, dicts, booleans, 
    and None. 
    """ 
    _safe_names = {'None': None, 'True': True, 'False': False} 
    if isinstance(node_or_string, basestring): 
     node_or_string = parse(node_or_string, mode='eval') 
    if isinstance(node_or_string, Expression): 
     node_or_string = node_or_string.body 
    def _convert(node): 
     if isinstance(node, Str): 
      return node.s 
     elif isinstance(node, Num): 
      return node.n 
     elif isinstance(node, Tuple): 
      return tuple(map(_convert, node.elts)) 
     elif isinstance(node, List): 
      return list(map(_convert, node.elts)) 
     elif isinstance(node, Dict): 
      return dict((_convert(k), _convert(v)) for k, v 
         in zip(node.keys, node.values)) 
     elif isinstance(node, Name): 
      if node.id in _safe_names: 
       return _safe_names[node.id] 
     elif isinstance(node, BinOp) and \ 
      isinstance(node.op, (Add, Sub)) and \ 
      isinstance(node.right, Num) and \ 
      isinstance(node.right.n, complex) and \ 
      isinstance(node.left, Num) and \ 
      isinstance(node.left.n, (int, long, float)): 
      left = node.left.n 
      right = node.right.n 
      if isinstance(node.op, Add): 
       return left + right 
      else: 
       return left - right 
     raise ValueError('malformed string') 
    return _convert(node_or_string) 
14

您正在寻找eval的替代品,但是为什么?你接受任意代码并执行它,为什么不使用eval?避免eval的唯一原因是因为它很危险,但最终创建的lambda会同样危险。

而且,记住,you really can't make it safe to do this in CPython

+4

**无法完成**是一种很少应用于Python的短语。 * ast.literal_eval *函数是如何评估任意代码,同时限制你可以接受的一个很好的例子。另外,如果OP使用* trusted *输入,则* eval *或* exec *是完全合理的(例如Guido在* timeit *模块中使用它们)。 –

+1

'ast.literal_eval'不适合OP的问题,因为他希望表达式具有评估。我的意思是继续使用'eval',这很危险,但他的实际目标也是如此,所以其他任何方法都是同样危险的。 –

+0

不错的博客文章。 – dreftymac