issue-15 了解闭包里如何使用外围作用域里的变量

来源:互联网 发布:淘宝小样四大靠谱店 编辑:程序博客网 时间:2024/06/05 15:14

先来看一个例子,有一个列表,我们想要对它排序,但是呢,如果这个列表里的元素在另一个列表里,那么,这个元素有优先排序权,我们来写一个程序:

# -*- coding: utf-8 -*-

numbers = [3,6,1,2,4,9,7,8]group = [4,2,7]def sort_priority(values,group):    def helper(x):        if x in group:            return (0,x)        else:            return (1,x)    values.sort(key=helper)    return valuesprint(sort_priority(numbers,group))
输出:
[2, 4, 7, 1, 3, 6, 8, 9]
我们一起来看下这个程序,首先先了解sort函数,对一个列表排序,直接list.sort(),如果想用自己定义的函数来排序,就是list.sort(key=func),所以这里的func就是helper函数,
这个函数的输入变量就是这个list中的每一个元素,执行完以后就会返回一个数组(0,x),对数组的排序,先看第一个元素,所以(1,6)是大于(0,7)的,这样就可以把在
第二个列表中的元素优先排序。
所以这个程序里的helper函数,就是闭包,闭包就是一种定义在某个作用域中的函数,这个函数引用了这个作用域中的变量,这个helper函数就是用了这个numbers列表中的变量
好的,我们继续看下假如我们设置一个flag,就是如果找到了这个元素在另一个列表里,我们就更改这个flag,否则不改,这样我们就知道到底最后有没有这种元素。
# -*- coding: utf-8 -*-numbers = [3,6,1,2,4,9,7,8]group = [4,2,7]def sort_priority(values,group):    found = False    def helper(x):        if x in group:            found = True            return (0,x)        else:            return (1,x)    values.sort(key=helper)    return values, foundprint(sort_priority(numbers,group))

输出:
([2, 4, 7, 1, 3, 6, 8, 9], False)
这里就奇怪了,我们找到了啊,为啥这里found还是false呢,这里就是涉及到了闭包的作用域问题,当表达式中引用变量时,Python解释器会按照下面的顺序来遍历各个作用域,
以解析这个引用。
1.当前函数的作用域
2.任何外围作用域
3.包含当前代码的模块的作用域
4.内置作用域,就是那些内置函数(比如str,len,sort等)的作用域
如果这些都没有找到,就会抛出异常,nameerror。
给变量赋值时,规则有点不同,如果当前作用域已经定义了这个变量,那么这个变量就会被重新赋值,如果没有定义,那么就会认为这次赋值时一次定义加赋值操作,而新定义
的变量,其作用域就是当前的这个函数。
所以当我们在helper函数里进行一个赋值操作时,found=True,就相当于冲洗定义了一个found变量,并给他赋值,而不是在给外围作用域的found变量赋值,这个是Python故意这样设计
的,这样做可以防止函数中的局部变量污染函数外部的作用域和模块,如果不这样做,那么函数里的每一个赋值操作,都会影响外部的全局作用域,会混乱的。
那么我们如何才可以获取外部的变量呢?这里我们可以加一个声明就可以了,nonlocal found
# -*- coding: utf-8 -*-numbers = [3,6,1,2,4,9,7,8]group = [4,2,7]def sort_priority(values,group):    found = False    def helper(x):        if x in group:            nonlocal found            found = True            return (0,x)        else:            return (1,x)    values.sort(key=helper)    return values, foundprint(sort_priority(numbers,group))

输出
([2, 4, 7, 1, 3, 6, 8, 9], True)


nonlocal语句清楚的声明,如果你要用到这个变量,你要知道这个变量时外围作用域中的,修改的就是外围作用域的变量。 但是nonlocal 很容易想全局变量那样遭到乱用,所以还是建议不要在复杂的函数里用,你想想,如果声明和引用相距比较远,那么你可能意识不到引用的是哪里的变量,调试会很麻烦。


总结一下:

对于定义在某个作用域中的函数,就叫闭包,它只可以引用这个作用域中的变量

使用默认赋值操作时,在闭包里的话,不会影响外围作用域中的变量

要使用外围作用域的变量,在Python 3 中可以用nonlocal声明一下,但是记住,我们只在简单的函数里这样使用。


原创粉丝点击