一、背景知识
闭包(Closure) 是一个编程概念,在 Python 中是一个非常重要的特性。简单来说,闭包是一个函数,它能够记住并访问其外部作用域中的变量,即使外部函数已经执行完毕。
1. 详细解释:
在 Python 中,闭包指的是 一个函数嵌套在另一个函数中,并且内部函数引用了外部函数的变量。即使外部函数已经执行完毕,内部函数依然能够访问这些外部函数的变量,这些被引用的变量会“被记住”,这就是闭包的特性。
2. 形成闭包的条件
一个函数是闭包,必须满足以下三个条件:
- 有一个嵌套函数(即一个函数内部定义了另一个函数)。
- 内部函数引用了外部函数的变量。
- 外部函数返回了内部函数,并且内部函数在外部函数执行完毕后依然可以访问外部函数的变量。
3. 闭包的例子
3.1 基本闭包示例:
def outer():x = 10 # 外部函数的局部变量def inner():print(x) # 内部函数引用了外部函数的变量 xreturn inner # 返回内部函数# 获取闭包函数
closure = outer()# 调用闭包函数
closure()
输出:
10
解释:
outer
函数:它有一个局部变量x
,并定义了一个嵌套的inner
函数。inner
函数:它引用了外部函数outer
的变量x
。- 返回
inner
:outer
函数返回了inner
函数,即closure
,即使outer
函数已经执行完毕,closure
依然能够访问x
。 - 执行
closure()
:调用closure()
实际上是在执行inner()
,并打印了x
的值。
3.2 为什么会形成闭包?
当 outer()
执行时,它会返回 inner()
。此时 inner()
依然持有对 x
的引用(x
是 outer
函数的局部变量)。即使 outer()
函数已经结束执行,inner()
依然可以访问 x
,这就是闭包的特性。
4. 闭包的应用:生成器和装饰器
闭包在实际编程中有很多应用,特别是在以下两种场景中:
- 生成器函数:生成器函数利用闭包来记住每次迭代的状态。
- 装饰器:装饰器函数利用闭包来扩展或修改其他函数的行为,同时保留对原始函数的访问。
二、装饰器概述
在 Python 中,装饰器(Decorator)是一个非常强大的功能,它本质上是一个 函数,用于在不修改原始函数代码的情况下,为其添加额外的功能。简单来说,装饰器是一种 函数包装器,它可以用来在函数执行前后添加自定义逻辑。
1. 装饰器的工作原理
装饰器本质上是一个接受一个函数作为输入并返回一个函数的函数。它能够在不修改原始函数代码的情况下,为这个函数增加额外的功能。
1.1基本概念
- 接受一个函数作为参数:装饰器接收一个函数作为输入。
- 返回一个新函数:装饰器返回一个新的函数,通常是对原始函数的增强或修改版本。
1.2 举个简单的例子:
def my_decorator(func):def wrapper():print("Before the function is called.")func() # 调用原始函数print("After the function is called.")return wrapperdef say_hello():print("Hello!")# 使用装饰器
decorated_say_hello = my_decorator(say_hello)# 调用装饰后的函数
decorated_say_hello()
输出:
Before the function is called.
Hello!
After the function is called.
在上面的代码中:
my_decorator
是一个装饰器,它接受say_hello
函数作为参数,并返回一个新的wrapper
函数。wrapper
在调用say_hello
之前和之后增加了额外的打印语句。
1.3 使用 @
语法糖
在 Python 中,你可以通过 @
语法糖来简化装饰器的使用,避免显式地将原函数传递给装饰器。
@my_decorator
def say_hello():print("Hello!")# 直接调用装饰后的函数
say_hello()
输出和之前一样:
Before the function is called.
Hello!
After the function is called.
2. 装饰器的实际应用
装饰器通常用于以下几个场景:
- 日志记录:记录函数的执行日志。
- 权限验证:在执行函数之前检查用户是否有权限。
- 缓存:缓存函数的计算结果,以提高效率。
- 计时:测量函数执行的时间。
三、示例分析
1 装饰器语法糖
python提供了@符号作为装饰器的语法糖,使我们更方便的应用装饰函数。但使用语法糖要求装饰函数必须return一个函数对象
。因此我们将上面的func函数使用内嵌函数包裹并return。
1.1 函数装饰器
(1)不带参数装饰器,不带参数函数
def use_logging(func):print(f"传入函数名称: {func.__name__}")def _deco():print("%s is running" % func.__name__)func()return _decoprint("1" * 20)@use_logging
def bar():