«Частный» (реализация) класс в Python

107

Я кодирую небольшой модуль Python, состоящий из двух частей:

  • некоторые функции, определяющие публичный интерфейс,
  • класс реализации, используемый вышеуказанными функциями, но не имеющий смысла вне модуля.

Сначала я решил «спрятать» этот класс реализации, определив его внутри функции, использующей его, но это затрудняет читаемость и не может использоваться, если несколько функций повторно используют один и тот же класс.

Итак, помимо комментариев и строк документации, существует ли механизм, позволяющий пометить класс как «частный» или «внутренний»? Я знаю о механизме подчеркивания, но насколько я понимаю, он применяется только к именам переменных, функций и методов.

Oparisy
источник

Ответы:

175

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

class _Internal:
    ...

Это официальное соглашение Python для «внутренних» символов; "from module import *" не импортирует объекты с префиксом подчеркивания.

Изменить: ссылка на соглашение об одном подчеркивании

Фердинанд Бейер
источник
3
Я не знал, что правило подчеркивания распространяется на классы. Я не хочу загромождать свое пространство имен при импорте, поэтому я искал такое поведение. Спасибо!
oparisy
1
Поскольку вы утверждаете, что это "официальное" соглашение Python, было бы неплохо дать ссылку. В другом сообщении здесь говорится, что все является официальным, и есть ссылка на документацию.
flodin
11
python.org/dev/peps/pep-0008 - _single_leading_underscore: слабый индикатор «внутреннего использования». Например, «from M import *» не импортирует объекты, имена которых начинаются с символа подчеркивания. - Классы для внутреннего использования выделены подчеркиванием
Майлз
2
Ведущее подчеркивание - это соглашение о маркировке вещей как внутренних, «не связывайтесь с этим», в то время как все больше предназначено для модулей, предназначенных для использования с «from M import *», не обязательно подразумевая, что пользователи модуля не должны касаться этот класс.
Майлз,
65

Коротко:

  1. Вы не можете обеспечить конфиденциальность . В Python нет частных классов / методов / функций. По крайней мере, не строгая конфиденциальность, как в других языках, например Java.

  2. Вы можете только указать / предложить конфиденциальность . Это следует соглашению. Соглашение python для обозначения класса / функции / метода как закрытого заключается в том, чтобы предварять его знаком _ (подчеркивание). Например, def _myfunc()или class _MyClass:. Вы также можете создать псевдо-конфиденциальность, поставив перед методом два символа подчеркивания (например:) __foo. Вы не можете получить доступ к методу напрямую, но вы все равно можете вызвать его через специальный префикс, используя имя класса (например:) _classname__foo. Поэтому лучшее, что вы можете сделать, - это указать / предложить конфиденциальность, а не обеспечивать ее соблюдение.

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

Чтобы получить больше информации:

Карл Фаст
источник
В ответ на №1 вы вроде как можете обеспечить конфиденциальность методов. Использование двойного подчеркивания, такого как __method (self), сделает его недоступным за пределами класса. Но есть способ обойти это, назвав это как Foo () ._ Foo__method (). Думаю, он просто переименовал его в нечто более эзотерическое.
Эван Фосмарк,
1
Эта цитата верна.
Пол Дрейпер,
37

Определите __all__список имен, которые вы хотите экспортировать ( см. Документацию ).

__all__ = ['public_class'] # don't add here the 'implementation_class'
ДядяЗеив
источник
10

Иногда я использую такой шаблон:

Определите класс:

class x(object):
    def doThis(self):
        ...
    def doThat(self):
        ...

Создайте экземпляр класса, перезаписав имя класса:

x = x()

Определите символы, раскрывающие функциональность:

doThis = x.doThis
doThat = x.doThat

Удалите сам экземпляр:

del x

Теперь у вас есть модуль, который предоставляет только ваши общедоступные функции.

Theller
источник
2
Потребовалась минута, чтобы понять цель / функцию «перезаписи имени класса», но я широко улыбнулся, когда это сделал. Не уверен, когда буду использовать. :)
Зак Янг
Есть ли название у этой техники?
Бишвас Мишра,
8

По соглашению перед внутренними классами, функциями и переменными добавляется "_".

Бенджамин Петерсон
источник
4

Чтобы обратиться к проблеме соглашений о дизайне, и, как сказал Кристофер, на самом деле в Python не существует такого понятия, как «частный». Это может показаться странным для кого-то, кто имеет опыт работы с C / C ++ (например, я некоторое время назад), но в конечном итоге вы, вероятно, поймете, что следующих соглашений достаточно.

Видеть что-то с подчеркиванием впереди должно быть достаточно хорошим намеком, чтобы не использовать это напрямую. Если вас беспокоит загромождение help(MyClass)вывода (это то, на что все смотрят при поиске того, как использовать класс), подчеркнутые атрибуты / классы туда не включаются, так что вы в конечном итоге просто получите описание своего «общедоступного» интерфейса.

Кроме того, у всего публичного есть свои потрясающие преимущества, например, вы можете модульно тестировать практически все что угодно извне (чего вы не можете сделать с частными конструкциями C / C ++).

Димитрий Ткачук
источник
4

Используйте два символа подчеркивания для префикса имен «частных» идентификаторов. Для классов в модуле используйте одинарное подчеркивание в начале, и они не будут импортированы с использованием «from module import *».

class _MyInternalClass:
    def __my_private_method:
        pass

(В Python нет такой вещи, как истинный «частный». Например, Python просто автоматически изменяет имена членов класса с двойным подчеркиванием, чтобы быть __clssname_mymember. Так что на самом деле, если вы знаете искаженное имя, вы все равно можете использовать «частный» объект . См. Здесь. И, конечно, вы можете вручную импортировать «внутренние» классы, если хотите).

хромодер
источник
Почему два _? Одного достаточно.
S.Lott
Одного подчеркивания достаточно, чтобы запретить импорту импортировать классы и функции. Вам нужны два символа подчеркивания, чтобы вызвать функцию искажения имени Python. Должно было быть яснее; Я редактировал.
chroder
Теперь о продолжении. Зачем называть искажением? Какая от этого возможная польза?
S.Lott
5
Возможное преимущество раздражает других разработчиков, которым нужен доступ к этим переменным :)
Ричард Левассер,