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

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

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

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

mydict.mydict2.val 

будет ссылаться на

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

Ответ 1

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

Ответ 2

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

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'

Ответ 3

Установите 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

Ответ 4

Вывести из dict и реализовать __getattr__ и __setattr__.

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

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

Ответ 5

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

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

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

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

Ответ 6

Ткань имеет действительно приятную, минимальную реализацию. Расширяя это, чтобы разрешить вложенный доступ, мы можем использовать 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__ ". Теперь вы знаете, как!

Ответ 7

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

Ответ 8

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

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

Ответ 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 empty")
    else:
      return other

Ответ 10

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

импорт мунк

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

dict_munch = munch.munchify(temp_1)

dict_munch.person.fname

Ответ 11

Использовать __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

Ответ 12

Сам язык не поддерживает это, но иногда это все еще является полезным требованием. Помимо рецепта 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'
>>

Ответ 13

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 в объект, это должно сделать. Вы можете создать объект throwaway непосредственно перед доступом.

d = dict_to_object(d)

Ответ 14

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

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]

Ответ 15

Я закончил тем, что попробовал AttrDict и Bunch и обнаружили, что это способ замедлить работу. После того, как мы с другом познакомились с ним, мы обнаружили, что основной метод написания этих библиотек приводит к тому, что библиотека агрессивно рекурсивно рекурсирует через вложенный объект и создает копии словарного объекта. Имея это в виду, мы сделали два ключевых изменения. 1) Мы создали атрибуты lazy-loaded 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.

Ответ 16

Это решение является уточнением того, что предлагает epool, чтобы удовлетворить требование OP для последовательного доступа к вложенным dicts. Решение 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]

С этим классом теперь можно сделать что-то вроде: ABCD.

Ответ 17

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

https://github.com/skorokithakis/jsane

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

Ответ 18

Не прямой ответ на вопрос OP, но вдохновленный и, возможно, полезный для некоторых. Я создал объектное решение, используя внутренний __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"

Ответ 19

Один простой способ получить точечный доступ (но не доступ к массиву) - это использовать простой объект в 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"

... чтобы преобразовать его в dict:

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

Ответ 20

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

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__

Ответ 21

Недавно я наткнулся на библиотеку "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, когда у вас большие вложенные запросы.

Ответ 22

Решение вроде деликатного

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))