Python---装饰器
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、什么是python装饰器?
Python的装饰器可以理解为是一种“功能增强工具”,打个比方就如在你的手机上套一个保护壳,手机本身的功能并没有改变,但是多了保护、防摔的新功能
装饰器的核心作用:在不修改原函数代码的前提下,给函数加额外的功能
比如给函数加日志,给函数加计时,给函数加权限校验
现在可以给大家举一个通俗的例子
# 1. 定义装饰器:负责加新功能
def 加礼仪(原函数): # 接收原函数作为参数
def 包装函数(): # 包装原函数,加新功能
print("鞠躬~") # 调用原函数前的新功能
原函数() # 调用原函数(核心功能不变)
print("挥手~") # 调用原函数后的新功能
return 包装函数 # 返回包装好的新函数
# 2. 用装饰器给原函数加功能(语法糖:@装饰器名)
@加礼仪
def 打招呼():
print("你好呀!")
# 3. 调用函数
打招呼()
可以看出没有修改打招呼()本身的代码,但是调用时自动多了鞠躬和挥手的功能
所以加装饰器的好处在于:
1.不破坏源代码
2.代码复用
3.灵活切换
二、示例
1.用装饰器统计函数运行时间
代码如下(示例):
import time
def calculate_time(original_function):
def wrapped(*args,**kwargs):
start_time=time.time()
result=original_function(*args,**kwargs)
end_time=time.time()
run_time=end_time-start_time
print(f"函数{original_function.__name__}运行耗时为:{run_time:.4f}秒")
return result
return wrapped
# 2. 使用装饰器:给函数添加计时功能
@calculate_time # 等价于:sum_numbers = calculate_time(sum_numbers)
def sum_numbers(n):
"""计算1到n的累加和"""
total = 0
for i in range(1, n+1):
total += i
return total
# 3. 调用函数,查看效果
print("累加结果:", sum_numbers(1000000))

2.用类装饰器记录函数调用次数
代码如下(示例):
class CountCalls:
# 1. 初始化:接收被装饰的函数作为参数
def __init__(self, original_function):
self.original_function = original_function # 保存原函数
self.call_count = 0 # 维护状态:记录调用次数
# 2. 实现__call__方法:使类的实例可被调用(核心逻辑)
def __call__(self, *args, **kwargs):
# 每次调用时,次数+1
self.call_count += 1
# 打印调用信息
print(f"函数 {self.original_function.__name__} 已被调用 {self.call_count} 次")
# 调用原函数,并返回结果(*args和**kwargs确保参数正确传递)
return self.original_function(*args, **kwargs)
# 3. 使用类装饰器:用@类名装饰函数
@CountCalls
def add(a, b):
return a + b
@CountCalls
def greet(name):
return f"Hello, {name}!"
# 4. 测试调用
print(add(2, 3)) # 第一次调用add
print(add(5, 7)) # 第二次调用add
print(greet("Alice")) # 第一次调用greet
print(greet("Bob")) # 第二次调用greet
运行结果如下:
3.python内置装饰器
Python 提供了一些常用的内置装饰器,它们是 Python 标准库的一部分,用于解决特定场景的问题(如类属性管理、方法类型定义、缓存等)。下面介绍最常用的几个:
(1)@property:将方法转为属性访问
作用:把类中的方法 “伪装” 成属性,让你可以像访问普通属性一样调用方法,同时能控制属性的读取、修改、删除逻辑(比如做参数校验)。
场景:直接暴露类的属性(如 self.age)可能导致赋值不合法(比如年龄为负数)。用 @property 可以在访问 / 修改属性时自动执行校验逻辑。
class Person:
def __init__(self, name, age):
self.name = name
self._age = age # 用下划线表示“私有”属性(约定)
# 1. 定义 getter:用 @property 装饰,作为属性的“读”操作
@property
def age(self):
print("获取年龄...")
return self._age # 返回真正存储的值
# 2. 定义 setter:用 @属性名.setter 装饰,作为属性的“写”操作
@age.setter
def age(self, value):
print("设置年龄...")
if value < 0 or value > 150:
raise ValueError("年龄必须在 0-150 之间!")
self._age = value # 校验通过后才赋值
# 3. 定义 deleter:用 @属性名.deleter 装饰,作为属性的“删除”操作
@age.deleter
def age(self):
print("删除年龄...")
del self._age
# 使用
p = Person("Alice", 30)
# 像访问属性一样调用,实际执行 @property 装饰的 age 方法(getter)
print(p.age) # 输出:获取年龄... 30
# 赋值时,实际执行 @age.setter 装饰的 age 方法(setter)
p.age = 25 # 输出:设置年龄...
print(p.age) # 输出:获取年龄... 25
# 尝试赋值不合法的值,会触发校验
# p.age = 200 # 报错:ValueError: 年龄必须在 0-150 之间!
# 删除时,执行 @age.deleter 装饰的方法
del p.age # 输出:删除年龄...
(2)classmethod:定义类方法
作用:标记一个方法为”类方法“,它属于类本身,而非类的实例,调用时不需要创建实例,直接通过类名调用;方法的第一个参数时cls(代表类本身),而非self(实例)
场景:
实现“备选构造函数”(从不同参数创建实例)
处理与类相关的逻辑(如统计类的实例数量)
实例代码如下
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
@classmethod
def from_string(cls,date_str):#第一个参数时cls(代表Date类)
year,month,day=map(int,date_str.split("-"))
return cls(year,month,day)
#调用类方法:直接用类名调用,不需要先创建实例子
d=Date.from_string("2025-10-01")
print(d.year,d.month,d.day)
让方法与类绑定,方便通过类直接操作,适合定义与类相关的工具方法。
3. @staticmethod:定义静态方法
作用:标记一个方法为“静态方法”,它是类中的普通函数,既不依赖类(无cls参数),也不依赖实例(无self参数)
场景:实现与类相关,但不需要访问类或实例状态的功能
class MathUtils:
# 静态方法:计算两个数的最大公约数
@staticmethod
def gcd(a, b): # 无 self 或 cls 参数
while b:
a, b = b, a % b
return a
# 调用:可以通过类名或实例调用(但通常用类名)
print(MathUtils.gcd(12, 18)) # 输出:6
# 也可以创建实例调用(但没必要,静态方法与实例无关)
mu = MathUtils()
print(mu.gcd(24, 36)) # 输出:12
在类中封装独立的工具函数,避免污染全局命名空间,同时表明该函数与类的关联性。
总结
主要是介绍了一下python的装饰器这个语法,以及常用的装饰器例子
更多推荐



所有评论(0)