闭包与装饰器的详解
一、什么是闭包
概念:
在 Python 中,闭包(Closure)是指一种函数对象,它能够访问在其定义范围内但是不在其参数列表中的非全局变量。简而言之,闭包是一个函数及其相关的引用环境的组合体。 狼人杀案例演示:
python
def 狼人睁眼(狼人1,狼人2):
def 杀人(狼人1,狼人2): #内嵌函数杀人
if 狼人1指认==狼人2指认:
return 指认人
return 杀人 #返回内嵌函数杀人,因为在夜里杀人需要被法官知道,出现了函数里定义一个函数,然后又返回这个函数的语法。而这种语法在python中就叫做闭包
从案例我们可知:
满足闭包的条件必须要有:
- 闭包函数必须要有内嵌函数
- 内嵌函数必须要引用外层函数的变量
- 闭包函数返回内嵌函数的地址
二、什么是装饰器
(一)、概念:
在 Python 中,装饰器是一种用于修改或增强函数或方法行为的高级工具。它们实质上是函数,用于包装另一个函数,并提供一种简洁的语法来进行函数的扩展或修改。装饰器其实就是闭包的扩展应用
案例代码:
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. 更好的模块化: 装饰器可以使代码更模块化,每个装饰器可以关注于特定的功能,实现了更好的分离关注点。
代码演示:
python
#示例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.对有参函数进行装饰
示例代码:
python
#伪代码
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、使用装饰器对带不定长参数的函数进行修饰
示例代码:
python
#伪代码
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、装饰器装饰有返回值的函数
示例代码:
python
#伪代码
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、装饰器带参数
python
#伪代码
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种,一般最好是写带参。另外还有一个知识点注意
python
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
综合全部知识点,我们以后写装饰器的代码可以固定为:
python
def auth(func):
@functools.wrap(func)
def inner(*args,**kwargs):
'''hello world注释'''
res=func(*args,**kwargs)
return res
return inner
总结
注意装饰器的传参和定义及使用场景,明白装饰器的好处。最后,欢迎评论留言!