python的闭包

*从作用域的概念开始:

全局作用域:整个程序运行环境中都可见。

局部作用:函数、类内部可见。

两者特点:

1.局部变量使用范围不能超过其所在的局部作用域。

2.外层变量作用域在内层作用域可见。在内层作用域定义一个同名的局部变量时,相当于在当前作用域重新定义一个新的变量,但不会覆盖外层作用域的同名变量。举例如下:

 

x = 5
def foo():
    y = x + 1
    print(y)

foo()

 输出的结果是6;因为在foo()函数内引用到外部作用域的x变量。

 

x = 5
def foo():
    x += 1
    print(x)

foo()

这时候会报错:local variable 'x' referenced before assignment。为什么呢?因为x += 1等价于 x = x + 1,相当于在foo()函数内部重新定义一个局部变量x,那么foo()函数内部所有x都是这个局部变量x,但这个x还没有完成赋值,就被进行加1操作。

(python动态语言中赋值才算定义,才能被引用。这里x没有赋值就无法被引用)

 

解决办法:

x = 5
def foo():
    global x
    x += 1
    print(x)

foo()

 global将foo内的x声明为使用外部的全局作用域中定义的x。此时就会成功输出6。那么如果foo外部没有x变量的声明,是否还会报错呢?

def foo():
    global x
    x = 10
    x += 1
    print(x)

foo()

 此时会成功输出11。说明:一旦作用域中使用global关键字声明为全局的,那么x=10相当于在为全局作用域的变量x赋值。

 

*关键字global

1.使用global关键字的变量,将会被声明为外部的全局作用域中定义的变量

2.外部作用域的变量会在内部作用域中可见,但是也不要在内部的局部作用域中直接使用,因为函数的目的是为了封装,尽量与外界隔离。

3.如果函数需要使用外部全局变量,使用函数的形参传参解决。

 

 

 

 

*闭包概念

自由变量:未在本地作用域(非全局作用域!)中定义的变量。例如:定义在内层函数外的外层函数的作用域中的变量。

闭包:内层函数引用到外层函数的自由变量。(最常见的就是JavaScript)

 

 python2中实现闭包的举例如下:

def counter():
    c = [0]
    def inc():
        c[0] += 1
        return c[0]
       return inc

foo = counter()
print(foo(), foo()) # 打印1 2

c = 100
print(foo()) # 打印3

 此时c[0] += 1并不会像上面的x += 1情况报错。为什么呢?因为c已经在外层函数counter()里定义过,内层函数inc()里并不是对c的重新定义,而是对列表c内的元素进行修改。

c=100中的c和counter()中的c不一样,inc()引用的是自由变量,正是counter()中的变量c。

 

上面的例子是针对dict,list这些可变对象的闭包,但是对于int,string,float,tuple这些python中不可变对象变量的闭包,python3里可以使用nonlocal实现。

nonlocal:使用nonlocal关键字,将变量标记为上级的局部作用域中定义,但不能是全局作用域中定义的变量。

def counter():
    count = 0
    def inc():
        nonlocal count
        count += 1
        return count
    return inc

foo = counter()
print(foo())  # 1
print(foo()) # 2

 该例子就是一个正常使用的闭包。

 

a = 10
def counter():
    nonlocal a
    a += 1
    print(a)

    count = 0
    def inc():
        nonlocal count
        count += 1
        return count

    return inc

foo = counter()
foo()
foo()    

nonlocal a中a的上一级作用域是全局作用域,所以代码不能正常运行。

 

posted @ 2019-03-01 20:23  DoubleFishes  阅读(116)  评论(0编辑  收藏  举报