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

StackOverflow https://stackoverflow.com/questions/70528

Вопрос

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, это не что иное, как часто используемое имя для первого параметра функции.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top