這是我使用python寫的第一個類(也算是學習面向對象語言以來正式寫的第一個解耦的類),記錄下改進的過程。
分析需求
最初,因為使用time模塊顯示日期時,每次都要設置時間字符串的格式,挺麻煩,但還是忍了。
后來,在處理多線程任務時需要實現定時控制的功能,更麻煩,終于決定自己做一個解決這些問題的通用代碼(雖然網上有現成的模塊,但親手編寫這部分代碼正好能鍛煉一下我的面向對象編程)。
分析框架
剛開始,我計劃做一個模仿時鐘的抽象類,讓它獨立運行在一個線程中,讓它提供顯示日期、計時、設置定時任務的方法……然而由于缺乏規劃,編程亂糟糟的,這些方法的代碼和變量交雜在一起,難以入目,更難以擴展……氣得重構代碼,這次把顯示日期、計時、設置定時任務三大功能分別抽象成三個類,相互解耦,各自獨立運行,代碼變得簡潔多了。
ok,舊代碼就藏在git的歷史記錄里吧,這里貼出重構后的代碼。
顯示時間的類
import time import threading class _Clock: """ 自定義的時鐘類,用于獲取幾種不同格式的當前時間。 decimal : 設置time_float的精度,控制其保留幾位小數。 time_diff : 設置該時鐘與UTC+0時區的時差。如果不設置,會自動采用 本地時區。 """ def __init__(self, name=None, decimal=3, time_diff=None): self.name = name self.decimal = decimal self.time_diff = time_diff self.time_format = "%Y/%m/%d %H:%M:%S" # 時間字符串的格式 @property def time_float(self): """ UTC+0時區的時間戳,精度由self.decimal決定 """ return round(time.time(), self.decimal) @property def time_int(self): """ UTC+0時區的時間戳,精度為秒 """ return int(time.time()) @property def time_tuple(self): """ 本地時區的時間元組 """ if self.time_diff == None: return time.localtime(self.time_int) else: return time.gmtime(self.time_int+self.time_diff) @property def time_str(self): """ 本地時間的格式化字符串 """ return time.strftime(self.time_format, self.time_tuple)
秒表計時的類
class Timer(_Clock): """ 自定義的計時器,像秒表一樣,可以隨時查看當前計時、暫停計時、繼續計時。 ? 創建一個計時器之后,它就會開始計時。 ? 默認使用time.time()獲取時間,精度為毫秒。 ? 可以直接調用_Clock類的方法來獲取當前時間。 """ def __init__(self, *args, **kwargs): _Clock.__init__(self, *args, **kwargs) self.record = [] # 記錄每次使用的 (開始時刻,暫停時刻,計時時長) self.status = "initial" self.go() @property def count(self): """ 當前計時值 """ count = 0 for line in self.record: if line[2] == None: count += self.time_float - line[0] else: count += line[2] return round(count, self.decimal) def go(self): """ 開始計時 """ if self.status != "timing": self.record.append((self.time_float, None, None)) self.status = "timing" def pause(self): """ 暫停計時 """ # 如果該計時器在計時中,就暫停它,并計算這一段的計時時長 if self.status == "timing": last_line = self.record[-1] self.record.remove(last_line) current_time = self.time_float self.record.append( (last_line[0], current_time, round(current_time - last_line[0], self.decimal))) self.status = "paused"
定時任務的類
class Schedule(threading.Thread): """ 自定義的定時任務表,添加第一個定時任務后就創建一個線程,開始循環檢查 是否執行任務表中的任務。 ? 調用stop()來終止該線程。 """ def __init__(self, *args, **kwargs): threading.Thread.__init__(self, *args, **kwargs) self._askToStop = False self._schedule = [] # 保存定時任務表 self.status = "initial" def _get_time(self): """ 獲取當前時間 """ return time.time() def addTask(self, countDown, func, *args, **kwargs): """ 在任務表中增加一項定時任務:在倒計時countDown結束之后調用 函數func,并傳入參數*args和**kwargs。 ? 定時任務只會被執行一次,執行后就會被從任務表中刪除。 ? 定時任務只會在倒計時結束之后被執行,但無法保證無延遲。 """ if self.status == "initial": # 第一次添加定時任務時創建一個新線程 self.status = "running" self.start() task = [] if isinstance(countDown, (int, float)) and countDown > 0: task.append(self._get_time()+countDown) # 準備在指定時刻執行該任務 else: raise ValueError("'countDown' must be a positive int or float.") if callable(func): task.append(func) else: raise ValueError("'func' must be callable.") task.append(args) # 保存元組參數 task.append(kwargs) # 保存字典參數 self._schedule.append(task) self._schedule.sort(key=lambda task: task[0]) # 將任務表按時間戳的大小排序 def _doTask(self): """ 檢查任務表中各項任務的時間,判斷是否要執行它。 """ current_time = self._get_time() i = 0 while i < len(self._schedule): # 遍歷任務表 task = self._schedule[i] if task[0] <= current_time: # 如果該任務的時間不晚于當前時間,就創建一個線程去執行該任務,避免阻塞定時器線程 t1 = CreatThread(task[1], *task[2], **task[3]) t1.start() i += 1 else: break # 如果該任務的時間戳大于當前時間,就提前結束遍歷 del self._schedule[:i] # 刪除過時的任務 def run(self): """ 線程循環運行的內容 """ while not self._askToStop: self._doTask() # 結束時進行清理 self.status == "stopped" return 0 def stop(self): self._askToStop = True class CreatThread(threading.Thread): """ 一個簡單的創建線程的類 """ def __init__(self, func, *args, **kwargs): threading.Thread.__init__(self) self.func = func self.args = args self.kwargs = kwargs def run(self): self.func(*self.args, **self.kwargs)
源代碼:use_time.py
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
