«публичный» или «приватный» атрибут в Python?Какой самый лучший способ? - PullRequest
45 голосов
/ 29 декабря 2010

В Python у меня есть следующий пример класса:

class Foo:
    self._attr = 0

    @property
    def attr(self):
        return self._attr

    @attr.setter
    def attr(self, value):
        self._attr = value

    @attr.deleter
    def attr(self):
        del self._attr

Как видите, у меня есть простой "приватный" атрибут "_attr" и свойство для доступа к нему.Существует множество кодов для объявления простого закрытого атрибута, и я думаю, что философия «KISS» не объявляет все атрибуты подобным образом.

Итак, почему бы не объявить все мои атрибуты как открытые атрибуты, если яВам не нужен конкретный метод получения / установки / удаления?

Мой ответ будет таким: потому что принцип инкапсуляции (ООП) говорит об обратном!

Какой путь лучше?

Ответы [ 9 ]

78 голосов
/ 29 декабря 2010

Как правило, код Python стремится придерживаться принципа Uniform Access .В частности, принятым подходом является:

  • Отображение переменных вашего экземпляра напрямую, что позволяет, например, foo.x = 0, а не foo.set_x(0)
  • Если вам нужно обернуть доступы внутри методовпо любой причине используйте @property, который сохраняет семантику доступа.Таким образом, foo.x = 0 теперь вызывает foo.set_x(0).

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

foo.x += 1

, даже если код можетдействительно надо делать:

foo.set_x(foo.get_x() + 1)

Первое утверждение бесконечно более читабельно.Тем не менее, с помощью свойств вы можете добавить (в начале или позже) управление доступом, которое вы получаете при втором подходе.

Обратите также внимание, что переменные экземпляра, начинающиеся с одного подчеркивания, обычно личное.То есть, подчеркивание сигнализирует другим разработчикам, что вы считаете значение частным, и им не следует связываться с ним напрямую;однако ничто в языке не мешает им напрямую связываться с ним.

Если вы используете двойное подчеркивание (например, __x), Python немного запутывает имя.Однако переменная по-прежнему доступна извне класса через его запутанное имя.Это не совсем личное.Это просто ... более непрозрачно.И есть веские аргументы против использования двойного подчеркивания;с одной стороны, это может затруднить отладку.

14 голосов
/ 10 августа 2016

Префикс "dunder" (двойное подчеркивание, __) запрещает доступ к атрибуту, кроме как через средства доступа.

class Foo():
    def __init__(self):
        self.__attr = 0

    @property
    def attr(self):  
        return self.__attr

    @attr.setter
    def attr(self, value):
        self.__attr = value

    @attr.deleter
    def attr(self):
        del self.__attr

Некоторые примеры:

>>> f = Foo()
>>> f.__attr                          # Not directly accessible.
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__attr'
>>> '__attr' in f.__dir__()           # Not listed by __dir__()
False
>>> f.__getattribute__('__attr')      # Not listed by __getattribute__()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__attr'
>>> f.attr                            # Accessible by implemented getter.
0
>>> f.attr = 'Presto'                 # Can be set by implemented setter.
>>> f.attr
'Presto'
>>> f.__attr = 'Tricky?'              # Can we set it explicitly?
>>> f.attr                            # No. By doing that we have created a 
'Presto'                              # new but unrelated attribute, same name.
9 голосов
/ 29 декабря 2010

Проще говоря, принципы ООП неверны.Почему это длинная дискуссия, которая приводит к пламенным войнам и, вероятно, не по теме для этого сайта.: -)

В Python нет личных атрибутов, вы не можете защитить их, и это никогда не является реальной проблемой.Так что не надо.Легко!:)

Тогда возникает вопрос: есть ли у вас подчеркивание или нет.И в приведенном здесь примере вы точно не должны.Подчеркивание в Python - это соглашение, показывающее, что что-то является внутренним, а не частью API, и что вы должны использовать это на свой страх и риск.Это явно не тот случай, но это общее и полезное соглашение.

6 голосов
/ 29 декабря 2010

Python не имеет открытых или закрытых атрибутов. Все атрибуты доступны для всего кода.

self.attr = 0 #Done

Ваш метод никоим образом не делает приватным _attr, это всего лишь запутанность.

4 голосов
/ 27 февраля 2018

См. Ссылку: https://docs.python.org/2/tutorial/classes.html

9,6. Частные переменные и классовые локальные ссылки

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

Поскольку существует действительный вариант использования для закрытых членов класса (а именно, чтобы избежать столкновения имен имен с именами, определенными подклассами), существует ограниченная поддержка такого механизма, называемого искажение имени. Любой идентификатор формы __spam (по крайней мере два начальных подчеркивания, самое большее одно конечное подчеркивание) заменяется в текстовом виде на _classname__spam, где classname - это имя текущего класса с удаленными начальными подчеркиваниями. Такое искажение выполняется без учета синтаксической позиции идентификатора, если оно происходит в определении класса.

4 голосов
/ 30 декабря 2010

Как уже говорили другие, частные атрибуты в Python - это просто соглашение. Использование синтаксиса property следует использовать для специальной обработки, когда атрибуты связаны, изменены или удалены. Прелесть Python в том, что вы можете начать с обычного связывания атрибутов, например, self.attr = 0, и если позднее вы решите ограничить значение attr, скажем, 0 <= attr <=100, вы можете сделать attr свойство и определите метод, чтобы убедиться, что это условие истинно, без необходимости изменения какого-либо пользовательского кода.

1 голос
/ 24 апреля 2016

Чтобы сделать атрибут личным, вам просто нужно сделать self.__attr

class Foo:
    self.__attr = 0

    @property
    def attr(self):
        return self._attr

    @attr.setter
    def attr(self, value):
        self._attr = value

    @attr.deleter
    def attr(self):
        del self._attr
0 голосов
/ 29 декабря 2010

Хорошая особенность свойств в том, что они дали вам действительно классный интерфейс для работы.Иногда удобно получить свойство, основанное на некотором другом (то есть ИМТ определяется весом и ростом).Пользователь интерфейса не должен знать это, конечно.

Я предпочитаю этот способ, чем иметь явные методы получения и установки, как в Java, т.е.Путь приятнее.:)

0 голосов
/ 29 декабря 2010

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

...