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