Python中私有和受保护方法的继承

时间:2013-11-28 08:54:37

标签: python inheritance methods private protected

我知道,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'

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

6 个答案:

答案 0 :(得分:79)

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

具有前导双下划线且没有尾随双下划线的名称​​ mangled 以保护它们在继承时不会发生冲突。子类可以定义自己的__private()方法,这些方法不会干扰父类的相同名称。这些名称被视为 class private ;他们仍然可以从课外进入,但不太可能发生意外冲突。

通过在任何此类名称前加上额外的下划线和类名(无论名称如何使用或是否存在)来完成管理,有效地为它们提供了名称空间。在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:请注意,在错位中只使用简单的类名   name,所以如果子类选择相同的类名和属性   名字,你仍然可以得到名字冲突。

     

注2:名称修改可以进行某些用途,例如调试和   __getattr__(),不太方便。但是名称为mangling算法   有详细记录,易于手动执行。

     

注3:不是每个人都喜欢名字错误。尝试平衡需要   避免意外姓名冲突,可能会被高级来电者使用。

答案 1 :(得分:10)

__属性更改为_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()

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

答案 2 :(得分:6)

另外PEP8

  

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

     

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

     

Python使用类名来破坏这些名称:if class Foo有一个   名为__a的属性,Foo.__a无法访问该属性。 (坚持不懈   用户仍然可以通过调用Foo._Foo__a来获取访问权限。)通常情况下,   双引导下划线应仅用于避免名称冲突   使用设计为子类的类中的属性。

按照惯例,你也应该远离_such_methods。我的意思是你应该将它们视为private

答案 3 :(得分:3)

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

__private()

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

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

此功能将以两个下划线为前缀的类成员转换为:

  

_className.memberName

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

答案 4 :(得分:2)

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

如果您在父类上命名为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()

答案 5 :(得分:1)

AFAIK,在第二种情况下,Python执行“name mangling”,所以父类的__private方法的名称实际上是:

_Parent__private

你不能以这种形式在孩子身上使用它