"Private" (реализация) класса в Python

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

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

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

Итак, в дополнение к комментариям и docstrings, есть ли механизм для обозначения класса как "private" или "internal"? Я знаю механизм подчеркивания, но, как я понимаю, он применяется только к именам переменных, функций и методов.

Ответ 1

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

class _Internal:
    ...

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

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

Ответ 2

Короче:

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

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

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

Для получения дополнительной информации:

Ответ 3

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

__all__ = ['public_class'] # don't add here the 'implementation_class'

Ответ 4

Я иногда использую шаблон:

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

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

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

x = x()

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

doThis = x.doThis
doThat = x.doThat

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

del x

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

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

Используйте два символа подчеркивания для префиксных имен идентификаторов "private". Для классов в модуле используйте одно знаковое подчеркивание, и они не будут импортироваться с помощью "из импорта модуля".

class _MyInternalClass:
    def __my_private_method:
        pass

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