1、函數的返回值
python
In [4]: def add(x, y): ...: return x+y ...: In [5]: add(1, 2) Out[5]: 3 In [8]: def add(x, y): ...: return x+y ...: print(x+y) ...: In [9]: add(1, 2) Out[9]: 3 In [16]: def add(x, y): ...: return "abc", x+y ...: return x+y ...: In [17]: add(1, 2) Out[17]: ('abc', 3) In [18]: def add(x, y): ...: return ...: ...: In [19]: add(1, 2) In [20]: a = add(1, 2) In [21]: a In [22]: type(a) Out[22]: NoneType In [25]: def add(x, y): ...: return None ...: In [26]: add(1, 2) In [27]: type(add(1, 2)) Out[27]: NoneType
關鍵字:returnbash
return只能出如今函數中,能夠返回任何對象,能夠做爲元祖變相的返回多個值閉包
全部函數都有返回值,若是沒定義return則默認返回值Noneapp
return語句除了返回值以外還會結束函數ide
1個函數能夠有多個return語句,但只會執行一個
函數
2、做用域
ui
一、函數嵌套
spa
函數能夠嵌套定義
code
In [28]: def outter(): ...: def inner(): ...: print('inner') ...: print('outter') ...: inner() ...: In [29]: outter() outter inner
二、做用域orm
做用域是一個變量的可見範圍
函數內部是一個局部做用域,外面叫全局做用域
不一樣做用域變量不可見,可是上級做用域的變量對下級只讀可見
In [32]: x = 1 In [33]: def inc(): ...: x += 1 # x此時是局部變量,不能直接使用全局做用域的變量 ...: In [34]: inc() --------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) <ipython-input-34-ae671e6b904f> in <module>() ----> 1 inc() <ipython-input-33-661b9217054c> in inc() 1 def inc(): ----> 2 x += 1 3 UnboundLocalError: local variable 'x' referenced before assignment In [40]: x = 1 In [41]: def fn(): ...: print(x) ...: In [42]: fn() # 爲何這裏能打印出來,不拋出錯誤呢 1
變量的做用域爲定義此變量的做用域:
In [43]: def fn(): ...: name = "xxj" ...: print(name) ...: In [44]: fn() xxj In [45]: name --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-45-18697449d7c4> in <module>() ----> 1 name NameError: name 'name' is not defined
上級做用域對下級做用域是隻讀可見的
In [46]: def fn(): ...: xx = 1 ...: print(xx) ...: def inner(): ...: print(xx) ...: inner() ...: In [47]: fn() 1 1 In [48]: def fn(): ...: xx = 1 ...: print(xx) ...: def inner(): ...: xx = 2 ...: print(xx) ...: inner() ...: print(xx) ...: In [49]: fn() 1 2 1
global關鍵字能且只能引用全局做用域中的變量;引用後的變量能讀寫
若是全局做用域有此變量名,則引用,
沒有則須要定義,定義後此做用域和全局做用域中可見;不定義則會報錯,
In [53]: xx = 1 In [54]: def fn(): ...: global xx # global關鍵字能顯式的提高1個變量的做用域 ...: xx += 1 ...: In [55]: fn() In [56]: xx Out[56]: 2 In [57]: fn() In [58]: xx Out[58]: 3 In [68]: xxj --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-68-bc382da45e82> in <module>() ----> 1 xxj NameError: name 'xxj' is not defined In [69]: def fn(): ...: global xxj # 若是此變量沒有定義,則此提高變量做用域沒有意義 ...: ...: In [70]: fn() In [71]: xxj --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-71-bc382da45e82> in <module>() ----> 1 xxj NameError: name 'xxj' is not defined
除非你清楚的知道global會帶來什麼,而且明確的知道沒有global不行的話,不然不要用global;就是不建議使用global關鍵字
三、默認參數的做用域
函數也是對象,參數是函數對象的屬性,因此函數參數的做用域伴隨函數整個生命週期
In [92]: def fn(xy=[]): ...: xy.append(1) ...: print(xy) ...: In [93]: fn() [1] In [94]: fn() [1, 1] In [95]: fn() [1, 1, 1] In [96]: fn.__defaults__ # 函數默認參數的值保存在函數__defaults__屬性中 Out[96]: ([1, 1, 1],) In [98]: fn() [1, 1, 1, 1] In [100]: fn.__defaults__ Out[100]: ([1, 1, 1, 1],)
當使用可變類型數據做爲默認參數默認值時,須要特別注意。
In [105]: def fn(x=0, y=0): ...: x = 1 # 賦值即定義 ...: y = 2 ...: print(x) ...: print(y) ...: In [106]: fn.__defaults__ Out[106]: (0, 0) In [107]: fn() 1 2 In [108]: fn.__defaults__ Out[108]: (0, 0)
解決方案:
使用不可變類型做爲默認值
函數體內不改變默認值
In [109]: def fn(lst=None): # 若是傳入的參數是非None,那麼改變了傳入參數; ...: if lst is None: ...: lst = [] else: lst = lst[:] ...: lst.append(3) ...: print(lst) ...: In [110]: fn.__defaults__ Out[110]: (None,) In [111]: fn() [3] In [112]: fn.__defaults__ Out[112]: (None,) In [113]: def fn(lst=[]): ...: lst = lst[:] # 淺拷貝 ...: lst.append(2) # 不管如何不修改傳入參數 ...: print(lst) ...: In [114]: fn.__defaults__ Out[114]: ([],) In [115]: fn() [2] In [116]: fn.__defaults__ Out[116]: ([],)
一般若是使用一個可變參數做爲默認參數時,會使用None來代替
四、命名空間與LEGB
1)命名空間
理解Python的LEGB原則是理解Python命名空間的關鍵,而理解Python的命名空間又是理解Python中許多語法規定的關鍵。因此,Python的LEGB原則就成爲Python中一個很是核心的內容
白話一點講:命名空間是對變量名的分組劃分。
不一樣組的相同名稱的變量視爲兩個獨立的變量,所以隸屬於不一樣分組(即命名空間)的變量名能夠重複。
命名空間能夠存在多個,使用命名空間,表示在該命名空間中查找當前名稱。
命名空間表示變量的可見範圍,一個變量名能夠定義在多個不一樣的命名空間,相互之間並不衝突,但同一個命名空間中不能有兩個相同的變量名。
好比:兩個叫「張三」的學生能夠同時存在於班級A和班級B中,若是兩個張三都是一個班級,那麼帶來的麻煩複雜不少了,在Python中你不能這麼幹。
在Python中用字典來表示一個命名空間,命名空間中保存了變量(名字)和對象的映射關係,在Python中命名空間出如今哪些地方呢?有函數範圍內的命名空間(local),有模塊範圍內的命名空間(global),有python內建的命名空間(built-in),還有類對象的全部屬性組成的命名空間
Python一切皆對象,因此在Python中變量名是字符串對象
例如:
1
|
In [25]: a=10
|
表示創建字符串對象a
與Number對象10
之間的對應關係。因爲這是一種映射關係,因此,可使用鍵-值的形式來表示,即{name : object}。
前面已經說過,命名空間是對變量名的分組劃分,因此,Python的命名空間就是對許多鍵-值對的分組劃分,即,鍵值對的集合,所以:
Python的命名空間是一個字典,字典內保存了變量名稱與對象之間的映射關係
2)命名空間的生命週期
全部的命名空間都是有生命週期的,對於python內建的命名空間,python解析器啓動時建立,一直保留直至直python解析器退出時才消亡。而對於函數的local命名空間是在函數每次被調用的時候建立,調用完成函數返回時消亡,而對於模塊的global命名空間是在該模塊被import的時候建立,解析器退出時消亡。
3)做用域
一個做用域是指一段程序的正文區域,能夠是一個函數或一段代碼。
一個變量的做用域是指該變量的有效範圍。Python的做用域是靜態做用域,由於它是由代碼中得位置決定的,而命名空間就是做用域的動態表現。
函數定義了本地做用域,而模塊定義了全局做用域:
每一個模塊都是一個全局做用域,所以,全局做用域的範圍僅限於單個程序文件
每次對函數的調用都會建立一個新的本地做用域,賦值的變量除非聲明爲全局變量,不然均爲本地變量
全部的變量名均可以概括爲本地,全局或內置的(由__builtin__模塊提供)
4)LEGB原則
LEGB含義解釋:
L-Local(function);函數內的名字空間
E-Enclosing function locals;外部嵌套函數的名字空間(例如closure)
G-Global(module);函數定義所在模塊(文件)的名字空間
B-Builtin(Python);Python內置模塊的名字空間,builtin做用域,對應builtin命名空間,python內部定義的最頂層的做用域,在這個做用域裏面定義了各類內建函數:open、range、xrange、list等等
前面講到,Python的命名空間是一個字典,字典內保存了變量名與對象之間的映射關係,
所以,查找變量名就是在命名空間字典中查找鍵-值對。
Python有多個命名空間,所以,須要有規則來規定,按照怎樣的順序來查找命名空間,LEGB就是用來規定命名空間查找順序的規則。
LEGB規定了查找一個名稱的順序爲:local-->enclosing function locals-->global-->builtin
3、閉包
當一個函數結束了,函數的內部部分變量引用還存在,這就叫閉包
外層函數主要爲內層函數提供環境
定義在外層函數內,卻由內層函數引用的變量,在外層函數返回時,若是外層函數返回的值是內層函數,再次調用內層函數時,會記憶下內層函數調用的外層函數的變量。
python的閉包可使用可變容器實現,這也是python2惟一的方式
In [91]: def counter(): ...: c = [0] ...: def inc(): ...: c[0] += 1 ...: return c ...: return inc ...: In [92]: type(counter) Out[92]: function In [93]: type(counter()) Out[93]: function In [94]: type(counter()()) Out[94]: list In [95]: f = counter() In [96]: f() Out[96]: [1] In [97]: f() Out[97]: [2] In [98]: f() Out[98]: [3]
nonlocal關鍵字:
nonlocal關鍵字能且只能引用外部函數做用域中已存在的變量(不能在本身的做用域中定義);引用後的變量能讀寫
In [102]: def counter(): ...: x = 0 ...: def inc(): ...: nonlocal x ...: x += 1 ...: return x ...: return inc ...: In [103]: f = counter() In [104]: f() Out[104]: 1 In [105]: f() Out[105]: 2 In [106]: f() Out[106]: 3
4、遞歸函數
函數體內調用自身的函數
遞歸函數須要有合適的退出條件,不然就成了死循環; 遞歸須要邊界條件,遞歸前進段和遞歸返回段
在python中爲了保護解釋器,遞歸深度最大爲1000
python中應儘可能避免遞歸,效率低(能轉化爲迭代儘可能轉化爲迭代)
In [2]: def fib(n): ...: if n == 0: ...: return 1 ...: if n == 1: ...: return 1 ...: return fib(n-1) + fib(n-2) ...: In [3]: fib(5) Out[3]: 8 In [4]: fib(6) Out[4]: 13