简体   繁体   English

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

[英]Inheritance of private and protected methods in Python

I know, there are no 'real' private/protected methods in Python.我知道,Python 中没有“真正的”私有/受保护方法。 This approach isn't meant to hide anything;这种方法并不是要隐藏任何东西; I just want to understand what Python does.我只是想了解 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'

So, does this behaviour mean, that 'protected' methods will be inherited but 'private' won't at all?那么,这种行为是否意味着“受保护”的方法将被继承而“私有”的方法根本不会被继承?
Or did I miss anything?还是我错过了什么?

Python has no privacy model, there are no access modifiers like in C++, C# or Java. Python 没有隐私模型,没有像 C++、C# 或 Java 那样的访问修饰符。 There are no truly 'protected' or 'private' attributes.没有真正的“受保护”或“私有”属性。

Names with a leading double underscore and no trailing double underscore are mangled to protect them from clashes when inherited.带有前导双下划线且没有尾随双下划线的名称会被修改以保护它们在继承时不会发生冲突。 Subclasses can define their own __private() method and these will not interfere with the same name on the parent class.子类可以定义自己的__private()方法,这些方法不会干扰父类上的相同名称。 Such names are considered class private ;这样的名字被认为是类私有的 they are still accessible from outside the class but are far less likely to accidentally clash.它们仍然可以从课堂外访问,但意外冲突的可能性要小得多。

Mangling is done by prepending any such name with an extra underscore and the class name (regardless of how the name is used or if it exists), effectively giving them a namespace .通过在任何此类名称前面加上额外的下划线和类名称(无论名称如何使用或是否存在)来完成修改,从而有效地为它们提供了一个namespace In the Parent class, any __private identifier is replaced (at compilation time) by the name _Parent__private , while in the Child class the identifier is replaced by _Child__private , everywhere in the class definition.Parent类中,任何__private标识符都被(在编译时)名称_Parent__private_Parent__private ,而在Child类中,标识符被替换为_Child__private ,在类定义的任何地方。

The following will work:以下将起作用:

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

    def bar(self):
        self._Parent__private()

See Reserved classes of identifiers in the lexical analysis documentation:请参阅词法分析文档中的Reserved classes of identifiers

__*
Class-private names.类私有名称。 Names in this category, when used within the context of a class definition, are re-written to use a mangled form to help avoid name clashes between “private” attributes of base and derived classes.此类别中的名称在类定义的上下文中使用时,会重新编写以使用重整形式,以帮助避免基类和派生类的“私有”属性之间的名称冲突。

and the referenced documentation on names :以及有关名称的参考文档

Private name mangling : When an identifier that textually occurs in a class definition begins with two or more underscore characters and does not end in two or more underscores, it is considered a private name of that class.私有名称修改:当在类定义中以文本形式出现的标识符以两个或多个下划线字符开头且不以两个或多个下划线结尾时,它被视为该类的私有名称。 Private names are transformed to a longer form before code is generated for them.在为它们生成代码之前,私有名称被转换为更长的形式。 The transformation inserts the class name, with leading underscores removed and a single underscore inserted, in front of the name.转换插入类名,在名称前面删除前导下划线并插入一个下划线。 For example, the identifier __spam occurring in a class named Ham will be transformed to _Ham__spam .例如,出现在名为 Ham 的类中的标识符__spam将被转换为_Ham__spam This transformation is independent of the syntactical context in which the identifier is used.此转换与使用标识符的语法上下文无关。

Don't use class-private names unless you specifically want to avoid having to tell developers that want to subclass your class that they can't use certain names or risk breaking your class.不要使用类私有名称,除非您特别想避免告诉想要继承您的类的开发人员他们不能使用某些名称或冒破坏您的类的风险。 Outside of published frameworks and libraries, there is little use for this feature.在已发布的框架和库之外,此功能几乎没有用处。

The PEP 8 Python Style Guide has this to say about private name mangling: PEP 8 Python 风格指南对私有名称修改有这样的说法:

If your class is intended to be subclassed, and you have attributes that you do not want subclasses to use, consider naming them with double leading underscores and no trailing underscores.如果您的类打算成为子类,并且您有不希望子类使用的属性,请考虑使用双前导下划线而不是尾随下划线命名它们。 This invokes Python's name mangling algorithm, where the name of the class is mangled into the attribute name.这会调用 Python 的名称修改算法,其中类的名称被修改为属性名称。 This helps avoid attribute name collisions should subclasses inadvertently contain attributes with the same name.如果子类无意中包含具有相同名称的属性,这有助于避免属性名称冲突。

Note 1: Note that only the simple class name is used in the mangled name, so if a subclass chooses both the same class name and attribute name, you can still get name collisions.注 1:注意,在 mangled 名称中只使用了简单的类名,因此如果子类选择相同的类名和属性名,仍然会出现名称冲突。

Note 2: Name mangling can make certain uses, such as debugging and __getattr__() , less convenient.注意 2:名称修改可以使某些用途,例如调试和__getattr__()不太方便。 However the name mangling algorithm is well documented and easy to perform manually.然而,名称修改算法有据可查且易于手动执行。

Note 3: Not everyone likes name mangling.注 3:并非每个人都喜欢名称修改。 Try to balance the need to avoid accidental name clashes with potential use by advanced callers.尝试平衡避免意外名称冲突的需要与高级呼叫者的潜在使用。

The double __ attribute is changed to _ClassName__method_name which makes it more private than the semantic privacy implied by _method_name .__属性更改为_ClassName__method_name ,这使其比_method_name隐含的语义隐私更私密。

You can technically still get at it if you'd really like to, but presumably no one is going to do that, so for maintenance of code abstraction reasons, the method might as well be private at that point.如果您真的愿意,从技术上讲,您仍然可以使用它,但大概没有人会这样做,因此出于维护代码抽象的原因,该方法在那时也可能是私有的。

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()

This has the additional upside (or some would say primary upside) of allowing a method to not collide with child class method names.这具有允许方法不与子类方法名称冲突的额外好处(或有些人会说主要好处)。

By declaring your data member private :通过声明您的数据成员 private :

__private()

you simply can't access it from outside the class你根本无法从课堂外访问它

Python supports a technique called name mangling . Python 支持一种名为name mangling的技术。

This feature turns class member prefixed with two underscores into:此功能将带有两个下划线前缀的类成员转换为:

_className.memberName _className.memberName

if you want to access it from Child() you can use: self._Parent__private()如果你想从Child()访问它,你可以使用: self._Parent__private()

Also PEP8 says还有PEP8

Use one leading underscore only for non-public methods and instance variables.仅对非公共方法和实例变量使用一个前导下划线

To avoid name clashes with subclasses, use two leading underscores to invoke Python's name mangling rules.为避免与子类发生名称冲突,请使用两个前导下划线来调用 Python 的名称修改规则。

Python mangles these names with the class name: if class Foo has an attribute named __a , it cannot be accessed by Foo.__a . Python 使用类名修改这些名称:如果class Foo具有名为__a的属性,则Foo.__a无法访问它。 (An insistent user could still gain access by calling Foo._Foo__a .) Generally, double leading underscores should be used only to avoid name conflicts with attributes in classes designed to be subclassed. (坚持的用户仍然可以通过调用Foo._Foo__a获得访问权限。)通常,双前导下划线应该只用于避免与设计为子类化的类中的属性发生名称冲突。

You should stay away from _such_methods too, by convention.按照惯例,您也应该远离_such_methods I mean you should treat them as private我的意思是你应该把它们当作private

Although this is an old question, I encountered it and found a nice workaround.虽然这是一个老问题,但我遇到了它并找到了一个很好的解决方法。

In the case you name mangled on the parent class because you wanted to mimic a protected function, but still wanted to access the function in an easy manner on the child class.如果您在父类上命名 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))        

The idea is manually replacing the parents function name into one fitting to the current namespace.这个想法是手动将父函数名称替换为适合当前命名空间的名称。 After adding this in the init function of the child class, you can call the function in an easy manner.在子类的init函数中加入this后,就可以方便的调用该函数了。

self.__private()

AFAIK, in the second case Python perform "name mangling", so the name of the __private method of the parent class is really: AFAIK,在第二种情况下,Python 执行“名称修改”,因此父类的 __private 方法的名称实际上是:

_Parent__private

And you cannot use it in child in this form neither你也不能以这种形式在孩子中使用它

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM