深入解析Python装饰器:从基础到高级应用,掌握代码功能增强的强大工具

装饰器在Python中是一种非常强大的功能,它允许修改和增强函数或方法的行为,而不需要改变其本身的定义。

1. 装饰器基础

在深入理解装饰器之前,首先需要了解Python中函数也是对象,这意味着它们可以被赋值给变量、作为参数传递给其他函数,或者在其他函数内部定义。

函数作为对象

def greet(name):
    return f"Hello, {name}!"

greet_someone = greet
print(greet_someone("World"))

将函数greet赋值给变量greet_someone,之后通过新的变量名调用函数。输出结果是:“Hello, World!”。

在函数中定义函数

可以在一个函数内部定义另一个函数:

def speak(text):
    def whisper(t):
        return t.lower() + "..."
    return whisper(text)

print(speak("Hello World"))

输出结果是:“hello world…”。whisper是在speak内部定义并且只能在speak内部使用的函数。

函数作为参数

函数因为是对象,所以也可以作为参数传递给其他函数。

def greet(name):
    return f"Hello, {name}!"

def call_func(func):
    other_name = "Alice"
    return func(other_name)

print(call_func(greet))

这段代码将函数greet作为参数传递给call_func函数,输出结果是:“Hello, Alice!”。

2. 简单的装饰器

装饰器本质上是一个函数,它接受一个函数作为参数并返回一个新的函数。装饰器可以在不修改原有函数定义的情况下增加函数的功能。

创建装饰器

下面是一个简单的装饰器示例,它在函数调用前后分别打印出信息:

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

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

say_hello = my_decorator(say_hello)
say_hello()
# Something is happening before the function is called.
# Hello!
# Something is happening after the function is called.

my_decorator是一个装饰器,它内部定义了一个wrapper函数,该函数将被用来包裹原始函数say_hello以增加额外的功能(在函数执行前后打印消息)。然后通过say_hello = my_decorator(say_hello)应用装饰器。

使用@符号应用装饰器

Python提供了一个简便的方法来应用装饰器,即使用@符号:

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

say_hello()

跟之前的例子效果完全相同,但代码更加简洁、易读。@my_decorator语法是Python提供的一种便捷方式,可以轻松地将装饰器应用到函数上,注意这里的@my_decorator是上一个例子定义的函数。

3. 装饰器进阶

装饰器不仅限于无参数的函数,它们也可以装饰接收参数的函数。此外,装饰器本身也可以接收参数。

3.1 装饰带参数的函数

当装饰的函数需要接收参数时,需要在内部wrapper函数中正确处理这些参数

def trace(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with args {args} and kwargs {kwargs}")
        result = func(*args, **kwargs)
        print(f"{func.__name__} returned {result}")
        return result
    return wrapper

@trace
def calculate_area(length, width):
    return length * width

area = calculate_area(10, 20)
print(f"Area: {area}")
# Calling calculate_area with args (10, 20) and kwargs {}
# calculate_area returned 200
# Area: 200

trace装饰器被定义为监控任何函数的调用和返回值。装饰器内部的wrapper函数记录了传递给calculate_area函数的参数(lengthwidth)以及该函数的返回结果(计算的面积)。

装饰器接收函数参数的方式不限于使用 *args 和 **kwargs,这只是一种通用的方法而已,可以使得装饰器能够适用于任意参数的函数。

3.2 带参数的装饰器

有时我们希望装饰器本身能够接收参数。创建一个带参数的装饰器,该装饰器允许指定函数被调用的次数。

def repeat(num_times):
    """装饰器工厂,接收一个参数指定函数重复执行的次数。"""
    def decorator_repeat(func):
        """实际的装饰器,用于包装函数,使其可以重复执行指定次数。"""
        def wrapper(*args, **kwargs):
            """包装函数,执行被装饰函数多次,并在最后一次保留返回结果。"""
            result = None
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator_repeat

@repeat(num_times=2)
def greet(name):
    """一个简单的问候函数,打印问候语并不返回任何值。"""
    print(f"Hello, {name}!")

greet("Alice")
# Hello, Alice!
# Hello, Alice!
  1. 装饰器工厂repeat函数接收一个参数num_times,这个参数指定了被装饰的函数需要重复执行的次数。这使得repeat不仅仅是一个装饰器,而是一个装饰器工厂,它根据传入的参数创建并返回一个具体的装饰器。
  2. 实际的装饰器decorator_repeat是在repeat函数内部定义的函数,是真正的装饰器。它的任务是接收一个函数并返回一个包装过的版本。
  3. 包装函数wrapper函数是在decorator_repeat内部定义的。这个函数负责实际调用原始函数(在本例中为greet函数),并根据num_times的值重复执行它。

补充:

解释一下for _ in range(num_times):中的_

**_**是一个常用于循环中的占位符。当在循环中不需要使用到循环变量时,可以使用下划线(_)作为一个临时或者“不关心”的变量名。这是一个惯用法,表示循环次数的变量在循环体中没有被实际用到。

4. 使用装饰器的好处

  • 增加代码的复用性:通过装饰器,可以将某些功能独立出来,应用到多个函数上。
  • 增强函数功能:装饰器可以在不修改原始函数代码的情况下,增加额外的功能,如日志记录、权限检查等。
  • 简化代码结构:装饰器帮助我们将功能分离,避免每个函数都重复相同的代码,使得代码更加简洁和易于维护。

推荐我的相关专栏: python 错误记录

最近更新

  1. TCP协议是安全的吗?

    2024-04-21 04:44:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-21 04:44:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-21 04:44:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-21 04:44:02       20 阅读

热门阅读

  1. git本地提交记录 删除后如何找回

    2024-04-21 04:44:02       15 阅读
  2. 使用 vllm 运行 Llama3-8b-Instruct

    2024-04-21 04:44:02       14 阅读
  3. 【MySQL面试题pro版-13】

    2024-04-21 04:44:02       12 阅读
  4. 2022 E3 算法题第一题(Banana Count in A Given Letters)

    2024-04-21 04:44:02       14 阅读
  5. C++ 抽象

    2024-04-21 04:44:02       13 阅读
  6. 大数据分析可视化实训平台(1)

    2024-04-21 04:44:02       12 阅读
  7. 【QT教程】QT6QFuture与并发

    2024-04-21 04:44:02       14 阅读
  8. 打jar包

    打jar包

    2024-04-21 04:44:02      12 阅读
  9. RocketMQ同步消息发送失败重试DEMO

    2024-04-21 04:44:02       16 阅读
  10. 模块化编程

    2024-04-21 04:44:02       11 阅读
  11. ZCMU 1531: 序列的混乱程度

    2024-04-21 04:44:02       17 阅读