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

Python装饰器的使用和案例说明

发布时间:2023-05-26 03:22:45

Python装饰器是Python函数的一个特殊概念,它允许您在不修改代码的情况下修改或增强单个函数的功能。它们以函数作为参数,返回一个新的修改函数对象。Python装饰器的使用适用于函数和方法调用之前或之后的操作,如添加日志信息、性能分析、输入输出验证和缓存等。本文将会介绍Python装饰器的使用以及案例说明。

一、Python装饰器的基本使用

在Python中,有三种类型的函数可以使用修饰符功能:普通函数、类方法和静态方法。Python装饰器的基本使用示例如下:

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

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

wrapped_say_hello = my_decorator(say_hello)
wrapped_say_hello()

输出结果:

Something is happening before the function is called.
Hello!
Something is happening after the function is called.

装饰器函数将真实函数作为参数传递,并定义一个包装函数,该函数可以在调用真实函数之前和之后添加其他功能。通过将装饰器函数应用于真实函数,我们将得到一个新的包装函数,它将是原始函数的修改版本。执行此包装函数将添加包装功能。在这种情况下,我们将"decorated" say_hello()函数,以便它首先打印一些信息,然后执行原始的hello()函数,最后打印一些其他信息。

这个示例展示了一种基本的Python装饰器,但它是需要手动应用的。为了避免这种手动应用,Python提供了一种更美妙的语法:@语法糖。使用@语法糖,我们可以将一个装饰器函数直接应用于一个函数,并使其自动成为被装饰函数的变换。

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

输出结果:

Something is happening before the function is called.
Hello!
Something is happening after the function is called.

上述代码使用"@my_decorator"语法糖,将say_hello()函数可以作为my_decorator函的参数,在调用say_hello()函数时,自动执行my_decorator()函数的处理功能。

二、Python装饰器的案例说明

二1、创建缓存的装饰器

有些函数可能需要花费大量时间来处理结果。如果这些计算结果始终相同,则不必每次调用该函数时重新计算。这是使用缓存技术的理想地方。Python装饰器是缓存函数的良好选择,因为它们可以用于修改函数的行为,将结果存储在缓存中,而不必对原始代码进行修改。下面是一个创建缓存的装饰器示例:

import time
from functools import wraps

def memoize(function):
    memo = {}
    @wraps(function)
    def wrapper(*args):
        if args in memo:
            return memo[args]
        else:
            rv = function(*args)
            memo[args] = rv
            return rv
    return wrapper

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

start_time = time.time()
print(fibonacci(10))
end_time = time.time()
print("Run time : {0:.8f}".format(end_time - start_time))

start_time = time.time()
print(fibonacci(50))
end_time = time.time()
print("Run time : {0:.8f}".format(end_time - start_time))

输出结果:

55
Run time : 0.00007415
12586269025
Run time : 0.00000000

在这个例子中,memoize函数将创建一个用于存储计算结果的缓存字典。如果输入相同,则结果(进过包装)将从缓存中获取。否则,结果将通过调用原始函数进行计算,并将结果存储在缓存中,以备将来使用。在结果已经缓存的情况下,调用装饰函数将获得显着的性能提升。

在调用函数之前,装饰器使用@wraps(function)语法建立对原始函数的引用。这个语法会替换包装函数,为其添加一个__wrapped__属性,指向原始函数。

二2、检查函数参数的类型和值的有效性

在Python当中,并没有提供一种固定的方式,来使得程序员可以在函数的输入进行约束或检查。 而有一些IDE如pycharm可以帮助程序员来检查输入参数,但是有的程序员不再使用这个IDE。

下面的代码说明了装饰器如何使用类型注释来提取函数签名信息,并从这些签名信息中执行参数价值验证:

from inspect import signature
from functools import wraps

def type_check(*args, **kwargs):
    def inner_function(f):
        sig = signature(f)
        bound_types = sig.bind_partial(*args, **kwargs).arguments
        @wraps(f)
        def wrapper(*args, **kwargs):
            bound_values = sig.bind(*args, **kwargs)
            for name, value in bound_values.arguments.items():
                if name in bound_types:
                    if not isinstance(value, bound_types[name]):
                        raise TypeError("Argument {} must be {}".format(name, bound_types[name]))
            return f(*args, **kwargs)
        return wrapper
    return inner_function

@type_check(int)
def square(num):
    return num ** 2

print(square(2))    # 4
print(square('2'))  # TypeError: Argument num must be <class 'int'>

在该示例中,通过使用类型注释,我们可以从函数签名信息中检索每个参数的类型。 我们然后检查给定的参数值与注释的类型是否一致。如果不是,我们将抛出一个带有适当消息的TypeError异常。

作为一个额外的优势,该装饰器还可以处理关键字参数或没有注释的参数。

三、总结

Python装饰器提供了一种简单而优雅的方法,可以在单个函数或方法中实现一些特殊行为而不改变它们的核心代码。这为Python程序员提供了一种更易于使用的方法来组织代码并遵循良好的设计原则。装饰器可以应用于单个函数或类方法,并有助于提高代码质量、性能、可读性和可重用性。本文说明了Python装饰器的基本使用和案例说明,读者在开发中可以根据其实际需要来使用。