类与私有变量
class Test1: def __init__( self ): self.__test = 1 def getvalue( self ): return self.__test class Test2( Test1 ): def __init__( self ): Test1.__init__( self ) self.__test = 2 test = Test2()
为什么打印 test.getvalue() 会返回 1 呢?
3 个回答
在其他语言中,比如Java,工作原理也是一样的(你可以试试看!)
class Test1 {
private int test = 1;
public int getValue() {
return test;
}
}
class Test2 extends Test1 {
private int test = 2;
}
public class Test { // Run the test
public static void main(String[] args) {
Test2 t = new Test2();
System.out.println(t.getValue());
}
}
(我为什么在一个和Python有关的问题中发Java代码呢?因为有些评论说“这在任何面向对象的语言中都不行”和“这就是为什么你不应该对私有变量使用名字重整”——Java在面向对象方面和Python的做法不同,它不使用名字重整来处理私有变量,但行为是一样的)
在Test1中声明的方法可以访问Test1的私有变量。无论这个方法是从子类调用还是怎么调用,都不会改变这一点,除非子类重写了这个方法。私有成员并不会在子类中“消失”或“被覆盖”。它们依然存在,可以被父类的方法访问。
只有当Test2自己实现了getvalue()方法时,Test1的私有成员才会变得不可访问,而Test2的私有成员才会变得可访问。
换句话说,可以说私有成员不是“虚拟的”(或者“可重写的”)。它们是类及其方法的实现细节,不应该被重写。Test1.__test
和Test2.__test
是不同的实例变量(在Python中通过名字重整来实现)。
如果你想要传统的面向对象行为,也就是子类的成员可以覆盖父类的成员,那么在Python、Java中使用方法,或者在C++、C#、Delphi中使用虚方法;而不是私有属性。如果你想实现一些东西而不发生名字冲突,那么就使用私有属性。
这种行为是因为属性名以 __
开头的变量会被处理成不同的名字。简单来说,__test
在 Test1
类里面会变成 _Test1__test
,而在 Test2
类里面会变成 _Test2__test
,所以它们实际上是两个不同的属性。
在Python中,类 Foo
的私有成员 __bar
会被自动改名为 _Foo__bar
。所以在 Test1
中的 __test
会变成 _Test1__test
,而在 Test2
中的 __test
则变成 _Test2__test
。这两个成员其实是不同的。这是有意为之,目的是为了“避免与子类中定义的名字发生冲突”。
如果你希望子类能够看到这个变量,但又不想让它成为公共接口的一部分,可以使用一个下划线 _test
。