函數是一段具備特定功能的、可複用的語句組。python中函數用函數名來表示,並經過函數名進行功能調用。它是一種功能抽象,與黑盒相似,因此只要瞭解函數的輸入輸出方式便可,不用深究內部實現原理。函數的最突出優勢是:數組
在python中能夠把函數分爲:系統自帶函數、第三方庫函數、自定義函數。須要重點掌握的是「自定義函數」。app
自定義函數函數
自定義函數語法: def 函數名([參數列表]): 函數體 return語句 # 示例 def add1(x): x = x + 1 return x
函數經過「參數」和「返回值」來傳遞信息,並經過「參數列表」和「return語句」實現對二者的控制,詳見下圖:
學習
注意事項:測試
在定義好函數以後,有兩種方式對其進行調用:code
# 從本文件調用 def add1(x): x = x+2 return x add1(10) # 從其餘文件調用:從名爲addx的文件調用已經定義好的add1函數 import os os.chdir('D:\\data\\python_file') # 從其餘文件調用方法1 import addx addx.add1(4) # 從其餘文件調用方法2 from addx import add1 add1(9)
程序入口對象
如C、C++等語言都有一個main函數做爲程序的入口,main去調用函數庫,函數庫之間不能相互調用(以下圖A和B之間),即程序的運行是從main函數開始。而python是腳本語言,沒有統一入口,函數庫之間能夠互相調用。排序
因此,python代碼執行有兩種狀況:遞歸
而python經過if__name__ == 'main' : 語句,控制這兩種不一樣狀況下的代碼執行。
if__name__ == 'main' : 模擬入口
基於python代碼執行機理,可用if__name__ == 'main' : 語句模擬程序入口,實現對python代碼執行的控制。
# 直接做爲腳本執行 def add1(x): x = x+2 return x add1(10) # 模塊重用執行 import os os.chdir('D:\\data\\python_file') from addx import add1 add1(9)
因此,想運行以腳本直接執行時才執行的命令時,能夠將這些命令語句放在if__name__ == 'main': 判斷語句以後:
# 自定義一個函數add1 def add1(a): a=a+1 return a print(__name__) if __name__ == "__main__": # 腳本直接執行時,運行後面的語句;被import執行時,不運行後面的語句 print(add1(2)) # 函數正確性驗證和測試
從上面可知,函數最重要的三部分就是參數、函數體、返回值,而參數分爲形參和實參:
注意事項:
def printmax(a,b): # a, b是形參 if a>b: print(a) printmax(3,4) # 3, 4是實參 # 形參修改不影響實參 def add2(x): x = x+2 return x x = 10 print(add2(x)) print(x) # 形參修改影響實參 def add2(x): x.append(2) return x y = [1, 1] # 實參y是變序列(列表、字典、集合) print(add2(y)) # 函數返回值 print(y) # 修改形參影響實參
在定義函數時無需指定形參類型,在調用函數時,python會根據實參類型來自動推斷。而定義函數和調用函數的過程,能夠簡化爲下面圖中三步,實質就是經過參數和返回值傳遞信息,而參數的傳遞發生在第一和第三步。
函數參數多種多樣,根據參數傳遞發生的前後順序,能夠從兩個角度學習常見的一些參數:
同時,這些參數能夠組合使用(可變參數沒法和關鍵字參數組合),且參數定義的順序從左至右分別是:位置參數 >> 默認值參數 >> 可變參數 / 關鍵字參數 / 命名關鍵字參數。參數傳遞還有一種高級用法——參數傳遞的序列解包。
默認值參數
默認參數就是在調用函數的時候使用一些包含默認值的參數。
# 默認值參數b=5, c=10 def demo(a, b=5, c=10): print(a, b, c) demo(1, 2)
注意事項:
可變參數
可變參數就是容許在調用參數的時候傳入多個(≥0個)參數,可變參數分爲兩種狀況:
# 可變位置參數 def demo(*p): print(p) demo(1, 2, 3) # 參數在傳入時被自動組裝成一個元組 # 可變關鍵字參數 def demo(**p): print(p) demo(b='2', c='5', a='1') # 參數在傳入時被自動組裝成一個字典
位置參數
位置參數特色是調用函數時,要保證明參和形參的順序一致、數量相同。
# 位置參數 def demo(a, b, c): print(a, b, c) demo(1, 2, 3)
關鍵字參數
關鍵字參數容許在調用時以字典形式傳入0個或多個參數,且在傳遞參數時用等號(=)鏈接鍵和值。關鍵字參數最大優勢,就是使實參順序能夠和形參順序不一致,但不影響傳遞結果。
# 關鍵參數 def demo(a, b, c): print(a, b, c) demo(b=2, c=5, a=1) # 改變參數順序對結果不影響
命名關鍵字參數
命名關鍵字參數是在關鍵字參數的基礎上,限制傳入的的關鍵字的變量名。和普通關鍵字參數不一樣,命名關鍵字參數須要一個用來區分的分隔符*,它後面的參數被認爲是命名關鍵字參數。
# 這裏星號分割符後面的city、job是命名關鍵字參數 def person_info(name, age, *, city, job): print(name, age, city, job) person_info("Alex", 17, city="Beijing", job="Engineer")
參數傳遞的序列解包
參數傳遞的序列解包,是經過在實參序列前加星號(*)將其解包,而後按順序傳遞給多個形參。根據解包序列的不一樣,能夠分爲以下5種狀況:
序列解包 | 示例 |
---|---|
列表的序列解包 | *[3,4,5] |
元組的序列解包 | *(3,4,5) |
集合的序列解包 | *{3,4,5} |
字典的鍵的序列解包 | 若字典爲dic={'a':1,'b':2,'c':3},則解包代碼爲:*dic |
字典的值的序列解包 | 若字典爲dic={'a':1,'b':2,'c':3},則解包代碼爲:*dic.values() |
注意事項:
"""函數參數的序列解包""" def demo(a, b, c): print(a+b+c) demo(*[3, 4, 5]) # 列表的序列解包 demo(*(3, 4, 5)) # 元組的序列解包 demo(*{3, 4, 5}) # 集合的序列解包 dic = {'a': 1, 'b': 2, 'c': 3} demo(*dic) # 字典的鍵的序列解包 demo(*dic.values()) # 字典的值的序列解包 """位置參數和序列解包同時使用""" def demo(a, b, c): print(a, b, c) demo(*(1, 2, 3)) # 元組的序列解包 demo(1, *(2, 3)) # 位置參數和序列解包同時使用 demo(c=1, *(2, 3)) # 序列解包至關於位置參數,優先處理,正確用法 demo(*(3,), **{'c': 1, 'b': 2}) # 序列解包必須在關鍵字參數解包以前,正確用法
變量起做用的代碼範圍稱爲「變量的做用域」。不一樣做用域內變量名能夠相同,但互不影響。從變量做用的範圍分類,能夠把變量分類爲:
須要特別指出的是,局部變量的引用比全局變量速度快,應考慮優先使用。
全局變量聲明
有兩種方式能夠聲明全局變量:
特殊狀況,若局部變量和全局變量同名,那麼全局變量會在局部變量的做用域內被隱藏掉。
d = 2 # 全局變量 def func(a, b): c = a*b return c func(2, 3) def func(a, b): c = a*b d = 2 # 局部變量 return c func(2, 3) """聲明的全局變量,已在函數外定義""" n = 1 def func(a, b): global n n = b c = a*b return c s = func("knock~", 2) print(s, n) """聲明的全局變量,未在函數外定義,則新增""" def func(a, b): c = a*b global d # 聲明d爲全局變量 d = 2 return c func(2, 3) """局部變量和全局變量同名,則全局變量在函數內會被隱藏""" d = 10 # 全局變量d def func(a, b): d = 3 # 局部變量d c = a+b+d return c func(1, 2) d
lambda函數,又稱匿名函數,即沒有函數名字臨時使用的小函數。其語法以下:
lambda 函數參數:函數表達式 注意: - 匿名函數只能有一個表達式 - 該表達式的結果,就是函數的返回值 - 不容許包含其餘複雜語句,但表達式中能夠調用其餘函數
lambda函數的使用場景,主要在兩方面:
def f(x, y, z): return x+y+z # 位置參數 f(1, 2, 3) def f1(x, y=10, z=10): return x+y+z # 默認值參數 f(1) """把匿名函數賦值給一個變量,再利用變量來調用該函數,做用等價於自定義函數""" f=lambda x,y,z:x+y+z f(y=1,x=2,z=3) #關鍵值參數 L = ['ab', 'abcd', 'dfdfdg', 'a'] L.sort(key=lambda x: len(x)) # 按長度排序 L L=[('小明',90,80),('小花',70,90),('小張',98,99)] L.sort(key=lambda x:x[1],reverse=True) # 降序排序 L
在函數內部,能夠調用其餘函數。若是一個函數在內部調用自身自己,這個函數就是遞歸函數。但不是的函數調用本身都是遞歸,遞歸有其自身的特性:
從上圖可知,遞歸過程是函數調用本身,本身再調用本身,...,當某個條件獲得知足(基例)的時候就再也不調用,而後再一層一層地返回,直到該函數的第一次調用。遞歸函數的優勢是邏輯簡單清晰,缺點是過深的調用會致使棧溢出,在Python中,一般狀況下,這個深度是1000層,超過將拋出異常。
案例:用遞歸實現階乘
# 案例一:用遞歸實現階乘 def fact(n): if n==0: return 1 else: return n*fact(n-1) fact(5) # 案例二:實現字符串反轉 # 方法1,先轉成列表,調用列表的revers方法,再把列表轉成字符串 s = 'abcde' l = list(s) l.reverse() ''.join(l) # 方法2,切片的方法 s = 'abcde' s[::-1] # 方法3,遞歸的方法 s = 'abc' def reverse1(s): if s == '': return s else: print(s) return reverse1(s[1:])+s[0] reverse1(s)