You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何以类的形式实现带参数的Python装饰器?

用类实现带参数装饰器的正确姿势(适配类方法场景)

你遇到的这个问题我之前也碰到过——用类写带参数装饰器时,在类方法上用就会搞混self的指向,确实挺头疼的。核心问题在于你原来的写法返回的是装饰器类的实例方法,当Python处理类方法装饰时,会把装饰器实例当成第一个参数(也就是原方法的self)传进去,直接打乱了参数顺序。不过有几种靠谱的类实现方案能解决这个问题,我给你详细说说:

方案一:在包装函数中动态识别参数位置

这种思路是在__call__里返回一个通用的包装函数,手动区分普通函数和类方法/实例方法的参数结构,确保能正确拿到req和resp,同时完整传递原函数的所有参数:

import jsonschema
import functools
from some_module import _log, errors  # 替换成你的实际依赖

class jsonschema_validate(object):
    def __init__(self, schema):
        self._schema = schema

    def __call__(self, fn):
        @functools.wraps(fn)  # 保留原函数的元信息(名称、文档字符串等)
        def wrapper(*args, **kwargs):
            # 识别req和resp的位置:如果第一个参数是类/实例,就往后偏移一位
            req_idx = 1 if (len(args) > 0 and hasattr(args[0], '__class__') and not callable(args[0])) else 0
            resp_idx = req_idx + 1

            # 从位置参数或关键字参数中获取req和resp
            req = args[req_idx] if req_idx < len(args) else kwargs.get('req')
            resp = args[resp_idx] if resp_idx < len(args) else kwargs.get('resp')

            if not req or not resp:
                raise TypeError("Missing required parameters: req and resp")

            # 执行JSON Schema校验
            try:
                jsonschema.validate(req.media, self._schema, format_checker=jsonschema.FormatChecker())
            except jsonschema.ValidationError as e:
                _log.exception('Validation failed: %r', e)
                raise errors.HTTPBadRequest('Bad request')
            
            # 调用原函数,传递所有原始参数
            return fn(*args, **kwargs)
        return wrapper

这个方案的好处是兼容性强,不管装饰普通函数还是类方法/实例方法都能正常工作,因为它会自动适配参数结构。

方案二:利用Python描述器协议(更优雅的适配类方法)

如果你主要是用来装饰类方法,那可以利用Python的描述器协议来解决绑定问题。当装饰器类实现了__get__方法时,Python在访问类的实例方法时会自动调用这个方法,帮我们正确绑定原类的实例:

import jsonschema
import functools
from some_module import _log, errors

class jsonschema_validate(object):
    def __init__(self, schema):
        self._schema = schema
        self._fn = None

    def __call__(self, fn):
        self._fn = fn
        return self  # 返回装饰器实例本身,而非包装函数

    def __get__(self, instance, owner):
        # 当访问类的实例方法时,Python会调用这个方法
        # instance是原类的实例(比如MyResource的实例),owner是原类本身
        @functools.wraps(self._fn)
        def wrapper(*args, **kwargs):
            # 提取req和resp(此时args的第一个参数就是req,因为instance已经被绑定)
            req = args[0] if args else kwargs.get('req')
            resp = args[1] if len(args)>=2 else kwargs.get('resp')
            
            try:
                jsonschema.validate(req.media, self._schema, format_checker=jsonschema.FormatChecker())
            except jsonschema.ValidationError as e:
                _log.exception('Validation failed: %r', e)
                raise errors.HTTPBadRequest('Bad request')
            
            # 调用原函数时,把原类实例作为第一个参数传入
            return self._fn(instance, *args, **kwargs)
        return wrapper

使用这个装饰器的方式和你原来的写法完全一样:

class MyResource:
    @jsonschema_validate(my_schema)
    def post(self, req, resp):
        # 业务逻辑代码
        pass

当你调用my_resource_instance.post(req, resp)时,Python会先调用jsonschema_validate实例的__get__方法,拿到绑定了my_resource_instance的wrapper函数,此时wrapper接收的参数是(req, resp),调用原post方法时会自动把my_resource_instance作为第一个参数(也就是原方法的self)传进去,完美解决了参数错位的问题。

为什么原来的写法会出错?

你原来的代码中,__call__返回的是self._decorator——这是一个绑定到jsonschema_validate实例的方法。当装饰类方法时,Python会把装饰器实例当成第一个参数(也就是原方法的self)传给_decorator,导致原方法的参数顺序完全混乱:本来应该是(my_resource_instance, req, resp, ...),结果变成了(jsonschema_validate_instance, req, resp, ...),原方法需要的self(MyResource实例)根本没传,自然会报错缺少resp参数。

内容的提问来源于stack exchange,提问作者Anthony

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:02:36