装饰器:本质是函数,功能是装饰其他函数,就是为其他函数添加附加功能
编写装饰器的原则:
- 不能修改被装饰函数的源代码
- 不能修改被装饰函数的调用方式
接下来模拟一个应用场景从头编写一个装饰器:
有一个视频网站有四个模块:
def home():
print("---首页----")
def domestic():
print("----国内专区----")
def america():
print("----欧美专区----")
def japan():
print("----日韩专区----")
视频网站运营一段一时间后积攒了些客户,现在要为欧美和日韩模块进行收费,所以,调用该模块时,需要判断是否是付费vip会员。
通过之前的函数基础知识的学习,轻易能想到的实现思路是:
单独定义一个登陆函数,在调用欧美和日韩模块时调用:
user_status = False #用户登录了就把这个改成True
def login():
_username = "LZ" #假装这是DB里存的用户信息
_password = "abc123" #假装这是DB里存的用户信息
global user_status
if user_status == False:
username = input("user:")
password = input("pasword:")
if username == _username and password == _password:
print("welcome login....")
user_status = True
else:
print("wrong username or password!")
else:
print("用户已登录,验证通过...")
def home():
print("---首页----")
def domestic():
print("----国内专区----")
def america():
login() #执行前加上验证
print("----欧美专区----")
def japan():
login() #执行前加上验证
print("----日韩专区----")
#调用模块
home()
domestic()
america()
japan()
这样就能完成需求。但是!!这违反了软件开发中的一个原则“开放-封闭”原则,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展:
开放——封闭
- 封闭:已实现的功能代码块
- 开放:对扩展开放
抱着不改变功能模块源码的想法我们会想到用高阶函数,把模块函数当参数传入login()
函数中:
user_status = False #用户登录了就把这个改成True
def login(func): #把要执行的模块从这里传进来
_username = "LZ" #假装这是DB里存的用户信息
_password = "abc123" #假装这是DB里存的用户信息
global user_status
if user_status == False:
username = input("user:")
password = input("pasword:")
if username == _username and password == _password:
print("welcome login....")
user_status = True
else:
print("wrong username or password!")
if user_status == True:
func() # 看这里看这里,只要验证通过了,就调用相应功能
#原功能模块不改
#调用模块
home()
domestic()
login(america)
login(japan)
这样,不改变功能模块源码也完成了需求,但是改变了调用方式!试想:每个模块不同的人负责,这样的话就得要求负责相应模块的人都得去改调用方式。这。。。很危险啊。
现在在分析一下:之前函数基础时讲过函数也是变量,不改变函数调用方式的话,我们可以将login(america)
赋值给america
。但是有一个问题是当执行america = login(america)
代码时,就已经把america
执行了啊!我们接下来要做的就是,当america = login(america)
代码时,返回一个函数,但是不执行。等再调用america ()
才执行:
def login(func): #把要执行的模块从这里传进来
def inner():#再定义一层函数
_username = "alex" #假装这是DB里存的用户信息
_password = "abc!23" #假装这是DB里存的用户信息
global user_status
if user_status == False:
username = input("user:")
password = input("pasword:")
if username == _username and password == _password:
print("welcome login....")
user_status = True
else:
print("wrong username or password!")
if user_status == True:
func() # 看这里看这里,只要验证通过了,就调用相应功能
return inner #用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数
这样当调用时先america = login(america)
你在这里相当于把america这个函数替换了,然后在调用america ()
。Python中提供语法在要被装饰的函数前写@login
就相当于america = login(america)
。所以调用时:
#调用模块
home()
domestic()
@login
america()
@login
japan()
执行完america = login(america)
之后,america
就相当于调用的时inner
.所以,如果原来的america
如果有参数的话,在inner
函数上添加参数即可:
def login(func): #把要执行的模块从这里传进来
def inner(*args):#再定义一层函数
_username = "alex" #假装这是DB里存的用户信息
_password = "abc!23" #假装这是DB里存的用户信息
global user_status
if user_status == False:
username = input("user:")
password = input("pasword:")
if username == _username and password == _password:
print("welcome login....")
user_status = True
else:
print("wrong username or password!")
if user_status == True:
func() # 看这里看这里,只要验证通过了,就调用相应功能
return inner #用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数
#调用
@login
america('jack')
写到这,login
就是一个装饰器了。它是一个函数,只是给原来的函数增加了验证功能。既没有改变原函数代码,也没有改变调用方式!