2012-10-10 35 views
1

我只是做一個Foo類來解釋我的意思:,自我並不意味着實例?

class Foo: 
    def __init__(self, fakevalue): 
     self.fv = fakevalue 

    @staticmethod 
    def make_a_foo(): 
     return Foo(2) 

    def try_change_foo_value(self): 
     self = Foo.make_a_foo() 
     print "in change function, self.fv:", self.fv 

if(__name__ =='__main__'): 
    foo_instance = Foo(1) 
    print "foo_instance.fv:", foo_instance.fv 
    foo_instance.try_change_foo_value() 
    print "foo_instance.fv:", foo_instance.fv 

我想到:

foo_instance.fv: 1 
in change function, self.fv: 2 
foo_instance.fv: 2 

但結果是:

foo_instance.fv: 1 
in change function, self.fv: 2 
foo_instance.fv: 1 

我們可以看到自我價值有已經改變了,但實例值沒有。 爲什麼?以及如何解決這個問題?

回答

3

在這種情況下,self是一個指向調用者實例的指針。儘管您更改了try_change_foo_value中的指針,但它更改了函數中的參數:它在方法外沒有任何效果。

爲了澄清這一點,你可以考慮

a.try_change_foo_value()作爲簡寫Foo.try_change_foo_value(a)。這應該很明顯,當您更改self時,您正在更改本地變量,而調用者a保持不變。

爲了解決這個問題,你應該做的

def try_change_foo_value(self): 
    self.fv = 2 

但mgilson指出,這是更Python,只是說foo_instance.fv = 2

如果您想要實際更改foo_instance(而不是fv)的值,那麼您應該在其他位置執行此操作,而不是在方法上(這在概念上很少有意義)。

+0

請注意,在python中編寫一個只改變屬性值的函數實際上並非慣用。 *通常*直接更改屬性會更乾淨:'instance.fv = 2',而不是'instance.change_fv(2)'。 – mgilson

+0

@mgilson感謝您指出這一點,您絕對正確。我已更新以反映。 –

+0

謝謝!我知道了! – Yang

0

您正在更改方法try_change_foo_value中的容器「self」。請記住,自我是作爲參數傳入的,這意味着您可以更改自變量的內部引用;但是你不能改變調用者傳遞給你的引用。用Java思考。

public void changeIt(Foo foo) { 
    foo = new Foo(2); 
} 

如果我叫

Foo bar = new Foo(1); 
.changeIt(bar); 

我可以期待欄仍是 「新富(1)。」

+3

「用Java思考」 - 我寧願不要;-) – mgilson

1

self只是一個變量。對於常規方法(例如,未使用@staticmethod或@classmethod ...進行修飾),傳入的對象是實例。然而,在函數內部,你可以重新綁定self爲任何你想要的,但是這些改變不會被保留在函數之外,因爲賦值只是對右側的對象進行新的引用 - 除非你返回該引用或者將它存儲在一個範圍更廣的對象上,當函數結束後再也不會再聽到它(因爲你不再有引用/句柄),它將超出範圍。

考慮常規功能:

def foo(x): 
    x = 2 

a = 1 
foo(a) 
print a #prints 1, not 2 

這實在是沒有什麼不同。

0

try_change_foo_value,self作爲實例開始,但您在賦值時更改它。你可以通過打印的self進行檢查。

class Foo: 
    def __init__(self, fakevalue): 
     self.fv = fakevalue 

    @staticmethod 
    def make_a_foo(): 
     return Foo(2) 

    def try_change_foo_value(self): 
     print 'Originally, id(self) = %s' % id(self) 
     self = Foo.make_a_foo() 
     print 'After assignment, id(self) = %s' % id(self) 
     print "in change function, self.fv:", self.fv 

if __name__ =='__main__': 
    foo_instance = Foo(1) 
    print "foo_instance.fv:", foo_instance.fv 
    foo_instance.try_change_foo_value() 
    print "foo_instance.fv:", foo_instance.fv 

請注意id s。

foo_instance.fv: 1 
Originally, id(self) = 4299779608 
After assignment, id(self) = 4299779680 
in change function, self.fv: 2 
foo_instance.fv: 1 
0

在Python中有一個關於賦值的非常簡單的規則。在Python中指定名稱總是只是讓該名稱現在引用您剛分配給它的任何對象。它從來沒有實際上改變任何對象名稱之前提到的任務。

這樣做的一個簡單結果是,在行self = Foo.make_a_foo(),它是完全不相關什麼self在此之前。我們談論類和實例的事實與此無關。不管self之前是什麼,我們現在只需要使用名稱self即指新的Foo對象。

請記住,名稱只是一個句柄,可讓您獲得對象。給定的對象可以在任何給定的時刻由0,1或多個名稱來引用。賦值是影響名稱而不是對象的操作。您無法通過更改引用它的名稱來更改對象。

foo_instance = Foo(1) 
print "foo_instance.fv:", foo_instance.fv 
foo_instance.try_change_foo_value() 
print "foo_instance.fv:", foo_instance.fv 

當上述通話foo_instance = Foo(1),它會導致名稱foo_instance指一個新的對象。當您撥打foo_instance.try_change_foo_value()時,該方法範圍內的同一對象會被另外命名爲self。當您更改該名稱以引用新對象時,它對名稱爲foo_instance的原始對象沒有影響,該對象仍然指向該原始對象。

這是一件非常非常好的事情!否則,無論何時您調用一個函數並將其傳遞給一些對象,該函數對其自己的局部變量所做的所有賦值都會改變您的變量所指的內容!