在python中,self并不代表实例?

时间:2012-10-10 01:58:30

标签: python

我只是做一个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

我们可以看到自我值已经改变,但实例值没有改变。 为什么?以及如何解决这个问题?

5 个答案:

答案 0 :(得分: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指出的那样,仅仅说foo_instance.fv = 2就更像Pythonic了。

如果你想真正改变foo_instance的价值(而不仅仅是fv),你应该在其他地方改变,而不是在方法中(这在概念上没什么意义)。

答案 1 :(得分:1)

self只是一个变量。对于常规方法(例如,未使用@staticmethod或@classmethod ...装饰),传入的对象是实例。但是,在函数中,您可以将self重新绑定到您想要的任何内容,但更改不会保留在函数之外,因为赋值只是在右侧对对象进行新的引用 - 除非你返回那个引用或者将它存储在一个范围更广的对象上,当函数结束时再也听不到它会超出范围(因为你不再有引用/句柄)。

考虑常规功能:

def foo(x):
    x = 2

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

这真的没什么不同。

答案 2 :(得分:0)

您正在更改方法try_change_foo_value中的容器“self”。请记住,self作为参数传入,这意味着您可以更改变量self内部的引用;但是您无法更改调用者传递给您的引用。在Java中思考。

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

如果我打电话

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

我可以期待bar仍然是“新的Foo(1)。”

答案 3 :(得分:0)

try_change_foo_value中,self以实例开头,但您在分配时更改它。您可以打印id的{​​{1}}来查看此内容。

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 s。

id

答案 4 :(得分: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上仍然引用该原始对象的原始对象没有影响。

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