eval-Python 实用宝典 https://pythondict.com 有趣好用的Python教程 Wed, 18 Aug 2021 14:52:31 +0000 zh-Hans hourly 1 https://wordpress.org/?v=6.7.1 使用python的eval()与ast.literal_eval()? https://pythondict.com/python-qa/%e4%bd%bf%e7%94%a8python%e7%9a%84eval%ef%bc%88%ef%bc%89%e4%b8%8east-literal_eval%ef%bc%88%ef%bc%89%ef%bc%9f/ Wed, 18 Aug 2021 14:52:31 +0000 https://pythondict.com/?p=8559 问题:使用python的eval()与ast.literal_eval()?

我遇到了一些代码,eval()将其作为一种可能的解决方案。现在,我以前从未使用eval()过,但是,我遇到了很多有关它可能引起的潜在危险的信息。也就是说,我对使用它非常谨慎。

我的情况是我有一个用户输入:

datamap = raw_input('Provide some data here: ')

哪里datamap需要一本字典。我四处搜寻,发现eval()可以解决这个问题。我认为我可以在尝试使用数据之前检查输入的类型,这将是可行的安全预防措施。

datamap = eval(raw_input('Provide some data here: ')
if not isinstance(datamap, dict):
    return

我通读了文档,但仍不清楚这是否安全。eval是否在输入数据后或datamap调用变量后立即评估数据?

ast模块是.literal_eval()唯一安全的选择吗?

I have a situation with some code where eval() came up as a possible solution. Now I have never had to use eval() before but, I have come across plenty of information about the potential danger it can cause. That said, I’m very wary about using it.

My situation is that I have input being given by a user:

datamap = input('Provide some data here: ')

Where datamap needs to be a dictionary. I searched around and found that eval() could work this out. I thought that I might be able to check the type of the input before trying to use the data and that would be a viable security precaution.

datamap = eval(input('Provide some data here: ')
if not isinstance(datamap, dict):
    return

I read through the docs and I am still unclear if this would be safe or not. Does eval evaluate the data as soon as its entered or after the datamap variable is called?

Is the ast module’s .literal_eval() the only safe option?


回答 0

datamap = eval(raw_input('Provide some data here: '))意味着您实际上认为代码不安全之前对其进行了评估。调用该函数后,它将立即评估代码。另请参阅的危险eval

ast.literal_eval 如果输入不是有效的Python数据类型,则会引发异常,因此如果输入无效,则不会执行代码。

使用ast.literal_eval时,你需要eval。通常,您不应该评估文字Python语句。

datamap = eval(input('Provide some data here: ')) means that you actually evaluate the code before you deem it to be unsafe or not. It evaluates the code as soon as the function is called. See also the dangers of eval.

ast.literal_eval raises an exception if the input isn’t a valid Python datatype, so the code won’t be executed if it’s not.

Use ast.literal_eval whenever you need eval. You shouldn’t usually evaluate literal Python statements.


回答 1

ast.literal_eval() 仅认为Python语法的一小部分有效:

提供的字符串或节点只能由以下Python文字结构组成:字符串,数字,元组,列表,字典,布尔值和无。

传递__import__('os').system('rm -rf /a-path-you-really-care-about')ast.literal_eval()将引发一个错误,但eval()会愉快地擦拭您的驱动器。

由于看起来您只是让用户输入普通字典,所以请使用ast.literal_eval()。它可以安全地执行您想要的操作,仅此而已。

ast.literal_eval() only considers a small subset of Python’s syntax to be valid:

The string or node provided may only consist of the following Python literal structures: strings, bytes, numbers, tuples, lists, dicts, sets, booleans, and None.

Passing __import__('os').system('rm -rf /a-path-you-really-care-about') into ast.literal_eval() will raise an error, but eval() will happily delete your files.

Since it looks like you’re only letting the user input a plain dictionary, use ast.literal_eval(). It safely does what you want and nothing more.


回答 2

eval: 此功能非常强大,但是如果您接受字符串以从不受信任的输入中求值,则也非常危险。假设要评估的字符串是“ os.system(’rm -rf /’)”?它将真正开始删除计算机上的所有文件。

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

句法:

eval(expression, globals=None, locals=None)
import ast
ast.literal_eval(node_or_string)

例:

# python 2.x - doesn't accept operators in string format
import ast
ast.literal_eval('[1, 2, 3]')  # output: [1, 2, 3]
ast.literal_eval('1+1') # output: ValueError: malformed string


# python 3.0 -3.6
import ast
ast.literal_eval("1+1") # output : 2
ast.literal_eval("{'a': 2, 'b': 3, 3:'xyz'}") # output : {'a': 2, 'b': 3, 3:'xyz'}
# type dictionary
ast.literal_eval("",{}) # output : Syntax Error required only one parameter
ast.literal_eval("__import__('os').system('rm -rf /')") # output : error

eval("__import__('os').system('rm -rf /')") 
# output : start deleting all the files on your computer.
# restricting using global and local variables
eval("__import__('os').system('rm -rf /')",{'__builtins__':{}},{})
# output : Error due to blocked imports by passing  '__builtins__':{} in global

# But still eval is not safe. we can access and break the code as given below
s = """
(lambda fc=(
lambda n: [
    c for c in 
        ().__class__.__bases__[0].__subclasses__() 
        if c.__name__ == n
    ][0]
):
fc("function")(
    fc("code")(
        0,0,0,0,"KABOOM",(),(),(),"","",0,""
    ),{}
)()
)()
"""
eval(s, {'__builtins__':{}})

在上面的代码中().__class__.__bases__[0],对象本身就是什么。现在我们实例化了所有子类,这里我们的主要enter code here目标是从中找到一个名为n的类。

我们需要code对象和function实例化的子类的对象。这是CPython访问对象子类并附加系统的另一种方法。

从python 3.7开始,ast.literal_eval()更加严格了。不再允许对任意数字进行加减。链接

eval: This is very powerful, but is also very dangerous if you accept strings to evaluate from untrusted input. Suppose the string being evaluated is “os.system(‘rm -rf /’)” ? It will really start deleting all the files on your computer.

ast.literal_eval: 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, None, bytes and sets.

Syntax:

eval(expression, globals=None, locals=None)
import ast
ast.literal_eval(node_or_string)

Example:

# python 2.x - doesn't accept operators in string format
import ast
ast.literal_eval('[1, 2, 3]')  # output: [1, 2, 3]
ast.literal_eval('1+1') # output: ValueError: malformed string


# python 3.0 -3.6
import ast
ast.literal_eval("1+1") # output : 2
ast.literal_eval("{'a': 2, 'b': 3, 3:'xyz'}") # output : {'a': 2, 'b': 3, 3:'xyz'}
# type dictionary
ast.literal_eval("",{}) # output : Syntax Error required only one parameter
ast.literal_eval("__import__('os').system('rm -rf /')") # output : error

eval("__import__('os').system('rm -rf /')") 
# output : start deleting all the files on your computer.
# restricting using global and local variables
eval("__import__('os').system('rm -rf /')",{'__builtins__':{}},{})
# output : Error due to blocked imports by passing  '__builtins__':{} in global

# But still eval is not safe. we can access and break the code as given below
s = """
(lambda fc=(
lambda n: [
    c for c in 
        ().__class__.__bases__[0].__subclasses__() 
        if c.__name__ == n
    ][0]
):
fc("function")(
    fc("code")(
        0,0,0,0,"KABOOM",(),(),(),"","",0,""
    ),{}
)()
)()
"""
eval(s, {'__builtins__':{}})

In the above code ().__class__.__bases__[0] nothing but object itself. Now we instantiated all the subclasses, here our main enter code hereobjective is to find one class named n from it.

We need to code object and function object from instantiated subclasses. This is an alternative way from CPython to access subclasses of object and attach the system.

From python 3.7 ast.literal_eval() is now stricter. Addition and subtraction of arbitrary numbers are no longer allowed. link


回答 3

Python 渴望进行评估,因此无论eval(raw_input(...))用户eval随后对数据进行什么操作,只要它点击,就将评估用户的输入。因此,这是不安全的,尤其是在eval用户输入时。

使用ast.literal_eval


例如,在提示符下输入此命令对您非常不利:

__import__('os').system('rm -rf /a-path-you-really-care-about')

Python’s eager in its evaluation, so eval(input(...)) (Python 3) will evaluate the user’s input as soon as it hits the eval, regardless of what you do with the data afterwards. Therefore, this is not safe, especially when you eval user input.

Use ast.literal_eval.


As an example, entering this at the prompt could be very bad for you:

__import__('os').system('rm -rf /a-path-you-really-care-about')

回答 4

如果您需要的只是用户提供的词典,则可能是更好的解决方案json.loads。主要限制是json dict需要字符串键。另外,您只能提供文字数据,但情况也是如此literal_eval

If all you need is a user provided dictionary, possible better solution is json.loads. The main limitation is that json dicts requires string keys. Also you can only provide literal data, but that is also the case for literal_eval.


回答 5

我被困住了ast.literal_eval()。我在IntelliJ IDEA调试器中尝试过它,并一直None在调试器输出中返回。

但是稍后,当我将其输出分配给变量并以代码打印时。工作正常。共享代码示例:

import ast
sample_string = '[{"id":"XYZ_GTTC_TYR", "name":"Suction"}]'
output_value = ast.literal_eval(sample_string)
print(output_value)

其python版本3.6。

I was stuck with ast.literal_eval(). I was trying it in IntelliJ IDEA debugger, and it kept returning None on debugger output.

But later when I assigned its output to a variable and printed it in code. It worked fine. Sharing code example:

import ast
sample_string = '[{"id":"XYZ_GTTC_TYR", "name":"Suction"}]'
output_value = ast.literal_eval(sample_string)
print(output_value)

Its python version 3.6.


]]>
为什么使用“评估”是一种不好的做法? https://pythondict.com/python-qa/%e4%b8%ba%e4%bb%80%e4%b9%88%e4%bd%bf%e7%94%a8%e8%af%84%e4%bc%b0%e6%98%af%e4%b8%80%e7%a7%8d%e4%b8%8d%e5%a5%bd%e7%9a%84%e5%81%9a%e6%b3%95%ef%bc%9f/ Mon, 16 Aug 2021 17:59:14 +0000 https://pythondict.com/?p=9283 问题:为什么使用“评估”是一种不好的做法?

我正在使用以下类轻松存储我的歌曲的数据。

class Song:
    """The class to store the details of each song"""
    attsToStore=('Name', 'Artist', 'Album', 'Genre', 'Location')
    def __init__(self):
        for att in self.attsToStore:
            exec 'self.%s=None'%(att.lower()) in locals()
    def setDetail(self, key, val):
        if key in self.attsToStore:
            exec 'self.%s=val'%(key.lower()) in locals()

我觉得这比写一个代码if/else块更具扩展性。但是,这eval似乎被认为是不良做法,使用不安全。如果是这样,有人可以向我解释原因并向我展示定义上述类的更好方法吗?

I am using the following class to easily store data of my songs.

class Song:
    """The class to store the details of each song"""
    attsToStore=('Name', 'Artist', 'Album', 'Genre', 'Location')
    def __init__(self):
        for att in self.attsToStore:
            exec 'self.%s=None'%(att.lower()) in locals()
    def setDetail(self, key, val):
        if key in self.attsToStore:
            exec 'self.%s=val'%(key.lower()) in locals()

I feel that this is just much more extensible than writing out an if/else block. However, eval seems to be considered a bad practice and unsafe to use. If so, can anyone explain to me why and show me a better way of defining the above class?


回答 0

是的,使用eval是一种不好的做法。仅出于以下几个原因:

  1. 几乎总有一种更好的方法
  2. 非常危险和不安全
  3. 使调试困难

您可以使用setattr代替:

class Song:
    """The class to store the details of each song"""
    attsToStore=('Name', 'Artist', 'Album', 'Genre', 'Location')
    def __init__(self):
        for att in self.attsToStore:
            setattr(self, att.lower(), None)
    def setDetail(self, key, val):
        if key in self.attsToStore:
            setattr(self, key.lower(), val)

编辑:

在某些情况下,您必须使用eval或exec。但是它们很少见。当然,在您的情况下使用eval是一个不好的做法。我要强调不好的做法,因为eval和exec经常在错误的地方使用。

编辑2:

似乎有些不同意,在OP案件中,评估是“非常危险和不安全的”。对于这种特定情况,这可能是正确的,但一般而言并非如此。问题是一般性的,我列出的理由也适用于一般性情况。

编辑3: 重新排序的点1和4

Yes, using eval is a bad practice. Just to name a few reasons:

  1. There is almost always a better way to do it
  2. Very dangerous and insecure
  3. Makes debugging difficult
  4. Slow

In your case you can use setattr instead:

class Song:
    """The class to store the details of each song"""
    attsToStore=('Name', 'Artist', 'Album', 'Genre', 'Location')
    def __init__(self):
        for att in self.attsToStore:
            setattr(self, att.lower(), None)
    def setDetail(self, key, val):
        if key in self.attsToStore:
            setattr(self, key.lower(), val)

EDIT:

There are some cases where you have to use eval or exec. But they are rare. Using eval in your case is a bad practice for sure. I’m emphasizing on bad practice because eval and exec are frequently used in the wrong place.

EDIT 2:

It looks like some disagree that eval is ‘very dangerous and insecure’ in the OP case. That might be true for this specific case but not in general. The question was general and the reasons I listed are true for the general case as well.

EDIT 3: Reordered point 1 and 4


回答 1

使用eval是很弱的,不是一个明显的习惯。

  1. 它违反了“软件基本原理”。您的来源不是可执行文件的总和。除了您的资料来源外,还eval必须清楚地了解到的参数。因此,它是万不得已的工具。

  2. 通常,这是经过漫长设计的标志。动态构建动态源代码的理由很少。委托和其他OO设计技术几乎可以完成任何事情。

  3. 这会导致相对缓慢的小代码即时编译。通过使用更好的设计模式可以避免开销。

作为注脚,在精神错乱的社会主义者的手中,这可能效果不佳。但是,当遇到精神错乱的用户或管理员时,最好不要首先让他们理解Python。在真正的邪恶之手,Python可以承担责任。eval完全不会增加风险。

Using eval is weak, not a clearly bad practice.

  1. It violates the “Fundamental Principle of Software”. Your source is not the sum total of what’s executable. In addition to your source, there are the arguments to eval, which must be clearly understood. For this reason, it’s the tool of last resort.

  2. It’s usually a sign of thoughtless design. There’s rarely a good reason for dynamic source code, built on-the-fly. Almost anything can be done with delegation and other OO design techniques.

  3. It leads to relatively slow on-the-fly compilation of small pieces of code. An overhead which can be avoided by using better design patterns.

As a footnote, in the hands of deranged sociopaths, it may not work out well. However, when confronted with deranged sociopathic users or administrators, it’s best to not give them interpreted Python in the first place. In the hands of the truly evil, Python can a liability; eval doesn’t increase the risk at all.


回答 2

在这种情况下,可以。代替

exec 'self.Foo=val'

您应该使用内置函数setattr

setattr(self, 'Foo', val)

In this case, yes. Instead of

exec 'self.Foo=val'

you should use the builtin function setattr:

setattr(self, 'Foo', val)

回答 3

是的:

使用Python破解:

>>> eval(input())
"__import__('os').listdir('.')"
...........
...........   #dir listing
...........

下面的代码将列出在Windows计算机上运行的所有任务。

>>> eval(input())
"__import__('subprocess').Popen(['tasklist'],stdout=__import__('subprocess').PIPE).communicate()[0]"

在Linux中:

>>> eval(input())
"__import__('subprocess').Popen(['ps', 'aux'],stdout=__import__('subprocess').PIPE).communicate()[0]"

Yes, it is:

Hack using Python:

>>> eval(input())
"__import__('os').listdir('.')"
...........
...........   #dir listing
...........

The below code will list all tasks running on a Windows machine.

>>> eval(input())
"__import__('subprocess').Popen(['tasklist'],stdout=__import__('subprocess').PIPE).communicate()[0]"

In Linux:

>>> eval(input())
"__import__('subprocess').Popen(['ps', 'aux'],stdout=__import__('subprocess').PIPE).communicate()[0]"

回答 4

值得注意的是,对于有问题的特定问题,可以使用eval以下几种替代方法:

如上所述,最简单的方法是使用setattr

def __init__(self):
    for name in attsToStore:
        setattr(self, name, None)

一种不太明显的方法是__dict__直接更新对象的对象。如果您要做的只是将属性初始化为None,那么这比上面的方法要简单。但是考虑一下:

def __init__(self, **kwargs):
    for name in self.attsToStore:
       self.__dict__[name] = kwargs.get(name, None)

这使您可以将关键字参数传递给构造函数,例如:

s = Song(name='History', artist='The Verve')

它还允许您locals()更加明确地使用它,例如:

s = Song(**locals())

…并且,如果您确实要分配None名称的属性,请在中找到locals()

s = Song(**dict([(k, None) for k in locals().keys()]))

为对象提供属性列表默认值的另一种方法是定义类的__getattr__方法:

def __getattr__(self, name):
    if name in self.attsToStore:
        return None
    raise NameError, name

如果无法以常规方式找到named属性,则调用此方法。这种方法比简单地在构造函数中设置属性或更新的方式要简单一些__dict__,但是它的优点是除非存在该属性,否则不实际创建该属性,这样可以大大减少类的内存使用量。

所有这些的要点:通常有很多原因可以避免:避免eval执行无法控制的代码的安全性问题,无法调试的代码的实际问题等。但是,更重要的原因是通常,您不需要使用它。Python向程序员公开了很多内部机制,因此您几乎不需要编写编写代码的代码。

It’s worth noting that for the specific problem in question, there are several alternatives to using eval:

The simplest, as noted, is using setattr:

def __init__(self):
    for name in attsToStore:
        setattr(self, name, None)

A less obvious approach is updating the object’s __dict__ object directly. If all you want to do is initialize the attributes to None, then this is less straightforward than the above. But consider this:

def __init__(self, **kwargs):
    for name in self.attsToStore:
       self.__dict__[name] = kwargs.get(name, None)

This allows you to pass keyword arguments to the constructor, e.g.:

s = Song(name='History', artist='The Verve')

It also allows you to make your use of locals() more explicit, e.g.:

s = Song(**locals())

…and, if you really want to assign None to the attributes whose names are found in locals():

s = Song(**dict([(k, None) for k in locals().keys()]))

Another approach to providing an object with default values for a list of attributes is to define the class’s __getattr__ method:

def __getattr__(self, name):
    if name in self.attsToStore:
        return None
    raise NameError, name

This method gets called when the named attribute isn’t found in the normal way. This approach somewhat less straightforward than simply setting the attributes in the constructor or updating the __dict__, but it has the merit of not actually creating the attribute unless it exists, which can pretty substantially reduce the class’s memory usage.

The point of all this: There are lots of reasons, in general, to avoid eval – the security problem of executing code that you don’t control, the practical problem of code you can’t debug, etc. But an even more important reason is that generally, you don’t need to use it. Python exposes so much of its internal mechanisms to the programmer that you rarely really need to write code that writes code.


回答 5

其他用户指出了如何可以更改不依赖的代码eval; 我将提供一个使用的合法用例eval,即使在CPython中也可以找到一个用例:testing

这是我在test_unary.py其中测试是否(+|-|~)b'a'引发的一个示例TypeError

def test_bad_types(self):
    for op in '+', '-', '~':
        self.assertRaises(TypeError, eval, op + "b'a'")
        self.assertRaises(TypeError, eval, op + "'a'")

显然,这里的用法不是坏习惯;您定义输入,仅观察行为。eval方便测试。

看看这个搜索在eval,在CPython的Git仓库中进行; 大量使用eval进行测试。

Other users pointed out how your code can be changed as to not depend on eval; I’ll offer a legitimate use-case for using eval, one that is found even in CPython: testing.

Here’s one example I found in test_unary.py where a test on whether (+|-|~)b'a' raises a TypeError:

def test_bad_types(self):
    for op in '+', '-', '~':
        self.assertRaises(TypeError, eval, op + "b'a'")
        self.assertRaises(TypeError, eval, op + "'a'")

The usage is clearly not bad practice here; you define the input and merely observe behavior. eval is handy for testing.

Take a look at this search for eval, performed on the CPython git repository; testing with eval is heavily used.


回答 6

什么时候 eval()用于处理用户提供的输入时,您使用户能够拖放到提供以下内容:

"__import__('code').InteractiveConsole(locals=globals()).interact()"

您可以摆脱它,但是通常您不希望向量在您的应用程序中执行任意代码

When eval() is used to process user-provided input, you enable the user to Drop-to-REPL providing something like this:

"__import__('code').InteractiveConsole(locals=globals()).interact()"

You may get away with it, but normally you don’t want vectors for arbitrary code execution in your applications.


回答 7

除了@Nadia Alramli答案之外,由于我是Python的新手,并且渴望检查使用eval将如何影响计时,因此我尝试了一个小程序,以下是观察结果:

#Difference while using print() with eval() and w/o eval() to print an int = 0.528969s per 100000 evals()

from datetime import datetime
def strOfNos():
    s = []
    for x in range(100000):
        s.append(str(x))
    return s

strOfNos()
print(datetime.now())
for x in strOfNos():
    print(x) #print(eval(x))
print(datetime.now())

#when using eval(int)
#2018-10-29 12:36:08.206022
#2018-10-29 12:36:10.407911
#diff = 2.201889 s

#when using int only
#2018-10-29 12:37:50.022753
#2018-10-29 12:37:51.090045
#diff = 1.67292

In addition to @Nadia Alramli answer, since I am new to Python and was eager to check how using eval will affect the timings, I tried a small program and below were the observations:

#Difference while using print() with eval() and w/o eval() to print an int = 0.528969s per 100000 evals()

from datetime import datetime
def strOfNos():
    s = []
    for x in range(100000):
        s.append(str(x))
    return s

strOfNos()
print(datetime.now())
for x in strOfNos():
    print(x) #print(eval(x))
print(datetime.now())

#when using eval(int)
#2018-10-29 12:36:08.206022
#2018-10-29 12:36:10.407911
#diff = 2.201889 s

#when using int only
#2018-10-29 12:37:50.022753
#2018-10-29 12:37:51.090045
#diff = 1.67292

]]>
Python的eval()有什么作用? https://pythondict.com/python-qa/python%e7%9a%84eval%ef%bc%88%ef%bc%89%e6%9c%89%e4%bb%80%e4%b9%88%e4%bd%9c%e7%94%a8%ef%bc%9f/ Sat, 31 Jul 2021 05:07:37 +0000 https://pythondict.com/?p=7515 问题:Python的eval()有什么作用?

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

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

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

In the book that I am reading on Python, it keeps using the code eval(input('blah'))

I read the documentation, and I understand it, but I still do not see how it changes the input() function.

What does it do? Can someone explain?


回答 0

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

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

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

The eval function lets a Python program run Python code within itself.

eval example (interactive shell):

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

回答 1

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

eval() interprets a string as code. The reason why so many people have warned you about using this is because a user can use this as an option to run code on the computer. If you have eval(input()) and os imported, a person could type into input() os.system('rm -R *') which would delete all your files in your home directory. (Assuming you have a unix system). Using eval() is a security hole. If you need to convert strings to other formats, try to use things that do that, like int().


回答 2

这里有很多很好的答案,但没有一个描述在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]

Lots of good answers here, but none describe the use of eval() in the context of its globals and locals kwargs, i.e. eval(expression, globals=None, locals=None) (see docs for eval here).

These can be used to limit the functions that are available through the eval function. For example if you load up a fresh python interpreter the locals() and globals() will be the same and look something like this:

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

There are certainly functions within the builtins module that can do significant damage to a system. But it is possible to block anything and everything we don’t want available. Let’s take an example. Say we want to construct a list to represent a domain of the available cores on a system. For me I have 8 cores so I would want a list [1, 8].

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

Likewise all of __builtins__ is available.

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

Ok. So there we see one function we want exposed and an example of one (of many that can be much more complex) method that we do not want exposed. So let’s block everything.

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

We have effectively blocked all of the __builtins__ functions and as such brought a level of protection into our system. At this point we can start to add back in functions that we do want exposed.

>>>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

Now we have the cpu_count function available while still blocking everything we do not want. In my opinion, this is super powerful and clearly from the scope of the other answers, not a common implementation. There are numerous uses for something like this and as long as it is handled correctly I personally feel eval can be safely used to great value.

N.B.

Something else that is cool about these kwargs is that you can start to use shorthand for your code. Let’s say you use eval as part of a pipeline to execute some imported text. The text doesn’t need to have exact code, it can follow some template file format, and still execute anything you’d like. For example:

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

回答 3

在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转换为表达式并进行解释。通常,这被认为是不良做法。

In Python 2.x input(...) is equivalent to eval(raw_input(...)), in Python 3.x raw_input was renamed input, which I suspect lead to your confusion (you were probably looking at the documentation for input in Python 2.x). Additionally, eval(input(...)) would work fine in Python 3.x, but would raise a TypeError in Python 2.

In this case eval is used to coerce the string returned from input into an expression and interpreted. Generally this is considered bad practice.


回答 4

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

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

Maybe a misleading example of reading a line and interpreting it.

Try eval(input()) and type "1+1" – this should print 2. Eval evaluates expressions.


回答 5

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

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

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

eval() evaluates the passed string as a Python expression and returns the result. For example, eval("1 + 1") interprets and executes the expression "1 + 1" and returns the result (2).

One reason you might be confused is because the code you cited involves a level of indirection. The inner function call (input) gets executed first so the user sees the “blah” prompt. Let’s imagine they respond with “1 + 1” (quotes added for clarity, don’t type them when running your program), the input function returns that string, which is then passed to the outer function (eval) which interprets the string and returns the result (2).

Read more about eval here.


回答 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。

eval(), as the name suggests, evaluates the passed argument.

raw_input() is now input() in python 3.x versions. So the most commonly found example for the use of eval() is its use to provide the functionality that input() provided in 2.x version of python. raw_input returned the user-entered data as a string, while input evaluated the value of data entered and returned it.

eval(input("bla bla")) thus replicates the functionality of input() in 2.x, i.e., of evaluating the user-entered data.

In short: eval() evaluates the arguments passed to it and hence eval('1 + 1') returned 2.


回答 7

的有用应用之一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'}

One of useful applications of eval() is to evaluate python expressions from string. For example load from file string representation of dictionary:

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

Read it out as a variable and edit it:

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

Output:

{'Greeting': 'Hello world'}

回答 8

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

如果用户输入数字值,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

I’m late to answer this question but, no one seems to give clear answer to the question.

If an user enters a numeric value, input() will return a string.

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

So, eval() will evaluate returned value (or expression) which is a string and return integer/float.

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

Of cource this is a bad practice. int() or float() should be used instead of eval() in this case.

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

回答 9

如果要将评估字符串限制为简单文字,则可以使用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更强大。

Another option if you want to limit the evaluation string to simple literals is to use ast.literal_eval(). Some examples:

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}

From the docs:

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.

As for why it’s so limited, from the mailing list:

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.


]]>
eval,exec和compile有什么区别? https://pythondict.com/python-qa/eval%ef%bc%8cexec%e5%92%8ccompile%e6%9c%89%e4%bb%80%e4%b9%88%e5%8c%ba%e5%88%ab%ef%bc%9f/ Sun, 25 Jul 2021 05:21:22 +0000 https://pythondict.com/?p=7176 问题:eval,exec和compile有什么区别?

我一直在研究Python代码的动态评估,并遇到eval()compile()函数,以及exec语句。

有人可以解释之间的区别evalexec怎样的不同模式,compile()适应吗?

I’ve been looking at dynamic evaluation of Python code, and come across the eval() and compile() functions, and the exec statement.

Can someone please explain the difference between eval and exec, and how the different modes of compile() fit in?


回答 0

简短答案,即TL; DR

基本上,eval用于EVAL审视你们单个动态生成的Python表达式,并exec用于EXEC动态生成的Python代码仅针对其副作用尤特。

evalexec具有以下两个区别:

  1. eval仅接受一个表达式exec可以采用具有Python语句的代码块:循环try: except:class和函数/方法def初始化等。

    Python中的表达式就是变量赋值中的值:

    a_variable = (anything you can put within these parentheses is an expression)
  2. eval 返回给定表达式的值,而exec忽略其代码中的返回值,并始终返回None(在Python 2中,它是一条语句,不能用作表达式,因此它实际上不返回任何内容)。

在1.0-2.7版本中,exec有一条声明是因为CPython需要为函数生成另一种类型的代码对象,这些代码对象用于在函数exec内部产生副作用。

在Python 3中,exec是一个函数;它的使用对使用它的函数的已编译字节码没有影响。


因此基本上:

>>> a = 5
>>> eval('37 + a')   # it is an expression
42
>>> exec('37 + a')   # it is an expression statement; value is ignored (None is returned)
>>> exec('a = 47')   # modify a global variable as a side effect
>>> a
47
>>> eval('a = 47')  # you cannot evaluate a statement
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    a = 47
      ^
SyntaxError: invalid syntax

compile'exec'模式编译任何数目的语句编译成字节码隐含总是返回None,而在'eval'模式它编译一个单一表达式成字节码即返回该表达式的值。

>>> eval(compile('42', '<string>', 'exec'))  # code returns None
>>> eval(compile('42', '<string>', 'eval'))  # code returns 42
42
>>> exec(compile('42', '<string>', 'eval'))  # code returns 42,
>>>                                          # but ignored by exec

在这种'eval'模式下(eval如果传递了一个字符串,则在函数中),compile如果源代码包含语句或除单个表达式之外的任何其他内容,则会引发异常:

>>> compile('for i in range(3): print(i)', '<string>', 'eval')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print(i)
      ^
SyntaxError: invalid syntax

实际上,“ eval仅接受单个表达式”语句仅在将字符串(包含Python 源代码)传递给时适用eval。然后将其内部使用编译为字节码。compile(source, '<string>', 'eval')这才是真正的区别。

如果将一个code对象(包含Python 字节码)传递给execeval,则它们的行为相同,除了exec忽略返回值的事实外,它None始终会始终返回。因此eval,如果您只是将compile它先转换为字节码而不是将其作为字符串传递,则可以执行具有语句的内容:

>>> eval(compile('if 1: print("Hello")', '<string>', 'exec'))
Hello
>>>

即使已编译的代码包含语句,也可以正常工作。它仍然会返回None,因为那是从中返回的代码对象的返回值。compile

在这种'eval'模式下(eval如果传递了一个字符串,则在函数中),compile如果源代码包含语句或除单个表达式之外的任何其他内容,则会引发异常:

>>> compile('for i in range(3): print(i)', '<string>'. 'eval')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print(i)
      ^
SyntaxError: invalid syntax

答案越长,又称血腥细节

execeval

exec函数(在Python 2中为语句)用于执行动态创建的语句或程序:

>>> program = '''
for i in range(3):
    print("Python is cool")
'''
>>> exec(program)
Python is cool
Python is cool
Python is cool
>>> 

eval函数对单个表达式执行相同的操作,返回表达式的值:

>>> a = 2
>>> my_calculation = '42 * a'
>>> result = eval(my_calculation)
>>> result
84

execeval均接受该程序/表达到无论是作为一个运行strunicodebytes对象包含源代码,或者作为一个code对象包含的Python字节码。

如果str/ unicode/ bytes包含源代码传递给exec,它等效行为与:

exec(compile(source, '<string>', 'exec'))

并且eval类似地等效于:

eval(compile(source, '<string>', 'eval'))

由于所有表达式都可以用作Python中的语句(Expr在Python 抽象语法中被称为节点;反之则不成立),exec如果不需要返回值,则可以始终使用。也就是说,您可以使用eval('my_func(42)')exec('my_func(42)'),区别在于eval返回的返回值是my_func,并将其exec丢弃:

>>> def my_func(arg):
...     print("Called with %d" % arg)
...     return arg * 2
... 
>>> exec('my_func(42)')
Called with 42
>>> eval('my_func(42)')
Called with 42
84
>>> 

2,只有exec接受包含语句,源代码一样defforwhileimport,或者class,赋值语句(又名a = 42),或整个程序:

>>> exec('for i in range(3): print(i)')
0
1
2
>>> eval('for i in range(3): print(i)')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print(i)
      ^
SyntaxError: invalid syntax

双方execeval接受2个额外的位置参数- globalslocals-这是全局和局部变量的作用域,该代码看到。它们默认为globals()和,它们locals()在称为exec或的范围内eval,但任何字典都可以用于globals和,mapping用于localsdict当然包括)。这些不仅可以用于限制/修改代码中看到的变量,而且还经常用于捕获被引用exec代码创建的变量:

>>> g = dict()
>>> l = dict()
>>> exec('global a; a, b = 123, 42', g, l)
>>> g['a']
123
>>> l
{'b': 42}

(如果您显示整个的价值g,这将是更长的时间,因为execeval添加内置插件模块__builtins__来自动如果缺少它的全局变量)。

在Python 2中,该exec语句的正式语法实际上是exec code in globals, locals,如

>>> exec 'global a; a, b = 123, 42' in g, l

但是,替代语法exec(code, globals, locals)也一直被接受(见下文)。

compile

所述compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)内置的可用于加快与相同的码的重复调用execeval通过编译源到code对象预先。所述mode参数控制的那种代码片段的compile函数接受和种字节码它产生。选择是'eval''exec''single'

  • 'eval'模式需要一个表达式,并将生成字节码,运行时将返回该表达式的值:

    >>> dis.dis(compile('a + b', '<string>', 'eval'))
      1           0 LOAD_NAME                0 (a)
                  3 LOAD_NAME                1 (b)
                  6 BINARY_ADD
                  7 RETURN_VALUE
  • 'exec'接受从单个表达式到整个代码模块的任何类型的python构造,并像将其作为模块顶级语句一样执行它们。代码对象返回None

    >>> dis.dis(compile('a + b', '<string>', 'exec'))
      1           0 LOAD_NAME                0 (a)
                  3 LOAD_NAME                1 (b)
                  6 BINARY_ADD
                  7 POP_TOP                             <- discard result
                  8 LOAD_CONST               0 (None)   <- load None on stack
                 11 RETURN_VALUE                        <- return top of stack
  • 'single'是一种有限形式,如果最后一条语句是表达式语句,则该格式'exec'接受包含单个语句(或多个由分隔的语句;)的源代码,生成的字节码还将该表达式的值打印repr到标准output(!)上

    一个ifelifelse链,有一个循环else,并try用它exceptelsefinally块被视为一个单独的语句。

    包含2个顶级语句的源代码片段是的错误'single',但在Python 2中存在一个错误,有时会在代码中允许多个顶级语句。只有第一个被编译;其余的将被忽略:

    在Python 2.7.8中:

    >>> exec(compile('a = 5\na = 6', '<string>', 'single'))
    >>> a
    5

    在Python 3.4.2中:

    >>> exec(compile('a = 5\na = 6', '<string>', 'single'))
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<string>", line 1
        a = 5
            ^
    SyntaxError: multiple statements found while compiling a single statement

    这对于制作交互式Python Shell非常有用。但是,即使返回eval结果代码,也不返回表达式的值。

这样的最大区别execeval实际上来自compile函数及其模式。


除了将源代码编译为字节码之外,还compile支持将抽象语法树(Python代码的解析树)编译为code对象;并将源代码转换成抽象语法树(ast.parse用Python编写,仅调用compile(source, filename, mode, PyCF_ONLY_AST));这些代码用于动态修改源代码,以及动态代码创建,因为在复杂情况下,将代码作为节点树而不是文本行来处理通常会更容易。


虽然eval只允许您评估包含单个表达式的字符串,但是您可以eval使用整个语句,甚至可以是已被compile打包为字节码的整个模块。也就是说,对于Python 2,这print是一条语句,不能直接eval导致:

>>> eval('for i in range(3): print("Python is cool")')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print("Python is cool")
      ^
SyntaxError: invalid syntax

compile'exec'模式将它变成一个code对象,你就能eval 做到 ; 该eval函数将返回None

>>> code = compile('for i in range(3): print("Python is cool")',
                   'foo.py', 'exec')
>>> eval(code)
Python is cool
Python is cool
Python is cool

如果一个长相到evalexec源代码CPython的3,这是很明显的; 它们都PyEval_EvalCode使用相同的参数调用,唯一的区别是exec显式返回None

execPython 2和Python 3之间的语法差异

其中一个在Python的主要区别2exec一个声明,eval是一个内置的功能(两者都内置函数在Python 3)。众所周知exec,Python 2 中的正式语法为exec code [in globals[, locals]]

与大多数Python 2到3 移植 指南 似乎并不像 建议的那样execCPython 2中的语句也可以与看起来 完全execPython 3中的函数调用的语法一起使用。原因是Python 0.9.9具有exec(code, globals, locals)内置的在功能上!并且该内置函数在Python 1.0发布之前的某处exec语句替换。

由于这是可取的不破与Python 0.9.9向后兼容性,吉多·范罗苏姆在1993年增加了兼容性劈:如果code是长度为2或3的元组,并globalslocals未传递到exec声明,否则,code将被解释就像元组的第二个元素和第三个元素分别是globals和一样locals。即使在Python 1.4文档(在线最早可用的版本)中也没有提到兼容性hack ;因此对于移植指南和工具的许多作者并不了解,直到2012年11月再次对其进行了记录

第一个表达式也可以是长度为2或3的元组。在这种情况下,必须省略可选部分。形式exec(expr, globals)等同于exec expr in globals,而形式exec(expr, globals, locals)等同于exec expr in globals, locals。元组形式exec提供了与Python 3的兼容性,Python 3 exec是函数而不是语句。

是的,在CPython 2.7中它被方便地称为前向兼容选项(为什么使人们感到困惑,因为根本没有向后兼容选项),实际上它已经存在了二十年了

因此,虽然exec在Python 1和Python 2中是一个语句,而在Python 3和Python 0.9.9中是一个内置函数,

>>> exec("print(a)", globals(), {'a': 42})
42

在可能的每个广泛发行的Python版本中都具有相同的行为;并且也可以在Jython 2.5.2,PyPy 2.3.1(Python 2.7.6)和IronPython 2.6.1中使用(对它们的严格遵循CPython的未记录的行为表示敬意)。

在Pythons 1.0-2.7中,通过其兼容性技巧,您不能做的是将返回值存储exec到变量中:

Python 2.7.11+ (default, Apr 17 2016, 14:00:29) 
[GCC 5.3.1 20160413] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = exec('print(42)')
  File "<stdin>", line 1
    a = exec('print(42)')
           ^
SyntaxError: invalid syntax

(这在Python 3中也没有用,因为它exec总是返回None),或将引用传递给exec

>>> call_later(exec, 'print(42)', delay=1000)
  File "<stdin>", line 1
    call_later(exec, 'print(42)', delay=1000)
                  ^
SyntaxError: invalid syntax

某人可能实际使用过的一种模式,尽管可能性不大;

或在列表理解中使用它:

>>> [exec(i) for i in ['print(42)', 'print(foo)']
  File "<stdin>", line 1
    [exec(i) for i in ['print(42)', 'print(foo)']
        ^
SyntaxError: invalid syntax

这是对列表理解的滥用(请for改为使用循环!)。

The short answer, or TL;DR

Basically, eval is used to evaluate a single dynamically generated Python expression, and exec is used to execute dynamically generated Python code only for its side effects.

eval and exec have these two differences:

  1. eval accepts only a single expression, exec can take a code block that has Python statements: loops, try: except:, class and function/method definitions and so on.

    An expression in Python is whatever you can have as the value in a variable assignment:

    a_variable = (anything you can put within these parentheses is an expression)
    
  2. eval returns the value of the given expression, whereas exec ignores the return value from its code, and always returns None (in Python 2 it is a statement and cannot be used as an expression, so it really does not return anything).

In versions 1.0 – 2.7, exec was a statement, because CPython needed to produce a different kind of code object for functions that used exec for its side effects inside the function.

In Python 3, exec is a function; its use has no effect on the compiled bytecode of the function where it is used.


Thus basically:

>>> a = 5
>>> eval('37 + a')   # it is an expression
42
>>> exec('37 + a')   # it is an expression statement; value is ignored (None is returned)
>>> exec('a = 47')   # modify a global variable as a side effect
>>> a
47
>>> eval('a = 47')  # you cannot evaluate a statement
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    a = 47
      ^
SyntaxError: invalid syntax

The compile in 'exec' mode compiles any number of statements into a bytecode that implicitly always returns None, whereas in 'eval' mode it compiles a single expression into bytecode that returns the value of that expression.

>>> eval(compile('42', '<string>', 'exec'))  # code returns None
>>> eval(compile('42', '<string>', 'eval'))  # code returns 42
42
>>> exec(compile('42', '<string>', 'eval'))  # code returns 42,
>>>                                          # but ignored by exec

In the 'eval' mode (and thus with the eval function if a string is passed in), the compile raises an exception if the source code contains statements or anything else beyond a single expression:

>>> compile('for i in range(3): print(i)', '<string>', 'eval')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print(i)
      ^
SyntaxError: invalid syntax

Actually the statement “eval accepts only a single expression” applies only when a string (which contains Python source code) is passed to eval. Then it is internally compiled to bytecode using compile(source, '<string>', 'eval') This is where the difference really comes from.

If a code object (which contains Python bytecode) is passed to exec or eval, they behave identically, excepting for the fact that exec ignores the return value, still returning None always. So it is possible use eval to execute something that has statements, if you just compiled it into bytecode before instead of passing it as a string:

>>> eval(compile('if 1: print("Hello")', '<string>', 'exec'))
Hello
>>>

works without problems, even though the compiled code contains statements. It still returns None, because that is the return value of the code object returned from compile.

In the 'eval' mode (and thus with the eval function if a string is passed in), the compile raises an exception if the source code contains statements or anything else beyond a single expression:

>>> compile('for i in range(3): print(i)', '<string>'. 'eval')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print(i)
      ^
SyntaxError: invalid syntax

The longer answer, a.k.a the gory details

exec and eval

The exec function (which was a statement in Python 2) is used for executing a dynamically created statement or program:

>>> program = '''
for i in range(3):
    print("Python is cool")
'''
>>> exec(program)
Python is cool
Python is cool
Python is cool
>>> 

The eval function does the same for a single expression, and returns the value of the expression:

>>> a = 2
>>> my_calculation = '42 * a'
>>> result = eval(my_calculation)
>>> result
84

exec and eval both accept the program/expression to be run either as a str, unicode or bytes object containing source code, or as a code object which contains Python bytecode.

If a str/unicode/bytes containing source code was passed to exec, it behaves equivalently to:

exec(compile(source, '<string>', 'exec'))

and eval similarly behaves equivalent to:

eval(compile(source, '<string>', 'eval'))

Since all expressions can be used as statements in Python (these are called the Expr nodes in the Python abstract grammar; the opposite is not true), you can always use exec if you do not need the return value. That is to say, you can use either eval('my_func(42)') or exec('my_func(42)'), the difference being that eval returns the value returned by my_func, and exec discards it:

>>> def my_func(arg):
...     print("Called with %d" % arg)
...     return arg * 2
... 
>>> exec('my_func(42)')
Called with 42
>>> eval('my_func(42)')
Called with 42
84
>>> 

Of the 2, only exec accepts source code that contains statements, like def, for, while, import, or class, the assignment statement (a.k.a a = 42), or entire programs:

>>> exec('for i in range(3): print(i)')
0
1
2
>>> eval('for i in range(3): print(i)')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print(i)
      ^
SyntaxError: invalid syntax

Both exec and eval accept 2 additional positional arguments – globals and locals – which are the global and local variable scopes that the code sees. These default to the globals() and locals() within the scope that called exec or eval, but any dictionary can be used for globals and any mapping for locals (including dict of course). These can be used not only to restrict/modify the variables that the code sees, but are often also used for capturing the variables that the executed code creates:

>>> g = dict()
>>> l = dict()
>>> exec('global a; a, b = 123, 42', g, l)
>>> g['a']
123
>>> l
{'b': 42}

(If you display the value of the entire g, it would be much longer, because exec and eval add the built-ins module as __builtins__ to the globals automatically if it is missing).

In Python 2, the official syntax for the exec statement is actually exec code in globals, locals, as in

>>> exec 'global a; a, b = 123, 42' in g, l

However the alternate syntax exec(code, globals, locals) has always been accepted too (see below).

compile

The compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1) built-in can be used to speed up repeated invocations of the same code with exec or eval by compiling the source into a code object beforehand. The mode parameter controls the kind of code fragment the compile function accepts and the kind of bytecode it produces. The choices are 'eval', 'exec' and 'single':

  • 'eval' mode expects a single expression, and will produce bytecode that when run will return the value of that expression:

    >>> dis.dis(compile('a + b', '<string>', 'eval'))
      1           0 LOAD_NAME                0 (a)
                  3 LOAD_NAME                1 (b)
                  6 BINARY_ADD
                  7 RETURN_VALUE
    
  • 'exec' accepts any kinds of python constructs from single expressions to whole modules of code, and executes them as if they were module top-level statements. The code object returns None:

    >>> dis.dis(compile('a + b', '<string>', 'exec'))
      1           0 LOAD_NAME                0 (a)
                  3 LOAD_NAME                1 (b)
                  6 BINARY_ADD
                  7 POP_TOP                             <- discard result
                  8 LOAD_CONST               0 (None)   <- load None on stack
                 11 RETURN_VALUE                        <- return top of stack
    
  • 'single' is a limited form of 'exec' which accepts a source code containing a single statement (or multiple statements separated by ;) if the last statement is an expression statement, the resulting bytecode also prints the repr of the value of that expression to the standard output(!).

    An ifelifelse chain, a loop with else, and try with its except, else and finally blocks is considered a single statement.

    A source fragment containing 2 top-level statements is an error for the 'single', except in Python 2 there is a bug that sometimes allows multiple toplevel statements in the code; only the first is compiled; the rest are ignored:

    In Python 2.7.8:

    >>> exec(compile('a = 5\na = 6', '<string>', 'single'))
    >>> a
    5
    

    And in Python 3.4.2:

    >>> exec(compile('a = 5\na = 6', '<string>', 'single'))
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<string>", line 1
        a = 5
            ^
    SyntaxError: multiple statements found while compiling a single statement
    

    This is very useful for making interactive Python shells. However, the value of the expression is not returned, even if you eval the resulting code.

Thus greatest distinction of exec and eval actually comes from the compile function and its modes.


In addition to compiling source code to bytecode, compile supports compiling abstract syntax trees (parse trees of Python code) into code objects; and source code into abstract syntax trees (the ast.parse is written in Python and just calls compile(source, filename, mode, PyCF_ONLY_AST)); these are used for example for modifying source code on the fly, and also for dynamic code creation, as it is often easier to handle the code as a tree of nodes instead of lines of text in complex cases.


While eval only allows you to evaluate a string that contains a single expression, you can eval a whole statement, or even a whole module that has been compiled into bytecode; that is, with Python 2, print is a statement, and cannot be evalled directly:

>>> eval('for i in range(3): print("Python is cool")')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print("Python is cool")
      ^
SyntaxError: invalid syntax

compile it with 'exec' mode into a code object and you can eval it; the eval function will return None.

>>> code = compile('for i in range(3): print("Python is cool")',
                   'foo.py', 'exec')
>>> eval(code)
Python is cool
Python is cool
Python is cool

If one looks into eval and exec source code in CPython 3, this is very evident; they both call PyEval_EvalCode with same arguments, the only difference being that exec explicitly returns None.

Syntax differences of exec between Python 2 and Python 3

One of the major differences in Python 2 is that exec is a statement and eval is a built-in function (both are built-in functions in Python 3). It is a well-known fact that the official syntax of exec in Python 2 is exec code [in globals[, locals]].

Unlike majority of the Python 2-to-3 porting guides seem to suggest, the exec statement in CPython 2 can be also used with syntax that looks exactly like the exec function invocation in Python 3. The reason is that Python 0.9.9 had the exec(code, globals, locals) built-in function! And that built-in function was replaced with exec statement somewhere before Python 1.0 release.

Since it was desirable to not break backwards compatibility with Python 0.9.9, Guido van Rossum added a compatibility hack in 1993: if the code was a tuple of length 2 or 3, and globals and locals were not passed into the exec statement otherwise, the code would be interpreted as if the 2nd and 3rd element of the tuple were the globals and locals respectively. The compatibility hack was not mentioned even in Python 1.4 documentation (the earliest available version online); and thus was not known to many writers of the porting guides and tools, until it was documented again in November 2012:

The first expression may also be a tuple of length 2 or 3. In this case, the optional parts must be omitted. The form exec(expr, globals) is equivalent to exec expr in globals, while the form exec(expr, globals, locals) is equivalent to exec expr in globals, locals. The tuple form of exec provides compatibility with Python 3, where exec is a function rather than a statement.

Yes, in CPython 2.7 that it is handily referred to as being a forward-compatibility option (why confuse people over that there is a backward compatibility option at all), when it actually had been there for backward-compatibility for two decades.

Thus while exec is a statement in Python 1 and Python 2, and a built-in function in Python 3 and Python 0.9.9,

>>> exec("print(a)", globals(), {'a': 42})
42

has had identical behaviour in possibly every widely released Python version ever; and works in Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) and IronPython 2.6.1 too (kudos to them following the undocumented behaviour of CPython closely).

What you cannot do in Pythons 1.0 – 2.7 with its compatibility hack, is to store the return value of exec into a variable:

Python 2.7.11+ (default, Apr 17 2016, 14:00:29) 
[GCC 5.3.1 20160413] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = exec('print(42)')
  File "<stdin>", line 1
    a = exec('print(42)')
           ^
SyntaxError: invalid syntax

(which wouldn’t be useful in Python 3 either, as exec always returns None), or pass a reference to exec:

>>> call_later(exec, 'print(42)', delay=1000)
  File "<stdin>", line 1
    call_later(exec, 'print(42)', delay=1000)
                  ^
SyntaxError: invalid syntax

Which a pattern that someone might actually have used, though unlikely;

Or use it in a list comprehension:

>>> [exec(i) for i in ['print(42)', 'print(foo)']
  File "<stdin>", line 1
    [exec(i) for i in ['print(42)', 'print(foo)']
        ^
SyntaxError: invalid syntax

which is abuse of list comprehensions (use a for loop instead!).


回答 1

  1. exec不是表达式:Python 2.x中的语句和Python 3.x中的函数。它编译并立即评估字符串中包含的一条语句或一组语句。例:

    exec('print(5)')           # prints 5.
    # exec 'print 5'     if you use Python 2.x, nor the exec neither the print is a function there
    exec('print(5)\nprint(6)')  # prints 5{newline}6.
    exec('if True: print(6)')  # prints 6.
    exec('5')                 # does nothing and returns nothing.
  2. eval是一个内置函数(不是语句),该函数对一个表达式求值并返回该表达式产生的值。例:

    x = eval('5')              # x <- 5
    x = eval('%d + 6' % x)     # x <- 11
    x = eval('abs(%d)' % -100) # x <- 100
    x = eval('x = 5')          # INVALID; assignment is not an expression.
    x = eval('if 1: x = 4')    # INVALID; if is a statement, not an expression.
  3. compile是水平较低版本execeval。它不会执行或评估您的语句或表达式,但会返回可以执行此操作的代码对象。模式如下:

    1. compile(string, '', 'eval')返回如果您完成将执行的代码对象eval(string)。请注意,您不能在这种模式下使用语句。仅(单个)表达式有效。
    2. compile(string, '', 'exec')返回如果您完成将执行的代码对象exec(string)。您可以在此处使用任意数量的语句。
    3. compile(string, '', 'single')类似于exec模式,但是它将忽略除第一条语句以外的所有内容。请注意,带有结果的if/ else语句被视为单个语句。
  1. exec is not an expression: a statement in Python 2.x, and a function in Python 3.x. It compiles and immediately evaluates a statement or set of statement contained in a string. Example:

    exec('print(5)')           # prints 5.
    # exec 'print 5'     if you use Python 2.x, nor the exec neither the print is a function there
    exec('print(5)\nprint(6)')  # prints 5{newline}6.
    exec('if True: print(6)')  # prints 6.
    exec('5')                 # does nothing and returns nothing.
    
  2. eval is a built-in function (not a statement), which evaluates an expression and returns the value that expression produces. Example:

    x = eval('5')              # x <- 5
    x = eval('%d + 6' % x)     # x <- 11
    x = eval('abs(%d)' % -100) # x <- 100
    x = eval('x = 5')          # INVALID; assignment is not an expression.
    x = eval('if 1: x = 4')    # INVALID; if is a statement, not an expression.
    
  3. compile is a lower level version of exec and eval. It does not execute or evaluate your statements or expressions, but returns a code object that can do it. The modes are as follows:

    1. compile(string, '', 'eval') returns the code object that would have been executed had you done eval(string). Note that you cannot use statements in this mode; only a (single) expression is valid.
    2. compile(string, '', 'exec') returns the code object that would have been executed had you done exec(string). You can use any number of statements here.
    3. compile(string, '', 'single') is like the exec mode, but it will ignore everything except for the first statement. Note that an if/else statement with its results is considered a single statement.

回答 2

exec用于语句,不返回任何内容。eval用于表达式,并返回表达式的值。

表达式表示“某事”,而语句表示“做某事”。

exec is for statement and does not return anything. eval is for expression and returns value of expression.

expression means “something” while statement means “do something”.


]]>