2020年9月22日星期二

008 Python编程 從入門到實踐 第一部分 基礎知識 第 8 章 函數

 申明本站飛宇網 https://feiyetopro.blogspot.com/自網路收集整理之書籍文章影音僅供預覽交流學習研究,其[書籍、文章、影音]情節內容, 評論屬其個人行為, 與本網站無關。版權歸原作者和出版社所有,請在下載 24 小時內刪除,不得用作商業用途;如果您喜歡其作品,請支持訂閱購買[正版]謝謝!




8 章 函數

在本章中,你將學習編寫函數 。函數是帶名字的代碼塊,用於完成具體的工作。

要執行函式定義的特定任務,可調用該函數。需要在程式中多次執行同一項任務時,你無需反復編寫完成該任務的代碼,而只需調用執行該任務的函數,讓Python運行其中的代碼。你將發現,通過使用函數,程式的編寫、閱讀、測試和修復都將更容易。

在本章中,你還會學習向函數傳遞資訊的方式。你將學習如何編寫主要任務是顯示資訊的函數,還有用於處理資料並返回一個或一組值的函數。最後,你將學習如何將函數存儲在被稱為模組 的獨立檔中,讓主程序檔的組織更為有序。

8.1 定義函數

下面是一個列印問候語的簡單函數,名為greet_user() 

greeter.py

 

def greet_user():

     """顯示簡單的問候語"""

     print("Hello!")

 

greet_user()

 

 

 

 

 

 

 

 

這個示例演示了最簡單的函數結構。處的代碼行使用關鍵字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 case

 

The 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 禁止函數修改清單

有時候,需要禁止函數修改清單。例如,假設像前一個示例那樣,你有一個未列印的設計列表,並編寫了一個將這些設計移到列印好的模型清單中的函數。你可能會做出這樣的決定:即便列印所有設計後,也要保留原來的未列印的設計列表,以供備案。但由於你將所有的設計都移出了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:

- pepperoni

 

Making 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

import module_name as mn

from module_name import *

 

 

 

 

 

 

 

 

8-17 函數編寫指南 :選擇你在本章中編寫的三個程式,確保它們遵循了本節介紹的函數編寫指南。

8.8 小結

在本章中,你學習了:如何編寫函數,以及如何傳遞實參,讓函數能夠訪問完成其工作所需的資訊;如何使用位置實參和關鍵字實參,以及如何接受任意數量的實參;顯示輸出的函數和返回值的函數;如何將函數同清單、字典、if 語句和while 迴圈結合起來使用。你還知道了如何將函數存儲在被稱為模組 的獨立檔中,讓程式檔更簡單、更易於理解。最後,你學習了函數編寫指南,遵循這些指南可讓程式始終結構良好,並對你和其他人來說易於閱讀。

程式師的目標之一是,編寫簡單的代碼來完成任務,而函數有助於你實現這樣的目標。它們讓你編寫好代碼塊並確定其能夠正確運行後,就可置之不理。確定函數能夠正確地完成其工作後,你就可以接著投身於下一個編碼任務。

函數讓你編寫代碼一次後,想重用它們多少次就重用多少次。需要運行函數中的代碼時,只需編寫一行函式呼叫代碼,就可讓函數完成其工作。需要修改函數的行為時,只需修改一個代碼塊,而所做的修改將影響調用這個函數的每個地方。

使用函數讓程式更容易閱讀,而良好的函數名概述了程式各個部分的作用。相對於閱讀一系列的代碼塊,閱讀一系列函式呼叫讓你能夠更快地明白程式的作用。

函數還讓代碼更容易測試和調試。如果程式使用一系列的函數來完成其任務,而其中的每個函數都完成一項具體的工作,測試和維護起來將容易得多:你可編寫分別調用每個函數的程式,並測試每個函數是否在它可能遇到的各種情形下都能正確地運行。經過這樣的測試後你就能信心滿滿,深信你每次調用這些函數時,它們都將正確地運行。

在第9章,你將學習編寫類。類將函數和資料整潔地封裝起來,讓你能夠靈活而高效地使用它們。

0 留言:

發佈留言