【问题标题】:Is calling a "private" variable from a parent class (as a child class) violating Encapsulation从父类(作为子类)调用违反封装的“私有”变量
【发布时间】:2019-05-29 05:49:29
【问题描述】:

我正在尝试更多地了解 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的思路是一样的吗?

【问题讨论】:

  • _randomValueprotected 属性。所以不行。如果是__randomValue,那就是。
  • 您能否详细说明受保护属性的含义以及两个“_”与一个的区别?
  • 另外不要忘记 - 所有这些 _ 和 __ 只是约定 - 即使没有它们,代码也可以工作 - 它们只是向代码阅读器显示您的意图
  • @Drako 在_ 的情况下,是的,这是一个约定。但不适用于__,因为 python 会对这些属性进行名称修改并尝试隐藏它们。
  • @Drako 对,我明白了。我认为在任何时候继续练习封装都是一个好习惯,因为据我了解,其他语言会抛出错误。

标签: python python-3.x


【解决方案1】:

在 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,但我建议您不要这样做。再说一次,我们在这里都是同意的成年人,只需用一个下划线标记你的变量“私有”,除非我真的知道我在做什么,否则我不会碰它。

【讨论】:

    【解决方案2】:

    首先,让我稍微修改一下你的程序来解决一些问题:

    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 用来模拟私有变量的一种实现技巧。

    【讨论】:

      猜你喜欢
      • 2013-09-06
      • 1970-01-01
      • 1970-01-01
      • 2010-10-23
      • 2012-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-26
      相关资源
      最近更新 更多