装饰器是Python中最强大但也最难理解的概念之一。很多人被它绕晕了。今天用最直白的方式,让你彻底搞懂装饰器的原理和实战用法!
★ P1:装饰器的本质——函数即对象
理解装饰器之前,先要明白一个核心概念:Python中函数也是对象,可以像变量一样传递。这是一切的基础。
函数作为返回值:
def outer(): def inner(): print('inner执行了') return inner# 返回函数对象,不是调用它 result = outer()# result现在就是inner函数 result()# 调用result,执行inner # 输出:inner执行了
函数作为参数:
def add(a, b): return a + b def multiply(a, b): return a * b def apply_twice(func, x, y): return func(func(x, y), func(x, y)) print(apply_twice(add, 1, 2))# (1+2)+(1+2) = 6 print(apply_twice(multiply, 2, 3)) # (2*3)*(2*3) = 36
★ P2:手写第一个装饰器——理解原理
装饰器本质上就是一个"包装"函数。它接收一个函数作为参数,返回一个新的函数,在不改变原函数的基础上添加新功能。
最简单的装饰器:
def my_decorator(func): def wrapper(*args, **kwargs): print('=== 调用前 ===') result = func(*args, **kwargs)# 调用原函数 print('=== 调用后 ===') return result return wrapper @my_decorator def say_hello(name): print(f'你好,{name}!') say_hello('Rose') # 输出: # === 调用前 === # 你好,Rose! # === 调用后 ===
★ P3:实战装饰器——计时器、登录验证、日志
装饰器最大的价值是"横切关注点分离":把分散在多个函数中的重复逻辑(计时、认证、日志)抽离出来,统一处理。
装饰器1:函数计时器
import time from functools import wraps def timer(func): @wraps(func)# 保留原函数元数据 def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) elapsed = time.time() - start print(f'{func.__name__} 执行耗时: {elapsed:.4f}秒') return result return wrapper @timer def slow_function(): time.sleep(1) return '完成' slow_function()# slow_function 执行耗时: 1.0004秒
装饰器2:登录验证
from functools import wraps def login_required(func): @wraps(func) def wrapper(*args, **kwargs): if not getattr(func, 'is_logged_in', False): print('❌ 请先登录!') return None return func(*args, **kwargs) # 模拟登录标记 wrapper.is_logged_in = True return wrapper @login_required def get_profile(): print('✅ 获取用户资料...') get_profile()# ✅ 获取用户资料...
装饰器3:日志记录器
import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def log_calls(func): @wraps(func) def wrapper(*args, **kwargs): logger.info(f'调用 {func.__name__},参数: {args}, {kwargs}') try: result = func(*args, **kwargs) logger.info(f'{func.__name__} 返回: {result}') return result except Exception as e: logger.error(f'{func.__name__} 异常: {e}') raise return wrapper
★ P4:装饰器传参——带参数的装饰器工厂
如果装饰器本身需要参数,需要再包一层函数来接收参数,这叫"装饰器工厂"。
带参数的装饰器:
def repeat(times):# 接收装饰器参数 def decorator(func):# 接收被装饰函数 @wraps(func) def wrapper(*args, **kwargs): for _ in range(times): result = func(*args, **kwargs) return result return wrapper return decorator @repeat(times=3) def say_hi(): print('Hi!') say_hi()# 输出3次'Hi!'
@wraps的作用:保留原函数的__name__、__doc__等元数据,不用@wraps的话,装饰后函数名会变成wrapper。
★ P5:总结
今天彻底讲透了Python装饰器,从原理到实战一网打尽。核心要点: 1. 函数即对象:函数可以赋值给变量、作为参数、作为返回值,这是装饰器的基础;2. 装饰器本质:一个返回函数的函数,接收原函数作为参数,在不修改原函数的情况下添加新功能;3. @wraps必须用:保留被装饰函数的元数据(名字、文档字符串),避免调试困扰 4. args/kwargs:wrapper函数用*args/**kwargs接收任意参数转发给原函数;5. 装饰器工厂:如果装饰器需要参数,再包一层函数返回装饰器;6. 实战场景:计时器(性能分析)、登录验证(权限控制)、日志(行为追踪)、重试机制(容错处理)。实践建议:不要为了用装饰器而用装饰器。当你在多个函数中写重复的前后置逻辑(如检查参数、记录日志、测量时间)时,就是抽取成装饰器的最佳时机。装饰器是代码重复的克星,用好了事半功倍!
-------------------------它是数字世界里的一把杀猪刀
却总能巧夺天工
它的世界是纯粹0、1组合
却总能创造无尽幻想
......
本公众号关注数据价值分析、编程学习,将不定期更新社会热点数据分析结果、编程技巧,分享数据分析工具、方法、学习等内容,欢迎有兴趣的小伙伴加入。