函數
- def add(x,y): 函數名add(標識符)對應一個內存中的一個函數對象,因此也可以有多個標識符同時指向這個對象,同樣可以調用。
- 再次定義會創建一個新的function對象,不是對原來對象的修改;add(標識符)指向這個新的對象,原來的函數對象引用計數減一。
- 函數調用:add(2,3) 將(2,3)傳入add指向的函數對象中進行計算
- 函數是一個可調用對象;callable(add) ==> True
- 函數傳參
1、位置傳參;2、關鍵字傳參;3、可變位置傳參;4、keyworld-only;5、可變關鍵字傳參 - 關鍵字參數必須在位置參數後面,每個參數只能接受一個值,否則多值錯誤;位置參數可以使用關鍵字傳參
def sum(*iterable):
print(type(iterbale)) # iterbale ==> 元組,不傳入爲空元組
for i in iterable:
pass
def sum1(iterable):
print(type(iterbale))
for i in iterable:
pass
sum(x for i in range(10))) # 傳入生成器
sum1(range(5))) # 傳入一個對象
- 可變位置參數:收集所有位置參數作爲一個元組。
- 可變關鍵字參數:收集所有關鍵字參數作爲一個字典。
def fn(a, b=5, *args, **kwargs):
pass
fn(5, a=2, c=3, b=4,z=5)
##位置參數和關鍵字參數混用,關鍵字參數可以亂序,多餘的kwargs收集爲字典
- keyword-only
def fun(x=4, *args, y, **kwargs): # y只能接受關鍵字參數,keyword-only參數 pass def func(*args, x=0, y): # 正確,keyword-only 默認值可以在前 pass def func1(*,m): # m 必須爲關鍵字傳參 pass
- 元組,列表,字符串,迭代器,生成器,range對象 均可以解構
- 字典解構傳參
def fun(a, b):
pass
fun(**{"a":1,"b",2}) # ==> fun(a=1,b=2)
fun(*{"a":1,"b",2}.values()) # ==> fun(*(1,2))
- 函數返回值:使用return 返回,函數執行return後函數結束,並返回return 指定的內容,未定義return默認return None
- 函數作用域:函數定義的變量只在該函數內部,函數的局部作用域,也稱本地變量
- 全局變量、全局作用域:全局可見,可以向內部穿透,在函數內部可見
函數嵌套:
- 執行函數定義的時候不會執行內部的語句,內部定義的函數無法執行
def func(): x = 1 def fn(): x = x + 1 # 調用時將會報錯,未定義本地x的值之前,使用了本地的x
- global:聲明全局變量,該作用域中的該變量將會在全局中尋找該變量。
def func: global x # 聲明全局作用域,必須是全局作用域,上一層使用的nonlocal x = 1 # 在函數中可以定義全局變量,且執行後外部可用 x += 1
閉包
- 閉包:函數嵌套使用時,內層函數使用到了外層函數定義的局部變量,形成了閉包。
- python 中實現閉包的方式:
1.內部可以外部引用類型的變量,並可對其進行內部修改def func(): x = [1] def fn(): x.append[4] return fn foo = func() foo() # 每次輸出將會在上一次的結果上增加一個元素;x 的操作與外部形成了閉包
2.也可以使用nonlocal實現閉包:內部變量聲明使用外部定義的變量
def func():
x = 1
def fn():
nonlocal x #聲明該x 是外部變量 x
x = x + 1
return fn
foo = func()
foo() # 每次調用x 結果加1,foo函數未消亡,x 將不會被清除
- 如果在內部函數中定義一個變量,該變量名與外部變量名相同,那麼這個變量只會使用內部的定義的這個值,且需要滿足先賦值,後使用
def func(): x = 1 def fn(): y = x + 1 # 此處使用了x 變量,但是在下一行才定義x 所以錯誤 x = y # 此處定義了與外部變量同名變量,在本函數內部所有範圍內只能用該變量 # 如果沒有第5行對x的重新定義,第4行中x將會使用外部的x變量
- nonlocal:非該函數本地的變量,但是也不能是全局中的變量,在其他的任意一層定義即可使用該變量;函數多層嵌套時,使用nonlocal將會一層層向外部尋找該變量,直到最外層函數(非全局變量,不會匹配同名的global變量名)
def fn(): global c c = 0 def fn1(): c = 0 def fn2(): def fn3(): nonlocal c # 會使用5行的c值,與第3行的c不是同一個比變量 #若沒有第5行的c定義,也無法匹配第3行的c變量。nonlocal無法匹配global變量
- global和nonlocal都只是申明本函數作用域內該元素,並向上匹配尋找
默認值的作用域
- 當函數定義之後,會在內存中生成一個函數對象 ,此時函數已經定義了許多的方法和屬性(可以理解爲函數的元數據),例如函數名字,默認值等,函數參數的默認值已被加載到函數的兩個屬性之中保存,分別爲'. defaults','kwdefaults'中
def fun(a, b=1, c={}, *d , m=1, **kw): a = 1 x = 1 return x dir(fun) # 查看該函數定義後的已經存在屬性 ''' ['__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__'] ''' # 默認值被保存到 __defaults__ 和 __kwdefaults__中,分別保存位置參數 # 使用元組)和kw-only參數(使用字典) fun.__defaults__ # ==> ( 1, {} ) 保存了b和c的缺省值 fun.__kwdefaults__ # ==> {'m': 1} 保存了m 的默認值
- 注:當默認值爲引用類型時,使用變量操作時可能會引起默認值的改變(在使用默認值時,進行一些引起本對象的操作就會引起默認值改變)
如:list,bytearray,set,dict等可變類型對自身修改的操作,可變序列的 += 操作def fun(x = [], b=1): x.append(1) # 此時 x 是默認值列表中對象,直接操作默認值參數的對象 x += [1] # += 執行列表的extend,也是對x本對象的改變 b = 2 #b原本指向默認值列表中的1,現在指向新的對象2,原對象未改變
- LEGB原則:尋找一個變量時候的先後順序
- 當內部定義一個與全局變量或者build-in中相同的變量名時,將會使得全局或者build-in中的函數無法在本程序內使用,例如:
print = 1 # 定義一個變量爲print,此時print指向一個int對象, print(1) # 此時將會報錯,默認先使用本文件中的print,而不是build-in中
匿名函數
- 匿名函數普通定義
- 定義後可以使用變量接收,此時等同於 def
- 創建對象後直接調用,在函數後部直接()進行調用
特點:簡潔,快速實現某種功能,一次性創建使用,節省內存。lambda x : x + 1 # 返回值===> 對象,":"後不能出現賦值表達式 foo = lambda x : x + 1 # ===> 返回的對象使用一個變量接收,foo可調用 (lambda x : x+1)(1) # ==> 直接在創建後調用,()+ 傳入參數調用
- 使用場景
構造一個類型,完成某種功能# 返回一個和str功能相同的函數對象,效果相同 sorted([1,2,3,"a"], key = str) sorted([1,2,4,"a"], key = lambda x :str(x)) # 可以定製一些自己的比較方式,例如: sorted([1,2,4,"a"], key = lambda x :hash(x)) # 使用元素哈希值排序 sorted([1,2,4,"a"], key = lambda x :func(x)) # func爲自己定義的函數,實現自定義
- 構造某些類型,使用其返回值
d = defalutdict(lambda :0)
for k in "abc":
d[k] += 1 # 第一次將會調用lambda並返回初始值0值,完成累加
d = defalutdict(lambda :[]) # 構造列表 == list
for k in "abc":
d[k].append(1)
- 嵌套使用,可用於閉包
def fun(): x = [ ] return lambda a : x.append(a) # 匿名函數使用外層函數x,形成閉包
遞歸函數
- 函數調用過程:函數定義後,會在內存中生成函數對象,在調用函數時,函數將會在main函數之上壓棧,對應的參數分別進行壓棧,函數全部執行結束,函數對象進行彈棧,繼續執行main函數;如果函數未進行彈出棧時調用新的函數,函數將會繼續壓棧,直到棧上層函數執行完成彈出後下層函數纔會依次彈出;先進棧函數後出棧,後進棧函數一定會先彈出才能彈出下層函數
- 遞歸函數:在自己函數內部反覆調用自己,形成一個循環的調用。
- 間接遞歸:A調用B函數,B調用C,C調用A形成間接的遞歸調用
- 注:遞歸必須有邊界條件,達到某個條件將對停止自身調用,並進行一層層的return 返回,這是一個“逆向”的操作,先執行內部函數,在執行外層函數 ;
內層函數的 return 值將會成爲外一層函數調用結果,注意 return 值的控制。 - 遞歸思路:找出遞推公式,即:F( n) = f ( n - 1) 的關係,有了該關係,基本可以寫成以下形式
-
def func(n): if n == ? # 退出條件,可能不止一個 return ? # 一個確定的值,不可以再調用自身 return func( n - 1 ) # 根據實際遞推公式變化
- 遞歸分析: 分析返回結果 習慣從最裏層分析。
- 遞歸最大深度:無限遞歸將會消耗所有的棧資源,所以python中對遞歸層數進行了限制,查看方法:
import sys print(sys.getrecursionlimit()) # ==> 最大遞歸數
- 遞歸會使用大量函數的壓棧,開闢和清理新的棧空間需要時間和資源,效率較低;一般不使用