Вопрос по python, string, lambda – правильно создать лямбда-функцию из строки **

12

Учитывая строку, такую как

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

Я хочу сгенерировать соответствующую лямбда-функцию,

fn = lambda i,j: 2*(i+j) <= 100
  • I can do this with eval, but I am seeking a less evil method.

  • I have found

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

    but I haven't figure out how to execute the result!

  • Ideally, I would like to automatically pull out the parameter list ('i','j') as well - right now, I am just using re.findall('\w+'), but I would love to be able to properly use existing functions like cos instead of shadowing them as 'keywords'.


Я смотрел наСуществует ли библиотека Python для обработки сложных математических наборов (построенных с использованием математической нотации построения наборов)? и пытаясь выяснить, как лучше всего проанализировать нотацию построителя множеств в лямбдах, чтобы передать их решателю ограничений.

Я, в основном, желаю ast.literal_eval, который бы также распознавал переменные.

В идеале, учитываяi >= 20 Я хотел бы вернуться((lambda x: x >= 20), ['i']) который я мог бы затем кормить непосредственноconstraint.

Какую проблему вы пытаетесь решить? Вы делаете цикл чтения-интерпретации gp? starbolin

Ваш Ответ

2   ответа
14

eval, но почему? Вы принимаете произвольный код и выполняете его в любом случае, так почему бы не использоватьeval? Единственная причина, чтобы избежатьeval потому что это опасно, но лямбда, которую вы в итоге создадите, будет такой же опасной.

Кроме того, имейте в виду,вы действительно не можете сделать это безопасно в CPython

Can't be done это фраза, которая редко применяется к Python.ast.literal_eval Функция - прекрасный пример того, как оценивать произвольный код, ограничивая то, что вы можете принять. Также, если OP работает сtrusted вход, тоeval или жеexec вполне разумно (Гвидо использует их вtimeit модуль например).
ast.literal_eval непригоден для проблемы ОП, так как ему нужны выражения с оценкой в них. Моя точка зрения заключается в том, чтобы идти вперед и использоватьevalЭто опасно, но такова и его реальная цель, поэтому любой другой метод будет столь же опасным.
Хороший пост в блоге.
3

trusted source,eval() это самый простой, ясный и самый надежный путь.

Если ваш вкладuntrustedтогда это должно бытьsanitized.

Одним из разумных подходов является использование регулярных выражений. Убедитесь, что в строке нет вызовов функций, поиска атрибутов или двойных подчеркиваний.

В качестве альтернативы, более сложным подходом является обход дерева анализа 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)

Похожие вопросы