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

如何以Pythonic方式在类内定义装饰器管理handler方法?

类内标记Handler方法的Pythonic实现

问题背景

我正在构建一个Python类,希望将部分实例方法标记为handler,以便实例调用run方法时自动执行这些方法。最初的实现使用了全局的_handlers列表和装饰器:

_handlers = []
def handler_marker(marked_fun):
    _handlers.append(marked_fun)
    return marked_fun


class ManyHandler:
    def __init__(self):
        pass

    def run(self, input):
        for handling_fun in _handlers:
            handling_fun(self, input)

    @handler_marker
    def method_1(self, input):
        print(input)

    @handler_marker
    def method_2(self, input):
        print(input[::-1])

    def method_3(self, input):
        print("I'm not marked.")


if __name__ == "__main__":
    MH = ManyHandler()
    MH.run('Test string')

这个方案能运行,但_handlers和装饰器都在类外部,把_handlers作为类变量更合理。但直接将_handlers移到类内会遇到两种错误:

  • 装饰器定义在类之前:触发NameError: name 'ManyHandler' is not defined,因为装饰器不知道类的存在
  • 装饰器定义在类之后:类初始化时触发NameError: name 'handler_marker' is not defined,因为类识别不了装饰器

我尝试用额外的HandlerHandler类来管理,但不确定是否符合Pythonic风格,想找最优实现。

最优Pythonic实现

最简洁且符合Python习惯的方式是利用类装饰器(直观易维护),若需要子类继承收集逻辑则可使用元类。以下是具体方案:

方案1:类装饰器收集Handler方法

先定义装饰器标记方法,再用类装饰器统一收集所有标记的方法到类变量中:

def handler(fun):
    fun.is_handler = True
    return fun


def collect_handlers(cls):
    cls._handlers = [
        method for name, method in vars(cls).items()
        if hasattr(method, 'is_handler')
    ]
    return cls


@collect_handlers
class ManyHandler:
    def __init__(self):
        pass

    def run(self, input):
        for handling_fun in self._handlers:
            handling_fun(self, input)

    @handler
    def method_1(self, input):
        print(input)

    @handler
    def method_2(self, input):
        print(input[::-1])

    def method_3(self, input):
        print("I'm not marked.")


if __name__ == "__main__":
    MH = ManyHandler()
    MH.run('Test string')

方案2:元类自动收集Handler方法

如果需要子类自动继承handler的收集逻辑,元类是更合适的选择:

def handler(fun):
    fun.is_handler = True
    return fun


class HandlerMeta(type):
    def __new__(cls, name, bases, attrs):
        new_cls = super().__new__(cls, name, bases, attrs)
        # 收集自身及所有父类的handler方法
        new_cls._handlers = []
        for base in reversed(new_cls.__mro__):
            new_cls._handlers.extend(
                method for name, method in vars(base).items()
                if hasattr(method, 'is_handler')
            )
        return new_cls


class ManyHandler(metaclass=HandlerMeta):
    def __init__(self):
        pass

    def run(self, input):
        for handling_fun in self._handlers:
            handling_fun(self, input)

    @handler
    def method_1(self, input):
        print(input)

    @handler
    def method_2(self, input):
        print(input[::-1])

    def method_3(self, input):
        print("I'm not marked.")


if __name__ == "__main__":
    MH = ManyHandler()
    MH.run('Test string')

方案3:类内静态装饰器+事后收集

若坚持将装饰器放在类内,可以在类定义完成后补充收集逻辑:

class ManyHandler:
    _handlers = []

    @staticmethod
    def handler(fun):
        fun.is_handler = True
        return fun

    def __init__(self):
        pass

    def run(self, input):
        for handling_fun in self._handlers:
            handling_fun(self, input)

    @handler
    def method_1(self, input):
        print(input)

    @handler
    def method_2(self, input):
        print(input[::-1])

    def method_3(self, input):
        print("I'm not marked.")


# 类定义完成后统一收集handler方法
ManyHandler._handlers = [
    method for name, method in vars(ManyHandler).items()
    if hasattr(method, 'is_handler')
]

if __name__ == "__main__":
    MH = ManyHandler()
    MH.run('Test string')

方案对比

  • 类装饰器:最简洁直观,代码可读性高,维护成本低,适合不需要子类继承收集逻辑的场景,是最符合Pythonic风格的选择
  • 元类:功能更强大,支持子类自动继承handler收集逻辑,但相对复杂,适合有继承需求的场景
  • 类内静态装饰器+事后收集:满足装饰器在类内的需求,但需要额外的收集步骤,优雅度稍逊

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 08:14:58