Python的eval()有什么作用?


305

在我用Python阅读的书中,它一直在使用代码 eval(input('blah'))

我阅读了文档,但我理解了它,但仍然看不到它如何更改input()功能。

它有什么作用?有人可以解释吗?


4
评估函数尝试执行并解释以python代码形式传递给它的字符串(参数)。x = 1 print(eval('x + 1'))上面代码的输出将是2。这种方法的缺点是,用户获得编写代码的独立性,这会导致严重破坏。尽管您可以限制用户通过在eval函数中传递全局和局部参数来访问许多变量和方法。
ATIF IBAD KHAN,2013年

Answers:


276

eval函数允许Python程序在其内部运行Python代码。

评估示例(交互式外壳):

>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1

25
哈哈,这是一个简单的例子,但是您可以让用户键入一个任意命令并让python执行它。因此,您可以让用户输入命令字符串,然后让python将其作为代码运行。例如:eval(“ __ import __('os')。remove('file')”)。
BYS2'2

60
在您发现需要它之前,它似乎毫无用处。它在codepad.org等网站上使用,以允许您在测试环境中执行脚本。eval()也可以用来执行高度动态的代码,但是在使用它之前,您应该使自己充分了解安全和性能风险。
乔治·康明斯

6
@ GeorgeCummins,codepad.org不会使用eval,也不会使用eval
Mike Graham

16
@GeorgeCummins:codepag.org在沙盒中运行所有内容:chroot监狱,在虚拟机中执行ptrace检查,以防止恶意代码做任何不好的事情。比简单的评估要复杂得多。此外,eval是特定于Python的。键盘支持多种语言。
FogleBird '02

4
@GeorgeCummins,键盘运行一个非常复杂的系统以安全地运行任意程序。eval,除了不安全之外,无法像Codepad一样运行整个程序,因为它只能计算单个表达式。
Mike Graham

165

eval()将字符串解释为代码。之所以有如此多的人警告您使用此功能,是因为用户可以将其用作在计算机上运行代码的选项。如果你有eval(input())os进口的,一个人可以键入input() os.system('rm -R *')这将删除你的家目录中的所有文件。(假设您有一个Unix系统)。使用eval()是一个安全漏洞。如果您需要将字符串转换为其他格式,请尝试使用可实现此目的的东西int()


14
您的意思是使用evalwith input()是一个安全漏洞。不要放入input()eval语句,这样就可以了。
罗门(Rohmer)2015年

19
@Rohmer,不安全的数据可能来自任何地方:Web请求,表单输入字段,文件读取等等,而不仅仅是控制台输入。即使您自己编写文件,它仍然可以包含最初来自不受信任来源的输入。因此,eval在许多情况下,安全问题。
sanderd17

3
由于input通常是从控制台获取数据,因此用户可以直接退出程序并键入rm -R *...
cz

63

这里有很多很好的答案,但没有一个描述在eval()globalslocalskwargs 的上下文中的使用,即eval(expression, globals=None, locals=None)(请参阅此处的文档eval )。

这些可用于限制可通过eval功能使用的功能。例如,如果您加载了一个新的python解释器,则locals()globals()将是相同的,看起来像这样:

>>>globals()
{'__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__doc__': None,
 '__spec__': None, '__builtins__': <module 'builtins' (built-in)>,
 '__package__': None, '__name__': '__main__'}

builtins模块中肯定有一些功能可能会对系统造成重大损害。但是可以阻止任何我们不希望使用的东西。让我们举个例子。假设我们要构建一个列表,以表示系统上可用内核的域。对我来说,我有8个核心,因此我需要一个清单[1, 8]

>>>from os import cpu_count
>>>eval('[1, cpu_count()]')
[1, 8]

同样,所有这些__builtins__都可用。

>>>eval('abs(-1)')
1

好。因此,我们看到了一个我们想要公开的函数,以及一个我们不想公开的方法(其中许多可能更为复杂)的示例。因此,让我们阻止一切。

>>>eval('[1, cpu_count()]', {'__builtins__':None}, {})
TypeError: 'NoneType' object is not subscriptable

我们有效地阻止了所有__builtins__功能,因此为我们的系统带来了一定程度的保护。在这一点上,我们可以开始添加我们想要公开的功能。

>>>from os import cpu_count
>>>exposed_methods = {'cpu_count': cpu_count}
>>>eval('cpu_count()', {'__builtins__':None}, exposed_methods)
8
>>>eval('abs(cpu_count())', {'__builtins__':None}, exposed_methods)
TypeError: 'NoneType' object is not subscriptable

现在,我们可以使用该cpu_count功能,同时仍可以阻止所有不需要的功能。在我看来,这是非常强大的,并且显然是在其他答案的范围内,而不是常见的实现。诸如此类的东西有很多用途,只要处理得当,我个人认为eval可以安全地使用它,并获得很高的价值。

NB

这些方面很酷的一点kwargs是,您可以开始为代码使用简写形式。假设您将eval用作执行某些导入文本的管道的一部分。文本不需要确切的代码,它可以遵循某些模板文件格式,并且仍然可以执行所需的任何操作。例如:

>>>from os import cpu_count
>>>eval('[1,cores]', {'__builtins__': None}, {'cores': cpu_count()})
[1, 8]

29

在Python 2.x input(...)中等效于eval(raw_input(...)),在Python 3.x中raw_input已重命名input,我怀疑这会引起您的困惑(您可能正在查看inputPython 2.x 的文档)。此外,eval(input(...))在Python 3.x中可以正常工作,但TypeError在Python 2中可以提高a 。

在这种情况下eval,用于强制将返回的字符串input转换为表达式并进行解释。通常,这被认为是不良做法。


或这是一本Python 3.x书籍,这input意味着raw_input2.x中做了什么。
dan04 '02

1
是的,我写完最初的答案后对我来说是很明显的。
zeekay 2012年

6

也许是读一行并解释它的一个令人误解的例子。

尝试eval(input())输入"1+1"-这应该打印出来2。Eval计算表达式。


为什么要在引号之间输入?输入正在获取一个字符串,并将其传递给eval,而不执行代码,因此,如果我只键入1 + 1 ...,那应该没问题。
罗卡蒙德

事实是,您正在混合P2.x和3.x。在Python 2中,您的代码有效,但是两次评估没有意义。在python 3中,它没有,并返回一个字符串。
罗卡蒙德

6

eval()将传递的字符串评估为Python表达式并返回结果。例如,eval("1 + 1")解释并执行表达式"1 + 1"并返回结果(2)。

您可能会感到困惑的一个原因是,您引用的代码涉及一种间接级别。内部函数调用(输入)首先执行,因此用户看到“ blah”提示。假设它们以“ 1 +1”响应(为清楚起见添加了引号,请在运行程序时不要键入它们),输入函数返回该字符串,然后将其传递给外部函数(eval),该函数解释该字符串并返回结果(2)。

在此处阅读有关eval的更多信息。


6

eval()顾名思义,它评估传递的参数。

raw_input()现在input()是python 3.x版本。因此,最常见的使用示例eval()是它用于提供input()python 2.x版本提供的功能。raw_input以字符串的形式返回用户输入的数据,而input评估输入的数据的值并返回它。

eval(input("bla bla"))因此复制了input()2.x中的功能,即评估用户输入的数据。

简而言之:eval()计算传递给它的参数并因此eval('1 + 1')返回2。


6

的有用应用之一eval()是从字符串评估python表达式。例如从字典的文件字符串表示形式加载:

running_params = {"Greeting":"Hello "}
fout = open("params.dat",'w')
fout.write(repr(running_params))
fout.close()

将其作为变量读取并编辑:

fin = open("params.dat",'r')
diction=eval(fin.read())
diction["Greeting"]+="world"
fin.close()
print diction

输出:

{'Greeting': 'Hello world'}

7
这如何回答问什么的eval问题?
jkd

4

我迟迟未回答这个问题,但是似乎没人能给出明确的答案。

如果用户输入数字值,input()将返回一个字符串。

>>> input('Enter a number: ')
Enter a number: 3
>>> '3'
>>> input('Enter a number: ')
Enter a number: 1+1
'1+1'

因此,eval()将评估作为字符串的返回值(或表达式),并返回整数/浮点数。

>>> eval(input('Enter a number: '))
Enter a number: 1+1
2
>>> 
>>> eval(input('Enter a number: '))
Enter a number: 3.14
3.14

当然,这是一个坏习惯。int()或在这种情况下float()应使用eval()

>>> float(input('Enter a number: '))
Enter a number: 3.14
3.14

3

如果要将评估字符串限制为简单文字,则可以使用ast.literal_eval()。一些例子:

import ast

# print(ast.literal_eval(''))          # SyntaxError: unexpected EOF while parsing
# print(ast.literal_eval('a'))         # ValueError: malformed node or string
# print(ast.literal_eval('import os')) # SyntaxError: invalid syntax
# print(ast.literal_eval('1+1'))       # 2: but only works due to a quirk in parser
# print(ast.literal_eval('1*1'))       # ValueError: malformed node or string
print(ast.literal_eval("{'a':1}"))     # {'a':1}

文档

安全地评估表达式节点或包含Python文字或容器显示的字符串。提供的字符串或节点只能由以下Python文字结构组成:字符串,字节,数字,元组,列表,字典,集合,布尔值和无。

这可用于安全地评估包含来自不受信任来源的Python值的字符串,而无需自己解析值。它不能评估任意复杂的表达式,例如涉及运算符或索引的表达式。

至于为什么如此有限,请从邮件列表中进行

允许带有文字的运算符表达式是可能的,但是比当前实现复杂得多。一个简单的实现并不安全:您可以毫不费力地诱导CPU和内存的使用不受限制(尝试“ 9 ** 9 ** 9”或“ [无] * 9 ** 9”)。

至于有用性,此函数对于“读回”由repr()字符串化的文字值和容器很有用。例如,它可以用于序列化,其格式类似于JSON,但功能比JSON更强大。


1
ast.literal_eval不支持运算符,与您的'1+1'示例相反。尽管如此,它确实支持列表,数字,字符串等,因此是常见eval用例的不错选择。
benjimin

@benjimin哦,您是对的-它只是接受1 + 1的怪癖!stackoverflow.com/questions/40584417/...
布赖恩·伯恩斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.