作者mantour (朱子)
看板Python
標題Re: [問題] 關於class 內變數的問題
時間Thu Jan 22 13:27:48 2009
※ 引述《gardenest (股海尋燈)》之銘言:
: 因為一樣是變數的問題,所以我直接回這個標題。
: 1、
: a = 1
: class testing:
: def test(self):
: a = 0
: print a
: def test2(self):
: print a
: obj = testing()
: obj.test()
: obj.test2()
: print a
: ----------output
: 0
: 1
: 1
: -----------
: 看起來像是最上面第一行a的值並沒有被test()改成0,所以後面印出來的還是1。
: 2、
: 但是當我將a改成list的型態時,它似乎會被改變了。
: a = [1]
: class testing:
: def test(self):
: a.append(2)
: print a
: def test2(self):
: print a
: obj = testing()
: obj.test()
: obj.test2()
: print a
: ---------output
: [1,2]
: [1,2]
: [1,2]
: ---------
: 看起來a在test()裡被改變了。
: 以上這2個例子讓我感到很困惑,一個會被改,一個不會被改,
: 想請問為什麼會有這樣子的差異呢?
以下是我目前的理解,如果有什麼錯的地方還請各位指正^^
先簡化一下問題
def function1():
a=1
print a
return
def function2():
print a
return
a=0
function1()
function2()
print a
結果是
1
0
0
因為python沒有變數宣告
所以當你在函式內assign一個值給某個變數時
它就當作在這個函式內產生一個區域變數
所以function1的a就被當成是區域變數,後面的print就是print區域變數的值
而function2內沒有名為a的區域變數,所以它就會去外面找最近的a來print
同樣的
def function1():
a=[1]
print a
return
def function2():
print a
return
a=[0]
function1()
function2()
print a
這樣會得到
[1]
[0]
[0]
還有一個有趣的行為是
def function1():
print a
a=1
print a
return
a=0
function1()
它會回應: UnboundLocalError: local variable 'a' referenced before assignment
即是說,只要函式中任何一個位置assign了a的值,a就會在整個函式中被當成
區域變數(不管是assign前還是assign後),所以你就不能在assign之前對a取值
但是如果妳在函數中調用變數的method的話,因為函式中沒有同名的區域變數
所以它就會去外面找最近的同名變數
比如說
def function1():
a[0]=1
print a
return
a=[0]
function1()
print a
就會得到
[1]
[1]
因為在function1中 讀到a[0]=1這一行時,它會去找function1中有沒有指定'a'為區域
變數,沒有的話就會去外面找最近的a來用囉
這個特性有時可以玩一些tricks
例如:
counter=0
def function():
counter=counter+1
return counter
function()
它會說
UnboundLocalError: local variable 'counter' referenced before assignment
因為沒有變數宣告的機制,所以它把counter當作是區域變數了
一個變通的方法就是使用global statement
counter=0
def function():
global counter
counter=counter+1
return counter
function()
function()
function()
就會得到
1
2
3
...
在這邊global counter這一行的意思就是告訴interpreter說在這邊的'counter'
要用的是"最外面"的counter
然而你也可以用前面所說的特性來達到同樣的效果,例如:
counter=[0]
def function():
counter[0]=counter[0]+1
return counter[0]
function()
function()
function()
此外,global的使用還是有一些限制
例如:
def geniter():
c=0
def iter():
global c
c=c+1
return c
return iter
iter=geniter()
iter()
它又會跟你說:
NameError: global name 'c' is not defined
因為你使用global statement的時候
它會去「最外層」找全域變數來用
所以又會跟你說找不到…
但是使用前面的技巧,就還是可以達成目的:
def geniter():
c=[0]
def iter():
c[0]=c[0]+1
return c[0]
return iter
iter1=geniter()
iter2=geniter()
iter1()
iter1()
iter1()
iter2()
iter2()
iter2()
就會得到
1
2
3
1
2
3
一個新聞是在python 3.0中多了 nonlocal statement可以用來表示
我要使用「上一層」的變數
所以應該就可以寫成
def generator():
c=0
def f():
nonlocal c
c=c+1
return c
return f
感覺上會變得比較直覺(沒有裝3.0所以沒試過)
總之沒有宣告變數的機制其實還是有一些不方便的地方啦
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 140.112.213.158
※ 編輯: mantour 來自: 140.112.213.158 (01/22 13:31)
※ 編輯: mantour 來自: 140.112.213.158 (01/22 13:33)
推 aquarianboy:補充一下,第一個範例的結果應該是1 0 0 印了三次 :) 01/22 13:59
謝謝 已改^^
※ 編輯: mantour 來自: 140.112.213.158 (01/22 14:01)
推 gardenest:感謝man大的解答,非常詳細,這個問題困惑我好久,終於 01/22 15:38
→ gardenest:決了^__^ 01/22 15:38
→ gardenest:解決了^__^ 01/22 15:39