Miao's blog

《Python 編程:從入門到放棄》學習筆記

2020 年 12 月 28 日在圖靈社區購入《Python 編程:從入門到放棄實踐第二版》并開始學習。

|                      _oo0oo_
                      o8888888o
                      88" . "88
                      (| -_- |)
                      0\  =  /0
                    ___/`---'\___
                  .' \\|     |# '.
                 / \\|||  :  |||# \
                / _||||| -:- |||||- \
               |   | \\\  -  #/ |   |
               | \_|  ''\---/''  |_/ |
               \  .-\__  '-'  ___/-. /
             ___'. .'  /--.--\  `. .'___
          ."" '<  `.___\_<|>_/___.' >' "".
         | | :  `- \`.;`\ _ /`;.`/ - ` : | |
         \  \ `_.   \_ __\ /__ _/   .-` /  /
     =====`-.____`.___ \_____/___.-`___.-'=====
                       `=---='
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

               佛祖保佑         學習順利

第 2 章 變量和簡單數據類型

2.2 變量

變量的注意事項

  1. 變量只能包含字母和數字,下劃綫。
  2. 變量不能包含空格。
  3. 請勿使用 Python 的保留詞作爲變量名。
  4. 變量名應當簡潔又具有描述性。
  5. 慎用可能被人看錯的字母 l 和 O 作爲變量名。

編程語言要求嚴格,但并不關心拼寫是否正確。

變量是可以賦值的標簽,也可以說是變量指向特定的值。這將有利於搞清楚代碼是如何運行的。

練習 2-1 簡單消息

simple_message = "Python is the best."
print(simple_message)

練習 2-2 多條簡單消息

double_message = "Hello,"
print(double_message)
double_message = "How are you today?"
print(double_message)

2.3 字符串

在 Python 中,字符串就是包含在''""裏面的内容。

方法是Python可對數據執行的操作。在name.title()中,name後面的句點.讓Python對變量name執行方法title()指定的操作。

方法 title():用可以將字符串中每一個單詞的首字母都修改為大寫。

方法 upper():用可以將字符串中所有字母都修改為大寫。

方法 lower():用可以將字符串中所有字母都修改為小寫。

f 字符:可用於變量的合并。

\t\n:分別用於在一個字符串内添加制表符和換行符。

方法 rstrip():用於刪除字符串末尾的空白。

方法 lstrip():用於刪除字符串開頭的空白。

方法 strip():用於同時刪除字符串兩端的空白。

練習 2-3 個性化消息

name = "Mike"
say = f"Hello {name}, would you like to learn some Python today?"
print(say)

練習 2-4 調整名字的大小寫

name = "Miao"
name_upper = name.upper()
name_lower = name.lower()
name_title = name.title()
print(name.upper())
print(name.lower())
print(name.title())

練習 2-5 名言

famous_person = "Albert Einstein"
dictum = "A person who never made a mistake never tried anything new."
print(f'{famous_person} said, "{dictum}"')

練習 2-6 名言 2

famous_person = "Albert Einstein"
dictum = "A person who never made a mistake never tried anything new."
message = f'{famous_person} said, "{dictum}"'
print(message)

練習 2-7 剔除人名中的空白

a = "a\nbc "
b = " de\tf"
c = " g\nh\ti "
print(a)
print(b)
print(a.rstrip())
print(b.lstrip())
print(c.strip())

2.4 數

整數:在Python中,可對整數執行加(+)減(-)乘(*)除(/)乘方(**)運算。

空格不影響 Python 計算表達式的方式。

浮點數:Python將所有帶小數點的數稱為浮點數。

無論是哪種運算,只要有操作數是浮點數,Python要么得到的總是浮點數,甚至結果原本為 整體也是如此。將任意兩個數相除時,結果總是浮點數,即便這兩個數都是整數且能整除。

爲了對與使用者方便易讀,在書寫很大的數時可用下劃綫將其中的數字分組,如 100_000_000。Python 會自動忽略其中的下劃綫。

可在一行代碼中給多個變量賦值,這有助於延長程序並提高其突出性。

常量類似於變量,但其值在程序的整個生命週期內保持不變。Python 程序員會使用全大寫來指出應將某個變量視為常量。

練習 2-8 數字8

print(2 * 4)
print(2 ** 3)
print(4 + 4)
print(9 - 1)
print(16 / 2)

練習 2-9 最喜歡的數

favourite_num = (2 + 3)
print(f"My favourite number is {favourite_num}.")

2.5 注釋

用開頭的 # 來表示注釋。注釋應該清晰、簡潔。

2.6 Python 之禪

The Zen of Python, by Tim Peters

Beautiful is better than ugly. 
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

第 3 章 列表

3.1 列表是什麽

列表由一系列按特定順序排列的元素組成。在 Python 中,用方括號[]表示列表,並用逗號分隔其中的元素。

列表是有序集合,因此要訪問列表的任意元素,只需將該元素的位置(索引)告訴 Python即可。

bicycles = ['trek', 'cannondale', 'redline', 'specialized']
print(bicycles[0])

在 Python中,第一個列表元素的索引為 0,而不是 1。大多數編程語言是如此規定的,這與列表操作的底層實現相關。

通過將索引指定為-1,可讓 Python 返回最後一個列表元素。以此類推。

練習 3-1 姓名

name = ["xiao ming","xiao jun","xiao hong"]
print(name[0].title())
print(name[1].title())
print(name[2].title())

練習 3-2 問候語

name = ["xiao ming","xiao jun","xiao hong"]
print(f"Hi {name[0].title()}, how are you today?")
print(f"Hi {name[1].title()}, how are you today?")
print(f"Hi {name[2].title()}, how are you today?")

練習 3-3 自己的列表

name = ["car","motorcycle","plane"]
print(f"I want to have a {name[0].title()}.")
print(f"I hope to go to Moscow by {name[2]}.")
print(f"I would like wo own a Honda {name[1]}.")

3.2 修改、添加和刪除元素

要修改列表元素,可指定列表名和要修改的元素的索引,再指定該元素的新值。

方法 append():給列表附加(append)元素時,它將添加到列表末尾。

方法 insert():可在列表的任何位置添加新元素。為此,需要指定新元素的索引和值。

del 語句:用於刪除列表内已知位置的元素。

del 也可以刪除中間一段連續的元素,格式為:

其中,start 表示起始索引,end 表示結束索引。 del 會刪除從索引 startend 之間的元素,不包括 end 位置的元素。

方法 pop():暫時彈出(pop)列表指定位置的元素。若無指定位置,則彈出末尾元素。

方法 remove():刪除列表中已知值的元素。方法 remove() 只刪除第一個指定的值。如果要刪除的值可能在列表中出現多次,就需要使用循環來確保將每個值都刪除。

練習 3-4 嘉賓名單

name = ["Khrushchev", "Brezhnev", "Gorbachev"]
print(name)

練習 3-5 修改嘉賓名單

name = ["Khrushchev", "Brezhnev", "Gorbachev"]
unavailable_name = name[2]
print(f"{name[3].title()} is busy to attend.")
name[0] = "Yeltsin" 
print(name)

練習 3-6 添加嘉賓

name = ["Khrushchev", "Brezhnev", "Gorbachev"]
append_name_1 = "Ivasko"
append_name_2 = "Chernenko"
append_name_3 = "Andropov"
print(f"I found a bigger table so {append_name_1}, {append_name_2} and {append_name_3} can join us.")
name.insert(0, append_name_1) 
name.insert(1, append_name_2) 
name.append(append_name_3) 
print(name)
print(f"Dear Mr. {name[0]}, I am very happy to invite you to dinner.")
print(f"Dear Mr. {name[1]}, I am very happy to invite you to dinner.")
print(f"Dear Mr. {name[2]}, I am very happy to invite you to dinner.")
print(f"Dear Mr. {name[3]}, I am very happy to invite you to dinner.")
print(f"Dear Mr. {name[4]}, I am very happy to invite you to dinner.")

練習 3-7 縮減名單

name = ["Khrushchev", "Brezhnev", "Gorbachev"]
append_name_1 = "Ivasko"
append_name_2 = "Chernenko"
append_name_3 = "Andropov"
name.insert(0, append_name_1) 
name.insert(1, append_name_2) 
name.append(append_name_3) 
print(name)
print("Sorry sir, I can only invite two to dinner.")
popped_name = name.pop()
print(f"Sorry Mr. {popped_name}, I cannot invite you to dinner.")
popped_name = name.pop()
print(f"Sorry Mr. {popped_name}, I cannot invite you to dinner.")
popped_name = name.pop()
print(f"Sorry Mr. {popped_name}, I cannot invite you to dinner.")
popped_name = name.pop()
print(f"Sorry Mr. {popped_name}, I cannot invite you to dinner.")
print(name)
print(f"Dear Mr. {name[0]}, you are still invited.")
print(f"Dear Mr. {name[1]}, you are still invited.")
del name[0]
del name[0]
print(name)

3.3 組織列表

方法 sort():對列表按照字母順序永久排序。傳遞參數 reverse=True 即可反向排序。

注意sort() 不可直接輸出。

cars = ['bmw', 'audi', 'toyota', 'subaru']
cars.sort(reverse=True)
print(cars)

函數 sorted() 能夠按特定順序顯示列表元素,同時不影響它們在列表中的原始排列順序。如果要按與字母順序相反的順序顯示列表,也可向函數 sorted() 傳遞參數 reverse=True

方法 reverse():反轉列表元素的排列順序。

函數 len() :可快速獲悉列表的長度。

練習 3-8 放眼世界

place = ["Moscow", "Berlin", "Vienna", "London"]
print(place)
print(sorted(place))
print(place)
print(sorted(place))
print(sorted(place,reverse=True))
print(place)
place.reverse()
print(place)
place.reverse()
print(place)
place.sort()
print(place)
place.sort(reverse=True)
print(place)

練習 3-9 晚餐名單

name = ["Khrushchev", "Brezhnev", "Gorbachev"]
print(len(name))

練習 3-10 嘗試使用各個函數

3.4 使用列表時避免檢索錯誤

練習 3-11 有意引發錯誤

motorcycles = ['honda', 'yamaha', 'suzuki']
print(motorcycles[3])  # 錯誤

第 4 章 操作列表

4.1 遍歷整個列表

如果遍歷列表所有元素,對每個元素執行相同操作,可以使用 for 循環。如:

magicians = ['alice', 'david', 'carolina']
for magician in magicians:
    print(magician) 

如在 for 循環結束後需要繼續操作,則將相應的代碼放在 for 循環後面,且不縮進。如:

magicians = ['alice', 'david', 'carolina']
for magician in magicians:
    print(f"{magician.title()}, that was a great trick!")
    print(f"I can't wait to see your next trick, {magician.title()}.\n")

print("Thank you, everyone. That was a great magic show!")

4.2 避免縮進錯誤

練習 4-1 披薩

pizzas = ["pepperon", "veggie lover's", "curry chicken"]
for pizza in pizzas:
    print(f"I really like {pizza.title()} pizza!")

print("\nI really love pizza!")

練習從4-2 動物

animals = ["dog", "pig", "tiger"]
for animal in animals:
    print(f"A {animal} would be a good pet.")

print("\nAny of these animals would make a great pet!")

4.3 創建數值表

函數 range() 可以輕鬆生成一系列數。如生成數字 1~4

for value in range(1, 5): 
    print(value)

注意編程語言中常見的差一行爲

調用函數 range() 時,也可只指定一個參數,這樣它將從 0 開始。例如,range(6) 返回數 0~5

若要將 range() 的數轉換爲列表,可使用 list() 。如:

numbers = list(range(1, 6))
print(numbers) 

函數 range() 可在第三個參數指定步長,如輸出 1~10 的偶數:

even_numbers = list(range(2, 11, 2))
print(even_number)

函數 min(digits)max(digits)sum(digits) 分別用於找出數字列表的最大值、最小值和總和。

練習 4-3 數到 20

numbers = range(1,21)
for number in numbers:
    print(number)

練習 4-4 一百萬

numbers = range(1,1000001)
for number in numbers:
    print(number)

練習 4-5 一百萬求和

numbers = list(range(1,1000001))
print(min(numbers))
print(max(numbers))
print(sum(numbers))

練習 4-6 奇數

numbers = range(1,21,2)
for number in numbers:
    print(number)

練習 4-7 3 的倍數

numbers = range(0,31,3)
for number in numbers:
    print(number)

練習 4-8 立方

numbers = []
for value in range(1,11):
    numbers.append(value ** 3)

print(numbers)

練習 4-9 立方解析

numbers = [ value**3 for value in range(1,11)]
print(numbers)

4.4 使用列表的一部分

要創建切片,可指定要使用的第一個元素和最後一個元素的索引。與函數從 range() 一樣,Python 在到達第二個索引之前的元素後停止。如果沒有指定第一個索引,Python將自動從列表開頭開始。要讓切片終止於列表末尾,也可使用類似的語法。負數索引返回離列表末尾相應距離的元素。如:

players = ['charles', 'martina', 'michael', 'florence', 'eli']
print(players[0:3])
print(players[:4])
print(players[2:])
print(players[-3:])

如果要遍歷列表的部分元素,可在 for 循環中使用切片。

要復制列表,可創建一個包含整個列表的切片,方法是同時省略起始索引和終止索引 [:] 。這讓Python 創建一個始於第一個元素、終止於最後一個元素的切片,即整個列表的副本。如:

friend_foods = my_foods[:]
# NOT friend_foods = my_foods 
# 錯誤処將 my_foods 賦給 friend_foods,而不是將 my_foods 的副本賦給 friend_foods。

練習 4-10 切片

number = [value for value in range(1,11)]
print(f"The first three items in the list are: {number[:3]}")
print(f"Three items from the middle of the list are: {number[3:5]}")
print(f"The last three items in the list are: {number[-3:]}")

練習 4-11 你的披薩,我的披薩

pizzas = ["pepperon", "veggie lover's", "curry chicken"]
friend_pizzas = pizzas[:]
pizzas.append("i dont know pizza name 1")
friend_pizzas.append("i dont know pizza name 2")

pizza = []
for value in pizzas[:]:
    pizza.append(value)

print(pizza)

friend_pizza = []
for value in friend_pizzas[:]:
    friend_pizza.append(value)

print(friend_pizza)

練習 4-12 使用多個循環

my_foods = ['pizza', 'falafel', 'carrot cake']
friend_foods = my_foods[:]
my_foods = [my_food for my_food in my_foods[:]]
friend_foods = [friend_food for friend_food in friend_foods[:]]
print(f"My favorite foods are: {my_foods}")
print(f"My friend's favorite foods are: {friend_foods}")

4.5 元組

元組適合用來儲存一系列不可修改的元素。元組看起來很像清單,但使用圓括號而不是中括號來標識。定義元組後,就可以使用索引來訪問其元素,就像訪問列表元素一樣。如

dimensions = (200, 50)
print(dimensions[0])
print(dimensions[1])

像列表一樣,也可以為循環來遍曆元組中的所有值:

dimensions = (200, 50)
for dimension in dimensions:
    print(dimension) 

雖不可修改元組的元素,但可以給儲存元組的變量賦值。如:

dimensions = (200, 50)
print("Original dimensions:")
for dimension in dimensions:
    print(dimension)

dimensions = (400, 100)
print("\nModified dimensions:")
for dimension in dimensions:
    print(dimension)

練習 4-13 自助餐

foods = (1, 2, 3, 4, 5)
for food in foods:
    print(food)

modified_foods = (1, 2, 3, 6, 7)
for modified_food in modified_foods:
    print(modified_food)

4.6 設置代碼格式

代碼應當易於閲讀。

建議每級縮進都使用四個空格。在程序中混合使用製表符和空格可能導致極難排查的問題。如果混合使用了製表符和空格,可將文件中的所有製表符都轉換為空格。

建议每行不超过80字符。

要將程序的不同部分分開,可使用空行。

第 5 章 if 語句

5.1 一個簡單的實例

cars = ['audi', 'bmw', 'subaru', 'toyota']

for car in cars:
    if car == 'bmw':
        print(car.upper())
    else:
        print(car.title())

5.2 條件測試

要判斷兩個值是否不等,可以結合使用驚嘆號和等號 !=,其中的驚嘆號 ! 表示不,其他很多編程語言中亦是如此。

使用關鍵字 or 檢查多個條件時,只要至少有一個條件滿足就能通過整個測試。

練習 5-1 條件測試

car = 'subaru'
print("Is car == 'subaru'? I predict True.")
print(car == 'subaru')

print("\nIs car == 'audi'? I predict False.")
print(car == 'audi')

練習 5-2 更多條件測試

5.3 if 測試

最簡單的 if 語句只有一個測試和一個操作。第一行可包含任何條件測試,而在緊跟在測試後面的縮進代碼塊中,可執行任何操作。如果條件測試的結果為 True,Python 就會執行緊跟在 if 語句後面的代碼,否則 Python 將忽略這些代碼。如:

age  =  19
if age >= 18:
    print("You are old enough to vote!")

需要在條件測試通過時執行一個操作、在沒有通過時執行另一個操作時,可使用 Python 提供的 if-else 語句。 if-else 語句塊類似於簡單的if語句,但其中的 else 語句能夠指定條件測試未通過時要執行的操作。如:

age = 17
if age >= 18:
    print("You are old enough to vote!")
    print("Have you registered to vote yet?")
else: 
    print("Sorry, you are too young to vote.")
    print("Please register to vote as soon as you turn 18!")

在需要檢查超過兩個的情形的時候可使用 Python 提供的 if-elif-else 結構。 Python 只執行 if-elif-else 結構中的一個代碼塊。它依次檢查每個條件測試,直到遇到通過了的條件測試。測試通過後,Python 將執行緊跟在它後面的代碼,並跳過餘下的測試。如:

age  =  12
if age < 4:
    print("Your admission cost is $0.")
elif age < 18:
    print("Your admission cost is $25.")
else:
    print("Your admission cost is $40.")

上面實例也可簡化為:

age = 12
if age < 4:
    price = 0
elif age < 18:
    price = 25
else:
    price = 40

print(f"Your admission cost is ${price}.") 

可根據需要使用任意數量的 elif 代碼塊。Python 並不要求 if-elif 結構後面必須有 else 代碼塊,按需使用。

if-elif-else 结构功能强大,但仅适合用于只有一个条件满足的情况:遇到通过了的测试后,Python 就跳过余下的测试。儅必须检查关心的所有条件時,应使用一系列不包含 elifelse 代码块的简单 if 语句。

練習 5-3 外星人顔色

colour = "red"
if colour == "green":
    print("You get 5 score!")

colour = "red"
if colour == "red":
    print("You get 5 score!")

練習 5-4 外星人顔色 2

colour = "red"
if colour == "green":
    print("You get 5 score!")
if colour != "green":
    print("You get 10 score!")

## 第二個版本
colour = "red"
if colour == "green":
    print("You get 5 score!")
else:
    print("You get 10 score!")

練習 5-5 外星人顔色 3

colour = "red"
if colour == "green":
    print("You get 5 score!")
elif colour == "yellow":
    print("You get 10 score!")
else:
    print("You get 15 score!")

練習 5-6 人生的不同階段

age = "48"
if age < "2":
    print("嬰兒")
elif "2" <= age < "4":
    print("小屁孩")
elif "4" <= age < "13":
    print("兒童")
elif "13" <= age < "20":
    print("青少年")
elif "20" <= age < "65":
    print("成年人")
else:
    print("老年人")

練習 5-7 喜歡的水果

favorite_fruits = ["apple", "balana", "orange"]
for favorite_fruit in favorite_fruits:
    if favorite_fruit == "balana":
        print("You really like bananas!")

5.4 使用 if 語句處理列表

練習 5-8 以特殊方式跟管理員打招呼

usernames = ["a", "admin", "b", "c", "d"]
for username in usernames:
    if username == "admin":
        print("Hello admin, would you like to see a status report? ")
    else:
        print("Hello Jaden, thank you for logging in again.")

練習 5-9 處理沒有用戶的情形

usernames = []
if usernames:
    for username in usernames:
        print(username)
else:
    print("Are you sure you want a plain username?")

```python

#### 練習 5-10 檢查用戶名

```python
current_users = ["a", "admin", "b", "c", "d"]
new_users = ["c", "d", "e", "f", "g", ]

```python

#### 練習 5-11 序數

```python
num = 1
list = list(range(1,10))
for value in list:
    if value < 10:
        print(f"{num}th")
        num = num + 1
    elif value == 9:
        print("9th")
    else:
        print("I do not know how to do this.")

5.5 設置 if 語句的格式

一定要有良好的設置習慣。

練習 5-12 設置 if 語句的格式

練習 5-13 自己的想法

第 6 章 字典

6.1 一個簡單的字典

alien_0 = {'color': 'green', 'points': 5}

print(alien_0['color'])
print(alien_0['points'])

6.2 使用字典

在Python中,字典是一系列鍵值對。每個鍵都與一個值相關聯,可使用鍵來訪問相關聯的值。與鍵相關聯的值可以是數、字符串、列表乃至字典。事實上,可將任何Python對像用作字典中的值。

在Python中,字典用放在花括號 {} 中的一系列鍵值對錶示。

鍵值對是兩個相關聯的值。指定鍵時,Python將返回與之相關聯的值。鍵和值之間用冒號分隔,而鍵值對之間用逗號分隔。在字典中,想存儲多少個鍵值對都可以。

alien_0 = {'color': 'green', 'points': 5}

在該字典中,字符串 color 是一個鍵,與之相關聯的值為 green

要獲取與鍵相關聯的值,可依次指定字典名和放在方括號內的鍵。如:

alien_0 = {'color': 'green', 'points': 5}
new_points = alien_0['points']
print(f"You just earned {new_points} points!") 

字典是一種動態結構,可隨時在其中添加鍵值對。要添加鍵值對,可依次指定字典名、用方括號括起的鍵和相關聯的值。如:

alien_0 = {'color': 'green', 'points': 5}
print(alien_0)

alien_0['x_position'] = 0
alien_0['y_position'] = 25
print(alien_0)

在空字典中添加鍵值對有時候可提供便利,而有時候必須這樣做。為此,可先使用一對空花括號定義一個字典,再分行添加各個鍵值對。如:

alien_0  =  {}

alien_0['color'] = 'green'
alien_0['points'] = 5

print(alien_0) 

要修改字典中的值,可依次指定字典名、用方括號括起的鍵,以及與該鍵相關聯的新值。如:

alien_0 = {'color': 'green'}
print(f"The alien is {alien_0['color']}.")

alien_0['color'] = 'yellow'
print(f"The alien is now {alien_0['color']}.") 

對於字典中不再需要的信息,可使用del語句將相應的鍵值對徹底刪除。使用del語句時,必須指定字典名和要刪除的鍵。如:

alien_0 = {'color': 'green', 'points': 5}
print(alien_0)

del alien_0['points']
print(alien_0)

方法 get() 的第一個參數用於指定鍵,是必不可少的;第二個參數為指定的鍵不存在時要返回的值,是可選的。調用 get() 時,如果沒有指定第二個參數且指定的鍵不存在,Python 將返回值 None。這個特殊值表示沒有相應的值。

練習 6-1 人

info = {"first_name" : "m", "last_name" : "c",  "age": "99", "city" : "Beijing"}
print(f"My first name is {info['first_name'].title()}, ")
print(f"My last name is {info['last_name'].title()}, ")
print(f"I am {info['age']} y.o.")
print(f"and I am living in {info['city']}.")

練習 6-2 喜歡的數

num = {"a" : "1", "b" : "2", "c" : "3", "d" : "4", "e" : "5", }
print(f"A's favourite number is {num['a']},")
print(f"B's favourite number is {num['b']},")
print(f"C's favourite number is {num['c']},")
print(f"D's favourite number is {num['d']},")
print(f"E's favourite number is {num['e']}.")

練習 6-3 詞匯表

6.3 遍歷字典

要編寫遍歷字典的 for 循環,可聲明兩個變量,用於存儲鍵值對中的鍵和值。這兩個變量可以使用任意名稱。對於語句的第二部分包含字典名和方法 items(),它返回一個鍵值對列表。接下來,對於循環依次將每個鍵值對賦值給指定的兩個變量。如:

user_0 = {
    'username': 'efermi',
    'first': 'enrico',
    'last': 'fermi',
    }
for key, value in user_0.items():
    print(f"\nKey: {key}")
    print(f"Value: {value}")

在不需要使用字典中的值時可以使用方法 keys()

要以特定順序返回元素,一種辦法是在for循環中對返回的鍵進行排序。為此,可使用函數 sorted() 來獲得按特定順序排列的鍵列表的副本。

如果主要對字典包含的值感興趣,可使用方法 values() 來返回一個值列表,不包含任何鍵。為剔除重複項,可使用集合 set。集合中的每個元素都必須是獨一無二的。

favorite_languages = {
    --snip--
    }

print("The following languages have been mentioned:")
for language in set(favorite_languages.values()):
    print(language.title()

練習 6-4 詞匯表 2

練習 6-5 河流

rivers = {
    'nile': 'egypt',
    'amazon': 'brazil',
    'yangtze': 'china',
}
for river, country in rivers.items():
    print(f"The {river.title()} runs through {country.title()}.")

練習 6-6 調查

favorite_languages = {
    'a': 'python',
    'b': 'go',
    'c': 'julia',
}
people = ['a', 'b', 'c', 'd', 'e']

for person in favorite_languages.keys():
    print(f"Hi {person.title()}, thank you for participating in our survey.")

print()

for person in people:
    if person not in favorite_languages.keys():
        print(f"Hey {person.title()}, would you like to participate in our survey?")

6.4 嵌套

有時候,需要將一系列字典存儲在列表中,或將列表作為值存儲在字典中,這稱為嵌套。

練習 6-7 人們

info1 = {"first_name" : "m", "last_name" : "c",  "age": "99", "city" : "beijing"}
info2 = {"first_name" : "z", "last_name" : "y",  "age": "98", "city" : "xinyang"}
info3 = {"first_name" : "k", "last_name" : "m",  "age": "97", "city" : "puyang"}
persons = [info1, info2, info3]

people = []
for person in persons:
    people.append(person)

for person in people:
    print(f"{person['first_name'].title()}{person['last_name'].title()} is {person['age']} y.o and lived in {person['city'].title()}.")

練習 6-8 寵物

練習 6-9 喜歡的地方

favorite_places = {
    "mc" : ["moscow", "warsaw", "berlin"],
    "km" : ["beijing", "new york"],
    "yd" : ["singapore"],
}

for person, favorite_place in favorite_places.items():
    print(f"{person.title()}'s favorite places are: ")
    for value in favorite_place:
        print(f"\t{value.title()}")

練習 6-10 喜歡的數 2

練習 6-11 城市

練習 6-12 擴展

第 7 章 用戶輸入和 while 循環

7.1 函數 input() 的工作原理

函數 input() 讓程序暫停運行,等待用戶輸入一些文本。獲取用戶輸入後,Python 將其賦給一個變量,以方便你使用。

Pwd@Linux 每當使用函數 input() 時,都應指定清晰易懂的提示,準確地指出希望用戶提供什麼樣的信息——指出用戶應該輸入何種信息的任何提示都行。

通過在提示末尾包含一個空格,可將提示與用戶輸入分開,讓用戶清楚地知道其輸入始於何處。

使用函數 input() 時,Python 將用戶輸入解讀為字符串。如:

>>> age = input("How old are you? ")
How old are you? 21
>>> age
'21'

為解決這個問題,可使用函數 int(),它讓Python將輸入視為數值。函數 int() 將數的字符串表示轉換為數值表示,如:

>>> age = input("How old are you? ")
How old are you? 21
>>> age = int(age)
>>> age >= 18
True

理數值信息時,求模運算符 % 將兩個數相除並返回餘數。可使用求模運算符來判斷一個數是奇數還是偶數,如:

number = input("Enter a number, and I'll tell you if it's even or odd: ")
number = int(number)
if number % 2 == 0:
    print(f"\nThe number {number} is even.")
else:
    print(f"\nThe number {number} is odd.")

練習 7-1 汽車租賃

car = input("Which car would you like to rent: ")
print(f"Let me see if I can find you a {car.title()}.")

練習 7-2 餐館訂位

num = input("How many people will be dining?\n")
num = int(num)
if num > 8:
    print("There are no more spaces here.")
else:
    print("There is enough free space here.")

練習 7-3 10 的整倍數

num = input("You can enter any number here!\n")
num = int(num)

if num % 10 == 0:
    print("This number is an integer multiple of 10.")
else:
    print("This number is not an integer multiple of 10.")

7.2 while 循環簡介

for 循環用於針對集合中的每個元素都執行一個代碼塊,而 while 循環則不斷運行,直到指定的條件不滿足為止。如:

prompt = "\nTell me something, and I will repeat it back to you:"
prompt += "\nEnter 'quit' to end the program. "

active = True
while active:
    message = input(prompt)

    if message == 'quit':
        active = False
    else:
        print(message)

要立即退出 while 循環,不再運行循環中餘下的代碼,也不管條件測試的結果如何,可使用 break 語句。 break 語句用於控製程序流程,可用來控制哪些代碼行將執行、哪些代碼行不執行,從而讓程序按你的要求執行你要執行的代碼。如:

prompt = "\nPlease enter the name of a city you have visited:"
prompt += "\n(Enter 'quit' when you are finished.) "

while True:
    city = input(prompt)

    if city == 'quit':
        break
    else:
        print(f"I'd love to go to {city.title()}!")

要返回循環開頭,並根據條件測試結果決定是否繼續執行循環,可使用 continue 語句,它不像 break 語句那樣不再執行餘下的代碼並退出整個循環。

current_number = 0
while current_number < 10:
    current_number += 1
    if current_number % 2 == 0:
        continue

    print(current_number)

每個 while 循環都必須有停止運行的途徑,這樣才不會沒完沒了地執行下去。

current_number = current_number + 1 可簡寫為 current_number += 1

練習 7-4 披薩配料

ingredients = []
prompt = "What kind of pizza ingredients do you need?\n"

active = True

while True:
    values = input(prompt)

    if values != "quit":
        print(f"We will add {values} to the pizza.\n")
        ingredients.append(values)
    else:
        break

print(f"\nOK. We will add:")
for value in ingredients:
    print(f"\t{value}")
print("to the pizza. Thanks.")

練習 7-5 電影票

練習 7-6 三種出路

# 1 條件測試

ingredients = []
prompt = "What kind of pizza ingredients do you need?\n"

active = True

message = ""

while message != "quit":
    value = input(prompt)
    message = value
    ingredients.append(value)

print(f"\nOK. We will add:")
for value in ingredients:
    print(f"\t{value}")
print("to the pizza. Thanks.")

練習 7-7 無限循環

7.3 使用 while 循環處理列表和字典

在列表之間移動元素,如實例:

#  首先,创建一个待验证用户列表users.py
#   和一个用于存储已验证用户的空列表。
unconfirmed_users = ['alice', 'brian', 'candace']
confirmed_users = []

# 验证每个用户,直到没有未验证用户为止。
#   将每个经过验证的用户都移到已验证用户列表中。
while unconfirmed_users:
    current_user = unconfirmed_users.pop()

    print(f"Verifying user: {current_user.title()}")
    confirmed_users.append(current_user) 

# 显示所有已验证的用户。
print("\nThe following users have been confirmed:")
for confirmed_user in confirmed_users:
    print(confirmed_user.title())

在列表中刪除為特定值的所有元素,如實例:

pets = ['dog', 'cat', 'dog', 'goldfish', 'cat', 'rabbit', 'cat']
print(pets)

while 'cat' in pets:
    pets.remove('cat')

print(pets)

使用用戶輸入來填充字典,如實例:

responses  =  {}

# 设置一个标志,指出调查是否继续。
polling_active = True 

while polling_active:
    # 提示输入被调查者的名字和回答。
    name = input("\nWhat is your name? ")
    response = input("Which mountain would you like to climb someday? ")

    # 将回答存储在字典中。
    responses[name] = response

    # 看看是否还有人要参与调查。
    repeat = input("Would you like to let another person respond? (yes/ no) ")
    if repeat == 'no':
        polling_active = False 

    # 调查结束,显示结果。
    print("\n--- Poll Results ---")
    for name, response in responses.items():
        print(f"{name} would like to climb {response}.") 

練習 7-8 熟食店

sandwich_orders = ["a", "b", "c"]
finished_sandwiches = []

while sandwich_orders != []:
    sandwich_order = sandwich_orders.pop()
    finished_sandwiches.append(sandwich_order)
    print(f"I made your {sandwich_order}")

print()

for finished_sandwiche in finished_sandwiches:
    print(finished_sandwiche)

練習 7-9 五香烟熏牛肉賣完了

sandwich_orders = ["a", "pastrami", "b", "pastrami", "c", "pastrami"]

print("The pastrami has been sold out.")

while "pastrami" in sandwich_orders:
    sandwich_orders.remove("pastrami")

print(sandwich_orders)

練習 7-10 夢想的度假勝地

responses = {}

active = True

while active:
    name = input("\nWhat is your name? ")
    response = input("If you could visit one place in the world, where would you go? ")

    responses[name] = response

    repeat = input("Would you like to let another person respond? (yes/ no) ")
    if repeat.lower() == "no":
        active = False

for name, response in responses.items():
    print(f"\n{name.title()} would like to go to {response.title()}.")

第 8 章 函數

8.1 定義函數

使用關鍵字 def 來定義一個函數。如:

def greet_user():
    print("Hello!")

greet_user()

可在函數定義 def greet_user() 的括號內添加username。通過在這裡添加 username,可讓函數接受你給 username 指定的任何值。如:

def greet_user(username):
    print(f"Hello, {username.title()}!")

greet_user('jesse')

在函數 greet_user() 的定義中,變量 username 是一個形參(parameter),即函數完成工作所需的信息。在代碼 greet_user('jesse') 中,值 'jesse' 是一個實參(argument),即調用函數時傳遞給函數的信息。調用函數時,將要讓函數使用的信息放在圓括號內。在 greet_user('jesse') 中,將實參 'jesse' 傳遞給了函數 greet_user(),這個值被賦給了形參 username

練習 8-1 消息

def display_message():
    print("蛤?")

display_message()

練習 8-2 喜歡的圖書

def favorite_book(name):
    print(f"One of my favorite books is {name}.")

favorite_book("Alice in Wonderland")

8.2 傳遞實參

位置實參,如:

def describe_pet(animal_type, pet_name):
    print(f"\nI have a {animal_type}.")
    print(f"My {animal_type}'s name is {pet_name.title()}.")

describe_pet('hamster', 'harry')
describe_pet('dog', 'willie')

在函數中,可根據需要使用任意數量的位置實參,Python 將按順序將函數調用中的實參關聯到函數定義中相應的形參。

使用位置實參來調用函數時,如果實參的順序不正確,結果可能出乎意料;關鍵字實參是傳遞給函數的名稱值對。因為直接在實參中將名稱和值關聯起來,所以向函數傳遞實參時不會混淆。關鍵字實參讓你無須考慮函數調用中的實參順序,還清楚地指出了函數調用中各個值的用途。如:

def describe_pet(animal_type, pet_name):
    print(f"\nI have a {animal_type}.")
    print(f"My {animal_type}'s name is {pet_name.title()}.")

describe_pet(animal_type='hamster', pet_name='harry')

關鍵字實參的順序無關緊要,因為 Python 知道各個值該賦給哪個形參。

編寫函數時,可給每個形參指定默認值。在調用函數中給形參提供了實參時,Python 將使用指定的實參值;否則,將使用形參的默認值。如:

def describe_pet(pet_name, animal_type='dog'):
    print(f"\nI have a {animal_type}.")
    print(f"My {animal_type}'s name is {pet_name.title()}.")

describe_pet(pet_name='willie')
# or describe_pet('willie')

由於顯式地給 animal_type 提供了實參,Python 將忽略這個形參的默認值。

由於可混合使用位置實參、關鍵字實參和默認值,通常有多種等效的函數調用方式。

練習 8-3 T 賉

def make_shirt(size, figure):
    print(...)

make_shirt(xxx, xxxx)

練習 8-4 大號 T 賉

def make_shirt(size='bbbbbbig', figure):
    print(...)

make_shirt(xxxx)

練習 8-5 城市

def describe_city(city, country='China'):
    print(f"{city.title()} is in {country.title()}.")

describe_city('hubei')
describe_city('st. petersburg', 'russia')
describe_city('london', 'united kingdom')

8.3 返回值

函數返回的值稱為返回值。在函數中,可使用 return 語句將值返回到調用函數的代碼行。如:

def get_formatted_name(first_name, last_name):
    full_name = f"{first_name} {last_name}"
    return full_name.title()

musician = get_formatted_name('jimi', 'hendrix')
print(musician)

有時候,需要讓實參變成可選的,這樣使用函數的人就能只在必要時提供額外的信息。可使用默認值來讓實參變成可選的。如:

def get_formatted_name(first_name, last_name, middle_name=''):
    ## 如果沒有這個判斷,缺失的 middle_name 將由空格代替。
    if middle_name:
        full_name = f"{first_name} {middle_name} {last_name}"
    else:
        full_name = f"{first_name} {last_name}"
    return full_name.title()

musician = get_formatted_name('jimi', 'hendrix')
print(musician)

musician = get_formatted_name('john', 'hooker', 'lee')
print(musician)

結合使用函數和 while 循環,如:

def get_formatted_name(first_name, last_name):
    full_name = f"{first_name} {last_name}"
    return full_name.title()

while True:
    print("\nPlease tell me your name:")
    print("(enter 'q' at any time to quit)")

    f_name = input("First name: ")
    if f_name == 'q':
        break

    l_name = input("Last name: ")
    if l_name == 'q':
        break

    formatted_name = get_formatted_name(f_name, l_name)
    print(f"\nHello, {formatted_name}!")

練習 8-6 城市名

def city_country(city, country):
    output = f'"{city}, {country}"'
    return output.title()

answer = city_country('santiago', 'chile')
print(answer)

練習 8-7 專輯

def make_album(name, album_name, num='None'):
    if num:
        output = {"Name": name.title(), "Album name": album_name.title(), "Number": num}
    else:
        output = output = {"Name": name.title(), "Album name": album_name.title()}
    return output

output = make_album('abc', 'def', '100')
print(output)
output = make_album('jkl', 'mno')
print(output)

練習 8-8 用戶的專輯

def make_album(name, album_name, num='None'):
    if num:
        output = {"Name": name.title(), "Album name": album_name.title(), "Number": num}
    else:
        output = output = {"Name": name.title(), "Album name": album_name.title()}
    return output

while True:
    print("\nPlease tell me your name:")
    print("(enter 'q' at any time to quit)")

    name = input("\nName? ")
    if name == "q":
        break
    album_name = input("\nAlbum name? ")
    if name == "q":
        break
    break
output = make_album(name, album_name)
print(f"\n{output}")

8.4 傳遞列表

如:

def greet_users(names):
    for name in names:
        msg = f"Hello, {name.title()}!"
        print(msg)

usernames = ['hannah', 'ty', 'margot']
greet_users(usernames)

將列表傳遞給函數後,函數就可對其進行修改。在函數中對這個列表所做的任何修改都是永久性的。如:

unprinted_designs = ['phone case', 'robot pendant', 'dodecahedron']
completed_models = []

while unprinted_designs:
    current_design = unprinted_designs.pop()
    print(f"Printing model: {current_design}")
    completed_models.append(current_design)

print("\nThe following models have been printed:")
for completed_model in completed_models:
    print(completed_model)

# 或

def print_models(unprinted_designs, completed_models):
    while unprinted_designs:
        current_design = unprinted_designs.pop()
        print(f"Printing model: {current_design}")
        completed_models.append(current_design)

def show_completed_models(completed_models):
    print("\nThe following models have been printed:")
    for completed_model in completed_models:
        print(completed_model)

unprinted_designs = ['phone case', 'robot pendant', 'dodecahedron']
completed_models = []

print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)

相比於沒有使用函數的版本,這個程序更容易擴展和維護。如果發現需要對打印代碼進行修改,只需修改這些代碼一次,就能影響所有調用該函數的地方。與必須分別修改程序的多個地方相比,這種修改的效率更高。

有時候,需要禁止函數修改列表。可使用切片向函數傳遞列表的副本而非原件。這樣,函數所做的任何修改都只影響副本,而原件絲毫不受影響。

練習 8-9 消息

def show_messages(messages):
    for message in messages:
        print(message)

messages = ["a", "b", "c", "d"]
show_messages(messages)

練習 8-10 發送消息

def send_messages(values):
    sent_messages = []
    while values != []:
        sent_message = values.pop()
        print(sent_message)
        sent_messages.append(sent_message)

    print(values)
    print(sent_messages)

values = ["a", "b", "c", "d"]
send_messages(values)

練習 8-11 消息掃描

def send_messages(values):
    sent_messages = []
    archives = values[:]
    while values != []:
        sent_message = values.pop()
        print(sent_message)
        sent_messages.append(sent_message)

    print(values)
    print(archives)
    print(sent_messages)

values = ["a", "b", "c", "d"]
send_messages(values) 

8.5 傳遞任意數量的實參

形參名中的星號 * 讓 Python 創建一個空元組,並將收到的所有值都封裝到這個元組中。如:

def make_pizza(size, *toppings):
    print(f"\nMaking a {size}-inch pizza with the following toppings:")
    for topping in toppings:
        print(f"- {topping}")

make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

形參中的兩個星號 ** 讓 Python 創建一個空字典,並將收到的所有名稱值對都放到這個字典中。如:

def build_profile(first, last, **user_info):
    user_info['first_name'] = first
    user_info['last_name'] = last
    return user_info

user_profile = build_profile('albert', 'einstein',
                            location='princeton',
                            field='physics')
print(user_profile)

練習 8-12 三明治

def ingredients(*ingredients):
    for ingredient in ingredients:
        print(f"I need {ingredient}")

ingredients("a", "b", "c")

練習 8-13 用戶簡介

練習 8-14 汽車

def make_car(manufacture, model, **info):
    info['manufacture'] = manufacture
    info['model'] = model
    return info

car = make_car('subaru', 'outback', color='blue', tow_package=True)
print(car)

8.6 將函數存儲在模塊中

使用函數的優點之一是可將代碼塊與主程序分離。可以將函數存儲在稱為模塊的獨立文件中,再將模塊導入到主程序中。 import 語句允許在當前運行的程序文件中使用模塊中的代碼。

要讓函數是可導入的,得先創建模塊。模塊是擴展名為 .py 的文件,包含要導入到程序中的代碼。

可以導入整個模塊,也可以導入模塊中的部分函數,如:

from module_name import function_name

如果使用這種 import 語句導入了名為 module_name.py 的整個模塊,就可使用下面的語法來使用其中任何一個函數:

module_name.function_name()

可使用 as 為函數指定別名,如:

from module_name import function_name as fn复制

也可使用 as 為模塊指定別名,如:

使用星號 * 運算符可讓 Python 導入模塊中的所有函數,如:

from module_name import *

8.7 函數編寫指南

每個函數都應包含簡要地闡述其功能的註釋。該註釋應緊跟在函數定義後面,並採用文檔字符串格式。文檔良好的函數讓其他程序員只需閱讀文檔字符串中的描述就能夠使用它。他們完全可以相信代碼如描述的那樣運行,並且只要知道函數的名稱、需要的實參以及返回值的類型,就能在自己的程序中使用它。

練習 8-15 打印模型

練習 8-16 導入

練習 8-17 函數編寫指南

第 9 章 類

9.1 創建和使用類

類(class),如:

class Dog:

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def sit(self):
        print(f"{self.name} is now sitting.")

    def roll_over(self):
        print(f"{self.name} rolled over!")

my_dog = Dog('Willie', 6)
your_dog = Dog('Lucy', 3)

print(f"My dog's name is {my_dog.name}.")
print(f"My dog is {my_dog.age} years old.")
my_dog.sit()

print(f"\nYour dog's name is {your_dog.name}.")
print(f"Your dog is {your_dog.age} years old.")
your_dog.sit()

根據約定,在 Python 中,首字母大寫的名稱指的是類。這個類定義中沒有圓括號,因為要從空白創建這個類。

方法 __init__() :類中的函數稱為方法。__init__() 是一個特殊方法,每當創建新實例時,Python 都會自動運行它。在這個方法的名稱中,開頭和末尾各有兩個下劃線,這是一種約定,旨在避免 Python 默認方法與普通方法發生名稱衝突。務必確保 __init__() 的兩邊都有兩個下劃線。

可將類視為有關如何創建實例的說明。通常可認為首字母大寫的名稱指的是類,而小寫的名稱指的是根據類創建的實例。要訪問實例的屬性,可使用句點表示法。根據類創建實例後,就能使用句點表示法來調用 類中定義的任何方法了。要調用方法,可指定實例的名稱和要調用的方法,並用句點分隔。

可按需求根據類創建任意數量的實例。

練習 9-1 餐館

class Restaurant:
    def __init__(restaurant, restaurant_name, cuisine_type):
        restaurant.restaurant_name = restaurant_name.title()
        restaurant.cuisine_type = cuisine_type

    def describe_restaurant(restaurant):
        print(f"The restaurant's name is {restaurant.restaurant_name} and the type of cuisine is {restaurant.cuisine_type}.")

    def open_restaurant(restaurant):
        print("The restaurant is open.")

restaurant = Restauran t("abcde", "classic")
restaurant.describe_restaurant()
restaurant.open_restaurant()

練習 9-2 三家餐館

練習 9-3 用戶

class User:
    def __init__(self, first_name, last_name):
        self.first_name = first_name.title()
        self.last_name = last_name.title()

    def describe_user(self):
        print(f"Your first name is {self.first_name} and your last name is {self.last_name}.")

    def greet_user(self):
        print(f"Hello {self.first_name} {self.last_name}!")

self = User("Wukong", "Sun")
self.describe_user()
self.greet_user()

9.2 使用類和實例

能以三種方式修改屬性的值:直接通過實例進行修改,通過方法進行設置,以及通過方法進行遞增(增加特定的值)。

直接修改屬性的值,如:

class Car:
    --snip--

my_new_car = Car('audi', 'a4', 2019)
print(my_new_car.get_descriptive_name())

my_new_car.odometer_reading = 23
my_new_car.read_odometer()

通過方法修改屬性的值,如:

class Car:
    --snip--

    def update_odometer(self, mileage):
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

通過方法對屬性的值進行遞增,如:

class Car:
    --snip--

    def update_odometer(self, mileage):
        --snip--

    def increment_odometer(self, miles):
        self.odometer_reading += miles

my_used_car = Car('subaru', 'outback', 2015)
print(my_used_car.get_descriptive_name())

my_used_car.update_odometer(23_500)
my_used_car.read_odometer()

my_used_car.increment_odometer(100)
my_used_car.read_odometer()

練習 9-4 就餐人數

class Restaurant:
    def __init__(self, restaurant_name, cuisine_type):
        self.restaurant_name = restaurant_name.title()
        self.cuisine_type = cuisine_type
        self.number_served = 0

    def describe_restaurant(self):
        print(f"The restaurant's name is {self.restaurant_name} and the type of cuisine is {self.cuisine_type}.")

    def open_restaurant(self):
        print("The restaurant is open.")

    def read_number_served(self):
        print(f"{self.number_served} people have eaten in the restaurant.")

    def set_number_served(self, number):
        self.number_served = number

    def incream_number_served(self, number):
        self.number_served += number


restaurant = Restaurant("abcde", "classic")
restaurant.describe_restaurant()
restaurant.read_number_served()

restaurant.set_number_served(100)
restaurant.read_number_served()

restaurant.incream_number_served(500)
restaurant.read_number_served()

練習 9-5 嘗試登陸次數

class User:
    def __init__(self, first_name, last_name):
        self.first_name = first_name.title()
        self.last_name = last_name.title()
        self.login_attempts = 0

    def describe_user(self):
        print(f"Your first name is {self.first_name} and your last name is {self.last_name}.")

    def greet_user(self):
        print(f"Hello {self.first_name} {self.last_name}!")

    def read_login_attempts(self):
        print(f"The login attempt number is {self.login_attempts}.")

    def increment_login_attempts(self):
        self.login_attempts = self.login_attempts + 1

    def reset_login_attempts(self):
        self.login_attempts = 0

self = User("Wukong", "Sun")
self.describe_user()
self.greet_user()
self.read_login_attempts()

self.increment_login_attempts()
self.read_login_attempts()

self.reset_login_attempts()
self.read_login_attempts()

9.3 繼承

如果要編寫的類是另一個現成類的特殊版本,可使用繼承。一個類繼承另一個類時,將自動獲得另一個類的所有屬性和方法。原有的類稱為父類,而新類稱為子類。子類繼承了父類的所有屬性和方法,同時還可以定義自己的屬性和方法。

子類的方法 __init__():在既有類的基礎上編寫新類時,通常要調用父類的方法 __init__()。這將初始化在父類 __init__() 方法中定義的所有屬性,從而讓子類包含這些屬性。如:

class Car:

    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0

    def get_descriptive_name(self):
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()

    def read_odometer(self):
        print(f"This car has {self.odometer_reading} miles on it.")

    def update_odometer(self, mileage):
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, miles):
        self.odometer_reading += miles

class ElectricCar(Car):

    def __init__(self, make, model, year):
        super().__init__(make, model, year)

my_tesla = ElectricCar('tesla', 'model s', 2019)
print(my_tesla.get_descriptive_name())

給子類定義屬性和方法,讓一個類繼承另一個類後,就可以添加區分子類和父類所需的新屬性和新方法了。如:

class Car:
    --snip--

class ElectricCar(Car):

    def __init__(self, make, model, year):
        super().__init__(make, model, year)
        self.battery_size = 75

    def describe_battery(self):
        print(f"This car has a {self.battery_size}-kWh battery.")

my_tesla = ElectricCar('tesla', 'model s', 2019)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery()

對於父類的方法,只要它不符合子類模擬的實物的行為,都可以進行重寫。為此,可在子類中定義一個與要重寫的父類方法同名的方法。這樣,Python 將不會考慮這個父類方法,而只關注在子類中定義的相應方法。

斷給類添加細節時,我們可能發現其中包含很多專門針對類屬性和方法。在這種情況下,可將這些屬性和方法提取出來,放到一個新類中,並將一個新類實例作為原類的屬性。如:

class Car:
    --snip--

class Battery:
    def __init__(self, battery_size=75):
        self.battery_size = battery_size

    def describe_battery(self):
        print(f"This car has a {self.battery_size}-kWh battery.")

class ElectricCar(Car):
    def __init__(self, make, model, year):
        super().__init__(make, model, year)
        self.battery = Battery()

my_tesla = ElectricCar('tesla', 'model s', 2019)

print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()

解決問題時,從較高的邏輯層面(而不是語法層面)考慮;考慮的不是 Python,而是如何使用代碼來表示實物。達到這種境界後,對現實世界的建模方法沒有對錯之分。有些方法的效率更高,但要找出效率最高的表示法,需要經過一定的實踐。

練習 9-6 冰激凌小店

class Restaurant:
    def __init__(self, restaurant_name, cuisine_type):
        self.restaurant_name = restaurant_name.title()
        self.cuisine_type = cuisine_type

    def describe_restaurant(self):
        print(f"The restaurant's name is {self.restaurant_name} and the type of cuisine is {self.cuisine_type}.")

    def open_restaurant(self):
        print("The restaurant is open.")

class IceCreamStand(Restaurant):
    def __init__(self, restaurant_name, cuisine_type):
        super().__init__(restaurant_name, cuisine_type)
        self.flavors = []

    def describe_icecreamstand(self):
        print("There are many flavors of ice cream in the icecreamstand, such as:")
        for flavor in self.flavors:
            print(f"\t{flavor.title()}")

my_icecreamstand = IceCreamStand("A", "classic")
my_icecreamstand.flavors = ["a", "b", "c"]
my_icecreamstand.describe_icecreamstand()

練習 9-7 管理員

class User:
    def __init__(self, first_name, last_name):
        self.first_name = first_name.title()
        self.last_name = last_name.title()
        self.login_attempts = 0

    def describe_user(self):
        print(f"Your first name is {self.first_name} and your last name is {self.last_name}.")

class Admin(User):
    def __init__(self, first_name, last_name):
        super().__init__(first_name, last_name)
        self.privileges = ["can add post", "can delete post", "can ban user"]

    def show_privileges(self):
        print(f"{self.first_name} {self.last_name} has the following privileges:")
        for privilege in self.privileges:
            print(f"\t- {privilege}")

admin = Admin("Wukong", "Sun")
admin.show_privileges()

練習 9-8 權限

class User:
    def __init__(self, first_name, last_name):
        self.first_name = first_name.title()
        self.last_name = last_name.title()
        self.login_attempts = 0

    def describe_user(self):
        print(f"Your first name is {self.first_name} and your last name is {self.last_name}.")

class Privileges():
    def __init__(self):
        self.privileges = ["can add post", "can delete post", "can ban user"]

    def show_privileges(self):
        print("Admin has the following privileges:")
        for privilege in self.privileges:
            print(f"\t- {privilege}")

class Admin(User):
    def __init__(self, first_name, last_name):
        super().__init__(first_name, last_name)
        self.own_privileges = Privileges()

admin = Admin("Wukong", "Sun")
admin.own_privileges.show_privileges()

練習 9-9 電瓶升級

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0

    def get_descriptive_name(self):
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()

    def read_odometer(self):
        print(f"This car has {self.odometer_reading} miles on it.")

    def update_odometer(self, mileage):
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, miles):
        self.odometer_reading += miles

class Battery:
    def __init__(self, battery_size=75):
        self.battery_size = battery_size

    def describe_battery(self):
        print(f"This car has a {self.battery_size}-kWh battery.")

    def get_range(self):
        if self.battery_size == 75:
            range = 260
        elif self.battery_size == 100:
            range = 315
        print(f"This car can go about {range} miles on a full charge.")

    def upgrade_battery(self):
        if self.battery_size != 100:
            self.battery_size = 100
            print("Battery upgrade successfully!")

class ElectricCar(Car):
    def __init__(self, make, model, year):
        super().__init__(make, model, year)
        self.battery = Battery()

my_tesla = ElectricCar('tesla', 'model s', 2019)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range()

my_tesla.battery.upgrade_battery()
my_tesla.battery.get_range()

9.4 導入類

為遵循 Python 的總體理念,應讓文件盡可能整潔。 可將類存儲在模塊中,然後在主程序中導入所需的模塊。類似函數模塊。

練習 9-10 導入 Restaurant

練習 9-1 導入 Admin

練習 9-1 多個模塊

9.5 Python 標準庫

Python 標準庫是一組現成的模塊。可以使用標準庫中的任何函數和類,只需在程序開頭包含一條簡單的 import 語句即可。如模塊 random

>>> from random import randint
>>> randint(1, 6)
3

>>> from random import choice
>>> players = ['charles', 'martina', 'michael', 'florence', 'eli']
>>> first_up = choice(players)
>>> first_up
'florence'

練習 9-13 骰子

from random import randint

class Die:
    def __init__(self, sides = 6):
        self.sides = sides

    def roll_die(self, time = 10):
        print(f"with the {self.sides} sides dice, play in {time} times:" )
        for num in range(time):
            self.result = randint(1, self.sides)
            print(f"\tThe {num + 1} time: You get a result {self.result}.")

die = Die()
die.roll_die()

die = Die(10)
die.roll_die()

die = Die(20)
die.roll_die()

練習 9-14 彩票

from random import choice

possibilities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "a", "b", "c", "d", "e"]

winning_ticket = []
print("Let's see what the winning ticket is...")

while len(winning_ticket) < 4:
    pulled_item = choice(possibilities)
    if pulled_item not in winning_ticket:
        print(f" \tWe pulled a {pulled_item}!")
        winning_ticket.append(pulled_item)

練習 9-15 彩票分析

from random import choice

possibilities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "a", "b", "c", "d", "e"]

winning_ticket = []
print("Let's see what the winning ticket is...")

while len(winning_ticket) < 4:
    pulled_item = choice(possibilities)
    if pulled_item not in winning_ticket:
        print(f"\tWe pulled a {pulled_item}!")
        winning_ticket.append(pulled_item)

print(f"Ok ,the winning ticket are: {winning_ticket}")

my_ticket = []
num = 0
active = True
while active:
    while len(winning_ticket) < 4:
        pulled_item = choice(possibilities)
        if pulled_item not in my_ticket:
            my_ticket.append(pulled_item)
    num = num + 1
    for value in my_ticket:
        if value in winning_ticket:
            active = False
    if num == 100000000000:
        active = False
print(num)

練習 9-16 Python Module of the Week

9.6 類編碼風格

必須熟悉有些與類相關的編碼風格問題,在編寫的程序較複雜時尤其如此。

類名應採用駝峰命名法,即將類名中的每個單詞的首字母都大寫,而不使用下劃線。實例名和模塊名都採用小寫格式,並在單詞之間加上下劃線。

對於每個類,都應緊跟在類定義後麵包含一個文檔字符串。這種文檔字符串簡要地描述類的功能,並遵循編寫函數的文檔字符串時採用的格式約定。每個模塊也都應包含一個文檔字符串,對其中的類可用於做什麼進行描述。

可使用空行來組織代碼,但不要濫用。在類中,可使用一個空行來分隔方法;而在模塊中,可使用兩個空行來分隔類。

需要同時導入標準庫中的模塊和你編寫的模塊時,先編寫導入標準庫模塊的 import 語句,再添加一個空行,然後編寫導入編寫的模塊的 import 語句。在包含多條 import 語句的程序中,這種做法讓人更容易明白程序使用的各個模塊都來自何處。

第 10 章 文件和異常

10.1 從文件中讀取數據

讀取整個文件,如:

with open('pi_digits.txt') as file_object:
    contents = file_object.read()
print(contents.rstrip())

函數 open() 用來打開文件,接受一個參數:要打開的文件的名稱。 Python 將在當前執行的文件所在的目錄中查找指定的文件。函數 open() 返回一個表示文件的對象。在這裡,open('pi_digits.txt') 返回一個表示文件 pi_digits.txt 的對象,Python 將該對象賦給 file_object 供以後使用。關鍵字 with 在不再需要訪問文件後將其關閉。也可以調用 open()close() 來打開和關閉文件,但這樣做時,如果程序存在 bug 導致方法 close() 未執行,文件將不會關閉。如果在程序中過早調用 close(),將導致需要使用文件時它已關閉(無法訪問),這會導致更多的錯誤。使用方法 read() 讀取這個文件的全部內容,並將其作為一個字符串賦給變量 contents。這樣,通過打印 contents 的值,就可將這個文本文件的全部內容顯示出來。

read() 到達文件末尾時返回一個空字符串,而將這個空字符串顯示出來時就是一個空行。要刪除多出來的空行,可在函數調用 print() 中使用 rstrip()。

Python 只在當前文件夾中查找,而不會在其子文件夾中查找。要讓 Python 打開不與程序文件位於同一個目錄中的文件,需要提供文件路徑,讓 Python 到系統的特定位置去查找。

如,由於文件夾 text_files 位於文件夾 python_work 中,可以使用相對文件路徑來打開其中的文件:

with open('text_files/filename.txt') as file_object:

還可以將文件在計算機中的準確位置告訴 Python,這這稱為絕對文件路徑。如:

file_path = '/home/ehmatthes/other_files/text_files/_filename_.txt'
with open(file_path) as file_object:

要以每次一行的方式檢查文件,可對文件對象使用 for 循環。要消除這些多餘的空白行,可在函數調用 print() 中使用 rstrip()::

filename = 'pi_digits.txt'

with open(filename) as file_object:
    for line in file_object:
        print(line.rstrip()

使用關鍵字 with 時,open() 返回的文件對像只在 with 代碼塊內可用。如果要在 with 代碼塊外訪問文件的內容,可在 with 代碼塊內將文件的各行存儲在一個列表中,並在 with 代碼塊外使用該列表:可以立即處理文件的各個部分,也可以推遲到程序後面再處理。如:

filename = 'pi_digits.txt'

with open(filename) as file_object:
    lines = file_object.readlines()

for line in lines:
    print(line.rstrip())

使用文件的內容,如:

filename = 'pi_digits.txt'

with open(filename) as file_object:
    lines = file_object.readlines()

pi_string = ''
for line in lines:
    pi_string += line.strip()

print(pi_string)
print(len(pi_string))

可使用方法 replace() 將字符串中的特定單詞都替換為另一個單詞。如:

>>> message = "I really like dogs."
>>> message.replace('dog', 'cat')
'I really like cats.'

10.2 寫入文件

要將文本寫入文件,你在調用 open() 時需要提供另一個實參,告訴 Python 你要寫入打開的文件。在第二個實參可指定為讀取模式 r、寫入模式 w、附加模式 a 或讀寫模式 r+。如果省略了模式實參,Python 將以默認的只讀模式打開文件。

如果要寫入的文件不存在,函數 open() 將自動創建它。但如果指定的文件已經存在,Python 將在返回文件對象前清空該文件的內容。

寫入多行時需要指定換行符,如:

filename = 'programming.txt'

with open(filename, 'w') as file_object:
    file_object.write("I love programming.\n")
    file_object.write("I love creating new games.\n")

打開文件時可指定實參 a,以便將內容附加到文件末尾,而不是覆蓋文件原來的內容。

filename = 'programming.txt'

with open(filename, 'a') as file_object:
    file_object.write("I also love finding meaning in large datasets.\n")
    file_object.write("I love creating apps that can run in a browser.\n")

練習 10-3 訪客

def record_guest():
    name = input("Hey! Please type your name: ")
    with open("guest.txt", "a") as file_object:
        file_object.write(f"{name}\n")

record_guest()

練習 10-4 訪客名單

def record_guest():
    with open("guest.txt", "a") as file_object:
        file_object.write(f"{name.title()}\n")
    print(f"Welcome {name.title()}!")

while True:
    name = input("Please enter your name, if you need to log out, please enter 'Quit': ")
    if name.title() != 'Quit':
        record_guest()
    else:
       break

練習 10-5 調查

10.3 異常

ZeroDivisionError 異常:出現在 0 除某數時。

FileNotFoundError 異常:所讀取的文件不存在。

當認為可能會發生錯誤時,可編寫一個 try-except 代碼塊來處理可能引發的異常。 Python 嘗試運行一些代碼,並告訴它如果這些代碼引發了指定的異常該怎麼辦。依賴 try 代碼塊成功執行的代碼都應放到 else 代碼塊中。如:

print("Give me two numbers, and I'll divide them.")
print("Enter 'q' to quit.")

while True:
    first_number = input("\nFirst number: ")
    if first_number == 'q':
        break
    second_number = input("Second number: ")
    if second_number == 'q':
            break
        try:
            answer = int(first_number) / int(second_number)
        except ZeroDivisionError:
            print("You can't divide by 0!")
        else:
            print(answer)

try-except-else 代碼塊的工作原理大致如下。 Python 嘗試執行 try 代碼塊中的代碼,只有可能引發異常的代碼才需要放在 try 語句中。有時候,有一些僅在 try 代碼塊成功執行時才需要運行的代碼,這些代碼應放在 else 代碼塊中。 except 代碼塊告訴 Python,如果嘗試運行 try 代碼塊中的代碼時引發了指定的異常該怎麼辦。

又如:

filename = 'alice.txt'

try:
    with open(filename, encoding='utf-8') as f:
        contents = f.read()
except FileNotFoundError:
    print(f"Sorry, the file {filename} does not exist.")

用方法 split(),它能根據一個字符串創建一個單詞列表。如:

filename = 'alice.txt'

try:
    with open(filename, encoding='utf-8') as f:
        contents = f.read()
except FileNotFoundError:
    print(f"Sorry, the file {filename} does not exist.")
else:
      # 计算该文件大致包含多少个单词。
    words = contents.split()
    num_words = len(words)
    print(f"The file {filename} has about {num_words} words.")

分析多個文件,如:

def count_words(filename):
    try:
        with open(filename, encoding='utf-8') as f:
            contents = f.read()
    except FileNotFoundError:
        print(f"Sorry, the file {filename} does not exist.")
    else:
        words = contents.split()
        num_words = len(words)
        print(f"The file {filename} has about {num_words} words.")

filenames = ['alice.txt', 'siddhartha.txt', 'moby_dick.txt', 'little_women.txt']
for filename in filenames:
    count_words(filename)

可以使用 pass 語句來靜默失敗,如:

def count_words(filename):
    try:
        --snip--
    except FileNotFoundError:
        pass
    else:
        --snip--

filenames = ['alice.txt', 'siddhartha.txt', 'moby_dick.txt', 'little_women.txt']
for filename in filenames:
    count_words(filename)

練習 10-6 加法計算

def plus(a, b):
    try:
        c = int(a) + int(b)
        print(c)
    except ValueError :
        print("Please type number, not text!")

a = input("Please type a number: ")
b = input("Please type an another number: ")
plus(a, b)

練習 10-7 加法計算器

def plus(a, b):
    try:
        c = int(a) + int(b)
        print(c)
    except ValueError :
        print("Please type number, not text! If you want to quit, please type 'quit'.")

while True:
    a = input("Please type a number.If you want to quit, please type 'quit'. ")
    if a.title() == "Quit":
        break
    b = input("Please type an another number.If you want to quit, please type 'quit'. ")
    if b.title() == "Quit":
        break
    plus(a, b)

練習 10-8 貓和狗

練習 10-9 靜默的貓和狗

練習 10-10 常見單詞

def count_word(file_name, specific_word):
    try:
        with open(file_name, encoding='utf-8') as file:
            contents = file.read()
    except FileNotFoundError:
        print(f"Sorry, the file {file_name} does not exist.")
    else:
        print(contents.lower().count(specific_word))

count_word('Pride_and_Prejudigce_by_Jane_Austen.txt', 'the ')
count_word('Pride_and_Prejudice_by_Jane_Austen.txt', 'the')

10.4 存儲數據

可使用 json 模塊來存儲數據。

函數 json.dump() 接受兩個實參:要存儲的數據,以及可用於存儲數據的文件對象。先導入模塊 json,再創建一個數字列表。指定要將該數字列表存儲到哪個文件中。通常使用文件擴展名 .json 來指出文件存儲的數據為 JSON 格式。接下來,以寫入模式打開這個文件,讓 json 能夠將數據寫入其中。使用函數 json.dump() 將數字列表存儲到文件 numbers.json 中。如:

import json

numbers = [2, 3, 5, 7, 11, 13]

filename = 'numbers.json'
with open(filename, 'w') as f:
    json.dump(numbers, f)

使用 json.load() 將列表讀取到內存中。确保读取的是前面写入的文件,以读取方式打开该文件,使用函数 json.load() 加载存储在 JSON 中的信息,并将其赋给变量。如:

import json

filename = 'numbers.json'
with open(filename) as f:
    numbers = json.load(f)

print(numbers)

又如:

import json

filename = 'username.json'
try:
    with open(filename) as f:
        username = json.load(f)
except FileNotFoundError:
    username = input("What is your name? ")
    with open(filename, 'w') as f:
        json.dump(username, f)
        print(f"We'll remember you when you come back, {username}!")
else:
    print(f"Welcome back, {username}!")

要編寫出清晰而易於維護和擴展的代碼,最好每個函數都執行單一而清晰的任務。

練習 10-11 喜歡的數

import json

def record_num():
    num = input("What is yout favourite number? ")
    with open("10-11.json", "w") as file:
        json.dump(num, file)
    return num

def show_num():
    with open("10-11.json") as file:
        num = json.load(file)
        print(f"Ha ha! I know your favourite number is {num}")

record_num()

show_num()

練習 10-12 記住喜歡的數

import json

filename = "10-11.json"
try:
    with open("10-11.json") as file:
        num = json.load(file)
except FileNotFoundError:
    num = input("What is yout favourite number? ")
    with open("10-11.json", "w") as file:
        json.dump(num, file)
else:
    print(f"Ha ha! I know your favourite number is {num}")

練習 10-13 驗證用戶

import json

def get_stored_username():
    filename = 'username.json' 
    try:
        with open(filename) as f:
            username = json.load(f)
    except FileNotFoundError:
        return None
    else:
        return username

def get_new_username():
    username = input("What is your name? \n")
    filename = 'username.json'
    with open(filename, 'w') as f:
        json.dump(username.title(), f)
    return username

def greet_user():
    username = get_stored_username()
    if username:
        v = input(f"Is {username.title()} you name? <yes or no>\n")
        if v.lower() == "yes":
            print(f"Welcome back, {username.title()}!")
        else:
            username = get_new_username()
            print(f"We'll remember you when you come back, {username.title()}!")
    else:
        username = get_new_username()
        print(f"We'll remember you when you come back, {username.title()}!")

greet_user()

第 11 章

11.1 測試函數

Python 標準庫中的模塊 unittest 提供了代碼測試工具。單元測試用於核實函數的某個方面沒有問題。測試用例是一組單元測試,它們一道核實函數在各種情形下的行為都符合要求。良好的測試用例考慮到了函數可能收到的各種輸入,包含針對所有這些情形的測試。全覆蓋的測試用例包含一整套單元測試,涵蓋了各種可能的函數使用方式。

要为函数编写测试用例,可先导入模块 unittest 和要测试的函数,再创建一个继承 unittest.TestCase 的类,并编写一系列方法对函数行为的不同方面进行测试。如:

def get_formatted_name(first, last):
    full_name = f"{first} {last}"
    return full_name.title()
import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):

    def test_first_last_name(self):
        formatted_name = get_formatted_name('janis', 'joplin')
        self.assertEqual(formatted_name, 'Janis Joplin')

if __name__ == '__main__':
    unittest.main()

首先,导入模块 unittest 和要测试的函数 get_formatted_name()。创建了一个名为 NamesTestCase 的类,用于包含一系列针对 get_formatted_name() 的单元测试。这个类必须继承 unittest.TestCase 类,这样 Python 才知道如何运行测试。

NamesTestCase 只包含一个方法,用于测试 get_formatted_name() 的一个方面。将该方法命名为 test_first_last_name(),因为要核实的是只有名和姓的姓名能否被正确格式化。运行 test_name_function.py 时,所有以 test_ 打头的方法都将自动运行。在这个方法中,调用了要测试的函数。在本例中,使用实参'janis''joplin' 调用 get_formatted_name(),并将结果赋给变量 formatted_name

unittest 类最有用的功能之一:断言方法。断言方法核实得到的结果是否与期望的结果一致。调用 unittest 的方法 assertEqual(),并向它传递 formatted_name'Janis Joplin'。如果它们相等則測試通過。

練習 11-1 城市和國家

from city_functions import city_functions
import unittest

class CityTestCase(unittest.TestCase):
    def test_city_functions(self):
        formatted_name = city_functions("shijiazhuang", "china")
        self.assertEqual(formatted_name, "Shijiazhuang, China")

if __name__ == "__main__":
    unittest.main()

練習 11-2 人口數量

from city_functions import city_functions
import unittest

class CityTestCase(unittest.TestCase):
    def test_city_functions(self):
        formatted_name = city_functions("santiago", "chile", "5000000")
        self.assertEqual(formatted_name, "Santiago, Chile - population 5000000")

if __name__ == "__main__":
    unittest.main()

11.2 測試類

Python 在 unittest.TestCase 類中提供了很多斷言方法。如:

方法 用途
assertEqual(a, b) 核实 a == b
assertNotEqual(a, b) 核实 a != b
assertTrue(x) 核实 xTrue
assertFalse(x) 核实 xFalse
assertIn(_item_, _list_) 核实_item__list_
assertNotIn(_item_, _list_) 核实_item_不在_list_

測試一個類,如:

class AnonymousSurvey:

    def __init__(self, question):
        self.question = question
        self.responses = []

    def show_question(self):
        print(self.question)

    def store_response(self, new_response):
        self.responses.append(new_response)

    def show_results(self):
        print("Survey results:")
        for response in self.responses:
            print(f"- {response}")
from survey import AnonymousSurvey

# 定义一个问题,并创建一个调查。
question = "What language did you first learn to speak?"
my_survey = AnonymousSurvey(question)

# 显示问题并存储答案。
my_survey.show_question()
print("Enter 'q' at any time to quit.\n")
while True:
    response = input("Language: ")
    if response == 'q':
        break
    my_survey.store_response(response)

# 显示调查结果。
print("\nThank you to everyone who participated in the survey!")
my_survey.show_results()

這個類首先存儲了一個調查問題,並創建了一個空列表,用於存儲答案。這個類包含打印調查問題的方法,在答案列表中添加新答案的方法,以及將存儲在列表中的答案都打印出來的方法。要創建該類的實例,只需提供一個問題即可。有了表示調查的實例後,就可使用 show_question() 來顯示其中的問題,使用 store_response() 來存儲答案並使用 show_results() 來顯示調查結果。

進行測試,如:

import unittest
from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):
    def test_store_single_response(self):
        question = "What language did you first learn to speak?"
        my_survey = AnonymousSurvey(question)
        my_survey.store_response('English')
        self.assertIn('English', my_survey.responses)

    def test_store_three_responses(self):
        question = "What language did you first learn to speak?"
        my_survey = AnonymousSurvey(question)
        responses = ['English', 'Spanish', 'Mandarin']
        for response in responses:
            my_survey.store_response(response)

        for response in responses:
              elf.assertIn(response, my_survey.responses)

if __name__ == '__main__':
    unittest.main()

首先導入模塊 unittest 和要測試的類 AnonymousSurvey。將測試用例命名為 TestAnonymousSurvey,它也繼承了 unittest.TestCase。第一個測試方法驗證:調查問題的單個答案被存儲後,會包含在調查結果列表中。

在前面的 test_survey.py 中,每個測試方法中都創建了一個 AnonymousSurvey 實例,並在每個方法中都創建了答案。 unittest.TestCase 類包含的方法 setUp() 讓我們只需創建這些對像一次,就能在每個測試方法中使用。如果在 TestCase 類中包含了方法 setUp(),Python 將先運行它,再運行各個以 test_ 打頭的方法。這樣,在每個測試方法中,都可使用在方法 setUp() 中創建的對象。如:

import unittest
from survey import AnonymousSurvey

class TestAnonymousSurvey(unittest.TestCase):

    def setUp(self):
        question = "What language did you first learn to speak?"
        self.my_survey = AnonymousSurvey(question)
        self.responses = ['English', 'Spanish', 'Mandarin']

    def test_store_single_response(self):
        self.my_survey.store_response(self.responses[0])
        self.assertIn(self.responses[0], self.my_survey.responses)

    def test_store_three_responses(self):
        for response in self.responses:
            self.my_survey.store_response(response)
        for response in self.responses:
            self.assertIn(response, self.my_survey.responses)

if __name__ == '__main__':
    unittest.main()

方法 setUp() 做了兩件事情:創建一個調查對象,以及創建一個答案列表。存儲這兩樣東西的變量名包含前綴 self(即存儲在屬性中),因此可在這個類的任何地方使用。這讓兩個測試方法都更簡單,因為它們都不用創建調查對象和答案了。方法 test_store_single_response() 核實 self.responses 中的第一個答案 self.responses[0] 被妥善地存儲,而方法 test_store_three_response() 核實 self.responses 中的全部三個答案都被妥善地存儲。

練習 11-3 雇員

import unittest
from employ import Employee

class TestEmployee(unittest.TestCase):
    def test_salary(self):
        self.me = Employee("miao", "chiang", 100)
        self.me.give_raise()
        self.assertEqual(self.me.final_salary, 200)

if __name__ == '__main__':
    unittest.main()

End.