2012-06-20 70 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