剛開始學習python,python相對于java確實要簡潔易用得多。內存回收類似hotspot的可達性分析, 不可變對象也如同java得Integer類型,with函數(shù)類似新版本C++的特性,總體來說理解起來比較輕松。只是函數(shù)部分參數(shù)的"*"與"**",閉包等問題,著實令人迷糊了一把,弄清概念后寫下此文記錄下來,也希望本文能夠幫助其他初學者。
所以本文是一篇學習筆記,著重于使用的細節(jié)和理解上,首先分別介紹了函數(shù)各種參數(shù)類型在調用和聲明時的區(qū)別,及其在混用時需要注意的一些細節(jié),之后講了閉包相關的內容。如果有不對的地方歡迎指正。
理解這個問題得關鍵在于要分開理解調用和聲明語法中3者得區(qū)別.
#這里先說明python函數(shù)調用得語法為:func(positional_args, keyWord_args, *tuple_grp_nonkw_args, **dict_grp_kw_args) #為了方便說明,之后用以下函數(shù)進行舉例 def test(a,b,c,d,e): PRint a,b,c,d,e舉個例子來說明這4種調用方式得區(qū)別:
#-------------------------------#positional_args方式>>> test(1,2,3,4,5)1 2 3 4 5#這種調用方式的函數(shù)處理等價于a,b,c,d,e = 1,2,3,4,5print a,b,c,d,e#-------------------------------#keyword_args方式>>> test(a=1,b=3,c=4,d=2,e=1)1 3 4 2 1#這種處理方式得函數(shù)處理等價于a=1b=3c=4d=2e=1print a,b,c,d,e#-------------------------------#*tuple_grp_nonkw_args方式>>> x = 1,2,3,4,5>>> test(*x)1 2 3 4 5#這種方式函數(shù)處理等價于a,b,c,d,e = xprint a,b,c,d,e#特別說明:x也可以為dict類型,x為dick類型時將鍵傳遞給函數(shù)>>> y{'a': 1, 'c': 6, 'b': 2, 'e': 1, 'd': 1}>>> test(*y)a c b e d#---------------------------------#**dict_grp_kw_args方式>>> y{'a': 1, 'c': 6, 'b': 2, 'e': 1, 'd': 1}>>> test(**y)1 2 6 1 1#這種函數(shù)處理方式等價于a = y['a']b = y['b']... #c,d,e不再贅述print a,b,c,d,e接下來說明不同參數(shù)類型混用的情況,要理解不同參數(shù)混用得語法需要理解以下幾方面內容.
首先要明白,函數(shù)調用使用參數(shù)類型必須嚴格按照順序,不能隨意調換順序,否則會報錯. 如 (a=1,2,3,4,5)會引發(fā)錯誤,; (*x,2,3)也會被當成非法.
其次,函數(shù)對不同方式處理的順序也是按照上述的類型順序.因為#keyword_args方式和**dict_grp_kw_args方式對參數(shù)一一指定,所以無所謂順序.所以只需要考慮順序賦值(positional_args)和列表賦值(*tuple_grp_nonkw_args)的順序.因此,可以簡單理解為只有#positional_args方式,#*tuple_grp_nonkw_args方式有邏輯先后順序的.
最后,參數(shù)是不允許多次賦值的.
舉個例子說明,順序賦值(positional_args)和列表賦值(*tuple_grp_nonkw_args)的邏輯先后關系:
#只有在順序賦值,列表賦值在結果上存在羅輯先后關系#正確的例子1>>> x = {3,4,5}>>> test(1,2,*x)1 2 3 4 5#正確的例子2>>> test(1,e=2,*x)1 3 4 5 2#錯誤的例子>>> test(1,b=2,*x)Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: test() got multiple values for keyword argument 'b'#正確的例子1,處理等價于a,b = 1,2 #順序參數(shù)c,d,e = x #列表參數(shù)print a,b,c,d,e#正確的例子2,處理等價于a = 1 #順序參數(shù)e = 2 #關鍵字參數(shù)b,c,d = x #列表參數(shù)#錯誤的例子,處理等價于a = 1 #順序參數(shù)b = 2 #關鍵字參數(shù)b,c,d = x #列表參數(shù)#這里由于b多次賦值導致異常,可見只有順序參數(shù)和列表參數(shù)存在羅輯先后關系 理解了函數(shù)調用中不同類型參數(shù)得區(qū)別之后,再來理解函數(shù)聲明中不同參數(shù)得區(qū)別就簡單很多了.
特別提醒:*arg 和 **arg可以為空值.
以下舉例說明上述規(guī)則:
#arg, *arg和**arg作用舉例def test2(a,*b,**c): print a,b,c#---------------------------#*arg 和 **arg可以不傳遞參數(shù)>>> test2(1)1 () {}#arg必須傳遞參數(shù)>>> test2()Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: test2() takes at least 1 argument (0 given)#----------------------------#*arg將順positional_args轉換為列表>>> test2(1,2,[1,2],{'a':1,'b':2})1 (2, [1, 2], {'a': 1, 'b': 2}) {}#該處理等價于a = 1 #arg參數(shù)處理b = 2,[1,2],{'a':1,'b':2} #*arg參數(shù)處理c = dict() #**arg參數(shù)處理print a,b,c#-----------------------------#**arg將keyword_args轉換為字典>>> test2(1,2,3,d={1:2,3:4}, c=12, b=1)1 (2, 3) {'c': 12, 'b': 1, 'd': {1: 2, 3: 4}}#該處理等價于a = 1 #arg參數(shù)處理b= 2,3 #*arg參數(shù)處理#**arg參數(shù)處理c = dict()c['d'] = {1:2, 3:4}c['c'] = 12c['b'] = 1print a,b,cdef test2(a,*b,**c): print a,b,c>>> test2(1, b=[1,2,3], c={1:2, 3:4},a=1)Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: test2() got multiple values for keyword argument 'a'#這里會報錯得原因是,總是先處理arg類型得參數(shù)#該函數(shù)調用等價于#處理arg類型參數(shù):a = 1a = 1 #多次賦值,導致異常#處理其他類型參數(shù)...print a,b,cpython的函數(shù),原本只能訪問兩個區(qū)域的變量:全局,和局部(函數(shù)上下文). 實際上,函數(shù)本身也是一個對象,也有自己的作用域. 閉包通過函數(shù)與引用集合的組合,使得函數(shù)可以在它被定義的區(qū)域之外執(zhí)行. 這個集合可以通過func_closure來獲取這個引用集合. 這與python處理全局變量得方式一樣,只不過全局變量將引用集合存儲在__globals__字段中.func_closure是一個存儲cell類型的元組,每個cell存儲一個上下文變量.
另外,舊版本得python的內部函數(shù)不能在其他作用域使用的原因,并不是因為每個作用域的變量嚴格相互隔離,而是脫離原本的作用域后,函數(shù)失去了原本上下文的引用。需要注意的是,閉包存儲的上下文信息一樣是淺拷貝,所以傳遞給內部函數(shù)的可變對象仍然會被其他擁有該對象引用得變量修改.
舉個例子:
>>> def foo(x,y):... def bar():... print x,y... return bar... #查看func_closure的引用信息>>> a = [1,2]>>> b = foo(a,0)>>> b.func_closure[0].cell_contents[1, 2]>>> b.func_closure[1].cell_contents0>>> b()[1, 2] 0#可變對象仍然能被修改>>> a.append(3)>>> b.func_closure[0].cell_contents[1, 2, 3]>>> b()[1, 2, 3] 0新聞熱點
疑難解答