變量從作用域分類
作用范圍從小到大為,小作用域的可以調用大作用域的內容。
- 局部 Local
- 閉包 Enclosing
- 全局 Global
- 內建 Build-in
局部變量
局部變量是定義在函數中的,因此其作用域是在函數內部。
def
example
(
)
:
v
=
1
#局部變量
print
(
v
)
由于局部變量作用域只在函數內部有效,因此程序會報錯
Traceback
(
most recent call last
)
:
File
"test.py"
,
line
3
,
in
<
module
>
print
(
v
)
NameError
:
name
'v'
is
not
defined
全局變量
和局部變量相對,全局變量是定義在函數外的變量,因此具有更大的作用域。
v
=
1
def
example
(
)
:
print
(
v
)
example
(
)
運行結果
1
注意事項
python與C有許多不同的地方
(1)例子1
v
=
1
def
example
(
)
:
v
=
2
print
(
v
)
example
(
)
print
(
v
)
運行結果
2
1
當想要在函數中對全局變量進行賦值時,如上操作,python會生成新的局部變量,而不是修改全局變量的值,可以通過global標記在局部作用域中聲明全局變量。
v
=
1
def
example
(
)
:
global
v
v
=
2
print
(
v
)
example
(
)
print
(
v
)
運行結果
2
2
(1)例子2
def
example
(
)
:
print
(
v
)
v
=
1
example
(
)
運行結果
1
由于python是解釋性語言,不需要進行編譯,程序運行時每次讀源文件的一行代碼,并執行相應的操作,因此上述代碼可以正常運行,不會出現找不到變量v的問題。而當我們使用如下代碼則會報錯。
def
example
(
)
:
print
(
v
)
example
(
)
v
=
1
運行結果
Traceback
(
most recent call last
)
:
File
"test.py"
,
line
4
,
in
<
module
>
example
(
)
File
"test.py"
,
line
2
,
in
example
print
(
v
)
NameError
:
name
'v'
is
not
defined
閉包
在python中“一切皆對象”,因此函數也被看作對象,因此產生了一種特殊的形式函數嵌套。如下例子
def
out_func
(
)
:
a
=
1
b
=
2
def
iner_func
(
)
:
return
a
+
1
return
iner_func
閉包是在內嵌函數生成的時候將其用到的環境以及自己本身都封裝在了一起。為了直觀理解閉包,我們介紹一下 code 對象, code 對象是指代碼對象,表示編譯成字節的的可執行Python代碼,或者字節碼。它有幾個比較重要的屬性:
- co_name: 函數的名稱
- co_nlocals: 函數使用的局部變量的個數
- co_varnames: 一個包含局部變量名字的元組
- co_cellvars: 是一個元組,包含嵌套的函數所引用的局部變量的名字
- co_freevars: 是一個元組,保存使用了的外層作用域中的變量名
- co_consts: 是一個包含字節碼使用的字面量的元組
有如下程序
def
out_func
(
)
:
a
=
1
b
=
2
c
=
3
def
iner_func1
(
)
:
return
a
+
1
def
iner_func2
(
)
:
return
b
+
1
return
iner_func1
func
=
out_func
(
)
print
(
out_func
.
__code__
.
co_varnames
)
print
(
out_func
.
__code__
.
co_cellvars
)
print
(
out_func
.
__code__
.
co_freevars
)
print
(
func
.
__code__
.
co_varnames
)
print
(
func
.
__code__
.
co_cellvars
)
print
(
func
.
__code__
.
co_freevars
)
運行結果
(
'c'
,
'iner_func1'
,
'iner_func2'
)
(
'a'
,
'b'
)
(
)
(
)
(
)
(
'a'
,
)
前三個輸出是關于外部函數的一些屬性,由于out_func函數中聲明了幾個局部變量,因此包含三個變量。第二個輸出是內部的函數用到的變量,而iner_func1與iner_func2用到了變量a與b。第三個輸出是使用了的外部作用域的變量,因此是空。
后三個輸出是關于內部函數的一些屬性,前兩個輸出由于iner_func1沒有使用到因此為空,由于iner_func1用到了外部變量a,因此只有變量a。
若是采用三層的嵌套
def
outout_func
(
)
:
c
=
3
def
out_func
(
)
:
a
=
1
b
=
2
def
iner_func
(
)
:
return
a
+
1
+
c
return
iner_func
return
out_func
(
)
func
=
outout_func
(
)
print
(
func
.
__code__
.
co_varnames
)
print
(
func
.
__code__
.
co_cellvars
)
print
(
func
.
__code__
.
co_freevars
)
運行結果
(
)
(
)
(
'a'
,
'c'
)
在學習閉包的過程中,我個人對一下幾種情況有點蒙圈。
程序一
f
=
[
]
v
=
[
0
]
for
i
in
range
(
5
)
:
v
[
0
]
=
i
def
b
(
)
:
x
=
v
return
x
f
.
append
(
b
)
for
i
in
range
(
5
)
:
print
(
f
[
i
]
(
)
)
print
(
id
(
f
[
i
]
(
)
)
)
運行結果
[
4
]
1618988720712
[
4
]
1618988720712
[
4
]
1618988720712
[
4
]
1618988720712
[
4
]
1618988720712
程序二
f
=
[
]
v
=
[
0
]
for
i
in
range
(
5
)
:
def
b
(
)
:
x
=
v
v
[
0
]
=
i
return
x
f
.
append
(
b
)
for
i
in
range
(
5
)
:
print
(
f
[
i
]
(
)
,
id
(
f
[
i
]
(
)
)
)
運行結果
[
0
]
1397297537608
[
1
]
1397297537608
[
2
]
1397297537608
[
3
]
1397297537608
[
4
]
1397297537608
兩個程序類似,但是輸出完全不同。
第一個程序之所以輸出全是相同的,是因為在閉包生成的過程中打包了 v 的地址作為閉包環境。在函數調用時將 x 指向了相同的地址,也就是 v 的地址,而在函數調用之前, v 進行了5次賦值,最后一次被賦為了4,因此每次的輸出均相同。
第二個程序之所以輸出不同,是因為閉包在生成過程中將 v 的地址以及 i 的地址都打包了,而 i 指向的是常數,因此具有不同的地址。所以當函數運行時, x 先指向了 v 的地址,之后 v 中的數字被替換為 i 指向的不同常量,因此具有不同的輸出。
在這里若我們將 i 替換為列表會發生什么呢,有如下程序
f
=
[
]
ha
=
[
0
]
z
=
[
0
]
for
i
in
range
(
5
)
:
z
[
0
]
=
i
def
b
(
)
:
x
=
ha
ha
[
0
]
=
z
[
0
]
return
x
f
.
append
(
b
)
for
i
in
range
(
5
)
:
print
(
f
[
i
]
(
)
,
id
(
f
[
i
]
(
)
)
)
運行結果
[
4
]
2863541936712
[
4
]
2863541936712
[
4
]
2863541936712
[
4
]
2863541936712
[
4
]
2863541936712
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
