Чи є в Python "приватні" змінні в класах?


578

Я приїжджаю зі світу Java та читаю « Шаблони, рецепти та ідіоми» Брюса Екклса « Пітон 3» .

Читаючи про класи, далі йдеться про те, що в Python не потрібно оголошувати змінні екземпляра. Ви просто використовуєте їх у конструкторі, і бум, вони там є.

Так, наприклад:

class Simple:
    def __init__(self, s):
        print("inside the simple constructor")
        self.s = s

    def show(self):
        print(self.s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

Якщо це правда, то будь-який об’єкт класу Simpleможе просто змінити значення змінної sза межами класу.

Наприклад:

if __name__ == "__main__":
    x = Simple("constructor argument")
    x.s = "test15" # this changes the value
    x.show()
    x.showMsg("A message")

На Java нас вчили про публічні / приватні / захищені змінні. Ці ключові слова мають сенс, оскільки час від часу вам потрібні змінні в класі, до якого ніхто з класу не має доступу.

Чому цього не потрібно в Python?


17
Ви мали на увазі змінні екземпляра , а не змінні класу , правда?
PaulMcG

13
Ви повинні перевірити властивості: docs.python.org/library/functions.html#property . Просто використовуйте getter, і ваша змінна буде захищена.
rubik

Коротка і чітка відповідь тут . Сподіваюся, це допоможе.
Premkumar chalmeti

Відповіді:


962

Це культурно. У Python ви не записуєте до змінних екземплярів або класів інших класів. У Java нічого не заважає зробити те саме, якщо ви дійсно хочете - адже ви завжди можете редагувати джерело самого класу, щоб досягти того ж ефекту. Python упускає цей вигляд безпеки та спонукає програмістів до відповідальності. На практиці це працює дуже добре.

Якщо ви хочете чомусь імітувати приватні змінні, ви завжди можете використовувати __префікс з PEP 8 . Python маніпулює іменами змінних на зразок, __fooщоб їх не було легко видно коду поза класом, який їх містить (хоча ви можете обійти його, якщо ви досить визначені, як і ви можете обійти захист Java, якщо працюєте на ньому ).

За цією ж умовою, _префікс означає триматися подалі, навіть якщо технічно вам це не заважають . Ви не граєте зі змінними іншого класу, схожими на __fooабо _bar.


14
Що має сенс. Однак я не думаю, що в Java є якийсь спосіб отримати доступ до приватних змінних за межами класу (за винятком фактичної зміни джерела класу курсу). Є там?
всюдисущий

168
Я, як правило, віддаю перевагу пітонному способу, але я не вважаю, що шлях на Java такий безглуздий, як і ти. Оголошення чогось приватного швидко повідомляє комусь, що читає код, щось дуже корисне: це поле змінюється лише всередині цього класу.
Неділя

67
@Omnipresent, ви можете використовувати рефлексію.
rapadura

76
Дозвольте мені зрозуміти це, тож Python не реалізує публічні чи приватні атрибути, оскільки "це притворність безпеки та спонукає програмістів до відповідальності", проте громада заохочує використання "_" для позначення приватних змінних та методів? Можливо, python визначально повинен мати державний та приватний ні? Їх головне призначення - сказати вам, який API ви повинні використовувати для взаємодії з класом. Вони служать документацією, яка дозволяє вам використовувати ці методи, а не використовувати їх. Вони не є "приводом безпеки", вони є документацією API, яку IDE може використовувати навіть для керівництва!
PedroD

19
Це хороша відповідь, і ваші міркування, безумовно, справедливі, але я не погоджуюся з одним аспектом. Метою модифікаторів доступу ніколи не була безпека . Швидше, вони є засобом явної демаркації (і значною мірою нав'язування) тих частин класу, які вважаються внутрішніми, а які піддаються зовнішнім користувачам цього класу. Конвенції (культура), безумовно, є достовірною альтернативою модифікаторам доступу, і обидва способи мають свої плюси і мінуси, але це вводить в оману те, що модифікатори доступу на рівні мови будь-яким чином мають бути "захищеними" у звичайному розумінні слово.
devios1

159

Приватні змінні в python - це більш-менш хак: інтерпретатор навмисно перейменовує змінну.

class A:
    def __init__(self):
        self.__var = 123
    def printVar(self):
        print self.__var

Тепер, якщо ви спробуєте отримати доступ __varпоза визначенням класу, це не вдасться:

 >>>x = A()
 >>>x.__var # this will return error: "A has no attribute __var"

 >>>x.printVar() # this gives back 123

Але ви можете легко піти з цього:

 >>>x.__dict__ # this will show everything that is contained in object x
               # which in this case is something like {'_A__var' : 123}

 >>>x._A__var = 456 # you now know the masked name of private variables
 >>>x.printVar() # this gives back 456

Ви, мабуть, знаєте, що методи в OOP викликаються так:, x.printVar() => A.printVar(x)якщо A.printVar()можна отримати доступ до якогось поля в x, до цього поля можна отримати доступ і зовні A.printVar() ... врешті-решт, функції створюються для повторного використання, особливих повноважень не надається заявам всередині.

Гра відрізняється, коли бере участь компілятор ( конфіденційність - це концепція рівня компілятора ). Він знає про визначення класу з модифікаторами контролю доступу, тому може помилитися, якщо правила не дотримуються під час компіляції


3
коротше, це не інкапсуляція
watashiSHUN,

2
Мені цікаво, чи є PHP щось подібне з його приватними змінними goofy - оскільки приватні змінні насправді не мають сенсу в інтерпретованій мові - я маю на увазі, яку оптимізацію він може робити, знаючи, що змінна x є приватною, якщо вона не складена?
NoBugs

1
Як ми можемо рандомізувати шаблон приватних змінних?
crisron

@crisron те саме питання
IanS

5
@watashiSHUN "Коротше кажучи, це не капсулювання" => так, це так. Інкапсуляція стосується лише використання публічного API, щоб код клієнта був захищений від змін в реалізації. Конвенції про іменування - це цілком коректний спосіб сказати, що таке API і що таке реалізація, і справа в тому, що воно просто працює.
bruno desthuilliers

30

Як правильно зазначено у багатьох коментарях вище, не забуваймо про головну мету модифікаторів доступу: допомогти користувачам коду зрозуміти, що слід змінити, а що не слід. Коли ви бачите приватне поле, з ним не заплутаєтесь. Отже, це здебільшого синтаксичний цукор, який легко досягти в Python за допомогою _ та __.


4
Я думаю, це важливий момент, як і будь-який. Під час налагодження коду (я знаю, я слабкий, щоб ввести помилки), знайте, які класи можуть змінити змінну члена, спрощує процес налагодження. Принаймні, якщо змінна захищена якоюсь сферою. Аналогічне поняття - це функції const у C ++. Я знаю, що змінні учасників там не були змінені, і тому я навіть не розглядаю цей метод як потенційну причину поганого налаштування змінної. Хоча це може зробити подальшу розробку розширень класів / додавання функцій, обмеження видимості коду полегшує налагодження.
MrMas

18

У конвенції підкреслення є різні приватні змінні.

In [5]: class Test(object):
   ...:     def __private_method(self):
   ...:         return "Boo"
   ...:     def public_method(self):
   ...:         return self.__private_method()
   ...:     

In [6]: x = Test()

In [7]: x.public_method()
Out[7]: 'Boo'

In [8]: x.__private_method()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-8-fa17ce05d8bc> in <module>()
----> 1 x.__private_method()

AttributeError: 'Test' object has no attribute '__private_method'

Є деякі тонкі відмінності, але заради програмування ідеологічної чистоти, її достатньо.

Існують приклади декораторів @private, які більш чітко реалізують цю концепцію, але YMMV. Можливо, можна також написати визначення класу, яке використовує мета


14
Я усвідомлюю, що це досить пізно на вечірку, але це посилання з’являється в Google, коли виходить проблема з Google. Це не розповідає всієї історії. __xоскільки змінна всередині класу Aнасправді переписується компілятором _A__x, вона все ще не є повністю приватною і все ще може отримати доступ до неї.
Zorf

1
Звичайно, якщо я бачу змінну на ім'я _A__x, я не збираюся її торкатися. Це може бути заразним. Я втечу від цього біса.
Матін Ульхак

Я впевнений, що це не справжній приват. Але принципових міркувань для жорсткого виконання приватного в C ++ та Java (тощо), оптимізації компілятора, насправді не існує в Python, тому приватне за умовами домовленості досить добре. Звичайна умова Python полягає в тому, що він сподівається, що ви будете вести себе без нагляду. (І це пастка для новачків, але ви знаєте, тільки
Shayne

14

"У java нас вчили про публічні / приватні / захищені змінні"

"Чому це не потрібно в python?"

З тієї ж причини це не потрібно в Java.

Ви можете вільно користуватися - або не використовувати privateі protected.

Як програміст Python і Java, я виявив , що privateі protectedдуже, дуже важливі концепції дизайну. Але як практична справа, у десятках тисяч рядків Java та Python я ніколи фактично не використовував privateабо protected.

Чому ні?

Ось моє запитання "захищений від кого?"

Інші програмісти в моїй команді? У них є джерело. Що означає захищений, коли вони можуть його змінити?

Інші програмісти інших команд? Вони працюють в одній компанії. Вони можуть - за допомогою телефонного дзвінка - отримати джерело.

Клієнти? Це програмування на роботу (як правило). Клієнти (як правило) володіють кодом.

Отже, від кого саме я захищаю?


118
-1: Я згоден з Поркулем. Йдеться не про заборону доступу чи приховування чогось, а про неявну документацію API. Розробники, а також компілятори / інтерпретатор / перевірка коду легко зрозуміти, які члени рекомендується використовувати, а яких не слід торкатися (або принаймні обережно). У більшості випадків це було б жахливим безладом, якби всі члени класу чи модуля були публічними. Розгляньте відмінність приватних / захищених / публічних членів як службу, сказавши: "Ей, ці члени важливі, тоді як вони використовуються внутрішньо і, мабуть, не корисні для вас".
Обен Сонне

7
@ S.Lott: Я погоджуюся, що документи API мають більш високий пріоритет і часто є єдиним способом спілкування призначеного використання API. Але іноді імена та видимість членів (з точки зору приватного / публічного) достатньо говорять самостійно. Крім того, я бачу вашу думку, що ідея неявної документації не працює добре в редакторах без перевірки API, але вона дійсно корисна для IDE із заповненням коду. Якщо припустити, що ви прочитали документи API вже деякий час тому, це допоможе вам запам'ятати, як використовувати клас. Все не працювало б так розумно, якби не було різниці між приватними та громадськими членами.
Обен Сонне

21
Пізно до дискусії, але все, що Поркул і Обен вимагають тут, вирішується ідеально адекватно конвенцією "приставки з підкресленням" (і без шкоди, яку може заподіяти виконання компілятором цієї конвенції)
ncoghlan

38
@ S.Lott Я не хлопець-пітон, тому з цього погляду я не буду коментувати. Однак, як розробник Java, це справді жахливі поради. -1
дбирне

11
Ого. Ви повністю пропускаєте суть, даєте дуже погану пораду, ображаєте всіх, хто не погоджується з вами з цього приводу, але ви все одно отримуєте значки та понад 1000 балів репутації за цю "відповідь".
Ерік Думініл

12

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

class Foo:

    def __init__(self, bar):
        self._bar = bar

    @property
    def bar(self):
        """Getter for '_bar'."""
        return self._bar

Таким чином, хтось або щось, на що посилається bar, насправді посилається на повернене значення barфункції, а не на саму змінну, і тому до неї можна отримати доступ, але не змінити її. Однак, якщо хтось дійсно цього хотів, він може просто використати _barі призначити йому нове значення. Немає надійного способу заборонити комусь отримати доступ до змінних та методів, які ви хочете приховати, як уже було сказано неодноразово. Однак, використовуючи propertyнайяскравіше повідомлення, яке ви можете надіслати, що змінну не слід редагувати. propertyможе також використовуватися для більш складних шляхів доступу до getter / setter / deleter, як пояснено тут: https://docs.python.org/3/library/functions.html#property


Джанго теж цінує це.
babygame0ver

10

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

Дивіться тут докладніше про це.

Загалом, реалізація орієнтації на об'єкт Python є трохи примітивною порівняно з іншими мовами. Але я насолоджуюся цим, власне. Це дуже концептуально проста реалізація і добре поєднується з динамічним стилем мови.


Так. Краса полягає в тому, що метапрограмування здібностей python означають, що ви можете реально реалізувати фантазійні речі, якщо хочете (І є бібліотеки, які реалізують @ приватні / @ захищені / т. Д. Декоратори та ін. Пекло Я навіть бачив бібліотеку, яка накидала класи прототипу стилів JS без жодної розумної причини), але на практиці це просто не так потрібно .. Я ненавиджу мем "python / js / що є lisp", оскільки його майже ніколи не буває правдою, але python ділиться "відсічками метапрограмування в поєднанні з простим синтаксисом 'з цією мовою
Shayne

8

Єдиний раз, коли я коли-небудь використовую приватні змінні, це коли мені потрібно робити інші речі під час запису чи читання із змінної, і як таке мені потрібно змусити використовувати сеттер та / або геттер.

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

Коли ви працюєте над проектом, де кожен може написати розширення, сповіщення користувачів про застарілі методи, які повинні зникнути в декількох випусках, отже, життєво важливо мінімізувати злам модуля як мінімум при оновленнях.

Отже, моя відповідь; якщо ви та ваші колеги підтримуєте простий набір коду, захист змінних класу не завжди необхідний. Якщо ви пишете розширювану систему, тоді стає вкрай необхідним, коли будуть внесені зміни до ядра, які повинні бути зафіксовані всіма розширеннями за допомогою коду.


8

Вибачте, хлопці, що "воскресили" нитку, але, сподіваюся, це допоможе комусь:

У Python3, якщо ви просто хочете "інкапсулювати" атрибути класу, як у Java, ви можете просто зробити те саме, що і таке:

class Simple:
    def __init__(self, str):
        print("inside the simple constructor")
        self.__s = str

    def show(self):
        print(self.__s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

Щоб створити це, виконайте такі дії:

ss = Simple("lol")
ss.show()

Зауважте, що: print(ss.__s)призведе до помилки.

На практиці Python3 придушить ім'я глобального атрибута. Повернення цього як "приватний" атрибут, як у Java. Ім'я атрибута все ще є глобальним, але недоступним чином, як приватний атрибут іншими мовами.

Але не бійтеся цього. Це не має значення. Це теж робить свою роботу. ;)


2
це існує з моменту Python 1.5.2 IIRC, і він все ще не перешкоджає доступу до атрибуту через його викривлене ім'я.
bruno desthuilliers

7

приватні та захищені поняття дуже важливі. Але python - просто інструмент для складання прототипів та швидкого розвитку з обмеженими ресурсами, доступними для розвитку, тому деякі рівні захисту не так суворо дотримуються в python. Ви можете використовувати "__" для учасника класу, він працює належним чином, але виглядає недостатньо добре - кожен доступ до такого поля містить ці символи.

Крім того, ви можете помітити, що концепція python OOP не є ідеальною, маленький малюнок або рубін набагато ближче до чистої концепції OOP. Навіть C # або Java ближче.

Python - дуже хороший інструмент. Але це спрощена мова OOP. Синтаксично та концептуально спрощено. Основна мета існування python - забезпечити розробникам можливість дуже швидко читати код з високим рівнем абстракції.


Переосмислення "Приватний" і "Захищений" є важливим у тому, що в статично складених мовах компілятор може створювати прямі виклики до приватного методу, але повинен спиратися на таблицю пошуку для публічних методів. Thiis просто не проблема з динамічними мовами. Нарешті, такі мови, як C ++, мають наслідки для успадкування та розв'язання методів. У Python та Ruby дуже схожі реалізації OO, тому порівняння безглуздо. Smalltalk насправді не має поняття публічних / приватних повідомлень. Безкоштовно додавати приватне як категорію, але суто дорадче.
Шейне

Для подальшого мого твердження. Frorm кодує гігієнічну точку зору, так, вони важливі для інкапсуляції, але це не потрібно для цього, тому декоратори @private (тощо) є більш дорадчими за будь-що, але як приватні / громадські не додають нічого корисного для оптимізації в нестатична мова, її не реалізовують на глибокому рівні, як це було б у мові складеної на зразок java чи c
Shayne

4

У Python немає приватних змінних, таких як C ++ або Java. Ви можете отримати доступ до будь-якої змінної члена в будь-який час, якщо захочете, також. Однак приватні змінні в Python вам не потрібні, тому що в Python непогано розкривати змінні учасників класів. Якщо у вас є необхідність інкапсулювати змінну члена, ви можете зробити це, використовуючи пізніше "@property", не порушуючи існуючий код клієнта.

У python єдине підкреслення "_" використовується для вказівки, що метод або змінна не вважається частиною загальнодоступних api класу і що ця частина api може змінюватися між різними версіями. Ви можете використовувати ці методи / змінні, але ваш код може порушитися, якщо використовувати нову версію цього класу.

Подвійне підкреслення "__" не означає "приватну змінну". Ви використовуєте його для визначення змінних, які є "класовими локальними" і які не можна легко перекрити підкласами. Він маніпулює назвою змінних.

Наприклад:

class A(object):
    def __init__(self):
        self.__foobar = None # will be automatically mangled to self._A__foobar

class B(A):
    def __init__(self):
        self.__foobar = 1 # will be automatically mangled to self._B__foobar

Ім'я .__ ім'я foobar автоматично привласнюється до self._A__foobar в класі A. У класі B воно перетворюється на self._B__foobar. Таким чином, кожен підклас може визначити власну змінну __foobar, не змінюючи змінну (ів) своїх батьків. Але ніщо не заважає вам отримати доступ до змінних, починаючи з подвійних підкреслень. Однак керування іменами не дозволяє випадково викликати ці змінні / методи.

Я настійно рекомендую дивитися, як Реймонд Хеттінгерс розмовляє "Інструментарій розробки класів Pythons" від Pycon 2013 (має бути доступний на Youtube), що дає хороший приклад, чому і як слід використовувати @property та "__" - змінні екземплярів.


Я збираюся перевірити цю розмову. Це @propertyріч є частиною стандартного Python, чи вона специфічна для IDE?
bballdave025

Його частина стандартна з пітона 2.6. Якщо ви використовуєте старішу версію, все ще є можливість використовувати propertyвбудовану функцію, яка доступна з python 2.2
Hatatister

0

Насправді ви можете імітувати C#геттера та сетера за допомогою цього простого трюку:

class Screen(object):

    def getter_setter_y(self, y, get=True):
        if get is True:
            Screen.getter_setter_y.value = y
        else:
            return Screen.getter_setter_y.value

     def getter_setter_x(self, x, get=True):
         if get is True:
             Screen.getter_setter_x.value = x
         else:
             return Screen.getter_setter_x.value

Потім використовуйте його аналогічно C#:

scr = Screen()
scr.getter_setter_x(100)
value =  scr.getter_setter_x(0, get=False)
print (value)

Це просто оголошення статичної локальної змінної у функції, яка буде грати роль get / set, оскільки це єдиний спосіб ділитися змінною методами get і set, не роблячи її глобальним для класу чи файлу.

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