函數的返回值和作用域
1、返回值
<code>def
guess
(x)
:if
x >3
:return
"> 3"
else
:return
"<= 3"
print(guess(10
)) /<code>
1> Python 函數使用 return 語句返回 "返回值”
2> 所有函數都有返回值,如果沒有 return 語句,隱式調用 return None
3> return 語句並不一定是函數的語句塊的最後一條語句
4> 一個函數可以存在多個 return 語句,但是隻有一條可以被執行。如果沒有一條 return 語句被執行到,隱式調用 return None
5> 如果有必要,可以顯示調用return None,可以簡寫為return
6> 如果函數執行了 return 語句,函數就會返回,當前被執行的 return 語句之後的其它語句就不會被執行了
7> 返回值的作用: 結束函數調用、返回 "返回值”
2、能一次返回多個值嘛?
<code>def
showvalues
()
:return
1
,3
,5
print(showvalues()) /<code>
函數不能同時返回多個值
return 1, 3, 5 看似返回多個值,隱式的被 python 封裝成了一個元組
x, y, z = showvalues() 使用解構提取返回值更為方便
3、函數作用域**
3.1 作用域
一個標識符的可見範圍,這就是標識符的作用域,一般常說變量的作用域。
<code>x =20
def
fn
()
: x =100
fn() print(x) /<code>
注意:每一個函數都會開闢一個作用域。
3.2 作用域分類
全局作用域:
在整個程序運行環境中都可見
全局作用域中的變量稱為全局變量
局部作用域:
在函數、類等內部可見
局部作用域中的變量稱為局部變量,其使用範圍不超過其所在局部作用域
<code>def
fn1
()
: x =1
def
fn2
()
: print(x) print(x) /<code>
<code> x =5
def
foo
()
: print(x) foo() /<code>
一般來講外部作用域變量在函數內部可見,可以使用
反過來,函數內部的局部變量,不能在函數外部看到
4、函數嵌套
在一個函數中定義另一個函數
<code>def
outer
()
:def
inner
()
: print('inner'
) print('outer'
) inner() outer() inner() /<code>
內部函數 inner 不能在外部直接使用,會拋出 NameError 異常,因為它在函數外部不可見。
其實,inner 不過就是一個 標識符 ,就是一個 函數 outer 內部定義的變量 而已。
5、嵌套函數的作用域
<code>def
outer
()
: o =65
def
inner
()
: o =97
print('inner'
, o) print('outer 1 '
, o) inner() print('outer 2 '
, o) outer() /<code>
外層變量在內部作用域可見。
內層作用域中,如果定義了和外層相同的變量,相當於在當前函數作用域中重新定義了一個新的變量,這個內層變量並不能覆蓋掉外部作用域中的變量。
6、一個賦值語句的問題
<code>x =100
def
fn
()
: y = x +200
print(y) fn() /<code>
<code>x =100
def
fn
()
: x +=1
print(x) fn() /<code>
<code>x =100
def
fn
()
: print(x) x +=1
print(x) fn() /<code>
能否解決呢?可以,使用 global 語句
<code>x =100
def
fn
()
:global
x print(x) x +=1
print(x) fn() print(x) /<code>
注意:全局變量一般情況不推薦修改,一旦在作用域中使用 global 聲明全局變量,那麼相當於在對全局變量賦值、定義。
global 使用原則:
1> 外部作用域變量會在內部作用域可見,但也不要在這個內部的局部作用域中直接使用,因為函數的目的就是為了封裝,儘量與外界隔離。
2> 如果函數需要使用外部全局變量,請儘量使用函數的形參定義,並在調用傳實參解決。
3> 一句話:不用 global,學習它就是為了深入理解變量作用域。
<code> y = []def
foo
()
: y.append(1
) foo() foo() print(y) /<code>
<code> y = []def
foo
(x)
: x.append(1
) foo(y) foo(y) print(y) /<code>
7、閉包**
自由變量:未在本地作用域中定義的變量。例如 定義在內層函數外的外層函數的作用域中的變量 。
閉包:就是一個概念,出現在嵌套函數中,指的是 內層函數引用到了外層函數的自由變量 ,就形成了閉包。很多語言都有這個概念,最熟悉就是 JavaScript。
<code>def
counter
()
: c = [0
]def
inc
()
: c[0
] +=1
return
c[0
]return
inc m = counter() m() m() m() print(m()) /<code>
<code>def
counter
()
:global
c c =0
def
inc
()
:global
c c +=1
return
creturn
inc m = counter() m() m() m() print(m()) /<code>
<code>def
counter
()
: c =0
def
inc
()
:nonlocal
c c +=1
return
creturn
inc m = counter() m() m() m() print(m()) /<code>
nonlocal 語句:將變量標記為不在本地作用域定義,而是 在上級的某一級局部作用域 中定義,但 不能是全局作用域中 。
8、默認值的作用域
<code>def
foo
(x=
1
): x +=1
print(x) foo() foo()def
bar
(x=[])
: x.append(1
) print(x) bar() bar() /<code>
為什麼上列 bar 函數第二次調用打印的是 [1, 1]?
因為函數也是對象,每個函數定義被執行後,就生成了一個函數對象和函數名這個標識符關聯。
python 把函數的默認值放在了函數對象的屬性中,這個屬性就伴隨著這個函數對象的整個生命週期。
<code># 查看foo
.__defaults__
屬性,它是個元組def
bar
(x=[]):x
.append
(1
)bar
() #[1]
bar
() #[1, 1]
[1]
([1
],)[1, 1]
([1
,1
],) # 元組不變,記錄的是地址,引用類型變化 /<code>
<code>def
foo
(x, m=
123
, n='abc'
): m=456
n='def'
print(x) print(foo.__defaults__) foo('yang'
) print(foo.__defaults__) /<code>
<code>def foo(x, m=123
, *, n='abc'
, t=[1
,2
]): m=456
n='def'
t.append
(12
) #t[:].append
(12
) # t[:],全新複製一個列表,避免引用計數123
,) {'n'
:'abc'
,'t'
: [1
,2
]} foo('yang'
)123
,) {'n'
:'abc'
,'t'
: [1
,2
,12
]} /<code>
<code>def
x
(a=[])
: a = a + [5
] print(x.__defaults__) x() x() print(x.__defaults__)def
y
(a=[])
: a += [5
] print(y.__defaults__) y() y() print(y.__defaults__) l1 = [1
,2
] l2 = [3
,4
] l3 = l1 + l2 print(id(l1), id(l2), id(l3)) l3 += l2 print(id(l3)) 執行結果:2279905055304
2279905055816
2279906334216
2279906334216
/<code>
9、變量名解析原則 LEGB**
Local, 本地作用域 、局部作用域的 local 命名空間,函數調用時創建,調用結束消亡。
Enclosing,Python2.2 時引入了 嵌套函數,實現了閉包,這個就是 嵌套函數的外部函數的命名空間 。
Global, 全局作用域 ,即一個模塊的命名空間。模塊被 import 時創建, 解釋器退出時消亡。
Build-in, 內置模塊的命名空間 ,生命週期從 python 解釋器啟動時創建到解釋器退出時消亡。例如 print(open),print 和 open 都是內置的變量。
所以一個名詞的查找順序就是 LEGB
10、函數的銷燬
定義一個函數就是生成一個函數對象,函數名指向的就是函數對象。可以使用del語句刪除函數,使其引用計數減 1。可以使用同名標識符覆蓋原有定義,本質上也是使其引用計數減 1。Python程序結束時,所有對象銷燬。函數也是對象,也不例外,是否銷燬,還是看引用計數是否減為 0。