"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". См. здесь. И, конечно, вы можете вручную импортировать "внутренние" классы, если хотите).