Python的装饰器小记

Python装饰器

1、简介
本质: Python的装饰器就是一个闭包。
目的: 简化代码操作

2、使用装饰器的原则:不改变被装饰函数的属性等性质

  • 使用中间人g对象帮助传递参数
  • 使用内层装饰器@functools.wraps(view_func)回复被装饰函数的属性等性质(举例2)

3、举例1:定义验证登录状态的装饰器

# 使用中间人g对象作为装饰器和被装饰函数中的参数传递者
from flask import session, jsonify, g
from myihome.utils.response_code import RET
import functools  # python的内置模块,存放函数工具

# 闭包:外层函数一般就是定义为被装饰的函数(view_func(例如这里是:set_user_avatar))"的@外层函数"
def login_required(view_func):
    # 内层函数一般定义为wrapper,并且由于传递的参数不确定,使用*args, **kwargs待定
    # @functools.wraps(view_func)这个函数装饰器专门是用来装饰内层函数的,
    # 1参数:外层函数接受的参数,直接传给里面的就可以了,
    # 2意义:内层装饰器加上之后会改变一些特性:functools的wraps会将wrapper相关的属性和名字恢复为view_func的属性和名字,参考博客的例子2
    @functools.wraps(view_func)  # 在写装饰器的时候需要习惯将这个内层装饰器补上,避免改变被装饰函数的特性
    def wrapper(*args, **kwargs):
        # 判断用户的登录状态
        user_id = session.get("user_id")

        # 如果用户是登录的,执行视图函数
        if user_id is not None:
            # g对象的应用,保存user_id,让其作为参数传递对象,在视图函数中可以通过g对象获取保存数据
            g.user_id = user_id
            return view_func(*args, **kwargs)
        else:
            # 如果未登录,返回未登录的信息
            return jsonify(errno=RET.SESSIONERR, errmsg="用户未登录")
    return wrapper


# 使用中间人g对象作为装饰器和被装饰函数中的参数传递者,g对象就是提供来保存数据的
# 在一次请求之中如果涉及到多个函数请求参数的时候就可以使用g对象来传参数
@login_required
def set_user_avatar():
	# 本来是可以直接操作session获取user_id的,
	# 但是使用的装饰器里面已经获取到了user_id,由装饰器的原则,不可能变成 def set_user_avatar(user_id):的,所以可以使用中间人g对象传递过来,不必重复操作一遍
	# user_id = session.get("user_id")
    user_id = g.user_id
    pass

# set_user_avatar() 的执行就是执行wrapper-> wrapper() 直接传递参数到wrapper()里面

4、举例2: 内层装饰器@functools.wraps(func)的作用
①首先:未加上装饰器:

def login_required(func):
    def wrapper(*args, **kwargs):
        pass
    return wrapper

def test():
    """test python"""
    pass

print(test.__name__) 
print(test.__doc__)  

Python中万物皆对象,直接打印出函数test()的名字和说明文档:

test
test python

②加上装饰器:

def login_required(func):
    def wrapper(*args, **kwargs):
        pass
    return wrapper

@login_required
def test():
    """test python"""
    pass
# test -> wrapper :执行test(), 实质是执行wrapper()
print(test.__name__)  # wrapper.__name__
print(test.__doc__)  # wrapper.__doc__

打印的结果为:

wrapper
None

可见已经改变test()的属性了,违反了装饰器的原则。
③加上内层函数装饰器:

import functools

def login_required(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        pass
    return wrapper
    
@login_required
def test():
    """test python"""
    pass
    
# test -> wrapper

print(test.__name__)  # wrapper.__name__
print(test.__doc__)  # wrapper.__doc__

打印结果为:

test
test python

可见被装饰函数的属性被恢复了。

参考代码及项目URL:
https://github.com/too-hoo/myiHome/blob/master/myihome/utils/commons.py

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章