想要了解装饰器,你首先要了解一个概念-----闭包.装饰器的实现原理和闭包几乎是一模一样的..所以说,不要着急,跟随我的脚步,先跟着我理解闭包..之后直接使用装饰器...
需要具备的知识基础:
- 1.python的多值参数
- 2.python中一切皆对象(包括函数)
python装饰器的用武之地:
:::::::::::::::::::::::::::::::::先进入情境:::::::::::::::::::::::::::::
某年某月某日假设你有一个如下的函数
def fun():
pass
此时为了性能优化的需求,你想要知道这个函数运行需要多长的时间.那么你可能会想到这么写
import time
def fun():
t_1=time.time()
pass
t_2=time.time()
all_time=t_2-t_1#即为程序运行所需的总时间
.....OK,一个函数看起来这样写也无妨,,,但是,假如让你测试二十多个乃至几百个函数的运行时间,你再这样写.....你不觉得麻烦吗?????
从而python的装饰器或者说闭包的概念就来了.....
作用:装饰器和闭包,可以辅助你用更加简洁易懂的代码内用来丰富你函数的功能..
比如说:你是一个自律的baby,你喜欢健身..那么你需要一直把哑铃带在身上吗???你要吃饭,你需要一直把碗装在身上吗???不需要吧..健身吃饭这些我们也需要,只不过需求的频率不算太高,跟你的呼吸,看视频用眼的频次比起来可差太多了吧.所以嘛,你自己好好品品
用处:
1.简化代码内容
2.增加被装饰函数的功能
只要满足以上两个需求,你就有理由来使用装饰器了.并且使用装饰器也会让你写的代码逼格很高有木有???(手动滑稽)
先来了解第一个概念
一.闭包
我们知道函数也是对象.然而函数名内部就存着该函数的内存地址..
而闭包一般形式就是一个外部函数outer_fun()嵌套一个或多个内部函数inner_fun(),,且内部函数可以访问外部函数的局部变量(即使外部函数已经调用完毕),并且外部函数会return inner_fun
闭包特点:
1.inner_fun()可以访问outer_fun()的局部变量,即使outer_fun()已经调用完毕并且销毁
2.inner_fun()甚至可以改变outer_fun()的局部变量,不过需要结合nonlocal来声明一下这个局部变量
#一个简单的闭包函数
def outer_fun(x):
z=3
def inner_fun(y):
return x+y+z
return inner_fun
#调用这个闭包函数
closure_fun=outer_fun(10)
value=closure_fun(5)
print(value)
'''运行结果value是18'''
(假设我想修改z的值,那么我需要再inner_fun()里面先写上'nonlocal z'声明一下,之后再给z赋值)
接下来,你跟着我的思路,一起来理解一下闭包函数形式下实现的和装饰器一模一样的功能(以此来理解装饰的内部运行)
装饰器本质也是函数,并且装饰器语法实现的和用闭包实现的装饰器功能是完全等价的
第一位上场的是我们的主要功能函数(未来的被装饰函数)
#作用是计算的n的阶乘
def n_factorial(n):
if n < 0:
return "n不能是负数"
elif n == 0 or n == 1:
return 1
else:
result = 1
for i in range(2, n+1):
result *= i
return result
第二位上场的是我们的闭包函数(未来的装饰器函数)
def calculate_time(fun):
def wrapper(*args):
t_1 = time.time()
n = fun(*args)#这个函数以外的都是我们添加的计算时间的功能的实现
t_2 = time.time()
print("程序运行所需时间为:{}".format(t_2 - t_1))
return n
return wrapper
第三位上场的是我们的闭包函数的调用方法
new_n_factorial=calculate_time(n_factorial)
i=new_n_factorial(20)
print(i)
上述的执行步骤:
1.(从第三位看起)先调用了calculate_time()函数,并传入了参数n_factorial.(我们之前说了函数名包含了函数的地址,所以相当于参数是函数地址)
1.1进入calculate_time函数,并传入n_factorial参数(其实就是fun,只需要在fun前面加上一个括号就可以调用了)
1.2执行了def wrapper(*args)的操作.(注意:只是执行了定义函数的操作,没有调用,没有调用就是没有启用函数.....比较重要的是,其实在执行定义函数的过程中,将fun传了进去.那个wrapper函数里面的fun(*args)其实就是完完全全等价于n_factorial函数的...因为函数地址都是一样的..所以,你再转过来头看一下,这个wrapper()函数是不是就是添加了额外功能的n_factorial()函数..所以我们的下一个目标就是执行这个wrapper函数)
1.3return wrapper(可见,return的是wrapper的函数地址值,因此我们的下一步就是去调用他啦..怎么调用呢?只要你的对象是函数对象,只需要在变量名之后加一个()并传入参数就可以调用了)
2.return的wrapper被new_n_factorial变量接收..(所以此时,就可以知道这个wrapper()函数是完完全全等价于new_n_factorial()函数的...所以你想理解new_n_factorial()函数的过程,直接看wrapper()函数的过程就可以了...你品品,你细品)
3.调用new_n_factorial()并传入参数20
3.1开始执行new_n_factorial()...执行过程参考wrapper()函数的内部定义
4.执行完new_n_factorial()后return n被i接受
5.最后print(i),结束
闭包形式过程总结:
上述函数扮演的角色:
1.n_factorial():主要功能函数..
2.calculate_time)():将wrapper()函数与n_factorial()函数绑定起来的函数..
3.wrapper():相当于加了额外功能的n_factorial()函数..
********************************分界线****************************************
二.装饰器
接下来我们引入装饰器,及其语法
装饰器使用步骤:
1.分清 被装饰函数 和 装饰器函数 并写好代码
2.在被装饰函数的上方一行写上'@'+'装饰器函数函数名'(例如:装饰器函数名字为haha(),那么被装饰函数上方就要写'@haha')
3.调用的时候,直接用 被装饰函数 的函数名调用即可
代码如下:
def calculate_time(fun):
def wrapper(*args):
t_1 = time.time()
n = fun(*args)#这个函数以外的都是我们添加的计算时间的功能的实现
t_2 = time.time()
print("程序运行所需时间为:{}".format(t_2 - t_1))
return n
return wrapper
@calculate_time'''装饰器语法'''
def n_factorial(n):
if n < 0:
return "n不能是负数"
elif n == 0 or n == 1:
return 1
else:
result = 1
for i in range(2, n+1):
result *= i
return result
n_factorial_value=n_factorial(10000)
所以可见,用装饰器写代码大大简化看闭包形式的写法..并且闭包形式的写法大大简化了为主要功能函数(即被装饰函数)添加额外功能的写法