目錄
#一個Python 定時器上下文管理器
了解 Python 中的上下文管理器
创建 Python 计时器上下文管理器
使用 Python 定时器上下文管理器
写在最后
首頁 後端開發 Python教學 如何用上下文管理器擴充 Python 計時器

如何用上下文管理器擴充 Python 計時器

Apr 12, 2023 pm 08:43 PM
python 計時器 內容管理器

上文中我們建立的第一個 Python 計時器類,然後逐步擴展我們 Timer 類,其程式碼也是較為豐富強大。我們不能滿足於此,仍然需要模板一些程式碼來使用Timer:

  • 首先,實例化類別
  • 其次,在要計時的程式碼區塊之前呼叫.start()
  • 最後,在程式碼區塊之後呼叫.stop()

如何用上下文管理器擴充 Python 計時器

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

Python 有一個獨特的構造,用於在程式碼區塊之前和之後呼叫函數:上下文管理器。

了解 Python 中的上下文管理器

上下文管理器長期以來一直是 Python 中重要的一部分。由 PEP 343 於 2005 年引入,並首次在 Python 2.5 中實現。可以使用 with 關​​鍵字辨識程式碼中的上下文管理器:

with EXPRESSION as VARIABLE:
BLOCK
登入後複製

EXPRESSION 是一些傳回上下文管理器的 Python 表達式。首先上下文管理器綁定到變數名稱 VARIABLE上,BLOCK 可以是任何常規的 Python 程式碼區塊。情境管理器保證程式在 BLOCK 之前呼叫一些程式碼,在 BLOCK 執行之後呼叫一些其他程式碼。這樣即使 BLOCK 引發異常,後者也是會照樣執行。

上下文管理器最常見的用途是處理不同的資源,如檔案、鎖定和資料庫連接等。上下文管理器用於使用資源後釋放和清理資源。以下範例僅透過列印包含冒號的行來示範 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() 執行期間有效。這個單行程式碼區塊在 fp 的上下文中執行。

fp 是上下文管理器是什麼意思? 從技術上講,就是 fp 實現了 上下文管理器協定。 Python 語言底層有許多不同的協定。協議可以被視為說明我們程式碼必須實作哪些特定方法的合約。

上下文管理器協定由兩種方法組成:

  1. 進入與上下文管理器相關的上下文時呼叫.__enter__()。
  2. 退出與上下文管理器相關的上下文時呼叫.__exit__()。

換句話說,要自行建立情境管理器,需要寫一個實作 .__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是一個上下文管理器,它實現了上下文管理器協議,使用如下:

from studio import Studio
with Studio("云朵君"):
print("正在忙 ...")
登入後複製
你好 云朵君
正在忙 ...
一会儿见, 云朵君
登入後複製

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

接下來,注意 self.__enter__() 的回傳值受 as 約束。建立情境管理器時,通常會希望從 .__enter__() 回傳 self 。可以如下使用此回傳值:

from greeter import Greeter
with Greeter("云朵君") as grt:
print(f"{grt.name} 正在忙 ...")
登入後複製
你好 云朵君
云朵君 正在忙 ...
一会儿见, 云朵君
登入後複製

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

    ##exc_type:異常類型
  • exc_val:異常值
  • exc_tb:異常的錯誤堆疊資訊
這三個參數用於上下文管理器中的錯誤處理,它們以 sys.exc_info () 的返回值返回。當主邏輯代碼沒有報異常時,這三個參數將都為None。

如果在執行區塊時發生異常,那麼程式碼將使用異常類型、異常實例和回溯物件(即exc_type、exc_value和exc_tb)呼叫 .__exit__() 。通常情況下,這些在上下文管理器中會被忽略,而在引發異常之前調用 .__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

現在我們初步了解了上下文管理器是什麼以及如何建立自己的上下文管理器。在上面的例子中,我們只是為了建立一個上下文管理器,卻寫了一個類別。如果只是要實作一個簡單的功能,寫一個類別未免有點過於繁雜。這時候,我們就想,如果只寫一個函數就可以實作上下文管理器就好了。

這個點Python早就想到了。它給我們提供了一個裝飾器,你只要按照它的程式碼協定來實作函數內容,就可以將這個函數物件變成一個上下文管理器。

我们按照 contextlib 的协议来自己实现一个上下文管理器,为了更加直观我们换个用例,创建一个我们常用且熟悉的打开文件(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)
登入後複製

在被装饰函数里,必须是一个生成器(带有yield),而 yield 之前的代码,就相当于__enter__里的内容。yield 之后的代码,就相当于__exit__ 里的内容。

上面这段代码只能实现上下文管理器的第一个目的(管理资源),并不能实现第二个目的(处理异常)。

如果要处理异常,可以改成下面这个样子。

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包括定义新上下文管理器的便捷方法,以及可用于关闭对象、抑制错误甚至什么都不做的现成上下文管理器!

创建 Python 计时器上下文管理器

了解了上下文管理器的一般工作方式后,要想知道它们是如何帮助处理时序代码呢?假设如果可以在代码块之前和之后运行某些函数,那么就可以简化 Python 计时器的工作方式。其实,上下文管理器可以自动为计时时显式调用 .start() 和.stop()。

同样,要让 Timer 作为上下文管理器工作,它需要遵守上下文管理器协议,换句话说,它必须实现 .__enter__() 和 .__exit__() 方法来启动和停止 Python 计时器。从目前的代码中可以看出,所有必要的功能其实都已经可用,因此只需将以下方法添加到之前编写的的 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 现在就是一个上下文管理器。实现的重要部分是在进入上下文时, .__enter__() 调用 .start() 启动 Python 计时器,而在代码离开上下文时, .__exit__() 使用 .stop() 停止 Python 计时器。

from timer import Timer
import time
with Timer():
time.sleep(0.7)
登入後複製
Elapsed time: 0.7012 seconds
登入後複製

此处注意两个更微妙的细节:

  • .__enter__()​ 返回self​,Timer 实例,它允许用户使用as​ 将Timer ​实例绑定到变量。例如,使用with Timer() as t:​ 将创建指向Timer ​对象的变量t。
  • .__exit__()​ 需要三个参数,其中包含有关上下文执行期间发生的任何异常的信息。代码中,这些参数被打包到一个名为exc_info 的元组中,然后被忽略,此时 Timer 不会尝试任何异常处理。

在这种情况下不会处理任何异常。上下文管理器的一大特点是,无论上下文如何退出,都会确保调用.__exit__()。在以下示例中,创建除零公式模拟异常查看代码功能:

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 也会打印出经过的时间。

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

现在我们将一起学习如何使用 Timer 上下文管理器来计时 "下载数据" 程序。回想一下之前是如何使用 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() 的调用进行记时监控。使用上下文管理器可以使代码更短、更简单、更易读:

# 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()
登入後複製

此代码实际上与上面的代码相同。主要区别在于没有定义无关变量t,在命名空间上无多余的东西。

写在最后

将上下文管理器功能添加到 Python 计时器类有几个优点:

  • 省时省力:只需要一行额外的代码即可为代码块的执行计时。
  • 可读性高:调用上下文管理器是可读的,你可以更清楚地可视化你正在计时的代码块。

使用 Timer 作为上下文管理器几乎与直接使用 .start() 和 .stop() 一样灵活,同时它的样板代码更少。在该系列下一篇文章中,云朵君将和大家一起学习如何将 Timer 也用作装饰器,并用于代码中,从而更加容易地监控代码完整运行过程,我们一起期待吧!

以上是如何用上下文管理器擴充 Python 計時器的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1673
14
CakePHP 教程
1429
52
Laravel 教程
1333
25
PHP教程
1278
29
C# 教程
1257
24
PHP和Python:解釋了不同的範例 PHP和Python:解釋了不同的範例 Apr 18, 2025 am 12:26 AM

PHP主要是過程式編程,但也支持面向對象編程(OOP);Python支持多種範式,包括OOP、函數式和過程式編程。 PHP適合web開發,Python適用於多種應用,如數據分析和機器學習。

在PHP和Python之間進行選擇:指南 在PHP和Python之間進行選擇:指南 Apr 18, 2025 am 12:24 AM

PHP適合網頁開發和快速原型開發,Python適用於數據科學和機器學習。 1.PHP用於動態網頁開發,語法簡單,適合快速開發。 2.Python語法簡潔,適用於多領域,庫生態系統強大。

sublime怎麼運行代碼python sublime怎麼運行代碼python Apr 16, 2025 am 08:48 AM

在 Sublime Text 中運行 Python 代碼,需先安裝 Python 插件,再創建 .py 文件並編寫代碼,最後按 Ctrl B 運行代碼,輸出會在控制台中顯示。

PHP和Python:深入了解他們的歷史 PHP和Python:深入了解他們的歷史 Apr 18, 2025 am 12:25 AM

PHP起源於1994年,由RasmusLerdorf開發,最初用於跟踪網站訪問者,逐漸演變為服務器端腳本語言,廣泛應用於網頁開發。 Python由GuidovanRossum於1980年代末開發,1991年首次發布,強調代碼可讀性和簡潔性,適用於科學計算、數據分析等領域。

Python vs. JavaScript:學習曲線和易用性 Python vs. JavaScript:學習曲線和易用性 Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Golang vs. Python:性能和可伸縮性 Golang vs. Python:性能和可伸縮性 Apr 19, 2025 am 12:18 AM

Golang在性能和可擴展性方面優於Python。 1)Golang的編譯型特性和高效並發模型使其在高並發場景下表現出色。 2)Python作為解釋型語言,執行速度較慢,但通過工具如Cython可優化性能。

vscode在哪寫代碼 vscode在哪寫代碼 Apr 15, 2025 pm 09:54 PM

在 Visual Studio Code(VSCode)中編寫代碼簡單易行,只需安裝 VSCode、創建項目、選擇語言、創建文件、編寫代碼、保存並運行即可。 VSCode 的優點包括跨平台、免費開源、強大功能、擴展豐富,以及輕量快速。

notepad 怎麼運行python notepad 怎麼運行python Apr 16, 2025 pm 07:33 PM

在 Notepad 中運行 Python 代碼需要安裝 Python 可執行文件和 NppExec 插件。安裝 Python 並為其添加 PATH 後,在 NppExec 插件中配置命令為“python”、參數為“{CURRENT_DIRECTORY}{FILE_NAME}”,即可在 Notepad 中通過快捷鍵“F6”運行 Python 代碼。

See all articles