MST
星途 面试题库

面试题:Python自定义函数的元编程与装饰器应用

编写一个Python装饰器,它可以用于任何函数。这个装饰器要实现以下功能:记录函数的调用次数,记录每次调用函数时传入的参数,并且可以根据传入的参数动态修改函数的行为。例如,如果传入参数中某个特定关键字参数的值为True,函数的返回值需要进行特定的转换(比如字符串全部转大写)。请说明实现过程中涉及到的元编程概念以及如何确保装饰器的通用性和可扩展性。
42.7万 热度难度
编程语言Python

知识考点

AI 面试

面试题答案

一键面试
def custom_decorator(func):
    call_count = 0
    def wrapper(*args, **kwargs):
        nonlocal call_count
        call_count += 1
        print(f"函数 {func.__name__} 调用次数: {call_count}")
        print(f"传入的位置参数: {args}")
        print(f"传入的关键字参数: {kwargs}")
        result = func(*args, **kwargs)
        if '特定关键字参数' in kwargs and kwargs['特定关键字参数']:
            if isinstance(result, str):
                result = result.upper()
        return result
    return wrapper


@custom_decorator
def test_function(a, b, c=False):
    return f"{a} {b}"


元编程概念

  1. 装饰器本身就是元编程的体现:元编程是指程序可以在运行时操纵自身或其他程序的结构和行为。装饰器允许我们在不修改函数源代码的情况下,为函数添加额外的功能。这里的装饰器 custom_decorator 就是在运行时动态地给 test_function 添加了记录调用次数和参数,以及根据参数修改行为的功能。
  2. 闭包:在装饰器实现中,wrapper 函数形成了闭包。闭包是指内部函数可以访问外部函数的变量,即使外部函数已经返回。这里 wrapper 函数可以访问并修改 call_count 变量,即使 custom_decorator 函数已经执行完毕返回 wrapper 函数对象。

确保通用性和可扩展性

  1. 通用性
    • 接受任意参数:装饰器内部的 wrapper 函数使用 *args**kwargs 来接受任意数量和类型的位置参数和关键字参数。这样,无论被装饰的函数定义了怎样的参数列表,装饰器都可以正常工作。
    • 不依赖函数返回值类型:在对返回值进行处理时,通过 isinstance 判断返回值类型,而不是假设其类型。例如,只对字符串类型的返回值进行转大写操作,这样可以适应不同返回值类型的函数。
  2. 可扩展性
    • 易于添加新功能:如果需要添加新的功能,比如记录函数执行时间,只需要在 wrapper 函数内部适当位置添加代码即可,不会影响到被装饰函数和其他功能。
    • 参数化行为:通过关键字参数来动态修改函数行为,使得装饰器具有灵活性。例如,通过 特定关键字参数 来决定是否对返回值进行转换。如果需要更多的动态行为,可以添加更多的关键字参数,并在 wrapper 函数中相应处理。