Python 装饰器核心原理解析
装饰器本质上是一个接受函数作为参数并返回一个新函数的函数。它允许我们在不修改原函数代码的情况下,为函数添加额外的功能。
简单来说,装饰器就像给礼物盒包装纸。杯子(原函数)的功能是盛水,但为了美观或保护,我们给它套上一个盒子(装饰器)。盒子本身不改变杯子的用途,但赋予了它新的属性。
一、基本概念
下面通过一个例子来解释这个概念。
假设现在有一个普通函数:
def say_hello():
print("你好!")
say_hello()
# 输出:你好!
假如我们想在说'你好'之前和之后都加上一些辅助语句,传统做法是修改原函数。但使用装饰器,我们可以这样做:
def decorator(func):
# 这是具体的装饰逻辑
def wrapper():
print("准备开始啦!")
func() # 这里调用原来的函数
print("结束啦!")
return wrapper
def say_hello():
print("你好!")
# 把 say_hello 这个函数,放进 decorator 这个包装盒里
decorated_say_hello = decorator(say_hello)
# 运行包装后的新函数
decorated_say_hello()
# 输出结果:
# 准备开始啦!
# 你好!
# 结束啦!
通过上面的例子便能理解那句话:'装饰器本质上是一个接受函数作为参数并返回一个新函数的函数。'在 decorator 函数中,参数是另一个函数(func),并且最终返回了一个函数(wrapper)。
很多人对这种嵌套形式可能不太理解。可以这样想:decorator 是装饰器本身,wrapper 是包装层。就像用包装纸包裹苹果,不管材质如何,包裹后就可以说对这个苹果进行了装饰。
更简洁的写法:@ 语法
Python 提供了更简单的语法糖,直接用 @装饰器名字:
def decorator(func):
def wrapper():
print("准备开始啦!")
func()
print("结束啦!")
return wrapper
@decorator # 就像给函数戴上一顶魔法帽子
def say_hello():
print("你好!")
say_hello()
# 直接调用就会自动被装饰
# 输出结果:
# 准备开始啦!
# 你好!
# 结束啦!
二、装饰器的类型
1. 函数装饰器
不带参数的函数装饰器
# 这是一个'加工机器',它会给任何函数加上'说你好'的功能
def say_hello_machine(func):
def wrapper():
print("你好!") # 加上的'巧克力酱'
func() # 原来的圆筒冰淇淋
print("再见!") # 加上的'坚果碎'
return wrapper
@say_hello_machine # 直接把'圆筒'放进'机器'
def my_name():
print("我是小明")
my_name()
# 输出结果:
# 你好!
# 我是小明
# 再见!
带参数的函数装饰器
有时候我们需要动态配置装饰器的行为,比如决定说多少次'你好'。
# 这个机器自己需要一个参数,来决定说多少次你好
def say_hello_machine_times(times):
# 机器的调节按钮
def real_decorator(func):
def wrapper():
for i in range(times):
print("你好!")
func()
return wrapper
return real_decorator
# 使用这个高级机器,把'调节按钮'拧到 3
@say_hello_machine_times(3)
def my_name():
print("我是小明")
my_name()
# 输出结果:
# 你好!
# 你好!
# 你好!
# 我是小明
2. 类装饰器
除了用函数做'加工机器',我们还可以用一个'类'来组装成一个机器。
class MyDecorator:
def __init__(self, func):
self.func = func
def __call__(self):
print("类装饰器说:开始!")
self.func()
print("类装饰器说:结束!")
@MyDecorator
def my_name():
print("我是小明")
my_name()
# 输出结果:
# 类装饰器说:开始!
# 我是小明
# 类装饰器说:结束!
3. 内置装饰器
Python 标准库提供了一些常用的内置装饰器。
(1) @property
它能把一个方法变成属性,调用时无需括号。
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return self.radius * self.radius * 3.14
circle = Circle(10)
print(circle.area) # 直接当属性用,不用加括号!
# 输出:314.0
(2) @classmethod
规定这个方法属于整个类,而不是某个具体对象。
class ThirdGradeClass:
total_students = 0
def __init__(self, name):
self.name = name
ThirdGradeClass.total_students += 1
@classmethod
def get_total_students(cls):
return cls.total_students
xiaoming = ThirdGradeClass("小明")
xiaohong = ThirdGradeClass("小红")
# 调用类方法,不需要通过某个具体的学生,通过类名就可以调用
print(ThirdGradeClass.get_total_students())
# 输出:2
(3) @staticmethod
把一个普通的函数'挂'在类里面,但与类和对象都没有关系,只是一个工具。
class ThirdGradeClass:
@staticmethod
def add_numbers(a, b):
return a + b
result1 = ThirdGradeClass.add_numbers(5, 3)
result2 = xiaoming.add_numbers(10, 2)
print(result1) # 输出:8
print(result2) # 输出:12
三、装饰器的应用场景
在实际开发中,装饰器常用于解耦业务逻辑与横切关注点(如日志、权限、性能监控)。
场景 1:自动日志记录
你想知道哪些函数被执行了,并在控制台记录下来。如果没有装饰器,每个函数都要手动写 print。
没有装饰器:
def build_house():
print("记录:玩家建造了房子!")
print("砰!砰!砰!一栋房子盖好了!")
def mine_ore():
print("记录:玩家挖矿了!")
print("镐子咣咣咣,挖到了钻石!")
如果有一百个动作,就要写一百遍,维护成本极高。
使用装饰器:
def log_action(func):
def wrapper():
print(f"记录:玩家进行了 {func.__name__} 操作!")
func()
return wrapper
@log_action
def build_house():
print("砰!砰!砰!一栋房子盖好了!")
@log_action
def mine_ore():
print("镐子咣咣咣,挖到了钻石!")
build_house()
mine_ore()
效果: 代码变得非常干净。要修改记录格式,只需要改装饰器这一个地方。
场景 2:速度计时
测试一下某个动作耗时多久。
import time
def timer(func):
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print(f"【{func.__name__}】动作耗时:{end_time - start_time:.2f} 秒")
return wrapper
@timer
def chop_tree():
print("咔嚓...咔嚓...")
time.sleep(2)
print("获得木头*5!")
chop_tree()
效果: 不用在 chop_tree 里写任何计时代码,穿上 @timer 装备即可。
场景 3:权限检查
有些操作需要满足特定条件(如等级)才能执行。
player_level = 8
def check_level(required_level):
def decorator(func):
def wrapper():
if player_level >= required_level:
func()
else:
print(f"权限不足!需要 {required_level} 级,你只有 {player_level} 级。")
return wrapper
return decorator
@check_level(required_level=10)
def use_ender_chest():
print("成功打开末影箱,存取物品!")
use_ender_chest()
效果: 权限检查逻辑集中在装饰器内,所有需要检查的函数只需'穿'上装备,无需重复编写 if ... else ...。

