Python闭包-创新互联

闭包

如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)

创新互联公司主营开阳网站建设的网络公司,主营网站建设方案,app开发定制,开阳h5小程序开发搭建,开阳网站营销推广欢迎开阳等地区企业咨询

这里是一个闭包的例子:

def addx(x):
    def adder(y):
        return x + y
    return adder

if __name__ == '__main__':
    func = addx(10)
    print(func(1))
    print(func(2))
    print(func(3))

执行结果:

11
12
13

这里例子里,adder(y) 就是一个内部函数。它里面引用了外部的变量x,x是外部作用域addx(x)里的变量,但是不是全局变量。所以内部的adder(y)是一个闭包。
精炼一些:闭包=函数块+定义函数时的环境。adder就是函数块,x就是环境。

闭包的注意事项

作用域的问题

这个例子里应该是一个函数的作用域的问题,和闭包没太大关系:

def foo():
    x = 0
    def f():
        x = 1
        return x
    print(x)  # 0
    print(f())  # 1
    print(x)  # 0

if __name__ == '__main__':
    foo()

内部函数和外部函数都定义了变量x。这里内部没有引用外部的变量x,还是生成了一个自己的局部变量,也叫x,这个变量还外部的函数的x变量是没有关系的。所以这里的问题只是一个作用域的问题。

操作外部变量

下面的这个函数是有问题的,语法有错误:

def squares():
    x = 0

    def f():
        x = x + 1
        return x * x
    return f

看似符合闭包的要求,但是标量x出现在了赋值符号 '=' 的左边。python规则指定所有在赋值语句左面的变量都是局部变量。这里因为x被认为是局部变量,然后再执行x+1的时候就只会在局部里找这个x的值,但是找不到,所以就报错了,错误信息如下:

UnboundLocalError: local variable 'x' referenced before assignment

解决方案1
避免直接引用外部,如果引用的是外部的列表、字典等。那么变量名就不会直接出现在赋值符号左边了:

def squares():
    x = [0]

    def f():
        x[0] = x[0] + 1
        return x[0] * x[0]
    return f

如果直接要引用的是外部的列表、字典这类变量,用就不会遇到这类问题。但是这里例子里,这么做感觉也不好

解决方案2
如果要引用的外部变量就是一个简单的数值或者字符串,虽然上面的方法可行,但是还有更好的做法。
使用 nonlocal 声明,把内层的局部变量设置成外层局部可用,但是还不是全局的。类似声明全局变量的 global 的用法。这里主要是因为python里不需要像其他语言里,有类型的var之类的关键字来声明变量。变量直接赋值就完成了声明,平时用起来很方便,但是在这里,因为在操作的时候变量直接出现在赋值符号左边了,就会被认为新定义了一个局部变量了。
完整的示例:

def squares():
    x = 0

    def f():
        nonlocal x
        x = x + 1
        return x * x
    return f

if __name__ == '__main__':
    func = squares()
    print(func())
    print(func())
    print(func())

闭包的作用

闭包主要是在函数式开发过程中使用。下面介绍的两种使用场景,用面向对象也是可以很简单的实现的。但是在用Python进行函数式编程时,闭包对数据的持久化以及按配置产生不同的功能,是很有帮助的。

让函数还可以拥有状态

当闭包执行完后,仍然能够保持住当前的运行环境。上面也提过了:闭包=函数块+定义函数时的环境。这个环境可以在闭包里改变,并且保持下去。
下面是一个类似移动棋子的例子。先在坐标0,0创建棋子。然后可以用内部函数移动棋子。移动后,棋子的状态也更新了,下次再移动棋子,就是在之前的位置的基础上再进行的移动:

def create_piece():
    x = 0
    y = 0

    def move(offset_x=0, offset_y=0):
        nonlocal x, y
        x += offset_x
        y += offset_y
        return x, y
    return move

if __name__ == '__main__':
    player = create_piece()  # 这里是不是和面向对象里的使用前,生成对象的实例很像?
    print(player())  # 打印当前坐标
    player(1, 1)  # 移动棋子
    print(player())  # 打印当前坐标
    print(player(1, 3))  # 再移动棋子并打印坐标

配置函数的参数

闭包可以根据外部作用域的局部变量来得到不同的结果,这有点像一种类似配置功能的作用,我们可以修改外部的变量,闭包根据这个变量展现出不同的功能。
下面的例子,统计字符串里,某个特定的字符出现了多少次。要统计哪个字符,就生成一个对应的闭包,生成的时候把参数传入:

def count_letter(a):
    def count(s):
        x = 0
        for i in s:
            if i == a:
                x += 1
        return x
    return count

if __name__ == '__main__':
    s = 'Hello World !!!'
    count_l = count_letter('l')
    count_space = count_letter(' ')
    print(count_l(s))
    print(count_space(s))

这个用处也是可以用面向对象方便的解决的。相当于生成实例的时候给构造函数传入不同的值。

总结

面向对象里把闭包的这点作用都覆盖了,所以貌似不会也没什么。主要是为了支持函数式编程使用的。

另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。

标题名称:Python闭包-创新互联
分享地址:https://www.cdcxhl.com/article18/phogp.html

成都网站建设公司_创新互联,为您提供网站设计服务器托管网站制作网页设计公司外贸建站自适应网站

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

手机网站建设