zoukankan      html  css  js  c++  java
  • 面试题-python 什么是闭包(closure)?

    前言

    前面学了装饰器,那么闭包和装饰器有什么区别呢?
    闭包传递的是变量,而装饰器传递的是函数对象,只是传的参数内容不一样,闭包的概念包含了装饰器,可以说装饰器是闭包的一种,它只是传递函数对象的闭包。

    先看一个面试题

    先看一个经典的面试题,很有代表性, 运行以下代码会输出什么呢?为什么会是这种结果?

    def fun():
        temp = [lambda x: i*x for i in range(4)]
        return temp
    
    for everyLambda in fun():
        print(everyLambda(2))
    

    运行结果

    6
    6
    6
    6
    

    运行的结果是4个6 ,并不是我们想的 :0, 2, 4, 6。上面的代码用到了列表推导式,还有个匿名函数lambda,直接去阅读不太好理解,可以把匿名函数转成自己定义一个函数.
    于是上面的代码等价于:

    """
    # 作者-上海悠悠 QQ交流群:717225969
    # blog地址 https://www.cnblogs.com/yoyoketang/
    def fun():
        temp = [lambda x: i*x for i in range(4)]
        return temp
    
    for everyLambda in fun():
        print(everyLambda(2))
    """
    
    
    def fun():
        temp = []
        for i in range(4):
            def inner(x):
                return i*x
            temp.append(inner)
        return temp
    
    for everyLambda in fun():
        print(everyLambda(2))
    

    为了更好的理解,可以先去掉外面的一层fun()

    temp = []
    for i in range(4):
        def inner(x):
            return i*x
        temp.append(inner)
    
    for everyLambda in temp:
        print(everyLambda(2))
    

    这里只定义了一个函数 inner(), 有 2 个变量,i 是函数外部的变量,x 是函数内部的变量。
    现在问题的关键在理解函数外部变量和函数内部变量的区别了, 接下来再看一个简单的例子

    # 作者-上海悠悠 QQ交流群:717225969
    # blog地址 https://www.cnblogs.com/yoyoketang/
    
    
    a = 1
    
    
    def myfunc(b):
        return a+b
    
    print(myfunc(100))
    a = 2
    a = 3
    print(myfunc(100))
    print(myfunc(100))
    

    运行结果:101 103 103
    也就是函数外部变量a是可变的,后面给a重新赋值了,会替换前面的值。上面的 inner(x) 函数也是一样,外部变量 i 的值是0, 1, 2, 3变化,最后的3 会覆盖前面的值,所以得到的结果都是6
    如何解决上面的问题,接下来就是要说的闭包的概念了!

    什么是闭包?

    闭包就是外部函数中定义了一个内部函数,当外部函数返回内部函数对象(注意是函数对象)时,程序接收了内部函数的定义(此时并未被执行),当再次执行这个返回值时,这个被返回的函数才能被执行。
    创建一个闭包必须满足以下几点:

    • 必须有一个内嵌函数
    • 内嵌函数必须引用外部函数中的变量
    • 外部函数的返回值必须是内嵌函数

    闭包和装饰器的区别:闭包传递的是变量,而装饰器传递的是函数,除此之外没有任何区别,或者说装饰器是闭包的一种,它只是传递函数的闭包。
    以下是闭包的一个标准示例:

    # 作者-上海悠悠 QQ交流群:717225969
    # blog地址 https://www.cnblogs.com/yoyoketang/
    
    
    def outer(age):
        def inner(name):
            print("my name is %s. my age is %s." % (name, age))
        return inner
    
    demo = outer("18")
    demo("yoyo")
    # 运行结果:my name is yoyo. my age is 18.
    

    上面的问题,用闭包来解决

    # 作者-上海悠悠 QQ交流群:717225969
    # blog地址 https://www.cnblogs.com/yoyoketang/
    
    
    # 闭包
    def temp(i):      # 一个外层函数
        def inner(x):    # 内层函数
            return i*x
        return inner
    
    a = []
    for i in range(4):
        a.append(temp(i))
    print(a)
    
    for j in a:
        print(j(2))
    

    运行结果

    [<function temp.<locals>.inner at 0x000002A8EE929AE8>, 
    <function temp.<locals>.inner at 0x000002A8EE929B70>, 
    <function temp.<locals>.inner at 0x000002A8EE929BF8>, 
    <function temp.<locals>.inner at 0x000002A8EE929C80>]
    0
    2
    4
    6
    

    使用列表推导式

    # 闭包
    def temp(i):      # 一个外层函数
        def inner(x):    # 内层函数
            return i*x
        return inner
    
    
    def fun():
        temps = [temp(i) for i in range(4)]
        return temps
    
    for everyLambda in fun():
        print(everyLambda(2))
    

    这样就可以得到我们的预期结果:0 2 4 6

    通过上面的案例就可以了解到闭包的作用了,它保存了函数的外部变量,不会随着变量的改变而改变了。

  • 相关阅读:
    Ui——创建视图的方法及过程
    iOS设计模式----原型模式
    浅谈OC中排序的方法
    Solid Dominoes Tilings (轮廓线dp打表 + 容器)
    Shell Necklace (dp递推改cdq分治 + fft)
    Rigid Frameworks (画图二分图规律 + DP + 数学组合容斥)
    PowMod (欧拉推式子 + 指数循环节)
    Necklace (全排列 + 匈牙利)
    GCD (RMQ + 二分)
    Game (思维)
  • 原文地址:https://www.cnblogs.com/yoyoketang/p/14479432.html
Copyright © 2011-2022 走看看