为什么对exec进行Python 3更改会破坏此代码? [英] Why did Python 3 changes to exec break this code?

查看:85
本文介绍了为什么对exec进行Python 3更改会破坏此代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我浏览了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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆