Python 如何在装饰器中使用其他函数?

开发 前端
在装饰器中使用其他函数是一种常见的需求,可以帮助你将复杂的功能分解成更小、更易于管理的部分。

前言

在装饰器中使用其他函数是一种常见的需求,可以帮助你将复杂的功能分解成更小、更易于管理的部分。

使用辅助函数进行日志记录

假设你有一个装饰器,用于在函数调用前后记录日志。你可以将日志记录的功能提取到一个单独的辅助函数中。

import logging
# 配置日志记录
logging.basicConfig(level=logging.INFO)
def log_message(message):
    logging.info(message)
def log_function_call(func):
    def wrapper(*args, **kwargs):
        log_message(f"Calling {func.__name__} with args={args} kwargs={kwargs}")
        result = func(*args, **kwargs)
        log_message(f"{func.__name__} returned {result}")
        return result
    return wrapper
@log_function_call
def add(a, b):
    return a + b
add(3, 5)

使用辅助函数进行输入验证

假设你有一个装饰器,用于验证函数的输入参数。你可以将输入验证的逻辑提取到一个单独的辅助函数中。

def validate_input(*types):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if len(args) != len(types):
                raise TypeError("Number of arguments does not match expected types")
            for arg, type_ in zip(args, types):
                if not isinstance(arg, type_):
                    raise TypeError(f"Argument {arg} is not of type {type_}")
            return func(*args, **kwargs)
        return wrapper
    return decorator
def is_positive(number):
    if number <= 0:
        raise ValueError("Number must be positive")
    return number
@validate_input(int, int)
def add(a, b):
    return a + b
@validate_input(int)
def square(n):
    return n ** 2
add(3, 5)  # 正常运行
square(4)  # 正常运行
# square(-4)  # 抛出 ValueError

使用辅助函数进行缓存

假设你有一个装饰器,用于缓存函数的结果。你可以将缓存的逻辑提取到一个单独的辅助函数中。

from functools import lru_cache
def cache_results(func):
    @lru_cache(maxsize=128)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper
def compute_fibonacci(n):
    if n <= 1:
        return n
    return compute_fibonacci(n - 1) + compute_fibonacci(n - 2)
@cache_results
def fibonacci(n):
    return compute_fibonacci(n)
print(fibonacci(10))  # 输出: 55
print(fibonacci(10))  # 直接从缓存中获取结果

使用辅助函数进行权限验证

假设你有一个装饰器,用于验证用户是否有权限调用某个函数。你可以将权限验证的逻辑提取到一个单独的辅助函数中。

def check_permission(permission):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if not has_permission(permission):
                raise PermissionError(f"User does not have permission {permission}")
            return func(*args, **kwargs)
        return wrapper
    return decorator
def has_permission(permission):
    # 假设这里有一个权限检查的逻辑
    return permission == "admin"
@check_permission("admin")
def admin_action():
    print("Performing admin action")
@check_permission("user")
def user_action():
    print("Performing user action")
admin_action()  # 正常运行
# user_action()  # 抛出 PermissionError

使用辅助函数进行性能测量

假设你有一个装饰器,用于测量函数的执行时间。你可以将性能测量的逻辑提取到一个单独的辅助函数中。

import time
def measure_time(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:.4f} seconds to execute")
        return result
    return wrapper
def get_current_time():
    return time.time()
@measure_time
def slow_function():
    time.sleep(2)
    print("Slow function completed")
slow_function()

如何在装饰器中使用多个装饰器?

基本的多重装饰器

假设你有两个装饰器 @log_function_call 和 @measure_time,分别用于日志记录和性能测量。

import logging
import time
# 配置日志记录
logging.basicConfig(level=logging.INFO)
def log_message(message):
    logging.info(message)
def log_function_call(func):
    def wrapper(*args, **kwargs):
        log_message(f"Calling {func.__name__} with args={args} kwargs={kwargs}")
        result = func(*args, **kwargs)
        log_message(f"{func.__name__} returned {result}")
        return result
    return wrapper
def measure_time(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:.4f} seconds to execute")
        return result
    return wrapper
@log_function_call
@measure_time
def add(a, b):
    time.sleep(1)  # 模拟耗时操作
    return a + b
result = add(3, 5)
print(f"Result: {result}")

带参数的多重装饰器

假设你有一个带参数的装饰器 @repeat,用于多次调用函数,同时还有一个 @log_function_call 装饰器。

import logging
# 配置日志记录
logging.basicConfig(level=logging.INFO)
def log_message(message):
    logging.info(message)
def log_function_call(func):
    def wrapper(*args, **kwargs):
        log_message(f"Calling {func.__name__} with args={args} kwargs={kwargs}")
        result = func(*args, **kwargs)
        log_message(f"{func.__name__} returned {result}")
        return result
    return wrapper
def repeat(num_times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            results = []
            for _ in range(num_times):
                result = func(*args, **kwargs)
                results.append(result)
            return results
        return wrapper
    return decorator
@log_function_call
@repeat(3)
def greet(name):
    return f"Hello, {name}!"
results = greet("Alice")
for result in results:
    print(result)

使用 functools.wraps 保留元数据

为了保留被装饰函数的元数据(如名称、文档字符串等),可以使用 functools.wraps。

import logging
import time
import functools
# 配置日志记录
logging.basicConfig(level=logging.INFO)
def log_message(message):
    logging.info(message)
def log_function_call(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        log_message(f"Calling {func.__name__} with args={args} kwargs={kwargs}")
        result = func(*args, **kwargs)
        log_message(f"{func.__name__} returned {result}")
        return result
    return wrapper
def measure_time(func):
    @functools.wraps(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:.4f} seconds to execute")
        return result
    return wrapper
@log_function_call
@measure_time
def add(a, b):
    time.sleep(1)  # 模拟耗时操作
    return a + b
result = add(3, 5)
print(f"Result: {result}")
print(add.__name__)  # 输出: add
print(add.__doc__)   # 输出: None 或者函数的文档字符串

组合多个带参数的装饰器

假设你有两个带参数的装饰器 @repeat 和 @log_level,分别用于多次调用函数和设置日志级别。

import logging
import functools
# 配置日志记录
logging.basicConfig(level=logging.DEBUG)
def set_log_level(level):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            logger = logging.getLogger(func.__name__)
            logger.setLevel(level)
            result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator
def repeat(num_times):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            results = []
            for _ in range(num_times):
                result = func(*args, **kwargs)
                results.append(result)
            return results
        return wrapper
    return decorator
@set_log_level(logging.INFO)
@repeat(3)
def greet(name):
    logger = logging.getLogger(greet.__name__)
    logger.info(f"Greeting {name}")
    return f"Hello, {name}!"
results = greet("Alice")
for result in results:
    print(result)

总结

通过将装饰器中的复杂逻辑提取到单独的辅助函数中,可以使装饰器更加模块化和易于维护。这些辅助函数可以被多个装饰器复用,从而提高代码的重用性和可读性。

通过组合多个装饰器,可以实现更复杂的功能。多个装饰器的执行顺序是从内到外,因此最靠近函数定义的装饰器会首先被应用。使用 functools.wraps 可以保留被装饰函数的元数据,使代码更加清晰和易读。希望这些示例能帮助你更好地理解如何在装饰器中使用多个装饰器。

责任编辑:华轩 来源: 测试开发学习交流
相关推荐

2022-05-10 09:12:16

TypeScript装饰器

2010-02-01 17:50:32

Python装饰器

2022-06-23 08:00:53

PythonDateTime模块

2023-02-27 17:36:33

LinuxSIGINT信号

2022-10-13 00:03:00

JavaScripSQL函数

2020-09-09 07:00:00

TensorFlow神经网络人工智能

2023-02-07 07:47:52

Python装饰器函数

2010-03-10 14:03:41

python处理文本

2023-08-24 10:24:54

GitLabPodman

2020-12-08 22:07:08

PythonWebDjango

2021-03-09 07:27:40

Kafka开源分布式

2022-05-17 08:25:10

TypeScript接口前端

2021-06-09 09:36:18

DjangoElasticSearLinux

2015-08-27 09:46:09

swiftAFNetworkin

2011-08-10 09:31:41

Hibernateunion

2024-01-18 08:37:33

socketasyncio线程

2020-11-30 11:55:07

Docker命令Linux

2019-09-16 19:00:48

Linux变量

2014-07-02 09:47:06

SwiftCocoaPods

2020-04-09 10:18:51

Bash循环Linux
点赞
收藏

51CTO技术栈公众号