装饰器
装饰器
什么是装饰器:
装饰器利用了函数也可以作为参数传递和闭包的特性,可以让我们的函数在执行之前或者执行之后方便的添加一些代码。这样就可以做很多事情了,比如@classmethod
装饰器可以将一个普通的方法设置为类方法,@staticmethod
装饰器可以将一个普通的方法设置为静态方法等。所以明白了装饰器的原理以后,我们就可以自定义装饰器,从而实现我们自己的需求。
理解:
拿网站开发的例子来说。网站开发中,经常会碰到一些页面是需要登录后才能访问的。那么如果每次都在访问的视图函数中判断,很麻烦,而且代码很难维护,示例如下:
user = { "is_login": False } def edit_user(): if user['is_login'] == True: print('用户名修改成功') else: print('跳转到登录页面') def add_article(): if user['is_login'] == True: print('添加文章成功') else: print('跳转到登录页面') edit_user() add_article()
以上现在是只有两个函数,如果以后网站越来越大,需要做判断的地方越来越大,那么这种判断将显得非常低效并且难以维护,因此这时候我们可以采用装饰器来解决:
def edit_user(): print('用户名修改成功') def add_article(): print('添加文章成功') def login_required(func): def wrapper(): if user['is_login'] == True: func() else: print('跳转到登录页面') return wrapper login_required(edit_user) login_required(add_article)
这样我们把这个判断用户是否登录的逻辑就已经单独抽出放到login_required
这个装饰器中了,以后如果某个函数想要做登录限制,那么就先传给login_required
这个装饰器就可以了。
但是以上方式写法很别扭,每次调用一个函数的时候要记得先传给login_required
,容易忘记每次都要写,因此我们采用另外一种写法:
def login_required(func): def wrapper(): if user['is_login'] == True: func() else: print('跳转到登录页面') return wrapper @login_required def edit_user(): print('用户名修改成功') @login_required def add_article(): print('添加文章成功') edit_user() add_article()
以上这种写法是装饰器中正确的写法,在函数定义开头的地方,通过@装饰器名
就可以了,这样在调用edit_user
和add_article
的时候,就不需要手动的传给login_required
了。
被装饰的函数带有参数:
被装饰的函数有参数是非常普遍的一种情况,这时候我们只需要在里面的函数中传递参数就可以了:
def login_required(func): def wrapper(username): if user['is_login'] == True: func(username) else: print('跳转到登录页面') return wrapper @login_required def edit_user(username): print('用户名修改成功:%s'%username) edit_user("zhiliao")
也有可能被装饰的函数中,参数是不固定的,因此这时候写一个固定的参数,不能成为一个普遍的装饰器,这时候可以采用*args
和**kwargs
组合起来,包含所有的参数:
def login_required(func): def wrapper(*args,**kwargs): if user['is_login'] == True: func(*args,**kwargs) else: print('跳转到登录页面') return wrapper @login_required def edit_user(username): print('用户名修改成功:%s'%username) edit_user()
带参数的装饰器:
装饰器也可以传递参数。只不过如果给装饰器传递参数了,那么就要在这个装饰器中写两个方法了,示例代码如下:
def login_required(site='front'): def outter_wrapper(func): def inner_wrappre(*args,**kwargs): if site == 'front': if user['is_login'] == True: print('进入到前台了') func(*args,**kwargs) else: print('跳转到前台的首页') else: if user['is_login'] == True: print('进入到后台了') func(*args,**kwargs) else: print('跳转到后台的首页') return inner_wrappre return outter_wrapper @login_required('front') def edit_user(): print('用户名修改成功') @login_required('front') def add_article(): print('添加文章成功') edit_user()
wraps装饰器:
采用之前的装饰器,会让我们的函数失去一些属性,比如__name__
,这样在一些代码中会产生错误,比如Flask
开发中。如果我们想要用装饰器,并且仍想保留函数的一些属性,比如__name__
,那么可以使用wraps
装饰器,以下是没有使用wraps
装饰器的代码:
def login_required(func): def wrapper(*args,**kwargs): if user['is_login'] == True: func(*args,**kwargs) else: print('跳转到登录页面') return wrapper @login_required def edit_user(username): print('用户名修改成功:%s'%username) edit_user() print(edit_user.__name__) # 打印wrapper
以下再使用wraps
装饰器,来优化代码:
from functools import wraps def login_required(func): @wraps def wrapper(*args,**kwargs): if user['is_login'] == True: func(*args,**kwargs) else: print('跳转到登录页面') return wrapper @login_required def edit_user(username): print('用户名修改成功:%s'%username) edit_user() print(edit_user.__name__) # 打印edit_user