• 浅谈python闭包


    1.何为闭包

    在百度百科里面,看到了这样的定义:

    闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

    闭包包含自由(未绑定到特定对象)变量,这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)。

    从上面两段话我们可以更好的理解:在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。

    下面是一个闭包的小例子:

    def outer():
        a = 6
        def inner():
            b = 8
            print(a)
            print(b)
        return inner
    
    if __name__ == '__main__':
        res = outer()
        res()
    
    执行结果:
    
    >>>6
    >>>8

    我们可以看到,当调用外部函数outer()时,会返回一个内部函数的引用res,再通过res()执行了内部函数,返回打印结果6和8

    这上面的例子中,a作为外部函数outer()的局部变量,其被分配的内存在外部函数执行后应被释放,但在外部函数执行后,发现自己的局部变量将被内部函数引用,就把这个变量绑定给了内部函数,然后再自己结束。此处的outer()的局部变量a称之为自由变量,因此,对于内部函数inner()而言,其自由变量和局部变量我们可以通过下面的命令得到:

    >>> res.__code__.co_freevars
    ('a',)
    >>> res.__code__.co_varnames
    ('b',)

    上面我们说到,外部函数的局部变量a被绑定到了内部函数,我们可以通过返回函数res的__closure__属性找到它

    >>> res.__closure__[0].cell_contents
    6

    2.闭包的变量

    def outer():
        a = 6
        def inner():
            b = 8
            a += 1
            print(a)
            print(b)
        return inner
    
    if __name__ == '__main__':
        res = outer()
        res()
    
    执行函数会报错:UnboundLocalError: local variable 'a' referenced before assignment

    这个报错的意思是内部函数引用的变量a在赋值前已经被引用,原因在于变量a是数字,不可变类型,a += 1相当于在内部函数中创建了局部变量a = a + 1,这样a不再是自由变量,也不存在闭包。

    这里我们可以尝试下外部函数的局部变量为可变类型的情况:

    def outer():
        a = [1,2,3]
        def inner():
            b = 8
            a = [4,5]
            print(a)
            print(b)
        return inner
    
    if __name__ == '__main__':
        res = outer()
        res()
    
    执行结果:
    [4, 5]
    8

    可以看到,当a为可变类型时,函数可以正确执行。那么当a为不可变类型但又想在内部函数中改变a的值该怎么做,答案是利用nonlocal关键字。

    def outer():
        a = 6
        def inner():
            nonlocal a
            b = 8
            a += 1
            print(a)
            print(b)
        return inner
    
    if __name__ == '__main__':
        res = outer()
        res()
    
    执行结果:
    7
    8

    3.闭包的应用

    下面模拟一个nba球员信息的小例子

    首先用普通函数实现:

    class PlayerInfo():
        def __init__(self, name):
            self.name = name
        def position(self, position):
            return "{} is {}".format(self.name, position)
    
    curry = PlayerInfo("Stephen Curry")
    print(curry.position("PG"))
    lbj = PlayerInfo("Lebron James")
    print(lbj.position("SF"))
    
    执行后:
    
    Stephen Curry is PG
    Lebron James is SF

    下面用闭包函数来实现

    def get_name(name):
        def get_position(position):
            return "{} is {}".format(name,position)
        return get_position
    
    if __name__ == "__main__":
        player01 = get_name("Stephen Curry")
        print(player01("PG"))
        player02 = get_name("Lebron James")
        print(player02("SF"))

    因此我们可以看出,对于实现少量方法的类,我们可以用闭包代替。

  • 相关阅读:
    Form 中调用指定请求并给定默认参数
    OAF 汇总行的做法
    EBS 开发常用SQL
    EBS 中常用的配置文件及说明
    OAF 常见概念介绍
    OAF 多语言的实现
    OAF 个性化基础
    OAF 开发前置配置
    条款20 STL函数对象
    条款19 command 模式与好莱坞法则
  • 原文地址:https://www.cnblogs.com/m-chen/p/10130804.html
Copyright © 2020-2023  润新知