V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
wuwukai007
V2EX  ›  Python

Python 类装饰器中如何使用 functools.wraps 保留原函数的属性?

  •  
  •   wuwukai007 · 2019-08-12 16:28:24 +08:00 · 1963 次点击
    这是一个创建于 1710 天前的主题,其中的信息可能已经有所发展或是发生改变。
    15 条回复    2019-08-12 22:31:07 +08:00
    wuwukai007
        1
    wuwukai007  
    OP
       2019-08-12 16:29:11 +08:00
    class check_token(RefreshJSONWebTokenSerializer,VerifyJSONWebTokenSerializer):
    func = None

    def __init__(self,fun,*args,**kwargs):
    self.func = fun

    @functools.wraps(func)
    def __call__(self,request,*args, **kwargs):
    try:
    token = request.COOKIES.get('token')
    payload =self. _check_payload(token=token)
    user = self._check_user(payload=payload)
    wuwukai007
        2
    wuwukai007  
    OP
       2019-08-12 16:29:33 +08:00
    使用这种曲线那 fun 的方式,还是不行
    myyou
        3
    myyou  
       2019-08-12 16:33:54 +08:00
    class check_token(RefreshJSONWebTokenSerializer,VerifyJSONWebTokenSerializer):

    def __init__(self,fun,*args,**kwargs):
    pass

    def __call__(self, func):
    @wraps(func)
    def wrapped_function(request, *args, **kwargs):
    token = request.COOKIES.get('token')
    payload =self. _check_payload(token=token)
    user = self._check_user(payload=payload)
    return func(request, *args, **kwargs)
    return wrapped_function
    wuwukai007
        4
    wuwukai007  
    OP
       2019-08-12 16:37:38 +08:00
    @myyou 我试一下,主要是用类装饰器后,函数中如果用 super 方法,拿到的是类装饰器中的 super
    wuwukai007
        5
    wuwukai007  
    OP
       2019-08-12 16:41:18 +08:00
    class check_token(generics.ListCreateAPIView,):
    queryset = models.Report.objects.all()
    serializer_class = Report_Serializer
    authentication_classes = []
    @check_token
    def post(self,request,*args,**kwargs):
    return Response({'content':'111'},status=200)

    @check_token
    def list(self, request, *args, **kwargs):
    resp = super(generics.ListCreateAPIView,self).list(request,*args,**kwargs)
    return resp


    用了上面的类装饰器,我的 list 函数调用 ListCreateAPIVie 中的 list 中 super 方法,拿到的是装饰器中继承的类,而不是 ListCreateAPIVie 中的 list
    myyou
        6
    myyou  
       2019-08-12 16:49:38 +08:00
    你是不是把 def __call__(self, func):这里的 self 传给了 func 函数,导致了 func 的 self 函数被替换?
    wuwukai007
        7
    wuwukai007  
    OP
       2019-08-12 16:55:56 +08:00
    @myyou 在__call__中使用闭包,可以用 functools 装饰,但是还是拿不到本身的类
    myyou
        8
    myyou  
       2019-08-12 17:01:05 +08:00
    class check_token(RefreshJSONWebTokenSerializer,VerifyJSONWebTokenSerializer):

    def __call__(self, func):
    @wraps(func)
    def wrapped_function(func_self, request, *args, **kwargs):
    token = request.COOKIES.get('token')
    payload =self. _check_payload(token=token)
    user = self._check_user(payload=payload)
    return func(func_self, request, *args, **kwargs)
    return wrapped_function

    类按这样写
    装饰器写成 @check_token() 加括号试试
    wuwukai007
        9
    wuwukai007  
    OP
       2019-08-12 17:02:50 +08:00
    @myyou 加了,里面随便放了参数,self 关键字我也改了,不行爱
    wuwukai007
        10
    wuwukai007  
    OP
       2019-08-12 17:03:40 +08:00
    def __call__(self,func):
    print(self,func)
    @functools.wraps(func)
    def hander(selff,request,*args,**kwargs):
    print(selff)
    try:
    token = request.COOKIES.get('token')
    payload =selff. _check_payload(token=token)
    user = selff._check_user(payload=payload)
    except Exception as e:
    # r = Response({'code': 400, 'message': e.detail[0]})
    r = Response({'code': 400, 'message': str(e)})
    # r.set_cookie('token','111')
    return r
    else:
    resp = func(selff, request, *args, **kwargs)
    try:
    new_token = selff.validate({'token':token})
    except Exception as e:
    return Response({'code':'令牌更新错误'})
    else:
    resp.set_cookie('token',new_token['token'])
    return resp
    return hander



    @check_token('1')
    def list(self, request, *args, **kwargs):
    resp = super(generics.ListCreateAPIView,self).list(request,*args,**kwargs)
    return resp
    wuwukai007
        11
    wuwukai007  
    OP
       2019-08-12 17:10:02 +08:00
    现在是可以拿到了本身的函数,但是本身函数继承的类中的方法拿不到,报没有方法等
    myyou
        12
    myyou  
       2019-08-12 17:11:06 +08:00
    resp = super(generics.ListCreateAPIView,self).list(request,*args,**kwargs)
    改称 super().list(request,*args,**kwargs)
    myyou
        13
    myyou  
       2019-08-12 17:14:44 +08:00
    super()方法会使用类的 mro 表第二个类的实例,所以用 super 的时候要注意一点
    wuwukai007
        14
    wuwukai007  
    OP
       2019-08-12 18:24:02 +08:00
    @myyou 我傻 b 了,我的 hander 里面用 self 关键字,调用装饰器中继承的方法,func 中用 selff,使用本身的实例就好了 多谢!
    http://www.chaoyue.red/static/media/1565602027(1).jpg
    留言被限制半小时…
    vkhsyj
        15
    vkhsyj  
       2019-08-12 22:31:07 +08:00
    update_wrapper,进源码看一下 wraps 怎么实现的就知道怎么做了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5940 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 02:35 · PVG 10:35 · LAX 19:35 · JFK 22:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.