0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Python装饰器如何增加任务超时退出功能

科技绿洲 来源:Python实用宝典 作者:Python实用宝典 2023-11-01 10:53 次阅读

任务超时退出

我们日常在使用的各种网络请求库时都带有timeout参数,例如request库。这个参数可以使请求超时就不再继续了,直接抛出超时错误,避免等太久。

如果我们自己开发的方法也希望增加这个功能,该如何做呢?

方法很多,但最简单直接的是使用并发库futures,为了使用方便,我将其封装成了一个装饰器,代码如下:

import functools
from concurrent import futures

executor = futures.ThreadPoolExecutor(1)

def timeout(seconds):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            future = executor.submit(func, *args, **kw)
            return future.result(timeout=seconds)
        return wrapper
    return decorator

定义了以上函数,我们就有了一个超时结束的装饰器,下面可以测试一下:

import time

@timeout(1)
def task(a, b):
    time.sleep(1.2)
    return a+b

task(2, 3)

结果:

---------------------------------------------------------------------------
TimeoutError                              Traceback (most recent call last)
...
D:Anaconda3libconcurrentfutures_base.py in result(self, timeout)
    432                 return self.__get_result()
    433             else:
-- > 434                 raise TimeoutError()
    435 
    436     def exception(self, timeout=None):

TimeoutError:

上面我们通过装饰器定义了函数的超时时间为1秒,通过睡眠interwetten与威廉的赔率体系 函数执行超过1秒时,成功的抛出了超时异常。

程序能够在超时时间内完成时:

@timeout(1)
def task(a, b):
    time.sleep(0.9)
    return a+b

task(2, 3)

结果:

5

可以看到,顺利的得到了结果。

这样我们就可以通过一个装饰器给任何函数增加超时时间,这个函数在规定时间内还处理不完就可以直接结束任务。

前面我将这个装饰器将所需的变量定义到了外部,其实我们还可以通过类装饰器进一步封装,代码如下:

import functools
from concurrent import futures

class timeout:
    __executor = futures.ThreadPoolExecutor(1)

    def __init__(self, seconds):
        self.seconds = seconds

    def __call__(self, func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            future = timeout.__executor.submit(func, *args, **kw)
            return future.result(timeout=self.seconds)
        return wrapper

经测试使用类装饰器能得到同样的效果。

注意:使用@functools.wraps的目的是因为被装饰的func函数元信息会被替换为wrapper函数的元信息,而@functools.wraps(func)将wrapper函数的元信息替换为func函数的元信息。最终虽然返回的是wrapper函数,元信息却依然是原有的func函数。

在函数式编程中,函数的返回值是函数对象被称为闭包。

日志记录

如果我们需要记录部分函数的执行时间,函数执行前后打印一些日志,装饰器是一种很方便的选择。

代码如下:

import time
import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        res = func(*args, **kwargs)
        end = time.perf_counter()
        print(f'函数 {func.__name__} 耗时 {(end - start) * 1000} ms')
        return res
    return wrapper

装饰器 log 记录某个函数的运行时间,并返回其执行结果。

测试一下:

@log
def now():
    print('2021-7-1')

now()

结果:

2021-7-1
函数 now 耗时 0.09933599994838005 ms

缓存

如果经常调用一个函数,而且参数经常会产生重复,如果把结果缓存起来,下次调用同样参数时就会节省处理时间。

定义函数:

import math
import random
import time


def task(x):
    time.sleep(0.01)
    return round(math.log(x**3 / 15), 4)

执行:

%%time
for i in range(500):
    task(random.randrange(5, 10))

结果:

Wall time: 5.01 s

此时如果我们使用缓存的效果就会大不一样,实现缓存的装饰器有很多,我就不重复造轮子了,这里使用functools包下的LRU缓存:

from functools import lru_cache

@lru_cache()
def task(x):
    time.sleep(0.01)
    return round(math.log(x**3 / 15), 4)

执行:

%%time
for i in range(500):
    task(random.randrange(5, 10))

结果:

Wall time: 50 ms

约束某个函数的可执行次数

如果我们希望程序中的某个函数在整个程序的生命周期中只执行一次或N次,可以写一个这样的装饰器:

import functools


class allow_count:
    def __init__(self, count):
        self.count = count
        self.i = 0

    def __call__(self, func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            if self.i >= self.count:
                return
            self.i += 1
            return func(*args, **kw)
        return wrapper

测试:

@allow_count(3)
def job(x):
    x += 1
    return x


for i in range(5):
    print(job(i))

结果:

1
2
3
None
None
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 参数
    +关注

    关注

    11

    文章

    1832

    浏览量

    32201
  • 代码
    +关注

    关注

    30

    文章

    4782

    浏览量

    68544
  • python
    +关注

    关注

    56

    文章

    4795

    浏览量

    84646
收藏 人收藏

    评论

    相关推荐

    python学习:三个测试库的装饰实现思路

    Python 中实现参数化测试的几个库,并留下一个问题: 它们是如何做到把一个方法变成多个方法,并且将每个方法与相应的参数绑定起来的呢? 我们再提炼一下,原问题等于是:在一个类中,如何使用装饰
    的头像 发表于 09-27 11:44 3159次阅读
    <b class='flag-5'>python</b>学习:三个测试库的<b class='flag-5'>装饰</b><b class='flag-5'>器</b>实现思路

    理解Python装饰及其工作原理

    Python 是一种对新手很友好的语言。但是,它也有很多较难掌握的高级功能,比如装饰(decorator)。很多初学者一直不理解装饰
    发表于 10-08 11:39 2211次阅读

    分享python 7个好用的装饰

    ): return x + y4、deprecated这个相信大家在使用别的包时都遇到过,当要下线一个老版本的函数的时候就可以使用这个装饰。安装:pip install Deprecated
    发表于 06-15 16:54

    如何实现事件结构中存在多个超时功能

    实现事件结构中存在多个超时功能
    发表于 04-03 14:57 2次下载

    一文读懂Python装饰

    装饰前,还要先要明白一件事,Python 中的函数和 Java、C++不太一样,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数。
    发表于 04-28 10:48 3427次阅读
    一文读懂<b class='flag-5'>Python</b><b class='flag-5'>装饰</b><b class='flag-5'>器</b>

    让你学写Python装饰的五大理由

    你必须学写Python装饰的五个理由
    的头像 发表于 03-02 10:06 1906次阅读

    Python的函数装饰器使用方法

    Python中的装饰是一种可以装饰其它对象的工具,简单地说,他们是修改其他函数的功能的函数。该工具本质上是一个可调用的对象(callabl
    的头像 发表于 01-21 11:36 1586次阅读
    <b class='flag-5'>Python</b>的函数<b class='flag-5'>装饰</b>器使用方法

    Python装饰的原理和案例

    Python中的装饰器用于扩展可调用对象的功能,而无需修改其结构。基本上,装饰函数包装另一个函数以增强或修改其行为。我们可以通过一个具体的
    的头像 发表于 07-01 11:35 2233次阅读

    Python定时任务的实现方式

    在日常工作中,我们常常会用到需要周期性执行的任务,一种方式是采用 Linux 系统自带的 crond 结合命令行实现。另外一种方式是直接使用Python。接下来整理的是常见的Python定时
    的头像 发表于 10-08 15:20 5729次阅读

    Python装饰的使用

    定义 首先我们先来了解下装饰的定义。顾名思义,在Python中,装饰本质上就是一个函数,它可以接收一个函数作为参数,然后返回一个新的函数
    的头像 发表于 06-21 16:54 746次阅读

    装饰模式和代理模式的区别

    什么是装饰模式 装饰模式(Decorator Pattern): 在不改变对象自身的基础上,在程序运行期间给对象动态的添加职责; 感觉和继承如出一辙,不改变父类,子类可拓展
    的头像 发表于 10-08 14:25 3843次阅读
    <b class='flag-5'>装饰</b><b class='flag-5'>器</b>模式和代理模式的区别

    Python自制简单实用的日志装饰

    在写代码的时候,往往会漏掉日志这个关键因素,导致功能在使用的时候出错却无法溯源。 其实,只需要写一个非常简单的日志装饰,我们就能大大提升排查问题的效率。 1.简陋版装饰
    的头像 发表于 10-21 14:39 720次阅读
    <b class='flag-5'>Python</b>自制简单实用的日志<b class='flag-5'>装饰</b><b class='flag-5'>器</b>

    Python 自制简单实用的日志装饰

    在写代码的时候,往往会漏掉日志这个关键因素,导致功能在使用的时候出错却无法溯源。 其实,只需要写一个非常简单的日志装饰,我们就能大大提升排查问题的效率。 1.简陋版装饰
    的头像 发表于 10-31 15:05 489次阅读
    <b class='flag-5'>Python</b> 自制简单实用的日志<b class='flag-5'>装饰</b><b class='flag-5'>器</b>

    如何写一个简单的装饰

    今天介绍的是一个已经存在十三年,但是依旧不红的库 decorator,好像很少有人知道他的存在一样。 这个库可以帮你做什么呢 ? 其实很简单,就是可以帮你更方便地写python装饰代码,更重
    的头像 发表于 11-01 09:54 492次阅读
    如何写一个简单的<b class='flag-5'>装饰</b><b class='flag-5'>器</b>

    【每天学点AI】一个例子带你了解Python装饰到底在干嘛!

    今天我们来聊聊一种能给你的代码变得“加料”的神器——Python装饰。就像一杯咖啡,原本它是苦的,为了让它符合我的口味,我给它添加了糖,添加之后就完美的符合了我的口味。那么,装饰
    的头像 发表于 09-20 16:54 552次阅读
    【每天学点AI】一个例子带你了解<b class='flag-5'>Python</b><b class='flag-5'>装饰</b><b class='flag-5'>器</b>到底在干嘛!