从父类(作为子类)调用 "private" 变量是否违反封装



我试图更多地了解python变量的作用域。

截至目前,我不想破坏或违反声明为私有的变量(即"self._variable")的封装。

我想知道如果子类直接从其父类调用变量,是否会破坏封装。例如:

class Parent:
def __init__():
self._randomVariable = ''
class Child(Parent):
def__init__():
super().__init__()
def doSomething():
self._randomVariable = 'Test'

Chid.doSomething() 是否在技术上破坏了在其方法中直接调用self._randomVariable的封装,即使它是一个子类?

我找不到任何特定于Python的封装,而是基于Java的东西。Java和Python之间的想法是一样的吗?

Encapsulation 在 Python 中并不像在大多数其他语言(Java、C++ 等)中那样重要,你真的不应该太担心它。在 Python 社区中,我们有这样一个原则,即"我们在这里都是同意的成年人"。

这意味着,如果你去弄乱别人的代码,这是你的责任,但如果其他人真的知道他们在做什么,也不要阻止他们弄乱你的代码。出于这个原因,Python中没有真正的privateprotected,你不应该像在Java中那样担心它们。

但正如现在所清楚的那样,仍然存在某种带有下划线的隐私。那么,它们通常用于什么?

单个下划线前缀变量(例如self._var)

它们用于私有变量和受保护变量。在变量前面加上下划线(主要是)是一种约定,它只是告诉读者"这个变量由这个类在内部使用,不应该从外部访问"。好吧,如果你的子类需要它,他们仍然可以使用它。如果你在课外需要它,你仍然可以使用它。但这是你的责任,确保你不会破坏任何东西。

还有其他一些小影响,例如from module import *不导入下划线前缀变量,但隐私约定是要点。

双下划线前缀变量(例如self.__var)

也称为"dunder"(双下)变量,这些变量用于名称重整。这个想法是,如果你有一个通用的变量名,并且你担心子类可能会使用相同的变量名作为它们的内部内容,你可以使用双下划线前缀来保护你的变量不会被意外覆盖。这样你的self.__var就变得self._BaseClassName__var,你的子类的self.__var变得self._SubClassName__var。现在它们不会重叠。

虽然该效果可用于模拟其他语言的private,但我建议您不要这样做。再说一次,我们在这里都是同意的成年人,只需用一个下划线标记您的变量"private",除非我真的知道我在做什么,否则我不会碰它。

首先,让我稍微更改一下您的程序以解决一些问题:

class Parent:
def __init__(self):
self.__randomVariable = 'Test1'
class Child(Parent):
def __init__(self):
super().__init__()
def doSomething(self):
self.__randomVariable = 'Test2'

我向方法添加了self作为第一个参数,因为定义方法时需要这样做。

我只是为了示例而将父类中的赋值更改为'Test1'而不是''

我还使用了带有双下划线的__randomVariable,因为您的问题是关于私有变量的,而私有变量需要两个下划线。在Python中,私有变量并不是真正的私有变量,但是使用"名称重整"机制将它们转换为名为_Parent.__randomVariable_Child.__randomVariable的变量,具体取决于声明它们的类。

关于范围。如果要引用self.__randomVariable则仅引用该类中定义的名为__randomVariable的私有变量。事实上,在您的情况下,会有两个不同的此类私有变量。在超类中会有一个定义,由于"名称重整"的机制,它实际上被存储为_Parent.__randomVariable。子类中定义了那个,它存储为_Child.__randomVariable.因此,在您的示例中,您的子类实际上没有从其父类访问变量;相反,它为子类的实例定义一个新的私有变量。

下面是一些示例代码,用于说明使用上述定义时会发生什么情况:

c = Child()
print(c._Parent__randomVariable)
# prints Test1, i.e. the value assigned in the parent class
c.doSomething()
# calling doSomething will execute the assignment in the subclass
print(c._Parent__randomVariable)
# still prints Test1, i.e. the private variable in the parent class
# did not get reassigned in the method doSomething
print(c._Child__randomVariable)
# This one prints Test2, so in fact what happened is that a new
# private attribute was created in the subclass.

因此,在这一段代码中,没有"违反封装",因为它在父类和子类中创建不同的私有实例变量。话虽如此,私有实例变量并没有真正"封装",因为当您知道名称重整机制时,您始终可以访问它们(即使您不应该这样做)。这是我在上面的代码示例中所做的:我通过分别编写c._Parent__randomVariablec._Child__randomVariable来访问父类和子类的私有变量__randomVariable。它只是Python用来模拟私有变量的实现技巧。

最新更新