Python дает нам возможность создавать «частные» методы и переменные внутри класса, добавляя двойные подчеркивания к имени, например: __myPrivateMethod(). Как же тогда это объяснить?
>>> class MyClass:
... def myPublicMethod(self):
... print 'public method'
... def __myPrivateMethod(self):
... print 'this is private!!'
...
>>> obj = MyClass()
>>> obj.myPublicMethod()
public method
>>> obj.__myPrivateMethod()
Traceback (most recent call last):
File "", line 1, in
AttributeError: MyClass instance has no attribute '__myPrivateMethod'
>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']
>>> obj._MyClass__myPrivateMethod()
this is private!!
В чем дело?!
Я немного объясню это для тех, кто не совсем понял.
>>> class MyClass:
... def myPublicMethod(self):
... print 'public method'
... def __myPrivateMethod(self):
... print 'this is private!!'
...
>>> obj = MyClass()
Что я там сделал, так это создал класс с общедоступным методом и частным методом и создал его экземпляр.
Затем я вызываю его общедоступный метод.
>>> obj.myPublicMethod()
public method
Затем я пытаюсь вызвать его частный метод.
>>> obj.__myPrivateMethod()
Traceback (most recent call last):
File "", line 1, in
AttributeError: MyClass instance has no attribute '__myPrivateMethod'
Здесь все выглядит хорошо; мы не можем это назвать. На самом деле это «личное». На самом деле это не так. Запуск dir () на объекте открывает новый волшебный метод, который python волшебным образом создает для всех ваших «частных» методов.
>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']
Имя этого нового метода всегда является подчеркиванием, за которым следует имя класса, за которым следует имя метода.
>>> obj._MyClass__myPrivateMethod()
this is private!!
Так много для инкапсуляции, а?
В любом случае, я всегда слышал, что Python не поддерживает инкапсуляцию, так зачем даже пытаться? Что дает?
Он был создан для целей модульного тестирования, поэтому вы можете использовать этот «хак» для модульного тестирования частных методов вашего класса извне.
Разве тестирование частных методов не является антипаттерном? Частные методы наверняка будут использоваться в некоторых общедоступных методах, иначе они просто не будут использоваться навсегда. И правильный способ тестирования частных методов (основанный на моих знаниях, полученных в ходе работы над ThoughtWorks) - это писать тесты только для общедоступных методов, которые охватывают все случаи. Если это работает нормально, вам вообще не нужно тестировать частные методы извне.
@VishnuNarang: Да, это то, чему часто учат. Но, как всегда, почти «религиозный» подход «всегда do this, никогда do that» - единственное, что «никогда» не годится. Если модульные тесты используются «только» для регрессионных тестов или тестирования общедоступного API, вам не нужно тестировать частные. Но если вы занимаетесь разработкой, управляемой модульными тестами, есть веские причины для тестирования приватных методов во время разработки (например, когда сложно имитировать определенные необычные / экстремальные параметры через общедоступный интерфейс). Некоторые языки / среды модульного тестирования не позволяют вам этого делать, что, IMHO, не очень хорошо.
@MarcoFreudenberger Я понимаю вашу точку зрения. У меня есть опыт разработки, основанной на модульных тестах. Часто, когда становится сложно имитировать параметры, чаще всего это решается изменением и улучшением дизайна. Мне еще предстоит встретить сценарий, в котором дизайн был бы идеальным, и все же модульное тестирование чрезвычайно сложно избежать тестирования частных методов. Я буду следить за такими случаями. Спасибо. Я был бы признателен, если бы вы могли поделиться одним сценарием из головы, чтобы помочь мне понять.
У меня нет подходящего сценария, который прямо или косвенно не задействовал бы внешнее оборудование (например, я много работаю над системами машинного зрения, для определенных значений, передаваемых частным методам, необходимо создать определенные изображения, которые может быть сложно - потому что общедоступный интерфейс будет принимать изображения с камеры или из набора тестовых изображений в случае модульных тестов).
Действительно хорошая идея, что все мы взрослые по согласию. Факт: в группе достаточно программистов, как и в любой другой группе, мудрость распространяется неравномерно. Если бы все знали все, stackoverflow не существовало бы. Вот еще одна цитата: «Я пришел к философии, а ушел к реальности».
@masi Плюс, если вы больше, чем простейшая кодовая обезьяна, вам придется манипулировать несколькими мыслями во время программирования. Чистые кодировщики делают методы небольшими, чтобы не помнить слишком много вещей. Если мне нужно подражать безопасности типов в голове, это еще одна вещь, которая отвлекает от сосредоточения внимания на архитектуре и алгоритмах.
Разоблачая частные методы, питон, похоже, предпочитает «печень, свисающая из грудной клетки», хотя ему и предлагают «мы все взрослые, в Python мы доверяем».






От http://www.faqs.org/docs/diveintopython/fileinfo_private.html
Strictly speaking, private methods are accessible outside their class, just not easily accessible. Nothing in Python is truly private; internally, the names of private methods and attributes are mangled and unmangled on the fly to make them seem inaccessible by their given names. You can access the __parse method of the MP3FileInfo class by the name _MP3FileInfo__parse. Acknowledge that this is interesting, then promise to never, ever do it in real code. Private methods are private for a reason, but like many other things in Python, their privateness is ultimately a matter of convention, not force.
или, как выразился Гвидо ван Россум: «мы все взрослые».
-1: это просто неправильно. Двойное подчеркивание никогда не предназначено для использования в первую очередь как закрытое. Ответ от Alya ниже показывает истинное намерение синтаксиса искажения имени. Настоящее соглашение - это одно подчеркивание.
Попробуйте использовать только одно подчеркивание, и вы увидите результат. @nosklo
Это не похоже на то, что вы абсолютно не можете обойти конфиденциальность членов на любом языке (арифметика указателей в C++, отражения в .NET / Java).
Дело в том, что вы получите ошибку, если попытаетесь случайно вызвать закрытый метод. Но если вы хотите прострелить себе ногу, сделайте это.
Обновлено: вы не пытаетесь защитить свои вещи с помощью OO-инкапсуляции, не так ли?
Нисколько. Я просто подчеркиваю, что было бы странно давать разработчику простой и, на мой взгляд, волшебный способ доступа к «частным» свойствам.
Да, я просто попытался проиллюстрировать это. Если сделать его закрытым, это просто означает, что вы не должны обращаться к нему напрямую, заставляя компилятор жаловаться. Но действительно очень хочется сделать это, он может. Но да, на Python это проще, чем на большинстве других языков.
В Java вы действительно можете защитить данные с помощью инкапсуляции, но для этого нужно быть умным, запускать ненадежный код в SecurityManager и быть очень осторожным. Даже Oracle иногда ошибается.
Соглашение об именах class.__stuff позволяет программисту знать, что он не предназначен для доступа к __stuff извне. Искажение имени делает маловероятным, что кто-то сделает это случайно.
Конечно, вы все еще можете обойти это, это даже проще, чем на других языках (которые, кстати, также позволяют вам это делать), но ни один программист Python не стал бы этого делать, если бы он заботился об инкапсуляции.
Это просто один из тех вариантов языкового дизайна. В какой-то степени они оправданы. Они делают это так, что вам нужно довольно далеко отойти от своего пути, чтобы попытаться вызвать метод, и если вам действительно это нужно так сильно, у вас должна быть довольно веская причина!
В качестве возможных приложений на ум приходят отладочные хуки и тестирование, которые, конечно же, используются ответственно.
Обычно используется фраза «мы все здесь взрослые по согласию». Добавляя одинарное подчеркивание (не раскрывать) или двойное подчеркивание (скрывать), вы сообщаете пользователю вашего класса, что хотите каким-то образом сделать этот член «частным». Однако вы доверяете всем остальным вести себя ответственно и уважать это, если только у них нет веских причин не делать этого (например, отладчики, автозавершение кода).
Если вам действительно нужно что-то частное, вы можете реализовать это в расширении (например, в C для CPython). Однако в большинстве случаев вы просто изучаете питонический образ действий.
так есть ли какой-то протокол-оболочка, который я должен использовать для доступа к защищенной переменной?
«Защищенных» переменных нет больше, чем «частных». Если вы хотите получить доступ к атрибуту, который начинается с подчеркивания, вы можете просто сделать это (но обратите внимание, что автор этого не одобряет). Если вам необходимо получить доступ к атрибуту, который начинается с двойного подчеркивания, вы можете изменить имя самостоятельно, но вы почти наверняка не захотите этого делать.
Скремблирование имен используется, чтобы гарантировать, что подклассы случайно не переопределят частные методы и атрибуты своих суперклассов. Он не предназначен для предотвращения преднамеренного доступа извне.
Например:
>>> class Foo(object):
... def __init__(self):
... self.__baz = 42
... def foo(self):
... print self.__baz
...
>>> class Bar(Foo):
... def __init__(self):
... super(Bar, self).__init__()
... self.__baz = 21
... def bar(self):
... print self.__baz
...
>>> x = Bar()
>>> x.foo()
42
>>> x.bar()
21
>>> print x.__dict__
{'_Bar__baz': 21, '_Foo__baz': 42}
Конечно, он не работает, если два разных класса имеют одно и то же имя.
Для тех из нас, кому лень прокручивать / искать: Раздел 9.6 прямая ссылка
Красиво - причина положительная! Я читал / слышал, что это было просто для предотвращения преднамеренного доступа извне (без фактического блокирования). Рад видеть, что есть и дополнительные преимущества.
Вы должны поставить один знак подчеркивания, чтобы указать, что переменная должна рассматриваться как частная. Опять же, это не мешает кому-то получить к нему доступ.
@miya о да, это так, и он получил это от документация
Если следовать критерию «предотвратить преднамеренный доступ», большинство языков ООП не поддерживают действительно закрытые члены. Например, в C++ у вас есть прямой доступ к памяти, а в доверенном коде C# можно использовать частное отражение.
Вот почему я никогда не балуюсь классами, интерфейс которых был разработан мной. Каждый, кто может его подклассифицировать, знает, над чем он работает, и, скорее всего, у него будет причина перегрузить определенные вещи, поэтому одного подчеркивания достаточно, чтобы сделать методы и свойства приватными. Я действительно использую dunders при реализации интерфейсов, разработанных другими, чтобы не загромождать api и предотвратить ошибки в случае, если они разделены на подклассы на основе знаний об их ABS. Хорошая причина для использования dunders - это, например, реализация io.BufferedIOBase, где ничего не должно мешать работе буферов.
Обновление с Python 3.4: имя с префиксом подчеркивания (например, _spam) должно рассматриваться как закрытая часть API (будь то функция, метод или член данных). Это следует рассматривать как деталь реализации и может быть изменено без предварительного уведомления. docs.python.org/3/tutorial/classes.html#tut-private
Аналогичное поведение наблюдается, когда имена атрибутов модуля начинаются с одного символа подчеркивания (например, _foo).
Атрибуты модуля, названные как таковые, не будут скопированы в модуль импорта при использовании метода from*, например:
from bar import *
Однако это соглашение, а не языковое ограничение. Это не частные атрибуты; на них может ссылаться и манипулировать ими любой импортер. Некоторые утверждают, что из-за этого Python не может реализовать настоящую инкапсуляцию.
Когда я впервые перешел с Java на Python, я ненавидел this. Это до смерти напугало меня.
Сегодня это может быть только одна вещь Я люблю больше всего о Python.
Мне нравится находиться на платформе, где люди доверяют друг другу и не чувствуют, что им нужно строить непроницаемые стены вокруг своего кода. В строго инкапсулированных языках, если в API есть ошибка, и вы выяснили, что идет не так, вы все равно не сможете ее обойти, потому что необходимый метод является частным. В Python позиция такая: «конечно». Если вы думаете, что понимаете ситуацию, возможно, вы даже читали ее, тогда все, что мы можем сказать, - это «удачи!».
Помните, что инкапсуляция даже не слабо связана с «безопасностью» или защитой от детей на лужайке. Это просто еще один шаблон, который следует использовать, чтобы упростить понимание кода.
@CamJackson Javascript - ваш пример ?? Единственный широко используемый язык с наследованием на основе прототипов и языком, который поддерживает функциональное программирование? Я думаю, что JS намного сложнее выучить, чем большинство других языков, поскольку он требует нескольких шагов, ортогональных по сравнению с традиционным ООП. Не то чтобы это мешало идиотам писать JS, они просто этого не знают;)
@Sudar Да, когда мы говорим о культуре принуждения программистов к написанию вещей определенным образом, Java, вероятно, является лучшей «противоположностью» Python в этом отношении. Но Javascript меня раздражает по-другому: P (см. Мой последний комментарий)
API-интерфейсы на самом деле являются действительно хорошим примером того, почему инкапсуляция важна и когда предпочтительнее использовать частные методы. Метод, предназначенный как частный, может исчезнуть, изменить подпись или, что хуже всего, изменить поведение - и все это без предупреждения - в любой последующей новой версии. Будет ли ваш умный взрослый член команды действительно помнить, что через год, когда вы обновляете, она получила доступ к предполагаемому частному методу? Будет ли она там больше работать?
Я не согласен с этим аргументом. В производственном коде я, скорее всего, никогда не буду использовать API, в котором есть ошибка, которая заставляет меня менять публичные члены, чтобы заставить его «работать». API должен работать. Если этого не произойдет, я бы отправил отчет об ошибке или сам сделал тот же API. Мне не нравится эта философия, и я не очень люблю Python, хотя с его синтаксисом интересно писать небольшие скрипты на ...
Никто не заставляет вас использовать недокументированные API, но если вы этого хотите, кто вам скажет? Модульное тестирование приходит на ум как вариант использования.
В Java есть Method.setAccessible и Field.setAccessible. Также страшно?
Принуждение в Java и C++ происходит не потому, что Java не доверяет пользователю, в то время как Python. Это потому, что компилятор и / или виртуальная машина могут делать различные предположения, имея дело с тем, как он ведет свои дела, если он знает эту информацию, например, C++ может пропустить весь уровень косвенного обращения, используя обычные старые вызовы C вместо виртуальных вызовов, и что имеет значение, когда вы работаете с высокопроизводительными или высокоточными материалами. Python по своей природе не может эффективно использовать информацию без ущерба для ее динамизма. Оба языка нацелены на разные вещи, поэтому ни один из них не является «неправильным».
Я противоположен твоему комментарию. Я пришел с Python и начал изучать Java и C++. Private - это не недоверие к товарищам по команде. Без типичных ошибок, когда новый разработчик в команде обращается к методу, выходящему за рамки бизнес-логики. то, что должно быть частным и вызываться специально с помощью некоторой логики, теперь стало общедоступным и может вызываться извне. еще страшнее, когда разработчик, совершающий эту ошибку, делает это на общедоступной конечной точке, открывая приложение для sec. уязвимости. Мне нравится питон, но я ненавижу этот его аспект.
Это самое определение. может быть сек. недостаток. разработчики приходят и уходят командами. разработчику может быть поручено добавить общедоступную конечную точку, которая позволяет аффилированным лицам передавать новых регистрантов для создания пользователей. Однако сказал разработчик, перепрофилирует создание учетной записи, сделанное другими разработчиками. Класс создания учетной записи вызывает несколько методов, которые проверяют домены на предмет мошенничества. Но этот новый разработчик напрямую обращается к финальному методу, передавая значение для проверки домена. теперь общедоступная конечная точка обходит проверки на мошенничество. Если бы этот последний метод был частным, ничего бы этого не произошло. это заставит его течь через автобус. логика.
Мы не делаем методы частными и общедоступными в целях безопасности. Мы делаем их, чтобы пользователи не могли нанести серьезный ущерб состоянию и писать программы с использованием изменчивых методов. Открытый метод представляет собой контракт, если я дам вам A, вы получите B. Частные методы не представляют собой контракт. Я могу изменить логику, которая превращает A в B, сколько мне нужно, пока интерфейс взаимодействия по-прежнему выполняет контракт. Если я дам вам доступ к частным методам, вы будете их использовать. Затем, когда я меняю реализацию, ваша программа ломается. Этого не происходит с общедоступными методами, составляющими контракт.
"Мне нравится находиться на платформе, где люди доверяют друг другу и не чувствуют, что им нужно строить непроницаемые стены вокруг своего кода." Полная чушь. Отказ от установленных принципов дизайна не заставит ваших коллег полюбить вас, это как раз наоборот. Python во многих отношениях никогда не достигнет структуры и формализма типизированных языков JVM или функционального чутья Haskell. Это хорошо для некоторых вещей, но не нужно красить свинью помадой.
@einnocent Меня не убеждает, что приватный метод может измениться в любой момент без уведомления. Фактически то же самое можно применить и к Python, docs.python.org/3/tutorial/classes.html#tut-private, _underscore_method() следует рассматривать как деталь реализации. Таким образом, потребитель класса должен принять во внимание, что он может измениться в любой момент. Если кто-то использует ваши частные методы в своем производственном коде, то это не ваша проблема, если вы измените свои личные данные.
Самая большая ошибка Java заключалась в том, чтобы не различать общедоступный и опубликованный метод (метод, который должен вызываться извне jar), соединение python в этой ошибке делает все видимым. В любом достаточно большом проекте управление релизами и зависимостями превращается в полный кошмар (добавьте сверху, что некоторые ошибки могут быть обнаружены только во время выполнения, а тикающая бомба обслуживается)
import re
import inspect
class MyClass :
def __init__(self) :
pass
def private_function ( self ) :
try :
function_call = inspect.stack()[1][4][0].strip()
# See if the function_call has "self." in the begining
matched = re.match( '^self\.', function_call )
if not matched :
print 'This is Private Function, Go Away'
return
except :
print 'This is Private Function, Go Away'
return
# This is the real Function, only accessible inside class #
print 'Hey, Welcome in to function'
def public_function ( self ) :
# i can call private function from inside the class
self.private_function()
### End ###
self = MyClass()self.private_function(). : D Конечно, это не работает в классах, но вам просто нужно определить пользовательскую функцию: def foo(self): self.private_function()
На всякий случай непонятно: никогда делает это в реальном коде;)
Я бы не стал загружать регулярное выражение только для того, чтобы проверить выражение /^.../, сэкономить время и использовать 0 == function_call.index('self.').
@ThorSummoner Или просто function_call.startswith('self.').
inspect.stack()[1][4][0].strip() <- что это за магические числа 1, 4 и 0?
self - это всего лишь условность. можно использовать другое имя для первого аргумента
@arun, почему 1 4 0?
но когда я вызываю obj.public_function () ... он все еще показывает «Это закрытая функция, уходи». не должен ли он показывать «добро пожаловать в работу»
@SudoBash - идиотский вопрос: почему бы не использовать это в реальном коде?
@Shuklaswag Это усложняет работу с вашим кодом, поскольку создает препятствия на пути его отладки. Попытки обойти правила языка с ненужной смекалкой редко заканчиваются хорошо. (Он также не блокирует публичные вызовы функции).
Эту проблему можно довольно легко решить, выполнив self = MyClass(); self.private_function(), и она не работает при вызове с использованием x = self.private_function() внутри метода.
не уверен, что понимаю этот ответ. Он демонстрирует, как иметь действительно частный метод, но не отвечает на вопрос «почему у pyhthon нет действительно частных методов?», Верно?
В Python 3.4 это поведение:
>>> class Foo:
def __init__(self):
pass
def __privateMethod(self):
return 3
def invoke(self):
return self.__privateMethod()
>>> help(Foo)
Help on class Foo in module __main__:
class Foo(builtins.object)
| Methods defined here:
|
| __init__(self)
|
| invoke(self)
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
>>> f = Foo()
>>> f.invoke()
3
>>> f.__privateMethod()
Traceback (most recent call last):
File "<pyshell#47>", line 1, in <module>
f.__privateMethod()
AttributeError: 'Foo' object has no attribute '__privateMethod'
https://docs.python.org/3/tutorial/classes.html#tut-private
Note that the mangling rules are designed mostly to avoid accidents; it still is possible to access or modify a variable that is considered private. This can even be useful in special circumstances, such as in the debugger.
Даже если вопрос старый, я надеюсь, что мой фрагмент может быть полезен.
Самая важная проблема, связанная с частными методами и атрибутами, - сказать разработчикам, чтобы они не вызывали их вне класса, и это инкапсуляция. можно неправильно понять безопасность от инкапсуляции. когда кто-то намеренно использует подобный синтаксис (ниже), о котором вы упомянули, вам не нужна инкапсуляция.
obj._MyClass__myPrivateMethod()
Я перешел с C#, и сначала для меня это тоже было странно, но через некоторое время я пришел к мысли, что только то, как разработчики кода Python думают о ООП, отличается.
Why are Python's 'private' methods not actually private?
Насколько я понимаю, они не могу приватные. Как можно обеспечить конфиденциальность?
Очевидный ответ - «к закрытым членам можно получить доступ только через self», но это не сработает - self не является особенным в Python, это не что иное, как обычно используемое имя для первого параметра функции.
Любой идентификатор в форме __name (не менее двух ведущих подчеркиваний, не более одного подчеркивания в конце) публично заменяется на _classname__name, где classname - это имя текущего класса с удаленными ведущими подчеркиваниями.
Следовательно, __name является частным, а _classname__name - публичным.
https://docs.python.org/3/tutorial/classes.html#tut-private
class Cat:
def __init__(self, name='unnamed'):
self.name = name
def __print_my_name(self):
print(self.name)
tom = Cat()
tom.__print_my_name() #Error
tom._Cat__print_my_name() #Prints name
То же самое верно для Java или C#, если вы используете отражение (что каким-то образом вы там делаете).