总而言之:
-
在python 2和python 3中没有bug
-
不同的行为
exec
源于
执行程序
在python 2中是一个语句,而在python 3中它变成了一个函数。
请注意:
我这里什么都不说。这只是事实的集合
在所有其他答案和评论中都能找到。
我在这里所要做的就是揭示一些更加模糊的细节。
python 2和python 3的唯一区别是,
执行程序
能够在python 2中更改封闭函数的本地作用域(因为它是一个语句并且可以访问当前的本地作用域),并且在python 3中不能再这样做(因为它现在是一个函数,所以在它自己的本地作用域中运行)。
然而,这种刺激与
执行程序
声明,它只源于一个特殊的行为细节:
locals()
返回某个值,我要调用它“在调用
本地人()
,始终只引用本地作用域中的所有变量。
请注意
本地人()
未在python 2和3之间更改。所以,这种行为加上如何改变
执行程序
作品看起来很不稳定,但事实并非如此,因为它只是暴露了一些细节,而这些细节总是存在的。
“引用局部作用域中变量的作用域可变单例”是什么意思?
-
它是一个
scope-wise singleton
不管你多久打一次电话
本地人()
在同一范围内,返回的对象始终相同。
-
因此观察到
id(d) == id(locals())
,因为
d
和
本地人()
引用同一个对象,同一个单例,因为只能有一个(在不同的范围中,您会得到不同的对象,但在相同的范围中,您只能看到这个单例)。
-
它是
mutable
,因为它是一个普通对象,所以您可以更改它。
-
本地人()
强制对象中的所有条目再次引用本地范围中的变量。
-
如果您更改了对象中的某些内容(通过
D
,这会改变对象,因为它是一个普通的可变对象。
-
由于对象中的所有条目都是
references to the variables in the local scope
. 因此,如果您更改条目,这些条目将更改singleton对象,而不是“在更改引用之前指向的引用”的内容(因此您不会更改局部变量)。
-
在python中,字符串和数字是不可变的。这意味着,如果您为一个条目分配了一些东西,您不会更改条目指向的对象,而是引入一个新的对象,并为该条目分配一个对该对象的引用。例子:
a = 1
d = locals()
d['a'] = 300
# d['a']==300
locals()
# d['a']==1
除了优化,这还可以:
-
创建新的对象编号(1)-这是一些其他的单例,btw。
-
将指向此数字(1)的指针存储到
LOCALS['a']
(何处)
LOCALS
应为内部本地范围)
-
如果不存在,则创建
SINGLETON
对象
-
更新
单子
,因此它引用
当地人
-
存储的指针
单子
进入之内
LOCALS['d']
-
创建编号(300),即
不
顺便说一句,单人间。
-
将指向这些数字(300)的指针存储到
d['a']
-
因此
单子
也更新了。
-
但是
当地人
是
不
更新了,
所以局部变量
a
或
局部变量['a']
仍然是数字(1)
-
现在,
本地人()
再次被调用,
单子
已更新。
-
AS
D
指
单子
不是
当地人
,
D
也有变化!
关于这个令人惊讶的细节,为什么
1
是单身汉吗?
300
不是,看
https://stackoverflow.com/a/306353
但请不要忘记:数字是不可变的,所以如果您试图将一个数字更改为另一个值,您就可以有效地创建另一个对象。
结论:
你不能带回来
执行程序
python 2到python 3的行为(通过更改代码除外),因为无法再更改程序流之外的局部变量。
但是,您可以将python 3的行为引入python 2,这样您就可以在今天编写运行相同的程序,而不管它们是与python3还是python2一起运行。这是因为在(更新的)python 2中可以使用
执行程序
对于类似函数的参数(实际上,这些参数是2或3元组),with允许使用与python 3中已知语义相同的语法:
exec "code"
(只在python 2中工作)变为(在python2和3中工作):
exec("code", globals(), locals())
但要小心,那
"code"
不能再这样更改本地封闭范围。另请参见
https://docs.python.org/2/reference/simple_stmts.html#exec
最后几句话:
改变
执行程序
在python 3中很好。因为优化。
在python 2中,您无法在
执行程序
,因为包含不可变内容的所有局部变量的状态可能会发生不可预测的更改。这不可能再发生了。现在,函数调用的一般规则适用于
exec()
和其他功能一样。