Вопрос

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

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


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

Это было полезно?

Решение

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

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

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

Другие советы

Что касается одинарных и двойных ведущих подчеркиваний:оба они указывают на одну и ту же концепцию "приватности".То есть люди будут знать, что атрибут (будь то метод, "обычный" атрибут данных или что-либо еще) не является частью общедоступного 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's __double но создайте новый атрибут.) У этого атрибута нет другой защиты, поэтому вы все равно можете получить к нему прямой доступ, если знаете правильное имя:

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

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

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

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

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

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

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

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

Подробнее читайте в БОДРОСТЬ ДУХА 8, особенно раздел "Соглашения об именовании".

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

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

Избавление от зависимости

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

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

> __getattr__

и

> __setattr__

методы.

Редактировать:

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top