2015-09-05 1 views
2

Je travaille sur une application qui permet à un utilisateur de définir une fonction mathématique en entrant une chaîne de caractères (comme vous le voyez sur wolfram alpha) et en traçant la fonction sur plage définie. J'utilise la fonction eval() pour interpréter la chaîne et remplir une liste de valeurs y (liste de valeurs x déjà définies). Je n'autorise l'accès à eval() qu'à quelques fonctions mathématiques numpy et au nom de variable 'x'.Python3: eval() avec un dict personnalisé n'évaluant pas les nombres

Cette classe me permet de créer un objet pour chaque chaîne entrée par l'utilisateur et de créer deux listes, x et y, pour tracer avec matplotlib. Vous pouvez exécuter ce MWE et voir qu'il gère les fonctions de x fin (par exemple, sin (x), ln (x), 3 * x, etc.) et lance des exceptions pour les fonctions non mathématiques comme 'foo' comme souhaité. Cependant, en lui donnant un nombre comme '3' ou '4,00', eval() n'écrit rien dans la liste self.y. Vous pouvez imprimer la forme de self.y à différents points dans le code et vous voyez que dès que eval() s'exécute (quand la chaîne entrée est un nombre) la forme de self.y devient().

#!/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) 

Quelqu'un voit-il le problème ici? Je voudrais être capable de gérer l'utilisateur voulant tracer une fonction constante. Pour être clair, quand je lui donne une constante comme 4.0 je veux qu'il écrive une liste dans self.y (c'est en fait un tableau 1D numpy mais nous n'avons pas besoin d'être pédant) de la même longueur que self.x rempli de 4.0 .

Ce n'est pas pour une application Web ou un serveur, je suis parfaitement conscient des risques inhérents à eval() alors s'il vous plaît aucun doigt waggling à l'utiliser :)

+0

Pourriez-vous donner un exemple minimal pour décrire la fonctionnalité prévue. – shanmuga

Répondre

1

Le problème est que la parcelle doit liste des Les valeurs y ont la même longueur que la liste des valeurs x. Si l'expression que vous entrez implique x, cela fonctionne très bien, car x est un tableau numpy, donc faire des opérations mathématiques sur celui-ci produira un tableau de la même longueur. Mais si vous entrez juste un nombre y obtient un nombre unique, pas un tableau du tout.

Une possibilité serait de vérifier après la mise en y si elle est un tableau numpy, et sinon, on suppose qu'il est un numéro unique et répéter le nombre approprié de fois:

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

Votre code a plusieurs d'autres erreurs et des choses étranges. Dans votre dict que vous passez à eval, vous souhaitez utiliser {'__builtins__': None}, avec des guilles autour de __builtins__. En outre, votre utilisation de try/except est plutôt inutile. Faire except Exception suivi immédiatement par raise Exception ne fait rien, et cache les informations sur le type d'exception qui a été soulevée. Retirez simplement l'essai/excepté; Si une exception est générée, elle se propagera et vous verrez le type d'exception. De même votre essai/sauf à la fin n'est pas utile; Tout ce qu'il fait est imprimer un message qui est moins informatif que le message qui aurait été imprimé si vous n'aviez pas attrapé l'exception.

+0

J'ai ajouté votre suggestion de vérifier le type de classe de self.y et qui a bien géré le problème. Merci pour votre réponse concise et utile. J'ai également corrigé les autres problèmes que vous avez vus et attraper des exceptions en dehors de la classe. Merci beaucoup, voisin. – wrkyle