【函数】02、函数进阶
一、函數(shù)的返回值
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 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 |
關(guān)鍵字:return
? ???return只能出現(xiàn)在函數(shù)中,可以返回任何對象,可以作為元祖變相的返回多個值
? ?? 所有函數(shù)都有返回值,如果沒定義return則默認返回值None
?????return語句除了返回值之外還會結(jié)束函數(shù)
??? 1個函數(shù)可以有多個return語句,但只會執(zhí)行一個
二、作用域
1、函數(shù)嵌套
??函數(shù)可以嵌套定義
| 1 2 3 4 5 6 7 8 9 10 | In?[28]:?def?outter(): ????...:?????def?inner(): ????...:?????????print('inner') ????...:?????print('outter') ????...:?????inner() ????...:????? In?[29]:?outter() outter inner |
2、作用域
???作用域是一個變量的可見范圍
?? 函數(shù)內(nèi)部是一個局部作用域,外面叫全局作用域
?? 不同作用域變量不可見,但是上級作用域的變量對下級只讀可見
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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 |
變量的作用域為定義此變量的作用域:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 |
上級作用域?qū)ο录壸饔糜蚴侵蛔x可見的
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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關(guān)鍵字能且只能引用全局作用域中的變量;引用后的變量能讀寫
如果全局作用域有此變量名,則引用,
沒有則需要定義,定義后此作用域和全局作用域中可見;不定義則會報錯,
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | In?[53]:?xx?=?1 In?[54]:?def?fn(): ????...:?????global?xx????#?global關(guān)鍵字能顯式的提升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會帶來什么,并且明確的知道沒有g(shù)lobal不行的話,否則不要用global;就是不建議使用global關(guān)鍵字
3、默認參數(shù)的作用域
函數(shù)也是對象,參數(shù)是函數(shù)對象的屬性,所以函數(shù)參數(shù)的作用域伴隨函數(shù)整個生命周期
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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__?????#?函數(shù)默認參數(shù)的值保存在函數(shù)__defaults__屬性中 Out[96]:?([1,?1,?1],) In?[98]:?fn() [1,?1,?1,?1] In?[100]:?fn.__defaults__ Out[100]:?([1,?1,?1,?1],) |
??當(dāng)使用可變類型數(shù)據(jù)作為默認參數(shù)默認值時,需要特別注意。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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) |
解決方案:
? 使用不可變類型作為默認值
? 函數(shù)體內(nèi)不改變默認值
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | In?[109]:?def?fn(lst=None):?????#?如果傳入的參數(shù)是非None,那么改變了傳入?yún)?shù); ?????...:?????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)???#?無論如何不修改傳入?yún)?shù) ?????...:?????print(lst) ?????...:????? In?[114]:?fn.__defaults__ Out[114]:?([],) In?[115]:?fn() [2] In?[116]:?fn.__defaults__ Out[116]:?([],) |
??通常如果使用一個可變參數(shù)作為默認參數(shù)時,會使用None來代替
4、命名空間與LEGB
?1)命名空間?
?? ? ? 理解Python的LEGB原則是理解Python命名空間的關(guān)鍵,而理解Python的命名空間又是理解Python中許多語法規(guī)定的關(guān)鍵。所以,Python的LEGB原則就成為Python中一個非常核心的內(nèi)容
白話一點講:命名空間是對變量名的分組劃分。
? ? ? ?不同組的相同名稱的變量視為兩個獨立的變量,因此隸屬于不同分組(即命名空間)的變量名可以重復(fù)。
命名空間可以存在多個,使用命名空間,表示在該命名空間中查找當(dāng)前名稱。
? ? ? ?命名空間表示變量的可見范圍,一個變量名可以定義在多個不同的命名空間,相互之間并不沖突,但同一個命名空間中不能有兩個相同的變量名。
比如:兩個叫“張三”的學(xué)生可以同時存在于班級A和班級B中,如果兩個張三都是一個班級,那么帶來的麻煩復(fù)雜很多了,在Python中你不能這么干。
? ? ??在Python中用字典來表示一個命名空間,命名空間中保存了變量(名字)和對象的映射關(guān)系,在Python中命名空間出現(xiàn)在哪些地方呢?有函數(shù)范圍內(nèi)的命名空間(local),有模塊范圍內(nèi)的命名空間(global),有python內(nèi)建的命名空間(built-in),還有類對象的所有屬性組成的命名空間
Python一切皆對象,所以在Python中變量名是字符串對象
例如:
| 1 | In?[25]:?a=10 |
? ? ? 表示建立字符串對象a與Number對象10之間的對應(yīng)關(guān)系。由于這是一種映射關(guān)系,所以,可以使用鍵-值的形式來表示,即{name : object}。
前面已經(jīng)說過,命名空間是對變量名的分組劃分,所以,Python的命名空間就是對許多鍵-值對的分組劃分,即,鍵值對的集合,因此:
Python的命名空間是一個字典,字典內(nèi)保存了變量名稱與對象之間的映射關(guān)系
?2)命名空間的生命周期
? ? ? ?所有的命名空間都是有生命周期的,對于python內(nèi)建的命名空間,python解析器啟動時創(chuàng)建,一直保留直至直python解析器退出時才消亡。而對于函數(shù)的local命名空間是在函數(shù)每次被調(diào)用的時候創(chuàng)建,調(diào)用完成函數(shù)返回時消亡,而對于模塊的global命名空間是在該模塊被import的時候創(chuàng)建,解析器退出時消亡。
?3)作用域
??一個作用域是指一段程序的正文區(qū)域,可以是一個函數(shù)或一段代碼。
?一個變量的作用域是指該變量的有效范圍。Python的作用域是靜態(tài)作用域,因為它是由代碼中得位置決定的,而命名空間就是作用域的動態(tài)表現(xiàn)。
函數(shù)定義了本地作用域,而模塊定義了全局作用域:
? ? ? ?每個模塊都是一個全局作用域,因此,全局作用域的范圍僅限于單個程序文件
? ? ???每次對函數(shù)的調(diào)用都會創(chuàng)建一個新的本地作用域,賦值的變量除非聲明為全局變量,否則均為本地變量
? ? ? ?所有的變量名都可以歸納為本地,全局或內(nèi)置的(由__builtin__模塊提供)
? ? ??
?4)LEGB原則
LEGB含義解釋:
? ? ? ?L-Local(function);函數(shù)內(nèi)的名字空間
? ? ? ?E-Enclosing function locals;外部嵌套函數(shù)的名字空間(例如closure)
? ? ? ?G-Global(module);函數(shù)定義所在模塊(文件)的名字空間
? ? ? ?B-Builtin(Python);Python內(nèi)置模塊的名字空間,builtin作用域,對應(yīng)builtin命名空間,python內(nèi)部定義的最頂層的作用域,在這個作用域里面定義了各種內(nèi)建函數(shù):open、range、xrange、list等等??? ??
?前面講到,Python的命名空間是一個字典,字典內(nèi)保存了變量名與對象之間的映射關(guān)系,
因此,查找變量名就是在命名空間字典中查找鍵-值對。
Python有多個命名空間,因此,需要有規(guī)則來規(guī)定,按照怎樣的順序來查找命名空間,LEGB就是用來規(guī)定命名空間查找順序的規(guī)則。
??LEGB規(guī)定了查找一個名稱的順序為:local-->enclosing function locals-->global-->builtin
三、閉包
當(dāng)一個函數(shù)結(jié)束了,函數(shù)的內(nèi)部部分變量引用還存在,這就叫閉包
外層函數(shù)主要為內(nèi)層函數(shù)提供環(huán)境
定義在外層函數(shù)內(nèi),卻由內(nèi)層函數(shù)引用的變量,在外層函數(shù)返回時,如果外層函數(shù)返回的值是內(nèi)層函數(shù),再次調(diào)用內(nèi)層函數(shù)時,會記憶下內(nèi)層函數(shù)調(diào)用的外層函數(shù)的變量。
python的閉包可以使用可變?nèi)萜鲗崿F(xiàn),這也是python2唯一的方式
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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關(guān)鍵字:
??nonlocal關(guān)鍵字能且只能引用外部函數(shù)作用域中已存在的變量(不能在自己的作用域中定義);引用后的變量能讀寫
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 |
四、遞歸函數(shù)
? ??函數(shù)體內(nèi)調(diào)用自身的函數(shù)?? ? ??
? ??遞歸函數(shù)需要有合適的退出條件,否則就成了死循環(huán);?遞歸需要邊界條件,遞歸前進段和遞歸返回段?
? ? 在python中為了保護解釋器,遞歸深度最大為1000
? ? python中應(yīng)盡量避免遞歸,效率低(能轉(zhuǎn)化為迭代盡量轉(zhuǎn)化為迭代)??
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 |
本文轉(zhuǎn)自xiexiaojun51CTO博客,原文鏈接:?http://blog.51cto.com/xiexiaojun/1934142,如需轉(zhuǎn)載請自行聯(lián)系原作者
總結(jié)
以上是生活随笔為你收集整理的【函数】02、函数进阶的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在Java中选择Map/List/S
- 下一篇: 开始→运行→命令集锦