Python装饰器
2018-04-24 20:18 钱先生 阅读(157) 评论(0) 编辑 收藏 举报装饰器
- 本质是函数
- 功能: 装饰其他函数,即为其他函数添加附加功能
- 装饰器需要遵循的原则:
- 不能修改被装饰的函数
- 例: 现在有三个函数 , 各自有其功能. 如果需要给这三个函数都添加一个日志功能. 或这三个函数都已经在线上运行了, 现在需要再新增一些功能. 可以采取的方法:
- 修改源代码, 每个函数都添加这些功能 (风险非常大)
- 例: 现在有三个函数 , 各自有其功能. 如果需要给这三个函数都添加一个日志功能. 或这三个函数都已经在线上运行了, 现在需要再新增一些功能. 可以采取的方法:
- 不能修改被装饰的函数的调用方式
- 装饰器对被装饰的函数是完全透明的. 即装饰器完全不影响原函数.
- 不能修改被装饰的函数
1 import time 2 def timmer(func): # 装饰器 3 def warpper(*args, **kwargs): 4 start_time = time.time() 5 func() 6 stop_time = time.time() 7 print('the func run time is %s' %(stop_time-start_time)) 8 return warpper 9 10 @timmer 11 def test1(): 12 time.sleep(3) 13 print('test1') 14 15 16 test1()
实现装饰器知识储备:
- 函数即"变量"
-
1 def bar(): 2 print('bar') 3 4 5 def foo(): 6 print('foo') 7 bar() 8 9 10 foo() 11 12 13 print("=========================") 14 15 # 测试函数中引用的函数可以是定义在测试函数前面, 也可以是定义在测试函数后面的. 但一定是在调用函数语句的前面. 16 17 18 def foo1(): 19 print('foo1') 20 bar1() 21 22 23 def bar1(): 24 print('bar1') 25 26 27 foo1()
- 高阶函数
- a: 把一个函数名当做实参传给另外一个函数 (在不修改被装饰函数情况下为其添加功能)
-
1 print("=========== 返回函数内存地址 ===========") 2 3 4 def bar(): 5 print('bar') 6 7 8 def test1(func): 9 print(func) 10 11 12 test1(bar) # 返回一段内存地址 13 14 15 print("=========== 返回函数执行结果 ===========") 16 17 18 # 若想返回函数的执行结果而不是函数的内存地址, 需要这样更改 19 def bar(): 20 print('bar') 21 22 23 def test1(func): 24 print(func) 25 func() 26 27 28 test1(bar) # 返回bar 29 30 31 print("=========== 高阶函数进阶版 ===========") 32 33 34 import time 35 36 37 def bar(): 38 print('bar') 39 40 41 def test1(func): # 实现了装饰器的功能. 运行bar()函数的同时附加了计时功能. 但是改变了函数调用方式,所以这种方法不能称为装饰器. 42 start_time = time.time() 43 func() # run bar 44 stop_time = time.time() 45 print('the func run time is %s' % (stop_time-start_time)) 46 47 48 test1(bar)
-
- 返回值中包含函数名 (不修改函数的调用方式)
-
1 import time 2 3 4 def bar(): 5 time.sleep(3) 6 print('bar') 7 8 9 def test2(func): 10 print(func) # 打印函数func的内存地址 11 return func # 返回函数func的内存地址 12 13 14 ''' 15 test2(bar) 和 test2(bar())的区别: 16 test2(bar) : 17 把函数bar()的内存地址传给函数test2 18 19 test2(bar()) : (不符合高阶函数的定义) 20 把函数bar()的运行结果传给函数test2 21 ''' 22 t = test2(bar) 23 print(t) 24 25 t() # 代表运行函数bar, 等同bar() 26 27 28 print("==========================") 29 30 # 下面这种写法不会报错, 也不会陷入死循环 31 bar = test2(bar) # 用变量bar接收函数test2()的运行结果(实际接收到的是函数bar的内存地址. 所以变量bar后面加()就可以调用函数bar()) 32 bar() # run bar
-
- a: 把一个函数名当做实参传给另外一个函数 (在不修改被装饰函数情况下为其添加功能)
- 嵌套函数
-
1 # 函数嵌套: 在一个函数的函数体内去声明一个新的函数. 2 3 4 def foo(): 5 print('foo') 6 7 def bar(): 8 print('bar') 9 10 bar() 11 12 13 foo() 14 15 16 print("=========== 局部作用域和全局作用域的访问顺序 ===========") 17 x = 0 18 def grandpa(): 19 x = 1 20 def dad(): 21 x=2 22 def son(): 23 x=3 24 print(x) 25 son() 26 dad() # 如果不调用dad()函数, 相当于定义了一个变量, 却没有用这个变量. 27 grandpa() # 返回3
-
- 高阶函数+嵌套函数 ==>装饰器
通用版装饰器
1 #!/usr/bin/python 2 # -*- coding: utf-8 -*- 3 import time 4 5 print("============== 不成功的装饰器,只有高阶函数没有嵌套 ==============") 6 def deco(func): 7 start_time = time.time() 8 return func 9 stop_time = time.time() 10 print('the func run time is %s' % (stop_time-start_time)) 11 12 13 def test1(): 14 time.sleep(3) 15 print('in the test1') 16 17 18 def test2(): 19 time.sleep(3) 20 print('in the test2') 21 22 23 test1 = deco(test1) # 获取函数返回值,将test1的内存地址传给变量test1. 24 print(test1) 25 test2 = deco(test2) 26 print(test2) 27 28 ''' 29 这样这条语句的返回值是in the test1, 装饰器没有发挥作用. 因为deco()函数中return func 后面的语句遇到return并没有执行. 30 ''' 31 32 33 print("============ 成功的装饰器, 高阶函数+嵌套函数(方法1) ============") 34 35 36 def timer(func): 37 def deco(): # 相当于定义了一个变量, 这个变量是个函数 38 start_time = time.time() 39 func() 40 stop_time = time.time() 41 print('the func run time is %s' % (stop_time-start_time)) 42 return deco # 返回函数deco的内存地址 43 44 45 def test1(): 46 time.sleep(3) 47 print('in the test1') 48 49 50 def test2(): 51 time.sleep(3) 52 print('in the test2') 53 54 55 test1 = timer(test1) # 将函数deco的内存地址赋值给变量test1 56 test1() # 执行deco 57 test2 = timer(test2) 58 test2() 59 60 61 print("============ 成功的装饰器, 高阶函数+嵌套函数(方法2) ============") 62 63 64 def timer(func): 65 def deco(): # 相当于定义了一个变量, 这个变量是个函数 66 start_time = time.time() 67 func() 68 stop_time = time.time() 69 print('the func run time is %s' % (stop_time-start_time)) 70 return deco # 返回函数deco的内存地址 71 72 73 @timer # 这个注释其实完成了一个动作: test1 = timer(test1) 74 def test1(): 75 time.sleep(3) 76 print('in the test1') 77 78 79 @timer # 这个注释其实完成了一个动作:test2 = timer(test2) 80 def test2(): 81 time.sleep(3) 82 print('in the test2') 83 84 85 test1() # 执行deco 86 test2() # 执行deco 87 88 ''' 89 注: 上述方法, 若是test2(arg1)函数中有参数, 则会出错. 90 在deco里传参(def deco(arg1))可以解决这个问题, 但是test1又会出错. 91 ''' 92 93 94 print("============ 非固定参数装饰器, 满足有参和无参的函数 (通用装饰器) ============") 95 def timer(func): 96 def deco(*args, **kwargs): 97 start_time = time.time() 98 func(*args, **kwargs) 99 stop_time = time.time() 100 print('the func run time is %s' % (stop_time - start_time)) 101 102 return deco # 返回函数deco的内存地址 103 104 105 @timer # 这个注释其实完成了一个动作: test1 = timer(test1) 106 def test1(): 107 time.sleep(3) 108 print('in the test1') 109 110 111 @timer # 这个注释其实完成了一个动作:test2 = timer(test2) 112 def test2(arg1): 113 time.sleep(3) 114 print('in the test2') 115 116 117 test1() # 执行deco 118 test2("abc") # 执行deco
完善版装饰器
- 处理函数的返回结果
1 #!/usr/bin/python 2 # -*- coding: utf-8 -*- 3 print("=========== 装饰器改变了函数的返回结果. 并没有返回函数自身的返回结果 ===========") 4 user, pwd = 'alex','abc123' 5 def auth(func): 6 def wrapper(*args, **kwargs): 7 username = input("username: ") 8 password = input("password: ") 9 10 if user == username and password == pwd: 11 print("\033[32;1mUser has passed authentication\033[0m") 12 func(*args, **kwargs) # 执行之后有一个Func的返回值,但装饰器并没有处理这个返回值 13 else: 14 exit("\033[32;1mInvalid username or password \033[0m") 15 return wrapper 16 17 18 def index(): 19 print("welcome to index page") 20 return ("from index") 21 @auth 22 def home(): 23 print("welcome to home page") 24 return("from home") 25 @auth 26 def bbs(): 27 print("welcome to bbs page") 28 return("from bbs") 29 30 31 index() 32 home() 33 print(home) 34 bbs() 35 36 37 print("=========== 完善版装饰器, 返回函数自身的返回结果(方法1)===========") 38 user, pwd = 'alex','abc123' 39 def auth(func): 40 def wrapper(*args, **kwargs): 41 username = input("username: ") 42 password = input("password: ") 43 44 if user == username and password == pwd: 45 print("\033[32;1mUser has passed authentication\033[0m") 46 return func(*args, **kwargs) 47 else: 48 exit("\033[32;1mInvalid username or password \033[0m") 49 return wrapper 50 51 52 def index(): 53 print("welcome to index page") 54 return ("from index") 55 @auth 56 def home(): 57 print("welcome to home page") 58 return("from home") 59 @auth 60 def bbs(): 61 print("welcome to bbs page") 62 return("from bbs") 63 64 65 index() 66 home() 67 print(home) 68 bbs() 69 70 71 print("=========== 完善版装饰器, 返回函数自身的返回结果(方法2)===========") 72 user, pwd = 'alex','abc123' 73 def auth(func): 74 def wrapper(*args, **kwargs): 75 username = input("username: ") 76 password = input("password: ") 77 78 if user == username and password == pwd: 79 print("\033[32;1mUser has passed authentication\033[0m") 80 res = func(*args, **kwargs) 81 print("----- after authentication -----") 82 return res 83 else: 84 exit("\033[32;1mInvalid username or password \033[0m") 85 return wrapper 86 87 88 def index(): 89 print("welcome to index page") 90 return ("from index") 91 @auth 92 def home(): 93 print("welcome to home page") 94 return("from home") 95 @auth 96 def bbs(): 97 print("welcome to bbs page") 98 return("from bbs") 99 100 101 index() 102 home() 103 print(home) 104 bbs()
终极版装饰器
- 提供多种认证方式 (用户名密码 /SSL /ldap )
-
1 print("=========== 终极版装饰器, 支持多种认证方式)===========") 2 user, pwd = 'alex','abc123' 3 def auth(auth_type): 4 print("auth func: ", auth_type) 5 def outer_wrapper(func): 6 def wrapper(*args, **kwargs): 7 print("wrapper func args ", *args, **kwargs) 8 if auth_type == "local": 9 username = input("username: ") 10 password = input("password: ") 11 12 if user == username and password == pwd: 13 print("\033[32;1mUser has passed authentication\033[0m") 14 func(*args, **kwargs) # 执行之后有一个Func的返回值,但装饰器并没有处理这个返回值 15 else: 16 exit("\033[32;1mInvalid username or password \033[0m") 17 elif auth_type == "ldap": 18 print('ldap') 19 return wrapper 20 return outer_wrapper 21 22 23 def index(): 24 print("welcome to index page") 25 return ("from index") 26 @auth(auth_type = "local") 27 def home(): 28 print("welcome to home page") 29 return("from home") 30 @auth(auth_type = "ldap") 31 def bbs(): 32 print("welcome to bbs page") 33 return("from bbs") 34 35 36 index() 37 home() 38 print(home) 39 bbs()
\