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

Python类装饰器如何保留原类名称?相关实现方法及疑问

类装饰器的元数据保留问题解决方法

首先,你遇到的类名被替换成Wrapper的问题,核心是你的装饰器创建了一个新的子类,但没有把原类的元数据(比如类名、文档字符串等)复制到这个子类上。@wraps之所以在类上报错,是因为它原本就是为函数装饰器设计的,直接用在类定义上不符合它的使用场景。下面一步步解决你的问题:

一、正确保留类名的类装饰器实现

我们可以用functools模块里的update_wrapper函数,它是@wraps的底层实现,能通用处理任何可调用对象(包括类,因为类本身也是可调用的),把原类的元数据复制到新的子类上。结合你的原始需求(装饰器接收create_fn参数),修改后的代码如下:

import functools

def decorate_module(create_fn):
    def decorator(Module):
        class Wrapper(Module):
            def __init__(self, cfg):
                self.c = create_fn(**cfg)
                super().__init__()
        
        # 关键:把原类的元数据复制到Wrapper子类
        functools.update_wrapper(Wrapper, Module)
        return Wrapper
    return decorator

这样处理后,装饰后的Module1、Module2的__name__就会保持原来的名称,而不是变成Wrapper,同时文档字符串、模块信息等也会被保留。

二、为什么@wraps用在类上会报错?

@wraps本质上是一个封装了update_wrapper的函数装饰器,它的设计目标是装饰函数(比如被装饰的函数或方法)。当你把它直接用在class Wrapper定义上方时,它会尝试把类当作函数来处理,自然会触发类型错误。而update_wrapper是直接操作两个对象的属性复制,不区分是函数还是类,所以能正常工作。

三、手动覆盖元数据需要处理哪些属性?

如果你不想用update_wrapper,想手动处理的话,需要覆盖以下几个核心双下划线属性:

  • __name__: 类的显示名称
  • __module__: 类所在的模块路径
  • __doc__: 类的文档字符串(docstring)
  • __qualname__: 类的限定名称(比如嵌套类的完整路径)
  • __annotations__: 类的注解信息(如果有定义的话)

手动处理的代码示例如下(放在return Wrapper之前):

Wrapper.__name__ = Module.__name__
Wrapper.__module__ = Module.__module__
Wrapper.__doc__ = Module.__doc__
Wrapper.__qualname__ = Module.__qualname__
Wrapper.__annotations__ = Module.__annotations__.copy()

不过还是推荐用update_wrapper,它已经帮你封装好了这些逻辑,而且能处理一些边缘情况。

四、@dataclass为什么能保留类名?

dataclass的实现逻辑和你的装饰器不一样:它不会创建新的子类,而是直接在原类的基础上修改——动态添加__init__、__repr__、__eq__等方法到原类中,所以原类的所有元数据(包括类名)都不会被替换,自然不需要额外处理名称问题。

完整使用示例

# 定义create函数
def create_fn1(**cfg):
    return f"资源创建完成:{cfg}"

def create_fn2(**cfg):
    return cfg.get('count', 0) * 3

# 装饰器使用
@decorate_module(create_fn1)
class Module1:
    """这是Module1的文档"""
    def __init__(self):
        self.data = "Module1的内部数据"

@decorate_module(create_fn2)
class Module2:
    def __init__(self):
        self.value = 100

# 测试验证
m1 = Module1({"type": "database", "host": "localhost"})
print(m1.c)  # 输出:资源创建完成:{'type': 'database', 'host': 'localhost'}
print(Module1.__name__)  # 输出:Module1(不再是Wrapper)
print(Module1.__doc__)  # 输出:这是Module1的文档

m2 = Module2({"count": 5})
print(m2.c)  # 输出:15
print(Module2.__name__)  # 输出:Module2

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 16:17:48