不間斷 Python 挑戰 Day 20 - 專題:我如何真確理解...類別?

2022/01/09閱讀時間約 13 分鐘
全球公衛教授和公共教育家漢斯.羅斯林(Hans Rosling)在其著名的暢銷書「真確」中,提出了13個關於全球貧富、人口、出生/死亡率、教育、公衛等的「留意認知落差測驗」,回答的人不乏受過良好教育的專業人士,結果顯示,受測者在前12題平均只答對2題,遠不如隨機亂選的黑猩猩(如果黑猩猩會答題的話)。作者指出,這樣的結果來自於人類的誇大直覺與偏誤認知,並舉出十個扭曲認知的直覺是如何造成我們誤解身處的世界。
這篇文章並沒有要繼續深入本書的精華,但這本書絕對是連微軟共同創辦人比爾.蓋茲(Bill Gates)都掛保證的好書。這篇的靈感來自於這13道題,事實上,作者所創立的Gapminder基金會就把這一系列的問題放在網路上,提供一般的民眾接受測試,而本篇的目的在於使用到目前為止所學習的概念,實作一個Python版本的測驗程式。

程式架構

資料整理

我們取「真確」書中13道題的其中12道(排除一個圖表題),使用字典建成12個問題與答案形成的鍵值對。
fact_data = [
  {"question": "現今全世界的低所得國家裡,多少女孩會讀完小學?"
          "(A)20%(B)40%(C)60%", "answer": "C"},
  {"question": "世界上的多數人是生活在哪裡?"
          "(A)低所得國家(B)中所得國家(C)高所得國家", "answer": "B"},
  {"question": "在過去20年,全球赤貧人口占總人口的比例是⋯⋯"
          "(A)幾乎翻倍(B)大致不變(C)幾乎減半", "answer": "C"},
  {"question": "現今全球的平均壽命是多少?"
          "(A)50歲(B)60歲(C)70歲", "answer": "C"},
  {"question": "現今全球有20億個兒童,年齡介於0到15歲之間。根據聯合國的估算,到了2100年全球會有多少個兒童?"
          "(A)40億(B)30億(C)20億", "answer": "C"},
  {"question": "根據聯合國的估算,到了2100年全球會再增加40億人,而主要原因是什麼?"
          "(A)兒童人口增加(不到15歲)(B)成年人口增加(15歲到74歲)(C)老年人口增加(75歲以上)", "answer": "B"},
  {"question": "過去100年間,全球死於天災的人數是如何變化?"
          "(A)幾乎翻倍(B)大致不變(C)幾乎減半", "answer": "C"},
  {"question": "現今全球多少2歲兒童有接種疫苗?"
          "(A)20%(B)50%(C)80%", "answer": "C"},
  {"question": "全球30歲的男性平均接受過10年的學校教育,而同齡的女性平均接受過幾年的學校教育?"
          "(A)9年(B)6年(C)3年", "answer": "A"},
  {"question": "1996年,老虎、熊貓和黑犀牛都列為瀕危動物。現在這3種動物裡,哪幾種面臨更迫切的危機?"
          "(A)2種(B)1種(C)0種", "answer": "C"},
  {"question": "全球多少人口享有電力?"
          "(A)20%(B)50%(C)80%", "answer": "C"},
  {"question": "全球氣象專家認為接下來一百年裡平均氣溫會⋯⋯"
          "(A)更暖(B)一樣(C)更冷", "answer": "A"}
]

建立測驗庫類別

建立一個測驗庫類別CreateTestPool,使每一個問題與答案的配對都能生成一個獨立的物件,這個物件包含兩個屬性,即為問題(self.question)與答案(self.answer)。接著,我們把這些獨立的物件放入一個串列(test_pool)中,以方便取用。
class CreateTestPool:
  def __init__(self, question, answer):
    self.question = question
    self.answer = answer
test_pool = []
for fact in fact_data:
  fact_obj = CreateTestPool(fact["question"], fact["answer"])
  test_pool.append(fact_obj)

建立測驗產生器類別

測驗產生器類別(TestGenerator)的目的在於接受測驗庫類別所生成的物件串列,並與使用者互動完成測驗與計分。因此,這個類別包含了以下屬性:
  • 題庫的物件串列(self.question_list)
  • 使用者得分(self.score)
  • 使用者當下測驗的題號(self.question_number)
為了完成這個測驗,我們需要有以下的操作(方法):
  • 查詢是否後面還有題目(is_last_questions(self))
  • 產生下一道題目(generate_next_question(self))
  • 檢查答案(check_answer(self, user_answer, correct_answer))
  • 查詢分數(get_score(self))
class TestGenerator:
  def __init__(self, question_list):
    self.score = 0
    self.question_number = 0
    self.question_list = question_list
  def is_last_questions(self):
    return self.question_number < len(self.question_list)
  def generate_next_question(self):
    current_question = self.question_list[self.question_number]
    self.question_number += 1
    user_answer = input(f"Q{self.question_number}: {current_question.question} (A/B/C)?: ")
    self.check_answer(user_answer, current_question.answer)
  def check_answer(self, user_answer, correct_answer):
    if user_answer.lower() == correct_answer.lower():
      self.score += 1
      print("\t 答案正確!")
    else:
      print("\t 答案錯誤!")
    print(f"\t 正確答案是 {correct_answer}.")
    print(f"\t 你的分數: {self.score}/{self.question_number}.")
  
  def get_score(self):
    return self.score

測驗主程式

建立一個測驗產生器類別的物件,並在while迴圈內查詢是否已完成測驗,以決定是否繼續呈現下一個題目或是計算總分。
fact_test = TestGenerator(test_pool)
while fact_test.is_last_questions():
  fact_test.generate_next_question()
# comments
print(f"你的總分: {fact_test.score}/{fact_test.question_number}.")
if fact_test.get_score() > 4:
  print("恭喜你答得比黑猩猩好!")
elif fact_test.get_score() == 4:
  print("黑猩猩4ni?")
else:
  print("趕快買本書來看吧!")
在此專題中,我們把測驗產生器包裝成一個類別,它不需要知道測驗的內容,只需要提供正確的測驗庫,它便可呈現整個測驗的過程給使用者;此外,我們也把建立測驗庫的功能包裝成一個類別,只需要專注於測驗資料的內容與格式,便可以輕鬆更新題庫。

測驗畫面

以下是程式執行與答題的畫面:
Q1: 現今全世界的低所得國家裡,多少女孩會讀完小學?(A)20%(B)40%(C)60% (A/B/C)?: C
	 答案正確!
	 正確答案是 C.
	 你的分數: 1/1.
Q2: 世界上的多數人是生活在哪裡?(A)低所得國家(B)中所得國家(C)高所得國家 (A/B/C)?: B
	 答案正確!
	 正確答案是 B.
	 你的分數: 2/2.
Q3: 在過去20年,全球赤貧人口占總人口的比例是⋯⋯(A)幾乎翻倍(B)大致不變(C)幾乎減半 (A/B/C)?: B
	 答案錯誤!
	 正確答案是 C.
	 你的分數: 2/3.
Q4: 現今全球的平均壽命是多少?(A)50歲(B)60歲(C)70歲 (A/B/C)?: C
	 答案正確!
	 正確答案是 C.
	 你的分數: 3/4.
Q5: 現今全球有20億個兒童,年齡介於0到15歲之間。根據聯合國的估算,到了2100年全球會有多少個兒童?(A)40億(B)30億(C)20億 (A/B/C)?: C
	 答案正確!
	 正確答案是 C.
	 你的分數: 4/5.
Q6: 根據聯合國的估算,到了2100年全球會再增加40億人,而主要原因是什麼?(A)兒童人口增加(不到15歲)(B)成年人口增加(15歲到74歲)(C)老年人口增加(75歲以上) (A/B/C)?: B
	 答案正確!
	 正確答案是 B.
	 你的分數: 5/6.
Q7: 過去100年間,全球死於天災的人數是如何變化?(A)幾乎翻倍(B)大致不變(C)幾乎減半 (A/B/C)?: C
	 答案正確!
	 正確答案是 C.
	 你的分數: 6/7.
Q8: 現今全球多少2歲兒童有接種疫苗?(A)20%(B)50%(C)80% (A/B/C)?: C
	 答案正確!
	 正確答案是 C.
	 你的分數: 7/8.
Q9: 全球30歲的男性平均接受過10年的學校教育,而同齡的女性平均接受過幾年的學校教育?(A)9年(B)6年(C)3年 (A/B/C)?: B
	 答案錯誤!
	 正確答案是 A.
	 你的分數: 7/9.
Q10: 1996年,老虎、熊貓和黑犀牛都列為瀕危動物。現在這3種動物裡,哪幾種面臨更迫切的危機?(A)2種(B)1種(C)0種 (A/B/C)?: B
	 答案錯誤!
	 正確答案是 C.
	 你的分數: 7/10.
Q11: 全球多少人口享有電力?(A)20%(B)50%(C)80% (A/B/C)?: C
	 答案正確!
	 正確答案是 C.
	 你的分數: 8/11.
Q12: 全球氣象專家認為接下來一百年裡平均氣溫會⋯⋯(A)更暖(B)一樣(C)更冷 (A/B/C)?: A
	 答案正確!
	 正確答案是 A.
	 你的分數: 9/12.
你的總分: 9/12.
恭喜你答得比黑猩猩好!

程式範例

為什麼會看到廣告
Wei-Jie Weng
Wei-Jie Weng
留言0
查看全部
發表第一個留言支持創作者!