學(xué)習(xí)Python爬蟲記錄第5篇——Python語法之函數(shù)

不管哪門編程語言,我們寫的最多的應(yīng)該就是函數(shù)了,我們使用別人提供的庫,實(shí)際上往往使用的就是這些庫中的各個(gè)函數(shù)了。那么,函數(shù)究竟是什么,如何編寫一個(gè)簡單的函數(shù)呢?

函數(shù)(Functions)是指可重復(fù)使用的程序片段。它們?cè)试S你為某個(gè)代碼塊賦予名字,允許你通過這一特殊的名字在你的程序任何地方來運(yùn)行代碼塊,并可重復(fù)任何次數(shù)。這就是所謂的調(diào)用(Calling)函數(shù)。我們已經(jīng)使用過了許多內(nèi)置的函數(shù),例如 lenrange。

函數(shù)概念可能是在任何復(fù)雜的軟件(無論使用的是何種編程語言)中重要的構(gòu)建塊,所以我們接下來將在本章中探討有關(guān)函數(shù)的各個(gè)方面。

函數(shù)可以通過關(guān)鍵字 def 來定義。這一關(guān)鍵字后跟一個(gè)函數(shù)的標(biāo)識(shí)符名稱,再跟一對(duì)圓括號(hào),其中可以包括一些變量的名稱,再以冒號(hào)結(jié)尾,結(jié)束這一行。隨后而來的語句塊是函數(shù)的一部分。下面的案例將會(huì)展示出這其實(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ù)。這個(gè)函數(shù)不使用參數(shù),因此在括號(hào)中沒有聲明變量。函數(shù)的參數(shù)只是輸入到函數(shù)之中,以便我可以傳遞不同的值給它,并獲得相應(yīng)的結(jié)果。

要注意到我們可以兩次調(diào)用相同的函數(shù),這意味著我們不必重新把代碼再寫一次。

函數(shù)參數(shù)1

函數(shù)可以獲取參數(shù),這個(gè)參數(shù)的值由你所提供,借此,函數(shù)便可以利用這些值來一些事情。這些參數(shù)與變量類似,這些變量的值在我們調(diào)用函數(shù)時(shí)已被定義,且在函數(shù)運(yùn)行時(shí)均已賦值完成。

函數(shù)中的參數(shù)通過將其放置在用以定義函數(shù)的一對(duì)圓括號(hào)中指定,并通過逗號(hào)予以分隔。當(dāng)我們調(diào)用函數(shù)時(shí),我們以同樣的形式提供需要的值。要注意在此使用的術(shù)語——在定義函數(shù)時(shí)給定的名稱稱作“形參”(Parameters),在調(diào)用函數(shù)時(shí)你所提供給函數(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 并使用兩個(gè)參數(shù)分別稱作 ab。我們使用一個(gè)簡單的 if...else 語句來找出更大的那個(gè)數(shù),并將它打印出來。

第一次調(diào)用函數(shù) print_max 時(shí),我們以實(shí)參的形式直接向函數(shù)提供這一數(shù)字。在第二次調(diào)用時(shí),我們將變量作為實(shí)參來調(diào)用函數(shù)。print_max(x, y) 將使得實(shí)參 x 的值將被賦值給形參 a,而實(shí)參 y 的值將被賦值給形參 b。在兩次調(diào)用中,print_max 都以相同的方式工作。

局部變量2

當(dāng)你在一個(gè)函數(shù)的定義中聲明變量時(shí),它們不會(huì)以任何方式與身處函數(shù)之外但具有相同名稱的變量產(chǎn)生關(guān)系,也就是說,這些變量名只存在于函數(shù)這一局部(Local)。這被稱為變量的作用域(Scope)。所有變量的作用域是它們被定義的塊,從定義它們的名字的定義點(diǎn)開始。

案例(保存為 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 的值時(shí),Python 使用的是在函數(shù)聲明之上的主代碼塊中聲明的這一參數(shù)的值。

接著,我們將值 2 賦值給 x。x 是我們這一函數(shù)的局部變量。因此,當(dāng)我們改變函數(shù)中 x 的值的時(shí)候,主代碼塊中的 x 則不會(huì)受到影響。

隨著最后一句 print 語句,我們展示出主代碼塊中定義的 x 的值,由此確認(rèn)它實(shí)際上不受先前調(diào)用的函數(shù)中的局部變量的影響。

global 語句

如果你想給一個(gè)在程序頂層的變量賦值(也就是說它不存在于任何作用域中,無論是函數(shù)還是類),那么你必須告訴 Python 這一變量并非局部的,而是全局(Global)的。我們需要通過 global 語句來完成這件事。因?yàn)樵诓皇褂?global 語句的情況下,不可能為一個(gè)定義于函數(shù)之外的變量賦值。

你可以使用定義于函數(shù)之外的變量的值(假設(shè)函數(shù)中沒有具有相同名字的變量)。然而,這種方式不會(huì)受到鼓勵(lì)而且應(yīng)該避免,因?yàn)樗鼘?duì)于程序的讀者來說是含糊不清的,無法弄清楚變量的定義究竟在哪。而通過使用 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 是一個(gè)全局變量——因此,當(dāng)我們?cè)诤瘮?shù)中為 x 進(jìn)行賦值時(shí),這一改動(dòng)將影響到我們?cè)谥鞔a塊中使用的 x 的值。

你可以在同一句 global 語句中指定不止一個(gè)的全局變量,例如 global x, y, z。

默認(rèn)參數(shù)值

對(duì)于一些函數(shù)來說,你可能為希望使一些參數(shù)可選并使用默認(rèn)的值,以避免用戶不想為他們提供值的情況。默認(rèn)參數(shù)值可以有效幫助解決這一情況。你可以通過在函數(shù)定義時(shí)附加一個(gè)賦值運(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ù)打印一串字符串。如果我們沒有提供一個(gè)數(shù)值,則將按照默認(rèn)設(shè)置,只打印一次字符串。我們通過為參數(shù) times 指定默認(rèn)參數(shù)值 1 來實(shí)現(xiàn)這一點(diǎn)。

在第一次使用 say 時(shí),我們只提供字符串因而函數(shù)只會(huì)將這個(gè)字符串打印一次。在第二次使用 say 時(shí),我們既提供了字符串,同時(shí)也提供了一個(gè)參數(shù) 5,聲明我們希望說(Say)這個(gè)字符串五次。

注意

只有那些位于參數(shù)列表末尾的參數(shù)才能被賦予默認(rèn)參數(shù)值,意即在函數(shù)的參數(shù)列表中擁有默認(rèn)參數(shù)值的參數(shù)不能位于沒有默認(rèn)參數(shù)值的參數(shù)之前。

這是因?yàn)橹凳前磪?shù)所處的位置依次分配的。舉例來說,def func(a, b=5) 是有效的,但 def func(a=5, b)無效的。

關(guān)鍵字參數(shù)3

如果你有一些具有許多參數(shù)的函數(shù),而你又希望只對(duì)其中的一些進(jìn)行指定,那么你可以通過命名它們來給這些參數(shù)賦值——這就是關(guān)鍵字參數(shù)(Keyword Arguments)——我們使用命名(關(guān)鍵字)而非位置(一直以來我們所使用的方式)來指定函數(shù)中的參數(shù)。

這樣做有兩大優(yōu)點(diǎn)——其一,我們不再需要考慮參數(shù)的順序,函數(shù)的使用將更加容易。其二,我們可以只對(duì)那些我們希望賦予的參數(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ù)有一個(gè)沒有默認(rèn)參數(shù)值的參數(shù),后跟兩個(gè)各自帶有默認(rèn)參數(shù)值的參數(shù)。

在第一次調(diào)用函數(shù)時(shí),func(3, 7),參數(shù) a 獲得了值 3,參數(shù) b 獲得了值 7,而 c 獲得了默認(rèn)參數(shù)值 10

在第二次調(diào)用函數(shù)時(shí),func(25, c=24),由于其所處的位置,變量 a 首先獲得了值 25。然后,由于命名——即關(guān)鍵字參數(shù)——指定,變量 c 獲得了值 24。變量 b 獲得默認(rèn)參數(shù)值 5。

在第三次調(diào)用函數(shù)時(shí),func(c=50, a=100),我們?nèi)渴褂藐P(guān)鍵字參數(shù)來指定值。在這里要注意到,盡管 ac 之前定義,但我們還是在變量 a 之前指定了變量 c。

可變參數(shù)4

有時(shí)你可能想定義的函數(shù)里面能夠有任意數(shù)量的變量,也就是參數(shù)數(shù)量是可變的,這可以通過使用星號(hào)來實(shí)現(xiàn)(將下方案例保存為 function_varargs.py):

def total(a=5, *numbers, **phonebook):
    print('a', a)

    #遍歷元組中的所有項(xiàng)目
    for single_item in numbers:
        print('single_item', single_item)

    #遍歷字典中的所有項(xiàng)目
    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)我們聲明一個(gè)諸如 *param 的星號(hào)參數(shù)時(shí),從此處開始直到結(jié)束的所有位置參數(shù)(Positional Arguments)都將被收集并匯集成一個(gè)稱為“param”的元組(Tuple)。

類似地,當(dāng)我們聲明一個(gè)諸如 **param 的雙星號(hào)參數(shù)時(shí),從此處開始直至結(jié)束的所有關(guān)鍵字參數(shù)都將被收集并匯集成一個(gè)名為 param 的字典(Dictionary)。

我們將在后面的章節(jié)探索有關(guān)元組與字典的更多內(nèi)容。

return 語句

return 語句用于從函數(shù)中返回,也就是中斷函數(shù)。我們也可以選擇在中斷函數(shù)時(shí)從函數(shù)中返回一個(gè)值。

案例(保存為 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ù)將會(huì)返回參數(shù)中的最大值,在本例中是提供給函數(shù)的數(shù)值。它使用一套簡單的 if...else 語句來找到較大的那個(gè)值并將其返回。

要注意到如果 return 語句沒有搭配任何一個(gè)值則代表著 返回 None。None 在 Python 中一個(gè)特殊的類型,代表著虛無。舉個(gè)例子, 它用于指示一個(gè)變量沒有值,如果有值則它的值便是 None(虛無)。

每一個(gè)函數(shù)都在其末尾隱含了一句 return None,除非你寫了你自己的 return 語句。你可以運(yùn)行 print(some_function()),其中 some_function 函數(shù)不使用 return 語句,就像這樣:

def some_function():
    pass

Python 中的 pass 語句用于指示一個(gè)沒有內(nèi)容的語句塊。

提示:有一個(gè)名為 max 的內(nèi)置函數(shù)已經(jīng)實(shí)現(xiàn)了“找到最大數(shù)”這一功能,所以盡可能地使用這一內(nèi)置函數(shù)。

DocStrings

Python 有一個(gè)甚是優(yōu)美的功能稱作文檔字符串(Documentation Strings),在稱呼它時(shí)通常會(huì)使用另一個(gè)短一些的名字docstrings。DocStrings 是一款你應(yīng)當(dāng)使用的重要工具,它能夠幫助你更好地記錄程序并讓其更加易于理解。令人驚嘆的是,當(dāng)程序?qū)嶋H運(yùn)行時(shí),我們甚至可以通過一個(gè)函數(shù)來獲取文檔!

案例(保存為 function_docstring.py):

def print_max(x, y):
    '''打印兩個(gè)數(shù)值中的最大數(shù)。

    這兩個(gè)數(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
打印兩個(gè)數(shù)值中的最大數(shù)。

    這兩個(gè)數(shù)都應(yīng)該是整數(shù)

它是如何工作的

函數(shù)的第一行邏輯行中的字符串是該函數(shù)的 文檔字符串(DocString)。這里要注意文檔字符串也適用于后面相關(guān)章節(jié)將提到的模塊(Modules)類(Class) 。

該文檔字符串所約定的是一串多行字符串,其中第一行以某一大寫字母開始,以句號(hào)結(jié)束。第二行為空行,后跟的第三行開始是任何詳細(xì)的解釋說明。5在此強(qiáng)烈建議你在你所有重要功能的所有文檔字符串中都遵循這一約定。

我們可以通過使用函數(shù)的 __doc__(注意其中的雙下劃綫)屬性(屬于函數(shù)的名稱)來獲取函數(shù) print_max 的文檔字符串屬性。只消記住 Python 將所有東西都視為一個(gè)對(duì)象,這其中自然包括函數(shù)。我們將在后面的類(Class)章節(jié)討論有關(guān)對(duì)象的更多細(xì)節(jié)。

如果你曾使用過 Python 的 help() 函數(shù),那么你應(yīng)該已經(jīng)了解了文檔字符串的用途了。它所做的便是獲取函數(shù)的 __doc__ 屬性并以一種整潔的方式將其呈現(xiàn)給你。你可以在上方的函數(shù)中嘗試一下——只需在程序中包含 help(print_max) 就行了。要記住你可以通過按下 q 鍵來退出 help。

自動(dòng)化工具可以以這種方式檢索你的程序中的文檔。因此,我強(qiáng)烈推薦你為你編寫的所有重要的函數(shù)配以文檔字符串。你的 Python 發(fā)行版中附帶的 pydoc 命令與 help() 使用文檔字符串的方式類似。

總結(jié)

我們已經(jīng)了解了許多方面的函數(shù),但我們依舊還未覆蓋到所有類型的函數(shù)。不過,我們已經(jīng)覆蓋到了大部分你每天日常使用都會(huì)使用到的 Python 函數(shù)。

接下來,我們將了解如何創(chuàng)建并使用 Python 模塊

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容