云计算、AI、云原生、大数据等一站式技术学习平台

网站首页 > 教程文章 正文

Python学不会来打我(39)装饰器详解:从函数闭包到实战应用

jxf315 2025-07-27 21:23:49 教程文章 2 ℃

在Python中,装饰器(Decorator) 是一个非常强大且独特的功能,它允许我们“包装”或“增强”现有函数的功能,而无需修改其源代码。这使得我们可以写出更优雅、可复用性更强的代码。

本文将详细讲解 Python装饰器的功能、使用方法、常见场景,并通过大量示例帮助你全面掌握这一重要概念。文章适合Python初学者阅读,也适合有一定基础的开发者复习巩固。


一、什么是装饰器?

装饰器本质上是一个函数,它接受另一个函数作为参数,并返回一个新的函数。

装饰器的主要作用是:

  • 在不修改原函数代码的前提下,为其添加新功能
  • 实现逻辑复用(如日志记录、权限检查、计时等)
  • 支持面向切面编程(AOP

装饰器的本质:高阶函数 + 函数闭包

def my_decorator(func):
    def wrapper():
        print("装饰器前置操作")
        func()
        print("装饰器后置操作")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

输出:

装饰器前置操作
Hello!
装饰器后置操作

在这个例子中:

  • my_decorator 是装饰器函数
  • say_hello 是被装饰的函数
  • 使用 @my_decorator 语法糖自动将 say_hello 传入装饰器并替换为返回的新函数

二、装饰器的基本结构与执行流程

示例:装饰器的等价写法

上面的例子也可以写成:

def say_hello():
    print("Hello!")

say_hello = my_decorator(say_hello)
say_hello()

这说明装饰器其实就是一个函数包装的过程。

执行流程图解:

[原始函数] → [装饰器函数处理] → [返回新函数]
     ↓             ↓                  ↓
say_hello() → my_decorator(say_hello) → wrapper()

三、装饰器的进阶用法

1. 处理带参数的函数

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("装饰器前置操作")
        result = func(*args, **kwargs)
        print("装饰器后置操作")
        return result
    return wrapper

@my_decorator
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

输出:

装饰器前置操作
Hello, Alice!
装饰器后置操作

通过使用 *args 和 **kwargs,可以支持任意参数类型的函数。


2. 带参数的装饰器

有时候我们需要给装饰器本身传递参数,这时可以再嵌套一层函数。

def repeat(num_times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def say_hi():
    print("Hi!")

say_hi()

输出:

Hi!
Hi!
Hi!

这个装饰器会根据传入的次数重复执行函数。


3. 多个装饰器叠加使用

我们可以为一个函数同时应用多个装饰器,它们的执行顺序是从内向外依次包裹。

def decorator1(func):
    def wrapper():
        print("装饰器1进入")
        func()
        print("装饰器1退出")
    return wrapper

def decorator2(func):
    def wrapper():
        print("装饰器2进入")
        func()
        print("装饰器2退出")
    return wrapper

@decorator1
@decorator2
def hello():
    print("Hello")

hello()

输出:

装饰器1进入
装饰器2进入
Hello
装饰器2退出
装饰器1退出

执行顺序相当于:

hello = decorator1(decorator2(hello))

4. 类作为装饰器

除了函数,我们还可以使用类来实现装饰器功能。

class MyDecorator:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("装饰器前置操作")
        result = self.func(*args, **kwargs)
        print("装饰器后置操作")
        return result

@MyDecorator
def say_hello():
    print("Hello!")

say_hello()

输出:

装饰器前置操作
Hello!
装饰器后置操作

这种方式更加面向对象,适用于需要保存状态的情况。


四、常用内置装饰器介绍

Python标准库中提供了一些常用的装饰器,下面介绍几个最常使用的。

1.@staticmethod和@classmethod

用于定义静态方法和类方法。

class MyClass:
    @staticmethod
    def static_method():
        print("这是静态方法")

    @classmethod
    def class_method(cls):
        print(f"这是类方法,
                调用类:{cls.__name__}")

MyClass.static_method()
MyClass.class_method()

2.@property

用于将类的方法伪装成属性访问。

class Person:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

p = Person("Alice")
print(p.name)  # 输出 Alice

这样可以让外部像访问属性一样使用方法,提高封装性。


3.@functools.wraps

用于保留原函数元信息(如名称、文档字符串等),避免被装饰器破坏。

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """装饰器的说明"""
        print("装饰器前置")
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def say_hello():
    """打招呼函数"""
    print("Hello")

print(say_hello.__name__)  
# 输出 say_hello(而不是 wrapper)

print(say_hello.__doc__)   
# 输出 招呼函数(而不是装饰器的说明)

五、装饰器的应用场景

场景1:日志记录

def log(func):
    def wrapper(*args, **kwargs):
        print(f"正在执行函数: {func.__name__}")
        result = func(*args, **kwargs)
        print(f"函数 {func.__name__} 执行完成")
        return result
    return wrapper

@log
def calculate_sum(a, b):
    return a + b

result = calculate_sum(3, 5)
print("结果:", result)

场景2:权限验证

def login_required(func):
    def wrapper(user, *args, **kwargs):
        if user.is_authenticated:
            return func(user, *args, **kwargs)
        else:
            print("权限不足,请先登录")
    return wrapper

class User:
    def __init__(self, is_authenticated):
        self.is_authenticated = is_authenticated

@login_required
def access_data(user):
    print("成功访问数据")

user = User(is_authenticated=False)
access_data(user)  # 权限不足,请先登录

场景3:性能计时

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"{func.__name__} 执行耗时:
                 {end_time - start_time:.4f} 秒")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(1)

slow_function()

场景4:缓存计算结果(Memoization)

def memoize(func):
    cache = {}
    def wrapper(n):
        if n not in cache:
            cache[n] = func(n)
        return cache[n]
    return wrapper

@memoize
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(100)) 
# 快速计算斐波那契数列

六、装饰器的优点与注意事项

优点:

  • 提高代码复用性,减少重复逻辑
  • 实现功能扩展而不修改原函数
  • 支持模块化设计,分离核心逻辑与附加逻辑
  • 简洁美观,提升代码可读性和可维护性

注意事项:

  • 装饰器顺序会影响执行流程,需注意多个装饰器的叠加顺序
  • 不要滥用装饰器,否则可能导致逻辑混乱
  • 使用 @functools.wraps 保留函数元信息
  • 小心闭包变量绑定问题(尤其是在循环中)

七、总结

装饰器是 Python 中非常强大的特性之一,它允许我们在不修改原有函数代码的情况下,动态地增强其功能。它是高阶函数和闭包机制的结合体,是编写高质量、可复用代码的重要工具。

通过本文的学习,你应该已经掌握了:

  • 装饰器的基本概念和构成原理
  • 如何编写和使用装饰器函数
  • 装饰器的内部工作机制(函数包装、参数传递)
  • 装饰器的高级用法(带参装饰器、类装饰器、多层装饰器)
  • 装饰器的常见应用场景(日志、权限、计时、缓存等)
  • 装饰器的优缺点及使用注意事项

作为 Python 初学者,建议你在练习中多尝试使用装饰器来优化代码结构,提升程序的灵活性和可维护性。

希望这篇文章能帮助你在 Python 编程之路上越走越远!

最近发表
标签列表