這里我們看看Python中函數定義的語法,函數的局部變量,函數的參數,Python中函數的形參可以有默認值,參數的傳遞是賦值操作,在函數調用時,可以對實參進行打包和解包
1,函數定義
關鍵字def引出函數定義,后面跟着函數名以及用括號括起來的一系列參數,然后從下一行開始函數體(function body),並且要縮進。
生成一個Fibnacci數列的序列,最大不超過某個數的函數
1 def fib(n): 2 '''get a list of fibnacci series to n''' 3 a, b = 0, 1 4 result = [] 5 while a<n: 6 result.append(a) 7 a, b = b, a+b 8 return result
運行:
>>> fib(3000)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584]
2,函數的局部變量
[這里的符號表(symbol table)等同於命名空間(namespace)]
函數體的執行會為這個函數的局部變量引入一個新的符號表(symbol table)。所有函數體中的賦值語句(assignment statement),都會把變量名和值存在這個符號表中。
而函數體中的引用一個變量時,首先查看函數的符號表,如果這個函數定義包裹在其它函數定義中,就依次查看外圍函數的符號表,然后查看全局符號表(也就是函數所屬的module的符號表),最后查看Python的內置類型和變量的符號表。
函數的行參也是存在於函數的局部符號表中。
一個例子,函數中引用的是在局部符號表中的參數l,而不是全局符號表中的l。
1 l = ['a', 'b'] #輸出[1,2] 2 def f(l): 3 print l 4 if __name__ == "__main__": 5 f([1,2])
函數調用的實參傳遞是通過賦值語句做的,所以傳遞的是object的引用,對於序列這樣的可變類型(mutable),按引用傳遞的話,如果序列做參數在函數體中改變它的值也會影響它在外圍符號表的值,這就像C++中的引用
1 l = ['a', 'b'] 2 def f(l): 3 l.append("22") 4 if __name__ == "__main__": 5 f(l) 6 print l
輸出結果是:
['a', 'b', '22']
3,函數的默認參數
3.1 基礎
函數可以有默認參數,有默認值的參數必須在沒有默認值的參數后面
1 def f(a, b=5, c="hello"): 2 print c, a+b 3 if __name__ == "__main__": 4 f(3) 5 f(3, 4, "hi")
函數參數的默認值如果取自一個變量,那這個默認值會在函數定義的地方被計算得到,默認值只會被計算一次
1 i = 6 2 def f(n=i): 3 print n 4 i = 7
會輸出6
3.2當參數的默認值是list這樣的可變對象(mutable object)
函數的默認參數只會被計算一次,不管函數被怎么調用,所以當參數是list這樣的可變對象時,但在函數體中其值被改變時,再次調用參數的默認值就是改變后的值
1 def f(n, l=[]): 2 l.append(n) 3 print l 4 if __name__ == "__main__": 5 f(1) 6 f(2) 7 f(3)
猜猜看會輸出什么?輸出
[1]
[1, 2]
[1, 2, 3]
也就是從f(2)調用開始,l的默認值就變了,不會再重新計算一次默認值了。
怎么避免這樣的情況呢?用下面的代碼
1 def f(n, l=None): 2 if l is None: 3 l = [] 4 l.append(n) 5 print l 6 if __name__ == "__main__": 7 f(1) 8 f(2) 9 f(3)
輸出結果:
[1]
[2]
[3]
None是個內置常量,當然不能被改變,每次f被調用就會用這個值給l賦值
[問題?]這里其實我有個待弄明白的問題,默認值是只會被計算一次還是參數只會被初始化一次,也就是符號表是每次函數調用都建立和銷毀,還是函數生存期一直存在,所用調用共用一個。
[答案]現在有答案了,函數的符號表,也就是其局部命名空間會在每次調用和返回時進行創建初始化和刪除。
4,關鍵字實參(keyword argument)
實參(argument)是指函數調用時傳遞進去的參數值(value),區別於形參(parameter)。
Python實參(argument)分為兩類,關鍵字實參和位置實參(positional argument)。
關鍵字實參就是在調用函數的時候,以name=value的形式傳遞參數,name就是參數定義的名字,也就是形參;關鍵字參數指明了參數名,所以可以不用管其調用時候順序。
位置實參就是只傳遞value的形式,顧名思義,這要靠實參的位置來匹配形參,關鍵字參數必須位於位置參數之后 。
舉一個簡單的例子
1 def f(a, b, c): 2 print "a =", a, "b =", b, "c =", c 3 if __name__ == "__main__": 4 f(5, c=8, b=2)
輸出結果:
a = 5 b = 2 c = 8
5,參數的解包(unpacking)
如果我們有一個list或一個dict,可把它直接作為參數傳給一個函數,里面的值可以解包出來傳給一個個參數。
5.1 解包為位置實參
可以把一個list或tuple解包,對應的值作為位置參數傳遞,調用的時候要以*args的形式
>>> range(2,5) [2, 3, 4] >>> args=[2,5] >>> range(*args) #注意調用語法“*args" [2, 3, 4]
range接受兩個參數,給它傳入一個tuple[2,5],解包。
5.2 解包為關鍵字實參
要解包為關鍵字參數,使用字典。字典的key為形參的name,是字符串,字典value為傳遞的實參。
語法上在調用的時候以**args的形式
使用4小節的例子,輸出結果相同
1 def f(a, b, c): 2 print "a =", a, "b =", b, "c =", c 3 if __name__ == "__main__": 4 d = {"a":5, "c":8, "b":2} 5 f(**d) #注意調用語法“**args"
6,參數的打包,傳遞任意個參數(packing)
可不可以給函數傳遞任意個參數呢,可以的,多余的實參可以被打包成一個元組(tuple),傳給一個形參。
這個行參在定義時前面加上“*”,即*args
一個小例子,把實參打包成tuple輸出
1 def multiple_argu(*args): 2 print args 3 if __name__ == "__main__": 4 multiple_argu('a', 'b', 'c', 'd')
輸出了一個tuple:
('a', 'b', 'c', 'd')
參考:
http://docs.python.org/3/glossary.html#term-argument Python文檔,術語
http://docs.python.org/2.7/tutorial/controlflow.html#defining-functions Python文檔