2017-04-19 9 views
1

eval()の値の範囲を制限できますか?Pythonのeval()結果の範囲を制限できますか?

  1. eval('12345678**9')は多数
  2. eval('1234567**8**9')どちらもリターンを返すも、私は#2と#1と一緒に暮らすことができませんでしたが、例外

をスローします。私が必要とするのは、32ビット整数の範囲の結果だけです。あまりにも多い数の計算を停止するようにevalに指示する方法がない、またはそこにいることを恐れていますか?これらの線に沿って

+4

に応答して、少しだけ数学的な実験です。evalを使用しないことで、evalの値の範囲を制限することができます。代わりに手動で文字列を解析します。 – wim

+1

私はそれが計算するのに非常に時間がかかると思う。 –

+0

編集してくれてありがとう、私は自分自身にもやってみた。 –

答えて

-1

何か:

from math import log 

def bounded_eval(expression, bits = 32): 
    nums = expression.split('**') 
    if len(nums) == 1: 
     val = eval(expression) 
     if log(val,2) > bits: 
      return "too large" 
     else: 
      return val 
    else: 
     base = nums[0] 
     power = '**'.join(nums[1:]) 
     base = eval(base) 
     power = eval(power) 
     if power*log(base,2) > bits: 
      return "too large" 
     else: 
      return pow(base,power) 

これは、潜在的なセキュリティリスクであるeval()を使用していますが、あなたは自分自身のコードが生成する算術式にそれを求めているならば、それは本当に問題ではありません。明らかに、エラーを発生させるコードによって "大きすぎる"を返すコードを置き換えることができます。

+0

はい、式は9桁の数字と演算子+、 - 、*、/または**のみで構成されます。唯一の危険は結果が非常に大きくなることです。私はPythonの初心者ですが、プログラミングの初心者ではありません。 –

1

astを使用して文字列を解析してツリーを歩く前に、「電卓」と書いています。 evalと違って、私は特に、ピッキングや操作は、私ができるようにしたいものを選んだ

import ast 
import ctypes 
import operator 

def _pow(a, b): 
    if isinstance(a, (ctypes.c_int, ctypes.c_float, ctypes.c_double)): 
     a = float(a.value) 
    if isinstance(b, (ctypes.c_int, ctypes.c_float, ctypes.c_double)): 
     b = float(b.value) 
    return ctypes.c_double(a ** b) 

def _wrap_bin_op(op): 
    def wrapper(a, b): 
     if isinstance(a, (ctypes.c_int, ctypes.c_float, ctypes.c_double)): 
      a = float(a.value) 
     if isinstance(b, (ctypes.c_int, ctypes.c_float, ctypes.c_double)): 
      b = float(b.value) 
     return ctypes.c_double(op(a, b)) 
    return wrapper 

def _wrap_unary_op(op): 
    def wrapper(a): 
     if isinstance(a, (ctypes.c_int, ctypes.c_float)): 
      a = float(a.value) 
     return ctypes.c_double(op(a)) 
    return wrapper 


_OP_MAP = { 
    ast.Add: _wrap_bin_op(operator.add), 
    ast.Sub: _wrap_bin_op(operator.sub), 
    ast.Pow: _wrap_bin_op(operator.pow), 
    ast.Mult: _wrap_bin_op(operator.mul), 
    ast.Div: _wrap_bin_op(operator.truediv), 
    ast.Invert: _wrap_unary_op(operator.neg), 
} 


class Calc(ast.NodeVisitor): 

    def visit_BinOp(self, node): 
     left = self.visit(node.left) 
     right = self.visit(node.right) 
     return _OP_MAP[type(node.op)](left, right) 

    def visit_Num(self, node): 
     if isinstance(node.n, int): 
      val = ctypes.c_int(node.n) 
     elif isinstance(node.n, float): 
      val = ctypes.c_double(node.n) 
     return val 

    def visit_Expr(self, node): 
     return self.visit(node.value) 

    @classmethod 
    def evaluate(cls, expression): 
     tree = ast.parse(expression) 
     calc = cls() 
     return calc.visit(tree.body[0]) 


print(Calc.evaluate('12345678**8')) 
print(Calc.evaluate('5 * 8')) 

注ことを - と私は支配している:この場合、あなたには、いくつかの策略をしたい場合は、あなたがこの作品を作ることができます彼らはどのように行動する。この場合、私はすべての計算を​​でやっています。HUGEの数字です。私はまた整数__pow__を防ぎ、特定の力に上げる前にこれらの引数を浮動小数点にすることを強制しています。

+0

これはいいですね。私は以前に 'literal_eval()'を使ってきましたが、最も単純な式にしか使えません。より複雑な構文解析の問題に 'ast'モジュールを使用する方法を知ることは良いことです。 +1 –

+0

@JohnColeman - うん、私はこれを数回やった。それはかなりきれいです。私はここで私の例でc型を使って実際に何かを購入しているのかどうかはわかりません。なぜなら、ctypes型は私が期待している数学演算子を提供していないからです。 – mgilson

0

John Colemanさんがお勧めするように、ここで私自身の解決方法を追加します。話してくれてありがとう、私はピュータンズの能力について多くのことを学んだ。

私はすでにコメントしたよう:

を私は「0.0' を連結経由で任意の数のフロートを作ることによって解決策を見つけ、evalのは( 『1234567.0 ** 8.0 ** 9.0』)それは大丈夫です、例外がスローされます。

ここでは、この評価が埋め込まれているより大きな文脈、です:私が持っていた

import itertools 
digits1to8 = list(str(i+1) for i in range(8)) #('1','2','3','4','5','6','7','8') 

with open("expressions.txt", "w") as outfile: 
    for operators in itertools.product(['','.0+','.0-','.0*','.0/','.0**'], repeat=8): 
     calculation = zip(digits1to8,operators) 
     expression = (''.join(list(itertools.chain(*calculation))))+'9.0' 
     try: 
      out = str(eval(expression))+',' 
      expression = expression.replace('.0','') 
      out = out.replace('.0,',',') + expression 
      if (not out.find('.')>0): 
       print(out, file=outfile) 
     except: 
      pass 

事前に[ ''、 '+'、 ' - '、 '*'、 '/'、 '**']は、 '。'、 '。0 +'、 '。0 - '、 '。0 *'、 '。0 /'、 '。0 **'全体的には、​​

関連する問題