6

请注意:这不是关于 eval() 的使用,而是关于使用和教授它的书的潜在质量(或缺乏质量)。所以在 Python 中已经有无数关于 eval() 的线程。

冒着招致 SO 的愤怒和反对票的风险,我还是决定问这个问题,以防万一。请多多包涵。对于这个特定的问题,我已经尝试过 Google 和 SO 本身(如您所见),但一无所获。不过,我可能是个盲人。

这个问题是关于臭名昭著的 eval() 函数的使用。

约翰·泽勒(John Zelle)有一本相对知名(如您所见)的书:http: //www.amazon.com/Python-Programming-Introduction-Computer-Science/dp/1590282418/ref=pd_sim_b_3

从技术上讲,它是一本使用 Python 作为编程语言的 CS1 书籍。很公平,这让作者承担了一些责任(“嘿,我想在这里教你一些宽泛的东西,而不是所有这些语法和安全细节”),但是当我开始阅读它时,我注意到,字面上第一个例子,使用

x = eval(input("Enter your number: "))

其中 x 应该是一个 int,因此我们需要将用户输入转换为一个 int。

我使用的是 Python 2.7.4,这本书是关于 Python 3 的,所以我从一开始就遇到了 print() 和 input() 和 eval() 的很多问题,不得不做一些研究来获得工作的例子。在我的研究过程中,我已经阅读了无数关于 Python 中 eval() 的意见(主要是在 SO 上),归结为它几乎总是很糟糕、存在安全风险、不必要的技术开销等等。用户的问题要复杂得多(有一个关于在做 wxPython 项目时使用 eval() 的问题),所以我不能保证我的案例和他们的案例完全相似,但仍然......

所以,我承认,我对这本书的了解并不多,但我已经达到了这样的程度,即稍后作者解释了 eval() 的使用,而没有提及其有争议的性质。他基本上说了我刚才所说的:我们需要 x 最终成为一个 int,所以这里有一个方便的方法来做到这一点。而且他似乎永远都在使用它。

我的问题是:如果作者从一开始就犯了这样的错误(或者这不是一个错误?我可能在这里遗漏了一些东西),这本书值得学习吗?我相信 Zelle 先生是一位伟大的 CS 老师,这表明了,但不管他是否愿意,除了算法和编程艺术之外,人们仍然会从他的书中学习 Python。那么,是否值得从一本对 Python 社区中看似普遍的问题保持沉默的书中学习 Python?我不希望 Zelle 先生成为一名 Python 黑客并揭开它所有的秘密,但是像这样的小细节可以成就或毁掉一个自学/自学的人。您对本学习材料有何建议?

PS另一方面,让我从一开始就做很多研究和实验(不知不觉地)非常酷:-)

谢谢!

4

5 回答 5

7

作为这本书的作者,让我来谈谈这个问题。

书中对 eval 的使用很大程度上是从 Python 2 到 Python 3 转换的历史产物(尽管在 Python 2 中使用输入时也存在同样的“缺陷”)。我很清楚在生产代码中使用 eval 的危险,其中输入可能来自不受信任的来源,但这本书不是关于基于 Web 的系统的生产代码。它是关于学习一些 CS 和编程原理的。书中确实没有任何内容可以远程视为生产代码。我的目标始终是使用最简单的方法来说明我试图提出的观点,而 eval 有助于做到这一点。

我不同意在所有情况下都宣称 eval 邪恶的人群。对于仅由其编写者运行的简单程序和脚本来说,它非常方便。在这种情况下,它是完全安全的。它允许简单的多个输入和表达式作为输入。在教学上,它强调表达评估的概念。Eval 暴露了解释语言的所有力量(和危险)。我一直在我自己的个人程序中使用 eval(而不仅仅是在 Python 中)。事后看来,我绝对同意我应该对 eval 的潜在风险进行一些讨论;无论如何,这是我在课堂上经常做的事情。

最重要的是,这本书有很多改进的方法(总是有的)。我不认为使用 eval 是一个致命的缺陷;它适用于所说明的程序类型和这些程序出现的上下文。我不知道本书中使用 Python 的方式有任何其他“不安全因素”,但是应该警告您(正如前言所解释的),有很多地方的代码并不完全是“Pythonic”。

于 2013-04-14T19:14:17.123 回答
5

由于eval在您给出的示例中是如此格格不入且不必要,我当然会对本书其他部分的安全性产生怀疑。作者是否会建议您将用户输入的字符串附加到 SQL 查询中?

我认为找到作者的电子邮件地址并直接询问他可能是值得的。

于 2013-04-14T04:53:01.617 回答
4

是的,这是错误的。但我想我知道它为什么在那里。

很多人input()在 Python 2.x 中使用,这是一个非常不幸的命名函数,因为它不仅读取输入,还评估它。如您所见,转换器2to3将每次使用转换为input()to :eval(input())

$ cat test.py
x = input("Enter your number: ")

$ 2to3 test.py
RefactoringTool: Skipping implicit fixer: buffer
RefactoringTool: Skipping implicit fixer: idioms
RefactoringTool: Skipping implicit fixer: set_literal
RefactoringTool: Skipping implicit fixer: ws_comma
RefactoringTool: Refactored test.py
--- test.py     (original)
+++ test.py     (refactored)
@@ -1 +1 @@
-x = input("Enter your number: ")
+x = eval(input("Enter your number: "))
RefactoringTool: Files that need to be modified:
RefactoringTool: test.py

所以我的猜测是它只是有点草率。来自亚马逊的描述:

这是 John Zelle 的 Python Programming 的第二版,针对 Python 3 进行了更新。

我认为有人在2to3没有彻底检查输出的情况下运行了所有代码示例。所以是的,input()在 Python 2.x 中使用是错误的,在2to3不检查输出的情况下使用也是错误的。

于 2013-04-14T10:28:19.433 回答
3

好吧,以这种方式组合 eval() 和 input() 会创建一个基本但可能非常有害的“外壳”。我没有读过这本书,但我会持保留态度。这不仅是一种不好的做法,它还实现了一种致命的功能组合。

于 2013-04-14T10:18:41.293 回答
2

eval应该拼写“是”evil来警告人们这一点;)除非绝对必须,否则您应该尝试并且永远不要使用它。在这种情况下,使用起来很直观int(),它更具可读性!此外,如果你真的不得不使用ast.literal_eval(它只评估字面值,顾名思义,因此它不会允许用户运行恶意代码)实际上是安全的,但不需要这个,也不需要eval()在这种情况下。

于 2013-04-14T04:41:20.560 回答