python的eval()做什么?

What does Python's eval() do?

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

我阅读了文档,并且理解了它,但是我仍然看不到它如何改变input()函数。

它是做什么的?有人能解释吗?


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

eval示例(交互式shell):

1
2
3
4
5
>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1


eval()将字符串解释为代码。之所以有这么多人警告您使用它,是因为用户可以使用它作为在计算机上运行代码的选项。如果您导入了eval(input())os,那么用户可以输入input()os.system('rm -R *'),这将删除主目录中的所有文件。(假设您有一个Unix系统)。使用eval()是一个安全漏洞。如果需要将字符串转换为其他格式,请尝试使用类似于int()的格式。


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

这些方法可用于限制通过eval方法可用的方法。例如,如果您加载一个新的python解释器,那么locals()globals()将是相同的,并且看起来像这样:

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

builtins模块中,当然有一些方法可以对系统造成重大损坏。但是我们可以阻止任何我们不想要的东西。让我们举个例子。假设我们想要构建一个列表来表示系统上可用核心的域。对于我来说,我有8个内核,所以我想要一个列表[1, 8]

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

同样,所有的__builtins__都可以使用。

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

好啊。在这里,我们看到一个我们想要公开的方法和一个我们不想要公开的方法的例子(很多方法中的一个可能更复杂)。所以让我们封锁一切。

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

我们已经有效地阻止了所有的__builtins__方法,因此给我们的系统带来了一定程度的保护。此时,我们可以开始添加我们希望公开的方法。

1
2
3
4
5
6
>>>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可以安全地使用到巨大的价值。

N.B.

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

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

在python 2.x中,input(...)相当于eval(raw_input(...)),在python 3.x中,raw_input被重命名为input,我怀疑这会导致您的混淆(您可能正在查看python 2.x中input的文档)。另外,在python 3.x中,eval(input(...))可以很好地工作,但在python 2中会提高TypeError

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


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

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

在这里阅读更多关于eval的信息。


顾名思义,eval()计算传递的参数。

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

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

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


eval()的一个有用的应用程序是从字符串计算python表达式。例如,从字典的文件字符串表示形式加载:

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

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

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

输出:

1
{'Greeting': 'Hello world'}


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

尝试eval(input())和键入"1+1"—这应该打印2。eval计算表达式。


我回答这个问题迟到了,但似乎没有人对这个问题给出明确的答案。

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

1
2
3
4
5
6
>>> input('Enter a number: ')
Enter a number: 3
>>> '3'
>>> input('Enter a number: ')
Enter a number: 1+1
'1+1'

因此,eval()将计算返回值(或表达式),该值是一个字符串,并返回整型/浮点型。

1
2
3
4
5
6
7
>>> 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()

1
2
3
>>> float(input('Enter a number: '))
Enter a number: 3.14
3.14

如果要将评估字符串限制为简单文本,另一种选择是使用ast.literal_eval()。一些例子:

1
2
3
4
5
6
7
8
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}

来自文档:

Safely evaluate an expression node or a string containing a Python literal or container display. The string or node provided may only consist of the following Python literal structures: strings, bytes, numbers, tuples, lists, dicts, sets, booleans, and None.

This can be used for safely evaluating strings containing Python values from untrusted sources without the need to parse the values oneself. It is not capable of evaluating arbitrarily complex expressions, for example involving operators or indexing.

至于为什么它如此有限,从邮件列表中可以看出:

Allowing operator expressions with literals is possible, but much more complex than the current implementation. A simple implementation is not safe: you can induce basically unbounded CPU and memory usage with no effort (try"9**9**9" or"[None] * 9**9").

As for the usefulness, this function is useful to"read back" literal values and containers as stringified by repr(). This can for example be used for serialization in a format that is similar to but more powerful than JSON.