目錄
1. 函數(shù)定義
定義函數(shù)
函數(shù)定義示例:
def cylinder_volume(height, radius):
pi = 3.14159
return height * pi * radius ** 2
定義 cylinder_volume 函數(shù)后,我們可以如下所示地調(diào)用該函數(shù)。
cylinder_volume(10, 3)
函數(shù)定義包含幾個(gè)重要部分。
-
函數(shù)頭部
我們從函數(shù)頭部開始举畸,即函數(shù)定義的第一行。
- 函數(shù)頭部始終以關(guān)鍵字
def
開始郑诺,表示這是函數(shù)定義羡鸥。 - 然后是函數(shù)名稱(在此例中是
cylinder_volume
想括,因?yàn)楹瘮?shù)名是要一個(gè)單詞曾沈,所以需要用_進(jìn)行連接)尘颓,遵循的是和變量一樣的命名規(guī)范。你可以在本頁(yè)面下方回顧下命名規(guī)范晦譬。 - 名稱之后是括號(hào),其中可能包括用英文逗號(hào)分隔的參數(shù)(在此例中是
height
和radius
)互广。形參(或?qū)崊ⅲ?/a>是當(dāng)函數(shù)被調(diào)用時(shí)作為輸入傳入的值敛腌,用在函數(shù)主體中卧土。如果函數(shù)沒(méi)有參數(shù),這些括號(hào)留空像樊。 - 頭部始終以英文冒號(hào)
:
結(jié)束尤莺。
?
-
函數(shù)主體
函數(shù)的剩余部分包含在主題中,也就是函數(shù)完成操作的部分生棍。
- 函數(shù)主體是在頭部行之后縮進(jìn)的代碼颤霎。在此例中是定義 π 和返回體積的兩行代碼。
- 在此主體中涂滴,我們可以引用參數(shù)并定義新的變量友酱,這些變量只能在這些縮進(jìn)代碼行內(nèi)使用。
- 主體將經(jīng)常包括
return
語(yǔ)句柔纵,用于當(dāng)函數(shù)被調(diào)用時(shí)返回輸出值缔杉。return
語(yǔ)句包括關(guān)鍵字return
,然后是經(jīng)過(guò)評(píng)估以獲得函數(shù)輸出值的表達(dá)式搁料。如果沒(méi)有return
語(yǔ)句或详,函數(shù)直接返回 None(例如內(nèi)置print()
函數(shù))。
?
-
函數(shù)的命名規(guī)范
函數(shù)名稱遵守和變量一樣的命名規(guī)范郭计。
- 僅在函數(shù)名稱中使用普通字母霸琴、數(shù)字和下劃線。不能有空格昭伸,需要以字母或下劃線開頭梧乘。
- 不能使用在 Python 中具有重要作用的保留字或內(nèi)置標(biāo)識(shí)符,我們將在這門課程中學(xué)習(xí)這方面的知識(shí)勋乾。要了解 python 保留字列表宋下,請(qǐng)參閱此處。
- 嘗試使用可以幫助讀者了解函數(shù)作用的描述性名稱辑莫。
?
默認(rèn)參數(shù)
我們可以向函數(shù)中添加默認(rèn)參數(shù)学歧,以便為在函數(shù)調(diào)用中未指定的參數(shù)提供默認(rèn)值。
def cylinder_volume(height, radius=5):
pi = 3.14159
return height * pi * radius ** 2
在上述示例中各吨,如果在函數(shù)調(diào)用中忽略了 radius
枝笨,則將該參數(shù)設(shè)為 5。如果我們調(diào)用 cylinder_volume(10)
揭蜒,該函數(shù)將使用 10 作為高度横浑,使用 5 作為半徑。但是屉更,如果調(diào)用 cylinder_volume(10, 7)
徙融,7 將覆蓋默認(rèn)的值 5。
此外注意瑰谜,我們按照位置向參數(shù)傳遞值欺冀∈骷ǎ可以通過(guò)兩種方式傳遞值:按照位置和按照名稱。下面兩個(gè)函數(shù)的效果是一樣的隐轩。
cylinder_volume(10, 7) # pass in arguments by position
cylinder_volume(height=10, radius=7) # pass in arguments by name
?
?
2. 變量作用域
變量作用域是指可以在程序的哪個(gè)部分引用或使用某個(gè)變量饺饭。
在函數(shù)中使用變量時(shí),務(wù)必要考慮作用域职车。如果變量是在函數(shù)內(nèi)創(chuàng)建的瘫俊,則只能在該函數(shù)內(nèi)使用該變量。你無(wú)法從該函數(shù)外面訪問(wèn)該變量悴灵。
# This will result in an error
def some_function():
word = "hello"
print(word)
這意味著你可以為在不同函數(shù)內(nèi)使用的不同變量使用相同的名稱扛芽。
# This works fine
def some_function():
word = "hello"
def another_function():
word = "goodbye"
像這樣在函數(shù)之外定義的變量依然可以在函數(shù)內(nèi)訪問(wèn)。
# This works fine
word = "hello"
def some_function():
print(word)
print(word)
注意称勋,我們可以在此函數(shù)內(nèi)以及函數(shù)外輸出 word胸哥。作用域?qū)斫庑畔⒃谟?Python 和任何編程語(yǔ)言編寫的程序中的傳遞方式來(lái)說(shuō)很關(guān)鍵。
?
?
3. 文檔
文檔使代碼更容易理解和使用赡鲜。函數(shù)尤其容易理解空厌,因?yàn)樗鼈兺ǔJ褂梦臋n字符串,簡(jiǎn)稱 docstrings银酬。文檔字符串是一種注釋嘲更,用于解釋函數(shù)的作用以及使用方式。下面是一個(gè)包含文檔字符串的人口密度函數(shù)揩瞪。
def population_density(population, land_area):
"""Calculate the population density of an area. """
return population / land_area
文檔字符串用三個(gè)引號(hào)引起來(lái)赋朦,第一行簡(jiǎn)要解釋了函數(shù)的作用。如果你覺得信息已經(jīng)足夠了李破,可以在文檔字符串中只提供這么多的信息宠哄;一行文檔字符串完全可接受,如上述示例所示嗤攻。
def population_density(population, land_area):
"""Calculate the population density of an area.
INPUT:
population: int. The population of that area
land_area: int or float. This function is unit-agnostic, if you pass in values in terms
of square km or square miles the function will return a density in those units.
OUTPUT:
population_density: population / land_area. The population density of a particular area.
"""
return population / land_area
如果你覺得需要更長(zhǎng)的句子來(lái)解釋函數(shù)毛嫉,可以在一行摘要后面添加更多信息。在上述示例中妇菱,可以看出我們對(duì)函數(shù)的參數(shù)進(jìn)行了解釋承粤,描述了每個(gè)參數(shù)的作用和類型。我們經(jīng)常還會(huì)對(duì)函數(shù)輸出進(jìn)行說(shuō)明闯团。
文檔字符串的每個(gè)部分都是可選的辛臊。但是,提供文檔字符串是一個(gè)良好的編程習(xí)慣房交。你可以在此處詳細(xì)了解文檔字符串慣例彻舰。
?
?
4. Lambda 表達(dá)式
使用Lambda
表達(dá)式創(chuàng)建匿名函數(shù),即沒(méi)有名稱的函數(shù)候味。lambda
表達(dá)式非常適合快速創(chuàng)建在代碼中以后不會(huì)用到的函數(shù)刃唤。尤其對(duì)高階函數(shù)或?qū)⑵渌瘮?shù)作為參數(shù)的函數(shù)來(lái)說(shuō)口猜,非常實(shí)用。
我們可以使用lambda
表達(dá)式將以下函數(shù)
def multiply(x, y):
return x * y
簡(jiǎn)寫為:
double = lambda x, y: x * y
?
Lambda 函數(shù)的組成部分
- 關(guān)鍵字
lambda
表示這是一個(gè)lambda
表達(dá)式透揣。 -
lambda
之后是該匿名函數(shù)的一個(gè)或多個(gè)參數(shù)(用英文逗號(hào)分隔),然后是一個(gè)英文冒號(hào) :川抡。和函數(shù)相似辐真,lambda 表達(dá)式中的參數(shù)名稱是隨意的。 - 最后一部分是被評(píng)估并在該函數(shù)中返回的表達(dá)式崖堤,和你可能會(huì)在函數(shù)中看到的
return
語(yǔ)句很像侍咱。
鑒于這種結(jié)構(gòu),lambda
表達(dá)式不太適合復(fù)雜的函數(shù)密幔,但是非常適合簡(jiǎn)短的函數(shù)楔脯。
?
Lambda 與 Map
map()
是一個(gè)高階內(nèi)置函數(shù),接受函數(shù)和可迭代對(duì)象作為輸入胯甩,并返回一個(gè)將該函數(shù)應(yīng)用到可迭代對(duì)象的每個(gè)元素的迭代器昧廷。下面的代碼使用 map()
計(jì)算 numbers
中每個(gè)列表的均值,并創(chuàng)建列表 averages
偎箫。
通過(guò)將 mean
函數(shù)替換為在 map()
的調(diào)用中定義的 lambda
表達(dá)式木柬,重寫這段代碼,使代碼更簡(jiǎn)練淹办。
numbers = [
[34, 63, 88, 71, 29],
[90, 78, 51, 27, 45],
[63, 37, 85, 46, 22],
[51, 22, 34, 11, 18]
]
def mean(num_list):
return sum(num_list) / len(num_list)
# before
averages = list(map(mean, numbers))
print(averages)
# after
averages_2 = list(map((lambda x: sum(x) / len(x)), numbers))
print(averages_2)
?
Lambda 與 Filter
filter()
是一個(gè)高階內(nèi)置函數(shù)眉枕,接受函數(shù)和可迭代對(duì)象作為輸入,并返回一個(gè)由可迭代對(duì)象中的特定元素(該函數(shù)針對(duì)該元素會(huì)返回 True)組成的迭代器怜森。下面的代碼使用filter()
從 cities
中獲取長(zhǎng)度少于 10 個(gè)字符的名稱以創(chuàng)建列表 short_cities
速挑。
通過(guò)將is_short
函數(shù)替換為在filter()
的調(diào)用中定義的lambda
表達(dá)式,重寫這段代碼副硅,使代碼更簡(jiǎn)練驹愚。
cities = ["New York City", "Los Angeles", "Chicago", "Mountain View", "Denver", "Boston"]
def is_short(name):
return len(name) < 10
# before
short_cities = list(filter(is_short, cities))
print(short_cities)
# after
short_cities = list(filter((lambda s: len(s) < 10), cities))
print(short_cities)
?
?
5. 迭代器和生成器
迭代器是每次可以返回一個(gè)對(duì)象元素的對(duì)象诚撵,例如返回一個(gè)列表。我們到目前為止使用的很多內(nèi)置函數(shù)(例如 enumerate)都會(huì)返回一個(gè)迭代器。
迭代器是一種表示數(shù)據(jù)流的對(duì)象免姿。這與列表不同,列表是可迭代對(duì)象畔师,但不是迭代器薄霜,因?yàn)樗皇菙?shù)據(jù)流。
生成器是使用函數(shù)創(chuàng)建迭代器的簡(jiǎn)單方式漱凝。也可以使用類定義迭代器疮蹦,更多詳情請(qǐng)參閱此處。
下面是一個(gè)叫做 my_range
的生成器函數(shù)茸炒,它會(huì)生成一個(gè)從 0 到 (x - 1) 的數(shù)字流愕乎。
def my_range(x):
i = 0
while i < x:
yield i
i += 1
注意阵苇,該函數(shù)使用了 yield 而不是關(guān)鍵字 return。這樣使函數(shù)能夠一次返回一個(gè)值感论,并且每次被調(diào)用時(shí)都從停下的位置繼續(xù)绅项。關(guān)鍵字 yield 是將生成器與普通函數(shù)區(qū)分開來(lái)的依據(jù)。
注意比肄,因?yàn)檫@段代碼會(huì)返回一個(gè)迭代器快耿,因此我們可以將其轉(zhuǎn)換為列表或用 for 循環(huán)遍歷它,以查看其內(nèi)容芳绩。例如掀亥,下面的代碼:
for x in my_range(5):
print(x)
輸出:
0
1
2
3
4
為何要使用生成器?
生成器是構(gòu)建迭代器的 “懶惰” 方式妥色。當(dāng)內(nèi)存不夠存儲(chǔ)完整實(shí)現(xiàn)的列表時(shí)搪花,或者計(jì)算每個(gè)列表元素的代價(jià)很高,你希望盡量推遲計(jì)算時(shí)嘹害,就可以使用生成器撮竿。但是這些元素只能遍歷一次。
另一種詳細(xì)的解釋如下(詳細(xì)說(shuō)明參見 該 stack overflow 頁(yè)面吼拥。)
由于使用生成器是一次處理一個(gè)數(shù)據(jù)倚聚,在內(nèi)存和存儲(chǔ)的需求上會(huì)比使用list方式直接全部生成再存儲(chǔ)節(jié)省很多資源。
由此區(qū)別凿可,在處理大量數(shù)據(jù)時(shí)惑折,經(jīng)常使用生成器初步處理數(shù)據(jù)后,再進(jìn)行長(zhǎng)期存儲(chǔ)枯跑,而不是使用 list惨驶。因?yàn)闊o(wú)論使用生成器還是 list,都是使用過(guò)就要丟棄的臨時(shí)數(shù)據(jù)敛助。既然功能和結(jié)果一樣粗卜,那就不如用生成器。
但是生成器也有自己的局限纳击,它產(chǎn)生的數(shù)據(jù)不能回溯续扔,不像list可以任意選擇。