Python的闭包:是一种定义在某个作用域D
中的函数F
,这种函数F
引用了作用域D
里面的变量。
案例1:将values
列表中的数字进行排序,同时位于group
中的数字放在不属于group
中元素之前。
def sort_priority(values,group):
def helper(x):
if x in group: #引用了上层作用域中的变量
return (0,x)
return (1,x)
values.sort(key=helper)
- Python的函数属于一级对象,所以可以直接引用函数、把函数赋给变量、把函数当作参数传给其他函数,在上述例子中,将
helper
闭包函数传给了sort
方法的key
参数。 - Python在对两个元组进行排序时,首先比较位置0处的元素大小,如果相等,再比较位置1处的元素大小,如果还是相等,则比较位置2处的元素,依次类推。
案例2:在案例1的基础上,返回values
中是否出现了在group
中的元素标志位found
。
def sort_priority(values,group):
found = False
def helper(x):
if x in group:
found = True
return (0,x)
return (1,x)
values.sort(key=helper)
return found
if __name__ == "__main__":
values = [1,2,4,9,92,100]
group = [1,2,100]
found = sort_priority(values,group)
print(values)
print(found)
输出结果:
[1, 2, 100, 4, 9, 92]
False
可以看出,排序的结果是对的,found
的返回值应该是True
,但却是False
。
这就需要考虑Python在引用变量和赋值时的规则。
1.引用变量时,Python解释器遍历作用域的顺序:
- 当前函数的作用域
- 任何外围作用域(例如,包含当前函数的其他函数)
- 包含当前代码的模块的作用域(也叫全局作用域,
global scope
) - 内置作用域(也就是包含
len
和str
等函数的作用域)
2.给变量赋值时,规则则有所不同。
如果当前作用域已经定义了这个变量,那么该变量就会具备新值。若是当前作用于内没有这个变量,Python则会把这次赋值视为对该变量的定义,使其称为局部变量。
可以看出,案例2中的helper
函数中的found = True
,被视为创建了helper
函数作用域内的局部变量,而并非外部变量(包含helper
函数的sort_priotity
)found
。
为了避免这种情况的存在,可以采用nonlocal
关键字,在helper
函数中指定found
变量,这样Python会在上层作用域中查找该变量。
def sort_priority(values,group):
found = False
def helper(x):
if x in group:
nonlocal found ###
found = True
return (0,x)
return (1,x)
values.sort(key=helper)
return found
if __name__ == "__main__":
values = [1,2,4,9,92,100]
group = [1,2,100]
found = sort_priority(values,group)
print(values)
print(found)
nonlocal
语句清楚的表明:如果在闭包内给该变量赋值,那么修改的是闭包外的那个作用域中的变量。这与global
语句互为补充,global
用来表示修改模块作用域中的变量。
主要区别:
global
修改全局变量(模块作用域中的变量)nonlocal
修改闭包作用域外的变量
注意点:除了简单的函数外,尽量不要用nonlocal
语句,否则遭到滥用,很难追踪代码异常。
如果使用nonlocal
的代码,已经写的越来越复杂了, 可以考虑将相关的函数封装成辅助类。
class Sorter(object):
def __init__(self,group):
super().__init__()
self.group = group
self.found = False
def __call__(self,x):
if x in self.group:
self.found = True
return (0,x)
return (1,x)
if __name__ == "__main__":
values = [1,2,4,9,92,100]
group = [1,2,100]
sorter = Sorter(group)
values.sort(key=sorter)
print(sorter.found)