Python入门之装饰器
装饰器
通俗点讲,装饰器就是在不改变原函数代码的前提下,为已经存在的函数添加额外的功能。装饰模式有很多经典的使用场景,例如插入日志、性能测试、事务处理等等,有了装饰器,就可以提取大量函数中与本身功能无关的类似代码,从而达到代码重用的目的。
因为刚学习编程,所以下面就简单写了个文件访问的函数;然后在不修改原函数前提下,通过装饰器来进一步实现文件访问的额外功能
#!/usr/bin/env python3 import os import sys def Read_File(fpath): with open(fpath,"r") as f: for line in f: print(line.strip()) Read_File(sys.argv[1]) 执行结果: [root@localhost tmp]# ./read_file.py test_file CD_BOOK hello world! hello linux! hello python!
接着我想在访问文件前增加用户认证功能(刚接触python,我这里只是简单的做了下认证);有人说可以直接在原函数中添加用户认证,不否认这是可行的。但是在实际中最好不要修改已开发好的函数代码,所以这里就可以通过装饰器来实现了!
还有一个原因就是,如果我的访问文件函数有很多个,那岂不是要在所有的函数中复制黏贴用户认证代码,这样就会造成好多冗余代码,代码会越来越不好维护。
下面就是个简单的的装饰器,不过有点特殊的事,被装饰的函数带参数而已;而简单的装饰器不带参数,其他写法都一样。
#!/usr/bin/env python3 import os import sys File_Passwd = "/tmp/file_passwd" #存放用户信息文件 def Is_User(): user = input("请输入用户名:") if os.path.isfile(File_Passwd): #判断用户信息文件是否存在 with open(File_Passwd,"r") as f: for line in f: if user == line.strip().split()[0]: #判断用户是否存在 passwd = input("请输入密码:") Num = 0 while Num < 3: if passwd == line.strip().split()[1]: #判断用户密码是否正确,会判断三次,若三次还没成功,就会退出程序 print("欢迎{}使用CD_BOOK加密!".format(user)) #用户认证成功后会退出循环 break #退出while循环 else: passwd = input("重新输入密码:") Num += 1 else: sys.exit("输出次数太对,请联系管理员!") break #退出for循环 else: continue else: sys.exit("用户不存在!") else: #若用户信息文件不存在,此时会创建一个并会自动添加一个用户admin,密码也是admin;y用户文件格式用户名,tab键,密码 with open(File_Passwd,"w") as f: f.write("admin\tadmin\n") sys.exit("用户不存在!") def Access_Limit(Func): #装饰器函数 def wrapper(arg): Is_User() #认证函数 Func(arg) return wrapper @Access_Limit #装饰器语法糖,@语法糖精简装饰器代码,
#@Access_Limit相当于
#Read_File = Access_Limit(Read_File)
#Read_File(fpath) def Read_File(fpath): with open(fpath,"r") as f: for line in f: print(line.strip()) Read_File(sys.argv[1]) 执行结果: [root@localhost tmp]# ./read_filebk.py test_file 请输入用户名:admin 请输入密码:admin 欢迎admin使用CD_BOOK加密! CD_BOOK hello world! hello linux! hello python!
特殊的装饰器:带参数的装饰器;装饰器带参数可以控制装饰器内部的一些流程操作;比如下面我想通过装饰器的参数来控制是否启用用户认证的文件访问
#!/usr/bin/env python3 import os import sys File_Passwd = "/tmp/file_passwd" def Is_User(): user = input("请输入用户名:") if os.path.isfile(File_Passwd): with open(File_Passwd,"r") as f: for line in f: if user == line.strip().split()[0]: passwd = input("请输入密码:") Num = 0 while Num < 3: if passwd == line.strip().split()[1]: print("欢迎{}使用CD_BOOK加密!".format(user)) break else: passwd = input("重新输入密码:") Num += 1 else: sys.exit("输出次数太对,请联系管理员!") break else: continue else: sys.exit("用户不存在!") else: with open(File_Passwd,"w") as f: f.write("admin\tadmin\n") sys.exit("用户不存在!") def Access_Limit(status = True): #装饰器本身含有函数 if status: #通过装饰器的函数判断是否执行 def _Access_Limit(Read_Func): #内嵌真正要执行的装饰器函数 def wrapper(arg): Is_User() Read_Func(arg) return wrapper else: def _Access_Limit(Read_Func): return Read_Func #直接返回函数本身,而不是调用函数 return _Access_Limit #返回内嵌装饰器函数 @Access_Limit(True)
#此处@Access_Limit(True)相当于
#Read_File = Access_Limit(True)
#Read_File(fpath) def Read_File1(fpath): with open(fpath,"r") as f: for line in f: print(line.strip()) @Access_Limit(False)
#此处@Access_Limit(False)
#Read_File = Access_Limit(False)
#Read_File(fpath) def Read_File2(fpath): with open(fpath,"r") as f: for line in f: print(line.strip()) print("需要密码访问的文件!") Read_File1(sys.argv[1]) print("\n\n不需要密码就能访问文件!") Read_File2(sys.argv[1]) 执行结果: [root@localhost tmp]# ./read_filebk.py test_file 需要密码访问的文件! 请输入用户名:admin 请输入密码:admin 欢迎admin使用CD_BOOK加密! CD_BOOK hello world! hello linux! hello python! 不需要密码就能访问文件! CD_BOOK hello world! hello linux! hello python!
通过例子可以看到,如果装饰器本身需要支持参数,那么装饰器就需要多一层的内嵌函数。
如果有多个装饰器的话,那调用顺序是怎样的呢!对于Python中的”@”语法糖,装饰器的调用顺序与使用 @ 语法糖声明的顺序相反。
#!/usr/bin/env python3 def Deco1_Print(Func): #print("Func is Deco1_Print!") def wrapper(): print("Func is Deco1_Print!") return Func() return wrapper def Deco2_Print(Func): #print("Func is Deco2_Print!") def wrapper(): print("Func is Deco2_Print!") return Func() return wrapper @Deco1_Print @Deco2_Print #2个@相当于 My_Print = Deco1_Print(Deco2_Print(My_Print)) def My_Print(): print("This is test!My_Print") My_Print() #执行My_Print()即 Deco1_Print(Deco2_Print(My_Print()))
python中的装饰器介绍到此结束,如果有不对之处还请大家多多指出以便改进,谢谢!
参考:http://python.jobbole.com/82344/