Наследование частных и защищенных методов в 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 ответов


Python не имеет модели конфиденциальности, нет модификаторов доступа, таких как в C++, C# или Java. Нет по-настоящему "защищенных" или "частных" атрибутов.

имена с ведущим двойным подчеркиванием и без последнего двойного подчеркивания являются исковеркали чтобы защитить их от столкновений при наследовании. Подклассы могут определять свои __private() метод, и они не будут мешать с тем же именем в родительском классе. Такие имена считаются мастер-класс; они по-прежнему доступны из-за пределов класса, но гораздо менее вероятно случайное столкновение.

Mangling выполняется путем добавления любого такого имени с дополнительным подчеркиванием и именем класса (независимо от того, как используется имя или если оно существует), эффективно давая им пространство имен. В Parent класс, любой __private идентификатор заменяется (во время компиляции) именем _Parent__private, в то время как в Child class идентификатор заменяется на _Child__private везде в определение класса.

следующие работы:

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

    def bar(self):
        self._Parent__private()

посмотреть зарезервированные классы идентификаторов в документации по лексическому анализу:

__*
Класс-частные имена. Имена в этой категории при использовании в контексте определения класса переписываются для использования искаженной формы, чтобы избежать столкновения имен между "частными" атрибутами базы и производными занятия.

и ссылки документация по именам:

частное имя коверкая: когда идентификатор, который текстуально встречается в определении класса, начинается с двух или более символов подчеркивания и не заканчивается двумя или более символами подчеркивания, он считается частным именем этого класса. Частные имена преобразуются в более длинную форму до создания для них кода. Преобразование вставляет имя класса, при удалении ведущих подчеркиваний и вставке одного подчеркивания перед именем. Например, идентификатор __spam происходящее в классе с именем Ham будет преобразовано в _Ham__spam. Это преобразование не зависит от синтаксического контекста, в котором используется идентификатор.

не используйте class-private имена, если вы конкретно хотите избежать необходимости сообщать разработчикам, которые хотят подкласс вашего класса, что они не могут использовать определенные имена или рискуешь сломать свой класс. За пределами опубликованных фреймворков и библиотек эта функция практически не используется.

на PEP 8 Python руководство по стилю имеет это сказать о частном имени mangling:

если ваш класс предназначен для подкласса, и у вас есть атрибуты что вы не хотите использовать подклассы, рассмотрите возможность именования их с помощью двойные ведущие подчеркивания и отсутствие завершающих подчеркиваний. Это вызывает Алгоритм искажения имени Python, где имя класса искорежено в имя атрибута. Это помогает избежать имени атрибута коллизии должны непреднамеренно содержать атрибуты подклассов с такое же имя.

Примечание 1: Обратите внимание, что в mangled используется только простое имя класса name, поэтому, если подкласс выбирает одно и то же имя класса и атрибут имя, вы все еще можете получить столкновения имен.

примечание 2: имя искажая может сделать некоторые пользы, как отладка и __getattr__() меньше удобный. Однако имя mangling алгоритм хорошо документирован и прост в исполнении вручную.

Примечание 3: Не все любят имя калечить. Попытайтесь сбалансировать потребность избегайте случайных столкновений имен с потенциальным использованием продвинутыми абонентами.


и PEP8 говорит

использовать один из ведущих подчеркивания только негосударственные методы и экземпляра переменная.

чтобы избежать столкновения имен с подклассами, используйте два ведущих подчеркивания для вызовите правила искажения имени Python.

Python искажает эти имена с именем класса: if class Foo есть атрибут с именем __a, он не может быть доступен Foo.__a. (Настойчивый пользователь может получить доступ по телефону Foo._Foo__a.) Обычно, двойные ведущие подчеркивания следует использовать только для избежания конфликтов имен с атрибутами в классах, предназначенных для подклассов.

вы должны держаться подальше от _such_methods тоже по соглашению. Я имею в виду вы должны относиться к ним как private


двойной


объявив свой член данных частным:

__private()

вы просто не можете получить к нему доступ из вне класса

Python поддерживает метод под названием имя коверкая.

эта функция превращает член класса с двумя символами подчеркивания в:

_className.memberName

Если вы хотите получить доступ к нему из Child() вы можете использовать: self._Parent__private()


хотя это старый вопрос, я сталкивался с этим и нашел хорошее решение.

в случае, если вы называете 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()

AFAIK, во втором случае Python выполняет "искажение имени", поэтому имя _ _ private метода родительского класса действительно:

_Parent__private

и вы не можете использовать его в child в этой форме ни