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

 

posted @ 2017-04-10 19:16  Techml  阅读(146)  评论(0编辑  收藏  举报