Python之什麼是函數
我們知道圓的面積計算公式爲:
S = πr²
當我們知道半徑r的值時,就可以根據公式計算出面積。假設我們需要計算3個不同大小的圓的面積:
r1 = 12.34 r2 = 9.08 r3 = 73.1 s1 = 3.14 * r1 * r1 s2 = 3.14 * r2 * r2 s3 = 3.14 * r3 * r3
當代碼出現有規律的重複的時候,你就需要當心了,每次寫3.14 * x * x不僅很麻煩,而且,如果要把3.14改成3.14159265359的時候,得全部替換。
有了函數,我們就不再每次寫s = 3.14 * x * x,而是寫成更有意義的函數調用 s = area_of_circle(x)
,而函數 area_of_circle 本身只需要寫一次,就可以多次調用。
抽象是數學中非常常見的概念。舉個例子:
計算數列的和,比如:1 + 2 + 3 + ... + 100,寫起來十分不方便,於是數學家發明了求和符號∑,可以把1 + 2 + 3 + ... + 100記作:
100 ∑n n=1
這種抽象記法非常強大,因爲我們看到∑就可以理解成求和,而不是還原成低級的加法運算。
而且,這種抽象記法是可擴展的,比如:
100 ∑(n²+1) n=1
還原成加法運算就變成了:
(1 x 1 + 1) + (2 x 2 + 1) + (3 x 3 + 1) + ... + (100 x 100 + 1)
可見,藉助抽象,我們才能不關心底層的具體計算過程,而直接在更高的層次上思考問題。
寫計算機程序也是一樣,函數就是最基本的一種代碼抽象的方式。
Python不但能非常靈活地定義函數,而且本身內置了很多有用的函數,可以直接調用。
Python之調用函數
Python內置了很多有用的函數,我們可以直接調用。
要調用一個函數,需要知道函數的名稱和參數,比如求絕對值的函數 abs,它接收一個參數。
可以直接從Python的官方網站查看文檔: http://docs.python.org/2/library/functions.html#abs
也可以在交互式命令行通過 help(abs) 查看abs函數的幫助信息。
調用 abs 函數:
>>> abs(100) 100 >>> abs(-20) 20 >>> abs(12.34) 12.34
調用函數的時候,如果傳入的參數數量不對,會報TypeError的錯誤,並且Python會明確地告訴你:abs()有且僅有1個參數,但給出了兩個:
>>> abs(1, 2) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: abs() takes exactly one argument (2 given)
如果傳入的參數數量是對的,但參數類型不能被函數所接受,也會報TypeError的錯誤,並且給出錯誤信息:str是錯誤的參數類型:
>>> abs('a') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: bad operand type for abs(): 'str'
而比較函數 cmp(x, y) 就需要兩個參數,如果 x<y,返回 -1,如果 x==y,返回 0,如果 x>y,返回 1:
>>> cmp(1, 2) -1 >>> cmp(2, 1) 1 >>> cmp(3, 3) 0
Python內置的常用函數還包括數據類型轉換函數,比如 int()函數可以把其他數據類型轉換爲整數:
>>> int('123') 123 >>> int(12.34) 12
str()函數把其他類型轉換成 str:
>>> str(123) '123' >>> str(1.23) '1.23'
Python之編寫函數
在Python中,定義一個函數要使用 def 語句,依次寫出函數名、括號、括號中的參數和冒號:,然後,在縮進塊中編寫函數體,函數的返回值用 return 語句返回。
我們以自定義一個求絕對值的 my_abs 函數爲例:
def my_abs(x): if x >= 0: return x else: return -x
請注意,函數體內部的語句在執行時,一旦執行到return時,函數就執行完畢,並將結果返回。因此,函數內部通過條件判斷和循環可以實現非常複雜的邏輯。
如果沒有return語句,函數執行完畢後也會返回結果,只是結果爲 None。
return None可以簡寫爲return。
Python函數之返回多值
函數可以返回多個值嗎?答案是肯定的。
比如在遊戲中經常需要從一個點移動到另一個點,給出座標、位移和角度,就可以計算出新的座標:
# math包提供了sin()和 cos()函數,我們先用import引用它:
import math def move(x, y, step, angle): nx = x + step * math.cos(angle) ny = y - step * math.sin(angle) return nx, ny
這樣我們就可以同時獲得返回值:
>>> x, y = move(100, 100, 60, math.pi / 6) >>> print x, y 151.961524227 70.0
但其實這只是一種假象,Python函數返回的仍然是單一值:
>>> r = move(100, 100, 60, math.pi / 6) >>> print r (151.96152422706632, 70.0)
用print打印返回結果,原來返回值是一個tuple!
但是,在語法上,返回一個tuple可以省略括號,而多個變量可以同時接收一個tuple,按位置賦給對應的值,所以,Python的函數返回多值其實就是返回一個tuple,但寫起來更方便。
Python之遞歸函數
在函數內部,可以調用其他函數。如果一個函數在內部調用自身本身,這個函數就是遞歸函數。
舉個例子,我們來計算階乘 n! = 1 * 2 * 3 * ... * n,用函數 fact(n)表示,可以看出:
fact(n) = n! = 1 * 2 * 3 * ... * (n-1) * n = (n-1)! * n = fact(n-1) * n
所以,fact(n)可以表示爲 n * fact(n-1),只有n=1時需要特殊處理。
於是,fact(n)用遞歸的方式寫出來就是:
def fact(n): if n==1: return 1 return n * fact(n - 1)
上面就是一個遞歸函數。可以試試:
>>> fact(1) 1 >>> fact(5) 120 >>> fact(100) 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L
如果我們計算fact(5),可以根據函數定義看到計算過程如下:
===> fact(5) ===> 5 * fact(4) ===> 5 * (4 * fact(3)) ===> 5 * (4 * (3 * fact(2))) ===> 5 * (4 * (3 * (2 * fact(1)))) ===> 5 * (4 * (3 * (2 * 1))) ===> 5 * (4 * (3 * 2)) ===> 5 * (4 * 6) ===> 5 * 24 ===> 120
遞歸函數的優點是定義簡單,邏輯清晰。理論上,所有的遞歸函數都可以寫成循環的方式,但循環的邏輯不如遞歸清晰。
使用遞歸函數需要注意防止棧溢出。在計算機中,函數調用是通過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞歸調用的次數過多,會導致棧溢出。可以試試計算 fact(10000)。