Python装饰器解析

Python装饰器是一个非常强大的工具,可以帮助我们以简洁的方式修改函数和类的行为。本文将对Python装饰器进行深入探讨,包括装饰器的基本概念、创建装饰器、使用装饰器以及如何处理更复杂的装饰器用例。

什么是Python装饰器

在Python中,装饰器基本上是一个函数,它接受一个函数作为输入并返回一个新的函数。

如何创建装饰器(关键点:高阶函数、嵌套函数)

Python装饰器的创建有两个关键要点:1) 高阶函数;2) 嵌套函数。高阶函数表示函数可以接受其他函数作为参数,或者返回一个函数作为结果。嵌套函数表示在一个函数内部可以定义另一个函数。

方式一:手动创建方式

# 创建装饰器
def my_decorator(func):
    def wrapper():
        print("Before function execution")
        func()
        print("After function execution")
    return wrapper


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


# 使用装饰器
say_hello_ = my_decorator(say_hello)


say_hello_()

运行结果:

在这里插入图片描述

这里,my_decorator就是一个装饰器。它接收一个函数say_hello作为输入,并返回了一个新的函数wrapper。

方式2:@语法糖方式

@语法糖方式使用装饰器非常简单,只需要在你想要“装饰”的函数上方添加@装饰器名即可。

执行效果与前面通过直接调用装饰器并将其赋值给原函数的方式是完全相同的。

# 创建装饰器
def my_decorator(func):
    def wrapper():
        print("Before function execution")
        func()
        print("After function execution")
    return wrapper


# 使用装饰器
@my_decorator
def say_hello():
    print("Hello!")


say_hello()

运行结果:
在这里插入图片描述

两种装饰器使用方法对比不同

可以看两种使用方式有何不同,结果都是一样的:

在这里插入图片描述

两种装饰器使用方法:多对多对比

第一种方法:手动应用

如下,在多对多情况时,say_hello1、say_hello2 和 say_goodbye1、say_goodbye2分别别从同一个原始函数say_hello、say_goodbye 派生的两个不同版本,但它们之间互不影响。

def decorator1(func):
    def wrapper():
        print("Decorator1")
        func()
    return wrapper


def decorator2(func):
    def wrapper():
        print("Decorator2")
        func()
    return wrapper


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


def say_goodbye():
    print("goodbye!")


say_hello1 = decorator1(say_hello)
say_hello2 = decorator2(say_hello)
say_goodbye1 = decorator1(say_goodbye)
say_goodbye2 = decorator2(say_goodbye)

say_hello1()
say_hello2()
say_goodbye1()
say_goodbye2()

在这里插入图片描述

第二种方法:使用@语法糖

使用@语法糖,无法直接在同一份代码中为同一个函数名称创建多个独立的装饰版本。一旦使用@语法糖,装饰后的函数将替代原始的函数定义。但是,可以通过定义不同的函数来实现类似的效果,例如:

def decorator1(func):
    def wrapper():
        print("Decorator1")
        func()
    return wrapper


def decorator2(func):
    def wrapper():
        print("Decorator2")
        func()
    return wrapper


@decorator1
def say_hello1():
    print("Hello!")


@decorator2
def say_hello2():
    print("Hello!")


@decorator1
def say_goodbye1():
    print("goodbye!")


@decorator2
def say_goodbye2():
    print("goodbye!")


say_hello1()
say_hello2()
say_goodbye1()
say_goodbye2()

在这里插入图片描述

总结(通用性还是手动方式好)

如果目的是创建多个独立的装饰函数版本,并且需要它们基于同一个原始函数,第一种手动方式更适合需求。使用@语法糖则适用于直接且单一地应用装饰器,每个函数独立定义。(要论通用性,还是第一种方式好,能方便保留原函数定义,如果用@语法糖,装饰之后,原函数就被改变了)

装饰器的返回值

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
encoding = 'utf-8'


def timer_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"{func.__name__} took {end_time - start_time} seconds to run.")
        return result
    return wrapper


@timer_decorator
def long_running_operation():
    time.sleep(2)
    return 'sleep complete'


print(long_running_operation())

运行结果:
在这里插入图片描述

这里,timer_decorator就是一个装饰器。它接收一个函数long_running_operation作为输入,并返回了一个新的函数wrapper

参数化装饰器

在很多情况下,我们希望我们的装饰器能够接受参数,以便我们能够对其行为进行更细粒度的控制。这就引出了所谓的参数化装饰器。

创建参数化装饰器

语法示例

这个装饰器用来重复执行函数指定次数:

def repeat(num_times):
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                func(*args, **kwargs)
        return wrapper
    return decorator_repeat
使用示例
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
encoding = 'utf-8'


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


@repeat(3)  # 表示要重复执行3次
def greet(name):
    print(f"Hello {name}")


greet("Alice")

在这里插入图片描述

类装饰器

除了函数装饰器,Python还支持类装饰器。类装饰器主要依赖于函数__call__(),每当你调用一个类的示例时,函数__call__()就会被执行。

创建类装饰器

语法示例

下面这个类装饰器CountCalls的主要目的是跟踪某个函数被调用的次数。它通过覆盖__call__方法来实现这一点,每当装饰的函数被调用时,就会增加num_calls计数器的值,并打印出该函数已被调用的次数:

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print(f"Call {self.num_calls} of {self.func.__name__!r}")
        return self.func(*args, **kwargs)
使用示例
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
encoding = 'utf-8'


class CountCalls:
    def __init__(self, func):
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print(f"Call {self.num_calls} of {self.func.__name__!r}")
        return self.func(*args, **kwargs)


@CountCalls
def say_hello(name):
    print(f"Hello, {name}")


say_hello("Alice")
say_hello("Bob")

运行结果:

在这里插入图片描述

ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍
ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐