Python eval 函数构建数学表达式计算器(2)

上一篇 / 下一篇  2022-09-09 11:02:58

  2022软件测试行业调查报告开始了,点击链接http://vote.51testing.com/ 填写问卷,五门测试实战课程任选两门免费学习。同时还有转发朋友圈免费领数据线的活动。快来参加吧~
  将输入限制为只有字数
  函数eval()的一个常见用例是计算包含标准Python字面符号的字符串,并将其变成具体的对象。
  标准库提供了一个叫做 literal_eval()[2] 的函数,可以帮助实现这个目标。虽然这个函数不支持运算符,但它支持 list, tuples, numbers, strings等等。
  >>> from ast import literal_eval
  >>> #  计算字面意义
  >>> literal_eval("15.02")
  15.02
  >>> literal_eval("[1, 15]")
  [1, 15]
  >>> literal_eval("(1, 15)")
  (1, 15)
  >>> literal_eval("{'one': 1, 'two': 2}")
  {'one': 1, 'two': 2}
  >>> # 试图计算一个表达式
  >>> literal_eval("sum([1, 15]) + 5 + 8 * 2")
  Traceback (most recent call last):
    ...
  ValueError: malformed node or string: <_ast.BinOp object at 0x7faedecd7668>
  注意,literal_eval()?只作用于标准类型的字词。它不支持使用运算符或变量名。如果向 literal_eval()? 传递一个表达式,会得到一个 ValueError。这个函数还可以将与使用eval()有关的安全风险降到最低。
  使用eval()与input()函数
  在 Python 3.x 中,内置函数 input() 读取命令行上的用户输入,去掉尾部的换行,转换为字符串,并将结果返回给调用者。由于 input()? 的输出结果是一个字符串,可以把它传递给 eval() 并作为一个 Python 表达式来计算它。
  >>> eval(input("Enter a math expression: "))
  Enter a math expression: 15 * 2
  30
  >>> eval(input("Enter a math expression: "))
  Enter a math expression: 5 + 8
  13
  我们可以将函数 eval()? 包裹在函数 input()? 中,实现自动计算用户的输入的功能。一个常见用例模拟 Python 2.x 中 input()? 的行为,input() 将用户的输入作为一个 Python 表达式来计算,并返回结果。
  因为它涉及安全问题,因此在 Python 2.x 中的 input() 的这种行为在 Python 3.x 中被改变了。
  构建一个数学表达式计算器
  到目前为止,我们已经了解了函数 eval()? 是如何工作的以及如何在实践中使用它。此外还了解到 eval()? 具有重要的安全漏洞,尽量在代码中避免使用 eval()?,然而在某些情况下,eval()? 可以为我们节省大量的时间和精力。因此,学会合理使用 eval() 函数还是蛮重要的。
  在本节中,将编写一个应用程序来动态地计算数学表达式。首先不使用eval()来解决这个问题,那么需要通过以下步骤:
  ·解析输入的表达式。
  · 将表达式的组成部分变为Python对象(数字、运算符、函数等等)。
  · 将所有的东西合并成一个表达式。
  · 确认该表达式在Python中是有效的。
  · 计算最终表达式并返回结果。
  考虑到 Python 可以处理和计算的各种表达式非常耗时。其实我们可以使用 eval() 来解决这个问题,而且通过上文我们已经学会了几种技术来规避相关的安全风险。
  首先创建一个新的Python脚本,名为mathrepl.py,然后添加以下代码。
   import math
   
   __version__ = "1.0"
   
   ALLOWED_NAMES = {
       k: v for k, v in math.__dict__.items() if not k.startswith("__")
   }
   
   PS1 = "mr>>"
  WELCOME = f"""
  MathREPL {__version__}, your Python math expressions evaluator!
  Enter a valid math expression after the prompt "{PS1}".
  Type "help" for more information.
  Type "quit" or "exit" to exit.
  """
  USAGE = f"""
  Usage:
  Build math expressions using numeric values and operators.
  Use any of the following functions and constants:
  {', '.join(ALLOWED_NAMES.keys())}
  """
  在这段代码中,我们首先导入 math 模块。这个模块使用预定义的函数和常数进行数学运算。常量 ALLOWED_NAMES? 保存了一个包含数学中非特变量名的字典。这样就可以用 eval() 来使用它们。
  我们还定义了另外三个字符串常量。将使用它们作为脚本的用户界面,并根据需要打印到屏幕上。
  现在准备编写核心功能,首先编写一个函数,接收数学表达式作为输入,并返回其结果。此外还需要写一个叫做 evaluate() 的函数,如下所示。
  def evaluate(expression):
      """Evaluate a math expression."""
      # 编译表达式
      code = compile(expression, "<string>", "eval")
      # 验证允许名称
      for name in code.co_names:
          if name not in ALLOWED_NAMES:
              raise NameError(f"The use of '{name}' is not allowed")
      return eval(code, {"__builtins__": {}}, ALLOWED_NAMES)
  以下是该功能的工作原理。
  ·定义了evaluate(),该函数将字符串表达式作为参数,并返回一个浮点数,代表将字符串作为数学表达式进行计算的结果。
  · 使用compile()将输入的字符串表达式变成编译的Python代码。如果用户输入了一个无效的表达式,编译操作将引发一个 SyntaxError。
  · 使用一个for循环,检查表达式中包含的名字,并确认它们可以在最终表达式中使用。如果用户提供的名字不在允许的名字列表中,那么会引发一个NameError。
  · 执行数学表达式的实际计算。注意将自定义的字典传递给了globals和locals。ALLOWED_NAMES保存了数学中定义的函数和常量。
  注意: 由于这个应用程序使用了 math 中定义的函数,需要注意,当我们用一个无效的输入值调用这些函数时,其中一些函数将抛出 ValueError 异常。
  例如,math.sqrt(-10)? 会引发一个异常,因为-10的平方根是未定义的。我们会在稍后的代码中看到如何捕捉该异常。
  为 globals 和 locals 参数使用自定义值,加上名称检查,可以将与使用eval()有关的安全风险降到最低。
  当在 main() 中编写其代码时,数学表达式计算器就完成了。在这个函数中,定义程序的主循环,结束读取和计算用户在命令行中输入的表达式的循环。
  在这个例子中,应用程序将:
  · 向用户打印一条欢迎信息
  · 显示一个提示,准备读取用户的输入
  · 提供获取使用说明和终止应用程序的选项
  · 读取用户的数学表达式
  · 计算用户的数学表达式
  · 将计算的结果打印到屏幕上
  def main():
      """Main loop: Read and evaluate user's input."""
      print(WELCOME)
      while True:
          #  读取用户的输入
          try:
              expression = input(f"{PS1} ")
          except (KeyboardInterrupt, EOFError):
              raise SystemExit()
          # 处理特殊命令
          if expression.lower() == "help":
              print(USAGE)
              continue
          if expression.lower() in {"quit", "exit"}:
              raise SystemExit()
          # 对表达式进行计算并处理错误
          try:
              result = evaluate(expression)
          except SyntaxError:
              # 如果用户输入了一个无效的表达式
              print("Invalid input expression syntax")
              continue
          except (NameError, ValueError) as err:
              # 如果用户试图使用一个不允许的名字
              # 对于一个给定的数学函数来说是一个无效的值
              print(err)
              continue
          # 如果没有发生错误,则打印结果
          print(f"The result is: {result}")
  if __name__ == "__main__":
      main()
  在main()?中,首先打印WELCOME消息。然后在一个try语句中读取用户的输入,以捕获键盘中断和 EOFError。如果这些异常发生,就终止应用程序。
  如果用户输入帮助选项,那么应用程序就会显示使用指南。同样地,如果用户输入quit或exit,那么应用程序就会终止。
  最后,使用evaluate()?来计算用户的数学表达式,然后将结果打印到屏幕上。值得注意的是,对 evaluate() 的调用会引发以下异常。
  ·SyntaxError:语法错误,当用户输入一个不符合Python语法的表达式时,就会发生这种情况。
  · NameError:当用户试图使用一个不允许的名称(函数、类或属性)时,就会发生这种情况。
  · ValueError:当用户试图使用一个不允许的值作为数学中某个函数的输入时,就会发生这种情况。
  注意,在main()中,捕捉了所有已知异常,并相应地打印信息给用户。这将使用户能够审查表达式,修复问题,并再次运行程序。
  现在已经使用函数 eval() 在大约七十行的代码中建立了一个数学表达式计算器。要运行这个程序,打开我们的系统命令行,输入以下命令。
  $ python3 mathrepl.py
  这个命令将启动数学表达式计算器的命令行界面(CLI),会在屏幕上看到类似这样的东西。
  MathREPL 1.0, your Python math expressions evaluator!
  Enter a valid math expression after the prompt "mr>>".
  Type "help" for more information.
  Type "quit" or "exit" to exit.
  mr>>
  现在我们可以输入并计算任何数学表达式。例如,输入以下表达式。
  mr>> 25 * 2
  The result is: 50
  mr>> sqrt(25)
  The result is: 5.0
  mr>> pi
  The result is: 3.141592653589793
  如果输入了一个有效的数学表达式,那么应用程序就会对其进行计算,并将结果打印到屏幕上。如果表达式有任何问题,那么应用程序会告诉我们。
  r>> 5 * (25 + 4
  Invalid input expression syntax
  mr>> sum([1, 2, 3, 4, 5])
  The use of 'sum' is not allowed
  mr>> sqrt(-15)
  math domain error
  mr>> factorial(-15)
  factorial() not defined for negative values
  在第一个示例中,漏掉了右括号,因此收到一条消息,告诉我们语法不正确。然后调用 sum()? ,这会得到一个解释性的异常消息。最后,使用无效的输入值调用“math”函数,应用程序将生成一条消息来识别输入中的问题。
  总结
  你可以使用Python的 eval() 从基于字符串或基于代码的输入中计算Python 表达式。当我们动态地计算Python表达式,并希望避免从头创建自己的表达式求值器的麻烦时,这个内置函数可能很有用。
  在本文中,我们已经学习了 eval() 是如何工作的,以及如何安全有效地使用它来计算任意Python表达式。
  使用Python的eval()来动态计算基本的Python表达式。
  使用eval()运行更复杂的语句,如函数调用、对象创建和属性访问。
  最大限度地减少与使用Python的eval()有关的安全风险。
2022软件测试行业调查报告开始了,点击链接http://vote.51testing.com/ 填写问卷,五门测试实战课程任选两门免费学习。同时还有转发朋友圈免费领数据线的活动。快来参加吧~

TAG: 软件开发 Python

 

评分:0

我来说两句

Open Toolbar