Почему «частные» методы Python на самом деле не являются частными?
-
09-06-2019 - |
Вопрос
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'
Здесь все выглядит хорошо;мы не можем позвонить.Фактически это «частное».Ну, на самом деле это не так.Бег реж() на объекте раскрывается новый магический метод, который Python волшебным образом создает для всех ваших «частных» методов.
>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']
Имя этого нового метода всегда представляет собой подчеркивание, за которым следует имя класса, а затем имя метода.
>>> obj._MyClass__myPrivateMethod()
this is private!!
Вот вам и инкапсуляция, да?
В любом случае, я всегда слышал, что Python не поддерживает инкапсуляцию, так зачем вообще пытаться?Что дает?
Решение
Скремблирование имен используется для того, чтобы подклассы случайно не переопределили частные методы и атрибуты своих суперклассов.Он не предназначен для предотвращения преднамеренного доступа извне.
Например:
>>> 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}
Конечно, это не работает, если два разных класса имеют одно и то же имя.
Другие советы
Пример частной функции
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 ###
Когда я впервые перешел с Java на Python, я ненавидел этот.Это напугало меня до смерти.
Сегодня это может быть только одно я люблю больше всего о Питоне.
Мне нравится находиться на платформе, где люди доверяют друг другу и не чувствуют необходимости строить непроницаемые стены вокруг своего кода.В сильно инкапсулированных языках, если в API есть ошибка, и вы выяснили, что пошло не так, вы все равно не сможете обойти ее, поскольку необходимый метод является закрытым.В Python отношение такое:"конечно".Если вы думаете, что понимаете ситуацию, возможно, даже прочитали ее, то все, что мы можем сказать, это «удачи!».
Помните, что инкапсуляция даже слабо связана с «безопасностью» или с защитой детей от лужайки.Это просто еще один шаблон, который следует использовать, чтобы облегчить понимание базы кода.
От http://www.faqs.org/docs/diveintopython/fileinfo_private.html
Строго говоря, частные методы доступны за пределами их класса, просто нелегко доступны.Ничто в Python не является действительно частным;Внутренне, имена частных методов и атрибутов искажены и непонятны на лету, чтобы они казались недоступными по указанным именам.Вы можете получить доступ к методу __parse класса mp3fileinfo по имени _mp3fileinfo__parse.Признайте, что это интересно, а затем обещайте никогда не делать это в реальном коде.Частные методы являются частными по какой -то причине, но, как и многие другие вещи в Python, их приватная, в конечном счете, является вопросом конвенции, а не силы.
Обычно используется фраза: «Мы все здесь взрослые люди по обоюдному согласию».Добавляя в начале одинарное подчеркивание (не раскрывать) или двойное подчеркивание (скрыть), вы сообщаете пользователю вашего класса, что вы хотите, чтобы этот член был каким-то образом «частным».Однако вы доверяете всем остальным вести себя ответственно и уважать это, если только у них нет веской причины не делать этого (например,отладчики, автодополнение кода).
Если вам действительно необходимо иметь что-то приватное, вы можете реализовать это в расширении (например.в C для CPython).Однако в большинстве случаев вы просто изучаете Pythonic-способ ведения дел.
Это не значит, что вы абсолютно не можете обойти конфиденциальность членов на любом языке (арифметика указателей в C++, размышления в .NET/Java).
Дело в том, что вы получите ошибку, если попытаетесь случайно вызвать приватный метод.Но если вы хотите выстрелить себе в ногу, сделайте это.
Редактировать:Вы не пытаетесь защитить свои данные с помощью объектно-ориентированной инкапсуляции, не так ли?
А class.__stuff
соглашение об именах позволяет программисту знать, что он не имеет права доступа __stuff
снаружи.Из-за искажения названий маловероятно, что кто-то сделает это случайно.
Правда, вы все равно можете обойти это, это даже проще, чем в других языках (которые, кстати, тоже позволяют вам это делать), но ни один программист Python не будет этого делать, если его заботит инкапсуляция.
Аналогичное поведение наблюдается, когда имена атрибутов модуля начинаются с одного подчеркивания (например,_фу).
Атрибуты модуля, названные так, не будут скопированы в импортирующий модуль при использовании from*
метод, например:
from bar import *
Однако это соглашение, а не языковое ограничение.Это не частные атрибуты;на них может ссылаться и манипулировать ими любой импортер.Некоторые утверждают, что из-за этого Python не может реализовать настоящую инкапсуляцию.
Это всего лишь один из вариантов языкового дизайна.На каком-то уровне они оправданы.Они делают это так, что вам нужно приложить немало усилий, чтобы попытаться вызвать этот метод, и если он вам действительно так нужен, у вас должна быть довольно веская причина!
В качестве возможных приложений на ум приходят отладочные приемы и тестирование, которые, конечно же, используются ответственно.
В 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
Обратите внимание, что правила искажения созданы главным образом для предотвращения несчастных случаев; по-прежнему возможно получить доступ к переменной, которая считается частной, или изменить ее. Это может быть даже полезно в особых случаях, например, в отладчике.
Даже если вопрос старый, я надеюсь, что мой фрагмент может быть полезен.
Самая важная задача в отношении частных методов и атрибутов — сказать разработчикам, чтобы они не вызывали их за пределами класса, и это инкапсуляция.можно неправильно понять безопасность инкапсуляции.когда кто-то намеренно использует такой синтаксис (ниже), о котором вы упомянули, вам не нужна инкапсуляция.
obj._MyClass__myPrivateMethod()
Я перешел с C#, и поначалу для меня это тоже было странно, но через некоторое время я пришел к мысли, что только то, как дизайнеры кода Python думают об ООП, отличается.
Почему «частные» методы Python на самом деле не являются частными?
Насколько я понимаю, они не мочь быть частным.Как можно обеспечить соблюдение конфиденциальности?
Очевидный ответ: «Доступ к частным членам возможен только через self
", но это не сработает - self
не является чем-то особенным в Python, это не что иное, как часто используемое имя для первого параметра функции.