零基礎學習 Python 之函式

Python空間發表於2019-01-06

寫在之前

今天我們又開始了新的篇章 -- 函式篇,在現代的任何科技門類,乃至於政治學,經濟學等都已經普遍使用函式,可以說函式的出現直接的加快了現代科技和社會的發展,下面就開始我們與 Python 中的函式的初次相見吧。

函式是什麼?

在我們學生時代的數學中,定義函式的方式一般是這樣的:y = ax + b,這是一個一次函式,當然我們也可以寫成 f(x) = ax + b,其中 x 是變數,可以代表任何數,但是這個並不是函式的全部,在函式中,其實變數並沒有規定只能是數,它可以是豬狗牛羊,也可以是花鳥木魚,說到這不知道你有沒有理解我的意思,其實,函式就是一種對映。

如果你嘗試著將變數 x 理解為小豬佩奇,那麼 ax + b 就是 a 個佩奇再加上 b,這個結果對應著的是另一個東西,比如熊大,即我們可以理解為 a 個佩奇加上 b 就對應的是熊大,這就是我們所說的對映關係。

如果你理解了這些,我們下面用純粹的中學的數學方式,在 Python 中建立函式:

>>> x = 6
>>> y = 2 * x + 1
>>> y
13
複製程式碼

在我們的學生時代我們就是這麼用的,那麼在 Python 中這種方式還有用嗎?上面的例子我們建立了一個所謂的函式,那麼我們來嘗試改變一下 x 的值:

>>> x = 7
>>> y
13
複製程式碼

結果是 y 的值並沒有改變,所以說用純粹的數學方式定義函式在 Python 中其實並沒有什麼用,所以我們要用一種新的定義函式的方式,請接著向下看。

如何定義函式?

在 Python 中定義了函式的格式,下面我舉一個例子來說一下 Python 中函式的格式和呼叫的方法:

>>> def add(x,y):
...    return x + y
複製程式碼

上面的例子雖然短小,但內有乾坤,下面我以此函式為例,詳述函式的組成。

  1. def :def 是函式的開始,也就是在宣告要建立一個函式的時候,一定要先使用 def,這就是告訴 Python 解析器,這裡要宣告的是一個函式。

  2. add:add 是函式的名稱,在 Python 中起名字的講究就是要起的有意義,能從函式的名字上看出這個函式是幹什麼的。同時函式的命名規範和變數名是一樣的,必須使用字母和下劃線開頭,且僅能含有字母,數字和下劃線。

  3. ( x,y ):這個是引數列表,要寫在括號裡,其中的引數指向函式的輸入,本例中函式有兩項輸入,但是通常情況下,輸入的個數可以是任意的,也包括 0 個。

  4. : :冒號非常重要,如果少了,會報錯,所以希望你們不要像我一樣。

  5. return a + b:這一行,就是函式體了,函式體是縮排了 4 個空格的程式碼塊,完成你需要完成的工作。 return 是函式的關鍵字,意思是要返回一個值,函式中的 return 也不是必須要寫的,如果不寫的話, Python 會預設返回一個值,那就是 None。

呼叫函式

在這之前,我們想一下我們為什麼要寫函式?理論上來說,不用函式也可以寫程式碼,之所以用函式,大佬們給我們總結了以下幾點,我在這借花獻佛:

  1. 寫函式可以降低程式設計的難度。通常將一個複雜的大問題分解成一系列更小的問題,然後小問題再分解成更小的問題,當問題細化到足夠簡單時,就可以分而治之。

  2. 程式碼重用。其實我們在程式設計的時候比較忌諱同一段程式碼不斷重複,因此有必要將某個常用的功能抽象為一段公用的程式碼,也就是函式。

從上面來看,使用函式還是很有必要的,下面就來看看函式是怎麼呼叫的:

>>> def add(x,y):
...    print('x = {}'.format(x))
...    print('y = {}'.format(y))
...    return x + y
複製程式碼

我們把之前的例子稍作改變,然後接下來看:

>>> add(10,3)
x = 10
y = 3
13
>>> add(3,10)
x = 3
y = 10
13
複製程式碼

函式呼叫,最關鍵的是要弄懂如何給函式的引數賦值,上面就是按照引數次序賦值,根據引數的位置,值與之相對應。

>>> add(x = 3,y = 10)
x = 3
y = 10
13
>>> add(y = 10,x = 3)
x = 3
y = 10
13
複製程式碼

還可以像上面一樣直接把賦值語句寫到裡面,這就明確了引數和物件的關係,這個時候順序就不重要了。

當然還可以在定義函式的時候就賦給一個預設值,如果不給那個有預設值的引數賦值,那麼它就用預設值,如果給它傳一個,則採用新傳給它的值:

>>> def add(x,y = 1):
...    print('x = {}'.format(x))
...    print('y = ',y)
...    return x + y
... 
>>> add(1)
x = 1
y =  1
2
>>> add(1,1000000)
x = 1
y =  1000000
1000001
複製程式碼

在這裡想在強調一次,引數和物件的關係與變數和物件的關係一樣,即在函式中的引數所傳的都是物件的引用,而不是物件本身

函式是很有深度的,需要我們深入探究,實踐過程中,有很多對函式的不同理解,需要我們在學習的過程中不斷的思考,下面我們學習一些函式的相關應用。

返回值

所謂的返回值,就是在呼叫函式的地方由函式返回的資料。下面我們用我們最熟悉的斐波那契數列為例,我們編寫一個函式來實現斐波那契數列:

>>> def fibs(n):
...    res = [0,1]
...    for i in range(n-2):
...            res.append(res[-2] + res[-1])
...    return res
... 
>>> if __name__ == "__main__":
...    now = fibs(10)
...    print(now)
...
複製程式碼

在上面的程式碼中我們首先定義了一個函式,名字叫做 fibs,引數是輸入一個整數(其實你輸入非整數也是可以的,只是結果不同),然後通過 now = fibs(10) 呼叫這個函式。這裡的引數給的是 10,這就以為著要得到的是 n = 10 的斐波那契數列。執行以後的結果如下 :

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
複製程式碼

當然如果你想換 n 的值,只需要在呼叫的時候修改一下引數就好了。然後我們來觀察上面的函式,最後有一個語句 return res,意思是將 res 的值返回,但是返回給誰呢?這要看是在什麼位置呼叫的函式。在上面的程式碼中,用 now = fibs(10) 呼叫了函式,那麼函式就將值返回到當前狀態,並記錄在記憶體中,然後把它賦值給變數 now。

需要注意的,上面雖然返回的是列表,但其實只是返回了一個返回值,有時候我們需要返回多個的時候,要用元組的方式。

>>> def my_digit():
...    return 1,2,3
... 
>>> now = my_digit()
>>> now
(1, 2, 3)
複製程式碼

對於上面的這個函式,其實我們還可以像下面一樣:

>>> x,y,z = my_digit()
>>> x
1
>>> y
2
>>> z
3
複製程式碼

並不是所以的函式都有 return,比如某些函式就只是執行一條語句或者乾脆什麼也不做,它們不需要返回值,其實看過昨天文章的朋友可能會有印象,其實它們也有,只不過是 None。比如下面的函式:

>>> def cau():
...    pass
... 
>>> now = cau()
>>> print(now)
None
複製程式碼

這個函式的作用就是什麼也不做,當然也就不需要 return。

我們可以特別注意一下那個 return,它其實還有一個作用,請看下面的例子:

>>> def my_info():
...    print('my name is rocky')
...    return
...    print('i like python')
... 
>>> my_info()
my name is rocky
複製程式碼

看出什麼了嗎?明明有兩個 print,在中間插入一個 return 以後,只執行了第一個 print,第二個並沒有執行。這是因為在第一個之後遇到 return,它告訴函式要終端函式體內的流程,所以 return 在這裡的作用就是:結束正在執行的流程,並離開函式體返回到呼叫的位置。

函式的文件

函式的文件,一般是寫在函式的名字下面,說明這個函式的用途,因為這個我感覺很重要,之前雖然也說過註釋的重要性,但還是感覺有必要再次說明。

def fibs(n):
 """
 這是一個求斐波那契數列的函式
 """
複製程式碼

在函式的下面,用三對引號的方式包裹著這個函式文件,也叫函式的說明。

比如我們用 dir 來檢視函式物件,比如 dir(type),我們會看到 doc,這個就是文件:

>>> dir(type)
['__abstractmethods__', '__base__', '__bases__', '__basicsize__', '__call__', '__class__', '__delattr__', '__dict__', '__dictoffset__', '__dir__', '__doc__', '__eq__', '__flags__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__instancecheck__', '__itemsize__', '__le__', '__lt__', '__module__', '__mro__', '__name__', '__ne__', '__new__', '__prepare__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasscheck__', '__subclasses__', '__subclasshook__', '__text_signature__', '__weakrefoffset__', 'mro']
>>> type.__doc__
"type(object_or_name, bases, dict)\ntype(object) -> the object's type\ntype(name, bases, dict) -> a new type"
複製程式碼

如果上面的例子在互動模式下的話,用 help(fibs),得到的也是三對引號所包裹的文件資訊,感興趣的可以嘗試一下。

函式的屬性

任何物件都具有屬性,我們前面的文章說過函式是物件,那麼函式也有屬性。

>>> def cau():
...    """this is a cau function"""
...    pass
...
複製程式碼

對於上面的函式,最熟悉的屬性應該就是上面提到的函式文件 doc,它可以用英文句號的方式表示為 cau.__doc__

>>> cau.__doc__
'this is a cau function'
複製程式碼

這就能體現出這種方式表示函式屬性的優勢,只要物件不同,不管你屬性的名字是否相同,用英文句號都可以說明屬性所對應的物件。

我們還可以給物件增加屬性,比如我們給 cau 增加一個 pig 屬性,並設定為 100,順便我們再呼叫一下它:

>>> cau.pig = 100
>>> cau.pig
100
複製程式碼

還記得上面我說的那個檢視物件屬性和方法的 dir 嗎?現在有請它閃亮登場:

>>> dir(cau)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'pig']
複製程式碼

在這裡列出了所有 cau 這個函式物件的屬性和方法,仔細觀察我們會發現,我們剛用過的 doc 和我們新增加的 pig 都在其中,至於你在糾結那些名字前後都是用雙下劃線的,你暫且可以把它們稱之為特殊屬性,所有的這些屬性都是可以用英文句點的方式呼叫,感興趣的可以試一試。

寫在之後

更多內容,歡迎關注公眾號「Python空間」,期待和你的交流。

在這裡插入圖片描述

相關文章