課時44:魔法方法:簡單定製

那是個好男孩發表於2018-08-25

目錄:

  一、簡單定製

  二、課時44課後習題及答案

 

****************

一、簡單定製

****************

基本要求:
1>> 定製一個計時器的類
2>> start和stop方法代表啟動計時和停止計時
3>> 假設計時器物件t1,print(t1)和直接呼叫t1均顯示結果
4>> 當計時器未啟動或已經停止計時,呼叫stop方法會給予溫馨的提示
5>> 兩個計時器物件可以進行相加:t1 + t2
6>> 只能使用提供的有限資源完成

 

你需要這些資源:

1>> 使用time模組的localtime方法獲取時間
   【擴充套件閱讀】:time 模組詳解(時間獲取和轉換)
2>> time.localtime返回struct_time的時間格式
3>> 表現你的類:__str__ ()和 __repr__()魔法方法

>>> class A:
    def __str__(self):
        return "小甲魚是帥哥"

    
>>> a = A()
>>> print(a)
小甲魚是帥哥
>>> a
<__main__.A object at 0x0000020BB2E537F0>
>>> class B:
    def __repr__(self):
        return "小甲魚是帥哥"

    
>>> b = B()
>>> b
小甲魚是帥哥

 

有了這些知識,可以開始編寫程式碼了:

 

import time as t

class MyTimer:
    #開始計時
    def start(self):
        self.start = t.localtime()
        print("計時開始...")
    #停止計時
    def stop(self):
        self.stop = t.localtime()
        print("計時結束!")

    """
    好,萬丈高樓平地起,把地基寫好後,應該考慮怎麼進行計算了。
    localtime()返回的是一個時間元組的結構,只需要在前邊6個元素,
    然後將stop的元素依此減去start對應的元素,將差值存放在一個新的列表裡:
    """
    #停止計時
    def stop(self):
        self.stop = t.localtime()
        self._calc()
        print("計時結束!")

    
    # 內部方法,計算執行時間
    def _calc(self):
        self.lasted = []
        self.prompt = "總共執行了"
        for index in range(6):
            self.lasted.append(self.stop[index] - self.start[index])
            self.prompt += str(self.lasted[index])

        print(self.prompt)
>>> t1 = MyTimer()
>>> t1.start()
計時開始...
>>> t1.stop()
總共執行了000008
計時結束!

已經基本實現計時功能了,接下來需要完成“print(t1)和直接呼叫t1均顯示結果”,那就要通過重寫__str__()和__repr__()魔法方法來實現:

    def __str__(self):
        return self.prompt
    __repr__ = __str__
>>> t1 = MyTimer()
>>> t1.start()
計時開始...
>>> t1.stop()
總共執行了000004
計時結束!
>>> t1
總共執行了000004

似乎做得很不錯了,但這裡還有一些問題。假使使用者不按常理出牌,問題就會很多:

>>> t1 = MyTimer()
>>> t1
Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    t1
  File "C:\Users\14158\AppData\Local\Programs\Python\Python37\lib\idlelib\rpc.py", line 617, in displayhook
    text = repr(value)
  File "C:\Users\14158\Desktop\lalallalalal.py", line 36, in __str__
    return self.prompt
AttributeError: 'MyTimer' object has no attribute 'prompt'

當直接執行t1的時候,Python會呼叫__str__()魔法方法,但它卻說這個類沒有prompt屬性。prompt屬性在哪裡定義的?在_calc()方法裡定義的,對不?但是沒有執行stop()方法,_calc()方法就沒有被呼叫到,所以也就沒有prompt屬性的定義了。

要解決這個問題也很簡單,大家應該還記得在類裡邊,用得最多的一個魔法方法是什麼?是__init__()嘛,所有屬於例項物件的變數只要在這裡邊先定義,就不會出現這樣的問題了。

    def __init__(self):
        self.prompt = "未開始計時!"
        self.lasted = []
        self.start = 0
        self.stop = 0
>>> t1 = MyTimer()
>>> t1
未開始計時!
>>> t1.start()
Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    t1.start()
TypeError: 'int' object is not callable

這裡又報錯了(當然是故意的),先檢查一下出現了什麼問題?

Python這裡丟擲了一個異常:TypeError: 'int' object is not callable

仔細瞧,在呼叫start()方法的時候報錯,也就是說,Python認為start是一個整型變數,而不是一個方法。為什麼呢?大家看__init__()方法裡,是不是也命名了一個叫做self.start的變數,如果類中的方法名和屬性同名,屬性會覆蓋方法。

好了,讓我們把self.start和self.stop都改為self.begin和self.end吧!

現在程式沒什麼問題了,但顯示的時間不怎麼好看,希望按章年月日時分秒來顯示,所以這裡新增一個列表用於存放對應的單位,然後再適當的地方增加溫馨提示:

 

import time as t

class MyTimer:
    def __init__(self):
        self.unit = ['', '', '', '小時', '分鐘', '']
        self.prompt = "未開始計時!"
        self.lasted = []
        self.begin = 0
        self.end = 0
    
    def __str__(self):
        return self.prompt

    __repr__ = __str__

    def __add__(self, other):
        prompt = "總共執行了"
        result = []
        for index in range(6):
            result.append(self.lasted[index] + other.lasted[index])
            if result[index]:
                prompt += (str(result[index]) + self.unit[index])
        return prompt
    
    # 開始計時
    def start(self):
        self.begin = t.localtime()
        self.prompt = "提示:請先呼叫 stop() 停止計時!"
        print("計時開始...")

    # 停止計時
    def stop(self):
        if not self.begin:
            print("提示:請先呼叫 start() 進行計時!")
        else:
            self.end = t.localtime()
            self._calc()
            print("計時結束!")

    # 內部方法,計算執行時間
    def _calc(self):
        self.lasted = []
        self.prompt = "總共執行了"
        for index in range(6):
            self.lasted.append(self.end[index] - self.begin[index])
            if self.lasted[index]:
                self.prompt += (str(self.lasted[index]) + self.unit[index])
        # 為下一輪計時初始化變數
        self.begin = 0
        self.end = 0

 

最後再寫一個魔法方法__add__(),讓兩個計時器物件相加會自動返回時間的和:

    def __add__(self,other):
        prompt = "總共執行了"
        result = []
        for index in range(6):
            result.append(self.lasted[index] + other.lasted[index])
            if result[index]:
                prompt += (str(result[index]) + self.unit[index])
        return prompt
>>> t1 = MyTimer()
>>> t1
未開始計時!
>>> t1.stop()
提示:請先呼叫 start() 進行計時!
>>> t1.start()
計時開始...
>>> t1
提示:請先呼叫 stop() 停止計時!
>>> t1.stop()
計時結束!
>>> t1
總共執行了7秒
>>> t2 = MyTimer()
>>> t2.start()
計時開始...
>>> t2.stop()
計時結束!
>>> t1 + t2
'總共執行了18秒'

 

看上去程式碼不錯,也能正常計算了。但是這個程式還有幾點不足的地方:

(1)如果開始計時的時間是(2022年2月22日16:30:30),停止時間是(2025年1月23日15:30:30),那按照我們用停止時間減開始時間的計算方式就會出現負數(3年-1月1天-1小時),你應該對此做一些轉換。
(2)現在的計算機速度都非常快,而我們這個程式最小的計算單位卻只是秒,精度是遠遠不夠的。

 

*******************************

二、課時44課後習題及答案

*******************************

 

 

相關文章