新聞中心
你是否對(duì)函數(shù)、類、方法、庫(kù)和模塊等花哨的編程術(shù)語(yǔ)感到困惑?你是否在與變量作用域斗爭(zhēng)?無(wú)論你是自學(xué)成才的還是經(jīng)過(guò)正式培訓(xùn)的程序員,代碼的模塊化都會(huì)令人困惑。但是類和庫(kù)鼓勵(lì)模塊化代碼,因?yàn)槟K化代碼意味著只需構(gòu)建一個(gè)多用途代碼塊集合,就可以在許多項(xiàng)目中使用它們來(lái)減少編碼工作量。換句話說(shuō),如果你按照本文對(duì) Python 函數(shù)的研究,你將找到更聰明的工作方法,這意味著更少的工作。

本文假定你對(duì) Python 很熟(LCTT 譯注:稍微熟悉就可以),并且可以編寫和運(yùn)行一個(gè)簡(jiǎn)單的腳本。如果你還沒(méi)有使用過(guò) Python,請(qǐng)首先閱讀我的文章: Python 簡(jiǎn)介 。
函數(shù)
函數(shù)是邁向模塊化過(guò)程中重要的一步,因?yàn)樗鼈兪切问交闹貜?fù)方法。如果在你的程序中,有一個(gè)任務(wù)需要反復(fù)執(zhí)行,那么你可以將代碼放入一個(gè)函數(shù)中,根據(jù)需要隨時(shí)調(diào)用該函數(shù)。這樣,你只需編寫一次代碼,就可以隨意使用它。
以下一個(gè)簡(jiǎn)單函數(shù)的示例:
- #!/usr/bin/env python3
- import time
- def Timer():
- print("Time is " + str(time.time() ))
創(chuàng)建一個(gè)名為 mymodularity 的目錄,并將以上函數(shù)代碼保存為該目錄下的 timestamp.py。
除了這個(gè)函數(shù),在 mymodularity 目錄中創(chuàng)建一個(gè)名為 __init__.py 的文件,你可以在文件管理器或 bash shell 中執(zhí)行此操作:
- $ touch mymodularity/__init__.py
現(xiàn)在,你已經(jīng)創(chuàng)建了屬于你自己的 Python 庫(kù)(Python 中稱為“模塊”),名為 mymodularity。它不是一個(gè)特別有用的模塊,因?yàn)樗龅闹皇菍?dǎo)入 time 模塊并打印一個(gè)時(shí)間戳,但這只是一個(gè)開(kāi)始。
要使用你的函數(shù),像對(duì)待任何其他 Python 模塊一樣對(duì)待它。以下是一個(gè)小應(yīng)用,它使用你的 mymodularity 軟件包來(lái)測(cè)試 Python sleep() 函數(shù)的準(zhǔn)確性。將此文件保存為 sleeptest.py,注意要在 mymodularity 文件夾 之外,因?yàn)槿绻銓⑺4嬖?mymodularity 里面,那么它將成為你的包中的一個(gè)模塊,你肯定不希望這樣。
- #!/usr/bin/env python3
- import time
- from mymodularity import timestamp
- print("Testing Python sleep()...")
- # modularity
- timestamp.Timer()
- time.sleep(3)
- timestamp.Timer()
在這個(gè)簡(jiǎn)單的腳本中,你從 mymodularity 包中調(diào)用 timestamp 模塊兩次。從包中導(dǎo)入模塊時(shí),通常的語(yǔ)法是從包中導(dǎo)入你所需的模塊,然后使用 模塊名稱 + 一個(gè)點(diǎn) + 要調(diào)用的函數(shù)名(例如 timestamp.Timer())。
你調(diào)用了兩次 Timer() 函數(shù),所以如果你的 timestamp 模塊比這個(gè)簡(jiǎn)單的例子復(fù)雜些,那么你將節(jié)省大量重復(fù)代碼。
保存文件并運(yùn)行:
- $ python3 ./sleeptest.py
- Testing Python sleep()...
- Time is 1560711266.1526039
- Time is 1560711269.1557732
根據(jù)測(cè)試,Python 中的 sleep 函數(shù)非常準(zhǔn)確:在三秒鐘等待之后,時(shí)間戳成功且正確地增加了 3,在微秒單位上差距很小。
Python 庫(kù)的結(jié)構(gòu)看起來(lái)可能令人困惑,但其實(shí)它并不是什么魔法。Python 被編程 為一個(gè)包含 Python 代碼的目錄,并附帶一個(gè) __init__.py 文件,那么這個(gè)目錄就會(huì)被當(dāng)作一個(gè)包,并且 Python 會(huì)首先在當(dāng)前目錄中查找可用模塊。這就是為什么語(yǔ)句 from mymodularity import timestamp 有效的原因:Python 在當(dāng)前目錄查找名為 mymodularity 的目錄,然后查找 timestamp.py 文件。
你在這個(gè)例子中所做的功能和以下這個(gè)非模塊化的版本是一樣的:
- #!/usr/bin/env python3
- import time
- from mymodularity import timestamp
- print("Testing Python sleep()...")
- # no modularity
- print("Time is " + str(time.time() ) )
- time.sleep(3)
- print("Time is " + str(time.time() ) )
對(duì)于這樣一個(gè)簡(jiǎn)單的例子,其實(shí)沒(méi)有必要以這種方式編寫測(cè)試,但是對(duì)于編寫自己的模塊來(lái)說(shuō),優(yōu)秀的實(shí)踐是你的代碼是通用的,可以將它重用于其他項(xiàng)目。
通過(guò)在調(diào)用函數(shù)時(shí)傳遞信息,可以使代碼更通用。例如,假設(shè)你想要使用模塊來(lái)測(cè)試的不是 系統(tǒng) 的 sleep 函數(shù),而是 用戶自己實(shí)現(xiàn) 的 sleep 函數(shù),更改 timestamp 代碼,使它接受一個(gè)名為 msg 的傳入變量,它將是一個(gè)字符串,控制每次調(diào)用 timestamp 時(shí)如何顯示:
- #!/usr/bin/env python3
- import time
- # 更新代碼
- def Timer(msg):
- print(str(msg) + str(time.time() ) )
現(xiàn)在函數(shù)比以前更抽象了。它仍會(huì)打印時(shí)間戳,但是它為用戶打印的內(nèi)容 msg 還是未定義的。這意味著你需要在調(diào)用函數(shù)時(shí)定義它。
Timer 函數(shù)接受的 msg 參數(shù)是隨便命名的,你可以使用參數(shù) m、message 或 text,或是任何對(duì)你來(lái)說(shuō)有意義的名稱。重要的是,當(dāng)調(diào)用 timestamp.Timer 函數(shù)時(shí),它接收一個(gè)文本作為其輸入,將接收到的任何內(nèi)容放入 msg 變量中,并使用該變量完成任務(wù)。
以下是一個(gè)測(cè)試測(cè)試用戶正確感知時(shí)間流逝能力的新程序:
- #!/usr/bin/env python3
- from mymodularity import timestamp
- print("Press the RETURN key. Count to 3, and press RETURN again.")
- input()
- timestamp.Timer("Started timer at ")
- print("Count to 3...")
- input()
- timestamp.Timer("You slept until ")
將你的新程序保存為 response.py,運(yùn)行它:
- $ python3 ./response.py
- Press the RETURN key. Count to 3, and press RETURN again.
- Started timer at 1560714482.3772075
- Count to 3...
- You slept until 1560714484.1628013
函數(shù)和所需參數(shù)
新版本的 timestamp 模塊現(xiàn)在 需要 一個(gè) msg 參數(shù)。這很重要,因?yàn)槟愕牡谝粋€(gè)應(yīng)用程序?qū)o(wú)法運(yùn)行,因?yàn)樗鼪](méi)有將字符串傳遞給 timestamp.Timer 函數(shù):
- $ python3 ./sleeptest.py
- Testing Python sleep()...
- Traceback (most recent call last):
- File "./sleeptest.py", line 8, in
- timestamp.Timer()
- TypeError: Timer() missing 1 required positional argument: 'msg'
你能修復(fù)你的 sleeptest.py 應(yīng)用程序,以便它能夠與更新后的模塊一起正確運(yùn)行嗎?
變量和函數(shù)
通過(guò)設(shè)計(jì),函數(shù)限制了變量的范圍。換句話說(shuō),如果在函數(shù)內(nèi)創(chuàng)建一個(gè)變量,那么這個(gè)變量 只 在這個(gè)函數(shù)內(nèi)起作用。如果你嘗試在函數(shù)外部使用函數(shù)內(nèi)部出現(xiàn)的變量,就會(huì)發(fā)生錯(cuò)誤。
下面是對(duì) response.py 應(yīng)用程序的修改,嘗試從 timestamp.Timer() 函數(shù)外部打印 msg 變量:
- #!/usr/bin/env python3
- from mymodularity import timestamp
- print("Press the RETURN key. Count to 3, and press RETURN again.")
- input()
- timestamp.Timer("Started timer at ")
- print("Count to 3...")
- input()
- timestamp.Timer("You slept for ")
- print(msg)
試著運(yùn)行它,查看錯(cuò)誤:
- $ python3 ./response.py
- Press the RETURN key. Count to 3, and press RETURN again.
- Started timer at 1560719527.7862902
- Count to 3...
- You slept for 1560719528.135406
- Traceback (most recent call last):
- File "./response.py", line 15, in
- print(msg)
- NameError: name 'msg' is not defined
應(yīng)用程序返回一個(gè) NameError 消息,因?yàn)闆](méi)有定義 msg。這看起來(lái)令人困惑,因?yàn)槟憔帉懙拇a定義了 msg,但你對(duì)代碼的了解比 Python 更深入。調(diào)用函數(shù)的代碼,不管函數(shù)是出現(xiàn)在同一個(gè)文件中,還是打包為模塊,都不知道函數(shù)內(nèi)部發(fā)生了什么。一個(gè)函數(shù)獨(dú)立地執(zhí)行它的計(jì)算,并返回你想要它返回的內(nèi)容。這其中所涉及的任何變量都只是 本地的:它們只存在于函數(shù)中,并且只存在于函數(shù)完成其目的所需時(shí)間內(nèi)。
Return 語(yǔ)句
如果你的應(yīng)用程序需要函數(shù)中特定包含的信息,那么使用 return 語(yǔ)句讓函數(shù)在運(yùn)行后返回有意義的數(shù)據(jù)。
時(shí)間就是金錢,所以修改 timestamp 函數(shù),以使其用于一個(gè)虛構(gòu)的收費(fèi)系統(tǒng):
- #!/usr/bin/env python3
- import time
- def Timer(msg):
- print(str(msg) + str(time.time() ) )
- charge = .02
- return charge
現(xiàn)在,timestamp 模塊每次調(diào)用都收費(fèi) 2 美分,但最重要的是,它返回每次調(diào)用時(shí)所收取的金額。
以下一個(gè)如何使用 return 語(yǔ)句的演示:
- #!/usr/bin/env python3
- from mymodularity import timestamp
- print("Press RETURN for the time (costs 2 cents).")
- print("Press Q RETURN to quit.")
- total = 0
- while True:
- kbd = input()
- if kbd.lower() == "q":
- print("You owe $" + str(total) )
- exit()
- else:
- charge = timestamp.Timer("Time is ")
- total = total+charge
在這個(gè)示例代碼中,變量 charge 為 timestamp.Timer() 函數(shù)的返回,它接收函數(shù)返回的任何內(nèi)容。在本例中,函數(shù)返回一個(gè)數(shù)字,因此使用一個(gè)名為 total 的新變量來(lái)跟蹤已經(jīng)進(jìn)行了多少更改。當(dāng)應(yīng)用程序收到要退出的信號(hào)時(shí),它會(huì)打印總花費(fèi):
- $ python3 ./charge.py
- Press RETURN for the time (costs 2 cents).
- Press Q RETURN to quit.
- Time is 1560722430.345412
- Time is 1560722430.933996
- Time is 1560722434.6027434
- Time is 1560722438.612629
- Time is 1560722439.3649364
- q
- You owe $0.1
內(nèi)聯(lián)函數(shù)
函數(shù)不必在單獨(dú)的文件中創(chuàng)建。如果你只是針對(duì)一個(gè)任務(wù)編寫一個(gè)簡(jiǎn)短的腳本,那么在同一個(gè)文件中編寫函數(shù)可能更有意義。唯一的區(qū)別是你不必導(dǎo)入自己的模塊,但函數(shù)的工作方式是一樣的。以下是時(shí)間測(cè)試應(yīng)用程序的最新迭代:
- #!/usr/bin/env python3
- import time
- total = 0
- def Timer(msg):
- print(str(msg) + str(time.time() ) )
- charge = .02
- return charge
- print("Press RETURN for the time (costs 2 cents).")
- print("Press Q RETURN to quit.")
- while True:
- kbd = input()
- if kbd.lower() == "q":
- print("You owe $" + str(total) )
- exit()
- else:
- charge = Timer("Time is ")
- total = total+charge
它沒(méi)有外部依賴(Python 發(fā)行版中包含 time 模塊),產(chǎn)生與模塊化版本相同的結(jié)果。它的優(yōu)點(diǎn)是一切都位于一個(gè)文件中,缺點(diǎn)是你不能在其他腳本中使用 Timer() 函數(shù),除非你手動(dòng)復(fù)制和粘貼它。
全局變量
在函數(shù)外部創(chuàng)建的變量沒(méi)有限制作用域,因此它被視為 全局 變量。
全局變量的一個(gè)例子是在 charge.py 中用于跟蹤當(dāng)前花費(fèi)的 total 變量。total 是在函數(shù)之外創(chuàng)建的,因此它綁定到應(yīng)用程序而不是特定函數(shù)。
應(yīng)用程序中的函數(shù)可以訪問(wèn)全局變量,但要將變量傳入導(dǎo)入的模塊,你必須像發(fā)送 msg 變量一樣將變量傳入模塊。
全局變量很方便,因?yàn)樗鼈兯坪蹼S時(shí)隨地都可用,但也很難跟蹤它們,很難知道哪些變量不再需要了但是仍然在系統(tǒng)內(nèi)存中停留(盡管 Python 有非常好的垃圾收集機(jī)制)。
但是,全局變量很重要,因?yàn)椴皇撬械淖兞慷伎梢允呛瘮?shù)或類的本地變量?,F(xiàn)在你知道了如何向函數(shù)傳入變量并獲得返回,事情就變得容易了。
總結(jié)
你已經(jīng)學(xué)到了很多關(guān)于函數(shù)的知識(shí),所以開(kāi)始將它們放入你的腳本中 —— 如果它不是作為單獨(dú)的模塊,那么作為代碼塊,你不必在一個(gè)腳本中編寫多次。在本系列的下一篇文章中,我將介紹 Python 類。
本文題目:使用Python函數(shù)進(jìn)行模塊化
當(dāng)前路徑:http://www.5511xx.com/article/dpcoedh.html


咨詢
建站咨詢
