Почему методы Python 'private' не являются частными?

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 не поддерживает инкапсуляцию, поэтому зачем даже пытаться? Что дает?

Ответы

Ответ 1

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

Например:

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

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

Ответ 2

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

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

Ответ 3

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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

Ответ 6

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

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

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

Ответ 7

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

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

from bar import *

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

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

С 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

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

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

Ответ 11

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

obj._MyClass__myPrivateMethod()

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