一、装饰器
装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能,比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里面每个函数都加一个功能,用来统计每个函数的运行时间是多少,找出来运行比较慢的函数,来优化代码,或者一个功能在调用之前必须登陆,就需要添加一个新的功能,来统计程序的运行时间,那这样的话,就得修改每个函数了,需要改代码,但是代码特别多,改完了公司倒闭了,这时候装饰器就能排上用场了,它可以不改变原有的函数,原来的函数和原来一模一样,什么都不需要改变,只需要在函数外部加上调用哪个装饰器就可以了。so,装饰器的作用就是不改变原来函数的调用方式,不改变原来函数的代码,给它增加了一个新功能。但是不改变函数,给它增加新功能,那是不可能的,装饰器只不过是偷偷改变了原来的函数而已,而原来的函数不知不觉。
说白了就是函数嵌套+高阶函数
作用:就是在不改变原有函数的调用方式,入参的情况下给函数添加新功能
偷偷摸摸的给函数加上新功能,但是不改变原来的函数
使用装饰器需要了解的知识:
*1 函数即变量,意思是在python中函数可以当一个变量来用,函数名就是一个变量,这个变量存的实际上是这个函数的内存地址,在调用的时候从函数名里面这个内存地址找到这个函数。()代表调用函数,如果只打印函数名的话实际上就是打印了一下内存地址。
def test():
print('nihao')
print(test) # 打印函数的内存地址
test() # 调用函数`<function test at 0x0000000002383E18>
*2 高阶和嵌套函数
之前的章节提过,实际上装饰器就是高阶函数和嵌套函数的结合体
下面我们写一个简单的装饰器,用来计算函数的运行时间。
import time
def bar():
time.sleep(3)
print('测试执行时间')
def test_time(func)
start_time = time.time()
func()
stop_time = time.time
print('函数的运行时间是%s'%(stop_time - start_time))
test_time(bar)
运行结果:
测试执行时间
函数的运行时间是3.000171661376953
但是这样的话,我们每次要调用test_time(bar),而不是直接调用bar(),所以就改变了函数的调用方式。
这时我们就想到了装饰器。如果不修改调用代码,也就是意味着调用bar(),需要和test_time(bar)一样的
效果,如果test_time(bar)并咩有直接产生调用函数的效果,而是返回一个函数的内存地址,那么将
test1(bar)的返回值赋值给bar,然后,调用bar()的代码完全不用修改!
import time
def test_time(func):
def deco():
start_time = time.time()
func()
stop_time = time.time()
print('函数的运行时间是%s'%(stop_time - start_time))
return deco
def bar():
time.sleep(3)
bar = test_time(bar) #把deco的内存地址返回给 bar
bar()
运行结果:
in the bar
函数的运行时间是3.000171661376953
函数test_time 其实就是一个装饰器,它把真正的业务方法func包裹在函数里面,看起来就像是bar被test_time装饰了
如果我们想偷懒不适用赋值语句bar = test_time(bar) ,那么我们就可以用到装饰器的语法糖@,语法糖的意思就是一种语法的简写
,他可以使代码看起来更加简洁,上面的bar = test_time(bar) 和@test_time 然后调用bar()效果是一样的。
import time
def test_time(func):
def deco():
start_time = time.time()
func()
stop_time = time.time()
print('函数的运行时间是%s'%(stop_time - start_time))
return deco
@test_time
def bar():
time.sleep(3)
print('in the bar')
运行的结果和上面一样
这样我们就提高了程序的可重复利用性,当其他函数需要调用装饰器时,可以直接调用。装饰器在python使用非常方便,这得益于python的函数
能够像朋友同的对象一样作为参数传递给其他函数,可以赋值给其他变量,可以作为返回值,被定义在另一个函数内。
如果装饰的函数带有参数,因为你也不知道被装饰的函数会传什么参数,所以可以使用可变参数和关键字参数来接收所有的参数,代码如下:
def test_time(func):
def deco(args,**kwargs):
start_time = time.time()
func(args,**kwargs)
stop_time = time.time()
print('函数的运行时间%s'%(stop_time - start_time))
return deco
@test_time
def test2(name,age):
time.sleep(2)
print('in the test2',name,age)
test2('niuhanyang',18)
运行结果:
in the test2: niuniu 18
the func run time is 2.0001144409179688
下面再用装饰器写一个实例,判断用户是否登录,判断的逻辑是这样,运行程序,打印菜单,如果是选择后台管理和添加商品,
就判断用户是否登录,如果没有登陆的话,让用户登陆,如果是查看商品就不需要登陆。
import time,json
def read_file(filename):
with open(filename) as fr:
res = json.load(fr)
return res
login_name = None
def auth(func):
dec(args,**kwargs):
global login_name
if login_name:
res = func(args,kwargs)
return res
else:
user_dic = read_file('users.json')
count = 0
while count ❤️:
username = input('请输入用户名:').strip()
password = input('请输入密码:').strip()
if username !='' and password ! ='':
if username in user_dic:
if user_dic[username]['password'] = passsword
login_name =username
res = func(*args,kwargs)
return res
else:
print('密码不正确!!!')
count +=1
else:
print('用户名不存在!!!')
count +=1
else:
print('用户名和密码都不能为空')
cout+ =1
return dec
下面是事先写好的uses.json
{
"admin": {
"password": "123456",
"cart": [
"mac",
"iphone"
]
},
"duguanglong": {
"password": "123456",
"cart": []
}
}
匿名函数
如果这个函数只需要执行一次,那就定义一个匿名函数,匿名函数只能处理比较简单的处理逻辑,只能写简单的表达式,不能写循环判断,比如三元运算符。
匿名函数使用lambda关键字,比如说要定义一个函数,它的功能是要返回两个数字相加,那就可以使用lambda,代码如下:
s = lambda x,y:x+y #冒号前面的x,y是入参,冒号后面的是返回值
print(s(1,9)) #因为函数即变量,如果没有顶一个变量把lambda存起来的话,他就不在内存里,就没有办法执行,所以把它放到s这个变量里面
s(1,9)
运行结果:10