Почему «приватные» методы Python на самом деле не приватны?

472

Python дает нам возможность создавать методы и переменные 'private' в классе, добавляя двойные подчеркивания к имени, например: __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'

Здесь все выглядит хорошо; мы не можем назвать это. Это, по сути, 'private'. Ну, на самом деле это не так. Запуск dir() на объекте показывает новый магический метод, который python создает магически для всех ваших методов 'private'.

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

Это новое имя метода всегда является символом подчеркивания, за которым следует имя класса, а затем имя метода.

>>> obj._MyClass__myPrivateMethod()
this is private!!

Так много для инкапсуляции, eh?

В любом случае, я всегда слышал, что Python не поддерживает инкапсуляцию, поэтому зачем даже пытаться? Что дает?

  • 209
    «Мы все согласные взрослые здесь».
  • 15
    То же самое верно для Java или C #, если вы используете отражение (что-то, что вы делаете там).
Показать ещё 7 комментариев
Теги:
python-2.7
encapsulation
information-hiding

11 ответов

438
Лучший ответ

Скремблирование имен используется для обеспечения того, чтобы подклассы не случайно переопределяли частные методы и атрибуты их суперклассов. Он не предназначен для предотвращения преднамеренного доступа извне.

Например:

>>> 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}

Конечно, он разбивается, если два разных класса имеют одинаковое имя.

  • 4
    Это так? С чего взял?
  • 12
    docs.python.org/2/tutorial/classes.html . Раздел: 9.6 на Частные переменные и классовые локальные ссылки.
Показать ещё 7 комментариев
204

Пример частной функции

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 ###
  • 12
    self = MyClass() self.private_function() . : D Конечно, это не работает в классах, но вы просто должны определить пользовательскую функцию: def foo(self): self.private_function()
  • 129
    На всякий случай неясно: никогда не делай этого в реальном коде;)
Показать ещё 10 комментариев
128

От http://www.faqs.org/docs/diveintopython/fileinfo_private.html

Строго говоря, частные методы доступный вне их класса, просто не легко доступны. Ничего в Python действительно личный; внутренне, имена частных методов и атрибуты искажены и не связаны на лету, чтобы заставить их казаться недоступными по их названию. Вы может получить доступ к методу __parse Класс MP3FileInfo по названию _MP3FileInfo__parse. Признайте, что это интересно, тогда обещайте никогда, никогда не делайте это в реальном коде. Частные методы приватны для разум, но, как и многие другие Python, их приватность в конечном счете, вопрос конвенции, а не сила.

  • 196
    или, как сказал Гвидо ван Россум: «Мы все взрослые».
  • 31
    -1: это просто неправильно. Двойное подчеркивание никогда не предназначено для использования в качестве частного. Ответ от Аля ниже говорит о подлинном намерении искажения имени в синтаксисе. Настоящее соглашение - это одно подчеркивание.
Показать ещё 1 комментарий
127

Когда я впервые пришел из Java в Python я hated. Это напугало меня до смерти.

Сегодня это может быть только одна вещь , которую я люблю больше всего о Python.

Мне нравится находиться на платформе, где люди доверяют друг другу и не чувствуют, что им нужно строить непроницаемые стены вокруг своего кода. В сильно инкапсулированных языках, если у API есть ошибка, и вы выяснили, что пошло не так, вы все равно не сможете обойти его, потому что необходимый метод является закрытым. В Python отношение: "уверен". Если вы думаете, что понимаете ситуацию, возможно, вы ее даже прочитали, тогда все, что мы можем сказать, "удачи!".

Помните, что инкапсуляция даже не слабо связана с "безопасностью" или не позволяет детям покинуть газон. Это просто еще один шаблон, который следует использовать для упрощения понимания базы кода.

  • 32
    @CamJackson Javascript это ваш пример ?? Единственный широко используемый язык, имеющий наследование на основе прототипов и язык, который благоприятствует функциональному программированию? Я думаю, что JS изучать намного сложнее, чем большинство других языков, так как он занимает несколько ортогональных шагов от традиционного ООП. Не то чтобы это мешало идиотам писать JS, они просто этого не знают;)
  • 0
    @Sudar Да, когда мы говорим о культуре принуждения программистов писать вещи определенным образом, Java, вероятно, является лучшей «противоположностью» Python в этом отношении. Но Javascript раздражает меня другими способами: P (см. Мой последний комментарий)
Показать ещё 8 комментариев
76

Обычно используется фраза: "Мы все соглашаем взрослых здесь". Добавив одно подчеркивание (не раскрывайте) или двойное подчеркивание (скрыть), вы сообщаете пользователю своего класса, что вы намерены, чтобы член был 'private' каким-то образом. Тем не менее, вы доверяете всем остальным вести себя ответственно и уважать это, если только у них нет веских причин (например, отладчиков, завершения кода).

Если у вас действительно есть что-то частное, вы можете реализовать его в расширении (например, в C для CPython). В большинстве случаев, однако, вы просто изучаете питоновский способ делать вещи.

  • 0
    так есть ли какой-то протокол оболочки, который я должен использовать для доступа к защищенной переменной?
  • 3
    Переменных нет больше, чем приватных. Если вы хотите получить доступ к атрибуту, который начинается с подчеркивания, вы можете просто сделать это (но учтите, что автор не одобряет это). Если вам нужен доступ к атрибуту, который начинается с двойного подчеркивания, вы можете сделать это самостоятельно, но вы почти наверняка не хотите этого делать.
29

Это не так, как вы абсолютно не можете обойти неприкосновенность членов на любом языке (указательная арифметика в С++, Reflections в .NET/Java).

Дело в том, что вы получаете сообщение об ошибке, если пытаетесь случайно вызвать частный метод. Но если вы хотите стрелять в ногу, продолжайте и делайте это.

Изменить: вы не пытаетесь защитить свои вещи посредством OO-инкапсуляции, не так ли?

  • 1
    Не за что. Я просто подчеркиваю, что странно дать разработчику простой и, на мой взгляд, волшебный способ доступа к «частным» свойствам.
  • 1
    Да, я просто попытался проиллюстрировать это. Если вы сделаете это закрытым, просто скажете: «Вы не должны обращаться к нему напрямую», заставив компилятор жаловаться. Но кто-то хочет действительно сделать это, он может. Но да, в Python это проще, чем в большинстве других языков.
Показать ещё 1 комментарий
12

Подобное поведение существует, когда имена атрибутов модуля начинаются с одного подчеркивания (например, _foo).

Атрибуты модуля, названные как таковые, не будут скопированы в модуль импорта при использовании метода from*, например:

from bar import *

Однако это соглашение, а не языковое ограничение. Это не частные атрибуты; на них можно ссылаться и манипулировать любым импортером. Некоторые утверждают, что из-за этого Python не может реализовать истинную инкапсуляцию.

11

Это только один из вариантов выбора языка. На каком-то уровне они оправданы. Они делают это так, что вам нужно идти довольно далеко от вашего пути, чтобы попытаться вызвать метод, и если вам это действительно нужно, вы должны иметь довольно вескую причину!

Отладочные крючки и тестирование приходят на ум как возможные приложения, которые, конечно, используются ответственно.

10

Соглашение об именах class.__stuff позволяет программисту понять, что он не предназначен для доступа к __stuff извне. Название mangling делает маловероятным, что кто-то сделает это случайно.

Правда, вы все равно можете обойти это, это еще проще, чем на других языках (что BTW также позволяет вам это делать), но ни один программист на Python не сделал бы этого, если бы он заботился об инкапсуляции.

3

С 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

Обратите внимание, что правила манипуляции предназначены главным образом для предотвращения несчастных случаев; все еще можно получить доступ или изменить переменную, которая считается конфиденциальной. Это может быть даже полезно в особых обстоятельствах, например, в отладчике.

Даже если вопрос старый, я надеюсь, что мой фрагмент может быть полезным.

0

Самая важная проблема, связанная с частными методами и атрибутами, заключается в том, чтобы сказать разработчикам не вызывать ее вне класса, и это инкапсуляция. вы можете неправильно понять безопасность от инкапсуляции. когда вы намеренно используете такой синтаксис (ниже), вы упомянули, вы не хотите инкапсуляции.

obj._MyClass__myPrivateMethod()

Я перешел с С#, и сначала это было странно для меня, но через некоторое время я пришел к мысли, что только то, как дизайнеры кода Python думают об ООП, отличается.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню