您好,登錄后才能下訂單哦!
?
?
函數返回值、作用域、enclosing閉包
?
目錄
函數的返回值:... 1
作用域:... 3
enclosing閉包:... 6
默認值的作用域:... 9
可變類型默認值:... 11
函數的銷毀:... 13
?
?
?
函數中return語句,在執行過程中只要一到return處會將函數打斷直接返回,而break是退出當前loop循環,return有break作用,但比break更狠;
?
總結:
python函數使用return語句返回“返回值”;
所有函數都有返回值,如果沒有return語句,隱式調用return None;
return語句并不一定是函數語句塊的最后一條語句;
一個函數可以存在多個return語句,但只有一條可被執行,如果沒有一條return語句被執行到,隱式調用return None;
如果有必要,可以顯式調用return None,簡寫為return;
如果函數執行了return語句,函數就會返回,當前被執行的return語句之后的其它語句就不會被執行了;
作用:結束函數調用、返回值;
?
返回多個值:
函數不能返回多個值;
return [1,2,3],是指明返回一個列表,是一個列表對象;
return 1,3,5,看似返回多個值,隱式的被python封裝成了一個tuple;
配合解構會更加方便,如x,y,z=showlist(),x,*_,y=showlist();
?
In [15]: def showplus(x):
??? ...:???? print(x)
??? ...:???? return x+1
??? ...:
In [16]: showplus(5)
5
Out[16]: 6
In [17]: def showplus(x):
??? ...:???? print(x)
??? ...:???? return x+1
??? ...:???? print(x+1)?? #會執行嗎?不會,廢語句
??? ...:????
In [18]: showplus(5)
5
Out[18]: 6
In [20]: def guess(x):
??? ...:???? if x>3:
??? ...:???????? return '>3'
??? ...:???? else:
??? ...:???????? return '<3'
??? ...:????
In [21]: guess(10)?? #多條return語句,return可以寫多個,但只能執行一個return
Out[21]: '>3'
In [22]: def showplus(x):
??? ...:???? print(x)
??? ...:???? return x+1
??? ...:???? return x+2?? #廢語句
??? ...:
In [23]: showplus(5)
5
Out[23]: 6
In [24]: def fn(x):
??? ...:???? for i in range(x):
??? ...:???????? if i>3:
??? ...:???????????? return i?? #執行到return處會將函數打斷直接返回
??? ...:???? else:
??? ...:???????? print('{} is not greater than 3'.format(x))?? #else段隱含有return None,一般都不寫除非明確指定返回None
??? ...:????????
In [25]: fn(5)
Out[25]: 4
In [26]: fn(3)?? #是控制臺輸出,不是函數返回
3 is not greater than 3
?
例:
In [27]: def showlist():
??? ...:???? return [1,3,5]?? #返回一個值[1,3,5]
??? ...:
In [28]: def showlist():
??? ...:???? return 1,3,5?? #返回多個值,封裝,等價于return (1,3,5)
??? ...:
In [29]: x,y,z=showlist()?? #使用解構提取更為方便
In [30]: x,y,z
Out[30]: (1, 3, 5)
?
函數嵌套:
在一個函數中定義了另外一個函數;
函數有可見范圍,這就是作用域的概念(對標識符作控制),函數內定義的在函數外不可見,函數外定義的在函數內可見;
內部函數不能被外部函數直接使用,會拋NameError異常;
?
例:
In [31]: def outer():
??? ...:???? def inner():
??? ...:???????? print('inner')
??? ...:???? print('outer')
??? ...:???? inner()
??? ...:????
?
In [32]: outer()
outer
inner
In [33]: inner()
---------------------------------------------------------------------------
NameError???????????????????????????????? Traceback (most recent call last)
<ipython-input-33-bc10f1654870> in <module>()
----> 1 inner()
NameError: name 'inner' is not defined
?
?
?
一個標識符的可見范圍,這就是標識符的作用域,一般常說的是變量的作用域;
?
global全局作用域,在整個程序運行環境中都可見;
local局部作用域,在函數、類,內部可見,局部變量使用范圍不能超過其所在的局部作用域;
?
global總結
x+=1,這種是特殊形式產生的錯誤,原因,先引用后賦值,而python動態語言是賦值才算定義,才能被引用;解決辦法,在這條語句前加x=0之類的賦值語句,或使用global告訴內部作用域去全局作用域中查找變量定義;
內部作用域使用x=5之類的賦值語句會重新定義局部作用域的變量x,但是,一旦這個作用域中使用global聲明為x為全局的,那么x=5相當于在全局作用域的x賦值;
global使用原則,外部作用域變量在內部作用域可見,但也不要在這個內部的局部作用域中直接使用,因為函數的目的就是為了封裝,盡量與外界隔離;如果函數需要使用外部全局變量,請使用函數的形參來傳參解決;一句話,不用global,學習它是為了深入理解變量作用域;
?
變量名解析原則(LEGB):
local,本地作用域,局部作用域的local命名空間,函數調用時創建,調用結束消亡;
enclosing,python2.2引入了嵌套函數,實現了閉包,這個就是嵌套函數的外部函數的命名空間;
global,全局作用域,即一個模塊的命名空間,模塊被import時創建,解釋器退出時消亡;
build-in,內建模塊的命名空間,生命周期從python解釋器啟動時創建到解釋器退出時消亡,如print(open),print和open都是內置的變量;
一個名詞的查找順序就是LEGB,即local-->enclosing-->global-->build-in;
?
例:
In [1]: x=5
In [2]: def fn():
?? ...:???? print(x)
?? ...:????
In [3]: fn()
5
?
例:
In [4]: x=5
In [5]: def fn():
?? ...:???? x+=1?? #即x=x+1,如果是y=x+1或x=1均正確,而x=x+1,x=是在賦值,=右邊又有x,已在用;相當于在fn內部定義一個局部變量x,那么fn內部所有x都是這個局部變量x了,但是這個x還沒有完成賦值,就被右邊拿來作加1操作了;解決:在x+=1前加入x=0或使用global關鍵字定義,使用global的話外層要有x的定義
?? ...:???? print(x)
?? ...:????
In [6]: fn()
---------------------------------------------------------------------------
UnboundLocalError???????????????????????? Traceback (most recent call last)
<ipython-input-6-e1d0f67027b9> in <module>()
----> 1 fn()
<ipython-input-5-9e265d33dd3d> in fn()
????? 1 def fn():
----> 2???? x+=1
????? 3???? print(x)
????? 4
UnboundLocalError: local variable 'x' referenced before assignment
?
例:
In [8]: def fn1():
?? ...:???? x=1?? #local variable,局部作用域,在fn1內,內部高度自治
?? ...:????
In [11]: del x
In [13]: def fn2():
??? ...:???? pritn(x)?? #x不可見
??? ...:????
In [14]: fn2()
---------------------------------------------------------------------------
NameError???????????????????????????????? Traceback (most recent call last)
……
?
例:
In [15]: def outer():
??? ...:???? o=65?? #外層變量作用域在內層作用域可見
??? ...:???? def inner():
??? ...:???????? print('inner {}'.format(o))
??? ...:???????? print(chr(o))
??? ...:???? print('outer {}'.format(o))
??? ...:???? inner()
??? ...:????
In [16]: outer()
outer 65
inner 65
A
In [17]: def outer2():
??? ...:???? o=65
??? ...:???? def inner():
??? ...:???????? o=97?? #賦值即定義,局部作用域,內層作用域inner中定義了o,相當于當前作用域中重新定義了一個新的變量o,但這個o并沒有覆蓋外層作用域outer2中的o
??? ...:???????? print('inner {}'.format(o))
??? ...:???????? print(chr(o))
??? ...:???? print('outer {}'.format(o))
??? ...:???? inner()
??? ...:????
In [18]: outer2()
outer 65
inner 97
a
?
例:
In [26]: x=5
In [27]: def fn():
??? ...:???? global x?? #使用global關鍵字的變量,將fn內的x聲明為使用外部的全局作用域中定義的x;全局作用域中必須有x的定義;如果全局作用域中沒有定義,拋NameError異常,NameError與之前的UnboundLocalError在本質上一樣
??? ...:???? x+=1
??? ...:???? print(x)
??? ...:????
In [28]: fn()
6
In [29]: del x
In [30]: del fn
In [31]: def fn():
??? ...:???? global x
??? ...:???? x+=1
??? ...:???? print(x)
??? ...:????
In [32]: fn()
---------------------------------------------------------------------------
NameError ????????????????????????????????Traceback (most recent call last)
……
NameError: name 'x' is not defined
In [34]: def foo():
??? ...:???? global x
??? ...:???? x=20?? #使用global關鍵字的變量,將foo內的x聲明為使用外部的全局作用域中定義的x;但x=20賦值即定義,x在內部作用域為一個外部作用域的變量賦值,所以x+=2不會報錯;這里x的作用域是全局
??? ...:???? x+=2
??? ...:???? print(x)
??? ...:????
In [35]: foo()
22
In [36]: x
Out[36]: 22
?
?
?
自由變量,未在本地作用域中定義的變量,如定義在內層函數外的外層函數的作用域中的變量;
閉包,是一個概念,出現在函數嵌套中,指內層函數引用到了外層函數的自由變量,很多語言都有這個概念,最常見的js;
python3的閉包,還可使用nonlocal關鍵字;
python2的閉包,只能對變量中引用的元素更改(c[0]+=1,c.append(1),c.add(2)),而python3中使用nonlocal能直接用x+=1;
使用nonlocal關鍵字,將變量標記為在上級的局部作用域中定義,但不能是全局作用域中定義的;
標識符可以沖突,如int='abc',將把int給覆蓋了;而關鍵字不能沖突,如nonlocal;
?
In [37]: def counter():
??? ...:???? c=[0]?? #自由變量
??? ...:???? def inner():
??? ...:???????? c[0]+=1?? #會報錯嗎?不會,與x+=1不一樣,不是對c變量改變,改的是c中的元素,c已在counter中定義了,而且inner中是修改c元素的值,而不是重新定義變量
??? ...:???????? return c[0]
??? ...:???? return inner?? #返回的是標識符,即函數對象,有()才是調用
??? ...:
In [38]: foo=counter()?? #foo是callable function可調用對象,即函數對象inner,
In [39]: inner()?? #全局內沒有定義過inner
---------------------------------------------------------------------------
NameError???????????????????????????????? Traceback (most recent call last)
<ipython-input-39-bc10f1654870> in <module>()
----> 1 inner()
NameError: name 'inner' is not defined
In [40]: print(foo(),foo())
1 2
In [41]: c=100?? #global variable,此處的c與counter中的c不一樣,inner里引用的是自由變量counter中的c
In [44]: print(foo())
3
?
例:
In [47]: def outer():
??? ...:???? c=5
??? ...:???? def inner():
??? ...:????? ???z=c+1?? #是enclosing
??? ...:???????? return z
??? ...:???? return inner
??? ...:
In [48]: def outer():
??? ...:???? c=5
??? ...:???? def inner():
??? ...:???????? c=6?? #不是enclosing
??? ...:???????? return c
??? ...:???? return inner
??? ...:
?
例:
In [49]: def outer():
??? ...:???? count=0
??? ...:???? def inner():
??? ...:???????? global count?? #此global僅在inner()和最外層可見,outer中不可見,最外層中若沒有定義count,在調用inner時會報錯
??? ...:???????? count+=1
??? ...:???????? return count
??? ...:???? return inner
??? ...:
In [51]: foo=outer()
In [52]: foo()?? #最外層未定義count
---------------------------------------------------------------------------
NameError???????????????????????????????? Traceback (most recent call last)
……
NameError: name 'count' is not defined
?
例:
In [53]: def outer():
??? ...:???? count=0
??? ...:???? def inner():
??? ...:???????? count+=1
??? ...:???????? return count
??? ...:???? return inner
??? ...:
In [54]: foo=outer()
In [55]: foo()?? #會報錯,使用global能解決,但使用的是全局變量而不是閉包,如果要對普通變量閉包,python3中可用nonlocal
---------------------------------------------------------------------------
……
UnboundLocalError: local variable 'count' referenced before assignment
In [61]: def outer():
??? ...:???? global count
??? ...:???? def inner():
??? ...:???????? count+=1
??? ...:???????? return count
??? ...:???? return inner
??? ...:
?
In [62]: foo=outer()
?
In [63]: foo()
---------------------------------------------------------------------------
……
UnboundLocalError: local variable 'count' referenced before assignment
?
例(閉包,使用nonlocal關鍵字):
In [69]: def outer():
??? ...:???? count=0?? #count是外層函數的局部變量,被內部函數引用
??? ...:???? def inner():
??? ...:???????? nonlocal count?? #內部函數使用nonlocal關鍵字聲明,count變量在上一級作用域中
??? ...:???????? count+=1
??? ...:???????? return count
??? ...:???? return inner
??? ...:
In [70]: foo=outer()
In [71]: foo()
Out[71]: 1
In [72]: foo()
Out[72]: 2
?
例:
In [73]: a=50
In [74]: def outer():
??? ...:???? nonlocal a?? #變量a不能在全局的作用域中
? File "<ipython-input-74-cd05dfa8d327>", line 2
??? nonlocal a
??? ^
SyntaxError: no binding for nonlocal 'a' found
?
例:
In [60]: def outer():
??? ...:???? count=0
??? ...:???? def middle():
??? ...:???????? def inner():
??? ...:???????????? nonlocal count?? #嵌套三層,是閉包
??? ...:???????????? count+=1
??? ...:???????????? return count
??? ...:???????? return inner
??? ...:???? return middle
??? ...:
In [61]: a=outer()
In [62]: b=a()
In [63]: c=b()
In [64]: c
Out[64]: 1
?
函數的缺省值一般用不可變類型;
python把函數的默認值放在了屬性中,這個屬性就伴隨著這個函數對象的整個生命周期,可查看foo.__defaults__屬性;
屬性__defaults__中使用tuple保存所有默認值,該元組中若有引用類型,是引用類型的元素變動,并不是該元組的變化;
屬性__defaults__中使用tuple保存所有默認值,它不會因為在函數體內使用了它而發生改變;
位置參數的默認值在__defaults__里;
關鍵字參數的默認值在__kwdefaults__里;
?
例:
In [1]: def foo(xyz=1):
?? ...:???? print(xyz)
?? ...:????
In [2]: foo()
1
In [3]: foo()
1
In [4]: print(xyz)
……
NameError: name 'xyz' is not defined
In [5]: def foo(xyz=[]):?? #引用類型要注意,xyz在函數調用完就消亡了,此處是因為默認值與棧有關
?? ...:???? xyz.append(1)
?? ...:???? print(xyz)
?? ...:????
In [6]: foo()
[1]
In [7]: foo()?? #因為函數也是對象,python把函數的默認值放在了屬性中,這個屬性就伴隨著這個函數對象的整個生命周期,可查看foo.__defaults__屬性
[1, 1]
In [8]: print(xyz)
……
NameError: name 'xyz' is not defined
In [9]: foo.__defaults__?? #屬性中使用元組保存所有值
Out[9]: ([1, 1],)
?
例:
In [10]: def foo(xyz=[],m=5,n=6):
??? ...:???? xyz.append(100)
??? ...:???? print(xyz)
??? ...:????
In [11]: print(1,foo.__defaults__)
1 ([], 5, 6)
In [12]: print(2,foo(),id(foo))?? #函數地址沒有變,即函數對象沒有變,調用它,它的屬性__defaults__中使用元組保存所有值;xyz默認值是引用類型,引用類型的元素變動,并不是元組的變化
[100]
2 None 140519573187848
In [14]: print(3,foo.__defaults__)
3 ([100], 5, 6)
In [15]: print(4,foo(),id(foo))
[100, 100]
4 None 140519573187848?? #兩次id(foo)有無變化,無,cpython的id取的是foo函數對象的地址
In [17]: print(5,foo.__defaults__)
5 ([100, 100], 5, 6)
?
例(非引用類型例子):
In [19]: def foo(w,u='abc',z=123):
??? ...:???? print(w,u,z)
??? ...:???? u='xyz'
??? ...:???? z=789
??? ...:???? print(w,u,z)
??? ...:????
In [20]: foo.__defaults__
Out[20]: ('abc', 123)
In [21]: foo('magedu')
magedu abc 123
magedu xyz 789
In [22]: foo.__defaults__?? #屬性__defaults__中使用tuple保存所有默認值,它不會因為在函數體內使用了它而發生改變
Out[22]: ('abc', 123)
?
?
?
如果使用默認值,就可能修改這個默認值;
有時候這個特性是好的,有時候這種特性不好,有副作用;
如何做到按需改變?2種方法:
方1、使用shadow copy創建一個新的對象,永遠不能改變傳入的參數;
方2、通過值的判斷,如if xyz is None:,就可靈活的選擇創建或修改傳入對象,這種方式更靈活,應用更廣泛,很多函數的定義(很多標準庫),都可看到使用None這個不可變的值作為默認參數,可以說這是一種慣用法;
?
例(方1):
In [24]: def foo(xyz=[],u='abc',z=123):
??? ...:???? xyz=xyz[:]
??? ...:???? xyz.append(1)
??? ...:???? print(xyz)
??? ...:???? return xyz
??? ...:
In [25]: foo()
[1]
Out[25]: [1]
In [26]: print(foo.__defaults__)
([], 'abc', 123)
In [27]: foo()
[1]
Out[27]: [1]
In [28]: print(foo.__defaults__)
([], 'abc', 123)
In [29]: foo([10])
[10, 1]
Out[29]: [10, 1]
In [30]: print(foo.__defaults__)
([], 'abc', 123)
In [31]: foo([10,5])
[10, 5, 1]
Out[31]: [10, 5, 1]
In [32]: print(foo.__defaults__)
([], 'abc', 123)
?
例(方2):
In [34]: def foo(xyz=None,u='abc',z=123):?? #常用,使用不可變類型默認值,如果使用缺省值None就創建一個列表,如果傳入一個列表就修改這個列表
??? ...:???? if xyz is None:
??? ...:???????? xyz=[]
??? ...:???? xyz.append(1)
??? ...:???? print(xyz)
??? ...:???? return xyz
??? ...:
In [35]: foo()
[1]
Out[35]: [1]
In [36]: print(foo.__defaults__)
(None, 'abc', 123)
In [37]: foo()
[1]
Out[37]: [1]
In [38]: print(foo.__defaults__)
(None, 'abc', 123)
In [39]: foo([10])
[10, 1]
Out[39]: [10, 1]
In [40]: print(foo.__defaults__)
(None, 'abc', 123)
In [41]: foo([10,5])
[10, 5, 1]
Out[41]: [10, 5, 1]
In [42]: print(foo.__defaults__)
(None, 'abc', 123)
In [43]: a=foo([10])
[10, 1]
In [44]: print(a)
[10, 1]
In [45]: b=foo()
[1]
In [46]: a=foo(b)
[1, 1]
In [47]: print(a)
[1, 1]
In [48]: c=foo(a)
[1, 1, 1]
?
?
?
全局函數的銷毀:
重新定義同名函數;
del語句刪除函數對象;
程序結束時;
?
局部函數銷毀:
重新在上級作用域定義同名函數;
del語句刪除函數對象,引用計數減1;
上級作用域銷毀時;
?
例:
In [49]: def foo(xyz=[],u='abc',z=123):
??? ...:???? xyz.append(1)
??? ...:???? return xyz
??? ...:
In [50]: print(foo(),id(foo),foo.__defaults__)
[1] 140519584127992 ([1], 'abc', 123)
In [51]: def foo(xyz=[],u='abc',z=123):?? #重新定義后,覆蓋了之前的foo
??? ...:???? xyz.append(1)
??? ...:???? return xyz
??? ...:
In [52]: print(foo(),id(foo),foo.__defaults__)
[1] 140519572782888 ([1], 'abc', 123)
In [53]: del foo
In [54]: print(foo(),id(foo),foo.__defaults__)
---------------------------------------------------------------------------
NameError???????????????????????????????? Traceback (most recent call last)
<ipython-input-54-d4746b4548a6> in <module>()
----> 1 print(foo(),id(foo),foo.__defaults__)
NameError: name 'foo' is not defined
?
例:
In [55]: def foo(xyz=[],u='abc',z=123):
??? ...:???? xyz.append(1)
??? ...:???? def inner(a=10):
??? ...:???????? pass
??? ...:???? def inner(a=100):
??? ...:???????? print(xyz)
??? ...:?? ??print(inner)
??? ...:???? return inner
??? ...:
In [56]: bar=foo()
<function foo.<locals>.inner at 0x7fcd43383ea0>
In [57]: print(id(foo),id(bar),foo.__defaults__,bar.__defaults__)
140519573186624 140519572782752 ([1], 'abc', 123) (100,)
In [58]: del bar
In [59]: print(id(foo),id(bar),foo.__defaults__,bar.__defaults__)
---------------------------------------------------------------------------
NameError???????????????????????????????? Traceback (most recent call last)
<ipython-input-59-3984e90a440f> in <module>()
----> 1 print(id(foo),id(bar),foo.__defaults__,bar.__defaults__)
NameError: name 'bar' is not defined
?
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。