函數是組織好的,可重覆使用的,用來實現單一,或相關聯功能的代碼段。函數能提高應用的模塊性,和代碼的重覆利用率。你已經知道Python提供了許多內建函數,比如print()。但你也可以自己創建函數,這被叫做用戶自定義函數。 1、語法 Python 定義函數使用 def 關鍵字,一般格式如下: 預設情況 ...
函數是組織好的,可重覆使用的,用來實現單一,或相關聯功能的代碼段。
函數能提高應用的模塊性,和代碼的重覆利用率。你已經知道Python提供了許多內建函數,比如print()。但你也可以自己創建函數,這被叫做用戶自定義函數。
1、語法
Python 定義函數使用 def 關鍵字,一般格式如下:
def 函數名(參數列表): 函數體
預設情況下,參數值和參數名稱是按函數聲明中定義的的順序匹配起來的。
定義一個由自己想要功能的函數,以下是簡單的規則:
- 函數代碼塊以 def 關鍵詞開頭,後接函數標識符名稱和圓括弧 ()。
- 任何傳入參數和自變數必須放在圓括弧中間,圓括弧之間可以用於定義參數。
- 函數的第一行語句可以選擇性地使用文檔字元串——用於存放函數說明。
- 函數內容以冒號起始,並且縮進。
- return [表達式] 結束函數,選擇性地返回一個值給調用方。不帶表達式的return相當於返回 None。
實例:
def hello(): return 'Hello world' def say(msg): print(msg) def sun(num1, num2): print('num1 + num2 =', num1, num2)
2、函數調用
Python內置了很多有用的函數,我們可以直接調用。要調用一個函數,需要知道函數的名稱和參數,比如求絕對值的函數abs,只有一個參數:
print('100的絕對值:', abs(100)) print('-20的絕對值:', abs(-20))
以上代碼,輸出:
100的絕對值: 100 -20的絕對值: 20
定義一個函數:給了函數一個名稱,指定了函數里包含的參數,和代碼塊結構。
這個函數的基本結構完成以後,你可以通過另一個函數調用執行,也可以直接從 Python 命令提示符執行。
3、參數傳遞
在 python 中,類型屬於對象,變數是沒有類型的:
a=[1,2,3] a="Python"
以上代碼中,[1,2,3] 是 List 類型,"Python" 是 String 類型,而變數 a 是沒有類型,它僅僅是一個對象的引用(一個指針),可以是 List 類型對象,也可以指向 String 類型對象。
python 函數的參數傳遞:
- 不可變類型:類似 c++ 的值傳遞,如 整數、字元串、元組。如fun(a),傳遞的只是a的值,沒有影響a對象本身。比如在 fun(a)內部修改 a 的值,只是修改另一個複製的對象,不會影響 a 本身。
- 可變類型:類似 c++ 的引用傳遞,如 列表,字典。如 fun(la),則是將 la 真正的傳過去,修改後fun外部的la也會受影響
python 中一切都是對象,嚴格意義我們不能說值傳遞還是引用傳遞,我們應該說傳不可變對象和傳可變對象。
什麼是可更改(mutable)與不可更改(immutable)對象?
在 python 中,str, tuple, 和 number 是不可更改的對象,而 list,dict 等則是可以修改的對象。例如:
不可變類型:變數賦值 a=5 後再賦值 a=10,這裡實際是新生成一個 int 值對象 10,再讓 a 指向它,而 5 被丟棄,不是改變a的值,相當於新生成了a。
可變類型:變數賦值 la=[1,2,3,4] 後再賦值 la[2]=5 則是將 list la 的第三個元素值更改,本身la沒有動,只是其內部的一部分值被修改了。
不可變對象在函數里修改了參數,原始的參數是不會改變的。例如:
def ChangeInt(a): a = 10 b = 2 ChangeInt(b) print(b) # 結果是 2
實例中有 int 對象 2,指向它的變數是 b,在傳遞給 ChangeInt 函數時,按傳值的方式複製了變數 b,a 和 b 都指向了同一個 Int 對象,在 a=10 時,則新生成一個 int 值對象 10,並讓 a 指向它。
可變對象在函數里修改了參數,那麼原始的參數也被改變了。例如:
# 可寫函數說明 def changeme(mylist): "修改傳入的列表" mylist.append([1, 2, 3, 4]) print("函數內取值: ", mylist) return # 調用changeme函數 mylist2 = [10, 20, 30] changeme(mylist2) print("函數外取值: ", mylist2)
傳入函數的和在末尾添加新內容的對象用的是同一個引用。故輸出結果如下:
函數內取值: [10, 20, 30, [1, 2, 3, 4]]
函數外取值: [10, 20, 30, [1, 2, 3, 4]]
4、參數
- 必需參數
- 關鍵字參數
- 預設參數
- 不定長參數
什麼是必需參數?必需參數須以正確的順序傳入函數。調用時的數量必須和聲明時的一樣。下麵的實例中調用say()函數,你必須傳入一個參數,不然會出現語法錯誤:
def say(msg): print(msg) # 列印傳入的參數 #調用say say('hello world')
以上代碼,輸出:
hello world
但,我們如下調用時,就會報錯:
def say(msg): print(msg) # 列印傳入的參數 #調用say say()
以上代碼,報錯:
TypeError: say() missing 1 required positional argument: 'msg'
調用函數的時候,如果傳入的參數數量不對,會報TypeError的錯誤,並且Python會明確地告訴你。
如果傳入的參數數量是對的,但參數類型不能被函數所接受,也會報TypeError的錯誤,並且給出錯誤信息:str是錯誤的參數類型:
print(abs('abc'))
以上代碼,會報錯:
TypeError: bad operand type for abs(): 'str'
什麼是關鍵字參數?關鍵字參數和函數調用關係緊密,函數調用使用關鍵字參數來確定傳入的參數值。使用關鍵字參數允許函數調用時參數的順序與聲明時不一致,因為 Python 解釋器能夠用參數名匹配參數值。
我們來看實例:
# 申明一個函數say,第一個參數為:name,第二個參數為:msg def say(name, msg): print('%s 說:%s' %(name, msg)) # 列印傳入的參數 # 普通方式調用say() say('roy', 'hello world') # 用關鍵字參數的方式調用say() say(msg='這是用關鍵字參數的方式調用的', name='roy')
以上代碼,輸出:
roy 說:hello world
roy 說:這是用關鍵字參數的方式調用的
我們可以看出,在第二次調用的時候,我們根本沒有按照函數申明時的參數順序去傳遞參數,但也是正確的輸出了內容。
什麼是預設參數?調用函數時,如果沒有傳遞參數,則會使用預設參數。以下實例中如果沒有傳入 msg 參數,則使用預設值:
# 申明一個函數say,第一個參數為:name,第二個參數為:msg def say(name, msg='hello world'): print('%s 說:%s' %(name, msg)) # 列印傳入的參數 # 調用 say('roy')
以上代碼,輸出:
roy 說:hello world
設置預設參數時,有幾點要註意:
一是必選參數在前,預設參數在後,否則Python的解釋器會報錯(思考一下為什麼預設參數不能放在必選參數前面);
二是如何設置預設參數。當函數有多個參數時,把變化大的參數放前面,變化小的參數放後面。變化小的參數就可以作為預設參數。
什麼是不定長參數?你可能需要一個函數能處理比當初聲明時更多的參數。這些參數叫做不定長參數,和上述幾種參數不同,聲明時不會命名。基本語法如下:
def functionname([formal_args,] *var_args_tuple ): "函數_文檔字元串" function_suite return [expression]
加了星號(*)的變數名會存放所有未命名的變數參數。如果在函數調用時沒有指定參數,它就是一個空元組。我們也可以不向函數傳遞未命名的變數。如下實例:
# 可寫函數說明 def printinfo(arg1, *vartuple): "列印任何傳入的參數" print("輸出: ") print(arg1) for var in vartuple: print(var) return # 調用printinfo 函數 printinfo(10) printinfo(70, 60, 50)
以上代碼,輸出:
輸出: 10 輸出: 70 60 50
5、匿名函數
python 使用 lambda 來創建匿名函數。
所謂匿名,意即不再使用 def 語句這樣標準的形式定義一個函數。
- lambda 只是一個表達式,函數體比 def 簡單很多。
- lambda的主體是一個表達式,而不是一個代碼塊。僅僅能在lambda表達式中封裝有限的邏輯進去。
- lambda 函數擁有自己的命名空間,且不能訪問自己參數列表之外或全局命名空間里的參數。
- 雖然lambda函數看起來只能寫一行,卻不等同於C或C++的內聯函數,後者的目的是調用小函數時不占用棧記憶體從而增加運行效率。
lambda 函數的語法只包含一個語句,如下:
lambda [arg1 [,arg2,.....argn]]:expression
實例:
# 可寫函數說明 sum = lambda arg1, arg2: arg1 + arg2 # 調用sum函數 print("相加後的值為 : ", sum(10, 20)) print("相加後的值為 : ", sum(20, 20))
以上代碼,輸出:
相加後的值為 : 30
相加後的值為 : 40
有些時候,當我們在傳入函數時,不需要顯式地定義函數,直接傳入匿名函數更方便。匿名函數有個限制,就是只能有一個表達式,無需寫return,返回值就是該表達式的結果。用匿名函數有個好處,因為函數沒有名字,不必擔心函數名衝突。此外,匿名函數也是一個函數對象,也可以把匿名函數賦值給一個變數,再利用變數來調用該函數
6、return語句
return [表達式] 語句用於退出函數,選擇性地向調用方返回一個表達式。不帶參數值的return語句返回None。
實例:
# 這是一個有return語句的函數 def hello(): return 'Hello world' # 這是一個有return語句的函數,預設返回None def say(msg): print(msg) # 調用 hello 並把它的結果賦值給 變數 msg = hello() print(msg) # 調用 say say('您好')
以上代碼,輸出:
Hello world
您好
7、註釋
函數的第一行語句可以選擇性地使用文檔字元串——用於存放函數說明,例如:
def test(): """ this is test :return: int string """ return 1
可以調用 __doc__ 查看函數的文檔:
def test(): """ this is test :return: int string """ return 1 print(test.__doc__)
以上代碼,輸出:
this is test :return: int string
在python3中你還可以這樣為函數添加註釋:
def dog(name:str, age:(1, 99), species:'狗狗的品種') -> tuple: return (name, age, species)
如上,可以使用:對參數逐個進行註釋,註釋內容可以是任何形式,比如參數的類型、作用、取值範圍等等,返回值使用->標註,所有的註釋都會保存至函數的屬性。
查看這些註釋可以通過 __annotations__ 獲取,結果會議字典的形式返回:
def dog(name:str, age:(1, 99), species:'狗狗的品種') -> tuple: return (name, age, species) print(dog.__annotations__)
以上代碼,輸出:
{'name': <class 'str'>, 'age': (1, 99), 'species': '狗狗的品種', 'return': <class 'tuple'>}
另外,使用函數註釋並不影響預設參數的使用:
def dog(name:str ='dobi', age:(1, 99) =3, species:'狗狗的品種' ='Labrador') -> tuple: return (name, age, species) print(dog())
以上代碼,輸出:
('dobi', 3, 'Labrador')