如何在装饰器方法中引用类静态数据?能否将全局变量handlers改为类静态变量?
如何在装饰器方法中引用类静态数据?能否将全局变量handlers改为类静态变量?
当然可以!你的核心需求是把全局的handlers字典挪到Foo类里作为静态变量,同时让装饰器能正确把方法注册到这个字典里——痛点就在于装饰器是在类定义阶段运行的,这时候既没有实例(没法用self),类本身也还没完全定义好(直接写Foo.handlers会报错)。
我给你两种实用的解决方案,你可以根据场景选择:
方案一:用元类自动收集Handler(推荐)
元类可以拦截类的创建过程,咱们先给方法打个标记,等类创建完成时再统一把标记过的方法注册到类的handlers里,完美避开类未定义的问题:
from typing import Callable, Type # 定义元类,负责收集并注册handler class HandlerMeta(type): def __new__(cls, class_name, bases, namespace): # 先初始化一个空的handler字典 handlers = {} # 遍历类定义里的所有成员,找出被标记的handler for attr_name, attr in namespace.items(): if hasattr(attr, "_handler_name"): handlers[attr._handler_name] = attr # 清理临时标记,避免污染类属性 delattr(attr, "_handler_name") # 创建类对象 new_class = super().__new__(cls, class_name, bases, namespace) # 把收集到的handler赋值给类的静态变量 new_class.handlers = handlers return new_class # 定义装饰器,给方法打标记(记录对应的请求名称) def handler(name: str) -> Callable[[Callable], Callable]: def decorator(func: Callable[..., None]) -> Callable: func._handler_name = name return func return decorator # 使用元类创建Foo类 class Foo(metaclass=HandlerMeta): @handler("a") def handle_a(self) -> None: print("处理请求'a'") @handler("b") def handle_b(self) -> None: print("处理请求'b'") def handle(self, name: str) -> None: # 用self.__class__.handlers支持子类扩展 self.__class__.handlers[name](self)
方案优势:
handlers是类的静态变量,每个子类可以拥有独立的handler字典,不会互相干扰- 完全自动收集,不需要额外的手动注册步骤
- 符合Python类创建的生命周期逻辑,不会出现未定义的错误
方案二:用__init_subclass__收集(无需元类)
如果不想用元类,也可以用__init_subclass__钩子——不过这个钩子默认是给子类用的,所以父类需要手动补一下收集逻辑:
from typing import Callable # 同样的标记装饰器 def handler(name: str) -> Callable[[Callable], Callable]: def decorator(func: Callable[..., None]) -> Callable: func._handler_name = name return func return decorator class Foo: handlers: dict[str, Callable[..., None]] = {} def __init_subclass__(cls) -> None: super().__init_subclass__() # 给子类收集handler for attr_name, attr in cls.__dict__.items(): if hasattr(attr, "_handler_name"): cls.handlers[attr._handler_name] = attr delattr(attr, "_handler_name") @handler("a") def handle_a(self) -> None: print("处理请求'a'") def handle(self, name: str) -> None: self.__class__.handlers[name](self) # 手动给Foo父类收集handler(因为__init_subclass__默认不处理自身) for attr_name, attr in Foo.__dict__.items(): if hasattr(attr, "_handler_name"): Foo.handlers[attr._handler_name] = attr delattr(attr, "_handler_name")
适配你的WebSocket场景
不管用哪种方案,你的WebSocket处理代码只需要小改一下,就能正常使用类的静态handler字典:
from websockets.sync.server import serve import json def wshandler(websocket): f = Foo() for msg in websocket: decoded = json.loads(msg) # 直接调用f.handle传入请求类型即可 f.handle(decoded['type']) with serve(wshandler, "localhost", 8888) as server: server.serve_forever()
再解释下你之前遇到的几个坑:
- 为什么
self.handlers不行?因为装饰器在类定义时就运行了,这时候还没有任何实例,self根本不存在 - 为什么
Foo.handlers不行?因为类定义是从上到下执行的,当装饰器被调用时,Foo类还没完全定义好,Python找不到这个名称 - 为什么不能直接用
@classmethod做装饰器?因为类内部的@classmethod方法要等类定义完成后才能被引用,装饰器运行时还拿不到这个方法
备注:内容来源于stack exchange,提问作者TrentP
相关产品推荐
相关产品推荐

