Python装饰器

目录

什么是装饰器

装饰器举例

@语法糖

类装饰器

***装饰器的高级用法***

带参数的装饰器

改成类装饰器

缓存装饰器

Flask中的路由装饰器


什么是装饰器

看看装饰器在官方文档上的解释:

一个函数返回另一个函数,通常以 @wrapper 语法作为函数转换的方式应用。装饰器的常见示例包括 classmethod()staticmethod()

装饰器语法仅仅是一种语法糖,以下两种函数定义在语义上是等价的:

def f(arg):
    ...
f = staticmethod(f)

@staticmethod
def f(arg):
    ...

相同的概念也存在于类中,但在类中的使用较少。有关装饰器的更多信息,请参阅有关函数定义和类定义的文档。


装饰器举例

首先定义一个判断是否为质数的函数

def is_prime(num):  #判断是否为质数
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True

现在我想增加一个时间逻辑,以便于性能调优,使用装饰器的思想用来记录is_prime(10000)需要的时间

import time


def is_prime(num):  # 判断是否为质数
    time.sleep(1)
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True


def count_time(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        func(*args, **kwargs)
        print("执行时间为:", time.time() - t1)

    return wrapper


is_prime = count_time(is_prime)    #装饰器count_time返回函数对象wrapper,相当于is_prime=wrapper
is_prime(10000)    #执行时间为: 1.0012288093566895

这里的count_time就是装饰器,装饰器内定义wrapper函数,把func这个函数当做参数传入,在wrapper函数中写入时间逻辑。

Debug后可以发现is_prime(10000)下一步会直接进入wrapper,也符合我注释里写的


@语法糖

Python 提供了 @ 语法糖,使得在函数或类上应用装饰器更加简洁和可读。@在项目中非常常见,我们经常会发现自己调用的外部函数或类中有@

上面提到的装饰器可以用@cout_time替换

is_prime = count_time(is_prime)

 

import time


def count_time(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        func(*args, **kwargs)
        print("执行时间为:", time.time() - t1)

    return wrapper


@count_time
def is_prime(num):  # 判断是否为质数
    time.sleep(1)
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True


if __name__ == "__main__":
    # is_prime = count_time(is_prime)
    is_prime(10000)

类装饰器

上面提到的是函数装饰器,接下来聊聊类装饰器。

__call__ 方法,允许对象像函数一样被调用。

import time


class CountTime:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        t2 = time.time()
        result = self.func(*args, **kwargs)
        print("执行时间为:", time.time() - t2)
        return result


def is_prime(num):  # 判断是否为质数
    time.sleep(1)
    if num < 2:
        return False
    elif num == 2:
        return True
    else:
        for i in range(2, num):
            if num % i == 0:
                return False
        return True


if __name__ == "__main__":
    # is_prime = CountTime(is_prime)
    is_prime(10000)

流程和函数装饰器类似,通过__init__()方法初始化类,再通过__call__()方法调用装饰方法。

由于类装饰器可以更好地组织代码,它们通常可以提高代码的可读性和可维护性。这其实也是类的优点。


***装饰器的高级用法***

带参数的装饰器

有时候,你可能想要创建一个装饰器,它接受一些参数来自定义装饰器的行为。这可以通过在装饰器内部再嵌套一个函数来实现。例如:

def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                func(*args, **kwargs)

        return wrapper

    return decorator


@repeat(3)
def say_hello():
    print("Hello!")


say_hello()

不使用语法糖@

# 手动应用装饰器到函数 say_hello

say_hello = repeat(3)(say_hello)

repeat(3)(say_hello),第一次看到这代码确实有点懵,下面来讲讲该程序的流程

1.调用 say_hello = repeat(3)(say_hello) 时,首先调用 repeat(3)。这将返回 decorator 函数,但并不会立即执行它。此时,decorator 函数已经捕获了参数 n

2.接下来,再次调用 decorator(say_hello),这将返回 wrapper 函数,但同样不会立即执行它。此时,wrapper 函数已经捕获了参数 func,这里的 func 是外部的 say_hello 函数。

3.调用 say_hello() 时,实际上是在执行 wrapper(*args, **kwargs),其中 *args**kwargs 对应于 say_hello 函数的参数。在这里,wrapper 函数将 say_hello 函数重复执行了三次。

改成类装饰器

class RepeatDecorator:
    def __init__(self, n):
        self.n = n

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            for _ in range(self.n):
                func(*args, **kwargs)
        return wrapper

@RepeatDecorator(3)
def say_hello():
    print("Hello!")

say_hello()

缓存装饰器

缓存装饰器是一种常见的装饰器类型,用于存储函数的计算结果,以便在后续相同输入的情况下可以直接返回缓存的结果,而不必重新计算。这可以显著提高函数的性能,尤其是在计算密集型或IO密集型的任务中。

下面是一个简单的缓存装饰器的示例:

def cache(func):
    # 创建一个字典用于缓存结果
    cache_dict = {}

    def wrapper(*args, **kwargs):
        # 构建缓存键,将参数元组和关键字参数转化为字符串
        key = str(args) + str(kwargs)
        
        # 如果结果已经在缓存中,则直接返回缓存的结果
        if key in cache_dict:
            return cache_dict[key]
        
        # 否则,调用原始函数计算结果
        result = func(*args, **kwargs)
        
        # 将结果存入缓存字典
        cache_dict[key] = result
        
        return result

    return wrapper

使用缓存装饰器的步骤如下:

  1. 定义一个带有缓存装饰器的函数。

  2. 装饰目标函数,例如:

@cache
def expensive_function(n):
    # 模拟耗时的计算任务
    result = sum(range(n))
    return result

现在,expensive_function 函数将自动使用缓存。如果相同的参数传递给函数,它将首先检查缓存并返回已存储的结果,而不会重新计算。

缓存装饰器的优点包括:

  • 提高性能:避免了相同输入的重复计算,节省了时间和计算资源。

  • 简化代码:无需在每个需要缓存的函数内部手动实现缓存逻辑,装饰器为您处理。

  • 可配置性:您可以根据需要灵活地调整缓存策略,例如设置缓存过期时间。

需要注意的是,缓存装饰器的实现可以更加复杂,可以根据具体需求添加过期时间、缓存大小限制等功能。另外,使用缓存时应该小心,确保不会导致内存占用过多或者缓存数据变得过时。

Flask中的路由装饰器

  1. @app.route()

    这是 Flask 中最常用的路由注册装饰器,它用于将 URL 路径与视图函数关联起来。你可以在装饰器中指定 URL 路径以及支持的 HTTP 请求方法。例如:

    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        return '欢迎访问首页'
    
    @app.route('/about')
    def about():
        return '关于我们'
    

    上述代码中,@app.route('/') 将根路径关联到 index 视图函数,@app.route('/about') 将 "/about" 路径关联到 about 视图函数。默认情况下,这些路由仅支持 GET 请求,但您可以使用 methods 参数来指定其他请求方法。

  2. @app.route() 装饰器的可选参数

    • methods:指定支持的 HTTP 请求方法,例如 methods=['GET', 'POST']
    • defaults:为视图函数的参数提供默认值,例如 defaults={'name': 'Guest'}
    • host:指定要匹配的主机名,例如 host='example.com'
    • subdomain:指定子域名,例如 subdomain='api'
  3. 动态路由

    你还可以在 URL 路径中使用变量,这样可以捕获 URL 中的特定值并将其传递给视图函数。例如:

    @app.route('/user/')
    def show_user(username):
        return f'用户: {username}'
    

    在这个例子中, 是一个动态变量,它会匹配 URL 中的任何值,并将其传递给 show_user 视图函数作为参数。

  4. 路由装饰器的返回值

    路由注册装饰器通常不返回任何值,但是您可以在视图函数内部使用 return 语句来返回响应内容,这将作为 HTTP 响应发送给客户端。例如:

    @app.route('/')
    def index():
        return '欢迎访问首页'
    

    在这个示例中,'欢迎访问首页' 字符串将作为 HTML 响应返回给客户端。

你可能感兴趣的:(python,后端,python,开发语言)