第15条 闭包内使用外围作用域的变量

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
  • 内置作用域(也就是包含lenstr等函数的作用域)

2.给变量赋值时,规则则有所不同。

如果当前作用域已经定义了这个变量,那么该变量就会具备新值。若是当前作用于内没有这个变量,Python则会把这次赋值视为对该变量的定义,使其称为局部变量。

可以看出,案例2中的helper函数中的found = True,被视为创建了helper函数作用域内的局部变量,而并非外部变量(包含helper函数的sort_priotityfound

为了避免这种情况的存在,可以采用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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值