Атрибуты объекта Python - методология доступа - PullRequest
23 голосов
/ 03 октября 2008

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

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


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

Ответы [ 7 ]

57 голосов
/ 03 октября 2008

Относительно одинарных и двойных подчеркиваний: оба обозначают одно и то же понятие «приватность». То есть люди будут знать, что атрибут (будь то метод, «нормальный» атрибут данных или что-то еще) не является частью общедоступного 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, он фактически превращается - во время компиляции - в доступ self._ClassA__double, и аналогично для ClassB. (Если бы метод из ClassB был назначен для __double, не включенный в код для краткости, он не коснулся бы ClassA __double, но создал бы новый атрибут.) Другой защиты от этот атрибут, так что вы можете получить к нему прямой доступ, если знаете правильное имя:

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

Так почему же это проблема?

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

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

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

23 голосов
/ 03 октября 2008

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

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

Если вам действительно нужно писать геттеры и сеттеры, то вам нужно искать «свойства класса python» и статью Райана Томайко о Геттеры / Сеттеры / Факсоры - отличное место для начала (хотя и немного долго)

8 голосов
/ 03 октября 2008

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

Одно подчеркивание не означает ничего особенного для python, это просто лучшая практика, чтобы сказать: «Эй, ты, вероятно, не хочешь получить к этому доступ, если не знаешь, что делаешь». Двойное подчеркивание, однако, заставляет python искажать имя внутри, делая его доступным только из класса, в котором оно определено.

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

Подробнее читайте в PEP 8 , особенно в разделе «Соглашения об именах».

3 голосов
/ 03 октября 2008

Я думаю, что большинство просто обращаются к ним напрямую, нет необходимости в методах 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/

1 голос
/ 03 октября 2008

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

Восстановление от зависимости

0 голосов
/ 03 октября 2008

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

0 голосов
/ 03 октября 2008

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

> __getattr__

и

> __setattr__

методы.

Edit:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...