2015-09-05 48 views
2

我正在研究一个应用程序,它允许用户通过输入一个字符串来定义一个数学函数(就像你在wolfram alpha上看到的那样)定义的范围。我使用eval()函数来解释字符串并填充y值列表(已定义的x值列表)。我只允许eval()访问一些常见的numpy数学函数和变量名'x'。Python3:与自定义字典不评估数字的eval()

该类允许我为用户输入的每个字符串创建一个对象,并创建两个列表x和y,用于使用matplotlib进行绘图。你可以运行这个MWE,看到它处理x罚函数(例如,sin(x),ln(x),3 * x等),并根据需要抛出非数学函数例如'foo'的异常。但是,给它一个数字如'3'或'4.00'会导致eval()在self.y列表中不写任何内容。您可以在代码中的各个点上打印self.y的形状,并且您会看到只要eval()运行(当输入的字符串是数字时),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中写入一个列表(它实际上是一维numpy数组,但我们不需要迂腐),长度与self.x相同,填充了4.0的。

这不是一个网络或服务器应用程序,我是非常清楚的固有EVAL的风险(),所以请没有手指晃着如何使用它:)

+0

您可以举一个最简单的例子来描述预期的功能。 – shanmuga

回答

1

的问题是需要情节的列表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什么都不做,并隐藏有关什么样的异常提出的信息。只要删除尝试/除外;如果发生异常,它会传播,你会看到它是什么样的异常。同样,你的尝试/除了最后没有用处。它所做的只是打印一条信息,该信息比没有发现异常情况下打印的信息要少。

+0

我添加了你的建议,检查self.y的类类型,并很好地处理了这个问题。感谢您的简洁和有益的答案。我还纠正了你所看到的其他问题,并将在课堂外捕获异常情况。亲切的谢谢你,邻居。 – wrkyle