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

Python装饰器 - 探究Python中的装饰器应用及其实现方法

发布时间:2023-06-16 13:42:58

Python中的装饰器是一种非常实用的语言特性,可以方便地实现代码的复用、增强和扩展。它本质上是一种函数,可以接受原函数作为参数,对其进行包装并返回包装后的函数。这种包装过程可以在不改变原函数功能的情况下,实现一些额外的操作,比如给函数添加新的功能、记录日志、缓存结果等等。

本文将探究Python中的装饰器应用及其实现方法。

一、装饰器使用场景

1. 记录函数执行时间

装饰器可以方便地记录函数执行时间,比如:

import time

def timeit(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} execution time: {end - start}s")
        return result
    return wrapper
        
@timeit
def foo():
    time.sleep(2)
    
foo()  # 输出: foo execution time: 2.0010483264923096s

2. 缓存函数结果

装饰器可以用来实现缓存函数结果,避免重复计算,比如:

def cache(func):
    cached_results = {}
    def wrapper(*args):
        if args in cached_results:
            return cached_results[args]
        result = func(*args)
        cached_results[args] = result
        return result
    return wrapper

@cache
def fib(n):
    if n <= 2:
        return 1
    return fib(n-1) + fib(n-2)

print(fib(30))  # 输出: 832040

3. 权限验证

装饰器可以用来验证用户权限,比如:

def admin_only(func):
    def wrapper(user, *args, **kwargs):
        if user != "admin":
            raise Exception("Only admin can access this resource")
        return func(*args, **kwargs)
    return wrapper

@admin_only
def sensitive_info():
    return "top secret"

print(sensitive_info("john"))  # 输出: Exception: Only admin can access this resource

二、实现装饰器的方法

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("Tom")  # 输出: Before the function is called.  Hello Tom!  After the function is called.

2. 类装饰器

装饰器也可以用类来实现。类装饰器定义一个callable类,并实现__init____call__方法。

class Decorator:
    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
        
@Decorator
def say_hello(name):
    print(f"Hello {name}!")
    
say_hello("Tom")  # 输出: Before the function is called.  Hello Tom!  After the function is called.

3. functools库

Python标准库提供了一个functools模块,其中有一个wraps函数,它可以用来在类装饰器中保留原函数的元信息。例如,在保留被装饰函数的docstring和函数名时,可以写成如下形式:

from functools import wraps

class Decorator:
    def __init__(self, func):
        wraps(func)(self)  # 保存原函数的docstring和函数名
        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

@Decorator
def say_hello(name):
    """Prints a greeting message."""
    print(f"Hello {name}!")
    
print(say_hello.__name__)  # 输出: 'say_hello'
print(say_hello.__doc__)   # 输出: 'Prints a greeting message.'

四、装饰器的应用场景

装饰器的应用非常广泛,下面列出一些典型的应用场景:

1. 计时器

可以用装饰器实现一个简单的计时器,比如:

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Elapsed time: {end_time - start_time:.4f}s")
        return result
    return wrapper

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

fibonacci(35)  # 输出: Elapsed time: 2.8830s

2. 缓存

可以用装饰器实现一个简单的缓存,避免重复计算,比如:

def cache(func):
    cached_results = {}
    def wrapper(*args, **kwargs):
        key = (args, tuple(kwargs.items()))
        if key in cached_results:
            print(f"Returning cached result for {args}, {kwargs}")
            return cached_results[key]
        print(f"Computing result for {args}, {kwargs}")
        result = func(*args, **kwargs)
        cached_results[key] = result
        return result
    return wrapper

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

fibonacci(35)  # 输出: Computing result for (35,), {}
               #      Computing result for (34,), {}
               #      Computing result for (33,), {}
               #      ...
               #      Returning cached result for ((2,), {}), {}
               #      Returning cached result for ((3,), {}), {}
               #      Returning cached result for ((4,), {}), {}
               #      Elapsed time: 2.7338s

3. 打印日志

可以用装饰器实现一个简单的日志器,记录函数的调用情况,比如:

def logger(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with arguments: {args}, {kwargs}")
        result = func(*args, **kwargs)
        print(f"Result is {result}")
        return result
    return wrapper

@logger
def add(a, b):
    return a + b

add(2, 3)  # 输出: Calling add with arguments: (2, 3), {}
           #      Result is 5

4. 验证权限

可以用装饰器实现一个简单的权限验证器,判断用户是否有访问资源的权限,比如:

def authorized_only(roles):
    def decorator(func):
        def wrapper(user, *args, **kwargs):
            if user["role"] not in roles:
                raise Exception(f"Unauthorized access to {func.__name__}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@authorized_only(["admin", "manager"])
def sensitive_info():
    return "top secret"

print(sensitive_info({"name": "John", "role": "user"}))  # 输出: Exception: Unauthorized access to sensitive_info

5. 批量处理数据

可以用装饰器实现一个简单的批量处理器,把多个调用合并成一个批量调用,比如:

`python

def batch_processor(batch_size):

def decorator(func):

def wrapper(data):

n_batches = (len(data) + batch_size - 1) // batch_size

results = []

for i in range(n_batches):

batch_data = data[i*batch_size : (i+1)*batch_size]

batch_result = func(batch_data)

results.extend(batch_result)

return