首页 > 代码库 > python--装饰器详解

python--装饰器详解

Python---装饰器详解

定义:

本质上是一个函数。作用是用来装饰另一个函数(即被装饰函数),给被装饰函数添加功能。前提是不能改变被装饰函数的源代码和调用方式。这样的一个函数称之为装饰器。

解析:

下面我们话不多说,直接用代码说明。下面是一个函数。

 

1 def add():
2     b=1+2
3     print(b
4
5 add()

 

程序输出:

————————

3

————————

现在我要给这个函数增加一个解释性的句子,如下,我们可以编写一个装饰器:

 1 #原函数
 2 def add():
 3     a=1+2
 4     print(a)  
 5 #装饰器
 6 def decorator(func):
 7     def warpper():
 8         print("1+2的结果是:")
 9         func()
10     return warpper
11 #注意此句   
12 add=decorator(add)
13 #调用函数
14 add()

程序输出:

——————————

1+2的结果是:

3

——————————

这样我们就成功的达成了我们的目的。这里要注意第12行的这一句,这一句是将add这个函数对象传入了decorator()函数,返回的是一个新函数变量,这个新函数对象又重新赋值给add,这样就可以保证不改变被装饰函数的调用方式不变。在Python语法中有一种更优雅的方式可以代替第十二行的语句。如下:

 

 1  #装饰器
 2 def decorator(func):
 3     def warpper():
 4         print("1+2的结果是:")
 5         func()
 6     return warpper
 7 
 8 #add=decorator(add)
 9 #原函数
10 @decorator#换成@符号
11 def add():
12     a=1+2
13     print(a)
14 #调用函数
15 add()

 

在被装饰函数前面直接加上“@xxx”(xxx为装饰器函数名)即可

被装饰函数有参数怎么办

如果被装饰器函数有参数呢?该怎们班?不用担心,我们可以用不定参数的形式来收集参数。实例代码如下:

 1 def decorator(func):
 2     def warpper(*args,**kwargs):
 3         print("相加的结果是:")
 4         func(*args,**kwargs)
 5     return warpper
 6 
 7 @decorator
 8 def add(x,y):
 9     a=x+y
10     print(a)
11 
12 add(2,3)
程序输出:
——————————————————
相加的结果是:
5
——————————————————

如上,我们给包装函数加上接收参数,然后传给func()函数就行了。这样不管被装饰函数有怎样的参数都不怕了。

 

下面写一个页面验证的装饰器。

大家知道有些网站的一部分页面是要求用户登录之后才可以访问的,比如下面的三个函数(分别代表三个页面):

1 def index():
2     print("welcome to the index page")
3 def home():
4     print("welcome to the home page")
5 def bbs():
6     print("welcome to the bbs page")
7     return "I am the return contents"

假如说现在我们要给home页面和bbs页面加上验证,显然现在更改源代码是不可行的。这个时候我们可以用装饰器,如下:

 1 username,passwd="jack","abc123"#模拟一个已登录用户
 2 def decorator(func):
 3     def warpper(*args,**kwargs):
 4         Username=input("Username:").strip()
 5         password=input("Password:").strip()
 6         if username==Username and passwd==password:
 7             print("Authenticate Success!")
 8             func(*args,**kwargs)
 9         else:
10             exit("Username or password is invalid!")
11     return warpper
12 
13 def index():
14     print("welcome to the index page")
15 @decorator
16 def home():
17     print("welcome to the home page")
18 @decorator
19 def bbs():
20     print("welcome to the bbs page")
21     return "I am the return contents"
22 
23 index()
24 home()
25 bbs()

 

程序结果:

————————

welcome to the index page    #index页面未验证直接可以登入
Username:jack
Password:abc123
Authenticate Success!           #登录的而情形
welcome to the home page
Username:jack                      #密码或用户名错误的情形
Password:123
Username or password is invalid!
————————

我们注意到bbs()是有返回值的,如果我们把上述代码的最后一句(第25行)改为“print(bbs())”之后再看看他的输出结果:

————————

welcome to the index page
Username:jack
Password:abc123
Authenticate Success!
welcome to the home page
Username:jack
Password:abc123
Authenticate Success!
welcome to the bbs page
None                              #返回值能么成None了???

————————

What happened! bbs()的返回值打印出来竟然是None。怎么会这样?这样的话不就改变了被装饰函数的源代码了吗?怎样才能解决呢?

我们来分析一下:

我们执行bbs函数其实就相当于执行了装饰器里的wrapper函数,仔细分析装饰器发现wrapper函数却没有返回值,所以为了让他可以正确保证被装饰函数的返回值可以正确返回,那么需要对装饰器进行修改:

1 username,passwd="jack","abc123"#模拟一个已登录用户
 2 def decorator(func):
 3     def warpper(*args,**kwargs):
 4         Username=input("Username:").strip()
 5         password=input("Password:").strip()
 6         if username==Username and passwd==password:
 7             print("Authenticate Success!")
 8            return func(*args,**kwargs)#在这里加一个return就行了
 9         else:
10             exit("Username or password is invalid!")
11     return warpper
12 
13 def index():
14     print("welcome to the index page")
15 @decorator
16 def home():
17     print("welcome to the home page")
18 @decorator
19 def bbs():
20     print("welcome to the bbs page")
21     return "I am the return contents"
22 
23 index()
24 home()
25 bbs()

如图加上第8行的return就可以解决了。下面我们在看看改后的程序输出:

————————

welcome to the index page
Username:jack
Password:abc123
Authenticate Success!
welcome to the home page
Username:jack
Password:abc123
Authenticate Success!
welcome to the bbs page
I am the return contents   #bbs()的返回值得到了正确的返回

——-——————

好了,返回值的问题解决了.

 

既然装饰器是一个函数,那装饰器可以有参数吗?

答案是肯定的。我们同样可以给装饰器加上参数。比如还是上面的三个页面函数作为例子,我们可以根据不同页面的验证方式来给程序不同的验证,而这个验证方式可以以装饰器的参数传入,这样我们就得在装饰器上在嵌套一层函数 了:

 1 username,passwd="jack","abc123"#模拟一个已登录用户
 2 def decorator(auth_type):
 3     def out_warpper(func):
 4         def warpper(*args,**kwargs):
 5             Username=input("Username:").strip()
 6             password=input("Password:").strip()
 7             if auth_type=="local":
 8                 if username==Username and passwd==password:
 9                     print("Authenticate Success!")
10                     return func(*args,**kwargs)
11                 else:
12                     exit("Username or password is invalid!")
13             elif auth_type=="unlocal":
14                 print("HERE IS UNLOCAL AUTHENTICATE WAYS")
15         return warpper
16     return out_warpper
17 
18 def index():
19     print("welcome to the index page")
20 @decorator(auth_type="local")
21 def home():
22     print("welcome to the home page")
23 @decorator(auth_type="unlocal")
24 def bbs():
25     print("welcome to the bbs page")
26     return "I am the return contents"
27 
28 index()
29 home()
30 bbs()

 

输出:

————————

welcome to the index page
Username:jack
Password:abc123
Authenticate Success!
welcome to the home page
Username:jack
Password:abc123
HERE IS UNLOCAL AUTHENTICATE WAYS

————————

可见,程序分别加入了第2行和第16行和中间的根据auth_type参数的判断的相关内容后, 就解决上述问题了。对于上面的这一个三层嵌套的相关逻辑,大家可以在 pycharm里头加上断点,逐步调试,便可发现其中的道理。

 

总结

要想学好迭代器就必须理解一下三条:

1.函数即变量(即函数对象的概念)

2.函数嵌套

3.函数式编程

 

 

 

 

作者:彭前超(QQ:3480487308)

时间:2017/6/10 23:09

转载请注明出处,谢谢合作。本人另有Python视频教程,想要的qq联系,非诚勿扰.

 

 

 

python--装饰器详解