十个 Python 技术面试必考的装饰器题目,搞懂这些,面试官直点头.
面试装饰器时,很多人只会背语法糖。我跟几个大厂面试官聊过,他们真正想听的,是你对函数、闭包、作用域的理解。下面十道题,全是硬骨头。
1. 一个函数被多个装饰器装饰,执行顺序是怎样的
代码写上
@decorator_a @decorator_b
def myfunc():
pass
实际执行是 myfunc = decorator_a(decorator_b(myfunc))。离函数最近的装饰器先处理,然后一层层向外包。面试官想看你能不能说出这个栈结构。
2. 写一个装饰器,统计函数每次调用的时间
核心是记录调用前后的时间差。注意不能用全局变量,要用闭包保存起始时间。实现时记得用 functools.wraps 保留原函数信息,否则元信息会丢失。
import time from functools import wraps
def timer(func):
@wraps(func)
def wrapper(args, kwargs):
start = time.time()
result = func(args, kwargs)
print(f'{func.__name__} 耗时 {time.time()-start:.2f} 秒')
return result
return wrapper
3. 如何给装饰器传参数
需要三层嵌套。最外层接收参数,中间层接收函数,内层处理逻辑。
def repeat(n): def decorator(func):
@wraps(func)
def wrapper(args, kwargs):
for _ in range(n):
func(args, kwargs)
return wrapper
return decorator
@repeat(3)
def say_hello():
print('你好')
4. 类装饰器怎么写
实现 __call__ 方法,让类的实例可调用。类装饰器的好处是可以保存状态。
class CountCalls: def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, args, kwargs):
self.count += 1
print(f'第 {self.count} 次调用')
return self.func(args, kwargs)
5. 装饰器如何保留原函数的元信息
很多人的装饰器会覆盖函数名和文档。用 @wraps(func) 手动复制 __name__、__doc__、__module__。这是面试高频点。
6. 写一个装饰器,限制函数调用频率
记录每次调用的时间戳,放进列表。每次调用前检查过去n秒内的调用次数。
def rate_limiter(max_calls, period): def decorator(func):
calls = []
@wraps(func)
def wrapper(args, kwargs):
now = time.time()
calls[:] = [t for t in calls if now - t < period] if len(calls) >= max_calls:
raise Exception('超频了')
calls.append(now)
return func(args, kwargs)
return wrapper
return decorator
7. 装饰器如何处理被装饰函数的返回值
不少人忘记返回结果。wrapper函数里必须 return func(args, kwargs),不然所有函数都变成返回None。面试时直接写这一行,面试官就知道你懂。
8. 用装饰器实现单例模式
把类的实例存在闭包里,每次调用类前检查实例是否存在。
def singleton(cls): instances = {}
@wraps(cls)
def get_instance(args, kwargs):
if cls not in instances:
instances[cls] = cls(args, kwargs)
return instances[cls]
return get_instance
@singleton
class Database:
pass
9. 如何判断一个函数是否被装饰了
检查函数有没有 __wrapped__ 属性。使用 @wraps 后,这个属性会被设置。也可以用 inspect.ismethod 或 type(func).__name__ 判断。面试官喜欢追问这个。
10. 装饰器里出现异常如何处理
有两种做法。一种是在wrapper里加try-except,捕获异常后记录日志再重新抛出。另一种是定义错误处理函数,由使用者决定怎么处理。
def error_handler(func): @wraps(func)
def wrapper(args, kwargs):
try:
return func(args, kwargs)
except Exception as e:
print(f'出错啦: {e}')
raise
return wrapper
这几个题目刷完,面试官问你装饰器,你就不用慌了。关键是自己动手敲一遍,别光看。