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

手把手帶你用Python實(shí)現(xiàn)一個(gè)計(jì)時(shí)器

 更新時(shí)間:2022年06月28日 14:09:40   作者:云朵君  
雖然Python是一種有效的編程語言,但純Python程序比C、Rust和Java等編譯語言中的對(duì)應(yīng)程序運(yùn)行得更慢,為了更好地監(jiān)控和優(yōu)化Python程序,今天將為大家介紹如何使用?Python?計(jì)時(shí)器來監(jiān)控程序運(yùn)行的速度,以便正對(duì)性改善代碼性能

雖然許多數(shù)據(jù)工作者認(rèn)為 Python 是一種有效的編程語言,但純 Python 程序比C、Rust 和 Java 等編譯語言中的對(duì)應(yīng)程序運(yùn)行得更慢,為了更好地監(jiān)控和優(yōu)化Python程序,云朵君將和大家一起學(xué)習(xí)如何使用 Python 計(jì)時(shí)器來監(jiān)控程序運(yùn)行的速度,以便正對(duì)性改善代碼性能。

為了更好地掌握 Python 計(jì)時(shí)器的應(yīng)用,我們后面還補(bǔ)充了有關(guān)Python類、上下文管理器和裝飾器的背景知識(shí)。因篇幅限制,其中利用上下文管理器和裝飾器優(yōu)化 Python 計(jì)時(shí)器,將在后續(xù)文章學(xué)習(xí),不在本篇文章范圍內(nèi)。

Python 計(jì)時(shí)器

首先,我們向某段代碼中添加一個(gè)Python 計(jì)時(shí)器以監(jiān)控其性能。

Python 定時(shí)器函數(shù)

Python 中的內(nèi)置time[1]模塊中有幾個(gè)可以測(cè)量時(shí)間的函數(shù):

  • monotonic()
  • perf_counter()
  • process_time()
  • time()

Python 3.7 引入了幾個(gè)新函數(shù),如thread_time()[2],以及上述所有函數(shù)的納秒版本,以_ns后綴命名。例如,perf_counter_ns()perf_counter()的納秒版本的。

perf_counter()返回性能計(jì)數(shù)器的值(以秒為單位),即具有最高可用分辨率的時(shí)鐘以測(cè)量短持續(xù)時(shí)間。

首先,使用perf_counter()創(chuàng)建一個(gè) Python 計(jì)時(shí)器。將把它與其他 Python 計(jì)時(shí)器函數(shù)進(jìn)行比較,看看 perf_counter() 的優(yōu)勢(shì)。

示例

創(chuàng)建一個(gè)腳本,定義一個(gè)簡(jiǎn)短的函數(shù):從清華云上下載一組數(shù)據(jù)。

import?requests
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'}
????res?=?requests.get(source_url,?headers=headers)
????with?open('dataset/datasets.zip',?'wb')?as?f:
????????f.write(res.content)
???????
if?__name__=="__main__":
????main()

我們可以使用 Python 計(jì)時(shí)器來監(jiān)控該腳本的性能。

第一個(gè) Python 計(jì)時(shí)器

現(xiàn)在使用函數(shù)time.perf_counter()函數(shù)創(chuàng)建一個(gè)計(jì)時(shí)器,這是一個(gè)非常適合針對(duì)部分代碼的性能計(jì)時(shí)的計(jì)數(shù)器。

perf_counter()從某個(gè)未指定的時(shí)刻開始測(cè)量時(shí)間(以秒為單位),這意味著對(duì)該函數(shù)的單個(gè)調(diào)用的返回值沒有用。但當(dāng)查看對(duì)perf_counter()兩次調(diào)用之間的差異時(shí),可以計(jì)算出兩次調(diào)用之間經(jīng)過了多少秒。

>>>?import?time
>>>?time.perf_counter()
394.540232282

>>>?time.perf_counter()??#?幾秒鐘后
413.31714087

在此示例中,兩次調(diào)用 perf_counter() 相隔近 19 秒??梢酝ㄟ^計(jì)算兩個(gè)輸出之間的差異來確認(rèn)這一點(diǎn):413.31714087 - 394.540232282 = 18.78。

現(xiàn)在可以將 Python 計(jì)時(shí)器添加到示例代碼中:

#?download_data.py
import?requests
import?time
def?main():
????tic?=?time.perf_counter()
????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)
????with?open('dataset/datasets.zip',?'wb')?as?f:
????????f.write(res.content)
????toc?=?time.perf_counter()
????print(f"該程序耗時(shí):?{toc?-?tic:0.4f}?seconds")
if?__name__=="__main__":
????main()

注意perf_counter()通過計(jì)算兩次調(diào)用之間的差異來打印整個(gè)程序運(yùn)行所花費(fèi)的時(shí)間。

print()函數(shù)中 f 字符串前面的表示這是一個(gè) f-string ,這是格式化文本字符串的較為便捷的方式。:0.4f是一個(gè)格式說明符,表示數(shù)字,toc - tic應(yīng)打印為帶有四位小數(shù)的十進(jìn)制數(shù)。

運(yùn)行程序可以看到程序經(jīng)過的時(shí)間:

該程序耗時(shí): 0.026 seconds

就是這么簡(jiǎn)單。接下來我們一起學(xué)習(xí)如何將 Python 計(jì)時(shí)器包裝到一個(gè)類、一個(gè)上下文管理器和一個(gè)裝飾器中,這樣可以更加一致和方便使用計(jì)時(shí)器。

一個(gè) Python 定時(shí)器類

這里我們至少需要一個(gè)變量來存儲(chǔ) Python 計(jì)時(shí)器的狀態(tài)。接下來我們創(chuàng)建一個(gè)與手動(dòng)調(diào)用 perf_counter() 相同的類,但更具可讀性和一致性。

創(chuàng)建和更新Timer類,使用該類以多種不同方式對(duì)代碼進(jìn)行計(jì)時(shí)。

$?python?-m?pip?install?codetiming

理解 Python 中的類

Class是面向?qū)ο缶幊痰闹饕獦?gòu)建塊。類本質(zhì)上是一個(gè)模板,可以使用它來創(chuàng)建對(duì)象

在 Python 中,當(dāng)需要對(duì)需要跟蹤特定狀態(tài)的事物進(jìn)行建模時(shí),類非常有用。一般來說,類是屬性的集合,稱為屬性,以及行為,稱為方法

創(chuàng)建 Python 計(jì)時(shí)器類

類有利于跟蹤狀態(tài)。在Timer類中,想要跟蹤計(jì)時(shí)器何時(shí)開始以及已經(jīng)多少時(shí)間。對(duì)于Timer類的第一個(gè)實(shí)現(xiàn),將添加一個(gè)._start_time屬性以及.start().stop()方法。將以下代碼添加到名為 timer.py 的文件中:

#?timer.py
import?time
class?TimerError(Exception):
????"""一個(gè)自定義異常,用于報(bào)告使用Timer類時(shí)的錯(cuò)誤"""

class?Timer:
????def?__init__(self):
????????self._start_time?=?None

????def?start(self):
????????"""Start?a?new?timer"""
????????if?self._start_time?is?not?None:
????????????raise?TimerError(f"Timer?is?running.?Use?.stop()?to?stop?it")
????????self._start_time?=?time.perf_counter()

????def?stop(self):
????????"""Stop?the?timer,?and?report?the?elapsed?time"""
????????if?self._start_time?is?None:
????????????raise?TimerError(f"Timer?is?not?running.?Use?.start()?to?start?it")

????????elapsed_time?=?time.perf_counter()?-?self._start_time
????????self._start_time?=?None
????????print(f"Elapsed?time:?{elapsed_time:0.4f}?seconds")

這里我們需要花點(diǎn)時(shí)間仔細(xì)地瀏覽代碼,會(huì)發(fā)現(xiàn)一些不同的事情。

首先定義了一個(gè)TimerError Python 類。該(Exception)符號(hào)表示TimerError 繼承自另一個(gè)名為Exception的父類。使用這個(gè)內(nèi)置類進(jìn)行錯(cuò)誤處理。不需要向TimerError添加任何屬性或方法,但自定義錯(cuò)誤可以更靈活地處理Timer內(nèi)部問題。

接下來自定義Timer類。當(dāng)從一個(gè)類創(chuàng)建或實(shí)例化一個(gè)對(duì)象時(shí),代碼會(huì)調(diào)用特殊方法.__init__()初始化實(shí)例。在這里定義的第一個(gè)Timer版本中,只需初始化._start_time屬性,將用它來跟蹤 Python 計(jì)時(shí)器的狀態(tài),計(jì)時(shí)器未運(yùn)行時(shí)它的值為None。計(jì)時(shí)器運(yùn)行后,用它來跟蹤計(jì)時(shí)器的啟動(dòng)時(shí)間。

注意: ._start_time的第一個(gè)下劃線(_)前綴是Python約定。它表示._start_time是Timer類的用戶不應(yīng)該操作的內(nèi)部屬性。

當(dāng)調(diào)用.start()啟動(dòng)新的 Python 計(jì)時(shí)器時(shí),首先檢查計(jì)時(shí)器是否運(yùn)行。然后將perf_counter()的當(dāng)前值存儲(chǔ)在._start_time中。

另一方面,當(dāng)調(diào)用.stop()時(shí),首先檢查Python計(jì)時(shí)器是否正在運(yùn)行。如果是,則將運(yùn)行時(shí)間計(jì)算為perf_counter()的當(dāng)前值與存儲(chǔ)在._start_time中的值的差值。最后,重置._start_time,以便重新啟動(dòng)計(jì)時(shí)器,并打印運(yùn)行時(shí)間。

以下是使用Timer方法:

from?timer?import?Timer
t?=?Timer()
t.start()
#?幾秒鐘后
t.stop()

Elapsed time: 3.8191 seconds

將此示例與前面直接使用perf_counter()的示例進(jìn)行比較。代碼的結(jié)構(gòu)相似,但現(xiàn)在代碼更清晰了,這也是使用類的好處之一。通過仔細(xì)選擇類、方法和屬性名稱,可以使你的代碼非常具有描述性!

使用 Python 計(jì)時(shí)器類

現(xiàn)在Timer類中寫入download_data.py。只需要對(duì)以前的代碼進(jìn)行一些更改:

#?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)
????with?open('dataset/datasets.zip',?'wb')?as?f:
????????f.write(res.content)
????t.stop()
if?__name__=="__main__":
????main()

注意,該代碼與之前使用的代碼非常相似。除了使代碼更具可讀性之外,Timer還負(fù)責(zé)將經(jīng)過的時(shí)間打印到控制臺(tái),使得所用時(shí)間的記錄更加一致。運(yùn)行代碼時(shí),得到的輸出幾乎相同:

Elapsed time: 0.502 seconds
...

打印經(jīng)過的時(shí)間Timer可能是一致的,但這種方法好像不是很靈活。下面我們添加一些更加靈活的東西到代碼中。

增加更多的便利性和靈活性

到目前為止,我們已經(jīng)了解到類適用于我們想要封裝狀態(tài)并確保代碼一致性的情況。在本節(jié)中,我們將一起給 Python 計(jì)時(shí)器加入更多便利性和靈活性,那怎么做呢?

  • 在報(bào)告消耗的時(shí)間時(shí),使用可調(diào)整的文本和格式
  • 日志記錄打印到控制臺(tái)、寫入到日志文件或程序的其他部分
  • 創(chuàng)建一個(gè)可以在多次調(diào)用中可積累的Python計(jì)時(shí)器
  • 構(gòu)建 Python 計(jì)時(shí)器的信息表示

首先,自定義用于報(bào)告所用時(shí)間的文本。在前面的代碼中,文本 f"Elapsed time: {elapsed_time:0.4f} seconds" 被生硬編碼到 .stop() 中。如若想使得類代碼更加靈活, 可以使用實(shí)例變量,其值通常作為參數(shù)傳遞給.__init__()并存儲(chǔ)到 self 屬性。為方便起見,我們還可以提供合理的默認(rèn)值。

要添加.textTimer實(shí)例變量,可執(zhí)行以下操作timer.py

#?timer.py
def?__init__(self,?text="Elapsed?time:?{:0.4f}?seconds"):
????self._start_time?=?None
????self.text?=?text

注意,默認(rèn)文本"Elapsed time: {:0.4f} seconds"是作為一個(gè)常規(guī)字符串給出的,而不是f-string。這里不能使用f-string,因?yàn)?code>f-string會(huì)立即計(jì)算,當(dāng)你實(shí)例化Timer時(shí),你的代碼還沒有計(jì)算出消耗的時(shí)間。

注意: 如果要使用f-string來指定.text,則需要使用雙花括號(hào)來轉(zhuǎn)義實(shí)際經(jīng)過時(shí)間將替換的花括號(hào)。

如:f"Finished {task} in {{:0.4f}} seconds"。如果task的值是"reading",那么這個(gè)f-string將被計(jì)算為"Finished reading in {:0.4f} seconds"

.stop()中,.text用作模板并使用.format()方法填充模板:

#?timer.py
def?stop(self):
????"""Stop?the?timer,?and?report?the?elapsed?time"""
????if?self._start_time?is?None:
????????raise?TimerError(f"Timer?is?not?running.?Use?.start()?to?start?it")

????elapsed_time?=?time.perf_counter()?-?self._start_time
????self._start_time?=?None
????print(self.text.format(elapsed_time))

在此更新為timer.py之后,可以將文本更改如下:

from?timer?import?Timer
t?=?Timer(text="You?waited?{:.1f}?seconds")
t.start()
#?幾秒鐘后
t.stop()

You waited 4.1 seconds

接下來,我們不只是想將消息打印到控制臺(tái),還想保存時(shí)間測(cè)量結(jié)果,這樣可以便于將它們存儲(chǔ)在數(shù)據(jù)庫中??梢酝ㄟ^從.stop()返回elapsed_time的值來實(shí)現(xiàn)這一點(diǎn)。然后,調(diào)用代碼可以選擇忽略該返回值或保存它以供以后處理。

如果想要將Timer集成到日志logging中。要支持計(jì)時(shí)器的日志記錄或其他輸出,需要更改對(duì)print()的調(diào)用,以便用戶可以提供自己的日志記錄函數(shù)。這可以用類似于你之前定制的文本來完成:

#?timer.py
#?...
class?Timer:
????def?__init__(
????????self,
????????text="Elapsed?time:?{:0.4f}?seconds",
????????logger=print
????):
????????self._start_time?=?None
????????self.text?=?text
????????self.logger?=?logger
????#?其他方法保持不變
????def?stop(self):
????????"""Stop?the?timer,?and?report?the?elapsed?time"""
????????if?self._start_time?is?None:
????????????raise?TimerError(f"Timer?is?not?running.?Use?.start()?to?start?it")
????????elapsed_time?=?time.perf_counter()?-?self._start_time
????????self._start_time?=?None
????????if?self.logger:
????????????self.logger(self.text.format(elapsed_time))

????????return?elapsed_time

不是直接使用print(),而是創(chuàng)建另一個(gè)實(shí)例變量 self.logger,引用一個(gè)接受字符串作為參數(shù)的函數(shù)。除此之外,還可以對(duì)文件對(duì)象使用logging.info().write()等函數(shù)。還要注意if中,它允許通過傳遞logger=None來完全關(guān)閉打印。

以下是兩個(gè)示例,展示了新功能的實(shí)際應(yīng)用:

from?timer?import?Timer
import?logging
t?=?Timer(logger=logging.warning)
t.start()
#?幾秒鐘后
t.stop()??#?A?few?seconds?later

WARNING:root:Elapsed time: 3.1610 seconds  
3.1609658249999484

t?=?Timer(logger=None)
t.start()
#?幾秒鐘后
value?=?t.stop()
value

4.710851433001153

接下來第三個(gè)改進(jìn)是積累時(shí)間度量的能力。例如,在循環(huán)中調(diào)用一個(gè)慢速函數(shù)時(shí),希望以命名計(jì)時(shí)器的形式添加更多的功能,并使用一個(gè)字典來跟蹤代碼中的每個(gè)Python計(jì)時(shí)器。

我們擴(kuò)展download_data.py腳本。

#?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'}
????for?i?in?range(10):
????????res?=?requests.get(source_url,?headers=headers)
????????with?open('dataset/datasets.zip',?'wb')?as?f:
????????????f.write(res.content)
????t.stop()
if?__name__=="__main__":
????main()

這段代碼的一個(gè)微妙問題是,不僅要測(cè)量下載數(shù)據(jù)所需的時(shí)間,還要測(cè)量 Python 存儲(chǔ)數(shù)據(jù)到磁盤所花費(fèi)的時(shí)間。這可能并重要,有時(shí)候這兩者所花費(fèi)的時(shí)間可以忽略不計(jì)。但還是希望有一種方法可以精確地計(jì)時(shí)沒一個(gè)步驟,將會(huì)更好。

有幾種方法可以在不改變Timer當(dāng)前實(shí)現(xiàn)的情況下解決這個(gè)問題,且只需要幾行代碼即可實(shí)現(xiàn)。

首先,將引入一個(gè)名為.timers的字典作為Timer的類變量,此時(shí)Timer的所有實(shí)例將共享它。通過在任何方法之外定義它來實(shí)現(xiàn)它:

class?Timer:
????timers?=?{}

類變量可以直接在類上訪問,也可以通過類的實(shí)例訪問:

>>>?from?timer?import?Timer
>>>?Timer.timers
{}

>>>?t?=?Timer()
>>>?t.timers
{}

>>>?Timer.timers?is?t.timers
True

在這兩種情況下,代碼都返回相同的空類字典。

接下來向 Python 計(jì)時(shí)器添加可選名稱??梢詫⒃撁Q用于兩種不同的目的:

  • 在代碼中查找經(jīng)過的時(shí)間
  • 累加同名定時(shí)器

要向Python計(jì)時(shí)器添加名稱,需要對(duì) timer.py 進(jìn)行更改。首先,Timer 接受 name 參數(shù)。第二,當(dāng)計(jì)時(shí)器停止時(shí),運(yùn)行時(shí)間應(yīng)該添加到 .timers 中:

#?timer.py
#?...
class?Timer:
????timers?=?{}
????def?__init__(
????????self,
????????name=None,
????????text="Elapsed?time:?{:0.4f}?seconds",
????????logger=print,
????):
????????self._start_time?=?None
????????self.name?=?name
????????self.text?=?text
????????self.logger?=?logger

????????#?向計(jì)時(shí)器字典中添加新的命名計(jì)時(shí)器
????????if?name:
????????????self.timers.setdefault(name,?0)

????#?其他方法保持不變
????
????def?stop(self):
????????"""Stop?the?timer,?and?report?the?elapsed?time"""
????????if?self._start_time?is?None:
????????????raise?TimerError(f"Timer?is?not?running.?Use?.start()?to?start?it")
????????elapsed_time?=?time.perf_counter()?-?self._start_time
????????self._start_time?=?None
????????if?self.logger:
????????????self.logger(self.text.format(elapsed_time))
????????if?self.name:
????????????self.timers[self.name]?+=?elapsed_time
????????return?elapsed_time

注意,在向.timers中添加新的Python計(jì)時(shí)器時(shí),使用了.setdefault()方法。它只在沒有在字典中定義name的情況下設(shè)置值,如果name已經(jīng)在.timers中使用,那么該值將保持不變,此時(shí)可以積累幾個(gè)計(jì)時(shí)器:

>>>?from?timer?import?Timer
>>>?t?=?Timer("accumulate")
>>>?t.start()

>>>?t.stop()??#?A?few?seconds?later
Elapsed?time:?3.7036?seconds
3.703554293999332

>>>?t.start()

>>>?t.stop()??#?A?few?seconds?later
Elapsed?time:?2.3449?seconds
2.3448921170001995

>>>?Timer.timers
{'accumulate':?6.0484464109995315}

現(xiàn)在可以重新訪問download_data.py并確保僅測(cè)量下載數(shù)據(jù)所花費(fèi)的時(shí)間:

#?download_data.py
import?requests
from?timer?import?Timer
def?main():
????t?=?Timer("download",?logger=None)
????source_url?=?'https://cloud.tsinghua.edu.cn/d/e1ccfff39ad541908bae/files/?p=%2Fall_six_datasets.zip&dl=1'
????headers?=?{'User-Agent':?'Mozilla/5.0'}
????for?i?in?range(10):
????????t.start()
????????res?=?requests.get(source_url,?headers=headers)
????????t.stop()
????????with?open('dataset/datasets.zip',?'wb')?as?f:
????????????f.write(res.content)
????download_time?=?Timer.timers["download"]
????print(f"Downloaded?10?dataset?in?{download_time:0.2f}?seconds")
????
if?__name__=="__main__":
????main()

現(xiàn)在你有了一個(gè)非常簡(jiǎn)潔的版本,Timer它一致、靈活、方便且信息豐富!也可以將本節(jié)中所做的許多改進(jìn)應(yīng)用于項(xiàng)目中的其他類型的類。

Timer改進(jìn)

最后一個(gè)改進(jìn)Timer,以交互方式使用它時(shí)使其更具信息性。下面操作是實(shí)例化一個(gè)計(jì)時(shí)器類,并查看其信息:

>>>?from?timer?import?Timer
>>>?t?=?Timer()
>>>?t
<timer.Timer?object?at?0x7f0578804320>

最后一行是 Python 表示對(duì)象的默認(rèn)方式。我們從這個(gè)結(jié)果中看到的信息,并不是很明確,我們接下來對(duì)其進(jìn)行改進(jìn)。

這里介紹一個(gè) dataclasses 類,該類僅包含在 Python 3.7 及更高版本中。

pip?install?dataclasses

可以使用@dataclass裝飾器將 Python 計(jì)時(shí)器轉(zhuǎn)換為數(shù)據(jù)類

#?timer.py
import?time
from?dataclasses?import?dataclass,?field
from?typing?import?Any,?ClassVar
#?...
@dataclass
class?Timer:
????timers:?ClassVar?=?{}
????name:?Any?=?None
????text:?Any?=?"Elapsed?time:?{:0.4f}?seconds"
????logger:?Any?=?print
????_start_time:?Any?=?field(default=None,?init=False,?repr=False)

????def?__post_init__(self):
????????"""Initialization:?add?timer?to?dict?of?timers"""
????????if?self.name:
????????????self.timers.setdefault(self.name,?0)

????#?其余代碼不變

此代碼替換了之前的 .__init__() 方法。請(qǐng)注意數(shù)據(jù)類如何使用類似于之前看到的用于定義所有變量的類變量語法的語法。事實(shí)上,.__init__()是根據(jù)類定義中的注釋變量自動(dòng)為數(shù)據(jù)類創(chuàng)建的。

如果需要注釋變量以使用數(shù)據(jù)類??梢允褂么俗⒔庀虼a添加類型提示。如果不想使用類型提示,那么可以使用 Any 來注釋所有變量。接下來我們很快就會(huì)學(xué)習(xí)如何將實(shí)際類型提示添加到我們的數(shù)據(jù)類中。

以下是有關(guān) Timer 數(shù)據(jù)類的一些注意事項(xiàng):

  • 第 6 行:@dataclass 裝飾器將 Timer 定義為數(shù)據(jù)類。
  • 第 8 行:數(shù)據(jù)類需要特殊的 ClassVar 注釋來指定 .timers 是一個(gè)類變量。
  • 第 9 到 11 行:.name、.text 和 .logger 將被定義為 Timer 上的屬性,可以在創(chuàng)建 Timer 實(shí)例時(shí)指定其值。它們都有給定的默認(rèn)值。
  • 第 12 行:回想一下 ._start_time 是一個(gè)特殊屬性,用于跟蹤 Python 計(jì)時(shí)器的狀態(tài),但它應(yīng)該對(duì)用戶隱藏。使用 dataclasses.field(), ._start_time 應(yīng)該從 .__init__() 和 Timer 的表示中刪除。
  • 除了設(shè)置實(shí)例屬性之外,可以使用特殊的 .__post_init__() 方法進(jìn)行初始化。這里使用它將命名的計(jì)時(shí)器添加到 .timers。

新 Timer 數(shù)據(jù)類與之前的常規(guī)類使用功能一樣,但它現(xiàn)在有一個(gè)很好的信息表示

from?timer?import?Timer
t?=?Timer()
t
Timer(name=None, 
  text='Elapsed time: {:0.4f} seconds',
  logger=<built-in function print>)
t.start()
#?幾秒鐘后
t.stop()

Elapsed time: 6.7197 seconds
6.719705373998295

總結(jié)

現(xiàn)在我們有了一個(gè)非常簡(jiǎn)潔的 Timer 版本,它一致、靈活、方便且信息豐富!我們還可以將本文中所做的許多改進(jìn)應(yīng)用于項(xiàng)目中的其他類型的類。

現(xiàn)在我們?cè)L問當(dāng)前的完整源代碼Timer。會(huì)注意到在代碼中添加了類型提示以獲取額外的文檔:

#?timer.py

from?dataclasses?import?dataclass,?field
import?time
from?typing?import?Callable,?ClassVar,?Dict,?Optional

class?TimerError(Exception):
????"""A?custom?exception?used?to?report?errors?in?use?of?Timer?class"""

@dataclass
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:
????????"""Add?timer?to?dict?of?timers?after?initialization"""
????????if?self.name?is?not?None:
????????????self.timers.setdefault(self.name,?0)

????def?start(self)?->?None:
????????"""Start?a?new?timer"""
????????if?self._start_time?is?not?None:
????????????raise?TimerError(f"Timer?is?running.?Use?.stop()?to?stop?it")

????????self._start_time?=?time.perf_counter()

????def?stop(self)?->?float:
????????"""Stop?the?timer,?and?report?the?elapsed?time"""
????????if?self._start_time?is?None:
????????????raise?TimerError(f"Timer?is?not?running.?Use?.start()?to?start?it")

????????#?Calculate?elapsed?time
????????elapsed_time?=?time.perf_counter()?-?self._start_time
????????self._start_time?=?None

????????#?Report?elapsed?time
????????if?self.logger:
????????????self.logger(self.text.format(elapsed_time))
????????if?self.name:
????????????self.timers[self.name]?+=?elapsed_time

????????return?elapsed_time

總結(jié)下: 使用類創(chuàng)建 Python 計(jì)時(shí)器有幾個(gè)好處:

  • 可讀性:仔細(xì)選擇類和方法名稱,你的代碼將更自然地閱讀。
  • 一致性:將屬性和行為封裝到屬性和方法中,你的代碼將更易于使用。
  • 靈活性:使用具有默認(rèn)值而不是硬編碼值的屬性,你的代碼將是可重用的。

這個(gè)類非常靈活,幾乎可以在任何需要監(jiān)控代碼運(yùn)行時(shí)間的情況下使用它。但是,在接下來的部分中,云朵君將和大家一起了解如何使用上下文管理器和裝飾器,這將更方便地對(duì)代碼塊和函數(shù)進(jìn)行計(jì)時(shí)。

以上就是手把手帶你用Python實(shí)現(xiàn)一個(gè)計(jì)時(shí)器的詳細(xì)內(nèi)容,更多關(guān)于Python計(jì)時(shí)器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論