类与私有变量

1 投票
3 回答
1131 浏览
提问于 2025-04-16 18:40
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 个回答

1

在其他语言中,比如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.__testTest2.__test是不同的实例变量(在Python中通过名字重整来实现)。

如果你想要传统的面向对象行为,也就是子类的成员可以覆盖父类的成员,那么在Python、Java中使用方法,或者在C++、C#、Delphi中使用虚方法而不是私有属性。如果你想实现一些东西而不发生名字冲突,那么就使用私有属性。

6

这种行为是因为属性名以 __ 开头的变量会被处理成不同的名字。简单来说,__testTest1 类里面会变成 _Test1__test,而在 Test2 类里面会变成 _Test2__test,所以它们实际上是两个不同的属性。

9

在Python中,类 Foo 的私有成员 __bar 会被自动改名为 _Foo__bar。所以在 Test1 中的 __test 会变成 _Test1__test,而在 Test2 中的 __test 则变成 _Test2__test。这两个成员其实是不同的。这是有意为之,目的是为了“避免与子类中定义的名字发生冲突”

如果你希望子类能够看到这个变量,但又不想让它成为公共接口的一部分,可以使用一个下划线 _test

撰写回答