python编程之执行带有局部副作用的代码

来源:互联网 发布:广联达梦龙网络计划 编辑:程序博客网 时间:2024/05/16 07:46

首先我们来做一个小实验:

>>>a=3>>>exec('b = a +1')>>>print(b)>>>14

然后我们在函数体内做同样的实验:

>>>def test():...    a = 13   ...    exec('b = a + 1')...    print(b)...    

运行结果却出现了错误:
NameError: global name ‘b’ is not defined
要解决此类问题,需要使用locals()函数在调用exec()之前获取一个保存了局部变量的字典,紧接着,就可以从本地字典中提取出修改过的值。示例如下:

def test():    a = 13    loc = locals()    exec('b = a + 1')    b = loc['b']    print(b)

运行这个函数之后结果为正确的 14

下面我们来进行一番讨论
要一般编程过程中要正确使用exec()其实是非常具有技巧性的,事实上,大多数考虑使用exec()的情况下,可能存在更加优雅的解决方案(例如装饰器、闭包、元类等)。
如果必须要使用exec(),那么可以参考以下原则:
默认情况下,exec()在调用方的局部或者全局作用域中执行代码。然而在函数内部,传递给exec()的局部作用是一个字典,而这个字典是实际局部变量的一份拷贝。因此,如果exec()中执行的代码对全局变量做出了任何修改,这个修改绝不会反应到实际的局部变量中去。以下示例将作出解释:

def test1():    a = 0    exec('a += 1')    print(a)

执行此函数结果为 0 ,可见当调用locals()来获取局部变量时,传递给exec()的是局部变量的拷贝。而在exec()执行完毕之后,通过检查字典的值,就能获取到修改过的变量值。下例可验证这一点:

def test2():    a = 0    loc = locals()    print('before:',loc)    exec('a += 1')    print('after:',loc)    #a = loc['a']    print(a)

执行结果如下:

    before: {'a': 0}    after: {'a': 1, 'loc': {...}}    0

观察最后一步的输出可以得知,除非从loc中将修改过的值写回x,否则变量x会保持不变。
每次使用locals()时都要小心操作的顺序问题。每次调用时,locals()将会接受局部变量的当前值,然后覆盖字典中的对应条目。请看以下示例:

def test3():    a = 0    loc = locals()    print(loc)    exec('a += 1')    print(loc)    locals()    print(loc)

执行结果如下

{'a': 0}{'loc': {...}, 'a': 1}{'loc': {...}, 'a': 0}

注意最后对locals()的调用是如何导致x被覆盖的。
除了使用locals()之外,另一种可选的方式是自己创建字典并传递给exec()。示例如下:

def test4():    a = 13    loc = {'a':a}    glb={ }    exec('b = a + 1',glb,loc)    b = loc['b']    print(b)

最后的运行结果为14
对于大部分针对exec()的应用,这就够了。我们需要确保exec()中访问的变量在全局和局部字典中经过你恰当的初始化。
最后提示:
在使用exec()时,想一想其他的可选方案,就像讨论一开始列出的类似选项

原创粉丝点击