问题: 装饰器可以装饰函数,也可以装饰类吗?

👇 点击展开/折叠 面试背诵模板(建议先背这个) 可以。装饰器可装饰任何可调用对象,包括函数、方法和类。装饰函数返回增强后的函数,装饰类返回增强后的类。两者本质相同:接收一个可调用对象,返回一个增强版本。区别在于装饰类时通常返回原类(添加属性/方法)或返回其实例(实现单例/注册)。类装饰器常用于添加类属性、注册到全局 registry、实现单例模式等。
---

装饰类的两种典型场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 场景1:为类添加属性或方法(返回原类)
def add_method(cls):
def new_method(self):
return "动态添加的方法"
cls.new_method = new_method
cls.author = "admin"
return cls

@add_method
class User:
pass

u = User()
print(u.new_method()) # "动态添加的方法"
print(User.author) # "admin"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 场景2:替换为实例(返回实例对象)
def singleton(cls):
instances = {}
def wrapper(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper # 返回函数,而非类!

@singleton
class Database:
def __init__(self):
self.connected = True

d1 = Database() # 实际调用的是 wrapper,返回同一个实例
d2 = Database()
print(d1 is d2) # True

装饰类 vs 装饰函数的返回结果对比

装饰目标 接收参数 返回结果 典型操作
函数 函数对象 函数对象(wrapper 闭包) 计时、日志、权限校验
类 类对象 类对象(原类增强)或实例对象 添加方法/属性、注册、单例

类实现装饰器 vs 函数实现装饰器(装饰类时)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 函数方式实现类装饰器(简洁)
def add_logging(cls):
def log_wrapper(self):
print(f"调用 {self.__class__.__name__}")
cls.log = log_wrapper
return cls

# 类方式实现类装饰器(适合带状态)
class AddAttribute:
def __init__(self, attr_name, attr_value):
self.attr_name = attr_name
self.attr_value = attr_value
def __call__(self, cls):
setattr(cls, self.attr_name, self.attr_value)
return cls

@AddAttribute("version", "1.0")
class Service:
pass
print(Service.version) # "1.0"

装饰类时的注意事项:保留元数据

functools.wraps 对类无效,因为类的 __name__、__doc__ 等属性是描述符,不能直接复制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# ⚠️ 类装饰器会覆盖类的元数据
def rename(cls):
cls.__name__ = "RenamedClass"
return cls

@rename
class Original:
pass

print(Original.__name__) # "RenamedClass"(被覆盖了)

# ✅ 手动保留:用 update_wrapper 或直接复制
from functools import update_wrapper

def preserve_meta(cls):
original_name = cls.__name__
original_doc = cls.__doc__
def decorator(cls):
cls.__name__ = original_name
cls.__doc__ = original_doc
return cls
return decorator