Python 2 和 Python 3 中 exec 函数的行为
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15086040/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Behavior of exec function in Python 2 and Python 3
提问by Holger
Following code gives different output in Python2and in Python3:
以下代码在 inPython2和 in 中给出了不同的输出Python3:
from sys import version
print(version)
def execute(a, st):
b = 42
exec("b = {}\nprint('b:', b)".format(st))
print(b)
a = 1.
execute(a, "1.E6*a")
Python2prints:
Python2印刷:
2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)]
('b:', 1000000.0)
1000000.0
Python3prints:
Python3印刷:
3.2.3 (default, Apr 11 2012, 07:15:24) [MSC v.1500 32 bit (Intel)]
b: 1000000.0
42
Why does Python2bind the variable binside the executefunction to the values in the string of the execfunction, while Python3doesn't do this? How can I achieve the behavior of Python2in Python3? I already tried to pass dictionaries for globals and locals to execfunction in Python3, but nothing worked so far.
为什么Python2将函数b内部的变量绑定到execute函数字符串中的exec值,而Python3没有这样做呢?我怎样才能实现Python2in的行为Python3?我已经尝试为全局变量和本地变量传递字典以exec在 中运行Python3,但到目前为止没有任何效果。
--- EDIT ---
- - 编辑 - -
After reading Martijns answer I further analyzed this with Python3. In following example I give the locals()dictionay as dto exec, but d['b']prints something else than just printing b.
在阅读了 Martijns 的回答后,我用Python3. 在下面的例子中,我给出了locals()关于dto的字典exec,但d['b']打印的东西不仅仅是打印b。
from sys import version
print(version)
def execute(a, st):
b = 42
d = locals()
exec("b = {}\nprint('b:', b)".format(st), globals(), d)
print(b) # This prints 42
print(d['b']) # This prints 1000000.0
print(id(d) == id(locals())) # This prints True
a = 1.
execute(a, "1.E6*a")
3.2.3 (default, Apr 11 2012, 07:15:24) [MSC v.1500 32 bit (Intel)]
b: 1000000.0
42
1000000.0
True
The comparison of the ids of dand locals()shows that they are the same object. But under these conditions bshould be the same as d['b']. What is wrong in my example?
的id的比较d和locals()显示,它们是相同的对象。但是在这些条件下b应该是一样的d['b']。我的例子有什么问题?
采纳答案by Martijn Pieters
There is a big difference between execin Python 2 and exec()in Python 3. You are treating execas a function, but it really is a statementin Python 2.
execPython 2 和exec()Python 3之间存在很大差异。您将其exec视为函数,但它实际上是Python 2 中的语句。
Because of this difference, you cannot change local variables in function scope in Python 3 using exec, even though it was possible in Python 2. Not even previously declared variables.
由于这种差异,您不能使用 更改 Python 3 中函数作用域中的局部变量exec,即使在 Python 2 中是可能的。甚至以前声明的变量也不行。
locals()only reflects local variables in one direction. The following never worked in either 2 or 3:
locals()只反映一个方向的局部变量。以下从未在 2 或 3 中起作用:
def foo():
a = 'spam'
locals()['a'] = 'ham'
print(a) # prints 'spam'
In Python 2, using the execstatement meant the compiler knew to switch off the local scope optimizations (switching from LOAD_FASTto LOAD_NAMEfor example, to look up variables in both the local and global scopes). With exec()being a function, that option is no longer available and function scopes are now alwaysoptimized.
在 Python 2 中,使用该exec语句意味着编译器知道要关闭局部范围优化(例如,从 切换LOAD_FAST到LOAD_NAME在局部和全局范围内查找变量)。随着exec()作为一个功能,该选项不再可用,现在功能作用域总是优化。
Moreover, in Python 2, the execstatement explicitly copies all variables found in locals()back to the function locals using PyFrame_LocalsToFast, but only if no globalsand localsparameters were supplied.
此外,在 Python 2 中,该exec语句使用 将所有在其中找到的变量显式复制locals()回函数 locals PyFrame_LocalsToFast,但前提是未提供globals和locals参数。
The proper work-around is to use a new namespace (a dictionary) for your exec()call:
正确的解决方法是为您的exec()调用使用新的命名空间(字典):
def execute(a, st):
namespace = {}
exec("b = {}\nprint('b:', b)".format(st), namespace)
print(namespace['b'])
The exec()documentationis very explicit about this limitation:
该exec()文件是有关此限制很明确的:
Note:The default localsact as described for function
locals()below: modifications to the default localsdictionary should not be attempted. Pass an explicit localsdictionary if you need to see effects of the code on locals after functionexec()returns.
注意:默认本地人的行为如
locals()下面的功能所述:不应尝试修改默认本地人字典。如果您需要在函数返回后查看代码对 locals 的影响,请传递一个显式的locals字典exec()。
回答by Holger
I'm afraid I can't explain it exactly, but it basically comes from the fact that b inside the function is local, and exec()appears to assign to the global b. You'll have to declare b to be global inside the function, andinside the exec statement.
恐怕我不能确切地解释它,但它基本上来自这样一个事实,即函数内部的 b 是局部的,并且exec()似乎分配给了全局 b。你必须申报b可能是全球在函数内部,并且exec语句里面。
Try this:
尝试这个:
from sys import version
print(version)
def execute1(a, st):
b = 42
exec("b = {}\nprint('b:', b)".format(st))
print(b)
def execute2(a, st):
global b
b = 42
exec("global b; b = {}\nprint('b:', b)".format(st))
print(b)
a = 1.
execute1(a, "1.E6*a")
print()
execute2(a, "1.E6*a")
print()
b = 42
exec("b = {}\nprint('b:', b)".format('1.E6*a'))
print(b)
Which gives me
这给了我
3.3.0 (default, Oct 5 2012, 11:34:49)
[GCC 4.4.5]
b: 1000000.0
42
b: 1000000.0
1000000.0
b: 1000000.0
1000000.0
You can see that outside the function, the global b is automatically picked up. Inside the function, you're printing the local b.
可以看到,在函数外,全局b被自动拾取。在函数内部,您正在打印本地 b。
Note that I would have thought that exec()always uses the global b first, so that in execute2(), you don't need to declare it inside the exec()function. But I find that doesn't work (which is the part I can't explain exactly).
请注意,我会认为exec()总是首先使用全局 b ,因此在 中execute2(),您不需要在exec()函数内声明它。但我发现这不起作用(这是我无法准确解释的部分)。
回答by LRGH
I'd say it's a bug of python3.
我会说这是python3的一个错误。
def u():
exec("a=2")
print(locals()['a'])
u()
prints "2".
打印“2”。
def u():
exec("a=2")
a=2
print(a)
u()
prints "2".
打印“2”。
But
但
def u():
exec("a=2")
print(locals()['a'])
a=2
u()
fails with
失败
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in u
KeyError: 'a'
--- EDIT --- Another interesting behaviour:
--- 编辑 --- 另一个有趣的行为:
def u():
a=1
l=locals()
exec("a=2")
print(l)
u()
def u():
a=1
l=locals()
exec("a=2")
locals()
print(l)
u()
outputs
产出
{'l': {...}, 'a': 2}
{'l': {...}, 'a': 1}
And also
并且
def u():
l=locals()
exec("a=2")
print(l)
print(locals())
u()
def u():
l=locals()
exec("a=2")
print(l)
print(locals())
a=1
u()
outputs
产出
{'l': {...}, 'a': 2}
{'l': {...}, 'a': 2}
{'l': {...}, 'a': 2}
{'l': {...}}
Apparently, the action of execon locals is the following:
显然,exec对本地人的操作如下:
- If a variable is set within
execand this variable was a local variable, thenexecmodifies the internal dictionary (the one returned bylocals()) and does not return it to its original state. A call tolocals()updates the dictionary (as documented in section 2 of python documentation), and the value set withinexecis forgotten. The need of callinglocals()to update the dictionary is not a bug of python3, because it is documented, but it is not intuitive. Moreover, the fact that modifications of locals withinexecdon't change the locals of the function is a documented difference with python2 (the documentation says "Pass an explicit locals dictionary if you need to see effects of the code on locals after function exec() returns"), and I prefer the behaviour of python2. - If a variable is set within
execand this variable did not exist before, thenexecmodifies the internal dictionary unless the variable is set afterwards. It seems that there is a bug in the waylocals()updates the dictionary ; this bug gives access to the value set withinexecby callinglocals()afterexec.
- 如果在其中设置了一个变量
exec并且该变量是一个局部变量,则exec修改内部字典(由 返回的字典locals())并且不会将其返回到其原始状态。调用locals()更新字典(如 python 文档的第 2 节所述),并且exec忘记了其中设置的值。需要调用locals()更新字典不是python3的bug,因为有文档说明,但是不直观。此外,修改exec局部变量不会改变函数的局部变量这一事实是与 python2 有记录的差异(文档说“如果您需要在函数 exec() 之后查看代码对局部变量的影响,则传递一个显式的局部变量字典返回”),我更喜欢python2的行为。 - 如果在其中设置了一个变量
exec并且该变量之前不存在,则exec修改内部字典,除非该变量是在之后设置的。locals()更新字典的方式好像有bug ;此错误exec通过调用locals()after来访问其中设置的值exec。
回答by Tino
To sum it up:
把它们加起来:
- There is no bug in Python 2 nor in Python 3
- The different behavior of
execstems fromexecbeing a statement in Python 2, while it became a function in Python 3.
- Python 2 和 Python 3 中都没有错误
- 的不同行为
exec源于exec在 Python 2 中是一个语句,而在 Python 3 中它变成了一个函数。
Please note:
I do not tell anything new here. This is just an assembly of the truth out there found in all the other answers and comments. All I try here is to bring light to some of the more obscure details.
请注意:
我在这里不讲任何新的东西。这只是在所有其他答案和评论中找到的真相的集合。我在这里所做的只是为一些更晦涩的细节带来光明。
The only difference between Python 2 and Python 3 is, that, indeed, execis able to change the local scope of the enclosing function in Python 2 (because it is a statement and can access the current local scope) and cannot do this anymore in Python 3 (because it now is a function, so runs in it's own local scope).
Python 2 和 Python 3 之间的唯一区别是,确实exec能够在 Python 2 中更改封闭函数的局部作用域(因为它是一个语句并且可以访问当前局部作用域)并且在 Python 中不能再这样做了3(因为它现在是一个函数,所以在它自己的本地范围内运行)。
The irritation, however, has nothing to do with the execstatement, it only stems from one special behavior detail:
然而,愤怒与exec声明无关,它仅源于一个特殊的行为细节:
locals()returns something, which I want to call "a scope-wise mutable singleton which, after the call to locals(), always only references all variables in the local scope".
locals()返回一些东西,我想称之为“一个范围明智的可变单例,在调用之后locals(),总是只引用本地范围内的所有变量”。
Please note that the behavior of locals()did not change between Python 2 and 3. So, this behavior together with change of how execworks looks like being erratic, but isn't, as it just exposes some detail, which always was there.
请注意,locals()Python 2 和 Python 3 之间的行为没有改变。因此,这种行为连同工作方式的改变exec看起来很不稳定,但事实并非如此,因为它只是暴露了一些始终存在的细节。
What does "a scope-wise mutable singleton which references variables in local scope" mean?
“在局部范围内引用变量的范围明智的可变单例”是什么意思?
- It is a
scope-wise singleton, as regardless how often you calllocals()in the same scope, the object returned is always the same.- Hence the observation, that
id(d) == id(locals()), becausedandlocals()refer to the same object, the same singleton, as there can only be one (in a different scope you get a different object, but in the same scope you only see this single one).
- Hence the observation, that
- It is
mutable, as it is a normal object, so you can alter it.locals()forces all entries in the object to reference the variables in the local scope again.- If you change something in the object (via
d), this alters the object, as it is a normal mutable object.
These changes of the singleton do not propagate back into the local scope, because all entries in the object are
references to the variables in the local scope. So if you alter entries, these changes the singleton object, and not the contents of where "the references pointed to before you change the reference" (hence you do not alter the local variable).In Python, Strings and Numbers are not mutable. This means, if you assign something to an entry, you do not change the object where the entry points to, you introduce a new object and assign a reference to that to the entry. Example:
a = 1 d = locals() d['a'] = 300 # d['a']==300 locals() # d['a']==1
Besides optimization this does:
- Create new object Number(1) - which is some other singleton, BTW.
- store pointer to this Number(1) into
LOCALS['a']
(whereLOCALSshall be the internal local scope) - If not already exist, create
SINGLETONobject - update
SINGLETON, so it references all entries inLOCALS - store pointer of the
SINGLETONintoLOCALS['d'] - Create Number(300), which is nota singleton, BTW.
- store pointer to these Number(300) into
d['a'] - hence the
SINGLETONis updated, too. - but
LOCALSis notupdated, so the local variableaorLOCALS['a']still is Number(1) - Now,
locals()is called again, theSINGLETONis updated. - As
drefers toSINGLETON, notLOCALS,dchanges, too!
- 它是 a
scope-wise singleton,因为无论您locals()在同一范围内调用的频率如何,返回的对象始终相同。- 因此观察到
id(d) == id(locals()),因为dandlocals()指的是同一个对象,同一个单例,因为只能有一个(在不同的范围内你会得到一个不同的对象,但在同一范围内你只能看到这个单一的对象)。
- 因此观察到
- 它是
mutable,因为它是一个普通对象,因此您可以对其进行更改。locals()强制对象中的所有条目再次引用局部作用域中的变量。- 如果您更改对象中的某些内容(通过
d),这会更改对象,因为它是一个普通的可变对象。
单例的这些更改不会传播回本地范围,因为对象中的所有条目都是
references to the variables in the local scope. 因此,如果您更改条目,这些将更改单例对象,而不是“更改引用之前指向的引用”的内容(因此您不会更改局部变量)。在 Python 中,字符串和数字是不可变的。这意味着,如果您为条目分配某些内容,则不会更改该条目指向的对象,而是引入一个新对象并将对该对象的引用分配给该条目。例子:
a = 1 d = locals() d['a'] = 300 # d['a']==300 locals() # d['a']==1
除了优化之外,还有:
- 创建新对象 Number(1) - 这是其他一些单例,顺便说一句。
- 将指向此 Number(1) 的指针存储到
LOCALS['a']
(其中LOCALS应为内部本地范围) - 如果不存在,则创建
SINGLETON对象 - update
SINGLETON,因此它引用中的所有条目LOCALS - 所述的存储指针
SINGLETON到LOCALS['d'] - 创建 Number(300),它不是单例,顺便说一句。
- 将指向这些 Number(300) 的指针存储到
d['a'] - 因此
SINGLETON也更新了。 - 但
LOCALS在没有更新,所以局部变量a或LOCALS['a']仍是数(1) - 现在,
locals()再次调用,SINGLETON更新。 - As
d指的是SINGLETON,不是LOCALS,也d发生了变化!
For more on this surprising detail, why
1is a singleton while300is not, see https://stackoverflow.com/a/306353But please do not forget: Numbers are immutable, so if you try to change a number to another value, you effectively create another object.
有关这个令人惊讶的细节的更多信息,为什么
1不是单例300,请参阅https://stackoverflow.com/a/306353但请不要忘记:数字是不可变的,因此如果您尝试将数字更改为另一个值,您实际上会创建另一个对象。
Conclusion:
结论:
You cannot bring back the execbehavior of Python 2 to Python 3 (except by changing your code), as there is no way to alter the local variables outside of the program flow anymore.
您无法将execPython 2的行为恢复到 Python 3(除非更改您的代码),因为无法再更改程序流之外的局部变量。
However, you can bring the behavior of Python 3 to Python 2, such that you, today, can write programs, which run the same, regardless if they run with Python 3 or Python 2. This is because in (newer) Python 2 you can use execwith function like arguments as well (in fact, those is a 2- or 3-tuple), with allows to use the same syntax with the same semantics known from Python 3:
但是,您可以将 Python 3 的行为引入 Python 2,这样您今天就可以编写运行相同的程序,无论它们是使用 Python 3 还是 Python 2 运行。这是因为在(较新的)Python 2 中,您也可以exec与类似参数的函数一起使用(实际上,它们是一个 2 元组或 3 元组),允许使用与 Python 3 相同的语义的相同语法:
exec "code"
(which only works in Python 2) becomes (which works for Python 2 and 3):
(仅适用于 Python 2)变为(适用于 Python 2 和 3):
exec("code", globals(), locals())
But beware, that "code"can no more alter the local enclosing scope this way. See also https://docs.python.org/2/reference/simple_stmts.html#exec
但要注意,这"code"不能再以这种方式改变本地封闭范围。另见https://docs.python.org/2/reference/simple_stmts.html#exec
Some very last words:
一些最后的话:
The change of execin Python 3 is good. Because of optimization.
execPython 3 中的 变化很好。因为优化。
In Python 2 you were not able to optimize across exec, because the state of all local variables which contained immutable contents could change unpredictably. This cannot happen anymore. Now the usual rules of function invocations apply to exec()like to all other functions, too.
在 Python 2 中,您无法优化 cross exec,因为包含不可变内容的所有局部变量的状态可能会发生不可预测的变化。这不能再发生了。现在函数调用的通常规则也适用exec()于所有其他函数。

