装饰器
一、介绍
- 器:代表函数的意思。装饰器本质就是是函数
- 功能:装饰其他函数,就是为其他函数添加附加功能
- 被装饰函数感受不到装饰器的存在
-
原则:
不能修改被装饰的函数的源代码(比如线上环境)
不能修改被装饰的函数的调用方式
-
实现装饰器知识储备:
函数即是“变量”
高阶函数
嵌套函数
高阶函数+嵌套函数=>装饰器
二、通过高阶函数+嵌套函数==>实现装饰器
先分析以下两段代码能不能运行?
1
2
3
4
5
6
7
|
def foo(): print ( "in the foo" ) bar() def bar(): print ( "in the bar" ) foo() |
1
2
3
4
5
6
|
def foo(): print ( "in the foo" ) bar() foo() def bar(): print ( "in the bar" ) |
第二段代码报错:
NameError: name 'bar' is not defined
1、变量知识回顾
定义变量:
如:定义变量:x=1,会在内存中找块内存空间把“1”存进去,把“1”的内存地址给x
前面提到:函数即变量
1
2
3
4
5
6
7
|
# 定义函数 def test(): pass # 就相当于把函数体赋值给test变量 test = '函数体' # 函数体就是一堆字符串而已 # 只不过函数调用要加上小括号调用 test() |
python内存回收机制,是解释器做的。解释器到底怎么去回收这个变量?
python解释器当中有种概念叫做引用计数。什么叫引用计数呢?
比如:定义x=1,之后又定义了y=1或y=x,实际上又把内存空间“1”的内存地址赋值给y
这里x代表一次引用,y代表一次引用。加起来两次引用。
python什么时候会把“1”这个内存空间清空呢?会回收内存呢?
当x这个变量没有了,y这个变量也没有了,便会把“1”这个内存空间清掉
1
|
del x # 删的只是变量名,内存中的值是解释器回收 |
匿名函数
1
|
lambda x:x * x |
匿名函数没有函数名,没有引用,所以会被垃圾回收机制立马回收掉。
所以匿名函数要赋值给变量,把函数体赋值给变量名
1
2
|
calc = lambda x:x*x print(calc(4)) |
现在可以再理解下最开始两段代码能不能运行的原因。
2、高阶函数(装饰器前奏)
什么叫高阶函数呢:
- 把一个函数名当做形实传给另外一个函数
- 返回值中包含函数名
1
2
3
4
5
|
def f1(): print ( "in the func1" ) def test1(func): print (func) test1(f1) |
运行结果(打印内存地址)
<function func1 at 0x000002805DE12378>
如下代码,能不能运行:
1
2
3
4
5
6
|
def f1(): print ( "in the func1" ) def test1(func): print (func) func() test1(f1) |
函数即变量,像“x=1,y=x”,同样f是一个是一个函数,可不可以像一个变量一样来回赋值呢?
1
2
3
4
5
6
7
8
9
10
|
import time def func1(): print ( "in the func1" ) time.sleep( 1 ) def test1(func): start_time = time.time() func() stop_time = time.time() print ( "the func run time is %s" % (stop_time - start_time)) test1(func1) |
到这里,貌似实现了装饰函数的功能。
看上面装饰器的原则:
这里:没有修改func1的源代码,但是调用方式改变了。现在是test1(func1),之前是func1()
现在能做到哪一点呢?
把一个函数名当做实参传给另外一个函数(不修改被装饰的函数源代码的情况下为其添加功能)
2) 下面用第二个条件(返回值中包含函数名),做另外一个高阶函数
1
2
3
4
5
6
7
8
|
import time def func2(): time.sleep( 1 ) print ( "in the func2" ) def test2(func): print (func) return (func) print (test2(func2)) |
运行结果:
<function func2 at 0x00000162F3672378>
<function func2 at 0x00000162F3672378>
把函数内存地址都打印出来了,看到这么多内存地址,有什么想法?
加上小括号就能运行。
上面代码“test2(func2())”和“test2(func2)”有什么区别?加上小括号是函数返回结果,不加是函数内存地址。所以加上小括号就不符合高阶函数定义了。
既然以后有了函数的内存地址,是不是可以赋值给其他变量?下面
1
2
3
4
5
6
7
8
9
10
|
import time def func2(): print ( "in the func2" ) time.sleep( 1 ) def test2(func): print (func) return (func) t = test2(func2) print (t) t() |
好像还没什么用,怎么让他有用呢?
把test2(func2)赋值给func2
1
2
3
4
5
6
7
8
9
|
import time def func2(): print ( "in the func2" ) time.sleep( 1 ) def test2(func): print (func) return (func) func2 = (test2(func2)) func2() |
这就是高阶函数的第二个好处:返回值中包含函数名(不修改函数的调用方式)
3、嵌套函数(装饰器前戏)
嵌套函数:在一个函数体内,用def去声明一个函数
1
2
3
4
5
6
|
def foo(): print ( "in the foo" ) def bar(): print ( "in the bar" ) bar() foo() |
看一下下面的代码是不是嵌套:
1
2
3
4
5
|
def foo(): print ( "in the foo" ) def bar(): foo() bar() |
注意函数嵌套和函数调用区别
局部作用域和全局作用域的访问顺序:
1
2
3
4
5
6
7
8
9
10
11
|
x = 0 def grandpa(): # x = 1 def dad(): x = 2 def son(): x = 3 print (x) son() dad() grandpa() |
三、装饰器
1、装饰器
前面铺垫了那么多,现在开讲正题:装饰器
先用高阶函数实现给函数不修改源代码的情况下添加功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import time def deco(func): start_time = time.time() func() stop_time = time.time() print ( "the func tun time is %s" % (stop_time - start_time)) def test1(): time.sleep( 1 ) print ( "in the test1" ) def test2(): time.sleep( 1 ) print ( "in the test2" ) deco(test1) deco(test2) |
按照上面说的,如何实现不改变调用方式?直接“test1 = deco(test1)”和“test2 = deco(test2)”吗?
别忘记了,第二种方式,高阶函数要加上return,如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import time def deco(func): start_time = time.time() return func() stop_time = time.time() print ( "the func tun time is %s" % (stop_time - start_time)) def test1(): time.sleep( 1 ) print ( "in the test1" ) def test2(): time.sleep( 1 ) print ( "in the test2" ) test1 = deco(test1) test2 = deco(test2) deco(test1) deco(test2) |
虽然没有修改源代码和调用方式,但是函数加上return,函数就结束了,然并卵。怎么实现呢?
前面一直在用高阶函数,还没有用嵌套函数,加上嵌套函数能不能实现呢?看一下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import time def timer(func): # timer(test1) func=test1 def deco(): start_time = time.time() func() stop_time = time.time() print ( "the func tun time is %s" % (stop_time - start_time)) return deco # 返回deco的内存地址 def test1(): time.sleep( 1 ) print ( "in the test1" ) def test2(): time.sleep( 1 ) print ( "in the test2" ) print (timer(test1)) # 可见:返回deco的内存地址 test1 = timer(test1) test1() timer(test2)() |
到此,完成实现了装饰器的功能。但是还是有点麻烦,如何能不要“test1 = timer(test1)”,
python解释器提供了语法糖“@”符合,给哪个函数新增功能,就加在哪个函数头部
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import time def timer(func): # timer(test1) func=test1 def deco(): start_time = time.time() func() stop_time = time.time() print("the func tun time is %s" %(stop_time-start_time)) return deco # 返回deco的内存地址 @timer def test1(): time.sleep(1) print("in the test1") @timer def test2(): time.sleep(1) print("in the test2") test1() test2() |
2、有参装饰器
前面实现了装饰器的功能,但是如果函数有参数,能不能也能运行呢
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import time def timer(func): # timer(test1) func=test1 def deco(): start_time = time.time() func() stop_time = time.time() print ( "the func tun time is %s" % (stop_time - start_time)) return deco # 返回deco的内存地址 @timer def test1(): time.sleep( 1 ) print ( "in the test1" ) @timer def test2(name): time.sleep( 1 ) print ( "in the test2" ,name) test1() test2() |
报错:丢失参数
TypeError: test2() missing 1 required positional argument: 'name'
@timer 相当于 test2=timer(test2) =deco
test2() 相当于运行deco(),所以没指定参数,报错。
如何传参数呢?为了适应各种不同参数的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import time def timer(func): # timer(test1) func=test1 def deco( * args, * * kwargs): start_time = time.time() func( * args, * * kwargs) stop_time = time.time() print ( "the func tun time is %s" % (stop_time - start_time)) return deco # 返回deco的内存地址 @timer def test1(): time.sleep( 1 ) print ( "in the test1" ) @timer def test2(name): time.sleep( 1 ) print ( "in the test2" ,name) test1() test2( "fgf" ) |
3、终极装饰器
注意,上面的例子中还没有涉及返回值,看下面的例子可以体会一下
假设:公司网站需要验证登录,有不同的验证方式:本地认证、LDAP认证等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
#/usr/bin/env python # -*- coding: UTF-8 -*- import time user,passwd = 'fgf' , 'abc123' def auth(auth_type): print ( "auth func:" ,auth_type) def outer_wrapper(func): def wrapper( * args, * * kwargs): print ( "wrapper func args:" , * args, * * kwargs) if auth_type = = "local" : username = input ( "Username:" ).strip() password = input ( "Password:" ).strip() if user = = username and passwd = = password: print ( "\033[32;1mUser has passed authentication\033[0m" ) res = func( * args, * * kwargs) # from home print ( "---after authenticaion " ) return res else : exit( "\033[31;1mInvalid username or password\033[0m" ) elif auth_type = = "ldap" : print ( "搞毛线ldap,不会。。。。" ) return wrapper return outer_wrapper def index(): print ( "welcome to index page" ) @auth (auth_type = "local" ) # home = wrapper() def home(): print ( "welcome to home page" ) return "from home" @auth (auth_type = "ldap" ) def bbs(): print ( "welcome to bbs page" ) index() print (home()) #wrapper() bbs() |
到此这篇关于Python装饰器详细讲解的文章就介绍到这了,更多相关Python装饰器内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!
原文链接:https://www.cnblogs.com/leixiaobai/articles/8031391.html