装饰器(Decorator):在代码运行期间动态增长功能的方式,称之为装饰器。app
本质上,装饰器就是一个返回函数的高阶函数。函数也是一个对象,并且函数对象能够被赋值给变量,所以经过变量也能调用该函数。函数
>>> def now(): ... print('2018-3-20') ... >>> f = now >>> f() 2018-3-20 >>> now.__name__ # 函数对象__name__属性,拿到函数名 'now' >>> f.__name__ 'now'
定义一个能打印日志的装饰器:网站
import datetime def log(func): # 装饰器接受一个函数做为参数,并返回一个函数 def wrapper(*args, **kw): print('call %s(): ' % func.__name__) return func(*args, **kw) return wrapper @log # 运用@语法把装饰器放置在函数定义处 def now(): print(datetime.datetime.now()) now() """ call now(): 2018-03-20 23:00:05.201096 """
把@log放到now()函数定义处,至关于执行语句now = log(now)。now()函数仍然存在,只是如今同名的now变量指向了新的函数log(now)。spa
所以在调用now()时,将执行log(now),在log()函数中返回的wrapper()函数。日志
wrapper()函数的参数定义是(*args, **kw),能够接受任意参数的调用。code
一家视频网站有如下几个板块:视频
def home(): print("首页".center(40, '-')) def america(): print("欧美专区".center(40, '-')) def japan(): print("日韩专区".center(40, '-')) def henan(): print("河南专区".center(40, '-'))
上线初期全部视频都是免费观看,用户量增多后,准备对一些板块收费。所以须要添加登录认证功能。对象
user_status = False # 用户登陆就把这个改成True def login(): _username = 'alex' # 伪装这是DB里存的用户信息 _password = 'abc!23' # 假设这是DB里存的用户信息 global user_status if user_status is False: # 用户状态为True时能够访问视频 username = input("user:") password = input("password:") if username == _username and password == _password: print("welcome login....") user_status = True else: print("用户已经登陆,验证经过!") def home(): print("首页".center(40,'-')) def america(): login() # 登录认证 print("欧美专区".center(40,'-')) def japan(): print("日韩专区".center(40,'-')) def henan(): login() # 登录认证 print("河南专区".center(40,'-')) henan() america()
上述代码虽然实现了登录认证功能,可是却违反了软件开发中一个重要原则:“开发—封闭”原则。规定代码不容许被修改但能够被扩展。blog
封闭:已实现的功能代码块不该该被修改。内存
开放:对现有功能的扩展开放。
高阶函数:把一个函数当作一个参数传给另外一个函数。应用该知识点改写以下:
user_status = False #用户登陆了就把这个改为True def login(func): #把要执行的模块从这里传进来 _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() # 看这里看这里,只要验证经过了,就调用相应功能 def home(): print("---首页----") def america(): #login() #执行前加上验证 print("----欧美专区----") def japan(): print("----日韩专区----") def henan(): #login() #执行前加上验证 print("----河南专区----") home() login(america) #须要验证就调用 login,把须要验证的功能 当作一个参数传给login # home() # america() login(henan)
以上改写也很差,修改了调用方式,须要认证的模块都须要修改调用方式。须要不改变原功能代码,又不改变原有调用方式,还能加上认证的代码改写。
user_status = False def login(func): _username = "alex" _password = "abc!23" global user_status if user_status == False: username = input("user:") password = input("password:") if username == _username and password == _password: print("welcome login...") user_status = True else: print("wrong username or password!") if user_status == True: func() home() henan = login(henan) america = login(america) henan() """ -------------------首页------------------- user:alex password:abc!23 welcome login... ------------------河南专区------------------ ------------------欧美专区------------------ Traceback (most recent call last): File "/Users/.../装饰器.py", line 91, in <module> henan() TypeError: 'NoneType' object is not callable """
上述改写不改变调用方式也不违反“开放——封闭”,可是america = login(america)时就会把america执行。
能够运用嵌套函数,在login中再定义一层,使得america = login(america)只调用到外层,不触发认证。login只返回里层函数的函数名,下次执行america()时再调用里层函数。
user_status = False def login(func): def inner(): #添加一层inner,装饰器套路 _username = "alex" _password = "abc!23" global user_status if user_status == False: username = input("user:") password = input("password:") if username == _username and password == _password: print("welcome login...") user_status = True else: print("wrong username or password!") if user_status: func() # henan()——老的河南函数 return inner # 加括号执行,不加括号返回内存地址 home() henan = login(henan) america = login(america) henan() """ -------------------首页------------------- user:alex password:abc!23 welcome login... ------------------河南专区------------------ """
还能够将上述代码改写,把america = login(america)改写为@login,并加在america函数前。
@login # 等价于henan = login(henan) def hubei(): #login() # 改写删除该行 print("湖北专区".center(40,'-')) hubei() """ user:alex password:abc!23 welcome login... ------------------湖北专区------------------ """
上述代码要是在调用函数的时候直接传参数是会报错的。由于调用hubei时,其实是调用login。
hubei = login(hubei),login返回inner内存地址。
第二次用户本身调用hubei("3p"),实际上至关于调用的是inner,所以给inner设置参数,能够解决问题。
user_status = False def login(func): def inner(*args,**kwargs): # 添加参数eg.3p _username = "alex" _password = "abc!23" global user_status if user_status == False: username = input("user:") password = input("password:") if username == _username and password == _password: print("welcome login...") user_status = True else: print("wrong username or password!") if user_status: # func(args,kwargs) # 这样写是固定两个参数 func(*args,**kwargs) # henan() 适配任意多个参数 return inner # 加括号执行,不加括号返回内存地址 def home(): print("首页".center(40,'-')) def america(): print("欧美专区".center(40,'-')) @login def japen(name): print("日韩专区".center(40, '-'), name) @login # henan = login(henan) def henan(style): print(("河南专区" + style).center(40, '-')) home() henan('3p') japen('oddry') """ -------------------首页------------------- user:alex password:abc!23 welcome login... -----------------河南专区3p----------------- ------------------日韩专区------------------ oddry """