2020年9月16日星期三

[課後作業] 第044講:魔法方法:簡單定制|課後測試題及答案

[課後作業] 第044講:魔法方法:簡單定制|課後測試題及答案




《零基礎入門學習Python》視頻下載地址:傳送門K
e<1-dWa.y
動動手:
qvBrkhRz版權屬於:bbs.fishc.com G
0.按照課堂中的程序,如果開始計時的時間是(2022年2月22日16:30:30),停止時間是(2025年1月23日15:30:30),那按照我們用停止時間減開始時間的計算方式就會出現負數,你應該對此做一些轉換。L<C^;f.
9;gYe"P
2G0u<
來自:bbs.fishc.com nhHDq
1.相信大家已經意識到不對勁了:為毛一個月一定要31天?不知道有可能也是30天或者29天嗎?(上一題我們的答案是假設一個月31天)^$ZyBzx



I}A2l.Y_X
沒錯,如果要正確得到月份的天數,我們還需要考慮是否閏年,還有每月的最大天數,所以太麻煩了……如果我們不及時糾正,我們會在錯誤的道路上越走越遠…… &eZ來自:bbs.fishc.com u
:qoe =hV

=-8f(SOrua
所以,這一次,小甲魚提出了更優秀的解決方案(Python官方推薦):用time模塊的perf_counter()和process_time()來計算,其中perf_counter()返回計時器的精準時間(系統的運行時間); process_time()返回當前進程執行CPU的時間總和。F&k1hv0-d+
%Wp<kc來自:bbs.fishc.com mv
題目:改進我們課堂中的例子,這次使用perf_counter()和process_time ()作為計時器。另外增加一個set_timer()方法,用於設置默認計時器(默認是perf_counter(),可以通過此方法修改為process_time())。{版權屬於:bbs.fishc.com Iz7it*
- GO ,'gm#h
~z<6PC{0^
N~m@B
2.既然咱都做到了這一步,那不如再深入一下。再次改進我們的代碼,讓它能夠統計一個函數運行若干次的時間
d!ksJ
要求一:函數調用的次數可以設置(默認是1000000次)N9_&版權屬於:bbs.fishc.com MySP
要求二:新增一個timing()方法,用於啟動計時器zBEegp#'s <
2d6uin.
函數演示:qp版權屬於:bbs.fishc.com U+96

  1. >>> ================================ RESTART ============== ==================
  2. >>>
  3. >>> def test():
  4.         text = "I love FishC.com!"
  5.         char = 'o'
  6.         if char in text:
  7.                 pass

  8.         
  9. >>> t1 = MyTimer(test)
  10. >>> t1.timing()
  11. >>> t1
  12. 總共運行了0.27 秒
  13. >>> t2 = MyTimer(test, 100000000)
  14. >>> t2.timing()
  15. >>> t2
  16. 總共運行了25.92 秒
  17. >>> t1 + t2
  18. '總共運行了26.19 秒'
複製代碼

3.請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!swW2_

回复您的答案即可查看參考答案!5來自:bbs.fishc.com BV_Jts#b
來自:bbs.fishc.com Bi?q)
^]Z$;V
動動手答案:

本帖隱藏的內容

Oj.cy,
0.答:參考代碼寫的比較“糾結”,期待魚油們寫出更漂亮的實現。jS{?kV*M
>l%$ODQ|r版權屬於:bbs.fishc.com
代碼清單:-#Powered by bbs.fishc.com RYc
`~!T?5FqX
  1. import time as t

  2. class MyTimer:
  3.     def __init__(self):
  4.         self.unit = ['年', '月', '天', '小時', '分鐘', '秒']
  5.         self.borrow = [0, 12, 31, 24, 60, 60]
  6.         self.prompt = "未開始計時!"
  7.         self.lasted = []
  8.         self.begin = 0
  9.         self.end = 0
  10.    
  11.     def __str__(self):
  12.         return self.prompt

  13.     __repr__ = __str__

  14.     def __add__(self, other):
  15.         prompt = "總共運行了"
  16.         result = []
  17.         for index in range(6):
  18.             result.append(self.lasted[index] + other.lasted[index])
  19.             if result[index]:
  20.                 prompt += (str(result[index]) + self.unit[index])
  21.         return prompt
  22.    
  23.     # 開始計時
  24.     def start(self):
  25.         self.begin = t.localtime()
  26.         self.prompt = "提示:請先調用stop() 停止計時!"
  27.         print("計時開始...")

  28.     # 停止計時
  29.     def stop(self):
  30.         if not self.begin:
  31.             print("提示:請先調用start() 進行計時!")
  32.         else:
  33.             self.end = t.localtime()
  34.             self._calc()
  35.             print("計時結束!")

  36.     # 內部方法,計算運行時間
  37.     def _calc(self):
  38.         self.lasted = []
  39.         self.prompt = "總共運行了"
  40.         for index in range(6):
  41.             temp = self.end[index] - self.begin[index]

  42.             # 低位不夠減,需向高位借位
  43.             if temp < 0:
  44.                 # 測試高位是否有得“借”,沒得借的話向再高位借......
  45.                 i = 1
  46.                 while self.lasted[index-i] < 1:
  47.                     self.lasted[index-i] += self.borrow[index-i] - 1
  48.                     self.lasted[index-i-1] -= 1
  49.                     i += 1
  50.                
  51.                 self.lasted.append(self.borrow[index] + temp)
  52.                 self.lasted[index-1] -= 1
  53.             else:
  54.                 self.lasted.append(temp)

  55.         # 由於高位隨時會被借位,所以打印要放在最後
  56.         for index in range(6):
  57.             if self.lasted[index]:
  58.                 self.prompt += str(self.lasted[index]) + self.unit[index]
  59.         
  60.         # 為下一輪計時初始化變量
  61.         self.begin = 0
  62.         self.end = 0
複製代碼

JLQPn{*
6[MV2Q1C3
1.代碼清單:版權屬於:bbs.fishc.com ) 1'
Corf[1euXn
  1. import time as t

  2. class MyTimer:
  3.     def __init__(self):
  4.         self.prompt = "未開始計時!"
  5.         self.lasted = 0.0
  6.         self.begin = 0
  7.         self.end = 0
  8.         self.default_timer = t.perf_counter
  9.    
  10.     def __str__(self):
  11.         return self.prompt

  12.     __repr__ = __str__

  13.     def __add__(self, other):
  14.         result = self.lasted + other.lasted
  15.         prompt = "總共運行了%0.2f 秒" % result
  16.         return prompt
  17.    
  18.     # 開始計時
  19.     def start(self):
  20.         self.begin = self.default_timer()
  21.         self.prompt = "提示:請先調用stop() 停止計時!"
  22.         print("計時開始...")

  23.     # 停止計時
  24.     def stop(self):
  25.         if not self.begin:
  26.             print("提示:請先調用start() 進行計時!")
  27.         else:
  28.             self.end = self.default_timer()
  29.             self._calc()
  30.             print("計時結束!")

  31.     # 內部方法,計算運行時間
  32.     def _calc(self):
  33.         self.lasted = self.end - self.begin
  34.         self.prompt = "總共運行了%0.2f 秒" % self.lasted
  35.         
  36.         # 為下一輪計時初始化變量
  37.         self.begin = 0
  38.         self.end = 0

  39.     # 設置計時器(time.perf_counter() 或time.process_time())
  40.     def set_timer(self, timer):
  41.         if timer == 'process_time':
  42.             self.default_timer = t.process_time
  43.         elif timer == 'perf_counter':
  44.             self.default_timer = t.perf_counter
  45.         else:
  46.             print("輸入無效,請輸入perf_counter 或process_time")
複製代碼

n$76d%3u+
M>p%QFd
2.代碼清單:NM@ep
f來自:bbs.fishc.com a!hvb:
  1. import time as t

  2. class MyTimer:
  3.     def __init__(self, func, number=1000000):
  4.         self.prompt = "未開始計時!"
  5.         self.lasted = 0.0
  6.         self.default_timer = t.perf_counter
  7.         self.func = func
  8.         self.number = number
  9.    
  10.     def __str__(self):
  11.         return self.prompt

  12.     __repr__ = __str__

  13.     def __add__(self, other):
  14.         result = self.lasted + other.lasted
  15.         prompt = "總共運行了%0.2f 秒" % result
  16.         return prompt

  17.     # 內部方法,計算運行時間
  18.     def timing(self):
  19.         self.begin = self.default_timer()
  20.         for i in range(self.number):
  21.             self.func()
  22.         self.end = self.default_timer()
  23.         self.lasted = self.end - self.begin
  24.         self.prompt = "總共運行了%0.2f 秒" % self.lasted
  25.         
  26.     # 設置計時器(time.perf_counter() 或time.process_time())
  27.     def set_timer(self, timer):
  28.         if timer == 'process_time':
  29.             self.default_timer = t.process_time
  30.         elif timer == 'perf_counter':
  31.             self.default_timer = t.perf_counter
  32.         else:
  33.             print("輸入無效,請輸入perf_counter 或process_time")
複製代碼

W@TGDY'eAv
其實,小甲魚有一件事一直瞞著大家……就是……關於Python代碼優化你需要知道的最重要問題是,決不要自己編寫計時函數!pn [W?
<kOZEs24

5[2y9
Bb=]3
為一個很短的代碼計時都很複雜,因為你不知道處理器有多少時間用於運行這個代碼?有什麼在後台運行?小小的疏忽可能破壞你的百年大計,後台服務偶爾被“喚醒”在最後千分之一秒做一些像查收信件,連接計時通信服務器,檢查應用程序更新,掃描病毒,查看是否有磁盤被插入光驅之類很有意義的事。在開始計時測試之前,把一切都關掉,斷開網絡的連接。再次確定一切都關上後關掉那些不斷查看網絡是否恢復的服務等等。eo*XO4}來自:bbs.fishc.com EB

接下來是計時框架本身引入的變化因素。Python解釋器是否緩存了方法名的查找?是否緩存代碼塊的編譯結果?正則表達式呢?你的代碼重複運行時有副作用嗎?不要忘記,你的工作結果將以比秒更小的單位呈現,你的計時框架中的小錯誤將會帶來不可挽回的結果扭曲。^7C.&[)-sf
]uf6DXPowered by bbs.fishc.com
Python社區有句俗語:“Python自己帶著電池。”別自己寫計時框架。Python具備一個叫做timeit的完美計時工具。+gwa=n_fu
!>w_U<ca
或許你現在怨恨小甲魚為什麼不早點說,但如果你在這節課的折騰中已經掌握了類的定制和使用,那小甲魚的目的就達到了。接下來,請認真閱讀更為專業的計時器用法及實現源碼:【擴展閱讀】timeit模塊詳解(準確測量小段代碼的執行時間),:+_%h
1版權屬於:bbs.fishc.com=qT wZ
A>d.W3

3.請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!

小甲魚希望你認真對待作業就像你希望小甲魚推出高質量視頻一樣渴望^_^

這節課我們一起來完成一個類的定制,
基本要求:
  • 定制一個計時器的類
  • start和stop方法代表啟動計時和停止計時
  • 假設計時器對象t1,print(t1) 和直接調用t1 均顯示結果
  • 當計時器未啟動或已經停止計時,調用stop方法會給予溫馨的提示
  • 兩個計時器對象可以進行相加:t1 + t2
  • 只能使用提供的有限資源完成
顯然,你需要這些資源:
  • 使用time模塊的localtime方法獲取時間
  • time.localtime 返回struct_time 的時間格式
你可以用索引值直接去索引它。
  • 因為直接調用對象就要顯示結果,所以需要表現你的類:__str__和__repr__,具體用法見:Python魔法方法詳解 - 基本的魔法方法
例如:
  1. >>> class A:
  2. def __str__(self):
  3. return("你好,来自江南的你!")
  4. >>> a = A()
  5. >>> print(a)
  6. 你好,来自江南的你!
  7. >>> a
  8. <__main__.A object at 0x000002D0B1F65390>
__str__ 用在被打印的時候需要以字符串的形式輸出的時候,就會找到 __str__魔法方法,然後把返回的值打印出來。
下面是__repr__的示例:
  1. >>> class B:
  2. def __repr__(self):
  3. return "你好,来自江南的你!"
  4. >>> b = B()
  5. >>> b
  6. 你好,来自江南的你!
下面代碼走起:
  1. import time as t
  2. class MyTimer:
  3. def __init__(self):
  4. self.unit = ['年', '月', '天', '小时', '分钟', '秒']
  5. self.prompt = "未开始计时!"
  6. self.lasted = []
  7. self.begin = 0
  8. self.end = 0
  9. def __str__(self):
  10. return self.prompt
  11. __repr__ = __str__
  12. def __add__(self, other):
  13. prompt = "总共运行了"
  14. result = []
  15. for index in range(6):
  16. result.append(self.lasted[index] + other.lasted[index])
  17. if result[index]:
  18. prompt += (str(result[index]) + self.unit[index])
  19. return prompt
  20. # 开始计时
  21. def start(self):
  22. self.begin = t.localtime()
  23. self.prompt = "提示:请先调用 stop() 停止计时!"
  24. print("计时开始...")
  25. # 停止计时
  26. def stop(self):
  27. if not self.begin:
  28. print("提示:请先调用 start() 进行计时!")
  29. else:
  30. self.end = t.localtime()
  31. self._calc()
  32. print("计时结束!")
  33. # 内部方法,计算运行时间
  34. def _calc(self):
  35. self.lasted = []
  36. self.prompt = "总共运行了"
  37. for index in range(6):
  38. self.lasted.append(self.end[index] - self.begin[index])
  39. if self.lasted[index]:
  40. self.prompt += (str(self.lasted[index]) + self.unit[index])
  41. # 为下一轮计时初始化变量
  42. self.begin = 0
  43. self.end = 0
進階定制
  • 如果開始計時的時間是(2022年2月22日16:30:30),停止時間是(2025年1月23日15:30:30),那按照我們用停止時間減開始時間的計算方式就會出現負數(3年-1月1天-1小時),你應該對此做一些轉換。
  • 現在的計算機速度都非常快,而我們這個程序最小的計算單位卻只是秒,精度是遠遠不夠的。(看下面的課後作業)

0 留言:

發佈留言