Python書籍閱讀與記錄 6.16 I

我感覺這樣記錄,對於我來說挺好的。因爲我看兩端對齊的語句容易走神,這樣記錄閱讀的話,就很少出現之前的情況。

我寫的初衷,也是自己來看,所以感覺寫的不好的,請保留下意見,謝謝。

代碼縮進情況,字體重複情況,因爲我能看懂,就沒改。

 

 

裏面的每一個字我都看過,加粗 括號  下劃線 等均是我的筆記。

 
 
第8章 函數
在本章中,你將學習編寫函
數 。函數是帶名字的代碼塊,用於完成具體的工作。
要執行函數定義的特定任務,可調用該函數。需要在程序中多次執行同一項任務時,你無需反覆編寫完成該任務的代碼,而只需調用執行該任務的函數,讓Python運行
其中的代碼。你將發現,通過使用函數,程序的編寫、閱讀、測試和修復都將更容易。
在本章中,你還會學習向函數傳遞信息的方式。你將學習如何編寫主要任務是顯示信息的函數,還有用於處理數據並返回一個或一組值的函數。最後,你將學習如何
將函數存儲在被稱爲模
模塊
塊 的獨立文件中,讓主程序文件的組織更爲有序。
8.1 定義函數
下面是一個打印問候語的簡單函數,名爲greet_user()
greeter.py
def greet_user():
"""顯示簡單的問候語"""
print("Hello!")
greet_user()
>>> def sum():
...  """123"""  #不知道文檔字符串是做什麼的  看我筆記
https://blog.csdn.net/nyist_yangguang/article/details/106781146
...  print('123')
...
>>> sum
<function sum at 0x0000029995DDC1E0>
>>> sum()
123
這個示例演示了最簡單的函數結構。處的代碼行使用關鍵字def 來告訴Python你要定義一個函數。這是函
義 ,向Python指出了函數名,還可能在括號內指出函數爲完成其
任務需要什麼樣的信息。在這裏,函數名爲greet_user() ,它不需要任何信息就能完成其工作,因此括號是空的(即便如此,括號也必不可少)。最後,定義以冒號結尾。
緊跟在def greet_user(): 後面的所有縮進行構成了函數體。處的文本是被稱爲文檔字
字符串 (docstring)的註釋,描述了函數是做什麼的。文檔字符串用三引號括
起,Python使用它們來生成有關程序中函數的文檔。
代碼行print("Hello!") (見)是函數體內的唯一一行代碼,greet_user() 只做一項工作:打印Hello!
要使用這個函數,可調用它。函數調用 讓Python執行函數的代碼。要調
用 函數,可依次指定函數名以及用括號括起的必要信息,如處所示。由於這個函數不需要任何信息,因
此調用它時只需輸入greet_user() 即可。和預期的一樣,它打印Hello! Hello!
8.1.1 向函數傳遞信息
只需稍作修改,就可以讓函數greet_user() 不僅向用戶顯示Hello! ,還將用戶的名字用作擡頭。爲此,可在函數定義def greet_user() 的括號內添加username 。通
過在這裏添加username ,就可讓函數接受你給username 指定的任何值。現在,這個函數要求你調用它時給username 指定一個值。調用greet_user() 時,可將一個名字
傳遞給它,如下所示:
def greet_user(username):
"""顯示簡單的問候語"""
print("Hello, " + username.title() + "!")
greet_user('jesse')
代碼greet_user('jesse') 調用函數greet_user() ,並向它提供執行print 語句所需的信息。這個函數接受你傳遞給它的名字,並向這個人發出問候:
Hello, Jesse!
同樣,greet_user('sarah') 調用函數greet_user() 並向它傳遞'sarah' ,打印Hello, Sarah! 。你可以根據需要調用函數greet_user() 任意次,調用時無論傳
入什麼樣的名字,都會生成相應的輸出。
8.1.2 實參和形參
前面定義函數greet_user() 時,要求給變量username 指定一個值。調用這個函數並提供這種信息(人名)時,它將打印相應的問候語。
在函數greet_user() 的定義中,變量username 是一個形
——函數完成其工作所需的一項信息。在代碼greet_user('jesse') 中,值'jesse' 是一個實參 。實參是
調用函數時傳遞給函數的信息。我們調用函數時,將要讓函數使用的信息放在括號內。在greet_user('jesse') 中,將實參'jesse' 傳遞給了函數greet_user() ,這個
值被存儲在形參username 中。 
注意
大家有時候會形參、實參不分,因此如果你看到有人將函數定義中的變量稱爲實參或將函數調用中的變量稱爲形參,不要大驚小怪。
動手試一試
 
8-1
消息
息 :編寫一個名爲display_message() 的函數,它打印一個句子,指出你在本章學的是什麼。調用這個函數,確認顯示的消息正確無誤。
8-2
喜歡
歡的
的圖
圖書
書 :編寫一個名爲favorite_book() 的函數,其中包含一個名爲title 的形參。這個函數打印一條消息,如One of my favorite books is
Alice in Wonderland 。調用這個函數,並將一本圖書的名稱作爲實參傳遞給它。
8.2 傳遞實參
鑑於函數定義中可能包含多個形參,因此函數調用中也可能包含多個實參。向函數傳遞實參的方式很多,可使用位
置實參 ,這要求實參的順序與形參的順序相同;也可使用關鍵字實
參 ,其中每個實參都由變量名和值組成;還可使用列表和字典。下面來依次介紹這些方式。
8.2.1 位置實參
你調用函數時,Python必須將函數調用中的每個實參都關聯到函數定義中的一個形參。爲此,最簡單的關聯方式是基於實參的順序。這種關聯方式被稱爲位置實參
爲明白其中的工作原理,來看一個顯示寵物信息的函數。這個函數指出一個寵物屬於哪種動物以及它叫什麼名字,如下所示:
pets.py
def describe_pet(animal_type, pet_name):
"""顯示寵物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet('hamster', 'harry')
這個函數的定義表明,它需要一種動物類型和一個名字(見)。調用describe_pet() 時,需要按順序提供一種動物類型和一個名字。例如,在前面的函數調用中,實
'hamster' 存儲在形參animal_type 中,而實參'harry' 存儲在形參pet_name 中(見)。在函數體內,使用了這兩個形參來顯示寵物的信息。
輸出描述了一隻名爲Harry的倉鼠:
I have a hamster.
My hamster's name is Harry.
1. 調用函數多次
你可以根據需要調用函數任意次。要再描述一個寵物,只需再次調用describe_pet() 即可:
def describe_pet(animal_type, pet_name):
"""顯示寵物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet('hamster', 'harry')
describe_pet('dog', 'willie')
第二次調用describe_pet() 函數時,我們向它傳遞了實參'dog' 'willie' 。與第一次調用時一樣,Python將實參'dog' 關聯到形參animal_type ,並將實'willie' 關聯到形參pet_name 。與前面一樣,這個函數完成其任務,但打印的是一條名爲Willie的小狗的信息。至此,我們有一隻名爲Harry的倉鼠,還有一條名爲Willie
小狗:
I have a hamster.
My hamster's name is Harry.
I have a dog.
My dog's name is Willie.
調用函數多次是一種效率極高的工作方式。我們只需在函數中編寫描述寵物的代碼一次,然後每當需要描述新寵物時,都可調用這個函數,並向它提供新寵物的信息。即便描述
寵物的代碼增加到了10行,你依然只需使用一行調用函數的代碼,就可描述一個新寵物。
在函數中,可根據需要使用任意數量的位置實參,Python將按順序將函數調用中的實參關聯到函數定義中相應的形參。
2. 位置實參的順序很重要
使用位置實參來調用函數時,如果實參的順序不正確,結果可能出乎意料:
def describe_pet(animal_type, pet_name):
"""顯示寵物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet('harry', 'hamster')
在這個函數調用中,我們先指定名字,再指定動物類型。由於實參'harry' 在前,這個值將存儲到形參animal_type 中;同理,'hamster' 將存儲到形參pet_name 中。
結果是我們得到了一個名爲Hamster harry
I have a harry.
My harry's name is Hamster.
如果結果像上面一樣搞笑,請確認函數調用中實參的順序與函數定義中形參的順序一致。
8.2.2 關鍵字實參
關鍵字實參是傳遞給函數的名稱值對你直接在實參中將名稱和值關聯起來了,因此向函數傳遞實參時不會混淆(不會得到名爲Hamsterharry這樣的結果)。關鍵字實參讓   #下面形參中設置字典來接受 鍵值對 利用到了關鍵字實參
你無需考慮函數調用中的實參順序,還清楚地指出了函數調用中各個值的用途。
下面來重新編寫pets.py,在其中使用關鍵字實參來調用describe_pet()
def describe_pet(animal_type, pet_name):
"""顯示寵物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet(animal_type='hamster', pet_name='harry')
函數describe_pet() 還是原來那樣,但調用這個函數時,我們向Python明確地指出了各個實參對應的形參。看到這個函數調用時,Python知道應該將實參'hamster'
'harry' 分別存儲在形參animal_type pet_name 中。輸出正確無誤,它指出我們有一隻名爲Harry的倉鼠。
關鍵字實參的順序無關緊要,因爲Python知道各個值該存儲到哪個形參中。下面兩個函數調用是等效的:
describe_pet(animal_type='hamster', pet_name='harry')
describe_pet(pet_name='harry', animal_type='hamster') 
注意
使用關鍵字實參時,務必準確地指定函數定義中的形參名。
8.2.3 默認值
編寫函數時,可給每個形參指定默
在調用函數中給形參提供了實參時,Python將使用指定的實參值;否則,將使用形參的默認值。因此,給形參指定默認值後,可在函數
調用中省略相應的實參。使用默認值可簡化函數調用,還可清楚地指出函數的典型用法。
例如,如果你發現調用describe_pet() 時,描述的大都是小狗,就可將形參animal_type 的默認值設置爲'dog' 。這樣,調用describe_pet() 來描述小狗時,就可不
提供這種信息:
def describe_pet(pet_name, animal_type='dog'):
"""顯示寵物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet(pet_name='willie')
這裏修改了函數describe_pet() 的定義,在其中給形參animal_type 指定了默認值'dog' 。這樣,調用這個函數時,如果沒有給animal_type 指定值,Python將把這個
形參設置爲'dog'
I have a dog.
My dog's name is Willie.
請注意,在這個函數的定義中,修改了形參的排列順序。由於給animal_type 指定了默認值,無需通過實參來指定動物類型,此在函數調用中只包含一個實參——寵物的名字。然而,Python依然將這個實參視爲位置實參,因此如果函數調用中只包含寵物的名字,這個實參將關聯到函數定義中的第一個形參。這就是需要將pet_name 放在形參列表
開頭的原因所在
現在,使用這個函數的最簡單的方式是,在函數調用中只提供小狗的名字:
describe_pet('willie')
這個函數調用的輸出與前一個示例相同。只提供了一個實參——'willie' ,這個實參將關聯到函數定義中的第一個形參——pet_name 。由於沒有給animal_type 提供實
參,因此Python使用其默認值'dog'
如果要描述的動物不是小狗,可使用類似於下面的函數調用:
describe_pet(pet_name='harry', animal_type='hamster')
由於顯式地給animal_type 提供了實參,因此Python將忽略這個形參的默認值。 
注意
使用默認值時,在形參列表中必須先列出沒有默認值的形參,再列出有默認值的實參。這讓Python依然能夠正確地解讀位置實參。
8.2.4 等效的函數調用
鑑於可混合使用位置實參、關鍵字實參和默認值,通常有多種等效的函數調用方式。請看下面的函數describe_pets() 的定義,其中給一個形參提供了默認值:
def describe_pet(pet_name, animal_type='dog'):
基於這種定義,在任何情況下都必須給pet_name 提供實參;指定該實參時可以使用位置方式,也可以使用關鍵字方式。如果要描述的動物不是小狗,還必須在函數調用中
animal_type 提供實參;同樣,指定該實參時可以使用位置方式,也可以使用關鍵字方式。
下面對這個函數的所有調用都可行:
# 一條名爲Willie的小狗  #因爲有形參默認值,所以缺省一個實參
describe_pet('willie') #位置實參
describe_pet(pet_name='willie') #位置實參
# 一隻名爲Harry的倉鼠  #沒有缺省實參
describe_pet('harry', 'hamster') #位置實參
describe_pet(pet_name='harry', animal_type='hamster')  #關鍵字實參
describe_pet(animal_type='hamster', pet_name='harry')  #關鍵字實參
這些函數調用的輸出與前面的示例相同。 
注意
使用哪種調用方式無關緊要,只要函數調用能生成你希望的輸出就行。使用對你來說最容易理解的調用方式即可
8.2.5 避免實參錯誤
等你開始使用函數後,如果遇到實參不匹配錯誤,不要大驚小怪。你提供的實參多於或少於函數完成其工作所需的信息時,將出現實參不匹配錯誤。例如,如果調用函
describe_pet() 時沒有指定任何實參,結果將如何呢?
def describe_pet(animal_type, pet_name):
"""顯示寵物的信息"""
print("\nI have a " + animal_type + ".")
print("My " + animal_type + "'s name is " + pet_name.title() + ".")
describe_pet()
Python發現該函數調用缺少必要的信息,而traceback指出了這一點:
Traceback (most recent call last):
File "pets.py", line 6, in <module>
describe_pet()
TypeError: describe_pet() missing 2 required positional arguments: 'animal_
type' and 'pet_name'
處,traceback指出了問題出在什麼地方,讓我們能夠回過頭去找出函數調用中的錯誤。在處,指出了導致問題的函數調用。在處,traceback指出該函數調用少兩個實參,
並指出了相應形參的名稱。如果這個函數存儲在一個獨立的文件中,我們也許無需打開這個文件並查看函數的代碼,就能重新正確地編寫函數調用。
Python讀取函數的代碼,並指出我們需要爲哪些形參提供實參,這提供了極大的幫助。這也是應該給變量和函數指定描述性名稱的另一個原因;如果你這樣做了,那麼無論對於
你,還是可能使用你編寫的代碼的其他任何人來說,Python提供的錯誤消息都將更有幫助。
如果提供的實參太多,將出現類似的traceback,幫助你確保函數調用和函數定義匹配。
動手試一試
8-3 T
:編寫一個名爲make_shirt() 的函數,它接受一個尺碼以及要印到T恤上的字樣。這個函數應打印一個句子,概要地說明T恤的尺碼和字樣。
使用位置實參調用這個函數來製作一件T恤;再使用關鍵字實參來調用這個函數。
8-4
大號
T
:修改函數make_shirt() ,使其在默認情況下製作一件印有字樣“I love Python”的大號T恤。調用這個函數來製作如下T恤:一件印有默認字樣的大號T
恤、一件印有默認字樣的中號T恤和一件印有其他字樣的T恤(尺碼無關緊要)。
8-5
城市
市 :編寫一個名爲describe_city() 的函數,它接受一座城市的名字以及該城市所屬的國家。這個函數應打印一個簡單的句子,如Reykjavik is in
Iceland 。給用於存儲國家的形參指定默認值。爲三座不同的城市調用這個函數,且其中至少有一座城市不屬於默認國家。
8.3 返回值
函數並非總是直接顯示輸出,相反,它可以處理一些數據,並返回一個或一組值。函數返回的值被稱爲返
值 。在函數中,可使用return 語句將值返回到調用函數的代碼行。
返回值讓你能夠將程序的大部分繁重工作移到函數中去完成,從而簡化主程序。
8.3.1 返回簡單值
下面來看一個函數,它接受名和姓並返回整潔的姓名:
formatted_name.py
def get_formatted_name(first_name, last_name):
"""返回整潔的姓名"""
full_name = first_name + ' ' + last_name
return full_name.title()
musician = get_formatted_name('jimi', 'hendrix')
print(musician)
函數get_formatted_name() 的定義通過形參接受名和姓(見)。它將姓和名合而爲一,在它們之間加上一個空格,並將結果存儲在變量full_name 中(見)。然後,
full_name 的值轉換爲首字母大寫格式,並將結果返回到函數調用行(見)。
調用返回值的函數時,需要提供一個變量,用於存儲返回的值。在這裏,將返回值存儲在了變量musician 中(見)。輸出爲整潔的姓名:
Jimi Hendrix
我們原本只需編寫下面的代碼就可輸出整潔的姓名,相比於此,前面做的工作好像太多了:
print("Jimi Hendrix")
但在需要分別存儲大量名和姓的大型程序中,像get_formatted_name() 這樣的函數非常有用。你分別存儲名和姓,每當需要顯示姓名時都調用這個函數。
8.3.2 讓實參變成可選的
有時候,需要讓實參變成可選的,這樣使用函數的人就只需在必要時才提供額外的信息。可使用默認值來讓實參變成可選的。
例如,假設我們要擴展函數get_formatted_name() ,使其還處理中間名。爲此,可將其修改成類似於下面這樣:
def get_formatted_name(first_name, middle_name, last_name):
"""返回整潔的姓名"""
full_name = first_name + ' ' + middle_name + ' ' + last_name
return full_name.title()
musician = get_formatted_name('john', 'lee', 'hooker')
print(musician)
只要同時提供名、中間名和姓,這個函數就能正確地運行。它根據這三部分創建一個字符串,在適當的地方加上空格,並將結果轉換爲首字母大寫格式:
John Lee Hooker
然而,並非所有的人都有中間名,但如果你調用這個函數時只提供了名和姓,它將不能正確地運行。爲讓中間名變成可選的,可給實參middle_name 指定一個默認值——空字
符串並在用戶沒有提供中間名時不使用這個實參。爲讓get_formatted_name() 在沒有提供中間名時依然可行,可給實參middle_name 指定一個默認值——空字符串,
並將其移到形參列表的末尾:
def get_formatted_name(first_name, last_name, middle_name=''):
"""返回整潔的姓名"""
if middle_name:
full_name = first_name + ' ' + middle_name + ' ' + last_name
else:
full_name = 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)
在這個示例中,姓名是根據三個可能提供的部分創建的。由於人都有名和姓,因此在函數定義中首先列出了這兩個形參。中間名是可選的,因此在函數定義中最後列出該形參,
並將其默認值設置爲空字符串(見)。
在函數體中,我們檢查是否提供了中間名。Python將非空字符串解讀爲True ,因此如果函數調用中提供了中間名,if middle_name 將爲True (見)。如果提供了中間
名,就將名、中間名和姓合併爲姓名,然後將其修改爲首字母大寫格式,並返回到函數調用行。在函數調用行,將返回的值存儲在變量musician 中;然後將這個變量的值打印
出來。如果沒有提供中間名,middle_name 將爲空字符串,導致if 測試未通過,進而執行else 代碼塊(見):只使用名和姓來生成姓名,並將設置好格式的姓名返回給函
數調用行。在函數調用行,將返回的值存儲在變量musician 中;然後將這個變量的值打印出來。
調用這個函數時,如果只想指定名和姓,調用起來將非常簡單。如果還要指定中間名,就必須確保它是最後一個實參,這樣Python才能正確地將位置實參關聯到形參(見)。
這個修改後的版本適用於只有名和姓的人,也適用於還有中間名的人:
Jimi Hendrix
John Lee Hooker
可選值讓函數能夠處理各種不同情形的同時,確保函數調用盡可能簡單。
8.3.3 返回字典
函數可返回任何類型的值,包括列表和字典等較複雜的數據結構。例如,下面的函數接受姓名的組成部分,並返回一個表示人的字典:
person.py
def build_person(first_name, last_name):
"""返回一個字典,其中包含有關一個人的信息"""
person = {'first': first_name, 'last': last_name}
return person
musician = build_person('jimi', 'hendrix')
print(musician)
函數build_person() 接受名和姓,並將這些值封裝到字典中(見)。存儲first_name 的值時,使用的鍵爲'first' ,而存儲last_name 的值時,使用的鍵
'last' 。最後,返回表示人的整個字典(見)。在處,打印這個返回的值,此時原來的兩項文本信息存儲在一個字典中:
{'first': 'jimi', 'last': 'hendrix'}
這個函數接受簡單的文本信息,將其放在一個更合適的數據結構中,讓你不僅能打印這些信息,還能以其他方式處理它們。當前,字符串'jimi' 'hendrix' 被標記爲名和
姓。你可以輕鬆地擴展這個函數,使其接受可選值,如中間名、年齡、職業或你要存儲的其他任何信息。例如,下面的修改讓你還能存儲年齡:
def build_person(first_name, last_name, age=''):
"""返回一個字典,其中包含有關一個人的信息"""
person = {'first': first_name, 'last': last_name}
if age:
person['age'] = age
return person
musician = build_person('jimi', 'hendrix', age=27)
print(musician)
在函數定義中,我們新增了一個可選形參age ,並將其默認值設置爲空字符串。如果函數調用中包含這個形參的值,這個值將存儲到字典中。在任何情況下,這個函數都會存儲
人的姓名,但可對其進行修改,使其也存儲有關人的其他信息。
8.3.4 結合使用函數和while循環
可將函數同本書前面介紹的任何Python結構結合起來使用。例如,下面將結合使用函數get_formatted_name() while 循環,以更正規的方式問候用戶。下面嘗試使用名
和姓跟用戶打招呼:
greeter.py
def get_formatted_name(first_name, last_name):
"""返回整潔的姓名"""
full_name = first_name + ' ' + last_name
return full_name.title()
# 這是一個無限循環!
while True:
print("\nPlease tell me your name:")
f_name = input("First name: ")
l_name = input("Last name: ")
formatted_name = get_formatted_name(f_name, l_name)
print("\nHello, " + formatted_name + "!")
在這個示例中,我們使用的是get_formatted_name() 的簡單版本,不涉及中間名。其中的while 循環讓用戶輸入姓名:依次提示用戶輸入名和姓(見)。
但這個while 循環存在一個問題:沒有定義退出條件。請用戶提供一系列輸入時,該在什麼地方提供退出條件呢?我們要讓用戶能夠儘可能容易地退出,因此每次提示用戶輸入
時,都應提供退出途徑。每次提示用戶輸入時,都使用break 語句提供了退出循環的簡單途徑:
def get_formatted_name(first_name, last_name):
"""返回整潔的姓名"""
full_name = 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("\nHello, " + formatted_name + "!")
我們添加了一條消息來告訴用戶如何退出,然後在每次提示用戶輸入時,都檢查他輸入的是否是退出值,如果是,就退出循環。現在,這個程序將不斷地問候,直到用戶輸入的
姓或名爲'q' 爲止:
Please tell me your name:
(enter 'q' at any time to quit)
First name: eric
Last name: matthes
Hello, Eric Matthes!
Please tell me your name:
(enter 'q' at any time to quit)
First name: q
動手試一試
 
8-6
城市
市名
名 :編寫一個名爲city_country() 的函數,它接受城市的名稱及其所屬的國家。這個函數應返回一個格式類似於下面這樣的字符串:
"Santiago, Chile"
至少使用三個城市-國家對調用這個函數,並打印它返回的值。
8-7
專輯
輯 :編寫一個名爲make_album() 的函數,它創建一個描述音樂專輯的字典。這個函數應接受歌手的名字和專輯名,並返回一個包含這兩項信息的字典。使
用這個函數創建三個表示不同專輯的字典,並打印每個返回的值,以覈實字典正確地存儲了專輯的信息。
給函數make_album() 添加一個可選形參,以便能夠存儲專輯包含的歌曲數。如果調用這個函數時指定了歌曲數,就將這個值添加到表示專輯的字典中。調用這個
函數,並至少在一次調用中指定專輯包含的歌曲數。
8-8
用戶
戶的
的專
專輯
輯 :在爲完成練習8-7編寫的程序中,編寫一個while 循環,讓用戶輸入一個專輯的歌手和名稱。獲取這些信息後,使用它們來調用函
make_album() ,並將創建的字典打印出來。在這個while 循環中,務必要提供退出途徑。
8.4 傳遞列表
你經常會發現,向函數傳遞列表很有用,這種列表包含的可能是名字、數字或更復雜的對象(如字典)。將列表傳遞給函數後,函數就能直接訪問其內容。下面使用函數來提高
處理列表的效率。
假設有一個用戶列表,我們要問候其中的每位用戶。下面的示例將一個名字列表傳遞給一個名爲greet_users() 的函數,這個函數問候列表中的每個人:
greet_users.py
def greet_users(names):
"""向列表中的每位用戶都發出簡單的問候"""
for name in names:
msg = "Hello, " + name.title() + "!"
print(msg)
usernames = ['hannah', 'ty', 'margot']
greet_users(usernames)
我們將greet_users() 定義成接受一個名字列表,並將其存儲在形參names 中。這個函數遍歷收到的列表,並對其中的每位用戶都打印一條問候語。在處,我們定義了一
個用戶列表——usernames ,然後調用greet_users() ,並將這個列表傳遞給它:
Hello, Hannah!
Hello, Ty!
Hello, Margot!
輸出完全符合預期,每位用戶都看到了一條個性化的問候語。每當你要問候一組用戶時,都可調用這個函數。
8.4.1 在函數中修改列表
將列表傳遞給函數後,函數就可對其進行修改。在函數中對這個列表所做的任何修改都是永久性的,這讓你能夠高效地處理大量的數據。
來看一家爲用戶提交的設計製作3D打印模型的公司。需要打印的設計存儲在一個列表中,打印後移到另一個列表中。下面是在不使用函數的情況下模擬這個過程的代碼:
printing_models.py
# 首先創建一個列表,其中包含一些要打印的設計
unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron']
completed_models = []
# 模擬打印每個設計,直到沒有未打印的設計爲止
# 打印每個設計後,都將其移到列表completed_models
while unprinted_designs:
current_design = unprinted_designs.pop()
#模擬根據設計製作3D打印模型的過程
print("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)
這個程序首先創建一個需要打印的設計列表,還創建一個名爲completed_models 的空列表,每個設計打印都將移到這個列表中。只要列表unprinted_designs 中還有設
計,while 循環就模擬打印設計的過程:從該列表末尾刪除一個設計,將其存儲到變量current_design 中,並顯示一條消息,指出正在打印當前的設計,再將該設計加入到
列表completed_models 中。循環結束後,顯示已打印的所有設計:
Printing model: dodecahedron
Printing model: robot pendant
Printing model: iphone caseThe following models have been printed:
dodecahedron
robot pendant
iphone case
爲重新組織這些代碼,我們可編寫兩個函數,每個都做一件具體的工作。大部分代碼都與原來相同,只是效率更高。第一個函數將負責處理打印設計的工作,而第二個將概述打
印了哪些設計:
def print_models(unprinted_designs, completed_models):
"""
模擬打印每個設計,直到沒有未打印的設計爲止
打印每個設計後,都將其移到列表completed_models
"""
while unprinted_designs:
current_design = unprinted_designs.pop()
# 模擬根據設計製作3D打印模型的過程
print("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 = ['iphone case', 'robot pendant', 'dodecahedron']
completed_models = []
print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)
處,我們定義了函數print_models() ,它包含兩個形參:一個需要打印的設計列表和一個打印好的模型列表。給定這兩個列表,這個函數模擬打印每個設計的過程:將
設計逐個地從未打印的設計列表中取出,並加入到打印好的模型列表中。在處,我們定義了函數show_completed_models() ,它包含一個形參:打印好的模型列表。給定
這個列表,函數show_completed_models() 顯示打印出來的每個模型的名稱。
這個程序的輸出與未使用函數的版本相同,但組織更爲有序。完成大部分工作的代碼都移到了兩個函數中,讓主程序更容易理解。只要看看主程序,你就知道這個程序的功能容
易看清得多:
unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron']
completed_models = []
print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)
我們創建了一個未打印的設計列表,還創建了一個空列表,用於存儲打印好的模型。接下來,由於我們已經定義了兩個函數,因此只需調用它們並傳入正確的實參即可。我們調
print_models() 並向它傳遞兩個列表;像預期的一樣,print_models() 模擬打印設計的過程。接下來,我們調用show_completed_models() ,並將打印好的模型
列表傳遞給它,讓其能夠指出打印了哪些模型。描述性的函數名讓別人閱讀這些代碼時也能明白,雖然其中沒有任何註釋。
相比於沒有使用函數的版本,這個程序更容易擴展和維護。如果以後需要打印其他設計,只需再次調用print_models() 即可。如果我們發現需要對打印代碼進行修改,只需
修改這些代碼一次,就能影響所有調用該函數的地方;與必須分別修改程序的多個地方相比,這種修改的效率更高。
這個程序還演示了這樣一種理念,即每個函數都應只負責一項具體的工作。第一個函數打印每個設計,而第二個顯示打印好的模型;這優於使用一個函數來完成兩項工作。編寫
函數時,如果你發現它執行的任務太多,請嘗試將這些代碼劃分到兩個函數中。別忘了,總是可以在一個函數中調用另一個函數,這有助於將複雜的任務劃分成一系列的步驟。
8.4.2 禁止函數修改列表  #關於函數傳遞列表的方式 https://blog.csdn.net/nyist_yangguang/article/details/106783134 
有時候,需要禁止函數修改列表。例如,假設像前一個示例那樣,你有一個未打印的設計列表,並編寫了一個將這些設計移到打印好的模型列表中的函數。你可能會做出這樣的
決定:即便打印所有設計後,也要保留原來的未打印的設計列表,以供備案。但由於你將所有的設計都移出了unprinted_designs ,這個列表變成了空的,原來的列表沒有
了。爲解決這個問題,可向函數傳遞列表的副本而不是原件;這樣函數所做的任何修改都隻影響副本,而絲毫不影響原件。
要將列表的副本傳遞給函數,可以像下面這樣做:
function_name(list_name[:])
切片表示法[:] 創建列表的副本。print_models.py中,如果不想清空未打印的設計列表,可像下面這樣調用print_models()
print_models(unprinted_designs[:], completed_models)
這樣函數print_models() 依然能夠完成其工作,因爲它獲得了所有未打印的設計的名稱,但它使用的是列表unprinted_designs 的副本,而不是列
表unprinted_designs 本身。像以前一樣,列表completed_models 也將包含打印好的模型的名稱,但函數所做的修改不會影響到列表unprinted_designs
雖然向函數傳遞列表的副本可保留原始列表的內容,但除非有充分的理由需要傳遞副本,否則還是應該將原始列表傳遞給函數,因爲讓函數使用現成列表可避免花時間和內存創
建副本,從而提高效率,在處理大型列表時尤其如此。
動手
手試
試一
一試
8-9
魔術
術師
師 :創建一個包含魔術師名字的列表,並將其傳遞給一個名爲show_magicians() 的函數,這個函數打印列表中每個魔術師的名字。
8-10
了不
不起
起的
的魔
魔術
術師
師 :在你爲完成練習8-9而編寫的程序中,編寫一個名爲make_great() 的函數,對魔術師列表進行修改,在每個魔術師的名字中都加入字樣“the
Great”。調用函數show_magicians() ,確認魔術師列表確實變了。
8-11
不變
變的
的魔
魔術
術師
師 :修改你爲完成練習8-10而編寫的程序,在調用函數make_great() 時,向它傳遞魔術師列表的副本。由於不想修改原始列表,請返回修改後的
列表,並將其存儲到另一個列表中。分別使用這兩個列表來調用show_magicians() ,確認一個列表包含的是原來的魔術師名字,而另一個列表包含的是添加了字
“the Great”的魔術師名字。
8.5 傳遞任意數量的實參
有時候,你預先不知道函數需要接受多少個實參,好在Python允許函數從調用語句中收集任意數量的實參。
例如,來看一個製作比薩的函數,它需要接受很多配料,但你無法預先確定顧客要多少種配料。下面的函數只有一個形參*toppings ,但不管調用語句提供了多少實參,這個
形參都將它們統統收入囊中:
pizza.py
def make_pizza(*toppings):
"""打印顧客點的所有配料"""
print(toppings)
make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
形參名*toppings 中的星號讓Python創建一個名爲toppings 的空元組,並將收到的所有值都封裝到這個元組中。函數體內的print 語句通過生成輸出來證明Python能夠處理
使用一個值調用函數的情形,也能處理使用三個值來調用函數的情形。它以類似的方式處理不同的調用,注意,Python將實參封裝到一個元組中,即便函數只收到一個值也如此:
('pepperoni',)
('mushrooms', 'green peppers', 'extra cheese')
現在,我們可以將這條print 語句替換爲一個循環,對配料列表進行遍歷,並對顧客點的比薩進行描述:
def make_pizza(*toppings):
"""概述要製作的比薩"""
print("\nMaking a pizza with the following toppings:")
for topping in toppings:
print("- " + topping)
make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
不管收到的是一個值還是三個值,這個函數都能妥善地處理:
Making a pizza with the following toppings:
- pepperoni
Making a pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese
不管函數收到的實參是多少個,這種語法都管用。
8.5.1 結合使用位置實參和任意數量實參
如果要讓函數接受不同類型的實參,必須在函數定義中將接納任意數量實參的形參放在最後。Python先匹配位置實參和關鍵字實參,再將餘下的實參都收集到最後一個形參中。
例如,如果前面的函數還需要一個表示比薩尺寸的實參,必須將該形參放在形參*toppings 的前面:
def make_pizza(size, *toppings):
"""概述要製作的比薩"""
print("\nMaking a " + str(size) +
"-inch pizza with the following toppings:")
for topping in toppings:
print("- " + topping)
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
基於上述函數定義,Python將收到的第一個值存儲在形參size 中,並將其他的所有值都存儲在元組toppings 中。在函數調用中,首先指定表示比薩尺寸的實參,然後根據需要
指定任意數量的配料。
現在,每個比薩都有了尺寸和一系列配料,這些信息按正確的順序打印出來了——首先是尺寸,然後是配料:
Making a 16-inch pizza with the following toppings:
- pepperoni
Making a 12-inch pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese
8.5.2 使用任意數量的關鍵字實參
有時候,需要接受任意數量的實參,但預先不知道傳遞給函數的會是什麼樣的信息。在這種情況下,可將函數編寫成能夠接受任意數量的鍵值對——調用語句提供了多少就接
受多少。一個這樣的示例是創建用戶簡介:你知道你將收到有關用戶的信息,但不確定會是什麼樣的信息。在下面的示例中,函數build_profile() 接受名和姓,同時還接受
任意數量的關鍵字實參:
user_profile.py
def build_profile(first, last, **user_info):
"""創建一個字典,其中包含我們知道的有關用戶的一切"""
profile = {}
profile['first_name'] = first
profile['last_name'] = last
for key, value in user_info.items():
profile[key] = value
return profile
user_profile = build_profile('albert', 'einstein',
location='princeton',
field='physics')
print(user_profile)
函數build_profile() 的定義要求提供名和姓,同時允許用戶根據需要提供任意數量的名稱值對。形參**user_info 中的兩個星號讓Python創建一個名爲user_info
空字典,並將收到的所有名稱值對都封裝到這個字典中。在這個函數中,可以像訪問其他字典那樣訪問user_info 中的名稱值對。
build_profile() 的函數體內,我們創建了一個名爲profile 的空字典,用於存儲用戶簡介。在處,我們將名和姓加入到這個字典中,因爲我們總是會從用戶那裏收到
這兩項信息。在處,我們遍歷字典user_info 中的鍵值對,並將每個鍵值對都加入到字典profile 中。最後,我們將字典profile 返回給函數調用行。
我們調用build_profile() ,向它傳遞名('albert' )、姓('einstein' )和兩個鍵值對(location='princeton' field='physics' ),並將返回
profile 存儲在變量user_profile 中,再打印這個變量:
{'first_name': 'albert', 'last_name': 'einstein',
'location': 'princeton', 'field': 'physics'}
在這裏,返回的字典包含用戶的名和姓,還有求學的地方和所學專業。調用這個函數時,不管額外提供了多少個鍵值對,它都能正確地處理。
編寫函數時,你可以以各種方式混合使用位置實參、關鍵字實參和任意數量的實參。知道這些實參類型大有裨益,因爲閱讀別人編寫的代碼時經常會見到它們。要正確地使用這
些類型的實參並知道它們的使用時機,需要經過一定的練習。就目前而言,牢記使用最簡單的方法來完成任務就好了。你繼續往下閱讀,就會知道在各種情況下哪種方法的效率
是最高的。
動手試一試
 
8-12
三明
明治
治 :編寫一個函數,它接受顧客要在三明治中添加的一系列食材。這個函數只有一個形參(它收集函數調用中提供的所有食材),並打印一條消息,對顧客
點的三明治進行概述。調用這個函數三次,每次都提供不同數量的實參。
8-13
用戶
戶簡
簡介
介 :複製前面的程序user_profile.py,在其中調用build_profile() 來創建有關你的簡介;調用這個函數時,指定你的名和姓,以及三個描述你的鍵-
對。
8-14
汽車
車 :編寫一個函數,將一輛汽車的信息存儲在一個字典中。這個函數總是接受制造商和型號,還接受任意數量的關鍵字實參。這樣調用這個函數:提供必不可
少的信息,以及兩個名稱值對,如顏色和選裝配件。這個函數必須能夠像下面這樣進行調用:
car = make_car('subaru', 'outback', color='blue', tow_package=True)
打印返回的字典,確認正確地處理了所有的信息。
8.6 將函數存儲在模塊中
函數的優點之一是,使用它們可將代碼塊與主程序分離。通過給函數指定描述性名稱,可讓主程序容易理解得多。你還可以更進一步,將函數存儲在被稱爲模塊 的獨立文件中,
再將模塊導入 到主程序中import 語句允許在當前運行的程序文件中使用模塊中的代碼。
通過將函數存儲在獨立的文件中,可隱藏程序代碼的細節,將重點放在程序的高層邏輯上。這還能讓你在衆多不同的程序中重用函數。將函數存儲在獨立文件中後,可與其他程
序員共享這些文件而不是整個程序。知道如何導入函數還能讓你使用其他程序員編寫的函數庫。
導入模塊的方法有多種,下面對每種都作簡要的介紹。
8.6.1 導入整個模塊
要讓函數是可導入的,得先創建模塊塊 是擴展名爲.py的文件,
包含要導入到程序中的代碼。下面來創建一個包含函數make_pizza() 的模塊。爲此,我們將文件pizza.py
除函數make_pizza() 之外的其他代碼都刪除:
pizza.py
def make_pizza(size, *toppings):
"""概述要製作的比薩"""
print("\nMaking a " + str(size) +
"-inch pizza with the following toppings:")
for topping in toppings:
print("- " + topping)
接下來,我們在pizza.py所在的目錄中創建另一個名爲making_pizzas.py的文件,這個文件導入剛創建的模塊,再調用make_pizza() 兩次:
making_pizzas.py
import pizza
pizza.make_pizza(16, 'pepperoni')
pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
Python讀取這個文件時,代碼行import pizza Python打開文件pizza.py,並將其中的所有函數都複製到這個程序中。你看不到複製的代碼,因爲這個程序運行時,Python在幕
後複製這些代碼。你只需知道,在making_pizzas.py中,可以使用pizza.py中定義的所有函數。
要調用被導入的模塊中的函數,可指定導入的模塊的名稱pizza 和函數名make_pizza() ,並用句點分隔它們(見)。這些代碼的輸出與沒有導入模塊的原始程序相同:
Making a 16-inch pizza with the following toppings:
- pepperoniMaking a 12-inch pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese
這就是一種導入方法:只需編寫一條import 語句並在其中指定模塊名,就可在程序中使用該模塊中的所有函數。如果你使用這種import 語句導入了名爲module_name.py
的整個模塊,就可使用下面的語法來使用其中任何一個函數:
module_name.function_name()
8.6.2 導入特定的函數
你還可以導入模塊中的特定函數,這種導入方法的語法如下:
from module_name import function_name
通過用逗號分隔函數名,可根據需要從模塊中導入任意數量的函數:
from module_name import function_0, function_1, function_2
對於前面的making_pizzas.py示例,如果只想導入要使用的函數,代碼將類似於下面這樣:
from pizza import make_pizza
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
若使用這種語法,調用函數時就無需使用句點。由於我們在import 語句中顯式地導入了函數make_pizza() ,因此調用它時只需指定其名稱。
8.6.3 使用as給函數指定別名
如果要導入的函數的名稱可能與程序中現有的名稱衝突,或者函數的名稱太長,可指定簡短而獨一無二的別
——函數的另一個名稱,類似於外號。要給函數指定這種特殊外
號,需要在導入它時這樣做。
下面給函數make_pizza() 指定了別名mp() 。這是在import 語句中使用make_pizza as mp 實現的,關鍵字as 將函數重命名爲你提供的別名:
from pizza import make_pizza as mp
mp(16, 'pepperoni')
mp(12, 'mushrooms', 'green peppers', 'extra cheese')
上面的import 語句將函數make_pizza() 重命名爲mp() ;在這個程序中,每當需要調用make_pizza() 時,都可簡寫成mp() ,而Python將運行make_pizza() 中的代
碼,這可避免與這個程序可能包含的函數make_pizza() 混淆。
指定別名的通用語法如下:
from module_name import function_name as fn
8.6.4 使用as給模塊指定別名
你還可以給模塊指定別名。通過給模塊指定簡短的別名(如給模塊pizza 指定別名p ),讓你能夠更輕鬆地調用模塊中的函數。相比於pizza.make_pizza()
p.make_pizza() 更爲簡潔:
import pizza as p
p.make_pizza(16, 'pepperoni')
p.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
上述import 語句給模塊pizza 指定了別名p ,但該模塊中所有函數的名稱都沒變。調用函數make_pizza() 時,可編寫代碼p.make_pizza() 而不
pizza.make_pizza() 這樣不僅能使代碼更簡潔,還可以讓你不再關注模塊名,而專注於描述性的函數名。這些函數名明確地指出了函數的功能,對理解代碼而言,它們
比模塊名更重要。
給模塊指定別名的通用語法如下:
import module_name as mn
8.6.5 導入模塊中的所有函數
使用星號(* )運算符可讓Python導入模塊中的所有函數:from pizza import *
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
import 語句中的星號讓Python將模塊pizza 中的每個函數都複製到這個程序文件中。由於導入了每個函數,可通過名稱來調用每個函數,而無需使用句點表示法。然而,使用
並非自己編寫的大型模塊時,最好不要採用這種導入方法:如果模塊中有函數的名稱與你的項目中使用的名稱相同,可能導致意想不到的結果:Python可能遇到多個名稱相同的函
數或變量,進而覆蓋函數,而不是分別導入所有的函數。
最佳的做法是,要麼只導入你需要使用的函數,要麼導入整個模塊並使用句點表示法。這能讓代碼更清晰,更容易閱讀和理解。這裏之所以介紹這種導入方法,只是想讓你在閱
讀別人編寫的代碼時,如果遇到類似於下面的import 語句,能夠理解它們:
from module_name import *
8.7 函數編寫指南
編寫函數時,需要牢記幾個細節應給函數指定描述性名稱,且只在其中使用小寫字母和下劃線描述性名稱可幫助你和別人明白代碼想要做什麼。給模塊命名時也應遵循上述
約定。
每個函數都應包含簡要地闡述其功能的註釋,該註釋應緊跟在函數定義後面,並採用文檔字符串格式。文檔良好的函數讓其他程序員只需閱讀文檔字符串中的描述就能夠使用
它:他們完全可以相信代碼如描述的那樣運行;只要知道函數的名稱、需要的實參以及返回值的類型,就能在自己的程序中使用它。
給形參指定默認值時,等號兩邊不要有空格:
def function_name(parameter_0, parameter_1='default value')
對於函數調用中的關鍵字實參,也應遵循這種約定:
function_name(value_0, parameter_1='value')
PEP 8https://www.python.org/dev/peps/pep-0008/ )建議代碼行的長度不要超過79字符,這樣只要編輯器窗口適中,就能看到整行代碼。如果形參很多,導致函數定義的長度超過了
79字符,可在函數定義中輸入左括號後按回車鍵,並在下一行按兩次Tab鍵,從而將形參列表和只縮進一層的函數體區分開來。
大多數編輯器都會自動對齊後續參數列表行,使其縮進程度與你給第一個參數列表行指定的縮進程度相同:
def function_name(
         parameter_0, parameter_1, parameter_2,
         parameter_3, parameter_4, parameter_5):
    function body...
如果程序或模塊包含多個函數,可使用兩個空行將相鄰的函數分開,這樣將更容易知道前一個函數在什麼地方結束,下一個函數從什麼地方開始。
所有的import 語句都應放在文件開頭,唯一例外的情形是,在文件開頭使用了註釋來描述整個程序。
動手試一試
 
8-15
打印
印模
模型
型 :將示例print_models.py中的函數放在另一個名爲printing_functions.py的文件中;在print_models.py的開頭編寫一條import 語句,並修改這個文件以使用導
入的函數。
8-16
導入
入 :選擇一個你編寫的且只包含一個函數的程序,並將這個函數放在另一個文件中。在主程序文件中,使用下述各種方法導入這個函數,再調用它:
import module_name
from module_name import function_name 
from module_name import function_name as fn #as 給函數指定別名
import module_name as mn  #as給模塊指定別名
from module_name import *
8-17
函數
數編
編寫
寫指
指南
南 :選擇你在本章中編寫的三個程序,確保它們遵循了本節介紹的函數編寫指南。
8.8 小結
在本章中,你學習了:如何編寫函數,以及如何傳遞實參,讓函數能夠訪問完成其工作所需的信息;如何使用位置實參和關鍵字實參,以及如何接受任意數量的實參;顯示輸出
的函數和返回值的函數;如何將函數同列表、字典、if 語句和while 循環結合起來使用。你還知道了如何將函數存儲在被稱爲模
模塊
塊 的獨立文件中,讓程序文件更簡單、更易於
理解。最後,你學習了函數編寫指南,遵循這些指南可讓程序始終結構良好,並對你和其他人來說易於閱讀。
程序員的目標之一是,編寫簡單的代碼來完成任務,而函數有助於你實現這樣的目標。它們讓你編寫好代碼塊並確定其能夠正確運行後,就可置之不理。確定函數能夠正確地完
成其工作後,你就可以接着投身於下一個編碼任務。
函數讓你編寫代碼一次後,想重用它們多少次就重用多少次。需要運行函數中的代碼時,只需編寫一行函數調用代碼,就可讓函數完成其工作。需要修改函數的行爲時,只需修
改一個代碼塊,而所做的修改將影響調用這個函數的每個地方。
使用函數讓程序更容易閱讀,而良好的函數名概述了程序各個部分的作用。相對於閱讀一系列的代碼塊,閱讀一系列函數調用讓你能夠更快地明白程序的作用。
函數還讓代碼更容易測試和調試。如果程序使用一系列的函數來完成其任務,而其中的每個函數都完成一項具體的工作,測試和維護起來將容易得多:你可編寫分別調用每個函
數的程序,並測試每個函數是否在它可能遇到的各種情形下都能正確地運行。經過這樣的測試後你就能信心滿滿,深信你每次調用這些函數時,它們都將正確地運行。
在第9章,你將學習編寫類。類將函數和數據整潔地封裝起來,讓你能夠靈活而高效地使用它們
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章