零基礎學習 Python 之函式
寫在之前
今天我們又開始了新的篇章 -- 函式篇,在現代的任何科技門類,乃至於政治學,經濟學等都已經普遍使用函式,可以說函式的出現直接的加快了現代科技和社會的發展,下面就開始我們與 Python 中的函式的初次相見吧。
函式是什麼?
在我們學生時代的數學中,定義函式的方式一般是這樣的:y = ax + b
,這是一個一次函式,當然我們也可以寫成 f(x) = ax + b
,其中 x 是變數,可以代表任何數,但是這個並不是函式的全部,在函式中,其實變數並沒有規定只能是數,它可以是豬狗牛羊,也可以是花鳥木魚,說到這不知道你有沒有理解我的意思,其實,函式就是一種對映。
如果你嘗試著將變數 x
理解為小豬佩奇,那麼 ax + 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
複製程式碼
上面的例子雖然短小,但內有乾坤,下面我以此函式為例,詳述函式的組成。
-
def
:def 是函式的開始,也就是在宣告要建立一個函式的時候,一定要先使用 def,這就是告訴 Python 解析器,這裡要宣告的是一個函式。 -
add
:add 是函式的名稱,在 Python 中起名字的講究就是要起的有意義,能從函式的名字上看出這個函式是幹什麼的。同時函式的命名規範和變數名是一樣的,必須使用字母和下劃線開頭,且僅能含有字母,數字和下劃線。 -
( x,y )
:這個是引數列表,要寫在括號裡,其中的引數指向函式的輸入,本例中函式有兩項輸入,但是通常情況下,輸入的個數可以是任意的,也包括 0 個。 -
:
:冒號非常重要,如果少了,會報錯,所以希望你們不要像我一樣。 -
return a + b
:這一行,就是函式體了,函式體是縮進了 4 個空格的程式碼塊,完成你需要完成的工作。 return 是函式的關鍵字,意思是要返回一個值,函式中的 return 也不是必須要寫的,如果不寫的話, Python 會預設返回一個值,那就是 None。
呼叫函式
在這之前,我們想一下我們為什麼要寫函式?理論上來說,不用函式也可以寫程式碼,之所以用函式,大佬們給我們總結了以下幾點,我在這借花獻佛:
-
寫函式可以降低程式設計的難度。通常將一個複雜的大問題分解成一系列更小的問題,然後小問題再分解成更小的問題,當問題細化到足夠簡單時,就可以分而治之。
-
程式碼重用。其實我們在程式設計的時候比較忌諱同一段程式碼不斷重複,因此有必要將某個常用的功能抽象為一段公用的程式碼,也就是函式。
從上面來看,使用函式還是很有必要的,下面就來看看函式是怎麼呼叫的:
>>> 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空間」,期待和你的交流。