python函数重点复盘(1):闭包

本文探讨了Python中的闭包,包括其定义、与普通内部函数的区别以及闭包的本质特征。通过实例解析了闭包如何直接访问外部变量,并介绍了闭包在装饰器函数中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、什么是闭包
1.1 一段简单的闭包函数代码
def outer1(outer_param):
    def inner():
        print("外部函数的参数是:%s" % outer_param)   # outer_param是外部函数outer的参数,是一个外部的变量,并不在函数inner中
    return inner                                     # 此处返回的是函数对象!而不是函数调用(inner())的结果
1.2 闭包类似于内部函数,但有所不同

从形式上看,闭包类似于简单的内部函数。

# 一个简单的内部函数
def outer2(outer_param):
	def inner(outer_param):
		print("外部函数的参数是:%s" % outer_param)
	return inner(outer_param)

从代码上看,闭包和普通内部函数的相似点有两个:

  • 在函数(outer)内部定义了一个新函数(inner);
  • 内部的新函数inner可以读取到外部函数的参数(outer_param)

然而细看代码,会发现他们也有几点不同,正是这几点不同让闭包成为了闭包:

1、虽然普通内部函数和闭包都可以读取到外部函数的参数(outer_param),但是其实二者读取到的方式是不同的!闭包是直接读取,
而普通内部函数中,inner是通过自己的参数读取到的。

也就是说:普通内部函数是 生成一个自己的内部变量,通过参数将外部变量的值赋值给这个内部变量,借以达到记录外部变量值的目的,而闭包则是直接访问了外部变量

这样子改写一下代码,就能看出差异了:

# 一个简单的内部函数
def outer(outer_param):
	def inner(inner_param):
		print("外部函数的参数是:%s" % inner_param)
	return inner(outer_param)

普通内部函数访问到外部函数的变量,是靠外部函数主动将变量以参数(inner_param)的形式传递给内部函数,而闭包则没有这个过程

2、普通内部函数返回的是调用内部函数后的结果,而闭包则是返回inner函数对象,尚未被调用执行。

# 普通的内部函数返回
a = outer2("hello")
>> 外部函数的参数是:hello
a
>> None                    # 内部函数只有print,没有返回值,所以为None

## 闭包函数
b = outer1("hello")
b
>> <function __main__.outer1.<locals>.inner()>   # 函数对象
b()
>> 外部函数的参数是:hello                         # 此时才调用它
1.3 闭包的本质是:被动态创建的,可以记录外部变量的函数

闭包的定义:闭包是指一个可以由另一个函数动态生成的函数,并且可以改变和存储函数外创建的变量的值。
在前面的普通内部函数中,inner也可以被看作是一个闭包,但是外部函数并没有返回这个函数,而是直接返回了这个函数执行的结果,所以我们并不能感受到inner作为一个动态生成的函数的意义。

1.3.1 被动态生成

回顾下这段闭包函数的代码:

def outer1(outer_param):
    def inner():
        print("外部函数的参数是:%s" % outer_param)   
    return inner                                    

每传入一个参数给外部函数并执行,这个函数都将返回一个新的inner,不同的inner函数对象,这就是“动态”的“生成”函数的过程。

a = outer1("hello")
b = outer1("world")

a
>> <function __main__.outer1.<locals>.inner()>
b 
>> <function __main__.outer1.<locals>.inner()>

而普通的内部函数是不具有这个过程中的。首先,它是通过参数传递的形式记录了外部变量的值,函数还是那个函数本身,只是每次调用执行的实际参数的值不一样了,并没有“生成”的概念;其次,普通内部函数没有返回inner函数的对象,只是把这个函数执行了一边。

1.3.2 记录外部变量

这个特性就比较明显了。从执行流上看,返回inner函数对象时,外部变量outer_param已经存进了这个函数对象里面。
在这里插入图片描述
在调用的时候会显式的感受到:

a = outer1("hello")
b = outer1("world")

a
>> <function __main__.outer1.<locals>.inner()>         # 可以抽象的理解为,这个函数对象里面有一个out_param的变量 = hello
b 
>> <function __main__.outer1.<locals>.inner()>         # 而这个函数对象里面也有一个out_param的变量 = world

# 当我们去调用这两个函数对象时,对应的值都可以显式的访问到
a()
>> 外部函数的参数是:hello
b()
>> 外部函数的参数是:world
2、闭包的应用示例:装饰器函数

装饰器(decorator)本质上也是一种函数,它把一个函数作为输入,同时返回一个新函数。关于装饰器的详细内容,请参见另外一篇博客:

这里仅仅强调一下装饰器中和闭包有关的部分,先看一段装饰器的示例代码:

def my_decorator(func):
	'''
	这个装饰器只是简单地输出了输入的函数名字,同时执行了这个函数
	'''
	def new_func(*args,**kwargs):
		print(func.__name__)                # 这里直接访问了外部变量func
		return func(*args,**kwargs)
	return new_func                         # 这里返回了一个新的函数!对象!

闭包在其中体现在两个方面:
1、内部函数new_func直接访问了外部变量func,并且输出了它的__name__属性
2、装饰器函数decorator返回了一个在其内部定义的函数对象new_func

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值