Python-闭包

作者: NJingZYuan | 来源:发表于2019-07-31 14:16 被阅读2次

0. 函数相关知识

1)Python中“一切皆对象”,函数也不例外

先定义一个函数:

def func():

    print('我是函数:{}'.format(func.__name__))  # __name__属性返回函数的名称

打印函数名:

>>> print(func)

<function func at 0x00000209EA722E18>

返回的是函数对象func,并指出了它的内存地址。

对函数使用type():

>>> type(func)

<class 'function'>

以上可以看出 函数 func 是 function类型的一个对象,属于可调用对象(具有__call__魔法方法)。

2)函数可以赋值给一个变量

函数作为一个对象,它可以作为其他函数的参数,也可以作为函数的返回值,还能够赋值给一个变量:

>>> f = func     

>>> f()          # 可调用对象后加()即可执行调用

我是函数:func      # func赋值给变量f,f可以作为函数被调用,但函数名仍为func

将函数赋值给变量,是将函数的引用传递给变量。至此,函数名和变量共同指向 function对象func。

3)函数作为函数的返回值

定义一个可变参数的求和函数:

def sum(*args):

    result = 0

    for n in args:

        result += n

    return result

如果此时调用函数,将会立即求和。

再定义一个函数,它能够延迟求和:

def lazy_sum(*args):

    def sum():

      result = 0

      for n in args:

          result += n

      return result

    return sum                # 函数sum作为返回值

调用函数lazy_sum()时,返回的是求和函数sum,而不是求和结果:

>>> f = lazy_sum(1, 2, 3)

>>> f

# f = 函数lazy_sum局部空间中的sum函数

<function lazy_sum.<locals>.sum at 0x000001ECB6C19840>

当调用函数f时,才会真正计算求和的结果:

>>> f()

6

这种嵌套定义函数,具有两个特征:

1)内部定义的函数使用了外部函数的参数或局部变量;

2)外部函数的返回值为内部函数(注意不是返回内部函数的调用);

最后有一点需要注意,每次调用lazy_sum(),都会返回一个新的函数,即使传入相同的参数:

>>> f1 = lazy_sum(1, 2, 3)

>>> f1 = lazy_sum(1, 2, 3)

>>> f1 == f2

False    # f1和f2指向的是不同内存地址的sum函数

1.闭包

1)概念:

函数嵌套定义的前提下,如果内部函数使用了外部函数的变量,并且外部函数返回值为内部函数,此时我们就把这个内部函数称为“闭包”。

2)特征:

也即闭包的构成条件为:

(1)函数嵌套定义为前提;

(2)内部函数使用了外部函数的变量;

(3)外部函数的返回值为内部函数;

3)原理:

在上面的lazy_sum()函数中:

def lazy_sum(*args):

    def sum():

      result = 0

      for n in args:

          result += n

      return result

    return sum                # 函数sum作为返回值

lazy_sum是外部函数,sum是内部函数,此时的sum就是闭包。

闭包(内部函数)sum引用了外部函数lazy_sum的变量,当lazy_sum返回sum时,其相关参数和变量就保存在了闭包sum中。其中的过程:定义外部函数 --> 操作系统为外部函数分配局部空间 --> 局部空间中定义变量和内部函数 --> 为内部函数开辟嵌套的局部空间 --> 返回内部函数。

过程中存在嵌套的局部空间(Enclosing),对于这个嵌套的局部空间来说,外部函数所在的局部空间就相当于其全局空间,其中的变量也相当于全局变量,因此内部函数可以像调用全局变量一样调用外部函数的变量。而也是由于这个嵌套的局部空间,使得内部函数保存了外部函数的变量。

闭包原理复杂,应用的时候牢记三点就可以:

1)闭包(内部函数)使用了外部函数的变量,并且能够保存外部函数的相关变量;

2)外部函数返回的是闭包函数,不是一个计算结果,该函数不会立即执行;

3)调用外部函数并赋值给变量,则该变量就等于保存了外部函数相关变量的内部函数;

4)应用:两个小伙伴聊天

# 定义闭包

def person(a):

    def inner(b):

      print('{}:{}'.format(a, b))

    return inner

david = person('David')    # 建立一个闭包对象david

jane = person('Jane')      # 建立闭包对象jane

david('天王盖地虎')

jane('宝塔镇河妖')

>>> 运行结果:

David:天王盖地虎

Jane:宝塔镇河妖

可以看到两种现象:1)调用person()时,inner延迟执行;2)inner保存了person的变量a;

5)关键字nonlocal

关键字nonlocal能够实现对闭包外部函数变量的修改操作。nonlocal --> 声明内部函数的变量是非本地的。类似于定义普通函数时的声明全局变量的关键字global。

# 定义闭包:不使用nonlocal

def outer():

    a = 10

    def inner():

        a = 20

        result = a + 1

        print(result)

    inner()

    print(a) 

    return inner

test1 = outer()  # 调用外层函数

test1()      # 调用内层函数

>>> 运行结果:

21

10

21   

#  a的值仍为10,说明内部函数没有修改掉外部函数变量a的值

#  此种情况下内部函数并没有使用外部函数的变量,而是自建了一个新的变量a

# 定义闭包:使用nonlocal

def outer():

    a = 10

    def inner():

        nonlocal a

        a = 20

        result = a + 1

        print(result)

    inner()

    print(a)

    return inner

test1 = outer()  # 调用外层函数

test1()      # 调用内层函数

>>> 运行结果

21

20

21   

# a的值被修改为20 

2.总结

闭包是Python中函数式编程的重要语法结构,属于函数的高阶应用。通过它的原理也能够实现“装饰器”的定义。

3.Enclosing 补充

当定义闭包函数时,外部函数的局部命名空间中会存在“Enclosing作用域”。闭包函数会存在于这个Enclosing作用域中,可以通过闭包函数的__closure__属性调出。Enclosing除了存放闭包函数外,还会自动添加闭包函数用到的外部函数的对象,但仅限自己所用的。

上面闭包应用的例子:

# 定义闭包

def person(a):

    a1 = 10

    def inner(b):

        print(inner.__closure__)      # 打印闭包函数的__closure__属性

        print('{}:{}'.format(a, b))

    return inner

inner.__closure__ = (<cell at 0x000001DC0A6D7E88: str object at 0x000001DC0A768E30>, <cell at 0x000001DC0A6D76A8: function object at 0x000001DC0A799620>)

str object 即是inner用到的参数a;function  object为它自己;而变量a1由于inner不会使用,所以不在其中。

相关文章

  • python-闭包

    函数里面的函数不被执行,返回了内部函数的引用。 ret(200), # 300传当前的值,可以计算 闭包的应用 如...

  • Python-闭包

    0. 函数相关知识 1)Python中“一切皆对象”,函数也不例外 先定义一个函数: def func(): p...

  • Python-闭包

    闭包的理解 我们可以将闭包理解为一种特殊的函数,这种函数由两个函数的嵌套组成,且称之为外函数和内函数,外函数返回值...

  • Python-闭包

    当函数调用完,函数内定义的,但是我们有时候需要,每次在这个变量的基础上完成一系列操作,比如:每次在这个变量的基础上...

  • python-闭包和装饰器

    https://www.zhihu.com/question/25950466/answer/31731502 代...

  • Python-闭包和修饰器

    作用域 闭包 code: 装饰器 code: 装饰器案例 code:

  • swift-闭包

    闭包 闭包定义 闭包简化 - 尾随闭包 闭包参数 闭包返回值 闭包的循环引用

  • 闭包,闭包,闭包

    1、这家伙到底是什么? 网上关于这个的讨论的太多了太多了,有各种的举例子,但是大部分还在寻找这个答案的小伙伴对于变...

  • 闭包-Closures [swift 5.1]

    闭包的语法 尾随闭包 闭包逃离 自动闭包

  • Day7 闭包(Closures)

    本页包含内容:• 闭包表达式• 尾随闭包• 值捕获• 闭包是引用类型• 逃逸闭包• 自动闭包 1、闭包表达式 闭包...

网友评论

    本文标题:Python-闭包

    本文链接:https://www.haomeiwen.com/subject/ylazrctx.html