Python闭包(Python Closures)介绍

简介: 介绍python的闭包语法。

0. 标题

Python闭包(Python Closures)介绍
这个文章,希望你可以从头到尾读三遍,就可以看懂了,第一遍看不懂很正常。

作者: quantgalaxy@outlook.com   
欢迎交流

1. What: 什么是闭包

1.1 闭包

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。
这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。
所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。
闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

另一种解释:闭包通常用于创建函数工厂,即返回函数的函数。当外部函数返回内部函数时,内部函数会保留对外部函数作用域的引用,形成闭包。

闭包是为了解决自由变量(free variable)的问题,即在函数内部的变量,可以在函数外被访问和调用。
通过闭包的方式,就可以实现这个功能。

Python不要求声明变量,而是假定在函数定义体中赋值的变量是局部变量。
闭包是一种函数,它会保留定义时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用了,但是仍然能使用那些绑定。

上面的说明,可能有些拗口,我们通过后面例子来具体说明下,看完例子后,再来看这个说明,就很好理解了。

2. How: python中的闭包如何使用

在Python中,闭包(closure)是指一个函数(通常称为内部函数),它包含对在其外部函数中定义的非全局变量的引用。
闭包允许内部函数访问其外部函数的作用域,即使外部函数已经执行完毕。

def outer_function(x):
    # 在外部函数中定义一个变量
    outer_variable = x

    # 在外部函数中定义一个内部函数
    def inner_function(y):
        # 内部函数可以访问外部函数的变量
        return outer_variable + y

    return inner_function

# 创建一个闭包函数
closure = outer_function(10)  # x is 10。返回了inner_function,并且inner_function内部的outer_variable为10

# 使用闭包函数
result = closure(5)  # y is 5
print(result)  # 输出 15

在这个示例中,outer_function 是外部函数,它接受一个参数 x 并定义了一个内部函数 inner_function,
内部函数引用了 outer_variable,这个变量是外部函数的参数。
当我们调用 outer_function(10) 时,它返回了 inner_function,并且 outer_variable 的值10被保持在内存中。
然后,我们可以多次调用 closure(5),每次它都会使用之前存储的 outer_variable(10) 的值,所以结果是 10 + 5 = 15。

闭包在许多情况下非常有用,例如在函数工厂、装饰器和回调函数等编程模式中。
它们允许您封装状态和行为,以便稍后在程序的不同部分使用。

3. Why:python闭包和自由变量的原理解释,为什么要有闭包

开始对闭包介绍的时候,有这么一段话:

Python不要求声明变量,而是假定在函数定义体中赋值的变量是局部变量。  
闭包是一种函数,它会保留定义时存在的自由变量的绑定,这样调用函数时,  
虽然定义作用域不可用了,但是仍然能使用那些绑定。

我们这里就对python的作用域具体解释下,以及说明下闭包出现解决了什么问题。

2.1 python的作用域说明

2.1.1 自由变量

b = 6
def f1(a):
    print(a)
    print(b)

执行后,输出1,6。
函数体外的b为全局变量,函数体内的b为自由变量。
因为自由变量b绑定到了全局变量,所以在函数f1中能正确访问。

2.1.2 全局变量和局部变量互斥

b = 6
def f2(a):
    print(a)
    print(b)
    b = 2

这次调用时候就报错了:

Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 3, in f2
UnboundLocalError: local variable 'b' referenced before assignment

报错:局部变量b在赋值前进行了引用。

这不是缺陷,而是Python设计:Python不要求声明变量,而是假定在函数定义体中赋值的变量是局部变量。
如果想让解释器把b当做全局变量,那么需要使用global声明:

b = 6
def f2(a):
    globle b
    print(a)
    print(b)
    b = 2

2.1.3 闭包的作用域

上个例子中,如果我们不想使用global关键字,还是想把变量当成原来函数的局部变量,我们该如何做呢?
这时候闭包出现了。
闭包是一种函数,它会保留定义时存在的自由变量的绑定,这样调用函数时,
虽然定义作用域不可用了,但是仍然能使用那些绑定。

def make_averager():
    series = []

    def averager(new_value):
        # series是自由变量
        series.append(new_value)
        total = sum(series)
        return totle / len(series)

    return averager

avg = make_averager()
avg(10)  # 10.0
avg(11)  # 10.5
avg(12)  # 11.0

这个例子没有报错,就是因为series被内部函数averager引用后,形成了闭包,
闭包会保留自由变量series的绑定,在调用avg(10)时继续使用这个绑定,即使make_averager()函数的局部作用域已经消失。
这就是闭包的作用,通过返回一个内部函数的方式,保留了对自由变量的绑定,解决了自由变量访问的问题,
并没有使用global关键字,但是可以访问到了外部函数make_averager定义的局部变量。

作者: quantgalaxy@outlook.com   
欢迎交流

2.1.4 闭包中的nonlocal

是不是所有自由变量都可以通过闭包直接访问呢?其实还有有一些区别的,看下面这个例子:

def make_averager():
    count = 0
    total = 0

    def averager(new_value):
        count += 1
        total += new_value
        return total / count

    return averager

运行后会报错:局部变量count在赋值前进行了引用。因为count +=1等同于count = count + 1,存在赋值,count就变成局部变量了。
total也是如此。
这里如果把count和total通过global关键字声明为全局变量,显然是不合适的,它们作用域最多只扩展到make_averager()函数内。
为了解决这个问题,Python3引入了nonlocal关键字声明,
nonlocal的作用是把变量标记为自由变量,即使在函数中为变量赋值了,也仍然是自由变量。

def make_averager():
    count = 0
    total = 0

    def averager(new_value):
        nonlocal count, total
        count += 1
        total += new_value
        return total / count

    return av

注意,对于列表、字典等可变类型来说,添加元素不是赋值,不会隐式创建局部变量。
对于数字、字符串、元组等不可变类型以及None来说,赋值会隐式创建局部变量。
可变对象添加元素不是赋值,不会隐式创建局部变量。

3. 总结

闭包就是用来解决函数嵌套时,自由变量如何处理的问题,它会保留自由变量的绑定,即使局部作用域已经消失。
对于不可变类型和None来说,赋值会隐式创建局部变量,把自由变量转换为局部变量,
这可能会导致程序报错:局部变量在赋值前进行了引用。
除了使用global声明为全局变量外,还可以使用nonlocal声明把局部变量强制变为自由变量,实现闭包。

4. 作者

作者: quantgalaxy@outlook.com   
欢迎交流
目录
相关文章
|
7月前
|
数据安全/隐私保护 Python
Python中装饰器、回调函数、闭包、派生的区别与应用详解
Python中装饰器、回调函数、闭包、派生的区别与应用详解
69 0
|
7天前
|
Python
Python进阶---闭包和装饰器
Python进阶---闭包和装饰器
21 2
|
13天前
|
Python
Python闭包函数和计时器
本文介绍了闭包函数的概念,它允许内部函数引用外部作用域的变量但无法修改它们。示例展示了如何使用闭包来封装函数。接着,文章讨论了如何在函数调用时添加开始和结束的打印语句,通过传递函数作为参数实现。然后,文章引入装饰器,通过闭包定义了一个`timer`装饰器,用于在函数执行前后打印消息。最后,给出了一个练习,实现了一个计算函数执行时间的装饰器,处理了带有参数的被装饰函数。
28 1
|
13天前
|
存储 Java 测试技术
Python中闭包和装饰器使用不当
【5月更文挑战第4天】Python中闭包和装饰器使用不当
15 1
|
13天前
|
存储 Java 测试技术
Python中闭包和装饰器使用不当Python中闭包和装饰器使用不当
【5月更文挑战第4天】Python中闭包和装饰器使用不当
11 2
|
13天前
|
Python
深入理解python的闭包函数
深入理解python的闭包函数
|
13天前
|
JavaScript 前端开发 Python
Python 高级主题: 解释 Python 中的闭包是什么?
【4月更文挑战第13天】闭包是内部函数引用外部变量的函数对象,作为外部函数的返回值。当外部函数执行完毕,其变量本应消失,但由于内部函数的引用,这些变量在内存中保持存活,形成闭包。例如,在外函数中定义内函数并返回内函数引用,实现对外部局部变量的持久访问。闭包在Python和JavaScript等语言中常见,是强大的编程工具,连接不同作用域并允许局部变量持久化,用于复杂程序设计。**
17 4
|
13天前
|
人工智能 机器人 测试技术
【python】python闭包的详细解读(傻瓜式教学)
【python】python闭包的详细解读(傻瓜式教学)
|
13天前
|
存储 程序员 Python
Python教程第9章 | 通俗易懂学闭包
本文通过一个需求探讨闭包的基本概念与用法,帮助快速理解闭包。
523 0
|
13天前
|
自然语言处理 安全 Python
Python中的闭包和高阶函数详解
Python中的闭包和高阶函数详解