utiliza uso sirve resultado qué que programacion para función funcion consola cadenas python math

uso - qué función de python se utiliza para el resultado de la consola



Evaluar una expresión matemática en una cadena (13)

eval es malvado

eval("__import__(''os'').remove(''important file'')") # arbitrary commands eval("9**9**9**9**9**9**9**9", {''__builtins__'': None}) # CPU, memory

Nota: incluso si usa set __builtins__ en None , aún podría ser posible usar introspection:

eval(''(1).__class__.__bases__[0].__subclasses__()'', {''__builtins__'': None})

Evaluar la expresión aritmética usando ast

import ast import operator as op # supported operators operators = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul, ast.Div: op.truediv, ast.Pow: op.pow, ast.BitXor: op.xor, ast.USub: op.neg} def eval_expr(expr): """ >>> eval_expr(''2^6'') 4 >>> eval_expr(''2**6'') 64 >>> eval_expr(''1 + 2*3**(4^5) / (6 + -7)'') -5.0 """ return eval_(ast.parse(expr, mode=''eval'').body) def eval_(node): if isinstance(node, ast.Num): # <number> return node.n elif isinstance(node, ast.BinOp): # <left> <operator> <right> return operators[type(node.op)](eval_(node.left), eval_(node.right)) elif isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1 return operators[type(node.op)](eval_(node.operand)) else: raise TypeError(node)

Puede limitar fácilmente el rango permitido para cada operación o cualquier resultado intermedio, por ejemplo, para limitar los argumentos de entrada para a**b :

def power(a, b): if any(abs(n) > 100 for n in [a, b]): raise ValueError((a,b)) return op.pow(a, b) operators[ast.Pow] = power

O para limitar la magnitud de los resultados intermedios:

import functools def limit(max_=None): """Return decorator that limits allowed returned values.""" def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): ret = func(*args, **kwargs) try: mag = abs(ret) except TypeError: pass # not applicable else: if mag > max_: raise ValueError(ret) return ret return wrapper return decorator eval_ = limit(max_=10**100)(eval_)

Ejemplo

>>> evil = "__import__(''os'').remove(''important file'')" >>> eval_expr(evil) #doctest:+IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TypeError: >>> eval_expr("9**9") 387420489 >>> eval_expr("9**9**9**9**9**9**9**9") #doctest:+IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... ValueError:

stringExp = "2^4" intVal = int(stringExp) # Expected value: 16

Esto devuelve el siguiente error:

Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: invalid literal for int() with base 10: ''2^4''

Sé que eval puede eval esto, pero ¿no hay un método mejor y, lo que es más importante, más seguro para evaluar una expresión matemática que se almacena en una cadena?



Creo que usaría eval() , pero primero verificaría que la cadena sea una expresión matemática válida, a diferencia de algo malicioso. Puede usar una expresión regular para la validación.

eval() también toma argumentos adicionales que puedes usar para restringir el espacio de nombres en el que opera para mayor seguridad.


De acuerdo, entonces el problema con eval es que puede escapar de su sandbox con demasiada facilidad, incluso si te deshaces de __builtins__ . Todos los métodos para escapar del sandbox se reducen a usar getattr u object.__getattribute__ (a través del operador . ) Para obtener una referencia a algún objeto peligroso a través de algún objeto permitido ( ''''.__class__.__bases__[0].__subclasses__ o similar). getattr se elimina estableciendo __builtins__ en None . object.__getattribute__ es el más difícil, ya que no se puede eliminar simplemente, porque el object es inmutable y porque eliminarlo lo rompería todo. Sin embargo, __getattribute__ solo es accesible a través de . operador, por lo que purgar eso de su entrada es suficiente para garantizar que eval no puede escapar de su entorno limitado.
Al procesar fórmulas, el único uso válido de un decimal es cuando está precedido o seguido por [0-9] , por lo que eliminamos todas las demás instancias de . .

import re inp = re.sub(r"/.(?![0-9])","", inp) val = eval(inp, {''__builtins__'':None})

Tenga en cuenta que mientras que python normalmente trata 1 + 1. como 1 + 1.0 , esto eliminará el final . y te dejo con 1 + 1 . Usted podría agregar ) , y EOF a la lista de cosas permitidas a seguir . , pero ¿para qué molestarse?


Esta es una respuesta tardía masiva, pero creo útil para futuras referencias. En lugar de escribir su propio analizador matemático (aunque el ejemplo anterior de pyparsing es genial) podría usar SymPy. No tengo mucha experiencia con eso, pero contiene un motor matemático mucho más potente de lo que cualquiera podría escribir para una aplicación específica y la evaluación de la expresión básica es muy fácil:

>>> import sympy >>> x, y, z = sympy.symbols(''x y z'') >>> sympy.sympify("x**3 + sin(y)").evalf(subs={x:1, y:-3}) 0.858879991940133

Muy bueno! A from sympy import * brinda mucho más soporte de funciones, como funciones trigonométricas, funciones especiales, etc., pero lo he evitado aquí para mostrar lo que viene de dónde.


La razón por la que eval y exec son tan peligrosos es que la función de compile predeterminada generará bytecode para cualquier expresión válida de python, y el eval o exec predeterminado ejecutará cualquier bytecode de python válido. Todas las respuestas hasta la fecha se han centrado en restringir el bytecode que se puede generar (mediante la desinfección de la entrada) o crear su propio idioma específico del dominio utilizando AST.

En su lugar, puede crear fácilmente una función simple de eval que sea incapaz de hacer algo nefasto y que pueda tener fácilmente controles de tiempo de ejecución en la memoria o el tiempo utilizado. Por supuesto, si es matemática simple, entonces hay un atajo.

c = compile(stringExp, ''userinput'', ''eval'') if c.co_code[0]==b''d'' and c.co_code[3]==b''S'': return c.co_consts[ord(c.co_code[1])+ord(c.co_code[2])*256]

La forma en que esto funciona es simple, cualquier expresión matemática constante se evalúa de forma segura durante la compilación y se almacena como una constante. El objeto de código devuelto por compilación consta de d , que es el bytecode de LOAD_CONST , seguido del número de la constante a cargar (generalmente el último en la lista), seguido de S , que es el bytecode para RETURN_VALUE . Si este atajo no funciona, significa que la entrada del usuario no es una expresión constante (contiene una variable o llamada de función o similar).

Esto también abre la puerta a algunos formatos de entrada más sofisticados. Por ejemplo:

stringExp = "1 + cos(2)"

Esto requiere en realidad evaluar el bytecode, que todavía es bastante simple. Python bytecode es un lenguaje orientado a la pila, así que todo es una simple cuestión de TOS=stack.pop(); op(TOS); stack.put(TOS) TOS=stack.pop(); op(TOS); stack.put(TOS) TOS=stack.pop(); op(TOS); stack.put(TOS) o similar. La clave es implementar únicamente los códigos de operación que son seguros (cargar / almacenar valores, operaciones matemáticas, devolver valores) y no inseguros (búsqueda de atributos). Si desea que el usuario pueda llamar a funciones (la razón completa para no utilizar el acceso directo anterior), simplemente haga que su implementación de CALL_FUNCTION solo permita funciones en una lista ''segura''.

from dis import opmap from Queue import LifoQueue from math import sin,cos import operator globs = {''sin'':sin, ''cos'':cos} safe = globs.values() stack = LifoQueue() class BINARY(object): def __init__(self, operator): self.op=operator def __call__(self, context): stack.put(self.op(stack.get(),stack.get())) class UNARY(object): def __init__(self, operator): self.op=operator def __call__(self, context): stack.put(self.op(stack.get())) def CALL_FUNCTION(context, arg): argc = arg[0]+arg[1]*256 args = [stack.get() for i in range(argc)] func = stack.get() if func not in safe: raise TypeError("Function %r now allowed"%func) stack.put(func(*args)) def LOAD_CONST(context, arg): cons = arg[0]+arg[1]*256 stack.put(context[''code''].co_consts[cons]) def LOAD_NAME(context, arg): name_num = arg[0]+arg[1]*256 name = context[''code''].co_names[name_num] if name in context[''locals'']: stack.put(context[''locals''][name]) else: stack.put(context[''globals''][name]) def RETURN_VALUE(context): return stack.get() opfuncs = { opmap[''BINARY_ADD'']: BINARY(operator.add), opmap[''UNARY_INVERT'']: UNARY(operator.invert), opmap[''CALL_FUNCTION'']: CALL_FUNCTION, opmap[''LOAD_CONST'']: LOAD_CONST, opmap[''LOAD_NAME'']: LOAD_NAME opmap[''RETURN_VALUE'']: RETURN_VALUE, } def VMeval(c): context = dict(locals={}, globals=globs, code=c) bci = iter(c.co_code) for bytecode in bci: func = opfuncs[ord(bytecode)] if func.func_code.co_argcount==1: ret = func(context) else: args = ord(bci.next()), ord(bci.next()) ret = func(context, args) if ret: return ret def evaluate(expr): return VMeval(compile(expr, ''userinput'', ''eval''))

Obviamente, la versión real de esto sería un poco más larga (hay 119 códigos de operación, 24 de los cuales están relacionados con las matemáticas). Agregar STORE_FAST y un par de otros permitiría la entrada como ''x=5;return x+x o similar, trivialmente fácil. Incluso se puede usar para ejecutar funciones creadas por el usuario, siempre que las funciones creadas por el usuario se ejecuten a través de VMeval (¡no las haga llamables!) O podrían ser utilizadas como una devolución de llamada en algún lugar. El manejo de bucles requiere soporte para los goto bytes goto , lo que significa cambiar de un iterador a while y mantener un puntero a la instrucción actual, pero no es demasiado difícil. Para la resistencia a DOS, el ciclo principal debería verificar cuánto tiempo ha pasado desde el inicio del cálculo, y ciertos operadores deben denegar la entrada por encima de un límite razonable (siendo BINARY_POWER el más obvio).

Si bien este enfoque es algo más extenso que un simple analizador gramatical para expresiones simples (ver más arriba simplemente capturando la constante compilada), se extiende fácilmente a entradas más complicadas, y no requiere lidiar con la gramática ( compile tomar cualquier cosa arbitrariamente complicada y reducirla a una secuencia de instrucciones simples).


Puede usar el módulo ast y escribir un NodeVisitor que verifique que el tipo de cada nodo sea parte de una lista blanca.

import ast, math locals = {key: value for (key,value) in vars(math).items() if key[0] != ''_''} locals.update({"abs": abs, "complex": complex, "min": min, "max": max, "pow": pow, "round": round}) class Visitor(ast.NodeVisitor): def visit(self, node): if not isinstance(node, self.whitelist): raise ValueError(node) return super().visit(node) whitelist = (ast.Module, ast.Expr, ast.Load, ast.Expression, ast.Add, ast.Sub, ast.UnaryOp, ast.Num, ast.BinOp, ast.Mult, ast.Div, ast.Pow, ast.BitOr, ast.BitAnd, ast.BitXor, ast.USub, ast.UAdd, ast.FloorDiv, ast.Mod, ast.LShift, ast.RShift, ast.Invert, ast.Call, ast.Name) def evaluate(expr, locals = {}): if any(elem in expr for elem in ''/n#'') : raise ValueError(expr) try: node = ast.parse(expr.strip(), mode=''eval'') Visitor().visit(node) return eval(compile(node, "<string>", "eval"), {''__builtins__'': None}, locals) except Exception: raise ValueError(expr)

Como funciona a través de una lista blanca en lugar de una lista negra, es seguro. Las únicas funciones y variables a las que puede acceder son aquellas a las que explícitamente le da acceso. Completé un dict con funciones relacionadas con las matemáticas para que pueda facilitar el acceso a ellas si lo desea, pero tiene que usarlo explícitamente.

Si la cadena intenta llamar a funciones que no se han proporcionado, o invocar cualquier método, se generará una excepción y no se ejecutará.

Como esto usa el analizador y el analizador integrados de Python, también hereda las reglas de precedencia y promoción de Python.

>>> evaluate("7 + 9 * (2 << 2)") 79 >>> evaluate("6 // 2 + 0.0") 3.0

El código anterior solo se ha probado en Python 3.

Si lo desea, puede agregar un decorador de tiempo de espera en esta función.



Si no desea utilizar eval, entonces la única solución es implementar el analizador gramatical apropiado. Eche un vistazo a Pyparsing .



Use eval en un espacio de nombres limpio:

>>> ns = {''__builtins__'': None} >>> eval(''2 ** 4'', ns) 16

El espacio de nombre limpio debe evitar la inyección. Por ejemplo:

>>> eval(''__builtins__.__import__("os").system("echo got through")'', ns) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 1, in <module> AttributeError: ''NoneType'' object has no attribute ''__import__''

De lo contrario, obtendrías:

>>> eval(''__builtins__.__import__("os").system("echo got through")'') got through 0

Es posible que desee dar acceso al módulo de matemáticas:

>>> import math >>> ns = vars(math).copy() >>> ns[''__builtins__''] = None >>> eval(''cos(pi/3)'', ns) 0.50000000000000011


Pyparsing se puede usar para analizar expresiones matemáticas. En particular, fourFn.py muestra cómo analizar expresiones aritméticas básicas. A continuación, he vuelto a envolver fourFn en una clase de analizador numérico para facilitar su reutilización.

from __future__ import division from pyparsing import (Literal, CaselessLiteral, Word, Combine, Group, Optional, ZeroOrMore, Forward, nums, alphas, oneOf) import math import operator __author__ = ''Paul McGuire'' __version__ = ''$Revision: 0.0 $'' __date__ = ''$Date: 2009-03-20 $'' __source__ = ''''''http://pyparsing.wikispaces.com/file/view/fourFn.py http://pyparsing.wikispaces.com/message/view/home/15549426 '''''' __note__ = '''''' All I''ve done is rewrap Paul McGuire''s fourFn.py as a class, so I can use it more easily in other places. '''''' class NumericStringParser(object): '''''' Most of this code comes from the fourFn.py pyparsing example '''''' def pushFirst(self, strg, loc, toks): self.exprStack.append(toks[0]) def pushUMinus(self, strg, loc, toks): if toks and toks[0] == ''-'': self.exprStack.append(''unary -'') def __init__(self): """ expop :: ''^'' multop :: ''*'' | ''/'' addop :: ''+'' | ''-'' integer :: [''+'' | ''-''] ''0''..''9''+ atom :: PI | E | real | fn ''('' expr '')'' | ''('' expr '')'' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* """ point = Literal(".") e = CaselessLiteral("E") fnumber = Combine(Word("+-" + nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-" + nums, nums))) ident = Word(alphas, alphas + nums + "_$") plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") lpar = Literal("(").suppress() rpar = Literal(")").suppress() addop = plus | minus multop = mult | div expop = Literal("^") pi = CaselessLiteral("PI") expr = Forward() atom = ((Optional(oneOf("- +")) + (ident + lpar + expr + rpar | pi | e | fnumber).setParseAction(self.pushFirst)) | Optional(oneOf("- +")) + Group(lpar + expr + rpar) ).setParseAction(self.pushUMinus) # by defining exponentiation as "atom [ ^ factor ]..." instead of # "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-right # that is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + / ZeroOrMore((expop + factor).setParseAction(self.pushFirst)) term = factor + / ZeroOrMore((multop + factor).setParseAction(self.pushFirst)) expr << term + / ZeroOrMore((addop + term).setParseAction(self.pushFirst)) # addop_term = ( addop + term ).setParseAction( self.pushFirst ) # general_term = term + ZeroOrMore( addop_term ) | OneOrMore( addop_term) # expr << general_term self.bnf = expr # map operator symbols to corresponding arithmetic operations epsilon = 1e-12 self.opn = {"+": operator.add, "-": operator.sub, "*": operator.mul, "/": operator.truediv, "^": operator.pow} self.fn = {"sin": math.sin, "cos": math.cos, "tan": math.tan, "exp": math.exp, "abs": abs, "trunc": lambda a: int(a), "round": round, "sgn": lambda a: abs(a) > epsilon and cmp(a, 0) or 0} def evaluateStack(self, s): op = s.pop() if op == ''unary -'': return -self.evaluateStack(s) if op in "+-*/^": op2 = self.evaluateStack(s) op1 = self.evaluateStack(s) return self.opn[op](op1, op2) elif op == "PI": return math.pi # 3.1415926535 elif op == "E": return math.e # 2.718281828 elif op in self.fn: return self.fn[op](self.evaluateStack(s)) elif op[0].isalpha(): return 0 else: return float(op) def eval(self, num_string, parseAll=True): self.exprStack = [] results = self.bnf.parseString(num_string, parseAll) val = self.evaluateStack(self.exprStack[:]) return val

Puedes usarlo así

nsp = NumericStringParser() result = nsp.eval(''2^4'') print(result) # 16.0 result = nsp.eval(''exp(2^4)'') print(result) # 8886110.520507872


[Sé que esta es una vieja pregunta, pero vale la pena señalar nuevas soluciones útiles a medida que aparecen]

Desde python3.6, esta capacidad ahora está integrada en el lenguaje , acuñado "f-strings" .

Ver: PEP 498 - Interpolación de cadenas literales

Por ejemplo (tenga en cuenta el prefijo f ):

f''{2**4}'' => ''16''