В котором я отвечаю на вопрос, который был задан
Почему Python не предлагает это из коробки?
Я подозреваю, что это связано с дзен Python : «Должен быть один - и желательно только один - очевидный способ сделать это». Это создаст два очевидных способа доступа к значениям из словарей: obj['key']
и obj.key
.
Предостережения и подводные камни
К ним относятся возможное отсутствие ясности и путаницы в коде. то есть, следующее может ввести в заблуждение кого- то, кто собирается поддержать ваш код на более позднем этапе, или даже вас, если вы не вернетесь к нему на некоторое время. Опять же, из дзен : «Читаемость имеет значение!»
>>> KEY = 'spam'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Если d
создается экземпляр или KEY
определяется или d[KEY]
назначается далеко от того места, где d.spam
он используется, это может легко привести к путанице в отношении того, что делается, поскольку это не часто используемая идиома. Я знаю, что это может сбить меня с толку.
Кроме того, если вы измените значение KEY
следующим образом (но пропустите изменение d.spam
), вы получите:
>>> KEY = 'foo'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: 'C' object has no attribute 'spam'
ИМО, не стоит усилий.
Другие предметы
Как уже отмечали другие, вы можете использовать любой хешируемый объект (не просто строку) в качестве ключа. Например,
>>> d = {(2, 3): True,}
>>> assert d[(2, 3)] is True
>>>
законно, но
>>> C = type('C', (object,), {(2, 3): True})
>>> d = C()
>>> assert d.(2, 3) is True
File "<stdin>", line 1
d.(2, 3)
^
SyntaxError: invalid syntax
>>> getattr(d, (2, 3))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: getattr(): attribute name must be string
>>>
не является. Это дает вам доступ ко всему диапазону печатных символов или других хешируемых объектов для ключей словаря, которых у вас нет при доступе к атрибуту объекта. Это делает возможной такую магию, как метакласс кэшированных объектов, например рецепт из Кулинарной книги Python (гл. 9) .
В котором я редактирую
Я предпочитаю эстетику spam.eggs
более spam['eggs']
(я думаю, она выглядит чище), и я действительно начал жаждать эту функциональность, когда я встретил namedtuple
. Но удобство в том, чтобы сделать следующее, превосходит это.
>>> KEYS = 'spam eggs ham'
>>> VALS = [1, 2, 3]
>>> d = {k: v for k, v in zip(KEYS.split(' '), VALS)}
>>> assert d == {'spam': 1, 'eggs': 2, 'ham': 3}
>>>
Это простой пример, но я часто нахожу себя использующим диктовки в других ситуациях, чем я использую obj.key
нотацию (то есть, когда мне нужно прочитать префы в файле XML). В других случаях, когда я испытываю желание создать экземпляр динамического класса и присвоить ему некоторые атрибуты по эстетическим соображениям, я продолжаю использовать dict для согласованности, чтобы улучшить читаемость.
Я уверен, что ОП давно решил эту проблему к своему удовлетворению, но если он все еще хочет эту функциональность, тогда я предлагаю ему загрузить один из пакетов из pypi, который предоставляет его:
Куча , с которой я больше знаком. Подклассdict
, так что у вас есть все, что функциональность.
AttrDict также выглядит довольно неплохо, но я не настолько знаком с ним и не изучил источник так подробно, как Bunch .
- Аддикт активно поддерживается и предоставляет доступ, подобный атрибуту, и многое другое.
- Как отмечается в комментариях Ротарети, Bunch устарел, но есть активный форк под названием Munch .
Однако, чтобы улучшить читабельность его кода, я настоятельно рекомендую не смешивать его стили обозначений. Если он предпочитает эту запись, он должен просто создать экземпляр динамического объекта, добавить к нему нужные атрибуты и назвать его днем:
>>> C = type('C', (object,), {})
>>> d = C()
>>> d.spam = 1
>>> d.eggs = 2
>>> d.ham = 3
>>> assert d.__dict__ == {'spam': 1, 'eggs': 2, 'ham': 3}
В котором я уточняю, чтобы ответить на дополнительный вопрос в комментариях
В комментариях (ниже) Элмо спрашивает:
Что делать, если вы хотите пойти еще глубже? (имеется в виду тип (...))
Хотя я никогда не использовал этот вариант использования (опять же, я склонен использовать вложенный dict
, для согласованности), работает следующий код:
>>> C = type('C', (object,), {})
>>> d = C()
>>> for x in 'spam eggs ham'.split():
... setattr(d, x, C())
... i = 1
... for y in 'one two three'.split():
... setattr(getattr(d, x), y, i)
... i += 1
...
>>> assert d.spam.__dict__ == {'one': 1, 'two': 2, 'three': 3}
collections.namedtuple
очень полезно для этого.