Python 装饰器
Python 装饰器
一、简单的装饰器
- 1.为什么要使用装饰器
装饰器的功能:在"不修改原函数"及其"调用方式"的情况下对原函数功能进行扩展
装饰器的本质:就是一个闭包函数
- 需求一:
网站刚上线为了吸引客户群体,所有版块都可免费观看,可经过一段时间后宽带费给公司带来了很大的开销,公司领导决定对其中某些版块进行收费(会员登录可看)。
因要对其某一版本进行认证,就把认证功能提取出来单独写个模块,然后直接调用即可。如下:
# 用户登录了就把这个改成True
user_status = False
def login():
"""
1.假装使用DB里用户信息
:return:
"""
_username = "Harry"
_password = "abc123"
# global 作用:如果用户登录成功就将函数外的"user_status"全局变量值修改为True
global user_status
if user_status == False:
username = input("username:")
password = input("password:")
if username == _username and password == _password:
print("welcome login....")
user_status = True
else:
print("wrong username or password!")
else:
print("用户已登录,验证通过...")
def home():
print("\033[05;34m-----首 页-----\033[0m")
def Movie():
login()
print("\033[02;36m-----电 影-----\033[0m")
def TV_series():
print("\033[03;37m-----电视剧-----\033[0m")
def Entertainment():
login()
print("\033[06;38m-----娱 乐-----\033[0m")
home()
Movie()
TV_series()
Entertainment()
运行结果:
-----首 页-----
username:Harry
password:abc123
welcome login....
-----电 影-----
-----电视剧-----
用户已登录,验证通过...
-----娱 乐-----
这样写存在一个问题:代码是实现了功能,但是如果现在有很多模块都需要增加认证功能,就需要修改认证模块的代码。这违反了"开放-封闭"原则(简单说:已实现功能的代码不允许被修改,但允许被扩展)
- 封闭--已实现的功能代码快
- 开放--对扩张开发
嗯。思考半天后想到了解决方案,可以使用高阶函数。就是把一个函数传给另外一个函数,直接把这个功能的函数名当做一个参数传给需要验证模块就行了
user_status = False # 用户登录了就把这个改成True
def login(func): # 把要执行的模块从这里传进来 此处的func就相当于是login(Movie)【Movie】函数
_username = "Harry" # 假装这是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() # 看这里看这里,只要验证通过了,就调用相应功能. 就是上边传送过来的函数.
def home():
print("\033[05;34m-----首 页-----\033[0m")
def Movie():
print("\033[02;36m-----电 影-----\033[0m")
def TV_series():
print("\033[03;37m-----电视剧-----\033[0m")
def Entertainment():
print("\033[06;38m-----娱 乐-----\033[0m")
home()
# 需要验证就调用 login,把需要验证的功能 当做一个参数传给login
login(Movie)
TV_series()
login(Entertainment)
执行结果:
-----首 页-----
user:Harry
pasword:abc123
welcome login....
-----电 影-----
-----电视剧-----
-----娱 乐-----
- 按照之前反馈的问题进行修改后,又犯了一个大忌.
修改了调用方式。想一想每个需要认证的模块,都必须调用login()方法。并把自己的认证方法给加上,之前不是这样调用的。试想如果100个模块都需要认证那这100个模块都得修改调用方式,这么多模块肯定都不是一个人写的,让每个人都修改调用方法才能加上认证,肯定会被骂死的。。
- 解决思路:
使用匿名函数【lambda】代码如下:
#这样就相当于取了一个别名. 人家在调用Movie时就等会执行login(Movie).通过login里的验证后.就会自动调用Movie功能
login(Movie) 修改为:Movie = login(Movie)
#这样就相当于取了一个别名. 人家在调用Entertainment时就等会执行login(Entertainment).通过login里的验证后.就会自动调用Entertainment功能
login(Entertainment)修改为:Entertainment = login(Entertainment)
执行结果:
-----首 页-----
user:Harry
pasword:abc123
welcome login....
-----电 影-----
-----电视剧-----
-----娱 乐-----
- 但这样写存在一个问题:
用户在未调用时. Movie = login(Movie)就会先把自己给执行了.应该是等用户调用时程序才执行的.这样应该怎么做呢?
想实现一开始你写的Movie = login(Movie)不触发你函数的执行.只需要在这个login里面再定义一层函数.第一次调用america = login(america)只调用到外层login.这个login虽然会执行.但不会触发认证了.因为认证的所有代码被封装在login里层的新定义 的函数里了.login只返回 里层函数的函数名.这样下次再执行america()时.就会调用里层函数啦.....代码如下:
- 终结版 装饰器 普通版本
user_status = False # 用户登录了就把这个改成True
def login(func): # 把要执行的模块从这里传进来
def inner(): # 再定义一层函数
_username = "Harry" # 假装这是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() # 看这里看这里,只要验证通过了,就调用相应功能
return inner # 用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数
def home():
print("\033[05;34m-----首 页-----\033[0m")
def Movie():
print("\033[02;36m-----电 影-----\033[0m")
def TV_series():
print("\033[03;37m-----电视剧-----\033[0m")
def Entertainment():
print("\033[06;38m-----娱 乐-----\033[0m")
home()
Movie = login(Movie) # 此处的Moive对应的是inner【return inner】的内存地址. 可以打印一下Movie看下是不是
print(Movie)
Movie()
TV_series()
Entertainment = login(Entertainment)
Entertainment()
执行结果:
-----首 页-----
<function login.<locals>.inner at 0x0000000000A72A60>
user:Harry
pasword:abc123
welcome login....
-----电 影-----
-----电视剧-----
-----娱 乐-----
总结:
使用 1.使用嵌套函数. 2.高阶函数。 实现了.在不改变原有代码+原有调用方式的情况下. 给我的模块加了新功能.
在开发中一个常用的玩法.叫做"语法糖". 官方叫"装饰器". 其实上边的代码还可以更简单.如下:
Movie = login(Movie) #将这行代码去掉.
- 只需要你想装饰的函数上面加上@login. 如下代码:
- 开发过程中都用装饰器时都使用该方法。如下
完美解决 "装饰器"
user_status = False # 用户登录了就把这个改成True
def login(func): # 把要执行的模块从这里传进来
def inner(): # 再定义一层函数
_username = "Harry" # 假装这是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() # 看这里看这里,只要验证通过了,就调用相应功能
return inner # 用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数
def home():
print("\033[05;34m-----首 页-----\033[0m")
@login
def Movie():
print("\033[02;36m-----电 影-----\033[0m")
@login
def TV_series():
print("\033[03;37m-----电视剧-----\033[0m")
def Entertainment():
print("\033[06;38m-----娱 乐-----\033[0m")
home()
Movie()
TV_series()
Entertainment()
执行结果:
-----首 页-----
user:Harry
pasword:abc123
welcome login....
-----电 影-----
-----电视剧-----
-----娱 乐-----
# 相当方便.想给谁加就给谁加. 相当于热插拔. 和上边的效果一样.并且这个更简单. 【 效果是一样的。】
装饰起作用:
1.完全符合. 开放--封闭原则.
2.不改变原有功能代码.不改变原有调用方式.
3.实现扩展新功能
二、传参数的装饰器
- 传一位参数的装饰器
user_status = False # 记录用户登录状态.默认未登录False 登录成功后.状态更改为True
def login(func): # 把要执行的板块从这里传进来
def inner(arg1): # 再定义一层函数
_username = "Harry" # 假装这是DB里存的用户信息
_password = "abc123" # 假装这是DB里存的用户信息
global user_status # 如果用户登录成功就将函数外的user_status全局变量值修改为True
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(arg1) # 看这里看这里.只要验证通过了.就调用相应功能. 意思是用户登录成功.
return inner # 用户调用login时.只会返回inner的内存地址、下次再调用时加上()才会执行inner函数
def home():
print("\033[05;34m-----首 页-----\033[0m")
@login
def Movie(style):
print("\033[02;36m-----电 影--%s---\033[0m"%style)
def TV_series():
print("\033[03;37m-----电视剧-----\033[0m")
@login
def Entertainment(style):
print("\033[06;38m-----娱 乐--%s---\033[0m"%style)
home()
Movie("AA")
TV_series()
Entertainment("BB")
执行结果:
-----首 页-----
user:Harry
pasword:abc123
welcome login....
-----电 影--AA---
-----电视剧-----
-----娱 乐--BB---
- 传多个参数的装饰器
user_status = False # 记录用户登录状态.默认未登录False 登录成功后.状态更改为True
def login(func): # 把要执行的板块从这里传进来
def inner(*args,**kwargs): # 再定义一层函数
_username = "Harry" # 假装这是DB里存的用户信息
_password = "abc123" # 假装这是DB里存的用户信息
global user_status # 如果用户登录成功就将函数外的user_status全局变量值修改为True
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(*args,**kwargs) # 看这里看这里.只要验证通过了.就调用相应功能. 意思是用户登录成功.
return inner # 用户调用login时.只会返回inner的内存地址、下次再调用时加上()才会执行inner函数
def home():
print("\033[05;34m-----首 页-----\033[0m")
@login
def Movie(style):
print("\033[02;36m-----电 影--%s---\033[0m"%style)
def TV_series():
print("\033[03;37m-----电视剧-----\033[0m")
@login
def Entertainment(*args,**kwargs):
print("\033[06;38m-----娱 乐--%s,%s---\033[0m"%(args,kwargs))
home()
Movie("AA")
TV_series()
Entertainment("AA","BB",9999)
执行结果:
-----首 页-----
user:Harry
pasword:abc123
welcome login....
-----电 影--AA---
-----电视剧-----
-----娱 乐--('AA', 'BB', 9999),{}---
此时终于搞定了所有需求,完全遵循开放-封闭原则. 代码如上:
三、第三方登录的装饰器
现在有很多网站都允许第三方登录【原因:避免用户记录很多用户名和密码】.如QQ、weixing(微信)、weibo(微博)等.这...此时如何解决了.
代码如下:【不同板块允许不通的认证方式,多参数.支持第三方登录验证】
user_status = False # 记录用户登录状态.默认未登录False 登录成功后.状态更改为True
def login(auth_type):
print(auth_type)
def auth(func): # 把要执行的板块从这里传进来
def inner(*args,**kwargs): # 再定义一层函数
if auth_type == "QQ":
_username = "Harry" # 假装这是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(*args,**kwargs) # 看这里看这里.只要验证通过了.就调用相应功能. 意思是用户登录成功.
else:
print("only support qq ")
return inner # 用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数
return auth
def home():
print("\033[05;34m-----首 页-----\033[0m")
@login
def Movie(*args,**kwargs):
print("\033[02;36m-----电 影--%s,%s---\033[0m"%(args,kwargs))
@login("QQ")
def TV_series(*args,**kwargs):
print("\033[03;37m-----电视剧--%s,%s---\033[0m"%(args,kwargs))
def Entertainment():
print("\033[06;38m-----娱 乐-----\033[0m")
home()
Movie("110")
TV_series("AA","BB")
执行结果:
<function Movie at 0x00000000011A2A60>
QQ
-----首 页-----
user:Harry
pasword:abc123
welcome login....
-----电视剧--('AA', 'BB'),{}---

浙公网安备 33010602011771号