← 返回题目列表

Python 的类装饰器是什么?和元类、函数装饰器有什么区别?

中等 第 18 / 21 题 更新于 2026/07/31
Python类装饰器装饰器元类

简化版

类装饰器(class decorator)是「作用在『类』上的装饰器」——它接收一个类作为参数,对这个类做修改/增强(加方法、加属性、注册、包装),然后返回这个类(或一个新类)。语法和函数装饰器一样用 @,只是装饰的目标是类:@register class MyClass: ... 等价于 MyClass = register(MyClass)两层含义:① 「装饰器是个函数/类,作用于类」——def add_repr(cls): cls.__repr__ = ...; return cls(给类加个 __repr__);② 和「函数装饰器」的区别只是目标不同(一个装饰函数、一个装饰类,机制一样)。典型用途:给类批量加方法/属性(@dataclass 就是类装饰器,自动生成 __init__/__repr__/__eq__)、注册类到某个表(插件系统 @register)、包装类(加单例、加日志、验证)、修改类的行为。和元类(metaclass)的区别:类装饰器在「类创建之后」对类做处理(简单、够用大多数场景);元类在「类创建的过程中」介入(更底层、更强大但更复杂、能影响子类)——「能用类装饰器就别用元类」。核心记忆:类装饰器接收类、修改/增强后返回类(@dataclass/@register);比函数装饰器只是目标是类;比元类简单(类创建后处理,不影响子类),能用装饰器就别用元类。

详细版

类装饰器 vs 函数装饰器 vs 元类

维度函数装饰器类装饰器元类
作用对象函数类的创建过程
时机函数定义后类定义后类创建中
影响子类不影响影响(子类也走元类)
复杂度
# 类装饰器:接收类、修改后返回
def add_repr(cls):
    def __repr__(self):
        return f"{cls.__name__}({self.__dict__})"
    cls.__repr__ = __repr__          # 给类加方法
    return cls                        # 返回类

@add_repr
class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

p = Point(1, 2)
print(p)            # Point({'x': 1, 'y': 2})(用装饰器加的 __repr__)
# 等价:Point = add_repr(Point)

# 注册模式(插件系统)
registry = {}
def register(cls):
    registry[cls.__name__] = cls     # 注册到全局表
    return cls

@register
class PluginA: ...
@register
class PluginB: ...
print(registry)     # {'PluginA': ..., 'PluginB': ...}

# 带参数的类装饰器
def register_as(name):
    def decorator(cls):
        registry[name] = cls
        return cls
    return decorator

@register_as("my_plugin")
class MyPlugin: ...

# 单例装饰器(返回包装后的行为)
def singleton(cls):
    instances = {}
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return get_instance              # 注意:这里返回函数(替换了类)

@singleton
class Database: ...
db1 = Database()
db2 = Database()
print(db1 is db2)   # True(单例)

# @dataclass 就是类装饰器(自动生成方法)
from dataclasses import dataclass
@dataclass
class User:
    name: str
    age: int
# 自动生成 __init__/__repr__/__eq__

# 类装饰器(用类实现,而非函数)
class CountInstances:
    def __init__(self, cls):
        self.cls = cls
        self.count = 0
    def __call__(self, *args, **kwargs):
        self.count += 1
        return self.cls(*args, **kwargs)

⚠️ 核心认知:类装饰器和函数装饰器本质是同一回事——都是「@decorator 把下面的目标(函数或类)传给 decorator、用返回值替换原目标」;@add_repr class Point 就是 Point = add_repr(Point);区别只是「装饰的目标是类而非函数」。类装饰器有两种常见「返回」方式:① 返回原类(修改后)——最常见,def deco(cls): cls.新方法 = ...; return cls(给类加方法/属性、注册、然后返回同一个类,类的身份不变);② 返回一个替换品——如单例装饰器返回一个「拦截实例化的函数/对象」(这时 Database 名字指向的其实不是原类了,用 isinstance 等要小心)。类装饰器和元类都能「定制类」,但定位不同:类装饰器在「类已经创建好之后」对它做后处理(拿到成品类,改改它),简单直接、够用绝大多数场景(加方法、注册、包装);元类在「类被创建的那一刻」介入(控制类是怎么造出来的),更底层、更强大(能修改类体的解析、能让所有子类自动应用),但也更复杂、更难懂。Python 社区的共识是「如果类装饰器能做到,就别用元类」——@dataclass 用装饰器而不是元类就是例证。类装饰器的一个局限是「不自动作用于子类」(装饰 A 不会影响 A 的子类 B),需要子类也装饰或改用元类/__init_subclass__

完整版教学

一、类装饰器是什么

先理解类装饰器的本质:

类装饰器:作用在"类"上的装饰器
  接收一个类,处理后返回一个类(或替换品)

语法(和函数装饰器一样):
  @decorator
  class MyClass: ...
  等价于:
  class MyClass: ...
  MyClass = decorator(MyClass)
  → @ 只是语法糖

装饰器做什么:
  decorator 接收 cls(类对象)
  → 可以:加方法/属性、注册、包装、修改
  → 返回 cls(或新的东西)替换原类名

最简单的例子:
  def add_hello(cls):
      cls.hello = lambda self: "hi"   # 给类加方法
      return cls                       # 返回类
  @add_hello
  class C: ...
  C().hello()   # "hi"

类是对象(所以能被装饰):
  class 定义会创建一个"类对象"(type 的实例)
  → 类对象可以被传递、修改(加属性、加方法)
  → 装饰器就是拿到这个类对象来处理

返回值的两种情况:
  ① 返回原类(修改后):类身份不变(最常见)
  ② 返回替换品(新类/函数/对象):类名指向别的东西

所以类装饰器接收类、处理后返回类;@ 是语法糖(MyClass=decorator(MyClass));类是对象可被改

类装饰器:作用在「类」上的装饰器——接收一个类、处理后返回一个类(或替换品)。语法(和函数装饰器一样)@decorator class MyClass: ... 等价于 class MyClass: ...; MyClass = decorator(MyClass)(@ 只是语法糖)。装饰器做什么:decorator 接收 cls(类对象)→ 可以加方法/属性、注册、包装、修改 → 返回 cls(或新的东西)替换原类名。最简单的例子def add_hello(cls): cls.hello = lambda self: "hi"; return cls类是对象(所以能被装饰):class 定义会创建一个「类对象」(type 的实例)、类对象可以被传递/修改、装饰器就是拿到这个类对象来处理。返回值的两种情况:① 返回原类(修改后、类身份不变、最常见)、② 返回替换品(新类/函数/对象、类名指向别的东西)。理解「类装饰器接收类、处理后返回类;@ 是语法糖(MyClass=decorator(MyClass));类是对象(type 的实例)可被修改;返回原类(修改后)或替换品」,就理解了类装饰器。

二、常见用途

理解类装饰器的典型应用:

① 给类批量加方法/属性:
   def add_methods(cls):
       cls.__repr__ = lambda self: f"{cls.__name__}(...)"
       cls.to_dict = lambda self: self.__dict__
       return cls
   → @dataclass 就是这类(自动生成 __init__/__repr__/__eq__)

② 注册(插件系统/工厂):
   registry = {}
   def register(cls):
       registry[cls.__name__] = cls
       return cls
   @register
   class Handler: ...
   → 类被定义时自动注册到表(无需手动登记)

③ 包装/增强类的行为:
   单例、缓存、日志、访问控制
   def singleton(cls):
       instances = {}
       def wrapper(*a, **k):
           if cls not in instances:
               instances[cls] = cls(*a, **k)
           return instances[cls]
       return wrapper   # 拦截实例化

④ 验证/约束类:
   def require_methods(*names):
       def deco(cls):
           for n in names:
               if not hasattr(cls, n):
                   raise TypeError(f"缺少 {n}")
           return cls
       return deco
   @require_methods("save", "load")
   class Model: ...   # 检查类有没有 save/load

⑤ 修改类属性/配置:
   给类设置默认配置、修改类变量等

带参数的类装饰器:
  @register_as("name")   # 装饰器工厂:先接收参数、返回真装饰器
  → def register_as(name): def deco(cls): ...; return deco

所以用途:批量加方法(@dataclass)/注册(插件)/包装(单例)/验证/改配置

类装饰器的典型应用① 给类批量加方法/属性@dataclass 自动生成 __init__/__repr__/__eq__);② 注册(插件系统/工厂)registry[cls.__name__] = cls、类被定义时自动注册无需手动登记);③ 包装/增强类的行为(单例、缓存、日志、访问控制,singleton 拦截实例化);④ 验证/约束类require_methods 检查类有没有某些方法);⑤ 修改类属性/配置带参数的类装饰器@register_as("name")(装饰器工厂:先接收参数、返回真装饰器,def register_as(name): def deco(cls): ...; return deco)。理解「用途:①批量加方法(@dataclass)②注册(插件系统 registry[name]=cls)③包装(单例/缓存/日志)④验证(检查有没有某方法)⑤改配置;带参数用装饰器工厂(接收参数返回真装饰器)」,就掌握了常见用途。

三、类装饰器 vs 函数装饰器

理解两者的异同:

本质相同:都是 @ 语法糖,装饰目标不同

函数装饰器:
  @deco
  def f(): ...
  → f = deco(f)(装饰函数)
  → 通常返回一个"包装函数"(wrapper)

类装饰器:
  @deco
  class C: ...
  → C = deco(C)(装饰类)
  → 通常返回"修改后的类"(或替换品)

区别1:目标不同(函数 vs 类)

区别2:常见返回方式不同
  函数装饰器:常返回 wrapper(包装原函数,加前后逻辑)
  类装饰器:常返回原类本身(修改后,加方法/注册)
           少数返回替换品(如单例返回拦截函数)

区别3:函数装饰器要保留元数据(wraps)
  函数装饰器返回 wrapper → 用 @functools.wraps 保留原函数名/文档
  类装饰器返回原类 → 类身份不变,一般不需要 wraps
  (若返回新类/函数,才需要处理元数据)

共同点:
  ① 都用 @ 语法(语法糖)
  ② 都可带参数(装饰器工厂)
  ③ 都能用函数或类实现装饰器
  ④ 都能堆叠(多个装饰器)

堆叠顺序(从下往上应用):
  @a
  @b
  class C: ...
  → C = a(b(C))(先 b 后 a)

所以类装饰器和函数装饰器本质相同(@语法糖),只是装饰目标是类(常返回修改后的类)

本质相同:都是 @ 语法糖、装饰目标不同函数装饰器@deco def f()f = deco(f)(装饰函数、通常返回包装函数 wrapper)。类装饰器@deco class CC = deco(C)(装饰类、通常返回修改后的类或替换品)。区别1:目标不同(函数 vs 类)区别2:常见返回方式不同(函数装饰器常返回 wrapper 包装原函数、类装饰器常返回原类本身修改后、少数返回替换品如单例)。区别3:函数装饰器要保留元数据(wraps)(函数装饰器返回 wrapper 用 @functools.wraps 保留原函数名/文档、类装饰器返回原类身份不变一般不需要 wraps)。共同点:都用 @ 语法、都可带参数(装饰器工厂)、都能用函数或类实现、都能堆叠。堆叠顺序(从下往上应用)@a @b class CC = a(b(C))(先 b 后 a)。理解「类装饰器和函数装饰器本质相同(@语法糖);目标不同(函数 vs 类);类装饰器常返回修改后的原类、函数装饰器常返回 wrapper;函数装饰器要 wraps 保留元数据、类装饰器返回原类一般不需要;都可带参数堆叠」,就掌握了两者异同。

四、类装饰器 vs 元类

理解类装饰器和元类的区别与选择:

两者都能"定制类",但介入时机和能力不同:

类装饰器:类创建"之后"的后处理
  class C: ...        # 类先正常创建
  C = decorator(C)    # 然后装饰器加工成品类
  → 拿到已经造好的类,改改它(加方法、注册)
  → 简单、直接、易懂

元类:类创建"之中"的介入
  class C(metaclass=Meta): ...
  → Meta.__new__/__init__ 控制"类是怎么被造出来的"
  → 能修改类体解析、控制继承、影响所有子类
  → 底层、强大、复杂

关键区别1:影响子类
  类装饰器:只装饰"被装饰的那个类",不影响子类
    @deco class A: ...
    class B(A): ...   # B 不会自动被 deco 装饰!
  元类:子类自动继承元类
    class A(metaclass=Meta): ...
    class B(A): ...   # B 也走 Meta(自动应用)

关键区别2:能力
  类装饰器:能做"后处理"(加方法、注册、包装)
  元类:能做"创建时控制"(拦截类体、验证类定义、
        自动注册所有子类、修改 MRO 等更底层的事)

选择原则:"能用类装饰器就别用元类"
  ① 加方法/属性、注册、包装 → 类装饰器(简单)
  ② 要影响所有子类、控制类创建过程 → 元类(或 __init_subclass__)
  ③ __init_subclass__(3.6+)是"轻量元类"替代
     (子类创建时的钩子,比元类简单、能影响子类)

@dataclass 的选择:用装饰器(不用元类)
  → 因为只需"给类加方法",装饰器够了、更简单

所以类装饰器=类创建后处理(简单、不影响子类)、元类=类创建中介入(强大、影响子类);能用装饰器就别用元类

两者都能「定制类」,但介入时机和能力不同类装饰器:类创建「之后」的后处理(类先正常创建、然后装饰器加工成品类、拿到已造好的类改改它、简单直接易懂);元类:类创建「之中」的介入class C(metaclass=Meta)Meta.__new__/__init__ 控制类是怎么被造出来的、能修改类体解析/控制继承/影响所有子类、底层强大复杂)。关键区别1:影响子类——类装饰器只装饰被装饰的那个类不影响子类(@deco class Aclass B(A) 不会自动被 deco 装饰)、元类子类自动继承元类(class B(A) 也走 Meta)。关键区别2:能力——类装饰器做后处理(加方法/注册/包装)、元类做创建时控制(拦截类体/验证类定义/自动注册所有子类/修改 MRO)。选择原则:「能用类装饰器就别用元类」——加方法/注册/包装用类装饰器(简单)、要影响所有子类/控制类创建过程用元类(或 __init_subclass__)、__init_subclass__(3.6+)是「轻量元类」替代。理解「类装饰器=类创建后处理(简单、不影响子类)、元类=类创建中介入(强大、影响所有子类);类装饰器不影响子类(@deco A 不影响 B(A))、元类影响子类;能用类装饰器就别用元类;@dataclass 用装饰器」,就掌握了类装饰器 vs 元类。

五、注意事项与陷阱

理解类装饰器的坑:

陷阱1:返回替换品时,类身份变了
  @singleton   # 返回的是拦截函数,不是原类
  class DB: ...
  print(type(DB))          # <class 'function'>(不是 type!)
  isinstance(db, DB)       # 可能出问题(DB 不是类了)
  → 返回替换品时,isinstance/issubclass/类型检查要小心
  → 尽量返回原类(修改后),保持类身份

陷阱2:不影响子类
  @add_repr class Base: ...
  class Sub(Base): ...      # Sub 继承了 __repr__(因为是加在 Base 上)
  → 但如果装饰器返回"新类",子类继承的是新类还是原类要注意
  → 加方法到 cls 上(return cls)→ 子类能继承(正常)
  → 返回包装(wrapper)→ 子类继承 wrapper 可能出问题

陷阱3:装饰器顺序(堆叠)
  @a @b class C → C = a(b(C))
  → 顺序影响结果(尤其都修改类时)

陷阱4:带参数的装饰器多一层
  @register_as("x") 是"装饰器工厂"
  def register_as(name):     # 接收参数
      def deco(cls):         # 真装饰器
          ...
          return cls
      return deco
  → 别忘了工厂返回装饰器、装饰器返回类

最佳实践:
  ① 优先返回原类(修改后),保持类身份
  ② 加方法/属性直接改 cls(return cls)
  ③ 需要影响子类 → 用元类或 __init_subclass__
  ④ 带参数用装饰器工厂
  ⑤ 能用装饰器就别用元类(简单优先)

所以陷阱:返回替换品类身份变(isinstance 出问题)、不影响子类、堆叠顺序、带参多一层

类装饰器的坑陷阱1:返回替换品时,类身份变了@singleton 返回拦截函数不是原类、type(DB) 是 function 不是 type、isinstance/类型检查要小心、尽量返回原类保持身份);陷阱2:不影响子类(加方法到 cls 上 return cls 子类能继承正常、返回包装 wrapper 子类继承可能出问题);陷阱3:装饰器顺序(堆叠)@a @b class CC = a(b(C))、顺序影响结果);陷阱4:带参数的装饰器多一层@register_as("x") 是装饰器工厂:接收参数返回真装饰器、真装饰器返回类)。最佳实践① 优先返回原类(修改后)保持身份、② 加方法/属性直接改 cls(return cls)、③ 需要影响子类用元类或 __init_subclass__、④ 带参数用装饰器工厂、⑤ 能用装饰器就别用元类。理解「陷阱:①返回替换品类身份变(type 变 function、isinstance 出问题、尽量返回原类)②不影响子类③堆叠顺序 a(b(C))④带参数多一层(工厂返回装饰器);最佳实践返回原类+直接改 cls+影响子类用元类/init_subclass」,就掌握了注意事项。

六、总结与实践

总结类装饰器:

核心:
  类装饰器 = 作用在类上的装饰器
  接收类、处理(加方法/注册/包装)、返回类
  @deco class C → C = deco(C)

常见用途:
  批量加方法(@dataclass)、注册(插件)、
  包装(单例/缓存)、验证、改配置

vs 函数装饰器:
  本质相同(@ 语法糖)、只是目标是类
  类装饰器常返回修改后的原类(不太需要 wraps)

vs 元类:
  类装饰器:类创建后处理(简单、不影响子类)
  元类:类创建中介入(强大、影响所有子类)
  → 能用类装饰器就别用元类

带参数:装饰器工厂(接收参数 → 返回装饰器 → 返回类)

关键陷阱:
  ① 返回替换品 → 类身份变(isinstance 出问题)
  ② 不自动影响子类(要影响用元类/__init_subclass__)

实践建议:
  ① 加方法/注册/包装 → 类装饰器(优先返回原类)
  ② 影响所有子类 → __init_subclass__ 或元类
  ③ 简单优先(能装饰器就别元类)

核心总结:
  类装饰器接收类、修改后返回类(@dataclass/@register)
  比函数装饰器只是目标是类
  比元类简单(创建后处理、不影响子类)

所以类装饰器接收类、修改返回类,比函数装饰器目标是类,比元类简单(不影响子类)

核心:类装饰器 = 作用在类上的装饰器(接收类、处理加方法/注册/包装、返回类、@deco class CC = deco(C))。常见用途:批量加方法(@dataclass)、注册(插件)、包装(单例/缓存)、验证、改配置。vs 函数装饰器:本质相同(@ 语法糖)、只是目标是类(类装饰器常返回修改后的原类、不太需要 wraps)。vs 元类:类装饰器类创建后处理(简单、不影响子类)、元类类创建中介入(强大、影响所有子类)——能用类装饰器就别用元类。带参数:装饰器工厂。关键陷阱:返回替换品类身份变、不自动影响子类。理解「类装饰器接收类、修改后返回类(@dataclass/@register);比函数装饰器只是目标是类;比元类简单(创建后处理、不影响子类);带参数用工厂;陷阱返回替换品身份变、不影响子类;能装饰器就别元类」,就掌握了总结与实践。

记忆钩子:「类装饰器(class decorator)是作用在『类』上的装饰器——接收一个类作为参数,对它做修改/增强(加方法、加属性、注册、包装),然后返回这个类(或替换品);语法和函数装饰器一样用@:@register class MyClass 等价于 MyClass=register(MyClass);★两种返回方式:①返回原类(修改后)——最常见,def deco(cls):cls.新方法=…;return cls(给类加方法/属性/注册,类身份不变)②返回替换品——如单例装饰器返回拦截实例化的函数(这时类身份变了、type(DB)变成 function、isinstance 要小心);★典型用途:给类批量加方法(@dataclass 就是类装饰器,自动生成__init__/repr/eq)、注册到表(插件系统@register)、包装(单例/缓存/日志)、验证类;★和函数装饰器区别只是目标不同(一个装饰函数常返回 wrapper 要用 functools.wraps、一个装饰类常返回修改后的原类不太需要 wraps),机制一样;★和元类(metaclass)的区别:类装饰器在『类创建之后』对成品类做后处理(简单、够用大多数场景、但★不影响子类——@deco class A 不会自动装饰 A 的子类 B),元类在『类创建的过程中』介入(更底层强大、能影响所有子类但更复杂);共识『能用类装饰器就别用元类』(@dataclass 用装饰器);要影响子类用__init_subclass__(3.6+,轻量元类替代)或元类;带参数用装饰器工厂」

七、常见误区与追问

  • 误区:类装饰器和函数装饰器是完全不同的机制。 本质相同——都是 @decorator 语法糖:@deco class C: ... 就是 C = deco(C)@deco def f(): ... 就是 f = deco(f);区别只在「装饰的目标是类还是函数」;机制、语法、带参数(装饰器工厂)、堆叠都一样;唯一实践差异是「常见的返回方式」——函数装饰器常返回一个包装函数(要用 functools.wraps 保留原函数元数据),类装饰器常返回修改后的原类(类身份不变、一般不需要 wraps)。
  • 误区:类装饰器一定要返回原来那个类。 不一定——常见的是返回原类(修改后,加了方法/属性/注册的同一个类,身份不变,最推荐),但也可以返回一个「替换品」(新类、函数、或包装对象);比如单例装饰器 singleton 通常返回一个「拦截实例化的函数」,这时被装饰的名字(如 Database)实际指向的不再是原类而是那个函数、type(Database) 会是 function 而非 type、isinstance 检查也会受影响;所以「返回替换品」要小心类型检查,尽量优先「返回修改后的原类」保持身份。
  • 误区:给一个类加了类装饰器,它的子类也会自动被装饰。 不会——类装饰器只作用于「被它直接装饰的那个类」,不会自动应用到子类:@deco class A: ... 之后 class B(A): ...,B 不会被 deco 装饰(除非 B 也显式加 @deco);不过如果装饰器是「往类上加方法/属性并 return cls」,那么子类会通过正常的继承机制继承到这些方法(因为它们加在了父类 A 上);但装饰器本身的「处理逻辑」(如注册、验证)只对 A 执行一次、不会对 B 执行;如果需要「所有子类都自动应用某处理」,要用元类或 __init_subclass__
  • 误区:要定制类的创建就必须用元类。 不一定——很多「定制类」的需求(给类加方法/属性、注册类、包装类的实例化、验证类定义)用「类装饰器」就能优雅完成,且比元类简单得多;元类是「类创建过程中介入」的重武器,主要用于「需要影响所有子类、控制类体解析、深度定制类创建」的场景;Python 社区的共识是「能用类装饰器(或 __init_subclass__)就别用元类」;@dataclass 就是用类装饰器而非元类实现的典范;只有当类装饰器和 __init_subclass__ 都做不到时才考虑元类。
  • 追问:类装饰器和元类都能定制类,它们有什么区别,怎么选? 主要区别在「介入时机」和「是否影响子类」:① 类装饰器在「类已经创建好之后」对成品类做后处理——你拿到一个造好的类对象、给它加方法/属性、注册它、或包装它,简单直接、易懂,但它只作用于被装饰的那个类、不会自动影响子类;② 元类在「类被创建的那一刻」介入——通过 metaclass=Meta,元类的 __new__/__init__ 控制「类是怎么被造出来的」,能修改类体的解析、验证类定义、修改 MRO,而且子类会自动继承元类(所有子类都走同样的元类逻辑),更底层、更强大但也更复杂难懂;选择原则是「能用类装饰器就别用元类」(简单优先):加方法/注册/包装/验证用类装饰器;需要「影响所有子类、控制类创建过程」时,优先考虑 __init_subclass__(3.6+ 引入的子类创建钩子,是轻量的元类替代、能影响子类但比元类简单),实在不够再用元类;@dataclass 选择用装饰器就是因为「只需给类加方法」、装饰器足够。
  • 追问:怎么写一个带参数的类装饰器? 用「装饰器工厂」——多套一层函数:最外层函数接收装饰器的参数、返回真正的装饰器(真正的装饰器接收类、处理后返回类);比如实现 @register_as("my_plugin") class P: ...def register_as(name):(接收参数 name) def decorator(cls):(真装饰器、接收类) registry[name] = cls(用参数 name) return cls(返回类) return decorator(工厂返回装饰器);调用时 @register_as("my_plugin") 先执行 register_as("my_plugin") 得到 decorator、再用 decorator 装饰类 P(即 P = register_as("my_plugin")(P));关键是理解「带参数 = 三层:工厂(参数) → 装饰器(类) → 返回类」,比无参数的类装饰器多一层。
  • 追问:类装饰器有哪些实际应用场景? 常见的有:① 自动生成样板方法——@dataclass 根据类的字段注解自动生成 __init____repr____eq__(甚至 __lt____hash__),省去手写;@functools.total_ordering 补全比较方法;② 注册到表——插件系统/工厂模式里 @register 让类被定义时自动登记到全局注册表(registry[cls.__name__] = cls),无需手动维护映射;③ 包装类的行为——@singleton(单例,拦截实例化保证只有一个实例)、缓存实例、给所有实例方法加日志或访问控制;④ 验证/约束类定义——检查类是否实现了某些必需的方法/属性,不满足就在定义时报错(提前发现);⑤ 给类批量设置默认配置、修改类变量;这些场景的共同点是「拿到一个类、对它做统一的加工/登记/包装」,用类装饰器比手写、比元类都更简洁清晰。

八、加强记忆

类装饰器(class decorator)是作用在「类」上的装饰器——接收一个类作为参数、对它做修改/增强(加方法、加属性、注册、包装),然后返回这个类(或替换品);语法和函数装饰器一样用 @@register class MyClass 等价于 MyClass = register(MyClass)两种返回方式① 返回原类(修改后)——最常见,def deco(cls): cls.新方法 = ...; return cls(给类加方法/属性/注册、类身份不变);② 返回替换品——如单例装饰器返回拦截实例化的函数(这时类身份变了、type(DB) 变成 function、isinstance 要小心)。典型用途:给类批量加方法(@dataclass 就是类装饰器、自动生成 __init__/__repr__/__eq__)、注册到表(插件系统 @register)、包装(单例/缓存/日志)、验证类。和函数装饰器区别只是目标不同(一个装饰函数常返回 wrapper 要用 functools.wraps、一个装饰类常返回修改后的原类不太需要 wraps),机制一样。和元类(metaclass)的区别:类装饰器在「类创建之后」对成品类做后处理(简单、够用大多数场景、但不影响子类——@deco class A 不会自动装饰 A 的子类 B),元类在「类创建的过程中」介入(更底层强大、能影响所有子类但更复杂);共识「能用类装饰器就别用元类」;要影响子类用 __init_subclass__(3.6+、轻量元类替代)或元类。一句话「类装饰器接收类、修改/增强(加方法/注册/包装)后返回类(@dataclass/@register);比函数装饰器只是目标是类;比元类简单(类创建后处理、不影响子类),能用装饰器就别用元类;返回替换品会改变类身份要小心;带参数用装饰器工厂」。