欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解利用上下文管理器擴展Python計時器

 更新時間:2022年06月29日 14:35:10   作者:云朵君  
本文將和大家一起了解什么是上下文管理器?和?Python?的?with?語句,以及如何完成自定義。然后擴展?Timer?以便它也可以用作上下文管理器,感興趣的可以了解一下

上文中,我們一起學習了手把手教你實現(xiàn)一個 Python 計時器。本文中,云朵君將和大家一起了解什么是上下文管理器 和 Python 的 with 語句,以及如何完成自定義。然后擴展 Timer 以便它也可以用作上下文管理器。最后,使用 Timer 作為上下文管理器如何簡化我們自己的代碼。

上文中我們創(chuàng)建的第一個 Python 計時器類,然后逐步擴展我們 Timer 類,其代碼也是較為豐富強大。我們不能滿足于此,仍然需要模板一些代碼來使用Timer

  • 首先,實例化類
  • 其次,在要計時的代碼塊之前調用 .start()
  • 最后,在代碼塊之后調用 .stop()

一個 Python 定時器上下文管理器

Python 有一個獨特的構造,用于在代碼塊之前和之后調用函數(shù):上下文管理器。

了解 Python 中的上下文管理器

上下文管理器長期以來一直是 Python 中重要的一部分。由 PEP 343 于 2005 年引入,并首次在 Python 2.5 中實現(xiàn)??梢允褂?nbsp;with 關鍵字識別代碼中的上下文管理器:

with?EXPRESSION?as?VARIABLE:
????BLOCK

EXPRESSION 是一些返回上下文管理器的 Python 表達式。首先上下文管理器綁定到變量名 VARIABLE上,BLOCK 可以是任何常規(guī)的 Python 代碼塊。上下文管理器保證程序在 BLOCK 之前調用一些代碼,在 BLOCK 執(zhí)行之后調用一些其他代碼。這樣即使 BLOCK 引發(fā)異常,后者也是會照樣執(zhí)行。

上下文管理器最常見的用途是處理不同的資源,如文件、鎖和數(shù)據(jù)庫連接等。上下文管理器用于使用資源后釋放和清理資源。以下示例僅通過打印包含冒號的行來演示 timer.py 的基本結構。此外,它展示了在 Python 中打開文件的常用習語:

with?open("timer.py")?as?fp:
????print("".join(ln?for?ln?in?fp?if?":"?in?ln))

class?TimerError(Exception):
class?Timer:
????timers:?ClassVar[Dict[str,?float]]?=?{}
????name:?Optional[str]?=?None
????text:?str?=?"Elapsed?time:?{:0.4f}?seconds"
????logger:?Optional[Callable[[str],?None]]?=?print
????_start_time:?Optional[float]?=?field(default=None,?init=False,?repr=False)
????def?__post_init__(self)?->?None:
????????if?self.name?is?not?None:
????def?start(self)?->?None:
????????if?self._start_time?is?not?None:
????def?stop(self)?->?float:
????????if?self._start_time?is?None:
????????if?self.logger:
????????if?self.name:

注意,使用 open() 作為上下文管理器,文件指針fp 不會顯式關閉,可以確認 fp 已自動關閉:

fp.closed

True

在此示例中,open("timer.py") 是一個返回上下文管理器的表達式。該上下文管理器綁定到名稱 fp。上下文管理器在 print() 執(zhí)行期間有效。這個單行代碼塊在 fp 的上下文中執(zhí)行。

fp 是上下文管理器是什么意思? 從技術上講,就是 fp 實現(xiàn)了 上下文管理器協(xié)議。Python 語言底層有許多不同的協(xié)議??梢詫f(xié)議視為說明我們代碼必須實現(xiàn)哪些特定方法的合同。

上下文管理器協(xié)議由兩種方法組成:

  • 進入與上下文管理器相關的上下文時調用 .__enter__()。
  • 退出與上下文管理器相關的上下文時調用 .__exit__()。

換句話說,要自己創(chuàng)建上下文管理器,需要編寫一個實現(xiàn) .__enter__() 和 .__exit__() 的類。試試 Hello, World!上下文管理器示例:

#?studio.py
class?Studio:
????def?__init__(self,?name):
????????self.name?=?name

????def?__enter__(self):
????????print(f"你好?{self.name}")
????????return?self

????def?__exit__(self,?exc_type,?exc_value,?exc_tb):
????????print(f"一會兒見,?{self.name}")

Studio是一個上下文管理器,它實現(xiàn)了上下文管理器協(xié)議,使用如下:

from?studio?import?Studio
with?Studio("云朵君"):
????print("正在忙?...")

你好 云朵君
正在忙 ...
一會兒見, 云朵君

首先,注意 .__enter__() 在做事之前是如何被調用的,而 .__exit__() 是在做事之后被調用的。該示例中,沒有引用上下文管理器,因此不需要使用 as 為上下文管理器命名。

接下來,注意 self.__enter__() 的返回值受 as 約束。創(chuàng)建上下文管理器時,通常希望從 .__enter__() 返回 self ??梢园慈缦路绞绞褂迷摲祷刂担?/p>

from?greeter?import?Greeter
with?Greeter("云朵君")?as?grt:
??print(f"{grt.name}?正在忙?...")

你好 云朵君
云朵君 正在忙 ...
一會兒見, 云朵君

在寫 __exit__ 函數(shù)時,需要注意的事,它必須要有這三個參數(shù):

  • exc_type:異常類型
  • exc_val:異常值
  • exc_tb:異常的錯誤棧信息

這三個參數(shù)用于上下文管理器中的錯誤處理,它們以 sys.exc_info() 的返回值返回。當主邏輯代碼沒有報異常時,這三個參數(shù)將都為None。

如果在執(zhí)行塊時發(fā)生異常,那么代碼將使用異常類型、異常實例和回溯對象(即exc_type、exc_valueexc_tb)調用 .__exit__() 。通常情況下,這些在上下文管理器中會被忽略,而在引發(fā)異常之前調用 .__exit__()

from?greeter?import?Greeter
with?Greeter("云朵君")?as?grt:
????print(f"{grt.age}?does?not?exist")

你好 云朵君
一會兒見, 云朵君
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: 'Greeter' object has no attribute 'age'

可以看到,即使代碼中有錯誤,還是照樣打印了 "一會兒見, 云朵君"。

理解并使用 contextlib

現(xiàn)在我們初步了解了上下文管理器是什么以及如何創(chuàng)建自己的上下文管理器。在上面的例子中,我們只是為了構建一個上下文管理器,卻寫了一個類。如果只是要實現(xiàn)一個簡單的功能,寫一個類未免有點過于繁雜。這時候,我們就想,如果只寫一個函數(shù)就可以實現(xiàn)上下文管理器就好了。

這個點Python早就想到了。它給我們提供了一個裝飾器,你只要按照它的代碼協(xié)議來實現(xiàn)函數(shù)內容,就可以將這個函數(shù)對象變成一個上下文管理器。

我們按照 contextlib 的協(xié)議來自己實現(xiàn)一個上下文管理器,為了更加直觀我們換個用例,創(chuàng)建一個我們常用且熟悉的打開文件(with open)的上下文管理器。

import?contextlib

@contextlib.contextmanager
def?open_func(file_name):
????#?__enter__方法
????print('open?file:',?file_name,?'in?__enter__')
????file_handler?=?open(file_name,?'r')
?
????#?【重點】:yield
????yield?file_handler

????#?__exit__方法
????print('close?file:',?file_name,?'in?__exit__')
????file_handler.close()
????return

with?open_func('test.txt')?as?file_in:
????for?line?in?file_in:
????????print(line)

在被裝飾函數(shù)里,必須是一個生成器(帶有yield),而 yield 之前的代碼,就相當于__enter__里的內容。yield 之后的代碼,就相當于__exit__ 里的內容。

上面這段代碼只能實現(xiàn)上下文管理器的第一個目的(管理資源),并不能實現(xiàn)第二個目的(處理異常)。

如果要處理異常,可以改成下面這個樣子。

import?contextlib

@contextlib.contextmanager
def?open_func(file_name):
????#?__enter__方法
????print('open?file:',?file_name,?'in?__enter__')
????file_handler?=?open(file_name,?'r')

????try:
????????yield?file_handler
????except?Exception?as?exc:
????????#?deal?with?exception
????????print('the?exception?was?thrown')
????finally:
????????print('close?file:',?file_name,?'in?__exit__')
????????file_handler.close()
????????return

with?open_func('test.txt')?as?file_in:
????for?line?in?file_in:
????????1/0
????????print(line)

Python 標準庫中的 contextlib包括定義新上下文管理器的便捷方法,以及可用于關閉對象、抑制錯誤甚至什么都不做的現(xiàn)成上下文管理器!

創(chuàng)建 Python 計時器上下文管理器

了解了上下文管理器的一般工作方式后,要想知道它們是如何幫助處理時序代碼呢?假設如果可以在代碼塊之前和之后運行某些函數(shù),那么就可以簡化 Python 計時器的工作方式。其實,上下文管理器可以自動為計時時顯式調用 .start() 和.stop()。

同樣,要讓 Timer 作為上下文管理器工作,它需要遵守上下文管理器協(xié)議,換句話說,它必須實現(xiàn) .__enter__()  .__exit__() 方法來啟動和停止 Python 計時器。從目前的代碼中可以看出,所有必要的功能其實都已經(jīng)可用,因此只需將以下方法添加到之前編寫的的 Timer 類中即可:

#?timer.py
@dataclass
class?Timer:
????#?其他代碼保持不變

????def?__enter__(self):
????????"""Start?a?new?timer?as?a?context?manager"""
????????self.start()
????????return?self

????def?__exit__(self,?*exc_info):
????????"""Stop?the?context?manager?timer"""
????????self.stop()

Timer 現(xiàn)在就是一個上下文管理器。實現(xiàn)的重要部分是在進入上下文時, .__enter__() 調用 .start() 啟動 Python 計時器,而在代碼離開上下文時, .__exit__() 使用 .stop() 停止 Python 計時器。

from?timer?import?Timer
import?time
with?Timer():
????time.sleep(0.7)

Elapsed time: 0.7012 seconds

此處注意兩個更微妙的細節(jié):

  • .__enter__() 返回 self,Timer 實例,它允許用戶使用 as 將 Timer 實例綁定到變量。例如,使用 with Timer() as t: 將創(chuàng)建指向 Timer 對象的變量 t。
  • .__exit__() 需要三個參數(shù),其中包含有關上下文執(zhí)行期間發(fā)生的任何異常的信息。代碼中,這些參數(shù)被打包到一個名為 exc_info 的元組中,然后被忽略,此時 Timer 不會嘗試任何異常處理。

在這種情況下不會處理任何異常。上下文管理器的一大特點是,無論上下文如何退出,都會確保調用.__exit__()。在以下示例中,創(chuàng)建除零公式模擬異常查看代碼功能:

from?timer?import?Timer
with?Timer():
????for?num?in?range(-3,?3):
????????print(f"1?/?{num}?=?{1?/?num:.3f}")

1 / -3 = -0.333
1 / -2 = -0.500
1 / -1 = -1.000
Elapsed time: 0.0001 seconds
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
ZeroDivisionError: division by zero

注意 ,即使代碼拋出異常,Timer 也會打印出經(jīng)過的時間。

使用 Python 定時器上下文管理器

現(xiàn)在我們將一起學習如何使用 Timer 上下文管理器來計時 "下載數(shù)據(jù)" 程序。回想一下之前是如何使用 Timer 的:

#?download_data.py
import?requests
from?timer?import?Timer
def?main():
????t?=?Timer()
????t.start()
????source_url?=?'https://cloud.tsinghua.edu.cn/d/e1ccfff39ad541908bae/files/?p=%2Fall_six_datasets.zip&dl=1'
????headers?=?{'User-Agent':?'Mozilla/5.0'}
????res?=?requests.get(source_url,?headers=headers)?
????t.stop()
????with?open('dataset/datasets.zip',?'wb')?as?f:
????????f.write(res.content)

if?__name__?==?"__main__":
????main()

我們正在對 requests.get() 的調用進行記時監(jiān)控。使用上下文管理器可以使代碼更短、更簡單、更易讀

#?download_data.py
import?requests
from?timer?import?Timer
def?main():
????source_url?=?'https://cloud.tsinghua.edu.cn/d/e1ccfff39ad541908bae/files/?p=%2Fall_six_datasets.zip&dl=1'
????headers?=?{'User-Agent':?'Mozilla/5.0'}
????with?Timer():
????????res?=?requests.get(source_url,?headers=headers)
????????
????with?open('dataset/datasets.zip',?'wb')?as?f:
????????f.write(res.content)

if?__name__?==?"__main__":
????main()

此代碼實際上與上面的代碼相同。主要區(qū)別在于沒有定義無關變量t,在命名空間上無多余的東西。

寫在最后

將上下文管理器功能添加到 Python 計時器類有幾個優(yōu)點:

  • 省時省力:只需要一行額外的代碼即可為代碼塊的執(zhí)行計時。
  • 可讀性高:調用上下文管理器是可讀的,你可以更清楚地可視化你正在計時的代碼塊。

使用 Timer 作為上下文管理器幾乎與直接使用 .start() 和 .stop() 一樣靈活,同時它的樣板代碼更少。

以上就是詳解利用上下文管理器擴展Python計時器的詳細內容,更多關于Python上下文管理器 計時器的資料請關注腳本之家其它相關文章!

相關文章

  • python實現(xiàn)提取COCO,VOC數(shù)據(jù)集中特定的類

    python實現(xiàn)提取COCO,VOC數(shù)據(jù)集中特定的類

    這篇文章主要介紹了python實現(xiàn)提取COCO,VOC數(shù)據(jù)集中特定的類,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-03-03
  • 實現(xiàn)用python算法計算圓周率的小訣竅

    實現(xiàn)用python算法計算圓周率的小訣竅

    什么!你不會背圓周率(鄙夷的眼神) 3.1415926535 8979323846 26433... 但是,我會算啊,本文用一個簡單的python代碼,教你計算圓周率
    2021-08-08
  • 詳解用Python練習畫個美隊盾牌

    詳解用Python練習畫個美隊盾牌

    這篇文章主要介紹了用Python練習畫個美隊盾牌,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-03-03
  • python 實現(xiàn)倒排索引的方法

    python 實現(xiàn)倒排索引的方法

    今天小編就為大家分享一篇python 實現(xiàn)倒排索引的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-12-12
  • python實現(xiàn)flappy bird游戲

    python實現(xiàn)flappy bird游戲

    這篇文章主要為大家詳細介紹了python實現(xiàn)flappy bird游戲,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • Python基于動態(tài)規(guī)劃算法解決01背包問題實例

    Python基于動態(tài)規(guī)劃算法解決01背包問題實例

    這篇文章主要介紹了Python基于動態(tài)規(guī)劃算法解決01背包問題,結合實例形式分析了Python動態(tài)規(guī)劃算法解決01背包問題的原理與具體實現(xiàn)技巧,需要的朋友可以參考下
    2017-12-12
  • django之用戶、用戶組及權限設置方式

    django之用戶、用戶組及權限設置方式

    這篇文章主要介紹了django之用戶、用戶組及權限設置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • pytorch實現(xiàn)手寫數(shù)字圖片識別

    pytorch實現(xiàn)手寫數(shù)字圖片識別

    這篇文章主要為大家詳細介紹了pytorch實現(xiàn)手寫數(shù)字圖片識別,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • Python?Flask?+?Redis?程序練習

    Python?Flask?+?Redis?程序練習

    這篇文章主要給大家分享Python?Flask?+?Redis?程序的練習,準備一個Python文件,名字為?app.py?提供一個web服務,可以訪問地址,返回一個Hello?Container?World!并且記錄訪問的次數(shù),下面來看看有趣的練習過程吧
    2022-01-01
  • 自制Python淘寶秒殺搶購腳本雙十一百分百中

    自制Python淘寶秒殺搶購腳本雙十一百分百中

    年年雙十一,年年搶不到,今年小編自制Python淘寶秒殺搶購腳本,百分百中,下面小編把我的實現(xiàn)思路分享給大家,有興趣的朋友借鑒下吧
    2021-11-11

最新評論