Подтвердить что ты не робот

Атрибуты объекта Python - методология доступа

Предположим, что у меня есть класс с некоторыми атрибутами. Как лучше (в Pythonic-OOP) лучше получить доступ к этим атрибутам? Как и obj.attr? Или, может быть, напишите get accessors? Каковы принятые стили именования для таких вещей?

Edit: Можете ли вы рассказать о лучших практиках атрибутов именования с одним или двумя ведущими подчеркиваниями? В большинстве модулей я вижу, что используется одно подчеркивание.


Если этот вопрос уже задан (и у меня есть подозрение, хотя поиск не принес результатов), укажите на него - и я закрою этот.

4b9b3361

Ответ 1

Общепринятый способ делать вещи - это просто использовать простые атрибуты, например

>>> class MyClass:
...     myAttribute = 0
... 
>>> c = MyClass()
>>> c.myAttribute 
0
>>> c.myAttribute = 1
>>> c.myAttribute
1

Если вам действительно нужно иметь возможность писать геттеры и сеттеры, то то, что вы хотите найти, это "свойства класса python" и статья Райана Томайко на Getters/Setters/Fuxors - отличное место для начала (хотя и немного длинное)

Ответ 2

Что касается одного и двух ведущих подчеркиваний: оба указывают на то же понятие "приватности". То есть, люди будут знать атрибут (будь то метод или "нормальный" атрибут данных или что-то еще) не является частью общедоступного API объекта. Люди будут знать, что прикоснуться к ней напрямую - это предложить катастрофу.

Кроме того, атрибуты подчеркивания с двумя ведущими символами подчеркивания (но не только одноликие символы подчеркивания) искажаются по имени, чтобы сделать их случайно доступными из подклассов или где-либо еще за пределами текущего класса менее вероятными. Вы можете получить к ним доступ, но не так, как тривиально. Например:

>>> class ClassA:
...     def __init__(self):
...         self._single = "Single"
...         self.__double = "Double"
...     def getSingle(self):
...         return self._single
...     def getDouble(self):
...         return self.__double
... 
>>> class ClassB(ClassA):
...     def getSingle_B(self):
...         return self._single
...     def getDouble_B(self):
...         return self.__double
... 
>>> a = ClassA()
>>> b = ClassB()

Теперь вы можете тривиально получить доступ к a._single и b._single и получить атрибут _single, созданный ClassA:

>>> a._single, b._single
('Single', 'Single')
>>> a.getSingle(), b.getSingle(), b.getSingle_B()
('Single', 'Single', 'Single')

Но попытка получить доступ к атрибуту __double в экземпляре a или b напрямую не будет работать:

>>> a.__double
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: ClassA instance has no attribute '__double'
>>> b.__double
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: ClassB instance has no attribute '__double'

И хотя методы, определенные в ClassA, могут напрямую обращаться к нему (при вызове в любом экземпляре):

>>> a.getDouble(), b.getDouble()
('Double', 'Double')

Методы, определенные на ClassB, не могут:

>>> b.getDouble_B()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in getDouble_B
AttributeError: ClassB instance has no attribute '_ClassB__double'

И прямо в этой ошибке вы получаете намек на то, что происходит. Имя атрибута __double при обращении к нему внутри класса с именем управляется, чтобы включить имя класса, к которому он обращается. Когда ClassA пытается получить доступ к self.__double, он фактически превращается - в compiletime - в доступ к self._ClassA__double, а также для ClassB. (Если метод ClassB должен был присваивать __double, который не был включен в код для краткости, он не касался бы ClassA __double, а создавал новый атрибут.) Нет другой защиты этого атрибута, поэтому вы можете получить доступ к нему напрямую, если знаете правильное имя:

>>> a._ClassA__double, b._ClassA__double
('Double', 'Double')

Итак, почему это проблема?

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

С другой стороны, поскольку атрибут тривиально переписан, он предлагает только поверхностную "защиту". Любой фрагмент кода все равно может попасть в атрибут вручную, хотя это будет зависеть от кода вашего имени от имени вашего класса и усилий на вашей стороне для реорганизации вашего кода или переименования вашего класса (при сохранении того же видимого пользователя имя, обычная практика в Python), без необходимости разрывали бы свой код. Они также могут "обмануть" Python за то, что они навязывают им имя, назвав свой класс таким же, как ваш: обратите внимание, как нет имени модуля, включенного в имя измененного атрибута. И, наконец, атрибут double-underscore все еще отображается во всех списках атрибутов и всех формах самоанализа, которые не должны пропускать атрибуты, начинающиеся с (одного) подчеркивания.

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

Ответ 3

Изменить: Можете ли вы подробнее рассказать о лучших методах присвоения имен с одним или двумя ведущими подчеркиваниями? В большинстве модулей я вижу, что используется одно подчеркивание.

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

Двойное ведущее и конечное подчеркивание обозначает специальную функцию, такую ​​как __add__, которая вызывается при использовании оператора +.

Подробнее в PEP 8, особенно в разделе "Соглашения об именах".

Ответ 4

Я думаю, что большинство просто обращаются к ним напрямую, нет необходимости в методах get/set.

>>> class myclass:
...     x = 'hello'
...
>>>
>>> class_inst = myclass()
>>> class_inst.x
'hello'
>>> class_inst.x = 'world'
>>> class_inst.x
'world'

Кстати, вы можете использовать функцию dir(), чтобы узнать, какие атрибуты/методы привязаны к вашему экземпляру:

>>> dir(class_inst)
['__doc__', '__module__', 'x']

Два ведущих подлога, "__" используются, чтобы сделать атрибут или функцию закрытой. Для других конвенций см. PEP 08: http://www.python.org/dev/peps/pep-0008/

Ответ 5

Python не требует определения аксессуаров с самого начала, поскольку преобразование атрибутов в свойства является быстрым и безболезненным. Для яркой демонстрации смотрите следующее:

Восстановление от наркомании

Ответ 6

В python нет реальной возможности делать getter/setters, вы все равно не можете защищать вещи, и если вам нужно выполнить дополнительный код при получении/настройке свойства, посмотрите на свойство() builtin (python -c ' помощь (недвижимость))

Ответ 7

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

> __getattr__

и

> __setattr__

методы.

Изменить:

Пока мой ответ по-прежнему верен, это не так, как я понял. Есть лучшие способы сделать аксессуар в python и не очень неудобно.