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

用Python的装饰器对函数进行修饰

发布时间:2023-05-21 03:20:47

装饰器是Python编程中非常重要的概念,它可以让我们在不改变函数代码的情况下,对函数进行一些额外的操作。 本文将详细介绍Python装饰器的基本概念、语法和应用。

装饰器的基本概念

装饰器是一个Python对象,可以与函数或方法相关联。它允许我们在不修改函数的源代码的情况下,拓展或修改函数的行为。可以理解为给“函数套上外衣”,让函数具有更多的功能。

例如,我们可以使用装饰器来记录一个函数被调用的次数、计时一个函数执行的时间、检查函数的参数类型等等。

装饰器的语法

装饰器是一个函数,它可以接收一个函数或方法作为参数,并返回一个新的函数或方法。装饰器的语法如下:

@decorator_function
def some_function():
    # Code here

其中,@decorator_function是装饰器函数的名称,它被放置在待装饰函数的声明之前。当some_function函数被调用时,装饰器函数会被调用并返回一个新的函数,这个新函数包含原始函数的一些额外逻辑。

装饰器函数的结构通常如下:

def decorator_function(original_function):
    
    def wrapper_function():
        # Some extra logic before the original function is called
        
        result = original_function()
        
        # Some extra logic after the original function is called
        return result
        
    return wrapper_function

其中,decorator_function是我们自定义的装饰器函数,它接收原始函数original_function作为参数,并返回包装函数wrapper_function。

wrapper_function函数是一个新的函数,它包含了一些额外的逻辑(在原始函数被调用前或调用后),并最终返回原始函数的结果。在wrapper_function中,我们可以实现一些记录、计时、验证、缓存等功能。

在一些简单的装饰器中,可以用@符号来简化代码,@符号后面跟着装饰器函数的名称。使用@符号使得装饰器函数的调用位置更加明确和易于理解。

装饰器应用案例

下面我们就来实现几个有用的装饰器。

1. 装饰器记录函数调用次数

我们可以使用一个统计器装饰器来记录一个函数被调用的次数,代码如下:

def counter(func):
    def wrapper(*args, **kwargs):
        wrapper.count += 1
        return func(*args, **kwargs)
    wrapper.count = 0
    return wrapper

@counter
def foo():
    print("Hello, World!")

foo()
foo()
print(f"{foo.count} times called")

运行结果为:

Hello, World!
Hello, World!
2 times called

上面的counter装饰器函数接受一个函数func作为参数,并返回一个新函数wrapper。wrapper函数记录了函数func被调用的次数。在调用foo函数的时候,实际上调用了wrapper函数,并统计调用次数。

2. 装饰器计时函数执行时间

我们可以使用一个计时器装饰器来计算一个函数执行的时间,代码如下:

import time

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

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

print(fib(30))

运行结果为:

Function fib took 0.49 seconds to run
832040

上面的timer装饰器函数接受一个函数func作为参数,并返回一个新函数wrapper。在wrapper函数中,我们先记录当前时间,然后执行函数func,并在结束时记录时间。最后打印出函数的执行时间。

在上面的例子中,我们使用了递归函数来计算斐波那契数列的第30个数,可以看到这个函数执行的时间比较长。

3. 装饰器检查函数参数类型

我们可以使用一个参数类型检查器装饰器来检查函数的参数类型是否正确,代码如下:

def type_check(*types):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for i, arg in enumerate(args):
                if not isinstance(arg, types[i]):
                    raise ValueError(f"Argument {i+1} should be of type {types[i].__name__}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@type_check(int, str)
def foo(num, name):
    print(f"{num} is a {type(num).__name__}, {name} is a {type(name).__name__}")

foo(42, "David")
foo("Alice", 23)

运行结果为:

42 is a int, David is a str
Traceback (most recent call last):
  File "decorator.py", line 17, in <module>
    foo("Alice", 23)
  File "decorator.py", line 6, in wrapper
    raise ValueError(f"Argument {i+1} should be of type {types[i].__name__}")
ValueError: Argument 1 should be of type int

上面的type_check装饰器函数接受多个类型作为参数,并返回一个新函数decorator。在decorator中,我们定义了一个新函数wrapper,用于检查函数的参数类型。如果参数类型不匹配,就会抛出ValueError异常。

在上面的例子中,我们定义了一个foo函数,它接受一个int类型和一个str类型的参数,并在函数执行时打印它们的类型。当我们调用foo函数时,我们 次传入了一个int和一个str类型的参数,第二次传入了一个str和一个int类型的参数。由于第二次调用参数类型不匹配,就会抛出异常。

总结

装饰器是Python编程中非常重要的概念,它允许我们在不修改函数源代码的情况下,拓展或修改函数的行为。在本文中,我们简要介绍了Python装饰器的基本概念、语法和应用,并给出了一些实例说明。使用装饰器可以使得我们的代码更加简洁、易于理解和维护。