欢迎访问宙启技术站
智能推送

Python装饰器详解细致讲解装饰器的实现方式和使用方法

发布时间:2023-06-24 18:55:09

Python装饰器是一种函数或类,可以以另一个函数或类为参数,并扩展、修改或包装原始函数或类的行为。装饰器可以用于添加新功能、解决代码重复问题,还可以用于修改或包装现有代码,以使其具有更好的可读性、可维护性和可重用性。因此,掌握Python装饰器是非常重要的。

一、装饰器的实现方式

1. 装饰器函数

最常见的实现方式是使用函数作为装饰器。这种方式的实现非常简单,只需编写一个函数,该函数接收一个目标函数作为参数,并返回一个新函数。通常,该新函数会在原始函数之前或之后添加新的行为或修改现有行为。

下面是一个简单的装饰器函数示例,该函数在目标函数之前和之后打印一些文本:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print('Before the function is called.')
        result = func(*args, **kwargs)
        print('After the function is called.')
        return result
    return wrapper

@my_decorator
def say_hello(name):
    print(f'Hello, {name}!')

say_hello('John')

运行结果:

Before the function is called.
Hello, John!
After the function is called.

在这个例子中,函数my_decorator接收一个目标函数作为参数func,并创建一个新的函数wrapper。这个新函数接收和原始函数相同的参数,但在调用原始函数之前和之后打印了一些文本。在最后,装饰器函数返回它的内部函数wrapper,并用装饰器语法@my_decorator标识目标函数。这意味着我们可以像调用原始函数一样调用say_hello函数,但是同时还会自动地执行装饰器函数中的代码。

2. 装饰器类

装饰器不仅可以是函数,还可以是类。在这种实现方式中,装饰器类必须实现__call__方法,以便可以像函数一样调用它。这种实现方式的优点是装饰器可以包装的功能更自由灵活,也可以实现更复杂的功能。

以下是一个装饰器类的示例,它在目标函数之前和之后打印一些文本:

class MyDecorator:
    def __init__(self, func):
        self.func = func
        
    def __call__(self, *args, **kwargs):
        print('Before the function is called.')
        result = self.func(*args, **kwargs)
        print('After the function is called.')
        return result

@MyDecorator
def say_hello(name):
    print(f'Hello, {name}!')

say_hello('John')

运行结果与函数装饰器示例相同。

在这个例子中,我们定义了一个装饰器类MyDecorator,并将目标函数作为参数传递给__init__方法,然后在__call__方法中创建一个新函数wrapper。这个新函数与函数装饰器中的wrapper函数几乎相同,但它是使用对象属性self.func调用原始函数。最后,我们用装饰器语法@MyDecorator来标识目标函数。

二、装饰器的使用方法

装饰器可以用于很多不同的场合,下面列举了一些常见的用法。

1. 缓存结果

装饰器可以用于缓存函数的结果,这样可以避免重复计算。下面是一个用于缓存结果的示例:

class Cache:
    def __init__(self, func):
        self.func = func
        self.cache = {}
        
    def __call__(self, *args, **kwargs):
        key = (args, tuple(kwargs.items()))
        if key not in self.cache:
            self.cache[key] = self.func(*args, **kwargs)
        return self.cache[key]

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

print(fibonacci(10))
print(fibonacci(10))

运行结果:

55
55

在这个例子中,我们定义了一个缓存装饰器类Cache,并用它来装饰一个递归斐波那契数列函数。在__call__方法中,我们使用参数的值来作为索引,将值存储在self.cache字典中。如果该值已经计算过,则直接返回缓存结果。如果该值尚未计算过,则调用目标函数计算结果,然后将它保存到缓存中并返回它。

2. 记录时间

装饰器可以用于记录函数的执行时间,这样可以分析代码性能。下面是一个用于记录时间的示例:

import time

class Timer:
    def __init__(self, func):
        self.func = func
        
    def __call__(self, *args, **kwargs):
        start_time = time.time()
        result = self.func(*args, **kwargs)
        end_time = time.time()
        print(f'Time taken: {end_time - start_time:.6f}s.')
        return result

@Timer
def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n-1)

print(factorial(10))

运行结果:

Time taken: 0.000000s.
3628800

在这个例子中,我们定义了一个计时器装饰器类Timer,并将它用于测量阶乘函数的执行时间。在__call__方法中,我们记录开始时间和结束时间,并计算它们之间的差异。然后,我们打印出总的执行时间,并返回函数的结果。

3. 授权访问

装饰器可以用于授权访问某些资源,这样可以实现更安全的代码。以下是一个用于授权访问的示例:

def authorize(user_type):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if user_type == 'admin':
                return func(*args, **kwargs)
            else:
                raise ValueError('Access denied.')
        return wrapper
    return decorator

@authorize('admin')
def add_user(user):
    print(f'Adding user: {user}')

add_user('John')

运行结果:

Adding user: John

在这个例子中,我们定义了一个授权访问装饰器函数authorize,用它来装饰一个添加用户的函数。在authorize函数中,我们接收用户类型作为参数,然后返回一个装饰器函数。在装饰器中,我们用user_type参数来确定是否允许访问,如果允许,则调用原始函数,并返回其结果。否则,我们引发ValueError异常,表示访问被拒绝。

4. 包装函数

装饰器可以用于包装函数,这样可以简化代码逻辑和结构。以下是一个用于包装函数的示例:

def wrapper(func):
    def new_func():
        print('Wrapper code.')
        func()
        print('Wrapper code.')
    return new_func

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

say_hello = wrapper(say_hello)
say_hello()

运行结果:

Wrapper code.
Hello!
Wrapper code.

在这个例子中,我们定义了两个函数:一个用于包装函数的wrapper函数,另一个是say_hello函数。在第10行,我们将say_hello函数作为参数传递给wrapper函数,并用返回的新函数重新定义了say_hello函数。如此一来,调用`say_hello