之前在讨论闭包的时候有提到:
Python会按LEGB的顺序来搜索变量:
要说明的是,这里的访问规则只对普通变量有效, 对象属性的规则与这无关(简单地说,访问一个对象的属性与此无关)。
L. Local. 局部作用域,即函数中定义的变量(没有用global声明)
E. Enclosing. 嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,比如上面的示例中的labmda所访问的x就在其父级函数test的局部作用域里。通常也叫non-local作用域。
G. Global(module). 在模块级别定义的全局变量(如果需要在函数内修改它,需要用global声明)
B. Built-in. built-in模块里面的变量,比如int, Exception等等
但此规则有一个重要的限制:
一个不在局部作用域里的变量默认是只读的,如果试图为其绑定一个新的值, Python认为是在当前的局部作用域里创建一个新的变量
如果确实要在一个函数里修改全局变量,Python提供了global关键字来声明一个变量是全局变量,声明以后就可以修改其值了。 然而global只能用来修改全局作用域里的变量,对于嵌套函数的情况无能为力,所以计数器的例子在Python 2.x中是无法实现的。
然而在Python 3中,一个新的关键字nonlocal的产生解决了这个问题
计数器:
def make_counter(): count = 0 def counter(): nonlocal count count += 1 return count return counter def make_counter_test(): mc = make_counter() print(mc()) print(mc()) print(mc())
也可以使用generator来实现类似的计数器
def counter_generator(): count = 0 while True: count += 1 yield count def counter_generator_test(): # below is for python 3.x and works well citer = counter_generator().__iter__() i = 0 while(i < 3) : print(citer.__next__()) i+=1
而今天在segmentfault上看到这个问题时却没有及时反应过来:
问题:
这段代码不用在函数中声明global x
就可以打印出x的值
x = 20 def getx(): print x getx()
那请问在哪些情况下必须要使用global
声明全局变量?
以下是一个多线程的python代码片段,其中的x,l
都是全局变量,但在threadcode()
函数中只声明了global x
没有global l
。完整的代码是可以成功运行,但是把global x
注释掉后就会报错。请问这是为什么,Lock对象比较特殊吗?
import threading, time, sys x = 50 l = threading.Lock() def threadcode(): global x l.acquire() print 'Thread %s invoked.' % threading.currentThread().getName() try: print 'Thread %s running.' % threading.currentThread().getName() x = x + 50 print 'Thread %s set x to %d.' % \ (threading.currentThread().getName(), x) finally: l.release() ...
解答:
对于Python2而言,对于一个全局变量,你的函数里如果只使用到了它的值,而没有对其赋值(指a = XXX
这种写法)的话,就不需要声明global。相反,如果你对其赋了值的话,那么你就需要声明global
。声明global
的话,就表示你是在向一个全局变量赋值,而不是在向一个局部变量赋值。
global关键字用来在函数或其他局部作用域中使用全局变量。但是如果不修改全局变量也可以不使用global关键字。
nonlocal关键字用来在函数或其他作用域中使用外层(非全局)变量。