今天咱們又開始了新的篇章 -- 函數篇,在現代的任何科技門類,乃至於政治學,經濟學等都已經廣泛使用函數,能夠說函數的出現直接的加快了現代科技和社會的發展,下面就開始咱們與 Python 中的函數的初次相見吧。python
在咱們學生時代的數學中,定義函數的方式通常是這樣的:y = ax + b
,這是一個一次函數,固然咱們也能夠寫成 f(x) = ax + b
,其中 x 是變量,能夠表明任何數,可是這個並非函數的所有,在函數中,其實變量並無規定只能是數,它能夠是豬狗牛羊,也能夠是花鳥木魚,說到這不知道你有沒有理解個人意思,其實,函數就是一種映射。git
若是你嘗試着將變量 x
理解爲小豬佩奇,那麼 ax + b
就是 a 個佩奇再加上 b,這個結果對應着的是另外一個東西,好比熊大,即咱們能夠理解爲 a 個佩奇加上 b 就對應的是熊大,這就是咱們所說的映射關係。編程
若是你理解了這些,咱們下面用純粹的中學的數學方式,在 Python 中創建函數:bash
>>> x = 6
>>> y = 2 * x + 1
>>> y
13
複製代碼
在咱們的學生時代咱們就是這麼用的,那麼在 Python 中這種方式還有用嗎?上面的例子咱們創建了一個所謂的函數,那麼咱們來嘗試改變一下 x 的值:app
>>> x = 7
>>> y
13
複製代碼
結果是 y 的值並無改變,因此說用純粹的數學方式定義函數在 Python 中其實並無什麼用,因此咱們要用一種新的定義函數的方式,請接着向下看。ssh
在 Python 中定義了函數的格式,下面我舉一個例子來講一下 Python 中函數的格式和調用的方法:函數
>>> def add(x,y):
... return x + y
複製代碼
上面的例子雖然短小,但內有乾坤,下面我以此函數爲例,詳述函數的組成。學習
def
:def 是函數的開始,也就是在聲明要建立一個函數的時候,必定要先使用 def,這就是告訴 Python 解析器,這裏要聲明的是一個函數。ui
add
:add 是函數的名稱,在 Python 中起名字的講究就是要起的有意義,能從函數的名字上看出這個函數是幹什麼的。同時函數的命名規範和變量名是同樣的,必須使用字母和下劃線開頭,且僅能含有字母,數字和下劃線。this
( 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空間」,期待和你的交流。