2017-05-07 14 views
2

数式を文字列として入力して評価するプログラムを書いています。これまでのところ、私はこれを考え出しました。簡単な文字列の方程式を安全に評価する

test_24_string = str(input("Enter your answer: ")) 
test_24 = eval(test_24_string) 

この式の文字列バージョンと評価版の両方が必要です。しかし、evalは非常に危険な機能です。しかし、それは方程式なのでint()を使うことはできません。数値を入力する場合のように、文字列から数式を評価するPython関数はありますか?

答えて

3

一方向はです。これは、主に最適化するためのモジュール(マルチスレッド)操作だが、それはまた、数学的なPythonの式を処理することができます

>>> import numexpr 
>>> numexpr.evaluate('2 + 4.1 * 3') 
array(14.299999999999999) 

あなたのpythonのようなタイプを取得する結果に.itemを呼び出すことができます。

>>> numexpr.evaluate('17/3').item() 
5.666666666666667 

をそれは第三者の拡張モジュールなので、ここでは過度の残虐行為かもしれませんが、それは確かにevalよりも安全であり、かなり多くの機能(numpymath操作を含む)をサポートしています。また、「変数の置換」をサポートしている場合:

>>> b = 10 
>>> numexpr.evaluate('exp(17)/b').item() 
2415495.27535753 

一つのpython標準ライブラリとの双方向、非常に限られast.literal_evalですが。これは、Pythonで最も基本的なデータ型とリテラルのために動作します:

>>> import ast 
>>> ast.literal_eval('1+2') 
3 

しかし、のような複雑な式で失敗します。

>>> ast.literal_eval('import os') 
SyntaxError: invalid syntax 

>>> ast.literal_eval('exec(1+2)') 
ValueError: malformed node or string: <_ast.Call object at 0x0000023BDEADB400> 

残念ながら+-以外の任意のオペレーターはできません。

>>> ast.literal_eval('1.2 * 2.3') 
ValueError: malformed node or string: <_ast.BinOp object at 0x0000023BDEF24B70> 

サポートされている種類のドキュメントをここにコピーしました。

Safely evaluate an expression node or a string containing a Python literal or container display. The string or node provided may only consist of the following Python literal structures: strings, bytes, numbers, tuples, lists, dicts, sets, booleans, and None.

+2

十分な奇妙な、しかしliteral_eval'だけ' + 'と' -'と整数のみに対応しています。他の演算子は同じ '不正な形式の... 'エラーを生成します。 py3.5でテストされています。 – robyschek

+0

@robyschek浮動小数点で失敗を再現できません( 'ast.literal_eval( '1.2 + 2.3')'が正しく動作します)。しかし、あなたはそうではありませんすべての演算子が可能です。私は別の方法を追加しました - (多くの)より多くの機能を実装します。 – MSeifert

+0

@MSeifertは 'numpy'をインストールしました。 'numpy'と' numexpr'をインポートした後、 'ModuleNotFoundError:No numexpr 'というモジュールがありません。私は再インストールしようとしました。 – Aamri

1

ポストフィックスエクスプレッションエバリュエーターを書くのはそれほど難しいことではありません。以下は実際の例です。 (githubの上でもavailable。)

import operator 
import math 

_add, _sub, _mul = operator.add, operator.sub, operator.mul 
_truediv, _pow, _sqrt = operator.truediv, operator.pow, math.sqrt 
_sin, _cos, _tan, _radians = math.sin, math.cos, math.tan, math.radians 
_asin, _acos, _atan = math.asin, math.acos, math.atan 
_degrees, _log, _log10 = math.degrees, math.log, math.log10 
_e, _pi = math.e, math.pi 
_ops = {'+': (2, _add), '-': (2, _sub), '*': (2, _mul), '/': (2, _truediv), 
     '**': (2, _pow), 'sin': (1, _sin), 'cos': (1, _cos), 'tan': (1, _tan), 
     'asin': (1, _asin), 'acos': (1, _acos), 'atan': (1, _atan), 
     'sqrt': (1, _sqrt), 'rad': (1, _radians), 'deg': (1, _degrees), 
     'ln': (1, _log), 'log': (1, _log10)} 
_okeys = tuple(_ops.keys()) 
_consts = {'e': _e, 'pi': _pi} 
_ckeys = tuple(_consts.keys()) 


def postfix(expression): 
    """ 
    Evaluate a postfix expression. 

    Arguments: 
     expression: The expression to evaluate. Should be a string or a 
        sequence of strings. In a string numbers and operators 
        should be separated by whitespace 

    Returns: 
     The result of the expression. 
    """ 
    if isinstance(expression, str): 
     expression = expression.split() 
    stack = [] 
    for val in expression: 
     if val in _okeys: 
      n, op = _ops[val] 
      if n > len(stack): 
       raise ValueError('not enough data on the stack') 
      args = stack[-n:] 
      stack[-n:] = [op(*args)] 
     elif val in _ckeys: 
      stack.append(_consts[val]) 
     else: 
      stack.append(float(val)) 
    return stack[-1] 

使用法: `

In [2]: from postfix import postfix 

In [3]: postfix('1 2 + 7 /') 
Out[3]: 0.42857142857142855 

In [4]: 3/7 
Out[4]: 0.42857142857142855 
関連する問題