之前在讨论闭包的时候有提到:

      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关键字用来在函数或其他作用域中使用外层(非全局)变量。