2012-06-20 8 views
11

Dada una cadena comocrear una función lambda de una cadena ** ** adecuadamente

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

quiero para generar la función lambda correspondiente,

fn = lambda i,j: 2*(i+j) <= 100 
  • puedo hacer esto con eval, pero estoy buscando un método menos malo.

  • he encontrado

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

    pero no he encontrar la manera de ejecutar el resultado!

  • Idealmente, me gustaría extraer automáticamente la lista de parámetros ('i', 'j') también - en este momento, solo estoy usando re.findall ('\ w +'), pero me encantaría para poder usar correctamente funciones existentes como cos en lugar de sombrearlas como 'palabras clave'.


estaba mirando Is there a Python library for handling complicated mathematical sets (constructed using mathematical set-builder notation)? y tratando de averiguar la mejor manera de analizar la notación de conjuntos constructor en las lambdas para alimentar a la restricción-solver.

Básicamente estoy deseando ast.literal_eval que también reconozca las variables.

Idealmente, dado i >= 20 me gustaría volver ((lambda x: x >= 20), ['i']) que podría alimentar directamente al constraint.

+0

¿Cuál es el problema que estás tratando de resolver? ¿Estás haciendo un bucle de lectura e interpretación gp? – starbolin

Respuesta

2

Si la entrada es de una fuente confiable , la eval() es la manera más fácil, más clara y más fiable para ir.

Si su entrada es que no se confía, entonces tiene que ser desinfectados .

Un enfoque razonable es el uso de una expresión regular. Asegúrese de que no haya llamadas a funciones, búsquedas de atributos o guiones bajos dobles en la cadena.

Alternativamente, un enfoque más sofisticado es recorrer el árbol de análisis sintáctico de AST para determinar si hay alguna llamada objetable.

Un tercer enfoque consiste en recorrer el árbol de análisis AST y ejecutarlo directamente. Eso te pone en control total sobre lo que recibe llamadas. La función ast.literal_eval toma este enfoque.Tal vez comiences con su origen y hagas algunas construcciones para cualquier operación que quieras admitir:

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

Está buscando una alternativa al eval, pero ¿por qué? Está aceptando código arbitrario y ejecutándolo de todos modos, ¿por qué no usar eval? La única razón para evitar eval es porque es peligroso, pero la lambda que termines creando será igual de peligrosa.

Además, tenga en cuenta, you really can't make it safe to do this in CPython

+4

** No se puede hacer ** es una frase que rara vez se aplica a Python. La función * ast.literal_eval * es un buen ejemplo de cómo evaluar código arbitrario mientras limita lo que puede aceptar. Además, si el OP está trabajando con * trusted * input, entonces * eval * o * exec * es perfectamente razonable (Guido los usa en el módulo * timeit * por ejemplo). –

+1

'ast.literal_eval' no es adecuado para el problema de OP, ya que quiere expresiones con evaluación en ellos. Mi punto aquí es seguir adelante y usar 'eval', es peligroso, pero también lo es su objetivo real, por lo que cualquier otro método será igualmente peligroso. –

+0

Bonita publicación de blog. – dreftymac

Cuestiones relacionadas