之前在讨论闭包的时候有提到:
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关键字用来在函数或其他作用域中使用外层(非全局)变量。