Как реорганизовать методы в Python, чтобы сделать их частными?

#python #private

#python #Частное

Вопрос:

Я знаю, что в Python нет такого понятия, как true private, потому что иногда вам нужно следовать:

  • запретить пользователям вызывать некоторые методы или предупреждать их, когда они это делают, но разрешить их вызывать из других модулей
  • определите, где используются эти методы, и выведите предупреждение на консоль или даже исключение, это позволит вам начать помечать методы как частные, не нарушая существующий код.
  • скрыть частные методы из автозаполнения IDE (необязательно) — возможно, используя одно подчеркивание перед его именем. Теперь мне интересно, нашел ли кто-нибудь красивый способ добиться такого поведения, может быть, тот, который использует annotations ?

Пожалуйста, помните, что это должно работать с существующей кодовой базой, поэтому оно должно поддерживать постепенную переработку существующего кода.

Последний пункт почти решен, единственный вопрос в том, должен ли я использовать одно подчеркивание или два подчеркивания?

Комментарии:

1. Как вы проводите различие в # 1? # 2 будет зависеть от IDE. # 3 может быть возможным, но будет генерировать фиктивные предупреждения (если вы не потрудитесь отличить «законных» абонентов от остальных, и даже скорее всего, это не будет идеальным в любом смысле этого слова) и в любом случае звучит как устаревание, а не конфиденциальность. Почему бы просто не изменить имя? Добавьте символ подчеркивания, ничто не говорит «частный», как это.

2. Просто используйте одно подчеркивание — искажение имени, вызванное двойным подчеркиванием, вызовет гораздо больше головной боли, чем решит.

Ответ №1:

У Python есть философия «согласия взрослых»: префиксные методы с подчеркиванием, чтобы пометить их как частные. Не вызывайте никаких методов с начальным подчеркиванием извне. Если вы это сделаете, вы сами по себе. Вы можете это сделать, но вас предупредили.

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

Ваша IDE должна иметь настраиваемое автозаполнение. Если нет, используйте Emacs 🙂

Комментарии:

1. @Paul: Часть Emacs была скорее шуткой. Мне действительно и честно все равно, что другие люди используют для редактирования своего кода. vi тоже в порядке. 🙂

Ответ №2:

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

Стандартное соглашение python — это префикс и подчеркивание имени метода. Это сообщает пользователю, что метод является частным и его не следует использовать.

Вы можете использовать двойные подчеркивания перед именем метода; это вызовет искажение имени.

Подробности см. в разделе 9.7 на этой странице: http://docs.python.org/tutorial/classes.htm .

Но это все еще не приватно; его можно вызвать.

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

tl; dr Вы не можете сделать что-либо закрытым, но вы можете усложнить поиск

Комментарии:

1. Двойные подчеркивания перед именем вызывают искажение имени. Двойные подчеркивания перед и после имени — это зарезервированные имена, которые не должны использоваться (за исключением предопределенных, конечно).

Ответ №3:

Возможно, определите свои частные методы во внутреннем классе и получите к нему внутренний доступ, используя self._private.method :

 class PublicObject(object):

    class PrivateObject(object):
        def __init__(self, public):
            self.public = public

        def private1(self):
            print "a private method, not visible outside the public wrapper on", id(self.public)

    def __init__(self):
        self._private = self.PrivateObject(self)

    def public1(self):
        print "a public method, which invokes a private one"
        return self._private.private1()

a = PublicObject()

print dir(a) # only shows public1, not private1; won't show private1 in most IDEs autocomplete

a.public1()
a.private1() # raises an exception
a._private.private1() # still possible to call private methods, but obvious when you are doing so
  

Если вы хотите, чтобы ProtectedObject отображался в цепочке наследования, создайте аналогичный _protected атрибут и используйте __getattr__ для доступа к атрибутам и методам super(PublicObject,self.public)._protected .

Кстати, то, о чем вы спрашиваете, действительно выходит за рамки философии Python, и добавление этого косвенного обращения через составной объект приведет к снижению производительности.

Ответ №4:

Вы можете украсить частные методы вызовом warnings.warn .

Я не уверен, что вы имеете в виду, запрещая людям, но позволяя модулям вызывать методы. Является ли различие в том, что одно происходит в интерактивном приглашении, а другое — нет? Если это так, вы можете проверить, запускается ли python из интерактивного приглашения, проверив значение sys.path[0] .

Когда python запускает скрипт, sys.path[0] он равен каталогу скрипта. Когда python запускает интерактивный сеанс, sys.path[0] устанавливается пустая строка '' . Итак, чтобы предупредить людей, но не скрипты, вы могли бы сделать

 import warnings
import functools

def warn_private(func):
    if not sys.path[0]:
        @functools.wraps(func)
        def wrapper(self,*args,**kwargs):
            warnings.warn('{f} is private'.format(f=func.__name__))
            return func(self,*args,**kwargs)
        return wrapper
    else:
        return func

class Foo(object):
    @warn_private
    def _bar(self):
        pass
  

Комментарии:

1. Разве это не будет предупреждать даже о законных внутренних вызовах _bar изнутри Foo?

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

3. @unutbu: Извините за это. Я надеялся, что вы этого не видели 🙂