2012-10-10 29 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的原始对象没有影响,该对象仍然指向该原始对象。

这是一件非常非常好的事情!否则,无论何时您调用一个函数并将其传递给一些对象,该函数对其自己的局部变量所做的所有赋值都会改变您的变量所指的内容!