導讀: 1.函數的定義 2.函數的文檔註釋 3.函數的參數與調用 4.函數參數的類型(重點) 5.函數的返回值 6.函數的嵌套調用 7.局部變數和全局變數 所謂函數,就是把 具有獨立功能的代碼塊 組織為一個小模塊,在需要的時候 調用。 函數的使用包含兩個步驟: 1.定義函數 —— 封裝 獨立的功能 ...
導讀:
1.函數的定義
2.函數的文檔註釋
3.函數的參數與調用
4.函數參數的類型(重點)
5.函數的返回值
6.函數的嵌套調用
7.局部變數和全局變數
所謂函數,就是把 具有獨立功能的代碼塊 組織為一個小模塊,在需要的時候 調用。
函數的使用包含兩個步驟:
1.定義函數 —— 封裝 獨立的功能
2.調用函數 —— 享受 封裝 的成果
函數的作用:在開發程式時,使用函數可以提高編寫的效率以及代碼的 重用 。
函數的特點:功能性;隱藏細節;避免編寫重覆的代碼
一、函數的定義
定義函數使用關鍵字 def ,後接函數名和放在圓括弧( )中的可選參數列表,函數內容以冒號起始並且縮進。一般格式如下:
def 函數名(參數列表):
"""文檔字元串"""
函數體
return [expression]
註意:參數列表可選,文檔字元串可選,return語句可選。
示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def fib(n):
"""Print a Fibonacci series"""
a, b = 0, 1
while b < n:
print(b, end=' ')
a, b = b, a+b
print()
fib(2000) # call
f = fib # assignment
f(2000)
code demo
函數名的值是一種用戶自定義的函數類型。函數名的值可以被賦予另一個名字,使其也能作為函數使用。
1.函數名稱 應該能夠表達 函數封裝代碼 的功能,方便後續的調用
2.函數名稱 的命名應該 符合 標識符的命名規則
二、函數的文檔註釋
函數體的第一個語句可以是三引號括起來的字元串, 這個字元串就是函數的文檔字元串,或稱為docstring 。我們可以使用 print(function.__doc__) 輸出文檔:
def fun():
"""Some information of this function.
This is documentation string."""
return
print(fun.__doc__)
文檔字元串主要用於描述一些關於函數的信息,讓用戶交互地瀏覽和輸出。建議養成在代碼中添加文檔字元串的好習慣。
在 函數調用 位置,使用help(函數名)快捷鍵 CTRL + Q 可以查看函數的說明信息。
註意:
因為 函數體相對比較獨立,函數定義的上方 ,應該和其他代碼(包括註釋)保留兩個空行。
小技巧:
在給函數添加註釋時,可以滑鼠游標放在 函數名 上,pycharm上會出現一個小燈,選擇 ”insert documentation string stub" 會自動添加註釋模板。
三、函數的參數與調用
3.1 參數的使用
在函數名的後面的小括弧內部填寫 參數;多個參數之間使用 , 分隔。
def sum_2_num(num1, num2): result = num1 + num2 print("%d + %d = %d" % (num1, num2, result)) sum_2_num(50, 20)
3.2 參數的作用
函數的參數,增加函數的 通用性,針對 相同的數據處理邏輯,能夠 適應更多的數據 在函數內部,把參數當做 變數 使用,進行需要的數據處理。
函數調用時,按照函數定義的參數順序,把 希望在函數內部處理的數據,通過參數傳遞。
3.3 形參和實參
形參:定義函數時,小括弧中的參數,是用來接收參數用的,在函數內部作為變數使用。
實參:調用函數時,小括弧中的參數,是用來把數據傳遞到函數內部用的。
3.4 函數的調用
通過 函數名() 即可完成對函數的調用。
# 只有在調用函數時,之前定義的函數才會被執行。
# 函數執行完成之後,會重新回到之前的程式中,繼續執行後續的代碼。
# 調用時,實參的個數和先後順序應該和定義函數中要求的一致。
註意:
不能將 函數調用 放在 函數定義 的上方!
因為在 使用函數名 調用函數之前,必須要保證Python 已經知道函數的存在,否則控制台會提示 NameError: name 'say_hello' is not defined (名稱錯誤:say_hello 這個名字沒有被定義)
四、函數參數的類型
4.1 必須參數
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def add(x, y): result = x + y return result c = add(2, 3) print(c) # 5View Code
4.2 預設參數(預設參數)
在形參中預設有值的參數,稱之為預設參數。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def add(x, y = 3, z=4): result = x + y +z return result c = add(2) #c = add(x = 2) print(c) # 9View Code
預設參數必須放在必須參數之後。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def add(x, y = 3, z=4): result = x + y +z return result c = add(2, 5) print(c) # 11View Code
調用時如果要改變預設參數的值,最好,使用關鍵字參數。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def add(x, y = 3, z=4): result = x + y +z return result c = add(2, z= 10) #c = add(x = 2) print(c) # 15View Code
關鍵字參數必須放在其他參數之後。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def add(x, y = 3, z=4, w=5): result = x + y +z return result c = add(2, y= 6, 4, w=7) #c = add(x = 2) print(c) # 報錯View Code
註意:通常情況下預設值只被計算一次,但如果預設值是一個可變對象時會有所不同, 如列表, 字典, 或 大多類的對象時。例如,下麵的函數在隨後的調用中會累積參數值:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def fun(a, L=[]): L.append(a) print(L) fun(1) # 輸出[1] fun(2) # 輸出[1, 2] fun(3) # 輸出[1, 2, 3]View Code
預設參數必須指向不變對象:
先定義一個函數,傳入一個 list,添加一個 END 再返回:
def add_end(L=[]): L.append('END') return L
當你正常調用時,結果似乎不錯:
>>> add_end([1, 2, 3]) [1, 2, 3, 'END'] >>> add_end(['x', 'y', 'z']) ['x', 'y', 'z', 'END']
當你使用預設參數調用時,一開始結果也是對的:
>>> add_end() ['END']
但是,再次調用 add_end()時,結果就不對了:
>>> add_end() ['END', 'END'] >>> add_end() ['END', 'END', 'END']
很多初學者很疑惑,預設參數是[],但是函數似乎每次都“記住了”上次添加了'END'後的 list。
原因解釋如下:
Python 函數在定義的時候,預設參數 L 的值就被計算出來了,即[],因為預設參數 L 也是一個變數,它指向對象[],每次調用該函數,如果改變了 L 的內容,則下次調用時,預設參數的內容就變了,不再是函數定義時的[]了。
所以,定義預設參數要牢記一點:預設參數必須指向不變對象!
要修改上面的例子,我們可以用 None 這個不變對象來實現:
def add_end(L=None): if L is None: L = [] L.append('END') return L
現在,無論調用多少次,都不會有問題:
>>> add_end() ['END'] >>> add_end() ['END']
為什麼要設計 str、 None 這樣的不變對象呢?因為不變對象一旦創建,對象內部的數據就不能修改,這樣就減少了由於修改數據導致的錯誤。
此外,由於對象不變,多任務環境下同時讀取對象不需要加鎖,同時讀一點問題都沒有。我們在編寫程式時,如果可以設計一個不變對象,那就儘量設計成不變對象。
4.3 關鍵字參數
函數也可以通過 keyword=value 形式的關鍵字參數來調用,因為明確指出了對應關係,所以參數的順序也就無關緊要了。
使用關鍵字參數,可以無視參數順序。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def add(x, y): result = x + y return result c = add(y = 3, x = 2) print(c) # 5View Code
4.4 可變參數(不定長參數)
有時可能需要一個函數能處理比當初聲明時更多的參數, 這些參數叫做不定長參數,聲明時不會命名。
通過在形參前加一個星號(*)或兩個星號(**)來指定函數可以接收任意數量的實參。
基本語法如下:
def functionname([formal_args,] *args, **kwargs):
"""函數_文檔字元串"""
function_suite
return [expression]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def fun(*args): print(type(args)) print(args) fun(1,2,3,4,5,6) # 輸出: # <class 'tuple'> # (1, 2, 3, 4, 5, 6) def fun(**args): print(type(args)) print(args) fun(a=1,b=2,c=3,d=4,e=5) # 輸出: # <class 'dict'> # {'d': 4, 'e': 5, 'b': 2, 'c': 3, 'a': 1}View Code
從兩個示例的輸出可以看出:
當參數形如 *args 時,會存放所有未命名的變數參數,傳遞給函數的任意個實參會按位置被包裝進一個元組(tuple);
當參數形如 **kwargs 時,會存放命名參數,傳遞給函數的任意個 key=value 實參會被包裝進一個字典(dict)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
>>> def fun(a, b, *args, **kwargs): ... """可變參數演示示例""" ... print "a =", a ... print "b =", b ... print "args =", args ... print "kwargs: " ... for key, value in kwargs.items(): ... print key, "=", value ... >>> fun(1, 2, 3, 4, 5, m=6, n=7, p=8) # 註意傳遞的參數對應 a = 1 b = 2 args = (3, 4, 5) kwargs: p = 8 m = 6 n = 7 >>> c = (3, 4, 5) >>> d = {"m":6, "n":7, "p":8} >>> fun(1, 2, *c, **d) # 註意元組與字典的傳參方式 # 結果同上 >>> fun(1, 2, c, d) # 註意不加星號與上面的區別 a = 1 b = 2 args = ((3, 4, 5), {'p': 8, 'm': 6, 'n': 7}) kwargs:View Code
如果很多個值都是不定長參數,那麼這種情況下,可以將預設參數放到*args的後面,但如果有**kwargs的話,**kwargs必須是最後的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def sum_nums_3(a, *args, b=22, c=33, **kwargs): print(a) print(b) print(c) print(args) print(kwargs) sum_nums_3(100, 200, 300, 400, 500, 600, 700, b=1, c=2, mm=800, nn=900) # 100 # 1 # 2 # (200, 300, 400, 500, 600, 700) # {'mm': 800, 'nn': 900}View Code
說明:
如果很多個值都是不定長參數,那麼這種情況下,可以將預設參數放到 *args的後面, 但如果有**kwargs的話,**kwargs必須是最後的。
4.5 解包參數
上一點說到傳遞任意數量的實參時會將它們打包進一個元組或字典,當然有打包也就有解包(unpacking)。通過 單星號和雙星號對List、Tuple和Dictionary進行解包:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def fun(a=1, b=2, c=3): print(a+b+c) fun() # 正常調用 list1 = [11, 22, 33] dict1 = {'a':40, 'b':50, 'c':60} fun(*list1) # 解包列表 fun(**dict1) # 解包字典 # 輸出: # 6 # 66 # 150View Code
註:*用於解包Sequence,**用於解包字典。需要拆的數據的個數要與變數的個數相同,否則程式會異常。解包字典會得到一系列的 key=value ,故本質上就是使用關鍵字參數調用函數。
解包:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
d = 1, 2, 3 print(type(d)) # <class 'tuple'> a, b, c = d print(a) # 1 print(b) # 2 print(c) # 3View Code
4.6 命名關鍵字參數
對於關鍵字參數,函數的調用者可以傳入任意不受限制的關鍵字參數。至於到底傳入了哪些,就需要在函數內部通過 kw 檢查。
仍以 person()函數為例,我們希望檢查是否有 city 和 job 參數:
def person(name, age, **kw): if 'city' in kw: # 有 city 參數 pass if 'job' in kw: # 有 job 參數 pass print('name:', name, 'age:', age, 'other:', kw) 但是調用者仍可以傳入不受限制的關鍵字參數: >>> person('Jack', 24, city='Beijing', addr='Chaoyang', zipcode=123456)
如果要限制關鍵字參數的名字,就可以用命名關鍵字參數,例如,只接收 city 和 job 作為關鍵字參數。這種方式定義的函數如下:
def person(name, age, *, city, job): print(name, age, city, job)
和關鍵字參數**kw 不同,命名關鍵字參數需要一個特殊分隔符*, *後面
的參數被視為命名關鍵字參數。
調用方式如下:
>>> person('Jack', 24, city='Beijing', job='Engineer') Jack 24 Beijing Engineer
命名關鍵字參數必須傳入參數名,這和位置參數不同。如果沒有傳入參數名,調用將報錯:
>>> person('Jack', 24, 'Beijing', 'Engineer') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: person() takes 2 positional arguments but 4 were given
由於調用時缺少參數名 city 和 job, Python 解釋器把這 4 個參數均視為位置參數,但 person()函數僅接受 2 個位置參數。
命名關鍵字參數可以有預設值,從而簡化調用:
def person(name, age, *, city='Beijing', job): print(name, age, city, job)
由於命名關鍵字參數 city 具有預設值,調用時,可不傳入 city 參數:
>>> person('Jack', 24, job='Engineer') Jack 24 Beijing Engineer
使用命名關鍵字參數時,要特別註意, *不是參數,而是特殊分隔符。
如果缺少*, Python 解釋器將無法識別位置參數和命名關鍵字參數:
def person(name, age, city, job): # 缺少 *, city 和 job 被視為位置參數 pass
可變參數無法和命名關鍵字參數混合。
五、函數的返回值
在程式開發中,有時候,會希望一個函數執行結束後,告訴調用者一個結果,以便調用者針對具體的結果做後續的處理。
返回值是函數完成工作後,最後給調用者的一個結果。在函數中使用 return 關鍵字可以返回結果。
調用函數一方,可以使用變數來接收函數的返回結果。
return後面可以是元組,列表、字典等,只要是能夠存儲多個數據的類型,就可以一次性返回多個數據。
如果return後面有多個數據,那麼預設是元組。
如果函數沒有返回值,仍然為變數賦值,則變數的值為None。
註意:return 表示返回,後續的代碼都不會被執行。
def sum_2_num(num1, num2): """對兩個數字的求和""" return num1 + num2 # 調用函數,並使用result變數接收計算結果 result = sum_2_num(10, 20) print("計算結果是 %d" % result)
在python中我們可不可以返回多個值?
>>> def divid(a, b): ... shang = a//b ... yushu = a%b ... return shang, yushu ... >>> sh, yu = divid(5, 2) >>> sh 5 >>> yu 1
本質是利用了元組。
六、函數的嵌套調用
一個函數裡面 又調用 了 另外一個函數,這就是 函數嵌套調用。
如果函數test2中,調用了另外一個函數test1那麼執行到調用 test1 函數時,會先把函數test1 中的任務都執行完,才會回到 test2 中調用函數 test1 的位置,繼續執行後續的代碼。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def test1(): print("*" * 20) def test2(): print("-" * 20) test1() print("-" * 20) test2() 結果: -------------------- ******************** --------------------View Code
6.1 列印分隔線案例
需求 1
定義一個函數能夠列印 任意重覆次數 的分隔線。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def print_line(char, times): print(char * times)View Code
需求 2
定義一個函數能夠列印 5行 的分隔線,分隔線要求符合需求1。
提示:工作中針對需求的變化,應該冷靜思考, 不要輕易修改之前已經完成的,能夠正常執行的函數!
def print_line(char, times): print(char * times) def print_lines(char, times): row = 0 while row < 5: print_line(char, times) row += 1
6.2 求3個數的平均值
需求1
求3個數的和
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def sum3Number(a,b,c): return a+b+c # return 的後面可以是數值,也可是一個表達式View Code
需求2
完成對3個數求平均值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def average3Number(a,b,c): # 因為sum3Number函數已經完成了3個數的就和,所以只需調用即可 # 即把接收到的3個數,當做實參傳遞即可 sumResult = sum3Number(a,b,c) aveResult = sumResult/3.0 return aveResult # 調用函數,完成對3個數求平均值 result = average3Number(11,2,55) print("average is %d"%result)View Code
七、局部變數和全局變數
局部變數:
局部變數,就是在函數內部定義的變數。
不同的函數,可以定義相同的名字的局部變數,但是各用個的不會產生影響。
局部變數的作用,為了臨時保存數據需要在函數中定義變數來進行存儲,這就是它的作用。
全局變數:
在函數外邊定義的變數叫做全局變數。
全局變數能夠在所有的函數中進行訪問。
如果在函數中修改全局變數,那麼就需要使用global進行聲明,否則出錯。
如果全局變數的名字和局部變數的名字相同,那麼使用的是局部變數的,小技巧:強龍不壓地頭蛇。
在函數內部定義的變數擁有一個局部作用域,在函數外定義的擁有全局作用域。
註意:在函數內部可以引用全局變數,但無法對其賦值(除非用 global 進行聲明)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
a = 5 #全局變數a def func1(): print('func1() print a=',a) def func2(): a = 21 # 局部變數a print('func2() print a =', a) def func3(): global a a = 10 # 修改全局變數a print('func3() print a =', a) func1() #5 func2() #21 func3() #10 print('the global a =', a) #10View Code
可變類型的全局變數:
在函數中不使用global聲明全局變數時不能修改全局變數的本質是不能修改全局變數的指向,即不能將全局變數指向新的數據。
對於不可變類型的全局變數來說,因其指向的數據不能修改,所以不使用global時無法修改全局變數。
對於可變類型的全局變數來說,因其指向的數據可以修改,所以不使用global時也可修改全局變數。
a = 1 def f(): a += 1 print(a) f() # 報錯 li = [1,] def f2(): li.append(1) print (li) f2() # [1, 1] print(li) # [1, 1]