为了账号安全,请及时绑定邮箱和手机立即绑定

计算字符串中的数学表达式

计算字符串中的数学表达式

有只小跳蛙 2019-05-30 16:27:53
计算字符串中的数学表达式stringExp = "2^4"intVal = int(stringExp)      # Expected value: 16这将返回以下错误:Traceback (most recent call last):  File "<stdin>", line 1, in <module>ValueError: invalid literal for int()with base 10: '2^4'我知道eval是否有一种更好、更安全的方法来计算存储在字符串中的数学表达式?
查看完整描述

3 回答

?
长风秋雁

TA贡献1757条经验 获得超7个赞

eval是邪恶的

eval("__import__('os').remove('important file')") # arbitrary commands

eval("9**9**9**9**9**9**9**9", {'__builtins__': None}) # CPU, memory

注意:即使您使用SET__builtins__到None仍然有可能通过内省来爆发:


eval('(1).__class__.__bases__[0].__subclasses__()', {'__builtins__': None})

使用ast

import ast

import operator as op


# supported operators

operators = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul,

             ast.Div: op.truediv, ast.Pow: op.pow, ast.BitXor: op.xor,

             ast.USub: op.neg}


def eval_expr(expr):

    """

    >>> eval_expr('2^6')

    4

    >>> eval_expr('2**6')

    64

    >>> eval_expr('1 + 2*3**(4^5) / (6 + -7)')

    -5.0

    """

    return eval_(ast.parse(expr, mode='eval').body)


def eval_(node):

    if isinstance(node, ast.Num): # <number>

        return node.n

    elif isinstance(node, ast.BinOp): # <left> <operator> <right>

        return operators[type(node.op)](eval_(node.left), eval_(node.right))

    elif isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1

        return operators[type(node.op)](eval_(node.operand))

    else:

        raise TypeError(node)

您可以很容易地限制每个操作或任何中间结果的允许范围,例如限制输入参数a**b:


def power(a, b):

    if any(abs(n) > 100 for n in [a, b]):

        raise ValueError((a,b))

    return op.pow(a, b)

operators[ast.Pow] = power

或者限制中间结果的大小:


import functools


def limit(max_=None):

    """Return decorator that limits allowed returned values."""

    def decorator(func):

        @functools.wraps(func)

        def wrapper(*args, **kwargs):

            ret = func(*args, **kwargs)

            try:

                mag = abs(ret)

            except TypeError:

                pass # not applicable

            else:

                if mag > max_:

                    raise ValueError(ret)

            return ret

        return wrapper

    return decorator


eval_ = limit(max_=10**100)(eval_)

>>> evil = "__import__('os').remove('important file')"

>>> eval_expr(evil) #doctest:+IGNORE_EXCEPTION_DETAIL

Traceback (most recent call last):

...

TypeError:

>>> eval_expr("9**9")

387420489

>>> eval_expr("9**9**9**9**9**9**9**9") #doctest:+IGNORE_EXCEPTION_DETAIL

Traceback (most recent call last):

...

ValueError:


查看完整回答
反对 回复 2019-05-30
?
慕无忌1623718

TA贡献1744条经验 获得超4个赞

一些安全的替代方案eval()sympy.sympify().evalf()*:

*塞佩sympify也是不安全的,根据以下来自文档的警告。

警告:请注意,此函数使用eval,因此不应该用于未经消毒的输入。


查看完整回答
反对 回复 2019-05-30
  • 3 回答
  • 0 关注
  • 831 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信