【问题标题】:Inheritance of private and protected methods in PythonPython中私有方法和受保护方法的继承
【发布时间】:2019-02-10 06:57:27
【问题描述】:

我知道,Python 中没有“真正的”私有/受保护方法。这种方法并不是要隐藏任何东西。我只是想了解 Python 是做什么的。

class Parent(object):
    def _protected(self):
        pass

    def __private(self):
        pass

class Child(Parent):
    def foo(self):
        self._protected()   # This works

    def bar(self):
        self.__private()    # This doesn't work, I get a AttributeError:
                            # 'Child' object has no attribute '_Child__private'

那么,这种行为是否意味着“受保护”方法将被继承而“私有”方法根本不会被继承?
还是我错过了什么?

【问题讨论】:

  • “这不起作用”是什么意思?
  • 我编辑了原始帖子。
  • 你必须这样称呼它,假设 c 是 Child c._Parent__private() 的一个实例
  • 它没有按应有的方式工作吗? AFAIK 私有方法不会被继承。 stackoverflow.com/questions/8241462/…

标签: python inheritance methods private protected


【解决方案1】:

Python 没有隐私模型,没有像 C++、C# 或 Java 那样的访问修饰符。没有真正的“受保护”或“私有”属性。

带有前导双下划线且没有尾随双下划线的名称被重整,以保护它们在继承时不会发生冲突。子类可以定义自己的__private() 方法,这些不会干扰父类上的同名。此类名称被视为类私有;它们仍然可以从课堂外访问,但发生意外冲突的可能性要小得多。

Mangling 是通过在任何此类名称前面加上一个额外的下划线和类名来完成的(无论名称如何使用或是否存在),从而有效地为它们提供一个 命名空间。在Parent 类中,任何__private 标识符都被名称_Parent__private 替换(在编译时),而在Child 类中,标识符被_Child__private 替换,在类定义中的任何位置。

以下将起作用:

class Child(Parent):
    def foo(self):
        self._protected()

    def bar(self):
        self._Parent__private()

参见词法分析文档中的Reserved classes of identifiers

__*
类私有名称。此类别中的名称在类定义的上下文中使用时,会被重写以使用重整形式,以帮助避免基类和派生类的“私有”属性之间的名称冲突。

以及引用的documentation on names

私有名称修改:当以文本形式出现在类定义中的标识符以两个或多个下划线字符开头并且不以两个或多个下划线结尾时,它被视为该类的私有名称.在为私有名称生成代码之前,私有名称会转换为更长的形式。转换插入类名,删除前导下划线并在名称前面插入一个下划线。例如,出现在名为 Ham 的类中的标识符 __spam 将转换为 _Ham__spam。这种转换与使用标识符的语法上下文无关。

不要使用类私有名称,除非您特别希望避免告诉想要子类化您的类的开发人员他们不能使用某些名称或冒着破坏您的类的风险。在已发布的框架和库之外,此功能几乎没有用处。

PEP 8 Python Style Guide 对私有名称修改有这样的看法:

如果你的类打算被子类化,并且你有属性 您不想让子类使用,请考虑将它们命名为 双前导下划线,没有尾随下划线。这调用 Python 的名称修饰算法,其中类的名称为 融入属性名称。这有助于避免属性名称 碰撞应该子类不经意地包含与 同名。

注意1:注意在mangled中只使用了简单的类名 名称,因此如果子类选择相同的类名和属性 名字,你仍然可以得到名字冲突。

注意 2:名称修饰可以有某些用途,例如调试和 __getattr__(),不太方便。但是名称修饰算法 有据可查且易于手动执行。

注 3:不是每个人都喜欢名字修饰。尝试平衡需要 避免与高级调用者可能使用的意外名称冲突。

【讨论】:

  • 我知道,这与这个名字修饰的事情有关。我知道我可以通过 _Parent__private 从类外部访问该方法,但我只是不明白为什么我不能在继承的类中调用它。我不知道,子类有自己的__private(),非常感谢您的明确回答!
  • __getstate__()__setstate__() 怎么样?孩子不能从其父母那里继承这些方法?
  • @BoltzmannBrain:这些名称的末尾也包含下划线,而不仅仅是名称的开头。这是一个不同类别的名称。请参阅documentation I linked to
  • 谢谢,但那里的文档并没有给我更多有用的信息。我已将此移至另一个问题:stackoverflow.com/questions/44444733/…
  • @Martijn:谢谢你的回答。也许我没有使用pythonic style 尝试将类中的所有变量设为私有,然后对这些私有变量使用getterssetters。好吧,我应该谷歌这个关于私有变量使用的问题。
【解决方案2】:

双重__ 属性更改为_ClassName__method_name,使其比_method_name 隐含的语义隐私更加私密。

从技术上讲,如果您真的愿意,您仍然可以使用它,但大概没有人会这样做,因此出于维护代码抽象的原因,该方法在那时还不如是私有的。

class Parent(object):
    def _protected(self):
        pass

    def __private(self):
        print("Is it really private?")

class Child(Parent):
    def foo(self):
        self._protected()

    def bar(self):
        self.__private()

c = Child()
c._Parent__private()

这有额外的好处(或者有些人会说主要的好处)允许方法不与子类方法名称发生冲突。

【讨论】:

  • 这在 Child 内部使用 Parent 私有方法调用的情况下不起作用
【解决方案3】:

通过将您的数据成员声明为私有:

__private()

你根本无法从课堂外访问它

Python 支持一种称为name mangling 的技术。

此功能将带有两个下划线前缀的类成员变成:

_className.memberName

如果你想从Child() 访问它,你可以使用:self._Parent__private()

【讨论】:

    【解决方案4】:

    还有PEP8

    仅对非公开方法和实例使用一个前导下划线 变量。

    为避免与子类发生名称冲突,请使用两个前导下划线 调用 Python 的名称修改规则。

    Python 将这些名称与类名混淆:如果 class Foo 有 名为__a 的属性,Foo.__a 无法访问它。 (一个坚持 用户仍然可以通过调用Foo._Foo__a 获得访问权限。)通常, 双前导下划线应仅用于避免名称冲突 具有设计为子类的类中的属性。

    按照惯例,您也应该远离_such_methods。我的意思是你应该把他们当作private

    【讨论】:

      【解决方案5】:

      虽然这是一个老问题,但我遇到了它并找到了一个很好的解决方法。

      如果您在父类上命名为 mangled,因为您想模仿受保护的函数,但仍想在子类上以简单的方式访问该函数。

      parent_class_private_func_list = [func for func in dir(Child) if func.startswith ('_Parent__')]
      
      for parent_private_func in parent_class_private_func_list:
              setattr(self, parent_private_func.replace("_Parent__", "_Child"), getattr(self, parent_private_func))        
      

      这个想法是手动将父函数名称替换为适合当前命名空间的名称。 在子类的init函数中加入这个后,就可以方便的调用该函数了。

      self.__private()
      

      【讨论】:

        【解决方案6】:

        AFAIK,在第二种情况下,Python 执行“名称修改”,因此父类的 __private 方法的名称实际上是:

        _Parent__private
        

        你也不能以这种形式在孩子中使用它

        【讨论】:

          猜你喜欢
          • 2013-12-14
          • 2011-07-09
          • 2020-09-09
          • 2012-01-09
          • 2015-03-26
          • 2016-03-17
          相关资源
          最近更新 更多