装饰器
- 装饰器定义;
- 本质是函数 : 函数的目的是完成特定的功能
- 装饰器功能:一个装饰其他函数功能的函数(为其他函数添加特定的功能)
抛出问题:
假如我们现在有10个函数,每个函数都有自己独特的功能,但是,现在我们需要给这10个函数添加一个记录日志的功能
# 定义日志的函数,然后将日志函数添加到test的十个函数中 def logger(): print("...logger...") def test1(): pass logger() def test2(): pass logger() def test3(): pass logger() test1() test2() test3()
特定场景:假如,这10个函数已经再线上运行了,比如说,现在需要再用户已经使用的软件中,给这10个函数添加新的功能,那么该怎么做?
-
- 如果我们直接修改函数的源代码,可能会导致软件崩溃,所以原则上,我们不能在已经上线的程序中修改源代码。
装饰器原则:
- 原则1:不能修改被装饰的函数的源代码
- 原则2:不能修改被装饰的函数的调用方式
- 装饰器对被装饰的函数是完全透明的,函数感知不到装饰器的存在,因为函数的源代码和调用方式都没有改变
初识装饰器:
- 计算函数运行时间的装饰器
import time # 创建获取函数运行时间的装饰器 def timmer(func): def warpper(*args, **kwargs): start_time = time.time() func() stop_time = time.time() print("the func run time is %s" % (stop_time-start_time)) return warpper @timmer #为test函数添加获取test函数运行时间的功能 def test(): time.sleep(3) print("in the test") test() """ 运行效果: in the test the func run time is 3.004085063934326 """
- 由上代码可以得知:
- 装饰器本身就是一个函数
- 装饰器不修改被装饰函数的源代码、同时也不修改被装饰函数的调用方式
- 对于test函数来说,装饰器timmer就和不存在一样
实现装饰器的知识储备:
- 函数即”变量“
- 高阶函数
- 嵌套函数
高阶函数 + 嵌套函数 =》 装饰器
函数即“变量”:
# 第一种 def foo(): print("in the foo") bar() foo() 第二种 def foo(): print("in the foo") bar() def bar(): print("in the bar") foo() # 第三种 def bar(): print("in the bar") def foo(): print("in the foo") bar() foo() # 第四种 def foo(): print("in the foo") bar() foo() def bar(): print("in the bar")
高阶函数:
-
把一个函数名当作实参传递给另一个函数:
import time def bar(): time.sleep(3) print("in the bar") # print(bar) # bar记录了bar函数在内存中的地址 def foo(func): start_time = time.time() func() stop_time = time.time() print("the func run time is %s" % (stop_time - start_time)) # 满足了装饰器的不修改被装饰器的源代码的条件,但是调用方式被修改了 foo(bar) """ 运行结果: in the bar the func run time is 3.0134708881378174 """
- 在不修改被装饰函数源代码的情况下为其添加功能
- 返回值中包含函数名
import time def bar(): time.sleep(3) print("in the bar") def foo(func): start_time = time.time() func() stop_time = time.time() print("the func run time is %s" % (stop_time - start_time)) return func # foo(bar()) #print(foo(bar)) # t = foo(bar) # print(t) # t = foo(bar) # t() # 不修改函数的调用方式,为其添加功能 bar = foo(bar) bar()
- 不修改被装饰函数的调用方式
嵌套函数:
- 在函数体内使用def关键字定义一个新的函数
# 嵌套函数 def foo(): print("in the foo") def bar(): print("in the bar") bar() # bar() #报错,因为bar的作用域仅在foo()函数体内,当foo函数运行结束,那么bar就会释放空间 foo()
- 变量作用域的访问顺序
x = 0 def grandfather(): x = 1 def father(): x = 2 def son(): x = 3 print(x) son() father() grandfather()
闭包:
当局部变量脱离了函数体后,依然可以使用:
def foo(): sum_1 = 100 def deco(): print(sum_1) return deco a = foo() a() #100 # print(sum_1) # 报错,局部变量不能在局部以外的地方使用
解释代码:
def foo(x): def deco(y): print(x+y) return deco a = foo(10) print(a) #<function foo.<locals>.deco at 0x00000275D6421040> a(5) # 15 b = foo(20) print(b) #<function foo.<locals>.deco at 0x0000017D992D10D0> b(5) #25 """ 所有函数都有一个__closure__属性,如果这个函数是一个闭包的话,那么它返回的是一个由cell对象组成 的元组对象。cell对象的cell_contents属性就是闭包中的自由变量 """ print(foo.__closure__) #None print(a.__closure__) #(<cell at 0x000002B1F02C1370: int object at 0x000002B1F0116A50>,) print(b.__closure__) #(<cell at 0x000002B1F018A0D0: int object at 0x000002B1F0116B90>,) print(a.__closure__[0].cell_contents) #10 print(b.__closure__[0].cell_contents) #20 """ 这解释了为什么局部变量脱离函数之后,还可以在函数之外被访问的原因,因为它存储在了闭包的cell_contens中了 """
实现装饰器:
import time def timer(func): def deco(): start_time = time.time() func() stop_time = time.time() print("the func run time is %s" % (stop_time - start_time)) return deco @timer def test1(): time.sleep(3) print("in the test1") @timer def test2(): time.sleep(3) print("in the test2") # test1 = timer(test1) # test2 = timer(test2) test1() test2()
改进后的装饰器:
import time def timer(func): def deco(*args, **kwargs): start_time = time.time() func(*args, **kwargs) stop_time = time.time() print("the func run time is %s" % (stop_time - start_time)) return deco @timer def test1(): time.sleep(3) print("in the test1") @timer def test2(name): time.sleep(3) print("in the test2 %s"% name) test1() test2("python")
优化后的装饰器:
import time def timer(func): def deco(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) stop_time = time.time() print("the func run time is %s" % (stop_time - start_time)) return res return deco @timer def test1(): time.sleep(1) print("in the test1") @timer def test2(name): time.sleep(2) print("in the test2 %s"% name) @timer def test3(name, age): time.sleep(3) print("in the test3 %s"% name) return age+1 test1() test2("python") #test3("某人飞",999) print(test3("某人飞",999))
加强版装饰器:
# 加强版装饰器 usern = "fjf" passwd = "123456" def dl(func,*args, **kwargs): username = input("请输入您的账号") password = input("请输入您的密码") if username == usern and password == passwd: print("登录成功") res = func(*args, **kwargs) return res else: print("您的账号或者密码错误") def login(auth_type): def wrapper_inout(func): def wrapper(*args, **kwargs): if auth_type == "loca": print("通过loca验证") dl(func, *args, **kwargs) elif auth_type == "loca1": print("通过loca1验证") dl(func, *args, **kwargs) elif auth_type == "ip": print("通过ip验证") dl(func, *args, **kwargs) else: print("暂不支持其他登录方式") return wrapper return wrapper_inout @login(auth_type="loca") def qq_user(): print("欢迎使用qq") @login(auth_type="loca1") def wx_user(name): print("欢迎%s使用微信"%name) @login(auth_type="ip") def wb_user(name, age): print("欢迎%s使用微博"%name) return age >= 18 @login(auth_type="smdx") def ys(): pass ys() qq_user() wx_user("某人飞") print(wb_user("某人飞", 17))
""" 闭包中的拓展知识点: 导入functools模块: 1.@functools.wraps(func):在闭包中将原函数func的文件名、注释文档等,传递给inner函数。 2.func.__name__:获取函数的名字(字符串), 比如:func 这是一个字符串,并不是一个func函数的地址 3.func.__doc__:获取函数的注释文档 """ import functools def auth(func): @functools.wraps(func) # inner.__name=func.__name inner.__doc__=func.__doc__ def inner(*args, **kwargs): """这个是inner函数的注释文档""" # 在原函数func执行之前添加的功能 res = func(*args, **kwargs) # 对接收到的参数进行拆包,传递给原函数func() # 在原函数func执行之之后添加的功能 return res # 将原函数func的返回值返回给调用者 return inner @auth def func(): """这个是func函数的注释文档""" print("this is func") return "func" print(func.__name__) # 原结果:inner 加上代码 @functools.wraps(func) 后结果为:func print(func.__doc__) # 原结果:这个是inner函数的注释文档 加上代码 @functools.wraps(func) 后结果为:这个是func函数的注释文档