不管哪門編程語言竹握,我們寫的最多的應(yīng)該就是函數(shù)了,我們使用別人提供的庫辆飘,實際上往往使用的就是這些庫中的各個函數(shù)了啦辐。那么污秆,函數(shù)究竟是什么,如何編寫一個簡單的函數(shù)呢昧甘?
函數(shù)(Functions)是指可重復(fù)使用的程序片段。它們允許你為某個代碼塊賦予名字战得,允許你通過這一特殊的名字在你的程序任何地方來運(yùn)行代碼塊充边,并可重復(fù)任何次數(shù)。這就是所謂的調(diào)用(Calling)函數(shù)常侦。我們已經(jīng)使用過了許多內(nèi)置的函數(shù)浇冰,例如 len
和 range
。
函數(shù)概念可能是在任何復(fù)雜的軟件(無論使用的是何種編程語言)中最重要的構(gòu)建塊聋亡,所以我們接下來將在本章中探討有關(guān)函數(shù)的各個方面肘习。
函數(shù)可以通過關(guān)鍵字 def
來定義。這一關(guān)鍵字后跟一個函數(shù)的標(biāo)識符名稱坡倔,再跟一對圓括號漂佩,其中可以包括一些變量的名稱,再以冒號結(jié)尾罪塔,結(jié)束這一行投蝉。隨后而來的語句塊是函數(shù)的一部分。下面的案例將會展示出這其實非常簡單:
案例(保存為 function1.py
):
def say_hello():
# 該塊屬于這一函數(shù)
print('hello world')
# 函數(shù)結(jié)束
say_hello() # 調(diào)用函數(shù)
say_hello() # 再次調(diào)用函數(shù)
輸出:
$ python function1.py
hello world
hello world
它是如何工作的
我們以上文解釋過的方式定義名為 say_hello
的函數(shù)征堪。這個函數(shù)不使用參數(shù)瘩缆,因此在括號中沒有聲明變量。函數(shù)的參數(shù)只是輸入到函數(shù)之中佃蚜,以便我可以傳遞不同的值給它庸娱,并獲得相應(yīng)的結(jié)果。
要注意到我們可以兩次調(diào)用相同的函數(shù)谐算,這意味著我們不必重新把代碼再寫一次熟尉。
函數(shù)參數(shù)1
函數(shù)可以獲取參數(shù),這個參數(shù)的值由你所提供氯夷,借此臣樱,函數(shù)便可以利用這些值來做一些事情。這些參數(shù)與變量類似腮考,這些變量的值在我們調(diào)用函數(shù)時已被定義雇毫,且在函數(shù)運(yùn)行時均已賦值完成。
函數(shù)中的參數(shù)通過將其放置在用以定義函數(shù)的一對圓括號中指定踩蔚,并通過逗號予以分隔棚放。當(dāng)我們調(diào)用函數(shù)時,我們以同樣的形式提供需要的值馅闽。要注意在此使用的術(shù)語——在定義函數(shù)時給定的名稱稱作“形參”(Parameters)飘蚯,在調(diào)用函數(shù)時你所提供給函數(shù)的值稱作“實參”(Arguments)馍迄。
案例(保存為 function_param.py
):
def print_max(a, b):
if a > b:
print(a, 'is maximum')
elif a == b:
print(a, 'is equal to', b)
else:
print(b, 'is maximum')
# 直接傳遞字面值
print_max(3, 4)
x = 5
y = 7
# 以參數(shù)的形式傳遞變量
print_max(x, y)
輸出:
$ python function_param.py
4 is maximum
7 is maximum
它是如何工作的
在這里,我們將函數(shù)命名為 print_max
并使用兩個參數(shù)分別稱作 a
和 b
局骤。我們使用一個簡單的 if...else
語句來找出更大的那個數(shù)攀圈,并將它打印出來。
第一次調(diào)用函數(shù) print_max
時峦甩,我們以實參的形式直接向函數(shù)提供這一數(shù)字赘来。在第二次調(diào)用時,我們將變量作為實參來調(diào)用函數(shù)凯傲。print_max(x, y)
將使得實參 x
的值將被賦值給形參 a
犬辰,而實參 y
的值將被賦值給形參 b
。在兩次調(diào)用中冰单,print_max
都以相同的方式工作幌缝。
局部變量2
當(dāng)你在一個函數(shù)的定義中聲明變量時,它們不會以任何方式與身處函數(shù)之外但具有相同名稱的變量產(chǎn)生關(guān)系诫欠,也就是說涵卵,這些變量名只存在于函數(shù)這一局部(Local)。這被稱為變量的作用域(Scope)荒叼。所有變量的作用域是它們被定義的塊缘厢,從定義它們的名字的定義點開始。
案例(保存為 function_local.py
):
x = 50
def func(x):
print('x is', x)
x = 2
print('Changed local x to', x)
func(x)
print('x is still', x)
輸出:
$ python function_local.py
x is 50
Changed local x to 2
x is still 50
它是如何工作的
當(dāng)我們第一次打印出存在于函數(shù)塊的第一行的名為 x
的值時甩挫,Python 使用的是在函數(shù)聲明之上的主代碼塊中聲明的這一參數(shù)的值贴硫。
接著,我們將值 2
賦值給 x
伊者。x
是我們這一函數(shù)的局部變量英遭。因此,當(dāng)我們改變函數(shù)中 x
的值的時候亦渗,主代碼塊中的 x
則不會受到影響挖诸。
隨著最后一句 print
語句,我們展示出主代碼塊中定義的 x
的值法精,由此確認(rèn)它實際上不受先前調(diào)用的函數(shù)中的局部變量的影響多律。
global
語句
如果你想給一個在程序頂層的變量賦值(也就是說它不存在于任何作用域中,無論是函數(shù)還是類)搂蜓,那么你必須告訴 Python 這一變量并非局部的狼荞,而是全局(Global)的。我們需要通過 global
語句來完成這件事帮碰。因為在不使用 global
語句的情況下相味,不可能為一個定義于函數(shù)之外的變量賦值。
你可以使用定義于函數(shù)之外的變量的值(假設(shè)函數(shù)中沒有具有相同名字的變量)殉挽。然而丰涉,這種方式不會受到鼓勵而且應(yīng)該避免拓巧,因為它對于程序的讀者來說是含糊不清的,無法弄清楚變量的定義究竟在哪一死。而通過使用 global
語句便可清楚看出這一變量是在最外邊的代碼塊中定義的肛度。
案例(保存為 function_global.py
):
x = 50
def func():
global x
print('x is', x)
x = 2
print('Changed global x to', x)
func()
print('Value of x is', x)
輸出:
$ python function_global.py
x is 50
Changed global x to 2
Value of x is 2
它是如何工作的
global
語句用以聲明 x
是一個全局變量——因此,當(dāng)我們在函數(shù)中為 x
進(jìn)行賦值時投慈,這一改動將影響到我們在主代碼塊中使用的 x
的值贤斜。
你可以在同一句 global
語句中指定不止一個的全局變量,例如 global x, y, z
逛裤。
默認(rèn)參數(shù)值
對于一些函數(shù)來說,你可能為希望使一些參數(shù)可選并使用默認(rèn)的值猴抹,以避免用戶不想為他們提供值的情況带族。默認(rèn)參數(shù)值可以有效幫助解決這一情況。你可以通過在函數(shù)定義時附加一個賦值運(yùn)算符(=
)來為參數(shù)指定默認(rèn)參數(shù)值蟀给。
要注意到蝙砌,默認(rèn)參數(shù)值應(yīng)該是常數(shù)。更確切地說跋理,默認(rèn)參數(shù)值應(yīng)該是不可變的——這將在后面的章節(jié)中予以更詳細(xì)的解釋择克。就目前來說,只要記住就行了前普。
案例(保存為 function_default.py
):
def say(message, times=1):
print(message * times)
say('Hello')
say('World', 5)
輸出:
$ python function_default.py
Hello
WorldWorldWorldWorldWorld
它是如何工作的
名為 say
的函數(shù)用以按照給定的次數(shù)打印一串字符串肚邢。如果我們沒有提供一個數(shù)值,則將按照默認(rèn)設(shè)置拭卿,只打印一次字符串骡湖。我們通過為參數(shù) times
指定默認(rèn)參數(shù)值 1
來實現(xiàn)這一點。
在第一次使用 say
時峻厚,我們只提供字符串因而函數(shù)只會將這個字符串打印一次响蕴。在第二次使用 say
時,我們既提供了字符串惠桃,同時也提供了一個參數(shù) 5
浦夷,聲明我們希望說(Say)這個字符串五次。
注意
只有那些位于參數(shù)列表末尾的參數(shù)才能被賦予默認(rèn)參數(shù)值辜王,意即在函數(shù)的參數(shù)列表中擁有默認(rèn)參數(shù)值的參數(shù)不能位于沒有默認(rèn)參數(shù)值的參數(shù)之前劈狐。
這是因為值是按參數(shù)所處的位置依次分配的。舉例來說呐馆,
def func(a, b=5)
是有效的懈息,但def func(a=5, b)
是無效的。
關(guān)鍵字參數(shù)3
如果你有一些具有許多參數(shù)的函數(shù)摹恰,而你又希望只對其中的一些進(jìn)行指定辫继,那么你可以通過命名它們來給這些參數(shù)賦值——這就是關(guān)鍵字參數(shù)(Keyword Arguments)——我們使用命名(關(guān)鍵字)而非位置(一直以來我們所使用的方式)來指定函數(shù)中的參數(shù)怒见。
這樣做有兩大優(yōu)點——其一,我們不再需要考慮參數(shù)的順序姑宽,函數(shù)的使用將更加容易遣耍。其二,我們可以只對那些我們希望賦予的參數(shù)以賦值炮车,只要其它的參數(shù)都具有默認(rèn)參數(shù)值舵变。
案例(保存為 function_keyword.py
):
def func(a, b=5, c=10):
print('a is', a, 'and b is', b, 'and c is', c)
func(3, 7)
func(25, c=24)
func(c=50, a=100)
輸出:
$ python function_keyword.py
a is 3 and b is 7 and c is 10
a is 25 and b is 5 and c is 24
a is 100 and b is 5 and c is 50
它是如何工作的
名為 func
的函數(shù)有一個沒有默認(rèn)參數(shù)值的參數(shù),后跟兩個各自帶有默認(rèn)參數(shù)值的參數(shù)瘦穆。
在第一次調(diào)用函數(shù)時纪隙,func(3, 7)
,參數(shù) a
獲得了值 3
扛或,參數(shù) b
獲得了值 7
绵咱,而 c
獲得了默認(rèn)參數(shù)值 10
。
在第二次調(diào)用函數(shù)時熙兔,func(25, c=24)
悲伶,由于其所處的位置,變量 a
首先獲得了值 25住涉。然后麸锉,由于命名——即關(guān)鍵字參數(shù)——指定,變量 c
獲得了值 24
舆声。變量 b
獲得默認(rèn)參數(shù)值 5
花沉。
在第三次調(diào)用函數(shù)時,func(c=50, a=100)
媳握,我們?nèi)渴褂藐P(guān)鍵字參數(shù)來指定值主穗。在這里要注意到,盡管 a
在 c
之前定義毙芜,但我們還是在變量 a
之前指定了變量 c
忽媒。
可變參數(shù)4
有時你可能想定義的函數(shù)里面能夠有任意數(shù)量的變量,也就是參數(shù)數(shù)量是可變的腋粥,這可以通過使用星號來實現(xiàn)(將下方案例保存為 function_varargs.py
):
def total(a=5, *numbers, **phonebook):
print('a', a)
#遍歷元組中的所有項目
for single_item in numbers:
print('single_item', single_item)
#遍歷字典中的所有項目
for first_part, second_part in phonebook.items():
print(first_part,second_part)
print(total(10,1,2,3,Jack=1123,John=2231,Inge=1560))
輸出:
$ python function_varargs.py
a 10
single_item 1
single_item 2
single_item 3
Inge 1560
John 2231
Jack 1123
None
它是如何工作的
當(dāng)我們聲明一個諸如 *param
的星號參數(shù)時晦雨,從此處開始直到結(jié)束的所有位置參數(shù)(Positional Arguments)都將被收集并匯集成一個稱為“param”的元組(Tuple)。
類似地隘冲,當(dāng)我們聲明一個諸如 **param
的雙星號參數(shù)時闹瞧,從此處開始直至結(jié)束的所有關(guān)鍵字參數(shù)都將被收集并匯集成一個名為 param
的字典(Dictionary)。
我們將在后面的章節(jié)探索有關(guān)元組與字典的更多內(nèi)容展辞。
return
語句
return
語句用于從函數(shù)中返回奥邮,也就是中斷函數(shù)。我們也可以選擇在中斷函數(shù)時從函數(shù)中返回一個值。
案例(保存為 function_return.py
):
def maximum(x, y):
if x > y:
return x
elif x == y:
return 'The numbers are equal'
else:
return y
print(maximum(2, 3))
輸出:
$ python function_return.py
3
它是如何工作的
maximum
函數(shù)將會返回參數(shù)中的最大值洽腺,在本例中是提供給函數(shù)的數(shù)值脚粟。它使用一套簡單的 if...else
語句來找到較大的那個值并將其返回。
要注意到如果 return
語句沒有搭配任何一個值則代表著 返回 None
蘸朋。None
在 Python 中一個特殊的類型核无,代表著虛無。舉個例子藕坯, 它用于指示一個變量沒有值团南,如果有值則它的值便是 None(虛無)
。
每一個函數(shù)都在其末尾隱含了一句 return None
炼彪,除非你寫了你自己的 return
語句吐根。你可以運(yùn)行 print(some_function())
,其中 some_function
函數(shù)不使用 return
語句辐马,就像這樣:
def some_function():
pass
Python 中的 pass
語句用于指示一個沒有內(nèi)容的語句塊拷橘。
提示:有一個名為
max
的內(nèi)置函數(shù)已經(jīng)實現(xiàn)了“找到最大數(shù)”這一功能,所以盡可能地使用這一內(nèi)置函數(shù)齐疙。
DocStrings
Python 有一個甚是優(yōu)美的功能稱作文檔字符串(Documentation Strings),在稱呼它時通常會使用另一個短一些的名字docstrings旭咽。DocStrings 是一款你應(yīng)當(dāng)使用的重要工具贞奋,它能夠幫助你更好地記錄程序并讓其更加易于理解。令人驚嘆的是穷绵,當(dāng)程序?qū)嶋H運(yùn)行時轿塔,我們甚至可以通過一個函數(shù)來獲取文檔!
案例(保存為 function_docstring.py
):
def print_max(x, y):
'''打印兩個數(shù)值中的最大數(shù)仲墨。
這兩個數(shù)都應(yīng)該是整數(shù)'''
# 如果可能勾缭,將其轉(zhuǎn)換至整數(shù)類型
x = int(x)
y = int(y)
if x > y:
print(x, 'is maximum')
else:
print(y, 'is maximum')
print_max(3, 5)
print(print_max.__doc__)
輸出:
$ python function_docstring.py
5 is maximum
打印兩個數(shù)值中的最大數(shù)。
這兩個數(shù)都應(yīng)該是整數(shù)
它是如何工作的
函數(shù)的第一行邏輯行中的字符串是該函數(shù)的 文檔字符串(DocString)目养。這里要注意文檔字符串也適用于后面相關(guān)章節(jié)將提到的模塊(Modules)與類(Class) 俩由。
該文檔字符串所約定的是一串多行字符串,其中第一行以某一大寫字母開始癌蚁,以句號結(jié)束幻梯。第二行為空行,后跟的第三行開始是任何詳細(xì)的解釋說明努释。5在此強(qiáng)烈建議你在你所有重要功能的所有文檔字符串中都遵循這一約定碘梢。
我們可以通過使用函數(shù)的 __doc__
(注意其中的雙下劃綫)屬性(屬于函數(shù)的名稱)來獲取函數(shù) print_max
的文檔字符串屬性。只消記住 Python 將所有東西都視為一個對象伐蒂,這其中自然包括函數(shù)煞躬。我們將在后面的類(Class)章節(jié)討論有關(guān)對象的更多細(xì)節(jié)。
如果你曾使用過 Python 的 help()
函數(shù),那么你應(yīng)該已經(jīng)了解了文檔字符串的用途了恩沛。它所做的便是獲取函數(shù)的 __doc__
屬性并以一種整潔的方式將其呈現(xiàn)給你在扰。你可以在上方的函數(shù)中嘗試一下——只需在程序中包含 help(print_max)
就行了。要記住你可以通過按下 q
鍵來退出 help
复唤。
自動化工具可以以這種方式檢索你的程序中的文檔健田。因此,我強(qiáng)烈推薦你為你編寫的所有重要的函數(shù)配以文檔字符串佛纫。你的 Python 發(fā)行版中附帶的 pydoc
命令與 help()
使用文檔字符串的方式類似妓局。
總結(jié)
我們已經(jīng)了解了許多方面的函數(shù),但我們依舊還未覆蓋到所有類型的函數(shù)呈宇。不過好爬,我們已經(jīng)覆蓋到了大部分你每天日常使用都會使用到的 Python 函數(shù)。
接下來甥啄,我們將了解如何創(chuàng)建并使用 Python 模塊