Python中使用def關鍵字來聲明函數,聲明函數的格式爲:html
def func_name(args): ...body... [return ...]
有3個須要注意的地方:python
return None
,即函數默認返回None結構若是函數體body語句只有一行,或者能夠簡寫爲一行,則能夠寫在def的同行。例如:express
def myfunc(x,y,z): print(x+y+z)
函數聲明好以後,就能夠執行函數,執行函數也稱爲調用函數,方式爲func_name(args)
,例如:編程
myfunc(1,2,3)
函數中每每會包含一個return或多個return語句,它能夠出如今函數中的任意位置處,它用來結束函數的執行,並返回給定的值。例如:數據結構
def func(x): return x+5
表示返回x+5
的值,返回值是一種值類型,因此能夠賦值給變量、能夠輸出、能夠操做等等。例如:閉包
print(func(3)) # 輸出返回值 a=func(4) # 賦值給變量 print(a) print(func(5)+3) # 數值操做
return語句是可選的,若是函數中不指定return語句,則默認返回None,即相似於return None
。app
函數的參數其實也是變量,只不過這些變量是獨屬於函數的本地變量,函數外部沒法訪問。在函數調用的時候,會將給定的值傳遞給函數的參數,這其實是變量賦值的過程。編程語言
def myfunc(x,y,z): print(x,y,z) myfunc(1,2,3)
def首先聲明好函數,而後到了myfunc(1,2,3)
時,表示調用函數(執行函數),調用函數時會將給定的值1,2,3
傳遞給函數的參數x,y,z
,其實就是變量賦值x=1,y=2,z=3
,而後使用print輸出它們。函數
因爲python是動態語言,無需先聲明變量,也無需指定變量的類型,因此python的函數參數和返回值很是的靈活。任何類型的變量或數據結構均可以傳遞給參數,這其實是變量賦值的過程。例如:3d
myfunc(1,2,3) myfunc("abc",2,"def") myfunc([1,2,3],4,5)
上面幾個函數調用語句中,賦值給參數的值能夠是數值類型,能夠是字符串類型,能夠是列表類型,也能夠是其它任何數據類型。
python函數的參數相比其它語言要複雜一些,意味着要靈活不少,短短一個小節的篇幅徹底無法解釋清楚,關於參數細節,詳細內容見後面的文章。
def用來聲明一個函數,python的函數包括函數名稱、參數、函數體、函數體中涉及到的變量、返回值。
實際上,函數名稱實際上是一個變量名,def表示將保存在某塊內存區域中的函數代碼體賦值給函數名變量。例如:
def myfunc(x,y,z): ...CODE...
上面表示將函數體賦值給變量名myfunc。以下圖:
既然是變量,就能夠進行輸出:
def myfunc(x): return x+5 print(myfunc)
輸出結果:
<function myfunc at 0x032EA6F0>
因爲python是解釋型語言,因此必須先定義函數,才能調用函數。
若是導入一個模塊文件,導入的時候會解釋、執行文件中的代碼,包括def語句,也就是說,導入文件時會先聲明好函數。
請必定理解本節內容,也許細節方面可能會有些不許確,但對於深刻理解函數來講(不只限於python語言),是很是有幫助的,特別是理解做用域規則的時候。
python是解釋性語言,讀一行解釋一行,解釋一行忘記一行。而函數是一種代碼塊,代碼塊是一個解釋單元,是一個總體。在代碼塊範圍內不會忘記讀取過的行,也不會讀一行就當即解釋一行,而是讀取完全部代碼塊內的行,而後統籌安排地進行解釋。關於這一點,在後面的文章代碼塊詳述中有很是詳細的解釋,建議一讀。
當python讀取到def所在行的時候,知道這是一個函數聲明語句,它有一個屬於本身的代碼塊範圍,因而會讀完整個代碼塊,而後解釋這個代碼塊。在這個解釋過程當中,會記錄好變量以及該變量的所屬做用域(是全局範圍內的變量仍是函數的本地變量),但必定注意,def聲明函數的過程當中不會進行變量的賦值(參數默認值除外,見下文),只有在函數調用的時候纔會進行變量賦值。換句話說,在def聲明函數的過程當中,在函數被調用以前,函數所記錄的變量一直都是變量的地址,或者通俗一點理解爲記錄變量的名稱,而不會進行變量的賦值替換。
實際上,變量的明確的值會看成常量被記錄起來。如a=10
的10被做爲常量,而變量a賦值給變量b時b=a
,a顯然不會做爲常量。
以下函數:
x=3 def myfunc(a,b): c=10 print(x,a,b,c) myfunc(5,6)
輸出結果爲:"3 5 6 10"。
上面的函數涉及到了4個變量:a、b、c、x。其中:
在def的過程當中,會完完整整地記錄好這些變量以及所屬做用域,但只會記錄,不會進行變量的賦值。以下圖:
而後函數被調用,這時候纔會開始根據記錄的做用域搜索變量是否存在,是否已經賦值(非本地變量),並對須要賦值的變量賦值:
a=5,b=6
,而後賦值普通的本地變量c=10
如圖:
最後執行print(x,a,b,c)
輸出這些變量的值。
還需注意,python是讀一行解釋一行的,在函數調用過程當中,由於c=10
在print()
的前面,因此是先賦值c=10
,再執行print,若是print在c=10
前面,則先執行print,再賦值,這顯然是錯誤的,由於print()中使用了變量c,但目前尚未對其賦值。這和其它語言可能有些不一樣(特別是編譯型語言),它們可能會無視變量賦值以及變量使用的位置先後關係。
若是上面的示例中,函數myfunc調用以前,將變量x賦值爲另外一個值:
x=3 def myfunc(a,b): c=10 print(x,a,b,c) x=33 myfunc(5,6)
這時將輸出:"33 5 6 10"。由於x是全局變量,只有在函數調用的時候纔會去找到變量x對應的值,而這時全局變量的值已是33。
匿名函數是指沒有名稱的函數,任何編程語言中,匿名函數都扮演着重要角色,它的功能很是靈活,可是匿名函數中的邏輯通常很簡單,不然直接使用命名函數更好,匿名函數經常使用於回調函數、閉包等等。
在python中使用lambda關鍵字聲明匿名函數,python中的lambda是一個表達式而不是一個語句,這意味着某些語句環境下可能沒法使用def聲明函數,但卻可使用lambda聲明匿名函數。固然,匿名函數能實現的功能,命名函數也以同樣都能實現,只不過有時候可能會比較複雜,可讀性會更差。
lambda聲明匿名函數的方式很簡單,lambda關鍵字後面跟上參數列表,而後一個冒號,冒號後跟一個表達式。
lambda argl, arg2,... argN :expression statement
lambda表達式返回一個匿名函數,這個匿名函數能夠賦值給一個變量。
例如:
# 聲明匿名函數,並賦值給變量f f = lambda x,y,z: x+y+z print(f)
輸出結果:
<function <lambda> at 0x027EA6F0>
既然匿名函數賦值給了變量,這個函數就像是命名變量同樣,能夠經過這個變量去調用這個匿名函數。固然,它畢竟仍是匿名函數,正如上面輸出的結果中function <lambda>
所示。並且,匿名函數並不是必定要賦值給變量。
# 調用匿名函數 print(f(2,3,4)) # 輸出9
匿名函數的返回值是冒號後面的表達式計算獲得的結果。對於上面的示例,它等價於return x+y+z
。
由於lambda是一個表達式,因此能夠寫在任何表達式能夠出現的位置處,而某些語句上下文環境中,並不能直接使用def來聲明。例如,將函數保存到一個列表中:
L=[ lambda x: x * 2, lambda x: x * 3, lambda x: x * 4 ] print(L[0](2)) print(L[1](2)) print(L[2](2))
上面的lambda出如今列表的內部,且這裏面的匿名函數並賦值給某個變量。像def語句就沒法出如今這樣的環境中,若是真要使用def來聲明函數,並保存到列表中,只能在L的外部使用def定義,而後將函數名來保存。
def f1(x): return x * 2 def f2(x): return x * 3 def f3(x): return x * 4 L=[f1,f2,f3] print(L[0](2)) print(L[1](2)) print(L[2](2))
看上去沒什麼問題,但函數定義的位置和列表L定義的位置可能會相差甚遠,可讀性可能會很是差。
同理的,還能夠將匿名函數保存在字典的value位置上:
key='four' print( { 'two':(lambda x: x * 2), 'three':(lambda x: x * 3), 'four':(lambda x: x * 4) }[key](2) )
函數內部能夠嵌套函數。通常來講,在函數嵌套時,內層函數會做爲外層函數的返回值(固然,並不是必須)。既然內層函數要做爲返回值,這個嵌套的內層函數更可能會是lambda匿名函數。
例如:
def f(x): y=10 def g(z): return x+y+z return g
上面的函數f()中嵌套了一個g(),並返回這個g()。其實上面示例中的g()是一個閉包函數。
既然f()返回的是函數,這個函數能夠賦值給其它變量,也能夠直接調用:
# 將嵌套的函數賦值給變量myfunc # 這時myfunc()和g()是等價的 myfunc = f(3) print( myfunc(5) ) # 直接調用g() print( f(3)(5) )
固然,嵌套lambda匿名函數也能夠,且更常見:
def f(x): y=10 return lambda z: x+y+z
看下面嵌套在循環內部的函數,在每一個迭代過程當中都聲明一個匿名函數,這個匿名函數返回循環控制變量i,同時將聲明的匿名函數保存到列表L中。
def f(): L=[] for i in range(5): L.append( lambda : i ) return L
但若是調用該函數f(),並調用保存在列表中的每一個匿名函數,會發現它們的值徹底相同,且都是循環迭代的最後一個元素值i=4
。
List = f() print(List[0]()) print(List[1]()) print(List[2]()) print(List[3]()) print(List[4]())
執行結果:
4 4 4 4 4
爲何會如此?爲何循環迭代過程當中的i沒有影響到匿名函數的返回值?這是一個很是值得思考的問題,若是不理解結果,請仔細回顧前文函數變量的細節。若是仍是不理解,請閱讀Python做用域詳述。
此處給幾個示例,這些示例的結果對於只學過python的人來講,可能會很容易理解,但對於學過其它語言的人來講,很容易混淆出錯。
此處並不會對這些示例的結果進行解釋,由於只要理解了前文函數變量的細節,這幾個示例的結果很容易理解。
一樣,更詳細的內容參見Python做用域詳述。
以下示例:
x=3 def f(): x=4 g() print("f:",x) def g(): print("g:",x) f()
輸出:
g: 3 f: 4
若是在調用函數前,修改全局變量x的值:
x=3 def f(): x=4 g() print("f:",x) def g(): print("g:",x) x=6 f()
輸出:
g: 6 f: 4
若是把g()的聲明放在f()的內部呢?
x=3 def f(): x=4 def g(): print("g:",x) print("f:",x) x=5 return g f()()
輸出:
f: 4 g: 5