您的位置:首页 > 编程语言 > Python开发

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

2017-07-15 00:29 99 查看
首先我们来做一个小实验:

>>>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()时,想一想其他的可选方案,就像讨论一开始列出的类似选项
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐