Python学习--装饰器
装饰器: 本质就是函数,为其他函数添加附加功能
装饰器原则:
1. 不修改被装饰函数的源代码
2. 不修改被装饰函数的调用方式
装饰器=高阶函数 + 函数嵌套 +闭包
1. 高阶函数
定义:
1. 函数接收的参数是一个函数名
2. 函数的返回值是一个函数名
满足上述任意一个条件的函数即为高阶函数
高阶函数示例
1 def foo(): 2 print("hello") 3 4 5 # 函数名作为参数 6 def func_1(func): 7 print("heiheihei") 8 func() 9 print("world") 10 11 12 # 函数名作为返回值 13 def func_2(func): 14 print("return value: %s" % func) 15 return func 16 17 # 函数名作为参数和返回值 18 def func_3(func): 19 print("from func_3") 20 return func 21 22 func_1(foo) 23 print('----------------') 24 func_2(foo) 25 print('----------------') 26 f = func_3(foo) 27 f() # f是func_3的返回值,即为函数foo的函数地址,f()执行函数foo输出“hello” 28 29 >>heiheihei 30 hello 31 world 32 ---------------- 33 return value: <function foo at 0x00000276CAAA3E18> 34 ---------------- 35 from func_3 36 hello
使用高阶函数实现在不改变函数调用方式的前提下,增加函数的功能
1 import time 2 3 # 为foo函数增加函数运行时间计时功能 4 5 def foo(): 6 time.sleep(2) 7 print("function foo running") 8 9 def timer(func): 10 start_time = time.time() 11 func() 12 stop_time = time.time() 13 14 print("running time %s" % (start_time - stop_time)) 15 16 return func 17 18 foo = timer(foo) 19 20 foo() 21 22 >>function foo running 23 running time -2.0002999305725098 24 function foo running 25 26 # 以foo()的形式运行函数foo,实现了不改变函数调用方式运行函数 27 # 但运行过程中多执行了一次foo, 28 # 第一次执行是timer函数中的func() 29 # 第二次执行是timer函数返回了foo函数名,最后运行foo()再次运行了foo原函数
根据以上示例可以得出单纯使用高阶函数无法完全实现在不改变函数调用方式的基础下增加函数的功能
2. 函数嵌套
定义:
在函数里面嵌套定义函数
1 def country(name): 2 print('country is %s' %name) 3 def province(): 4 print('guangdong') 5 def city(): 6 print('shenzhen') 7 city() 8 province() 9 10 country('china') 11 12 >>country is china 13 guangdong 14 shenzhen
3. 闭包
定义:
如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)
1 def country(name): 2 print('country is %s' %name) 3 def province(): 4 print('province is %s' % name) 5 def city(): 6 print('city is %s' % name) 7 city() 8 province() 9 10 country('china')
province()函数就是一个闭包
4. 使用高阶函数+函数嵌套+闭包实现为函数增加功能
import time def timer(func): def inner(): start_time = time.time() func() stop_time = time.time() print("running time is %s" % (stop_time- start_time)) return inner def foo(): print("foo function") time.sleep(2) f = timer(foo) # f就是timer函数的返回值--inner函数的函数指针 f() # f加括号执行f函数 >>foo function running time is 2.000217914581299
上述示例实现了为foo函数增加函数运行时间计时的功能,如果将f改为foo,则实现了不改变函数的调用方式同时增加了函数的功能,如下所示
1 import time 2 3 def timer(func): 4 def inner(): 5 start_time = time.time() 6 func() 7 stop_time = time.time() 8 print("running time is %s" % (stop_time- start_time)) 9 return inner 10 11 def foo(): 12 print("foo function") 13 time.sleep(2) 14 15 foo = timer(foo) 16 17 foo() 18 19 >> foo function 20 running time is 2.000906229019165 21 22 # 最后执行的foo函数相比于最初定义的foo函数,实现了增加函数运行时间计时的功能
到此基本实现了在不改变函数的调用方式以及函数源码的前提下,实现了增加函数功能,但还需要做一步函数赋值的操作,如果其他函数需要怎加同样的功能,还要对其他函数做赋值操作,这是很麻烦的。
使用装饰器可以避免这样的麻烦,只要在添加功能的目标函数前添加一行代码:@功能函数。实现方法如下所示
1 import time 2 3 def timer(func): 4 def inner(): 5 start_time = time.time() 6 func() 7 stop_time = time.time() 8 print("running time is %s" % (stop_time- start_time)) 9 return inner 10 11 @timer 12 def foo(): 13 print("foo function") 14 time.sleep(2) 15 16 foo() 17 18 >>foo function 19 running time is 2.000242233276367
5. 获取被装饰函数的返回值
1 import time 2 3 def timer(func): 4 def inner(): 5 start_time = time.time() 6 ret = func() # 执行func函数得到函数返回值,赋值给ret 7 stop_time = time.time() 8 print("running time is %s" % (stop_time- start_time)) 9 return ret # inner的返回值为ret 10 return inner # timer的返回值为inner函数名 11 12 @timer 13 def foo(): 14 print("foo function") 15 time.sleep(2) 16 return "return value foo" 17 18 ret = foo() 19 # 首先假设foo = A 20 # 执行foo() <==> 首先执行A = timer(foo),然后执行A() 21 # timer(foo)的返回值为inner,即A = inner 22 # 执行A() <==> 执行inner() 23 # 执行inner()过程中执行inner函数体中的所有代码,得到最初定义的foo函数的返回值“return value foo” 24 # 即执行A()得到返回值“return value foo” 25 # ret = A() 将A()的返回值赋值给ret, 即使用ret=foo()可以得到最初定义的foo函数的返回值 26 print(ret) 27 28 >>foo function 29 running time is 2.0001463890075684 30 return value foo
6. 被修饰函数有参数
1 import time 2 3 def timer(func): 4 def inner(*args, **kwargs): 5 start_time = time.time() 6 ret = func(*args, **kwargs) # 执行func函数得到函数返回值,赋值给ret 7 stop_time = time.time() 8 print("running time is %s" % (stop_time- start_time)) 9 return ret # inner的返回值为ret 10 return inner # timer的返回值为inner函数名 11 12 @timer 13 def foo(name): 14 print("usr name is %s" % name) 15 time.sleep(2) 16 return "return value foo" 17 18 ret = foo("Jack") 19 # 首先假设foo = A 20 # 执行foo() <==> 首先执行A = timer(foo),然后执行A() 21 # timer(foo)的返回值为inner,即A = inner 22 # 执行A() <==> 执行inner() 23 # 执行inner()过程中执行inner函数体中的所有代码, 24 # 执行到func函数时,即执行被修饰函数foo(name),foo有参数,则func也需要参数 25 # func的参数需要从A()的参数中获得,即inner的参数就是func的参数 26 # 由于参数是不定的,可以为函数添加参数 *args, **kwargs 27 28 print(ret) 29 30 >> usr name is Jack 31 running time is 2.0003693103790283 32 return value foo
7. 带参数装饰器
1 import time 2 3 def type_check(*args, **kwargs): 4 file_type = kwargs["file_type"] 5 print("file type %s" %file_type) 6 7 def timer(func): 8 def inner(*args, **kwargs): 9 start_time = time.time() 10 ret = func(*args, **kwargs) # 执行func函数得到函数返回值,赋值给ret 11 stop_time = time.time() 12 print("running time is %s" % (stop_time- start_time)) 13 return ret # inner的返回值为ret 14 return inner # timer的返回值为inner函数名 15 16 return timer 17 18 @type_check(file_type = "mysql") 19 def foo(name): 20 print("usr name is %s" % name) 21 time.sleep(2) 22 return "return value foo" 23 24 ret = foo("Jack") 25 # type_check加括号首先执行type_check(),获取type_check的参数 26 # foo <==> A = type_check(), A得到timer返回值 27 # 执行foo() <==> timer() 28 # 相比于无参装饰器,有参装饰器在无参装饰器的基础上套一层函数来传参 29 30 print(ret) 31 32 >>file type mysql 33 usr name is Jack 34 running time is 2.00007963180542 35 return value foo
8. 多个装饰器
1 def outer_0(func): 2 def inner(*args, **kwargs): 3 print('3.5') 4 ret = func(*args, **kwargs) 5 return ret 6 return inner 7 8 def outer(func): 9 def inner(*args, **kwargs): 10 print('123') 11 ret = func(*args, **kwargs) 12 print('456') 13 return ret 14 return inner 15 16 @outer_0 17 @outer 18 def index(a1, a2): 19 print('complex') 20 return a1 + a2 21 22 print(index(1, 2)) 23 24 25 >>3.5 26 >>123 27 >>complex 28 >>456 29 >>3