Определение функций частного модуля в Python

239

Согласно http://www.faqs.org/docs/diveintopython/fileinfo_private.html :

Как и большинство языков, в Python есть концепция приватных элементов:

  • Частные функции, которые нельзя вызывать извне их модуля

Однако, если я определю два файла:

#a.py
__num=1

и:

#b.py
import a
print a.__num

когда я запускаю, b.pyон печатается 1без каких-либо исключений. Является ли diveintopython неправильно, или я что-то неправильно понял? И есть ли какой - нибудь способ сделать определить функцию модуля , как частные?

olamundo
источник
Дело не в том, что diveintopython неправильный, а в их примере: >>> import fileinfo >>> m = fileinfo.MP3FileInfo() >>> m.__parse("/music/_singles/kairo.mp3") 1 Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'MP3FileInfo' instance has no attribute '__parse' fileinfo.MP3FileInfo () является экземпляром класса. Что дает это исключение, когда вы используете двойное подчеркивание. В то время как в вашем случае вы не создали класс, вы просто создали модуль. См. Также: stackoverflow.com/questions/70528/…
Гомер Эсмеральдо,

Ответы:

324

В Python «конфиденциальность» зависит от уровня «согласия взрослых» - вы не можете его заставить (больше, чем можете в реальной жизни ;-). Одно лидирующее подчеркивание означает, что вы не должны получать к нему доступ «извне» - два лидирующих подчеркивания (без конечных подчеркиваний) несут сообщение еще более убедительно ... но, в конце концов, оно все еще зависит от социальных соглашение и согласие: самоанализ Python достаточно силен, чтобы вы не могли надеть наручники любого другого программиста в мире, чтобы уважать ваши пожелания.

(Между прочим, хотя это и держится в секрете, то же самое относится и к C ++: с большинством компиляторов простая #define private publicстрочка перед #includeвашим .hфайлом - все, что нужно хитрым кодировщикам для хеша вашей "конфиденциальности" ...! -) )

Алекс Мартелли
источник
82
Ваша заметка на C ++ неверна. Используя #define private public, вы изменяете код, который отправляется компилятору, где происходит искажение имени.
носорогов
14
Также искажение C ++ неясно, но вряд ли секретно. Вы также можете "проанализировать" двоичный файл, созданный C ++. ОТ, прости.
Профессор Фалькен
47
Как обновление @rhinoinrepose, это не просто неверно, это неопределенное поведение в соответствии со стандартом для переопределения ключевого слова с помощью макроса препроцессора.
Кори Крамер
3
@AlexMartelli Не static void foo()такой частный, как сейчас. По крайней мере, он скрыт для компоновщика, и функция может быть полностью удалена путем встраивания.
user877329
3
В реальной жизни люди подвергаются судебному преследованию, если они нарушают законы
Lay González
290

Может быть путаница между рядовыми и рядовыми .

Закрытый модуль начинается с одного подчеркивания.
Такой элемент не копируется при использовании from <module_name> import *формы команды импорта; однако он импортируется при использовании import <moudule_name>синтаксиса ( см. ответ Бена Вильгельма ).
Просто удалите одно подчеркивание из числа .__ примера вопроса, и он не будет отображаться в модулях, импортирующих a.py с использованием from a import *синтаксиса.

А класс частные начинается с двумя символами подчеркивания (ака Dunder т.е. d-ouble под Score)
Такой переменный имеют свое название «искаженное» включить имя класса и т.д.
Это все еще может быть доступны за пределы класса логики, через искаженное имя.
Хотя искажение имени может служить средством для предотвращения несанкционированного доступа, его основная цель - предотвратить возможные конфликты имен с членами классов классов-предков. См. Забавную, но точную ссылку Алекса Мартелли на согласных взрослых, когда он описывает соглашение, используемое в отношении этих переменных.

>>> class Foo(object):
...    __bar = 99
...    def PrintBar(self):
...        print(self.__bar)
...
>>> myFoo = Foo()
>>> myFoo.__bar  #direct attempt no go
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__bar'
>>> myFoo.PrintBar()  # the class itself of course can access it
99
>>> dir(Foo)    # yet can see it
['PrintBar', '_Foo__bar', '__class__', '__delattr__', '__dict__', '__doc__', '__
format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__
', '__subclasshook__', '__weakref__']
>>> myFoo._Foo__bar  #and get to it by its mangled name !  (but I shouldn't!!!)
99
>>>
MJV
источник
Ну, пока. Есть ли какая-то причина, почему они не применяют модуль на уровне __private_function? Я столкнулся с этим и попал в ошибки из-за этого.
Санта
Спасибо за добрые слова @Terrabits, но я с радостью стою на втором месте после Алекса (далеко позади!) Во всем «Python». Кроме того, его ответы, как правило, более лаконичны и смешны, но при этом сохраняют высокий уровень авторитета, учитывая огромный вклад Алекса в язык и сообщество.
mjv
1
@mjv Это было такое полезное объяснение! Спасибо! Я был довольно озадачен этим поведением некоторое время. Мне бы очень хотелось, чтобы был выброшен какой-то тип ошибки, кроме AttributeError, если вы пытались получить прямой доступ к классу private; возможно, «PrivateAccessError» или что-то более понятное / полезное. (Так как получение ошибки о том, что у него нет атрибута, на самом деле не соответствует действительности ).
HFBrowning
82

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

Если вы определите личные имена в модуле, эти имена будут импортированы в любой скрипт, использующий синтаксис «имя_модуля импорта». Таким образом, предполагая, что вы правильно определили в своем примере модуль private, _num, в a.py, вот так ..

#a.py
_num=1

..Вы можете получить доступ к нему в b.py с символом имени модуля:

#b.py
import a
...
foo = a._num # 1

Чтобы импортировать только неприватные файлы из a.py, вы должны использовать синтаксис from :

#b.py
from a import *
...
foo = _num # throws NameError: name '_num' is not defined

Для ясности, однако, лучше быть явным при импорте имен из модулей, а не импортировать их все с помощью «*»:

#b.py
from a import name1 
from a import name2
...
Бен Вильгельм
источник
1
где вы указываете, какие функции / библиотеки импортируются? в init .py?
FistOfFury
При _namesвызове с ним не возникает риска конфликтов имен import a- они являются доступами, как a._namesпри использовании этого стиля.
Иосия Йодер
@FistOfFury Да, вы указываете функции, импортированные в __init__.pyфайл. Смотрите здесь для некоторой помощи по этому вопросу.
Майк Уильямсон
29

Python позволяет для частного класса члены с префиксом двойного подчеркивания. Этот метод не работает на уровне модулей, поэтому я думаю, что это ошибка в Dive Into Python.

Вот пример функций частного класса:

class foo():
    def bar(self): pass
    def __bar(self): pass

f = foo()
f.bar()   # this call succeeds
f.__bar() # this call fails
Эндрю Хэйр
источник
2
Я думаю, что целью OP является написание функций, которые недоступны вне, например, коммерческого пакета. В связи с этим этот ответ не является полным. Функция __bar () по-прежнему доступна извне через f._foo__bar (). Поэтому двойные подчеркивания не делают его приватным.
SevakPrime
24

Вы можете добавить внутреннюю функцию:

def public(self, args):
   def private(self.root, data):
       if (self.root != None):
          pass #do something with data

Нечто подобное, если вам действительно нужен этот уровень конфиденциальности.

Илиан Запрянов
источник
9
Почему это не лучший ответ?
Safay
2

Это древний вопрос, но в стандартной документации теперь рассматриваются как закрытые для модуля (одно подчеркивание), так и закрытые для класса (два подчеркивание) переменные:

The Python Tutorial » Классы » Частные переменные

user1338062
источник
1

встроенный с замыканиями или функциями является одним из способов. Это часто встречается в JS, но не требуется для не браузерных платформ или рабочих браузеров.

В Python это кажется немного странным, но если что-то действительно нужно скрыть, это может быть способом. Более конкретно, использование Python API и хранение вещей, которые должны быть скрыты в C (или другом языке), возможно, лучший способ. В противном случае я бы пошел, чтобы поместить код в функцию, вызвать ее и заставить возвращать элементы, которые вы хотите экспортировать.

Марсианская слава
источник
-12

В Python есть три режима через., Приватный, публичный и защищенный. При импорте модуля доступен только публичный режим. Поэтому частные и защищенные модули не могут быть вызваны извне модуля, т. Е. Когда он импортирован.

sravan
источник