Доступ к клавишам dict, как атрибут в Python?

Я нахожу более удобным доступ к ключам dict как obj.foo вместо obj['foo'] , поэтому я написал этот фрагмент:

 class AttributeDict(dict): def __getattr__(self, attr): return self[attr] def __setattr__(self, attr, value): self[attr] = value 

Тем не менее, я полагаю, что должна быть какая-то причина, что Python не предоставляет эту функциональность из коробки. Каковы были бы оговорки и ловушки доступа к ключам диктатора таким образом?

  • Пространства в словарных словах Python
  • добавить несколько значений для одного ключа в словаре Python
  • Как добавить несколько значений в ключ словаря в python?
  • Python: использование vars () для назначения строки переменной
  • перегруженный __iter__ обходит при выводе из dict
  • Разделите значения двух словарей на python
  • Python, как сохранить ключи / значения в том же порядке, что и объявлено?
  • Доступ к значению словаря по индексу в python
  • 21 Solutions collect form web for “Доступ к клавишам dict, как атрибут в Python?”

    Лучший способ сделать это:

     class AttrDict(dict): def __init__(self, *args, **kwargs): super(AttrDict, self).__init__(*args, **kwargs) self.__dict__ = self 

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

    • Это действительно работает!
    • Никакие методы класса словаря не затенены (например, .keys() работают просто отлично)
    • Атрибуты и элементы всегда синхронизируются
    • Попытка получить доступ к несуществующему ключу как атрибуту правильно повышает значение AttributeError вместо KeyError

    Минусы:

    • Такие методы, как .keys() , не будут работать нормально, если они будут перезаписаны входящими данными
    • Вызывает утечку памяти в Python <2.7.4 / Python3 <3.2.3
    • Pylint идет бананы с E1123(unexpected-keyword-arg) и E1103(maybe-no-member)
    • Для непосвященных это кажется чистой магией.

    Краткое объяснение того, как это работает

    • Все объекты python внутренне хранят свои атрибуты в словаре, который называется __dict__ .
    • Нет требования, чтобы внутренний словарь __dict__ должен был быть «просто простым dict», поэтому мы можем назначить любой подкласс dict() для внутреннего словаря.
    • В нашем случае мы просто присваиваем AttrDict() мы создаем (как мы находимся в __init__ ).
    • Вызвав метод __init__() super() мы убедились, что он (уже) ведет себя точно так же, как словарь, поскольку эта функция вызывает весь код экземпляра словаря .

    Одна из причин, почему Python не предоставляет эту функциональность из коробки

    Как отмечено в списке «cons», это объединяет пространство имен хранимых ключей (которое может исходить из произвольных и / или ненадежных данных!) С пространством имен встроенных атрибутов dict-метода. Например:

     d = AttrDict() d.update({'items':["jacket", "necktie", "trousers"]}) for k, v in d.items(): # TypeError: 'list' object is not callable print "Never reached!" 

    Вы можете иметь все юридические строковые символы как часть ключа, если используете нотацию массива. Например, obj['!#$%^&*()_']

    Из этого другого вопроса SO есть отличный пример реализации, который упрощает ваш существующий код. Как насчет:

     class AttributeDict(dict): __getattr__ = dict.__getitem__ __setattr__ = dict.__setitem__ 

    Гораздо более лаконичный и не оставляет места для дополнительного использования в ваших __getattr__ и __setattr__ в будущем.

    В чем я отвечаю на вопрос, который был задан

    Почему Python не предлагает его из коробки?

    Я подозреваю, что это связано с Zen Python : «Должен быть один – и желательно только один – очевидный способ сделать это». Это создаст два очевидных способа доступа к значениям из словарей: obj['key'] и obj.key .

    Предостережения и ошибки

    К ним относятся возможное отсутствие ясности и путаницы в коде. т. е. следующее может смутить кого то другого, кто собирается поддерживать ваш код на более поздний срок или даже на вас, если вы не вернетесь в него некоторое время. Опять же, от Дзэн : «Читаемость считается!»

     >>> KEY = 'foo' >>> d[KEY] = 1 >>> if d.foo == 1: ... ... ... 

    Если d экземпляр или KEY определен, или d[KEY] назначается далеко от того, где используется d.foo , это может легко привести к путанице в отношении того, что делается, поскольку это не обычная идиома. Я знаю, что это может смутить меня.

    Другие предметы

    Как отмечали другие, вы можете использовать любой хешируемый объект (а не только строку) в качестве ключа dict. Например,

     >>> d = {(2, 3): True,} >>> assert d[(2, 3)] is True >>> 

    является законным, но

     >>> C = type('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 >>> 

    не является. Это дает вам доступ ко всему диапазону печатаемых символов или других хешируемых объектов для ваших ключей словаря, которых у вас нет при доступе к атрибуту объекта. Это делает возможной такую ​​магию, как метаклас кешированного объекта, как рецепт из Cookbook Python (глава 9) .

    Где я редактирую

    Я предпочитаю эстетику spam.eggs над spam['eggs'] (я думаю, что он выглядит более чистым), и я действительно начал жаждать эту функциональность, когда встретил namedtuple . Но удобство быть в состоянии сделать следующие козыри.

     >>> KEYS = 'foo bar baz' >>> VALS = [1, 2, 3] >>> d = {k: v for k, v in zip(KEYS.split(' '), VALS)} >>> assert d == {'foo': 1, 'bar': 2, 'baz': 3} >>> 

    Это простой пример, но я часто использую dicts в разных ситуациях, чем использовать нотацию obj.key (т. obj.key Когда мне нужно читать prefs из XML-файла). В других случаях, когда я испытываю соблазн создать экземпляр динамического класса и пощекотать некоторые атрибуты по эстетическим соображениям, я продолжаю использовать dict для согласованности, чтобы повысить удобочитаемость.

    Я уверен, что OP долгое время разрешал это с его удовлетворением, но если он все еще хочет эту функциональность, я предлагаю загрузить один из пакетов из pypi, который предоставляет его:

    • Букет – это тот, с которым я больше знаком. Подкласс dict , поэтому у вас есть все эти функции.
    • AttrDict также выглядит так же хорошо, но я не так хорошо знаком с ним и не просматривал источник настолько подробно, как у меня есть Bunch .
    • Как отмечалось в комментариях Ротарети, Bunch устарел, но есть активная вилка под названием Munch .

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

     >>> C = type('type_C', (object,), {}) >>> d = C() >>> d.foo = 1 >>> d.bar = 2 >>> d.baz = 3 >>> assert d.__dict__ == {'foo': 1, 'bar': 2, 'baz': 3} >>> 

    Что делать, если вам нужен ключ, который был методом, например __eq__ или __getattr__ ?

    И вы не сможете получить запись, которая не начиналась с буквы, поэтому использование 0343853 в качестве ключа отсутствует.

    А что, если вы не хотите использовать строку?

    Caveat emptor: По некоторым причинам подобные классы, похоже, разбивают пакет многопроцессорности. Я просто боролся с этой ошибкой на некоторое время, прежде чем найти это SO: Исключение исключений в многопроцессорности python

    Вы можете вытащить удобный класс контейнера из стандартной библиотеки:

     from argparse import Namespace 

    чтобы избежать копирования кодовых битов. Нет стандартного доступа к словарю, но легко получить его обратно, если вы действительно этого хотите. Код в argparse прост,

     class Namespace(_AttributeHolder): """Simple object for storing attributes. Implements equality by attribute names and values, and provides a simple string representation. """ def __init__(self, **kwargs): for name in kwargs: setattr(self, name, kwargs[name]) __hash__ = None def __eq__(self, other): return vars(self) == vars(other) def __ne__(self, other): return not (self == other) def __contains__(self, key): return key in self.__dict__ 

    Кортежи можно использовать для ключей dict. Как вы получите доступ к кортежу в своей конструкции?

    Кроме того, namedtuple – удобная структура, которая может предоставлять значения через доступ к атрибуту.

    Это не работает в общности. Не все действительные ключи dict образуют адресные атрибуты («ключ»). Итак, вам нужно быть осторожным.

    Объекты Python – это в основном словари. Поэтому я сомневаюсь, что есть большая производительность или другое наказание.

    Не нужно писать свои собственные, поскольку setattr () и getattr () уже существуют.

    Преимущество объектов класса, вероятно, вступает в игру в определении класса и наследовании.

    Я создал это на основе ввода из этого потока. Мне нужно использовать odict, хотя, поэтому мне пришлось переопределить get и установить attr. Я думаю, что это должно работать для большинства специальных целей.

    Использование выглядит следующим образом:

     # Create an ordered dict normally... >>> od = OrderedAttrDict() >>> od["a"] = 1 >>> od["b"] = 2 >>> od OrderedAttrDict([('a', 1), ('b', 2)]) # Get and set data using attribute access... >>> od.a 1 >>> od.b = 20 >>> od OrderedAttrDict([('a', 1), ('b', 20)]) # Setting a NEW attribute only creates it on the instance, not the dict... >>> od.c = 8 >>> od OrderedAttrDict([('a', 1), ('b', 20)]) >>> od.c 8 

    Класс:

     class OrderedAttrDict(odict.OrderedDict): """ Constructs an odict.OrderedDict with attribute access to data. Setting a NEW attribute only creates it on the instance, not the dict. Setting an attribute that is a key in the data will set the dict data but will not create a new instance attribute """ def __getattr__(self, attr): """ Try to get the data. If attr is not a key, fall-back and get the attr """ if self.has_key(attr): return super(OrderedAttrDict, self).__getitem__(attr) else: return super(OrderedAttrDict, self).__getattr__(attr) def __setattr__(self, attr, value): """ Try to set the data. If attr is not a key, fall-back and set the attr """ if self.has_key(attr): super(OrderedAttrDict, self).__setitem__(attr, value) else: super(OrderedAttrDict, self).__setattr__(attr, value) 

    Это довольно крутой шаблон, уже упомянутый в потоке, но если вы просто хотите взять dict и преобразовать его в объект, который работает с автозаполнением в среде IDE и т. Д .:

     class ObjectFromDict(object): def __init__(self, d): self.__dict__ = d 

    По-видимому, теперь для этого есть библиотека – https://pypi.python.org/pypi/attrdict – которая реализует эту точную функциональность плюс рекурсивное слияние и загрузку json. Возможно, стоит посмотреть.

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

    Addict это отличная библиотека для этого: https://github.com/mewwts/addict, он заботится о многих проблемах, упомянутых в предыдущих ответах.

    Пример из документов:

     body = { 'query': { 'filtered': { 'query': { 'match': {'description': 'addictive'} }, 'filter': { 'term': {'created_by': 'Mats'} } } } } 

    С наркоманом:

     from addict import Dict body = Dict() body.query.filtered.query.match.description = 'addictive' body.query.filtered.filter.term.created_by = 'Mats' 

    Вот краткий пример неизменяемых записей с использованием встроенных collections.namedtuple :

     def record(name, d): return namedtuple(name, d.keys())(**d) 

    и пример использования:

     rec = record('Model', { 'train_op': train_op, 'loss': loss, }) print rec.loss(..) 
     class AttrDict(dict): def __init__(self): self.__dict__ = self if __name__ == '____main__': d = AttrDict() d['ray'] = 'hope' d.sun = 'shine' >>> Now we can use this . notation print d['ray'] print d.sun 

    Как отметил Doug, есть пакет Bunch, который вы можете использовать для достижения функциональности obj.key . На самом деле есть более новая версия, называемая

    NeoBunch

    У него есть отличная возможность конвертировать ваш dict в объект NeoBunch через функцию neobunchify . Я часто использую шаблоны Mako и передаю данные, поскольку объекты NeoBunch делают их более читабельными, поэтому, если вы, оказывается, закончите использовать обычный dict в своей программе Python, но хотите, чтобы нотация точек в шаблоне Mako вы могли использовать так:

     from mako.template import Template from neobunch import neobunchify mako_template = Template(filename='mako.tmpl', strict_undefined=True) data = {'tmpl_data': [{'key1': 'value1', 'key2': 'value2'}]} with open('out.txt', 'w') as out_file: out_file.write(mako_template.render(**neobunchify(data))) 

    И шаблон Мако может выглядеть так:

     % for d in tmpl_data: Column1 Column2 ${d.key1} ${d.key2} % endfor 

    Это не «хороший» ответ, но я думал, что это отличное (он не обрабатывает вложенные слова в текущей форме). Просто оберните свой dict в функцию:

     def make_funcdict(d={}, **kwargs) def funcdict(d={}, **kwargs): funcdict.__dict__.update(d) funcdict.__dict__.update(kwargs) return funcdict.__dict__ funcdict(d, **kwargs) return funcdict 

    Теперь у вас немного другой синтаксис. Для доступа к элементам dict в качестве атрибутов используется f.key . Чтобы получить доступ к элементам dict (и другим методам dict) обычным способом, сделайте f()['key'] и мы можем удобно обновить dict, вызвав f с помощью аргументов ключевого слова и / или словаря

    пример

     d = {'name':'Henry', 'age':31} d = make_funcdict(d) >>> for key in d(): ... print key ... age name >>> print d.name ... Henry >>> print d.age ... 31 >>> d({'Height':'5-11'}, Job='Carpenter') ... {'age': 31, 'name': 'Henry', 'Job': 'Carpenter', 'Height': '5-11'} 

    И вот оно. Я буду рад, если кто-то предложит преимущества и недостатки этого метода.

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

     class Map(dict): """ Example: m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer']) """ def __init__(self, *args, **kwargs): super(Map, self).__init__(*args, **kwargs) for arg in args: if isinstance(arg, dict): for k, v in arg.iteritems(): self[k] = v if kwargs: for k, v in kwargs.iteritems(): self[k] = v def __getattr__(self, attr): return self.get(attr) def __setattr__(self, key, value): self.__setitem__(key, value) def __setitem__(self, key, value): super(Map, self).__setitem__(key, value) self.__dict__.update({key: value}) def __delattr__(self, item): self.__delitem__(item) def __delitem__(self, key): super(Map, self).__delitem__(key) del self.__dict__[key] 

    Примеры использования:

     m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer']) # Add new key m.new_key = 'Hello world!' print m.new_key print m['new_key'] # Update values m.new_key = 'Yay!' # Or m['new_key'] = 'Yay!' # Delete key del m.new_key # Or del m['new_key'] 

    Решение:

     DICT_RESERVED_KEYS = vars(dict).keys() class SmartDict(dict): """ A Dict which is accessible via attribute dot notation """ def __init__(self, *args, **kwargs): """ :param args: multiple dicts ({}, {}, ..) :param kwargs: arbitrary keys='value' If ``keyerror=False`` is passed then not found attributes will always return None. """ super(SmartDict, self).__init__() self['__keyerror'] = kwargs.pop('keyerror', True) [self.update(arg) for arg in args if isinstance(arg, dict)] self.update(kwargs) def __getattr__(self, attr): if attr not in DICT_RESERVED_KEYS: if self['__keyerror']: return self[attr] else: return self.get(attr) return getattr(self, attr) def __setattr__(self, key, value): if key in DICT_RESERVED_KEYS: raise TypeError("You cannot set a reserved name as attribute") self.__setitem__(key, value) def __copy__(self): return self.__class__(self) def copy(self): return self.__copy__() 

    Позвольте мне опубликовать еще одну реализацию, которая основывается на ответе Кинваиса, но объединяет идеи из AttributeDict, предложенные в http://databio.org/posts/python_AttributeDict.html .

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

     class AttrDict(dict): """ A class to convert a nested Dictionary into an object with key-values that are accessible using attribute notation (AttrDict.attribute) instead of key notation (Dict["key"]). This class recursively sets Dicts to objects, allowing you to recurse down nested dicts (like: AttrDict.attr.attr) """ # Inspired by: # http://stackoverflow.com/a/14620633/1551810 # http://databio.org/posts/python_AttributeDict.html def __init__(self, iterable, **kwargs): super(AttrDict, self).__init__(iterable, **kwargs) for key, value in iterable.items(): if isinstance(value, dict): self.__dict__[key] = AttrDict(value) else: self.__dict__[key] = value 

    Чтобы добавить некоторое разнообразие в ответ, научный сборник sci-kit , это реализовано как Bunch :

     class Bunch(dict): """ Scikit Learn's container object Dictionary-like object that exposes its keys as attributes. >>> b = Bunch(a=1, b=2) >>> b['b'] 2 >>> bb 2 >>> bc = 6 >>> b['c'] 6 """ def __init__(self, **kwargs): super(Bunch, self).__init__(kwargs) def __setattr__(self, key, value): self[key] = value def __dir__(self): return self.keys() def __getattr__(self, key): try: return self[key] except KeyError: raise AttributeError(key) def __setstate__(self, state): pass 

    Все, что вам нужно, это получить методы setattr и getattr – проверка getattr для ключей dict и getattr на проверку фактических атрибутов. setstaet – это исправление для исправления для травления / рассыпания «сгустков» – если inerested проверяет https://github.com/scikit-learn/scikit-learn/issues/6196

    Python - лучший язык программирования в мире.