Подтвердить что ты не робот

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

Итак, означает ли это поведение, что "защищенные" методы будут унаследованы, но "частный" вообще не будет?
Или я ничего не пропустил?

4b9b3361

Ответ 1

У Python нет модели конфиденциальности, нет модификаторов доступа, таких как 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()

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

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

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

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

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

В руководстве по стилю Python PEP 8 сказано об управлении частным именем:

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

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

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

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

Ответ 2

Атрибут double __ изменен на _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()

У этого есть дополнительный потенциал роста (или кто-то сказал бы, что основной потенциал роста) позволяет методу не сталкиваться с именами метода дочернего класса.

Ответ 3

Также PEP8 говорит

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

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

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

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

Ответ 4

Объявление вашего личного члена данных:

__private()

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

Python поддерживает метод name mangling.

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

_className.memberName

если вы хотите получить к нему доступ из Child(), вы можете использовать: self._Parent__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

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