Python3: eval() with custom dict not evaluating numbers

Python3: eval() with custom dict not evaluating numbers

我正在开发一个应用程序,它允许用户通过输入字符串(就像您在 wolfram alpha 上看到的那样)来定义数学函数,并在某个预定义的范围内绘制该函数。我使用 eval() 函数来解释字符串并填充 y 值列表(已定义的 x 值列表)。我只允许 eval() 访问一些常见的 numpy 数学函数和变量名 'x'。

这 class 允许我为用户输入的每个字符串创建一个对象,并创建两个列表 x 和 y,以便使用 matplotlib 绘图。您可以 运行 这个 MWE 并看到它可以很好地处理 x 的函数(例如 sin(x)、ln(x)、3*x 等)并抛出非数学函数的异常,例如 'foo' 根据需要。但是,给它一个像“3”或“4.00”这样的数字会导致 eval() 不向 self.y 列表中写入任何内容。您可以在代码中的不同点打印 self.y 的形状,并且一旦 eval() 运行s(当输入的字符串是数字时)就会看到 self.y 的形状变成 ().

#!/usr/bin/env python3

import numpy as np
import matplotlib.pyplot as plt


class functionType():

    def __init__(self, funcStr, xlo=0.0, xhi=10.0, res=100):

        self.x = []
        self.y = []
        self.xlo = xlo
        self.xhi = xhi
        self.res = res
        self.funcStr = funcStr
        self.x = np.linspace(self.xlo, self.xhi, self.res)
        self.safe_dict = {'np':np,
                          'sin':np.sin,
                          'cos':np.cos,
                          'tan':np.tan,
                          'arcsin':np.arcsin,
                          'arccos':np.arccos,
                          'arctan':np.arctan,
                          'sinh':np.sinh,
                          'cosh':np.cosh,
                          'tanh':np.tanh,
                          'arcsinh':np.arcsinh,
                          'arccosh':np.arccosh,
                          'arctanh':np.arctanh,
                          'ln':np.log,
                          'log10':np.log10,
                          'log2':np.log2,
                          'exp':np.exp,
                          'sqrt':np.sqrt,
                          'abs':np.fabs,
                          'x':self.x}

        try:
            self.y = eval(self.funcStr,{__builtins__:None},self.safe_dict)
        except Exception:
            raise Exception

    def _reMakeData(self):

        self.x = np.linspace(self.xlo, self.xhi, self.res)
        self.safe_dict['x'] = self.x
        self.y = eval(self.funcStr,{__builtins__:None},self.safe_dict)

    def setXLow(self, value):
        self.xlo = value
        self._reMakeData()

    def setXHigh(self, value):
        self.xhi = value
        self._reMakeData()

    def setRes(self, value):
        self.res = value
        self._reMakeData()

    def getXLow(self):
        return self.xlo

    def getXHigh(self):
        return self.xhi

    def getRes(self):
        return self.res

    def getData(self):
        return self.x, self.y



func = input("gimme a function:  ")
try:
    plot1 = functionType(func)
    x, y = plot1.getData()
    plt.plot(x,y,marker='',color='red')
    plt.show()
except Exception as e:
    print("no good:  ",e)

有人看到这里的问题吗?我希望能够处理想要绘制常量函数的用户。需要明确的是,当我给它一个像 4.0 这样的常量时,我​​希望它将一个列表写入 self.y (它实际上是一个 1D numpy 数组,但我们不必迂腐)与 self.x 填充的长度相同4.0 的。

这不适用于 Web 或服务器应用程序,我完全了解 eval() 固有的风险,所以请不要犹豫使用它:)

问题是绘图需要 y 值列表与 x 值列表的长度相同。如果你输入的表达式涉及x,这就可以正常工作,因为x是一个numpy数组,所以对其进行数学运算将产生一个相同长度的数组。但是如果你只是输入一个数字 y 会被设置为一个数字,而不是一个数组。

一种可能是在设置y后检查它是否是一个numpy数组,如果不是,则假设它是一个数字并重复适当的次数:

    if not isinstance(self.y, np.ndarray):
        self.y = np.repeat(self.y, len(self.x))

您的代码还有其他一些错误和奇怪的事情。在你传递给 eval 的字典中,你想使用 {'__builtins__': None},并在 __builtins__ 周围加上引号。另外,您对 try/except 的使用毫无意义。执行 except Exception 后立即执行 raise Exception 没有任何作用,并且隐藏了有关引发何种异常的信息。只需删除 try/except;如果引发异常,它将向上传播,您将看到它是哪种异常。同样,您最后的 try/except 也没有用;它所做的只是打印一条消息,该消息的信息量少于如果您没有捕获异常时将打印的消息。