前端开发入门到精通的在线学习网站

网站首页 > 资源文章 正文

python闭包和装饰器详解

qiguaw 2025-03-12 19:57:11 资源文章 85 ℃ 0 评论

在Python中,闭包(Closure)是一种强大的功能,在函数式编程中,他能提供非常灵活的功能组合。它允许在一个函数内部定义另一个函数,并且内部函数可以访问外部函数的变量。

这些外部变量的值在内部函数被定义时就确定了,即使在外部函数的执行结束后,这些值依然可以被内部函数访问。

什么是闭包

闭包通常由两部分组成:

  • 外部函数:定义了一个或多个内部函数。
  • 内部函数:可以访问并操作外部函数的局部变量。
def outer(x):      
    def inner(y):
        return x + y
    return inner
 
# 使用闭包
add_five = outer(5)
print(add_five(10))  # 输出: 15

在这个例子中,outer是一个外部函数,它接收一个参数x,并定义了一个内部函数inner,该函数也接收一个参数y,并返回x + y的结果。然后,outer返回inner。当调用outer(5)时,它会返回一个新函数,这个新函数可以访问outer中的x变量(这里是5),并且可以像普通函数一样被调用。

如果你想在内部函数中对外部传过来的变量进行修改

def counter():
    count = 0
    def increment():
        count += 1
        return count
    return increment

c = counter()
print(c())
UnboundLocalError: cannot access local variable 'count' where it is not associated with a value

是的,它报错了,外部函数的变量无法在内部直接修改,这时候我们需要使用nonlocal关键字

def counter():
    count = 0
    def increment():
        nonlocal count  # 声明 count 不是局部变量,而是外部函数的变量
        count += 1
        return count
    return increment

c = counter()
print(c())  # 1
print(c())  # 2
print(c())  # 3

闭包的应用场景

闭包是 Python 中一种强大的特性,它在装饰器、状态保存和封装等场景中非常有用。



装饰器本质上是一个Python函数,是一种特殊的闭包

它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计。

有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

举个例子

def get_score(score):
    return ('今年我的成绩是:%s'%score)

现在有个新需求,把去年的成绩也打印出来,于是我们就添加记录去年成绩的代码。

def get_score(score):
    print('去年成绩:58')
    return ('今年我的成绩是:%s'%score)

这样也可以,但是如果我们有许多这样的函数,总不能一个个的手动修改吧,这个时候,为了减少重复写代码,我们可以这样做,重新定义一个函数:记录去年的信息 ,之后再执行真正的业务代码。

def last_info():
    print('去年成绩:58')

def get_score(score):
    last_info()
    return ('今年我的成绩是:%s'%score)

score1=get_score(55)
print(score1)
去年成绩:58
今年我的成绩是:55

嗯,这样看起来就优雅多了,

但是这样有很大风险,因为直接在函数内部增加一个函数,破坏了函数的完整性,如果大量修改很容易导致函数出问题。

那么装饰器这时候应该登场了,

Python中支持了@语法糖,直接放在函数上面而不必修改函数内部,这种写法优雅而美观,保证了函数完整性。

def last_info(func):
    def inner(*args,**kwargs): #位置参数and关键字参数,可表示任意参数
        print('去年成绩:58')
        return func(*args,**kwargs)
    return inner

@last_info
def get_score(score):
    return ('今年我的成绩是:%s'%score)

这样就高级了,而且只是在函数上面增加一个函数,完全不需要对函数内部进行修改。

但是这样有一个问题

print(get_score.__name__)   #
输出:inner

get_score函数的名字为何会变成了inner?

因为装饰器最终会返回一个可调用对象,而这个可调用对象才是正在最后被执行的,所以get_score被装饰器修饰后,得到的是inner这个函数(函数是可调用对象),于是乎get_score.__name__实际上是inner._name_。

那该怎么解决这个问题呢,加上wraps注解。

from functools import wraps
def last_info(func):
    @wraps(func)   			#这样可以保持函数的名称不被修改
    def inner(*args,**kwargs): #位置参数and关键字参数,可表示任意参数
        print('去年成绩:58')
        return func(*args,**kwargs)
    return inner

@last_info
def get_score(score):
    return ('今年我的成绩是:%s'%score)

print(get_score.__name__)
 输出:get_score

这里wraps本身也是一个装饰器,这个装饰器是一个带参数的装饰器,参数本身就是get_score函数。在获取get_score函数元信息时,实际上还是在执行inner._name_,但是inner有wraps装饰器,最终元信息是通过wraps装饰器返回的,wraps装饰器对get_score函数做了份拷贝,所以拿到的还是get_score函数的元信息。

建议写装饰器时都加上wraps注解,这是一个好习惯。

带参数的装饰器

上面我们说的装饰器都是没有参数的,只能输出定义好的去年成绩,无法修改。

现在有一个需求,比如:使用装饰器的时候可以输入去年的成绩。

装饰器还有更强大的功能,比如带参数的装饰器,装饰器允许我们在使用它时提供参数输入,这使得装饰器的功能更加有灵活性。


def last_info(score):
    def inner(func):
        def wapper(*args,**kwargs):
            print('去年成绩:%s'%score)
            return func(*args,**kwargs)
        return wapper
    return inner

@last_info(88)#带参数
def get_score(score):
    return ('今年我的成绩是:%s'%score)
#调用
score=get_score(100)
print(score)
去年成绩:88
今年我的成绩是:100

上面的lase_info就是一个可以输入参数的装饰器,其实他是装饰器外面在嵌套一个函数,并返回装饰器,我们也可以把他理解为一个带参数的闭包。

总的来说,装饰器的理念是对原函数、对象的功能进行加强,而且是在不破坏原函数内部结构的基础上进行功能加强,相当于对函数的重新封装。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表