python装饰器
在python的学习中,我们经常会遇到装饰器这个词。从他的名字看,起的作用应该是“装饰”,那么它具体是什么呢?今天我们就来分析一下。
基础知识
如果我们要学习装饰器,那么一定要了解内部函数和闭包的概念,因为可以这么说装饰器基于闭包实现,闭包基于内部函数实现。也可以说装饰器是闭包的一个应用。
1、内部函数
- 内部函数大致格式
#定义函数func()
def func():
#函数func()变量声明(局部/全局)
#函数func()中其他函数体,除内部函数以外
def inner_func(): #声明内部函数,内部函数可以看做是函数体
#内部函数变量声明(局部/全局)
#内部函数inner_func()函数体
# 调用内部函数inner_func()
inner_func()#如果不这样操作,则inner_func()函数永远不会被调用
#调用函数func()
func()
- 内部函数的特点
(1)可以直接访问外部函数的变量
(2)内部函数修改全局不可变变量时,需要在内部函数使用关键字global,格式:global 变量名
(3)内部函数修改外部函数的不可变变量时,需要在内部函数中使用关键字nonlocal,格式:nonlocal 变量名
(4)内部函数可以直接修改外部函数的可变类型的变量,不借助其他关键字
2、闭包
- 闭包是在内部函数的基础提出的概念,一般需要满足以下条件的函数是闭包。
(1)外部函数中定义了内部函数
(2)外部函数有返回值,且返回值是内部函数名(注意:返回是你函数名,inner_func,而不是inner_func(),带了()就是函数的调用了)
(3)内部函数引用了外部函数的变量
- 大致格式
#定义
def func():
...
def inner_func():
...
return inner_func#不带()
#调用func().返回的是inner_func()这个名字
inner_func_name=func()
#调用返回出来的内部函数inner_func()
inner_func_name()
- 闭包的作用:
(1)可使用同级的作用域
(2)读取其他元素的内部变量
(3)延长作用域
装饰器
在有了上面的知识之后,我们就可以开始介绍了装饰器了。
1、最简单的装饰器
我们首先来介绍最简单的装饰器,就是不带参数且只有一层的那种。
装饰器的一个最简单用处就是进行校验,当大家优酷等软件上下载视频时,是需要处于登录状态的,假设说没有登录,系统会提示,请登录后进行下载。
对于上面的例子我们进行抽象,可以得到以下模块:
(1)登录
(2)判断是否登录
(3)下载
那么假设,我们现在已经有了登录函数和下载函数(如下),那么怎么才能完成程序要求呢?
# islogin代表用户是否登录,False未登录,True已经登录
# 默认登录名:user 默认密码:user
import time
islogin = False
# 登录函数
def login():
# 登录时的用户名和密码由键盘提示输入
print("正在登录中......")
username = input("username:")
password = input("password:")
if username == 'user' and password == 'user':
print("登录成功,欢迎使用!")
return True # 用户密码匹配成功,True置1,登录成功
return False
# 下载函数(不涉及实现,只是提示)
@login_result # 声明装饰器@+装饰的函数名
def download():
print("下载已开始,请您耐心等待!")
for i in range(3, 0, -1):
print("倒计时:", i)
print("下载已经完成,可前至缓存中查看!")
当然,最粗暴简单的方法就是重写,将判断登录的功能写进去,但是那样的话,有些浪费时间,而且有些代码是不允许我们随心篡改的,比如:某工程的核心代码,一改动工程就瘫痪了。
这时我们需要另外一种比较简单方法来解决这个问题,这个方案就是装饰器。因为装饰器的作用就是在原有函数的基础上,去扩展该函数的功能。
我们先来介绍一下装饰器的格式特点:闭包+参数是函数。
那么我们现在用装饰器的方式来完成上面的功能要求。
注意:在使用装饰器的时候,我们要修饰哪个函数(给哪个函数扩展功能)就在哪个函数上面添加@+修饰函数的名字。
# islogin代表用户是否登录,False未登录,True已经登录
# 默认登录名:user 默认密码:user
import time
islogin = False
# 登录函数
def login():
# 登录时的用户名和密码由键盘提示输入
print("正在登录中......")
username = input("username:")
password = input("password:")
if username == 'user' and password == 'user':
print("登录成功,欢迎使用!")
return True # 用户密码匹配成功,True置1,登录成功
return False
# 装饰器的实现函数:login_result()
def login_result(download): # 参数是函数
def ornament(*args, **kwargs): # 使用可选参数,可以接受所有类型的参数
global islogin # 如果登录状态变更,需要进行修改,所以用global进行声明
if islogin: # 判断是否登录
download(*args, **kwargs) # 登录时执行
print("您还没有登录,请登录后重新进行下载!")
while islogin != True:
islogin = login() # 没有登录时执行
return ornament # 内部函数名
# 下载函数(不涉及实现,只是提示)
@login_result # 声明装饰器@+装饰的函数名
def download():
print("下载已开始,请您耐心等待!")
for i in range(3, 0, -1):
print("倒计时:", i)
print("下载已经完成,可前至缓存中查看!")
# 调用
download()
download()
从上面的程序,我们可以知道@login_result 的执行过程:
(1)明确被装饰函数是紧跟在它下面的函数(download)
(2)将被装饰函数作为参数传递给装饰器login_result函数
(3)执行装饰器login_result函数
用图解的方式展示一下:
2、多层装饰器
对一个函数的功能扩展是可以用好几个装饰器共同实现的,实现起来也很简单,就是定义了装饰器函数后,在需要被装饰的函数上面声明装饰器相当于简单装饰器的叠加。所以不展开叙述。
@装饰器1名称
@装饰器2名称
......
@装饰器n名称
def 被装饰函数名(参数列表):
注意:在多层装饰器的使用上,我们要注意就近原则,就是说离被修饰越近的函数(从底向上),越先被加载修饰,即n——>1的顺序。
3、带参数的装饰器
带参数的修饰器和简单修饰器相比,多了参数,它的格式也发生了相应的变化,但是实现机制没有区别。
-
带参数的装饰器特点如下:
(1)带参数的装饰器结构是三层的
(2)最外层的函数负责接收装饰器参数
(3)里面的内容还是原来装饰器的内容,并没有发生变化 -
带参数的装饰器格式:
#带参数的修饰器函数的实现
def ceng1(num):#第一层:接收装饰器的参数
def ceng2(func):#第二层:接收被装饰函数
def ceng3(*args,**kwargs):#第三层:接收函数参数
#函数体
return ceng3 #返出第三层的函数名
return ceng2 #返出来第二层的函数名
@ceng1(5)#带参数装饰器的声明
def download():
#函数体
#调用
download()
其实不管是哪种装饰器,他们起的作用在本质上是一样的,都是在不改动原有函数的基础上,对被修饰函数做功能上的扩展。