Python - Zelle书使用eval(),是不是错了?

时间:2013-04-14 04:24:54

标签: python eval

请注意:这不是关于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黑客并发现它的所有秘密,但是像这样的细节可能会成为一个自我教学/自我学习的人。您对此学习材料的建议是什么?

P.S。另一方面,让我从一开始就进行相当多的研究和实验(不知不觉)非常酷:-)

谢谢!

5 个答案:

答案 0 :(得分:9)

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

本书中使用eval主要是从Python 2到Python 3的转换的历史工件(尽管在Python 2中使用输入时存在相同的“缺陷”)。我很清楚在生产代码中使用eval的危险,其中输入可能来自不受信任的来源,但本书不是关于基于Web的系统的生产代码;它是关于学习一些CS和编程原理的。书中没有任何东西可以远程考虑生产代码。我的目标总是使用最简单的方法,让我说明我想要做的事情,而eval有助于做到这一点。

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

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

答案 1 :(得分:5)

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

很多人在Python 2.x中使用input(),这是一个非常不幸的命名函数,因为它不只是读取输入,它还会对它进行评估。转换器2to3会将input()的每次使用转换为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编程的第二版,针对Python 3进行了更新。

我认为有人在所有代码示例上运行2to3而没有仔细检查输出。所以是的,在Python 2.x中使用input()是错误的,如果不检查输出就使用2to3是错误的。

答案 2 :(得分:4)

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

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

答案 3 :(得分:3)

好吧,以这种方式组合eval()和input()可以创建一个基本但可能非常有害的“shell”。 我没有读过这本书,但我会把它拿出来。这不仅仅是不好的做法,它实现了一个致命的功能组合。

答案 4 :(得分:2)

是的eval应该拼写为evil,而不是警告人们;)除非绝对必要,否则你应该尝试从不使用它。在这种情况下,直观地使用int()代替它,它甚至更具可读性!此外,如果你真的不得不使用ast.literal_eval(它只评估文字,顾名思义,所以它不允许用户运行恶意代码)这实际上是安全的,但是没有必要这样做在这种情况下,不需要eval()