提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

一、什么是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的装饰器这个语法,以及常用的装饰器例子

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐