为什么对exec进行Python 3更改会破坏此代码? [英] Why did Python 3 changes to exec break this code?
问题描述
我浏览了SO上无数的"Python exec"线程,但找不到能解决我问题的线程.非常抱歉,如果以前已经有人问过这个问题.这是我的问题:
I looked through the myriad 'Python exec' threads on SO, but couldn't find one that answered my issue. Terribly sorry if this has been asked before. Here's my problem:
# Python 2.6: prints 'it is working'
# Python 3.1.2: "NameError: global name 'a_func' is not defined"
class Testing(object):
def __init__(self):
exec("""def a_func():
print('it is working')""")
a_func()
Testing()
# Python 2.6: prints 'it is working'
# Python 3.1.2: prints 'it is working'
class Testing(object):
def __init__(self):
def a_func():
print('it is working')
a_func()
Testing()
由于标准函数定义在两个Python版本中均有效,因此我认为问题一定是对exec工作方式的改变.我阅读了exec
的2.6和3的API文档,还阅读了"Python 3.0的新功能"页面,但看不到任何导致代码中断的原因.
As the standard function definition works in both Python versions, I'm assuming the problem must be a change to the way exec works. I read the API docs for 2.6 and 3 for exec
and also read the "What's New In Python 3.0" page and couldn't see any reason why the code would break.
推荐答案
您可以使用以下命令查看每个Python版本生成的字节码:
You can see the generated bytecode for each Python version with:
>>> from dis import dis
而且,对于每个口译员:
And, for each interpreter:
#Python 3.2
>>> dis(Testing.__init__)
...
5 10 LOAD_GLOBAL 1 (a_func)
...
#Python 2.7
>>> dis(Testing.__init__)
...
5 8 LOAD_NAME 0 (a_func)
...
如您所见,Python 3.2搜索名为a_func
的全局值(LOAD_GLOBAL),而2.7首先搜索本地范围(LOAD_NAME),然后再搜索全局范围.
As you can see, Python 3.2 searches for a global value (LOAD_GLOBAL) named a_func
and 2.7 first searches the local scope (LOAD_NAME) before searching the global one.
如果在exec
之后执行print(locals())
,则会看到a_func
是在__init__
函数内部创建的.
If you do print(locals())
after the exec
, you'll see that a_func
is created inside the __init__
function.
I don't really know why it's done that way, but seems to be a change on how symbol tables are processed.
顺便说一句,如果要在__init__
方法上创建一个a_func = None
以使解释器知道它是局部变量,则该方法将不起作用,因为现在的字节码将是LOAD_FAST
并且不会进行搜索,但直接从列表中获取值.
BTW, if want to create a a_func = None
on top of your __init__
method to make the interpreter know it's a local variable, it'll not work since the bytecode now will be LOAD_FAST
and that don't make a search but directly gets the value from a list.
我看到的唯一解决方案是将globals()
作为第二个参数添加到exec
,以便将a_func
创建为全局函数,并且可以被LOAD_GLOBAL
操作码访问.
The only solution I see is to add globals()
as second argument to exec
, so that will create a_func
as a global function an may be accessed by the LOAD_GLOBAL
opcode.
修改
如果删除exec
语句,Python2.7将字节码从LOAD_NAME
更改为LOAD_GLOBAL
.因此,使用exec
,您的代码在Python2.x上总是会变慢,因为它必须在本地范围内搜索更改.
If you remove the exec
statement, Python2.7 change the bytecode from LOAD_NAME
to LOAD_GLOBAL
. So, using exec
, your code will always be slower on Python2.x because it has to search the local scope for changes.
由于Python3的exec
不是关键字,因此解释器无法确定它是在真正执行新代码还是在执行其他操作……因此字节码不会更改.
As Python3's exec
is not a keyword, the interpreter can't be sure if it's really executing new code or doing something else... So the bytecode don't change.
例如
>>> exec = len
>>> exec([1,2,3])
3
tl;博士
exec('...', globals())
可能会解决问题
这篇关于为什么对exec进行Python 3更改会破坏此代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!