从文章《python31[变量的作用域+global]》想到的
这篇讲的比较繁琐,要看的朋友请耐心,由于篇幅所限不能列出所有测试的例子代码,最好是大家自己试验一下各种情况,动动手就会发现很多东西了。
Tech文章http://www.cnblogs.com/itech/archive/2011/01/27/1945501.html中有讲变量与作用域的问题,有一点(我用的是Python2.7,但这些东西在2和3里都没啥大变化)我稍微补充一下,:
如果函数中定义了同名的变量,则同名的全局变量被屏蔽,否则查找使用全局变量
def f1(): v1 = 'local' f2() print (v1) def f2(): print(v1) v1 = "Value1" f1() #Value1 #local
iTech已经讲的很清楚了,局部v1屏蔽了全局v1,所以f1中print出的是局部v1的值,f2中仍然是全局值,f1定义的v1作用域不会达到f2中。如果我们把顺序变一下看看会怎样:
def f1(): print(v1) f2() v1 = "local" print(v1) def f2(): print(v1) v1 = "Value1" f1()
运行的结果是:
File "D:\workplace\python\test\test.py", line 2, in f1
print(v1)
UnboundLocalError: local variable 'v1' referenced before assignment
按上面说法,预想到的f1()中第一个print(v1)打印出“local”的结果并未出现,我们下一步想到的是即使不打印局部也应该去寻找全局才对,但是结果并非如此。这是因为局部的v1已经屏蔽了全局的v1,但是它并未出现在使用之前(这一点和C++等还是一致的),所以python直接异常终止了。
如果我们不想在使用时要考虑这么多的麻烦,比如如果一个函数很长,我们在给一个变量赋值的时候不想去考虑是不是这个变量已经在函数体前部分使用到了,以避免出现上面说的屏蔽情况的出现,那么应该怎么办呢,就像iTech的最后一个例子使用global即可:
def f6(): global v6 print(v6) v6 = "local" print(v6) v6 = "global" f6() print(v6) #global #local #local
但一定要注意,最后一个print(v6)打印的是“local”,也就是说global“引用”了全局变量,局部的修改对全局是有效的!
晕,写完了才发现原来参考文章http://hi.baidu.com/xjtukanif/blog/item/f35000f1642670c00b46e03c.html里讲了差不多这么个情况,不过对于“局部作用域不应对全局变量进行赋值”这个问题并没有讲清楚,下面我讲一下我的理解,首先来看例子:
def scope1_f1(): print global_v global_v.append('local') print global_v global_v = ['global'] scope1_f1() def scope1_f2(): print global_v2 global_v2 = 'local' print global_v2 global_v2 = 'global' scope1_f2()
通过上面的分析,我们很容易的就可以看出scopel_f2()函数已经错了,但是scopel_f1()却是对的,这是为什么?我们再看:
def scope1_f1(): print global_v#请特别注意这句的存在 global_v.append('local') print global_v global_v = ['global'] scope1_f1() print global_v def scope1_f2(): global_v2 = 'local' print global_v2 global_v2 = 'global' scope1_f2() print global_v2 #['global'] #['global', 'local'] #['global', 'local'] #local #global
则会继续出现错误,即不允许赋值,可以这样理解append()时并没有产生新对象(还是引用,通过在函数外打印已经可以看出,局部修改确实起作用了),所以也就不存在覆盖不覆盖的问题了,scopel_f1的第一个print的还是全局,而=产生的是新对象,所以覆盖了全局变量,第一个print的是局部变量,但是局部的却是在之后声明初始化的,故出现UnboundLocalError错误。
啰嗦了这么久,总结一下:
复制与引用问题,这是理解最后一个例子的基础,建议搞清楚。对于不可变的类型,比如数字或字符串,=也相当于是deepcopy了(这句话还是来自iTech,哈哈),可以认为是复制,而不是引用了。
尽量pythonic,一定的编码习惯还是要注意的,比如上面说的局部覆盖全局的问题,如果能养成很好的习惯就很容易避免,自己理解起来也容易,使用了全局后不要随便执行赋值操作(习惯java的同学可能会比较难适应),新建变量时注意是不是有重名的全局变量等等,总的说来学习一门语言时还是把这些最基本的问题搞清楚了免得出现错误时自己都感到莫名其妙。