Как использовать точку «.» получить доступ к членам словаря?


284

Как сделать элементы словаря Python доступными через точку "."?

Например, вместо того, чтобы писать mydict['val'], я хотел бы написать mydict.val.

Также я хотел бы получить доступ к вложенным диктовкам таким образом. Например

mydict.mydict2.val 

будет относиться к

mydict = { 'mydict2': { 'val': ... } }

20
Многие из ситуаций, когда люди используют вложенные диктовки, будут также лучше или лучше обслуживаться диктовками с кортежами в качестве ключей, где их d[a][b][c]заменяет d[a, b, c].
Майк Грэм

7
Это не волшебство: foo = {}; foo [1,2,3] = "раз, два, три!"; foo.keys () => [(1,2,3)]
Брайан Оукли

10
Вот это да. Вау снова. Я не знал, что кортежи могут быть ключом к диктату. Ух ты третий раз.
bodacydo

3
Любой объект, который является «хэшируемым», может быть использован в качестве ключа dict. Большинство неизменяемых объектов также являются хэшируемыми, но только если все их содержимое является хэшируемым. Код d [1, 2, 3] работает потому, что "," - это оператор создания кортежа; это то же самое, что и d [(1, 2, 3)]. Скобки часто необязательны при объявлении кортежа.
Ларри Гастингс

6
Рассматривали ли вы случай, когда ключ имеет точку сам по себе - {"my.key":"value"}? Или когда ключом является ключевое слово, например «от»? Я рассматривал это пару раз, и это больше проблем и устранения неполадок, чем предполагаемых преимуществ.
Тодор Минаков

Ответы:


147

Вы можете сделать это, используя этот класс, который я только что сделал. С этим классом вы можете использовать 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!'
# Or
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']

21
Для работы над Python 3 я обновился .iteritems()до.items()
berto

13
Обратите внимание, что это будет вести себя иначе, чем обычные ожидания, в том смысле, что оно не будет расти, AttributeErrorесли атрибут не существует. Вместо этого он вернется None.
mic_e

Рекомендуем добавить getstate и setstate, чтобы глубокие копии и другие системы могли его поддерживать.
user1363990

4
Вы можете упростить ваш конструктор до self.update(*args,**kwargs). Также вы можете добавить __missing__(self,key): value=self[key]= type(self)(); return value. Затем вы можете добавить недостающие записи, используя точечную запись. Если вы хотите, чтобы его можно было выбрать, вы можете добавить __getstate__и__setstate__
Jens Munk

1
Это сделало бы hasattr(Map, 'anystring') is true. which means the hasattr would always return True due to overriding __getattr__`
Сяо

266

Я всегда держал это в файле утилит. Вы также можете использовать его как миксин на своих собственных занятиях.

class dotdict(dict):
    """dot.notation access to dictionary attributes"""
    __getattr__ = dict.get
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

mydict = {'val':'it works'}
nested_dict = {'val':'nested works too'}
mydict = dotdict(mydict)
mydict.val
# 'it works'

mydict.nested = dotdict(nested_dict)
mydict.nested.val
# 'nested works too'

5
Очень простой ответ, отлично! Вы случайно не знаете, что мне нужно для того, чтобы завершить табуляцию в IPython? Класс должен был бы реализовать __dir __ (self), но почему-то я не могу заставить его работать.
Андреас-ч

8
+1 для простоты. но, похоже, не работает на вложенных диктов. d = {'foo': {'bar': 'baz'}}; d = dotdict(d); d.foo.barвыдает ошибку атрибута, но d.fooработает нормально.
tmthyjames

2
Да, это не работает для сложных вложенных структур.
Дэвид

17
@tmthyjames, вы можете просто вернуть объект типа dotdict в методе getter для рекурсивного доступа к атрибутам с точечной нотацией, например: python class DotDict(dict): """dot.notation access to dictionary attributes""" def __getattr__(*args): val = dict.get(*args) return DotDict(val) if type(val) is dict else val __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__
TMKasun

4
После экспериментов с ним кажется, что getэто действительно плохая идея, поскольку она будет возвращаться Noneвместо того, чтобы
выдавать

117

устанавливать dotmap черезpip

pip install dotmap

Он делает все, что вы хотите, и подклассы dict, поэтому он работает как обычный словарь:

from dotmap import DotMap

m = DotMap()
m.hello = 'world'
m.hello
m.hello += '!'
# m.hello and m['hello'] now both return 'world!'
m.val = 5
m.val2 = 'Sam'

Кроме того, вы можете преобразовать его в и из dict объектов:

d = m.toDict()
m = DotMap(d) # automatic conversion in constructor

Это означает, что если что-то, к чему вы хотите получить доступ, уже находится в dict форме, вы можете превратить его в DotMapпростой для доступа:

import json
jsonDict = json.loads(text)
data = DotMap(jsonDict)
print data.location.city

Наконец, он автоматически создает нового ребенка DotMap экземпляры, чтобы вы могли делать такие вещи:

m = DotMap()
m.people.steve.age = 31

Сравнение с кучей

Полное раскрытие: я создатель DotMap . Я создал это, потому чтоBunch не хватало этих возможностей

  • запоминание элементов заказа добавляются и повторяются в этом порядке
  • автоматический ребенок DotMap создание элементов, которое экономит время и делает код чище, когда у вас много иерархии
  • построение из dictи рекурсивное преобразование всех дочерних dictэкземпляров вDotMap

2
:-) вы можете заставить его работать с ключами, которые уже имеют точку в имени? {"test.foo": "bar"}можно получить доступ через mymap.test.fooЭто было бы фантастически. Потребуется некоторое восстановление, чтобы преобразовать плоскую карту в глубокую карту, а затем применить к ней DotMap, но это того стоит!
dlite922

Ухоженная. Есть ли какой-нибудь способ заставить листинг / заполнение вкладок работать с клавишами в блокноте Jupyter? Точечный доступ является наиболее ценным для интерактивного использования.
Дмитрий

@Dmitri Классный продукт. Никогда не слышал об этом раньше, поэтому я не уверен, как заставить его автозаполнение работать. Я согласен с использованием DotMapавтозаполнения работает лучше всего. Я использую Sublime Text, который автозаполняет ранее набранные ключевые слова.
Крис Редфорд

1
Я считаю, что ему не хватает словарного извлечения для таких вещей, как **kwargsили c = {**a, **b}. На самом деле, он не работает тихо, он ведет себя как пустой словарь при извлечении.
Саймон Штрайхер

@SimonStreicher Я проверил это m = DotMap(); m.a = 2; m.b = 3; print('{a} {b}'.format(**m));и получил ожидаемое 2 3. Если у вас есть проверенный случай, который работает, dict()но не работает DotMap(), отправьте код на вкладку «Проблемы» в GitHub.
Крис Редфорд

56

Получите из dict и и осуществите __getattr__и __setattr__.

Или вы можете использовать Bunch, который очень похож.

Я не думаю, что это возможно, чтобы monkeypatch встроенный класс dict.


2
Что именно означает Monkeypatch? Я слышал об этом, но не использовал. (Извините, что задаю такие вопросы новичкам, я еще не настолько хорош в программировании (я всего лишь студент 2-го курса.))
bodacydo

9
Monkeypatching использует динамичность Python (или любого другого языка), чтобы изменить то, что обычно определяется в исходном коде. Это особенно относится к изменению определения классов после их создания.
Майк Грэм

Если вы часто используете эту функциональность, остерегайтесь скорости Bunch. Я использовал его довольно часто, и это заняло треть моего времени запроса. Проверьте мой ответ для более подробного объяснения этого.
JayD3e

22

У Fabric действительно хорошая минимальная реализация . Расширяя это, чтобы разрешить вложенный доступ, мы можем использовать a defaultdict, и результат будет выглядеть примерно так:

from collections import defaultdict

class AttributeDict(defaultdict):
    def __init__(self):
        super(AttributeDict, self).__init__(AttributeDict)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(key)

    def __setattr__(self, key, value):
        self[key] = value

Используйте это следующим образом:

keys = AttributeDict()
keys.abc.xyz.x = 123
keys.abc.xyz.a.b.c = 234

Это конкретизирует немного на ответ Кугель о «проистекают из Dict и и осуществлять __getattr__и __setattr__». Теперь ты знаешь как!


1
Это потрясающе!
Томас Клингер

Приятно включить defaultdict - однако это, кажется, работает только при запуске dict с нуля. Если нам нужно преобразовать существующий dict в «dotdict» рекурсивно. Вот альтернатива, dotdictкоторая позволяет dictрекурсивно преобразовывать существующий объект: gist.github.com/miku/…
miku

19

Я попробовал это:

class dotdict(dict):
    def __getattr__(self, name):
        return self[name]

ты __getattribute__тоже можешь попробовать

сделайте каждый dict типом dotdict достаточно хорошо, если вы хотите инициировать это из многослойного dict, попробуйте также реализовать __init__.


К сожалению, ответ @Kugel похож.
tdihp

1
tdihp, мне все еще нравится ваш ответ, потому что я понял его быстрее - в нем есть реальный код.
Игаль

1
+1 за фактический код. Но предложение @ Kugel об использовании Bunch также очень хорошо.
Даннид

Я нашел полезным встроить это в функцию, поместив def docdict(name):перед ней и затем `if isinstance (name, dict): return DotDict (name) возвращаемое имя`
Даниэль Москович

отличный простой пример .. Я немного расширил это так, чтобы вложенный dict легко связывался по цепочке, аналогично @DanielMoskovich, но также правильно возвращал листовые узлы для int, string и т. д. или ноль, если не найденclass dotdict(dict): def __getattr__(self, name): if name not in self: return None elif type(self[name]) is dict: return JsonDot(self[name]) else: return self[name]
D Sievers,

11

Не. Доступ к атрибутам и индексация - это разные вещи в Python, и вам не следует, чтобы они выполняли то же самое. Создайте класс (возможно, созданный namedtuple), если у вас есть что-то, что должно иметь доступные атрибуты, и используйте []нотацию, чтобы получить элемент из указания.


Спасибо за ответ. Но взгляните на этот вопрос, который я только что задал: stackoverflow.com/questions/2352252/… Похоже, это хорошая идея, чтобы использовать ее .вместо []доступа к сложным структурам данных в шаблонах Mako.
bodacydo

2
Я вижу пример использования для этого; на самом деле, я сделал это всего пару недель назад. В моем случае я хотел объект, который я мог получить доступ к атрибутам с точечной нотацией. Мне было очень просто просто наследовать от dict, поэтому я получил все встроенные функции dict, но открытый интерфейс этого объекта использует точечную нотацию (по сути это интерфейс только для чтения для некоторых статических данных). Мои пользователи гораздо счастливее с 'foo.bar', чем с 'foo ["bar"] ", и я счастлив, что смогу воспользоваться возможностями типа dict.
Брайан Оукли

10
Вы уже знаете хороший стиль Python: мы говорим вам, не притворяйтесь, что значения dict являются атрибутами. Это плохая практика. Например, что если вы хотите сохранить значение с тем же именем, что и существующий атрибут dict, например «items», «get» или «pop»? Возможно, что-то сбивает с толку. Так что не делай этого!
Ларри Гастингс

5
К сожалению, я забыл об атрибутах, таких как «items», «get» или «pop». Спасибо за то, что привели этот важный пример!
бодасидо

5
@ Габе, это было давно ... но я думаю, что стоит сказать. Это не «достаточно хорошо в JS»: это «достаточно ужасно в JS». Забавно, когда вы храните ключи / атрибуты, имена которых совпадают с именами других важных атрибутов в цепочке прототипов.
bgusach

11

Если вы хотите выбрать измененный словарь, вам нужно добавить несколько методов состояния к ответам выше:

class DotDict(dict):
    """dot.notation access to dictionary attributes"""
    def __getattr__(self, attr):
        return self.get(attr)
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

    def __getstate__(self):
        return self

    def __setstate__(self, state):
        self.update(state)
        self.__dict__ = self

Спасибо за комментарий о мариновании. Эта ошибка свела меня с ума, и я понял только, что это из-за этой проблемы!
Шагру

Также происходит, когда вы используете copy.deepcopy. Это дополнение необходимо.
user1363990

Упрощение:__getattr__ = dict.get
Мартино

9

Основываясь на ответе Кугеля и принимая во внимание слова предостережения Майка Грэма, что, если мы сделаем обертку?

class DictWrap(object):
  """ Wrap an existing dict, or create a new one, and access with either dot 
    notation or key lookup.

    The attribute _data is reserved and stores the underlying dictionary.
    When using the += operator with create=True, the empty nested dict is 
    replaced with the operand, effectively creating a default dictionary
    of mixed types.

    args:
      d({}): Existing dict to wrap, an empty dict is created by default
      create(True): Create an empty, nested dict instead of raising a KeyError

    example:
      >>>dw = DictWrap({'pp':3})
      >>>dw.a.b += 2
      >>>dw.a.b += 2
      >>>dw.a['c'] += 'Hello'
      >>>dw.a['c'] += ' World'
      >>>dw.a.d
      >>>print dw._data
      {'a': {'c': 'Hello World', 'b': 4, 'd': {}}, 'pp': 3}

  """

  def __init__(self, d=None, create=True):
    if d is None:
      d = {}
    supr = super(DictWrap, self)  
    supr.__setattr__('_data', d)
    supr.__setattr__('__create', create)

  def __getattr__(self, name):
    try:
      value = self._data[name]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[name] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value

  def __setattr__(self, name, value):
    self._data[name] = value  

  def __getitem__(self, key):
    try:
      value = self._data[key]
    except KeyError:
      if not super(DictWrap, self).__getattribute__('__create'):
        raise
      value = {}
      self._data[key] = value

    if hasattr(value, 'items'):
      create = super(DictWrap, self).__getattribute__('__create')
      return DictWrap(value, create)
    return value

  def __setitem__(self, key, value):
    self._data[key] = value

  def __iadd__(self, other):
    if self._data:
      raise TypeError("A Nested dict will only be replaced if it's empty")
    else:
      return other

8

Используйте SimpleNamespace:

>>> from types import SimpleNamespace   
>>> d = dict(x=[1, 2], y=['a', 'b'])
>>> ns = SimpleNamespace(**d)
>>> ns.x
[1, 2]
>>> ns
namespace(x=[1, 2], y=['a', 'b'])

1
Этот подход работает лучше. (с JSON загружен из файла)
GED

Учитывает ли это вложенные дикты?
Моджими

1
Не поддерживает вложенный Dict. docs.python.org/3.3/library/types.html#types.SimpleNamespace
Карсон

6

Мне нравится Munch, и он дает множество удобных опций поверх доступа к точке.

импорт мунк

temp_1 = {'person': {'fname': 'senthil', 'lname': 'ramalingam'}}

dict_munch = munch.munchify (temp_1)

dict_munch.person.fname


6

Недавно я наткнулся на библиотеку « Box », которая делает то же самое.

Команда установки: pip install python-box

Пример:

from box import Box

mydict = {"key1":{"v1":0.375,
                    "v2":0.625},
          "key2":0.125,
          }
mydict = Box(mydict)

print(mydict.key1.v1)

Я обнаружил, что он более эффективен, чем другие существующие библиотеки, такие как dotmap, которые генерируют ошибку рекурсии Python, когда у вас большие вложенные запросы.

ссылка на библиотеку и подробности: https://pypi.org/project/python-box/


5

Используйте __getattr__, очень просто, работает в Python 3.4.3

class myDict(dict):
    def __getattr__(self,val):
        return self[val]


blockBody=myDict()
blockBody['item1']=10000
blockBody['item2']="StackOverflow"
print(blockBody.item1)
print(blockBody.item2)

Вывод:

10000
StackOverflow

4

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

def get_var(input_dict, accessor_string):
    """Gets data from a dictionary using a dotted accessor-string"""
    current_data = input_dict
    for chunk in accessor_string.split('.'):
        current_data = current_data.get(chunk, {})
    return current_data

который бы поддерживал что-то вроде этого:

>> test_dict = {'thing': {'spam': 12, 'foo': {'cheeze': 'bar'}}}
>> output = get_var(test_dict, 'thing.spam.foo.cheeze')
>> print output
'bar'
>>

4

Чтобы основываться на ответе epool, эта версия позволяет вам получить доступ к любому внутреннему диктату через оператор точки:

foo = {
    "bar" : {
        "baz" : [ {"boo" : "hoo"} , {"baba" : "loo"} ]
    }
}

Например, foo.bar.baz[1].babaвозвращает "loo".

class Map(dict):
    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():
                    if isinstance(v, dict):
                        v = Map(v)
                    if isinstance(v, list):
                        self.__convert(v)
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                if isinstance(v, dict):
                    v = Map(v)
                elif isinstance(v, list):
                    self.__convert(v)
                self[k] = v

    def __convert(self, v):
        for elem in xrange(0, len(v)):
            if isinstance(v[elem], dict):
                v[elem] = Map(v[elem])
            elif isinstance(v[elem], list):
                self.__convert(v[elem])

    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]

1
Python 3: заменить iteritems()на items()и xrange()сrange()
sasawatc

3
def dict_to_object(dick):
    # http://stackoverflow.com/a/1305663/968442

    class Struct:
        def __init__(self, **entries):
            self.__dict__.update(entries)

    return Struct(**dick)

Если кто-то решит окончательно преобразовать это dictв объект, это должно быть сделано. Вы можете создать одноразовый объект непосредственно перед доступом.

d = dict_to_object(d)

def attr (** kwargs): o = лямбда: нет o .__ dict __. update (** kwargs) return o
throws_exceptions_at_you

2

Я закончил тем, что пытался ОБА AttrDict и Букетбиблиотеки и обнаружили, что они могут быть медленными для моего использования. После того, как мы с другом изучили его, мы обнаружили, что основной метод написания этих библиотек приводит к агрессивной рекурсии библиотеки через вложенный объект и копированию объекта словаря. Имея это в виду, мы сделали два ключевых изменения. 1) Мы сделали атрибуты с отложенной загрузкой 2) вместо создания копий словарного объекта, мы создаем копии легкого прокси-объекта. Это окончательная реализация. Увеличение производительности при использовании этого кода невероятно. При использовании AttrDict или Bunch только эти две библиотеки потребляли 1/2 и 1/3 соответственно моего времени запроса (что !?). Этот код сократил это время практически до нуля (где-то в диапазоне 0,5 мс). Это, конечно, зависит от ваших потребностей, но если вы используете эту функцию в своем коде,

class DictProxy(object):
    def __init__(self, obj):
        self.obj = obj

    def __getitem__(self, key):
        return wrap(self.obj[key])

    def __getattr__(self, key):
        try:
            return wrap(getattr(self.obj, key))
        except AttributeError:
            try:
                return self[key]
            except KeyError:
                raise AttributeError(key)

    # you probably also want to proxy important list properties along like
    # items(), iteritems() and __len__

class ListProxy(object):
    def __init__(self, obj):
        self.obj = obj

    def __getitem__(self, key):
        return wrap(self.obj[key])

    # you probably also want to proxy important list properties along like
    # __iter__ and __len__

def wrap(value):
    if isinstance(value, dict):
        return DictProxy(value)
    if isinstance(value, (tuple, list)):
        return ListProxy(value)
    return value

Смотрите оригинальную реализацию здесь по адресу https://stackoverflow.com/users/704327/michael-merickel .

Следует также отметить, что эта реализация довольно проста и не реализует все методы, которые могут вам понадобиться. Вам нужно будет написать их, как требуется, для объектов DictProxy или ListProxy.


0

Я хотел бы бросить свое собственное решение в кольцо:

https://github.com/skorokithakis/jsane

Он позволяет вам анализировать JSON во что-то, к чему вы можете получить доступ with.attribute.lookups.like.this.r(), в основном потому, что я не видел этот ответ до того, как начал работать над ним.


Python виновен в нескольких простых досадных ошибках проектирования, повышение KeyError- одна из них. Когда один доступ к ключу, который не существует, все, что ему нужно сделать, это вернуться, Noneкак в JS. Я большой поклонник автовивификации как для чтения, так и для письма. Ваша библиотека ближе всего к идеалу.
17

0

Не прямой ответ на вопрос ОП, но вдохновленный и, возможно, полезный для некоторых .. Я создал объектно-ориентированное решение с использованием внутреннего __dict__(ни в коем случае не оптимизированного кода)

payload = {
    "name": "John",
    "location": {
        "lat": 53.12312312,
        "long": 43.21345112
    },
    "numbers": [
        {
            "role": "home",
            "number": "070-12345678"
        },
        {
            "role": "office",
            "number": "070-12345679"
        }
    ]
}


class Map(object):
    """
    Dot style access to object members, access raw values
    with an underscore e.g.

    class Foo(Map):
        def foo(self):
            return self.get('foo') + 'bar'

    obj = Foo(**{'foo': 'foo'})

    obj.foo => 'foobar'
    obj._foo => 'foo'

    """

    def __init__(self, *args, **kwargs):
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self.__dict__[k] = v
                    self.__dict__['_' + k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self.__dict__[k] = v
                self.__dict__['_' + k] = v

    def __getattribute__(self, attr):
        if hasattr(self, 'get_' + attr):
            return object.__getattribute__(self, 'get_' + attr)()
        else:
            return object.__getattribute__(self, attr)

    def get(self, key):
        try:
            return self.__dict__.get('get_' + key)()
        except (AttributeError, TypeError):
            return self.__dict__.get(key)

    def __repr__(self):
        return u"<{name} object>".format(
            name=self.__class__.__name__
        )


class Number(Map):
    def get_role(self):
        return self.get('role')

    def get_number(self):
        return self.get('number')


class Location(Map):
    def get_latitude(self):
        return self.get('lat') + 1

    def get_longitude(self):
        return self.get('long') + 1


class Item(Map):
    def get_name(self):
        return self.get('name') + " Doe"

    def get_location(self):
        return Location(**self.get('location'))

    def get_numbers(self):
        return [Number(**n) for n in self.get('numbers')]


# Tests

obj = Item({'foo': 'bar'}, **payload)

assert type(obj) == Item
assert obj._name == "John"
assert obj.name == "John Doe"
assert type(obj.location) == Location
assert obj.location._lat == 53.12312312
assert obj.location._long == 43.21345112
assert obj.location.latitude == 54.12312312
assert obj.location.longitude == 44.21345112

for n in obj.numbers:
    assert type(n) == Number
    if n.role == 'home':
        assert n.number == "070-12345678"
    if n.role == 'office':
        assert n.number == "070-12345679"

0

Один простой способ получить точечный доступ (но не доступ к массиву) - использовать простой объект в Python. Как это:

class YourObject:
    def __init__(self, *args, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

... и используйте это так:

>>> obj = YourObject(key="value")
>>> print(obj.key)
"value"

... чтобы преобразовать его в диктовку:

>>> print(obj.__dict__)
{"key": "value"}

0

Это решение является усовершенствованным по сравнению с тем, которое предлагает epool, чтобы удовлетворить требование OP о последовательном доступе к вложенным диктовкам. Решение от epool не позволило получить доступ к вложенным dicts.

class YAMLobj(dict):
    def __init__(self, args):
        super(YAMLobj, self).__init__(args)
        if isinstance(args, dict):
            for k, v in args.iteritems():
                if not isinstance(v, dict):
                    self[k] = v
                else:
                    self.__setattr__(k, YAMLobj(v))


    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(YAMLobj, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(YAMLobj, self).__delitem__(key)
        del self.__dict__[key]

С помощью этого класса, теперь можно сделать что - то вроде: A.B.C.D.


0

Это также работает с вложенными диктовками и гарантирует, что добавляемые позже диктанты ведут себя одинаково:

class DotDict(dict):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Recursively turn nested dicts into DotDicts
        for key, value in self.items():
            if type(value) is dict:
                self[key] = DotDict(value)

    def __setitem__(self, key, item):
        if type(item) is dict:
            item = DotDict(item)
        super().__setitem__(key, item)

    __setattr__ = __setitem__
    __getattr__ = dict.__getitem__

0

Ответ @ derek73 очень аккуратный, но его нельзя мариновать или (глубоко) копировать, и он возвращаетNone отсутствующие ключи. Код ниже исправляет это.

Изменить: я не видел ответ выше, который касается точно такой же точки (голосование). Я оставляю ответ здесь для справки.

class dotdict(dict):
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

-1

Решение вид деликатный

class DotDict(dict):

    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __getattr__(self, key):

        def typer(candidate):
            if isinstance(candidate, dict):
                return DotDict(candidate)

            if isinstance(candidate, str):  # iterable but no need to iter
                return candidate

            try:  # other iterable are processed as list
                return [typer(item) for item in candidate]
            except TypeError:
                return candidate

            return candidate

        return typer(dict.get(self, key))
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.