如何从元类参数推断泛型类型,避免重复指定类型?
解决方案:从泛型基类自动提取被包装类型
你可以通过访问子类的__orig_bases__属性(Python 3.9+支持)提取泛型参数,避免重复指定类型。元类可自动解析泛型基类中的类型参数,替代手动传入的wrapped_class参数。
修改后的元类与代码示例
from typing import TypeVar, Generic, get_args, get_origin T = TypeVar('T') class Meta(type): def __new__( cls, name: str, bases: tuple[type, ...], cls_dict: dict[str, object], wrapped_class: type | None = None, ): # 未手动传入wrapped_class时,从泛型基类提取 if wrapped_class is None: for base in bases: origin = get_origin(base) if origin is Wrapper: wrapped_class = get_args(base)[0] break else: raise TypeError("必须指定wrapped_class,或继承自带泛型参数的Wrapper类") # 原有的方法检查逻辑 for member in wrapped_class.__dict__: if member.startswith("__"): continue if member not in cls_dict: raise TypeError(f"需要实现 {member}") return super().__new__(cls, name, bases, cls_dict) class Base: def foo(self): return "bar" class Wrapper(Generic[T], metaclass=Meta): def do_wrapper_stuff(self, arg: T) -> T: return arg # 仅需指定泛型参数,无需手动传wrapped_class class BaseWrapper(Wrapper[Base]): def foo(self): # 按权限逻辑实现或拦截方法 raise PermissionError("禁止直接调用foo方法")
关键说明
get_origin与get_args:这两个函数用于解析泛型类型,get_origin返回泛型的原始类(如Wrapper),get_args返回泛型参数(如(Base,))。- 兼容手动传入:保留
wrapped_class参数的可选性,满足特殊场景下的手动指定需求。 - 类型检查有效:泛型参数
Wrapper[Base]会被类型检查器识别,保障代码类型安全。
兼容旧版本的替代方案:工厂函数生成包装类
如果需要兼容Python 3.9以下版本,可通过工厂函数封装类型指定逻辑,让用户仅传入一次类型:
from typing import TypeVar, Generic T = TypeVar('T') class Meta(type): def __new__( cls, name: str, bases: tuple[type, ...], cls_dict: dict[str, object], wrapped_class: type, ): for member in wrapped_class.__dict__: if member.startswith("__"): continue if member not in cls_dict: raise TypeError(f"需要实现 {member}") return super().__new__(cls, name, bases, cls_dict) class Base: def foo(self): return "bar" class Wrapper(Generic[T], metaclass=Meta): def do_wrapper_stuff(self, arg: T) -> T: return arg def create_wrapper_class(wrapped_cls: type, name: str | None = None) -> type: name = name or f"{wrapped_cls.__name__}Wrapper" # 动态创建子类,自动传递泛型参数和元类参数 return type(name, (Wrapper[wrapped_cls],), {}, wrapped_class=wrapped_cls) # 使用工厂函数,仅传入一次Base类型 BaseWrapper = create_wrapper_class(Base) # 实现要求的方法 BaseWrapper.foo = lambda self: PermissionError("禁止直接调用foo方法")
为什么不继承被包装类?
正如你提到的,继承被包装类会让包装类自动拥有其所有属性和方法,无法通过元类检查强制实现权限控制逻辑。而泛型+元类的方式,既能让类型检查器识别被包装类型,又能在运行时强制包装类显式实现(或拦截)被包装类的方法,完美适配权限系统需求。
内容的提问来源于stack exchange,提问作者502E532E
相关产品推荐
相关产品推荐

