申明: 本站飛宇網 https://feiyetopro.blogspot.com/。自網路收集整理之書籍、文章、影音僅供預覽交流學習研究,其[書籍、文章、影音]情節內容, 評論屬其個人行為, 與本網站無關。版權歸原作者和出版社所有,請在下載 24 小時內刪除,不得用作商業用途;如果您喜歡其作品,請支持訂閱購買[正版]。謝謝!
第 15 章 模組
這是討論收集方式的最後一章。前面已經瞭解了清單、函數和物件,這一章中我們將學習模組。下一章中,我們將使用一個名為 Pygame 的模組開始畫一些圖形。
15.1 什麼是模組
模組就是某個東西的一部分。如果一個東西可以分為幾部分,或者你可以很容易地把它分解成多個不同部分,我們就說這個東西是模組化的。樂高(LEGO)積木可能就是模組化最好的例子。可以拿一堆不同的積木,用它們搭建不同的東西。
在 Python 中,-模-塊(module)-是-包-含在一個更大程式中的類似的部分。每個-模組或部分都是硬碟上的一個單獨的檔。可以把一個大程式分解為多個模組或檔。或者也可以反過來,從一個小的模組開始,逐漸增加其他部分來建立一個大程式。
15.2 為什麼使用模組
為什麼要那麼麻煩地把程式分解為較小的部分呢?要知道我們需要所有這些部分才能讓程式正常工作。為什麼不直接把所有內容都放在一個大檔中呢?
原因有幾個。
· 這樣做檔會更小,因而就能更容易地查找代碼。
· 一旦創建模組,這個模組就能在很多程式中使用。這樣下一次需要相同的功能時就不必再從頭開始了。
· 並不是所有模組都要使用。模組化意味著你可以使用各部分的不同組合來完成不同的任務,就像利用同樣的一組樂高積木可以搭建不同的東西一樣。
15.3 積木桶
在關於函數的第 13 章中,我們說過函數就像積木,那麼模組可以認為是一桶積-木。根據需要,你可以從一個桶中取很多或者很少的積木,也可以有很多桶不同的-積木。也許有一桶正方形積木,一桶長方形積木,還有一桶奇形怪狀的積木。程式師-通常也採用這種方法來使用模組,也就是說,他們會把類似的函數收集在一個模組-中。或者他們也有可能把一個專案需要的所有函數收集在一個模組中,就像你會把-搭城堡需要的所有積木都放在一個桶中一樣。
15.4 如何創建模組
下麵來創建模組。模組就是一個 Python 檔,類似代碼清單 15-1 中給出的檔。在一個 IDLE 編輯器視窗中鍵入代碼清單 15-1 中的代碼,把它保存為 my_module.py。
代碼清單 15-1 創建一個模組
#
this is the file "my_module.py"
#
we're going to use it in another program
def
c_to_f(celsius):
fahrenheit = celsius * 9.0 / 5 + 32
return fahrenheit
就這麼簡單!這樣就創建了一個模組!模組中只有一個函數,也就是 c_to_f() 函數,它會把溫度從攝氏度轉換為華氏度。
接下來我們在另一個程式中使用 my_module.py。
15.5 如何使用模組
要使用模組中的某個函數,首先必須告訴 Python 我們想要使用哪些模組。在程式中包含其他模組的 Python 關鍵字是 import。可以這樣使用:
import
my_module
下面寫一個程式來使用我們剛才編寫的模組,這裡我們想用 c_to_f() 函數完成溫度轉換。
前面已經瞭解了如何使用函數並向它傳遞參數。這裡惟一不同的是,函數與主程序不在同一個檔中,而在另外的一個單獨的檔中,所以必須使用 import。代碼清單 15-2 中的程式使用了我們剛才編寫的模組 my_module.py。
代碼清單 15-2 使用模組
創建一個新的 IDLE 編輯器視窗,鍵入這個程式。保存為 modular.py,然後運行這個程式,看看會發生什麼。需要把它保存到 my_module.py 所在的同一個資料夾(或目錄)下。
能正常工作嗎?應該會看到類似下面的結果:
>>>
========================= RESTART =========================
>>>
Enter
a temperature in Celsius: 34
Traceback
(most recent call last):
File
"C:/MyPythonPrograms/modular.py", line 4, in <module>
fahrenheit = c_to_f(celsius)
NameError:
name 'c_to_f' is not defined
程式不能正常工作!怎麼回事?錯誤消息指出函數 c_to_f() 沒有定義。不過我們很清楚前面已經在 my_module 中定義了這個函數,而且我們確實已經導入了這個模組。
出現這個問題的原因是,在 Python 中指定在其他模組中定義的函數時必須更加具體。解決這個問題的一種方法是把這一行代碼
fahrenheit
= c_to_f(celsius)
改為
fahrenheit
= my_module.c_to_f(celsius)
現在我們向 Python 特別指出:c_to_f() 函數在 my_module 模組中。做了這個修改後再試著運行程式,看看能不能正常工作。
15.6 命名空間
卡特提到的內容與命名空間(namespace)概念有關。這個話題有點複雜,不過確實需要知道,所以現在就來討論這個概念。
什麼是命名空間
假設在你們學校,你在 Morton 老師的班裡,班裡有個學生名叫 Shawn。現在 Wheeler 老師教的那個班也有一個名叫 Shawn 的學生。如果你在自己的班裡說“Shawn 有一個新書包”時,你們班的所有人都會知道(或者至少他們會認為),你指的是你們班的 Shawn。如果你想說另外那個班的 Shawn 就會說“Wheeler 老師班裡的 Shawn”或者“另外那個 Shawn”,或者其他類似的說法。
你們班裡只有一個 Shawn,所以你說 Shawn 時,同班的同學就會知道你說的是哪個人。換種說法來講,在你們班的這個空間裡,只有一個名字 Shawn。你們班就是你的命名空間,在這個命名空間裡只有一個 Shawn,所以不會有混淆。
現在,如果校長必須通過學校的廣播系統把 Shawn 叫到辦公室,她不會說“請 Shawn 到辦公室來一趟”。如果她這樣做,兩個 Shawn 都會出現在他的辦公室。對於使用廣播系統的校長來說,命名空間是整個學校。這說明,學校的每一個人都會聽到這個名字,而不只是一個班的同學。所以她必須更明確地指出她指的是哪一個 Shawn。她必須這樣說:“請 Morton 老師班裡的 Shawn 到辦公室來一趟。”
校長還可以用另一種方法找 Shawn,就是走到你們班門口說:“Shawn,請跟我來。”這裡只有一個 Shawn 聽到,所以校長能找到真正要找的那個 Shawn。在這種情況下,命名空間就只是一個教室,而不是整個學校。
一般來講,程式師把較小的命名空間(比如你的教室)稱作局部命名空間,而較大的命名空間(如整個學校)稱為全域命名空間。
導入命名空間
下面假設你們學校(John Young 學校)根本沒有一個名叫 Fred 的人。如果校長通過廣播系統想找 Fred,她肯定找不到這個人。現在假設與你們學校同在一條街上的另一個學校(Stephen Leacock 學校)正在進行部分校舍維修,這個學校把一個班級臨時搬到你們學校的活動房裡上課。在這個班裡,恰好有一個學生名叫 Fred。不過這個活動房還沒有連上廣播系統。如果校長找 Fred,肯定還是找不到。但是,如果她把這個新的活動房連入廣播系統,然後再找 Fred,就會找到 Stephen Leacock 學校的 Fred。
連接另一個學校的活動房屋,這在 Python 中就像導入一個模組。導入了模組,就可以訪問這個模組中的所有名字,包括所有變數、函數以及物件。
導入模組的含義與導入一個命名空間是一樣的。導入模組時,就導入了命名空間。
導入命名空間(模組)有兩種方法。可以這樣做:
import
StephenLeacock
如果這樣做,StephenLeacock 仍然是一個單獨的命名空間。你可以訪問這個命名空間,但是在使用之前必須明確地指定想要哪一個命名空間。所以校長必須這樣做:
call_to_office(StephenLeacock.Fred)
如果校長想找到 Fred,除了名字(Fred)外,她還必須給出命名空間(Stephen
Leacock)。在前面的溫度轉換程式中就是這樣做的。
為了讓這個程式正常工作,我們寫了這樣一行代碼:
fahrenheit
= my_module.c_to_f(celsius)
這裡指定了命名空間(my_module)以及函數名(c_to_f)。
用 from 導入
導入命名空間的另一種方法是:
from
StephenLeacock import Fred
如果校長這樣做,會把 StephenLeacock 的名字 Fred 包含到她的命名空間中,現在就可以這樣找到 Fred:
call_to_office(Fred)
因為 Fred 現在就在校長的命名空間中,所以她不必再去 StephenLeacock 命名空間找 Fred。
在這個例子中,校長只是從 StephenLeacock 把名字 Fred 導入她的局部命名空間中。如果她想導入所有人,可以這樣做:
from
StephenLeacock import *
在這裡,星號(*)表示全部。不過她必須當心,如果 Stephen Leacock 學校與 John Young 學校有同名的學生,就會出現混亂了。
太難了
到目前為止,你可能對命名空間的概念還是不太清楚。不用擔心!通過完成後面幾章的例子,你會越來越明白。後面需要導入模組時,我都會清楚地解釋要做什麼。
15.7 標準模組
我們已經知道了如何創建和使用模組,是不是總是必須編寫我們自己的模組?並不是這樣!這正是 Python 的妙處之一。
Python 提供了大量標準模組,可以用來完成很多工作,比如查找檔、報時(或計時)、生成亂數,以及很多其他功能。有時,人們說 Python“配有電池”,指的就是 Python 的所有標準模組。這稱為 Python 標準庫。
為什麼這些內容必須放在單獨的模組中呢?嗯,不是非得這樣,不過設計 Python 的人認為這樣會更高效。否則,每個 Python 程式都必須包含所有可能用到的函數。通過建立單獨的模組,就只需包含你真正需要的那些函數。
當然,有些內容(如 print、for 和 if-else)是 Python 的基本命令,所以這些基本命令不需要一個單獨的模組,它們都在 Python 的主要部分中。
如果 Python 沒有提供合適的模組來完成你想做的工作(如建立一個圖形遊戲),可以下載另外一些外掛程式模組,它們通常都是免費的!我們在這本書裡就包含了一些這樣的外掛程式模組,如果使用這本書網站上的安裝程式,就會同時安裝這些模組。或者,你也完全可以單獨安裝。下面來看幾個標準模組。
time
利用 time 模組,能夠獲取你的電腦時鐘的資訊,如日期和時間。還可以利用它為程式增加延遲。(有時電腦動 作太快,你必須讓它慢下來。)
time 模組中的 sleep() 函數可以用來增加一個延遲,也就是說,可以讓程式等待一段時間,什麼也不做。這就像讓你的程式睡眠,正是這個原因,這個函數名叫 sleep()。可以告訴它你要它睡多長時間(多少秒)。
代碼清單 15-3 中的程式展示了 sleep() 函數如何工作。鍵入這個程式,保存並運行,看看會發生什麼。
代碼清單 15-3 讓程式睡眠
import
time
print
"How",
time.sleep(2)
print
"are",
time.sleep(2)
print
"you",
time.sleep(2)
print
"today?"
要注意,調用 sleep() 函數時,必須在前面加上 time.。這是因為,儘管我們已經用 import 導入了 time,但是並沒有讓它成為主程式命名空間的一部分。所以每次想要使用 sleep() 函數時,都必須調用 time.sleep()。
如果試圖這樣做:
import
time
sleep(5)
這是不行的,因為 sleep() 並不在我們的命名空間中。我們會得到這樣一條錯誤消息:
NameError:
name 'sleep' is not defined
不過如果這樣導入:
from
time import sleep
就會告訴 Python,“在 time 模組中尋找名為 sleep 的變數(或者函數或物件),把它包含到我的命名空間中。”現在就可以直接使用 sleep() 函數,而不需要再在前面加上 time. 了:
from
time import sleep
print
'Hello, talk to you again in 5 seconds...'
sleep(5)
print
'Hi again'
如果想要得到這種將名字導入局部命名空間帶來的方便(這樣就無需每次都指定模組名),但是又不知道需要模組中的哪些名字,就可以使用星號(*)把所有名字都導入到我們的命名空間裡:
from
time import *
* 表示“全部”,這樣就會從模組導入所有可用的名字。使用這個命令必須特別當心。如果在我們的程式中創建了一個名字,而它與 time 模組中的一個名字相同,就會出現衝突。用 * 導入所有名字不是最佳做法,最好只導入你真正需要的部分。
還記得第 8 章代碼清單 8-6 中的倒計時程式嗎?現在你應該知道那個程式中 time.sleep(1) 的作用了吧。
亂數
random 模組用於生成亂數。這在遊戲和模擬中非常有用。
下面試著在交互模式中使用 random 模組:
>>>
import random
>>>
print random.randint(0, 100)
4
>>>
print random.randint(0, 100)
72
每次使用 random.randint() 時,會得到一個新的隨機整數。由於我們為它傳遞的參數是 0 和 100,所以得到的整數會介於 0 到 100 之間。我們在第 1 章的猜數程式中就是使用 random.randint() 來創建秘密數。
如果你想得到一個隨機的小數,可以使用 random.random()。不用在括弧裡放任何參數,因為 random.random() 總是會提供一個介於 0 到 1 之間的數:
>>>
print random.random()
0.270985467261
>>>
print random.random()
0.569236541309
如果你想得到其他範圍內的一個亂數,比如說 0 到 10 之間,只需要將結果乘 以 10:
>>>
print random.random() * 10
3.61204895736
>>>
print random.random() * 10
8.10985427783
你學到了什麼
在這一章,你學到了以下內容。
·
什麼是模組。
·
如何創建模組。
·
如何在另一個程式中使用模組。
·
什麼是命名空間。
·
局部和全域命名空間和變數是什麼意思。
·
如何把其他模組中的名字包含到你的命名空間中。
另外你還瞭解了幾個 Python 標準模組的例子。
測試題
1. 使用模組有哪些好處?
2. 如何創建模組?
3. 使用模組時所用的 Python 關鍵字是什麼?
4. 導入模組等同於導入一個__________________。
5. 要導入 time 模組從而能訪問這個模組中的所有名字(也就是所有變數、函數和物件),有哪兩種方法?
動手試一試
1. 編寫一個模組,包含第 13 章“動手試一試”中的“用大寫字母列印名字”函數。然後編寫一個程式導入這個模組,並調用這個函數。
2. 修改代碼清單 15-2 中的代碼,把 c_to_f() 包含到主程序的命名空間裡。也就是說,修改這個代碼,從而可以寫:
fahrenheit
= c_to_f(celsius)
而不是
fahrenheit
= my_module.c_to_f(celsius)
3. 編寫一個小程式,生成 1 到 20 之間的 5 個隨機整數的清單,並列印出來。
4. 編寫一個小程式,要求它工作 30 秒,每 3 秒列印一個隨機小數。
大澳飛宇註:
桑德編著的《父與子的編程之旅(與小卡特一起學Python)》是一本家長與孩子共同學習編程的入門書。作者是一對父子,他們以Python語言為例,詳盡細致地介紹了Python如何安裝、字符串和操作符等程序設計的基本概念,介紹了條件語句、函數、模塊等進階內容,最後講解了用Python實現游戲編程。
書中的語言生動活潑,敘述簡單明了。為了讓學習者覺得編程有趣,本書編排了很多卡通人物及場景對話,讓學習者在輕松愉快之中跨入計算機編程的大門。本書適合中小學生以及一切編程初學者。










0 留言:
發佈留言