"Приватний" (впроваджувальний) клас у Python


106

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

  • деякі функції, що визначають загальнодоступний інтерфейс,
  • клас реалізації, який використовується вищевказаними функціями, але який не має сенсу поза модулем.

Спочатку я вирішив "приховати" цей клас реалізації, визначивши його всередині функції, що використовує його, але це перешкоджає читанню і не може бути використане, якщо кілька функцій повторно використовують один і той же клас.

Отже, окрім коментарів та доктринжів, чи існує механізм для позначення класу як "приватного" чи "внутрішнього"? Мені відомо про механізм підкреслення, але, як я розумію, це стосується лише змінних, функцій та назв методів.

Відповіді:


174

Використовуйте один префікс підкреслення:

class _Internal:
    ...

Це офіційна конвенція Python щодо "внутрішніх" символів; "з імпорту модуля *" не імпортує підкреслені об'єкти з підкресленням.

Редагувати: Посилання на єдину умову підкреслення


3
Я не знав правила підкреслення, яке поширюється на класи. Я не хочу захаращувати простір своїх імен під час імпорту, тому така поведінка - те, що я шукав. Дякую!
oparisy

1
Оскільки ви заявляєте, що це "офіційна" конвенція пітонів, було б непогано за посиланням. Ще одна публікація тут говорить про те, що все є офіційним способом і має посилання на документацію.
flodin

11
python.org/dev/peps/pep-0008 - _single_leading_underscore: слабкий показник "внутрішнього використання". Наприклад, "з M import *" не імпортує об'єкти, ім'я яких починається з підкреслення. - Класи для внутрішнього використання мають провідне підкреслення
Майлз

2
Провідним підкресленням є умова позначення речей як внутрішніх, "не псуйтеся з цим", тоді як усе більше стосується модулів, призначених для використання з "з імпорту M" *, не обов'язково означаючи, що користувачі модулів не повинні торкатися що клас.
Майлз

65

Коротко:

  1. Ви не можете застосовувати конфіденційність . У Python немає приватних класів / методів / функцій. Принаймні, не сувора конфіденційність, як в інших мовах, таких як Java.

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

У цьому відношенні Python як Perl. Якщо перефразовувати відомий рядок про конфіденційність книги Perl, філософія полягає в тому, що вам слід залишатися поза вітальнею, оскільки вас не запросили, не тому, що її захищають рушницею.

Для отримання додаткової інформації:


У відповідь на номер 1 ви начебто можете забезпечити конфіденційність методів. Використання подвійного підкреслення, такого як __method (self), зробить його недоступним поза класом. Але є спосіб її обходу, називаючи його як Foo () ._ Foo__method (). Я думаю, це просто перейменовує його на щось більш езотеричне.
Еван Фосмарк

1
Ця цитата точна.
Пол Дрейпер


10

Модель, яку я іноді використовую, така:

Визначте клас:

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

Створіть екземпляр класу, замінивши ім'я класу:

x = x()

Визначте символи, які розкривають функціонал:

doThis = x.doThis
doThat = x.doThat

Видаліть сам екземпляр:

del x

Тепер у вас є модуль, який розкриває лише ваші загальнодоступні функції.


2
Минув хвилину, щоб зрозуміти мету / функцію "перезапису назви класу", але я отримав велику посмішку, коли це зробив. Не впевнений, коли я буду його використовувати. :)
Zach Young

Чи є назва цієї техніки?
Bishwas Mishra


4

Щоб вирішити питання дизайнерських конвенцій, і, як сказав Крістофер, насправді у Python не існує такого поняття, як "приватне". Це може здатися викривленим для когось із походження C / C ++ (як, наприклад, я назад), але, зрештою, ви, мабуть, зрозумієте, що наступних умов буде достатньо.

Бачити щось, що має підкреслення попереду, повинно бути досить хорошим підказом, щоб не використовувати його безпосередньо. Якщо ви переймаєтеся тим, що перебуває у зв'язку із завантаженням help(MyClass)результатів (на що звертаються всі, шукаючи, як використовувати клас), атрибути / класи, що підкреслюються, туди не входять, тож у вас описується лише "загальнодоступний" інтерфейс.

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


4

Використовуйте дві підкреслення для префіксації імен "приватних" ідентифікаторів. Для класів в модулі використовуйте єдине провідне підкреслення, і вони не будуть імпортовані за допомогою "з імпорту модуля *".

class _MyInternalClass:
    def __my_private_method:
        pass

(У Python не існує такого поняття, як справжній "приватний". Наприклад, Python автоматично автоматично маніпулює імена членів класу з подвійними підкресленнями __clssname_mymember. Так що справді, якщо ви знаєте зловмисне ім'я, то в будь-якому випадку можете використовувати "приватне" об'єднання . Дивіться тут. І, звичайно , ви можете вручну імпортувати «внутрішні» класи , якщо ви хочете).


Чому два _? Одного достатньо.
С.Лотт

Одного підкреслення достатньо для запобігання "імпорту" імпорту класів і функцій. Вам потрібні дві підкреслення, щоб викликати функцію управління ім'ям Python. Повинні були бути зрозумілішими; Я редагував.
chroder

Тепер, наступні заходи. Навіщо називати mangling? Яка можлива користь від цього?
S.Lott

5
Можлива користь дратує інших розробників, яким потрібен доступ до цих змінних :)
Richard Levasseur
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.