精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

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秒,通過睡眠模擬函數執行超過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

    文章

    1786

    瀏覽量

    32095
  • 代碼
    +關注

    關注

    30

    文章

    4751

    瀏覽量

    68359
  • python
    +關注

    關注

    56

    文章

    4782

    瀏覽量

    84463
收藏 人收藏

    評論

    相關推薦

    python學習:三個測試庫的裝飾實現思路

    Python 中實現參數化測試的幾個庫,并留下一個問題: 它們是如何做到把一個方法變成多個方法,并且將每個方法與相應的參數綁定起來的呢? 我們再提煉一下,原問題等于是:在一個類中,如何使用裝飾
    的頭像 發表于 09-27 11:44 ?3128次閱讀
    <b class='flag-5'>python</b>學習:三個測試庫的<b class='flag-5'>裝飾</b><b class='flag-5'>器</b>實現思路

    理解Python裝飾及其工作原理

    Python 是一種對新手很友好的語言。但是,它也有很多較難掌握的高級功能,比如裝飾(decorator)。很多初學者一直不理解裝飾
    發表于 10-08 11:39 ?2200次閱讀

    分享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 ?3418次閱讀
    一文讀懂<b class='flag-5'>Python</b><b class='flag-5'>裝飾</b><b class='flag-5'>器</b>

    讓你學寫Python裝飾的五大理由

    你必須學寫Python裝飾的五個理由
    的頭像 發表于 03-02 10:06 ?1894次閱讀

    Python的函數裝飾器使用方法

    Python中的裝飾是一種可以裝飾其它對象的工具,簡單地說,他們是修改其他函數的功能的函數。該工具本質上是一個可調用的對象(callabl
    的頭像 發表于 01-21 11:36 ?1565次閱讀
    <b class='flag-5'>Python</b>的函數<b class='flag-5'>裝飾</b>器使用方法

    Python裝飾的原理和案例

    Python中的裝飾器用于擴展可調用對象的功能,而無需修改其結構。基本上,裝飾函數包裝另一個函數以增強或修改其行為。我們可以通過一個具體的
    的頭像 發表于 07-01 11:35 ?2208次閱讀

    Python定時任務的實現方式

    在日常工作中,我們常常會用到需要周期性執行的任務,一種方式是采用 Linux 系統自帶的 crond 結合命令行實現。另外一種方式是直接使用Python。接下來整理的是常見的Python定時
    的頭像 發表于 10-08 15:20 ?5704次閱讀

    Python裝飾的使用

    定義 首先我們先來了解下裝飾的定義。顧名思義,在Python中,裝飾本質上就是一個函數,它可以接收一個函數作為參數,然后返回一個新的函數
    的頭像 發表于 06-21 16:54 ?724次閱讀

    裝飾模式和代理模式的區別

    什么是裝飾模式 裝飾模式(Decorator Pattern): 在不改變對象自身的基礎上,在程序運行期間給對象動態的添加職責; 感覺和繼承如出一轍,不改變父類,子類可拓展
    的頭像 發表于 10-08 14:25 ?3620次閱讀
    <b class='flag-5'>裝飾</b><b class='flag-5'>器</b>模式和代理模式的區別

    Python自制簡單實用的日志裝飾

    在寫代碼的時候,往往會漏掉日志這個關鍵因素,導致功能在使用的時候出錯卻無法溯源。 其實,只需要寫一個非常簡單的日志裝飾,我們就能大大提升排查問題的效率。 1.簡陋版裝飾
    的頭像 發表于 10-21 14:39 ?694次閱讀
    <b class='flag-5'>Python</b>自制簡單實用的日志<b class='flag-5'>裝飾</b><b class='flag-5'>器</b>

    Python 自制簡單實用的日志裝飾

    在寫代碼的時候,往往會漏掉日志這個關鍵因素,導致功能在使用的時候出錯卻無法溯源。 其實,只需要寫一個非常簡單的日志裝飾,我們就能大大提升排查問題的效率。 1.簡陋版裝飾
    的頭像 發表于 10-31 15:05 ?476次閱讀
    <b class='flag-5'>Python</b> 自制簡單實用的日志<b class='flag-5'>裝飾</b><b class='flag-5'>器</b>

    如何寫一個簡單的裝飾

    今天介紹的是一個已經存在十三年,但是依舊不紅的庫 decorator,好像很少有人知道他的存在一樣。 這個庫可以幫你做什么呢 ? 其實很簡單,就是可以幫你更方便地寫python裝飾代碼,更重
    的頭像 發表于 11-01 09:54 ?472次閱讀
    如何寫一個簡單的<b class='flag-5'>裝飾</b><b class='flag-5'>器</b>

    【每天學點AI】一個例子帶你了解Python裝飾到底在干嘛!

    今天我們來聊聊一種能給你的代碼變得“加料”的神器——Python裝飾。就像一杯咖啡,原本它是苦的,為了讓它符合我的口味,我給它添加了糖,添加之后就完美的符合了我的口味。那么,裝飾
    的頭像 發表于 09-20 16:54 ?525次閱讀
    【每天學點AI】一個例子帶你了解<b class='flag-5'>Python</b><b class='flag-5'>裝飾</b><b class='flag-5'>器</b>到底在干嘛!