likes
comments
collection
share

闭包与装饰器详解

作者站长头像
站长
· 阅读数 2

闭包与装饰器的详解

一、什么是闭包

概念:

在 Python 中,闭包(Closure)是指一种函数对象,它能够访问在其定义范围内但是不在其参数列表中的非全局变量。简而言之,闭包是一个函数及其相关的引用环境的组合体。 狼人杀案例演示:

def 狼人睁眼(狼人1,狼人2):
    def 杀人(狼人1,狼人2): #内嵌函数杀人
    if 狼人1指认==狼人2指认: 
        return 指认人
    return 杀人 #返回内嵌函数杀人,因为在夜里杀人需要被法官知道,出现了函数里定义一个函数,然后又返回这个函数的语法。而这种语法在python中就叫做闭包

从案例我们可知:

满足闭包的条件必须要有:
  1. 闭包函数必须要有内嵌函数
  2. 内嵌函数必须要引用外层函数的变量
  3. 闭包函数返回内嵌函数的地址

二、什么是装饰器

(一)、概念:

在 Python 中,装饰器是一种用于修改或增强函数或方法行为的高级工具。它们实质上是函数,用于包装另一个函数,并提供一种简洁的语法来进行函数的扩展或修改。装饰器其实就是闭包的扩展应用

案例代码:

#示例1:
#正常函数
def func():
    # print("before")
    print('这是一个普通的函数')
    # print("after")
    val='hello world!'
    return val

result=func()
print(result)

#示例2
#使用装饰器后
def outer(origin):
    def inner():
        # print("before")
        res=origin()
        # print("after")
        return res
    return inner

@outer #源代码是 func1=outer(func1)
def func1():
    prin('这是一个普通函数')
    val=[1,2,3,4,5]
    return val
result=func1()
print(result)

详解:正常情况下我们要在函数func的输出结果里添加两个输出语句before和after直接如代码示例1那样写。但是我们如果使用装饰器的话就要像示例2那样:func1=outer(func1)这个可以先从等号来讲解,因为一个等号是赋值,其实就是把outer赋值给func1,然后我们来看函数outer(),outer是一个嵌套函数,内层函数名字为inner,从上往下看发现outer的返回值为函数inner。再来看outer(func1),明显这里穿的是函数func1到函数outer里面了,而里面是用origin来接受到外部函数func1的再来看整个outer函数,发现是不是内部函数inner引用了外部函数的变量origin,并且还返回了origin,而outer又是一个嵌套函数且返回值为内嵌函数inner,所以可知这是一个闭包。再看inner发现其实返回的origin就是外部函数func1。所以,才能出现与示例一样的打印效果。最后代码func1=outer(func1)其实可以用@outer来代替即@ 函数名

(二)、学习装饰器的好处

1. 简化代码: 装饰器提供了一种简洁的语法,使得对函数进行修改或扩展的操作更加方便。

2. 增加可复用性: 可以创建通用的装饰器,用于多个函数,提高代码的可复用性。

3. 可读性和可维护性: 装饰器可以将一些与核心逻辑无关的功能分离出去,使得函数的主体逻辑更加清晰,提高代码的可读性和可维护性。

4. 更好的封装: 装饰器可以在不修改原始函数代码的情况下,对函数进行功能的增加或修改,实现了更好的封装性。

5. 更好的模块化: 装饰器可以使代码更模块化,每个装饰器可以关注于特定的功能,实现了更好的分离关注点。

代码演示:

#示例1

def func1():
    print("before")
    print('这是一个普通函数')
    print("after")
    val = [1, 2, 3, 4, 5]
    return val

result = func1()
print(result)
def func1():
    print("before")
    print('这是一个普通函数')
    print("after")
    val = [1, 2, 3, 4, 5]
    return val

result = func1()
print(result)
def func1():
    print("before")
    print('这是一个普通函数')
    print("after")
    val = [1, 2, 3, 4, 5]
    return val

result = func1()
print(result)

#示例2
def outer(origin):
    def inner():
        print("before")
        res = origin()
        print("after")
        return res

    return inner


@outer 
def func1():
    print('这是一个普通函数')
    val = [1, 2, 3, 4, 5]
    return val

result = func1()
print(result)
@outer  
def func1():
    print('这是一个普通函数')
    val = [1, 2, 3, 4, 5]
    return val

result = func1()
print(result)
@outer  
def func1():
    print('这是一个普通函数')
    val = [1, 2, 3, 4, 5]
    return val

result = func1()
print(result)

代码分析:如示例2所示如果调用多次函数func1我只需要有个装饰器就行然后其它的复制几份就可以,相同的是示例1也可以这么做,但是:如果你要在把before和after的值修改呢:如果使用示例1的代码你是不是要一个一个去改,而且代码一多起来是不是费时费力。而当你使用示例2只需要在装饰器里面改就行了。

(三)、带参的装饰器
1.对有参函数进行装饰

示例代码:

#伪代码
def func(function):
    def function_in(a):
        return function(a)
    return function_in
@func
def func(a):
    pass

#示例
def set_name(origin):
    def get_name(name):
        return origin(name)
    return get_name

@set_name
def func(name):
    print(f"你的名字是:{name}")
if __name__=="__main__":
    func('小明')
2、使用装饰器对带不定长参数的函数进行修饰

示例代码:

#伪代码
def func(function):
    def function_in(*args.**kwargs):
        function(*args,**kwargs)
    return function_in
@func
def test(*args,**kwargs):
    pass
  
#示例
def set_name(origin):
    def get_name(*args, **kwargs):
        return origin(*args, **kwargs)
    return get_name

@set_name
def func(*args, **kwargs):
    print(f"传入的值分别为: {args}{kwargs}")

if __name__ == "__main__":
    list1 = 1, 2, 3, 4
    dict1 = {"张三": 20, "李四": 22}
    func(list1, **dict1)
3、装饰器装饰有返回值的函数

示例代码:

#伪代码
def func(function):
    def function_in(*args,**kwargs)
        ret=function(*args,**kwargs)
        return ret
    return function_in
@func
def test(*args,**kwargs)
    return "success" #这里的success可以替换任何返回值
    
#示例
def set_name(origin):
    def get_name(*args, **kwargs):
        ret = origin(*args, **kwargs)
        print(f"函数 {origin.__name__} 的返回值为: {ret}") #方法.__name__是返回函数的名称
        return ret
    return get_name

@set_name
def func(x, y):
    return x * y

if __name__ == "__main__":
    func(5, 6)

4、装饰器带参数
#伪代码
def func(function):
    def func(*args,**kwargs)
        def function_in(*args,**kwargs):
            ret=function(*args,**kwargs)
            return ret
        return function_in
    return func

#示例代码:
def my_decorator_with_args(arg1, arg2):
    def decorator(func):
        def wrapper(*args, **kwargs):
            # 在这里使用传递给装饰器的参数 arg1 和 arg2
            print(f"装饰器参数: {arg1}, {arg2}")

            # 调用原始函数
            result = func(*args, **kwargs)

            # 在这里处理函数的返回值(如果需要)
            print(f"函数 {func.__name__} 的返回值为: {result}")

            return result
        return wrapper
    return decorator

# 使用装饰器带参数来装饰函数
@my_decorator_with_args("参数1", "参数2")
def example_function(x, y):
    return x + y

if __name__ == "__main__":
    example_function(3, 4)

本段总结: 综合上述代码发现装饰器是可以无参数,有参数和不定参3种,一般最好是写带参。另外还有一个知识点注意

def auth(func):
    @functools.wrap(func) # func是原函数的名字 其实这里就是把inner.__name__赋值给了func.__name__
    def inner(*args,**kwargs):
        '''hello world注释'''
        res=func(*args,**kwargs)
        return res
    return inner

def rbac():
    '''这是一个注释'''
print(rbac.__name__) #输出函数名
print(rbac.__doc__) #输出函数的注释

#提示:执行下面这段代码时先把@functools.wrap(func)注释掉
@auth
def admin():
    '''注释66'''
print(admin.__name__) #因为调用了装饰器auth,此时的admin就是inner所以输出函数名为inner
print(admin.__name__) #因此注释的输出hello world注释

@auth
def admin():
    '''注释66'''
print(admin.__name__) #所以这里输出admin
print(admin.__name__)  #所以这里输出注释66    

综合全部知识点,我们以后写装饰器的代码可以固定为:

def auth(func):
    @functools.wrap(func) 
    def inner(*args,**kwargs):
        '''hello world注释'''
        res=func(*args,**kwargs)
        return res
    return inner

总结

注意装饰器的传参和定义及使用场景,明白装饰器的好处。最后,欢迎评论留言!