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

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

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

84774 次浏览

Python has no privacy model, there are no access modifiers like in C++, C# or 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. 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. 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.

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:

__*
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. 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:

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. 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.

Note 2: Name mangling can make certain uses, such as debugging and __getattr__(), less convenient. However the name mangling algorithm is well documented and easy to perform manually.

Note 3: Not everyone likes name mangling. 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.

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.

AFAIK, in the second case Python perform "name mangling", so the name of the __private method of the parent class is really:

_Parent__private

And you cannot use it in child in this form neither

By declaring your data member private :

__private()

you simply can't access it from outside the class

Python supports a technique called name mangling.

This feature turns class member prefixed with two underscores into:

_className.memberName

if you want to access it from Child() you can use: self._Parent__private()

Also PEP8 says

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 mangles these names with the class name: if class Foo has an attribute named __a, it cannot be accessed by 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.

You should stay away from _such_methods too, by convention. I mean you should treat them as 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.

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.

self.__private()