引言
以前你有没有这样一段经历:很久之前你写过一个函数,现在你突然有了个想法就是你想看看,以前那个函数在你数据集上的运行时间是多少,这时候你可以修改之前代码为它加上计时的功能,
但是这样的话是不是还要大体读读你之前的这个的代码,稍微搞清楚一点它的逻辑,才敢给它添加新的东西。这样是不是很繁琐,要是你之前写的代码足够乱足够长,再去读它是不是很抓狂...。
实际工作中,我们常常会遇到这样的场景,可能你的需求还不只是这么简单。那么有没有一种可以不对源码做任何修改,并且可以很好的实现你所有需求的手段呢?答案当然是有,这就是今天我
们要介绍的python装饰器。有了装饰器,你除了不用担心前面提到的问题,并且还可以很好的处理接下来要做的事:那就是现在你又有了一个新的需求,比如为另一个函数添加计时功能,这时就
非常简单了,把要装饰的函数丢给装饰器就好了,它会自动给你添加完功能并返回给你。是不是很神奇?下面我们将一层层剥开它的神秘面纱。
闭包函数
在看装饰器之前,我们先来搞清楚什么是闭包函数。python是一种面向对象的编程语言,在python中一切皆对象,这样就使得变量所拥有的属性,函数也同样拥有。这样我们就可以理解在函数内创建一个函数的行为是完全合法的。这种函数被叫做内嵌函数,这种函数只可以在外部函数的作用域内被正常调用,在外部函数的作用域之外调用会报错,例如:
而如果内部函数里引用了外部函数里定义的对象(甚至是外层之外,但不是全局变量),那么此时内部函数就被称为闭包函数。闭包函数所引用的外部定义的变量被叫做自由变量。闭包从语法上看非常简单,但是却有强大的作用。闭包可以将其自己的代码和作用域以及外部函数的作用结合在一起。下面给出一个简单的闭包的例子:
def count(): a = 1 b = 1 def sum(): c = 1 return a + c # a - 自由变量 return sum
总结:什么函数可以被称为闭包函数呢?主要是满足两点:函数内部定义的函数;引用了外部变量但非全局变量。
装饰器定义及原则
有了闭包函数的概念,我们再去理解装饰器会相对容易一些。python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象(函数的指针)。装饰器函数的外部函数传入我要装饰的函数名字,返回经过修饰后函数的名字;内层函数(闭包)负责修饰被修饰函数。从上面这段描述中我们需要记住装饰器的几点属性,以便后面能更好的理解:
实质: 是一个函数
参数:是你要装饰的函数名(并非函数调用)
返回:是装饰完的函数名(也非函数调用)
作用:为已经存在的对象添加额外的功能
特点:不需要对对象做任何的代码上的变动
定义
装饰器本质还是一个函数,它是用来对函数函数进行装饰的函数,在不改变原来函数调用的基础上给原来的函数添加新的功能
原则:开放封闭原则
开放:对于原来的函数没有的功能(要添加的新的功能是开放的)
为什么要对扩展开放呢?
任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。
封闭:对于原来的函数已经实现的功能是封闭的(不能改变原来的函数)
为什么要对修改封闭呢?
就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对已经写好的函数进行了修改,很有可能影响其他已经在使用该函数的用户。
装饰器完美的遵循了这个开放封闭原则。
装饰器的主要功能及固定框架
装饰器的主要功能
在不改变原函数调用方式的基础上在原函数的前、后添加新的功能
装饰器的固定框架
def timer(func): def inner(*args,**kwargs): '''执行函数之前要做的''' re = func(*args,**kwargs) '''执行函数之后要做的''' return re return inner
语法糖
介绍
语法糖(Syntactic sugar),是由Peter J. Landin(和图灵一样的天才人物,是他最先发现了Lambda演算,由此而创立了函数式编程)创造的一个词语,它意指那些没有给计算机语言添加新功能,而只是对人类来说更“甜蜜”的语法。语法糖往往给程序员提供了更实用的编码方式,有益于更好的编码风格,更易读。不过其并没有给语言添加什么新东西。 举个例子:在C语言里用a[i]表示*(a+i),用a[i][j]表示*(*(a+i)+j),看来语法糖不是“现代语言”才有啊,连我们的老大哥C也有,而且这种写法简洁明了,也更好懂了。 实际上从面向过程到面向对象也是一种语法糖啊,C语言可以通过它的指针、类型转换,结构实现面向对象的编程风格,但是C++更进一步的推广了这种风格,更好用了,不过到了C#把OO的风格发挥得淋漓尽致。OO的编程风格对于面向过程来说是不是一种语法糖呢?如果生硬地照此理解,只有计算机硬件指令才不算语法糖,而其他一切利用编译器、汇编器将代码抽象,和自然语言更相近的手段都算语法糖。 现在很多很多编程思想,编程理论层出不穷,当然,对于学习来说我们是要抓住技术的核心,但对于工程来说如何提高工程质量,如何提高工程效率也是我们要关注的,既然这些语法糖能辅助我们以更好的方式编写代码为什么要“抵制“呢? 我想语法糖和其他编程思想一样重要,什么duck type,人本接口,最小接口,约定优于配置,其实不都是一些思想上的“语法糖“? 不过也并不是没有反对的声音,这其中就有图灵奖的第一个获得者: Alan Perlis。.net从2.0到3.0到3.5底层CLR并没有做更新,没有添加任何新的IL指令,所以C#从2.0到3.0中所有的特性都是语法糖,就算从1.0到2.0,也只有一个泛型不是语法糖,其余的新特性也都是如此,但是每一次C#发布新特性的时候我们不得不为之而鼓舞,为之而喝彩。新的语法可以酿造更好的编程风格,以前一些难以实现的方面现在也轻而易举了。 需要声明的是“语法糖”这个词绝非贬义词,它可以给我带来方便,是一种便捷的写法,编译器会帮我们做转换;而且可以提高开发编码的效率,在性能上也不会带来损失。
语法糖的作用
语法糖往往给程序员提供了更实用的编码方式,有益于更好的编码风格,更易读。不过其并没有给语言添加什么新东西。
语法糖示例:
1 import time 2 def wrapper(func): 3 def inner(): 4 start=time.time() 5 func() 6 end=time.time() 7 print(end-start) 8 return inner 9 @wrapper 10 def kkk():#相当于kkk=wrapper(kkk) 11 print('aaaaa') 12 kkk()
带参数的装饰器
假如你有成千上万个函数使用了一个装饰器,现在你想把这些装饰器都取消掉,你要怎么做?
一个一个的取消掉? 没日没夜忙活3天。。。
过两天你领导想通了,再让你加上又得忙乎几天超级麻烦啦 可不可以一次性搞定呢?答案是肯定的怎么做呢 ,仔细看下面的代码哦
def outer(flag): def timer(func): def inner(*args,**kwargs): if flag: print('''执行函数之前要做的''') re = func(*args,**kwargs) if flag: print('''执行函数之后要做的''') return re return inner return timer @outer(False) def func(): print(111) func()
多个装饰器装饰一个函数
有些时候,我们也会用到多个装饰器装饰同一个函数的情况
def wrapper1(func): def inner(): print('wrapper1 ,before func') func() print('wrapper1 ,after func') return inner def wrapper2(func): def inner(): print('wrapper2 ,before func') func() print('wrapper2 ,after func') return inner @wrapper2 @wrapper1 def f(): print('in f') f()
装饰器的具体应用
1.定义无参装饰器函数为被装饰器添加统计运行时间的功能
#定义闭包无参函数,为程序增加统计时间功能 import time def timer(func): #定义timer函数,func变量值为login def wrapper(): start_time=time.time() #设置函数起始时间 func() #函数名加()调用函数,即调用login(),然后调用结束后继续在wrapper函数中运行 stop_time=time.time() print("run time is %s"%(stop_time-start_time)) return wrapper #返回wrapper函数名,然后再次进入wrapper函数 @timer #相当于timer(login)-->赋值给timer函数名 def login(): #读取注册用户的信息,用户名,密码,输错次数,写入字典中 user={} with open("DB1",encoding="utf8") as f: for line in f: username_list=line.strip().split("|") #username_list--->['egon', '123', '2'] user[username_list[0]]={"name":username_list[0], "pwd":username_list[1], "times":username_list[2]} # print(user) #-->{'egon': {'name': 'egon', 'pwd': '123', 'times': '2'}, 'xuyaping': {'name': 'xuyaping', 'pwd': '123', 'times': '0'}, 'xyy': {'name': 'xyy', 'pwd': '123', 'times': '1'}} #读取黑名单用户,将黑名单用户加入列表中 with open("black_lockname",encoding="utf8") as f1: black_list=[] for line in f1: black_list.append(line.strip()) # print(black_list) while True: username = input("please input your username:").strip() passwd = input("please input your passwd:").strip() #用户在黑名单中 if username in black_list: print("该用户为黑名单用户,请滚") break # 用户为注册用户 elif username in user: user[username]["times"]=int(user[username]["times"]) if user[username]["times"]<3 and passwd==user[username]["pwd"]: print("登录成功") user[username]["times"]=0 #将修改后的信息重新写入DB1中 with open("DB1","w",encoding="utf8") as f3: for i in user: f3.write(i + "|" + user[i]["pwd"] + "|" + str(user[i]["times"]) + " ") break else: user[username]["times"]+=1 print("登录错误") # 将修改后的信息重新写入DB1中 with open("DB1", "w", encoding="utf8") as f3: for i in user: f3.write(i + "|" + user[i]["pwd"] + "|" + str(user[i]["times"]) + " ") if user[username]["times"]==3: black_list.append(username) print("账户被锁定") # 将修改后的信息重新写入black_lockname中 with open("black_lockname","w",encoding="utf8") as f4: for j in black_list: f4.write(j+ " ") break #用户不是注册用户 else: print("该用户没有注册") break login()
2.定义有参装饰器为被装饰器添加认证功能用户的信息可以源于file或者l三次验证失败锁定用户
#定义闭包有参函数,为程序增加验证功能 def auth2(auth_type): def auth(func): #func参数此时被赋值为login def wragger(*args,**kwargs): #wragger函数携带变量auth_type的值 if auth_type=="file": func() #运行函数login elif auth_type=="ldap": print("你他妈还想不想玩了?") return wragger return auth @auth2(auth_type="file") #相当于运行函数auth2(file),返回auth,auth(login)赋值给login函数名 def login(): #读取注册用户的信息,用户名,密码,输错次数,写入字典中 user={} with open("DB1",encoding="utf8") as f: for line in f: username_list=line.strip().split("|") #username_list--->['egon', '123', '2'] user[username_list[0]]={"name":username_list[0], "pwd":username_list[1], "times":username_list[2]} # print(user) #-->{'egon': {'name': 'egon', 'pwd': '123', 'times': '2'}, 'xuyaping': {'name': 'xuyaping', 'pwd': '123', 'times': '0'}, 'xyy': {'name': 'xyy', 'pwd': '123', 'times': '1'}} #读取黑名单用户,将黑名单用户加入列表中 with open("black_lockname",encoding="utf8") as f1: black_list=[] for line in f1: black_list.append(line.strip()) # print(black_list) while True: username = input("please input your username:").strip() passwd = input("please input your passwd:").strip() #用户在黑名单中 if username in black_list: print("该用户为黑名单用户,请滚") break # 用户为注册用户 elif username in user: user[username]["times"]=int(user[username]["times"]) if user[username]["times"]<3 and passwd==user[username]["pwd"]: print("登录成功") user[username]["times"]=0 #将修改后的信息重新写入DB1中 with open("DB1","w",encoding="utf8") as f3: for i in user: f3.write(i + "|" + user[i]["pwd"] + "|" + str(user[i]["times"]) + " ") break else: user[username]["times"]+=1 print("登录错误") # 将修改后的信息重新写入DB1中 with open("DB1", "w", encoding="utf8") as f3: for i in user: f3.write(i + "|" + user[i]["pwd"] + "|" + str(user[i]["times"]) + " ") if user[username]["times"]==3: black_list.append(username) print("账户被锁定") # 将修改后的信息重新写入black_lockname中 with open("black_lockname","w",encoding="utf8") as f4: for j in black_list: f4.write(j+ " ") break #用户不是注册用户 else: print("该用户没有注册") break login()