Як використовувати крапку "." отримати доступ до членів словника?


283

Як зробити учасників словника 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
Будь-який об'єкт, який є "хешируемим", може використовуватися як ключ диктату. Більшість непорушних об'єктів також є хешируемими, але лише в тому випадку, якщо весь їх вміст є хешированним. Код 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, щоб глибока копія та інші системи могли його підтримувати.
користувач1363990

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__`
Сяо

265

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

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 __ (само), але я якось не можу змусити його працювати.
andreas-h

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

2
Так, це не працює для складних вкладених структур.
Девід

16
@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

Акуратний. Будь-який спосіб зробити так, щоб перелік та доповнення вкладок працювали з ключами в блокноті Юпітера? Доступ у стилі крапки є найціннішим для інтерактивного використання.
Дмитро

@Dmitri Cool продукт. Ніколи не чув про нього раніше, тому я не знаю, як зробити його автозаповнення. Я погоджуюсь 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, який дуже схожий.

Я не думаю, що можливо вмонтовувати вбудований клас dict.


2
Що конкретно означає мавпа? Я чув про це, але не використовував. (Вибачте, що я задаю такі запитання новачкам, я ще не так добре займаюся програмуванням (я лише студентка 2 курсу.))
bodacydo

9
Monkeypatching використовує динамічність Python (або будь-якої мови), щоб змінити те, що зазвичай визначається у вихідному коді. Особливо це стосується зміни визначення класів після їх створення.
Майк Грехем,

Якщо ви багато використовуєте цю функціональність, остерігайтеся швидкості зв'язування. Я використовував його досить часто, і він закінчив витрачати третину мого запиту. Перевірте мою відповідь для більш детального пояснення цього.
JayD3e

22

Тканина має дійсно приємне, мінімальне виконання . Розширивши це, щоб дозволити вкладений доступ, ми можемо використовувати 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

Це трохи детальніше пояснює відповідь Кугеля: "Отримайте вислів і виконайте __getattr__і __setattr__". Тепер ви знаєте, як!


1
Цей дивовижний!
Томас Клінгер

Приємно включати вирок за замовчуванням - однак це, здається, працює лише тоді, коли починається випуск з нуля. Якщо нам потрібно перетворити існуючий дикт в "dotdict" рекурсивно. Ось альтернатива , dotdictяка дозволяє перетворити існуючий dictоб'єкт рекурсивно: gist.github.com/miku / ...
Міку

19

Я спробував це:

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

Ви можете спробувати __getattribute__ .

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


ой, відповідь @Kugel схожа.
tdihp

1
tdihp, мені ще подобається ваша відповідь, тому що я зрозумів її швидше - у неї є власне код.
yigal

1
+1 для фактичного коду. Але пропозиція @ Kugel щодо використання Bunch також дуже гарна.
Даннід

Мені здається корисним вбудувати цю функцію у функцію, поставивши def docdict(name):перед нею, а потім "if isin substance (name, dict): return DotDict (name) return name"
Даніель Москович

чудовий простий приклад .. Я трохи подовжив це, щоб вкладений дикт був легко пов'язаний ланцюжком, подібний до @DanielMoskovich, але також повертає вузли аркушів правильно для int, string та інше ... або null, якщо їх не знайдено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: ми говоримо вам, не робіть вигляд, що значення дикту є атрибутами. Це погана практика. Наприклад, що робити, якщо ви хочете зберегти значення з тим самим іменем, що і існуючий атрибут дикта, наприклад "items" або "get" або "pop"? Напевно, щось заплутане. Тож не робіть цього!
Ларрі Гастінгс

5
На жаль, я забув про такі атрибути, як "items", "get" або "pop. Дякуємо, що ви навели цей важливий приклад!
бодацидо

5
@Gabe, це вже давно ... але я думаю, що варто сказати. Це не "досить добре в JS": це "досить жахливо в JS". Це стає смішним, коли ви зберігаєте ключі / attr, які мають те саме ім'я, що й інші важливі атрибути в прототипі ланцюга.
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. Це доповнення потрібне.
користувач1363990

Спрощення:__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

Нещодавно я натрапив на бібліотеку " Коробка ", яка робить те саме.

Команда встановлення: 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

Сама мова це не підтримує, але іноді це все ж корисна вимога. Окрім рецепта групи, ви також можете написати невеликий метод, який може отримати доступ до словника за допомогою пунктирної рядки:

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 = lambda: None o .__ dict __. update (** kwargs) return o
bacs_exceptions_at_you

2

Я в кінцевому підсумку намагався БУТИ AttrDict і Bunchбібліотеки і виявив, що це спосіб сповільнити моє використання. Після того, як ми з другом подивилися, ми виявили, що основний метод написання цих бібліотек призводить до того, що бібліотека агресивно повторюється через вкладений об'єкт і створює копії словникового об’єкта протягом усього часу. Зважаючи на це, ми внесли дві ключові зміни. 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. Я великий шанувальник автовівіфікації як для читання, так і для письма. Ваша бібліотека найближча до ідеалу.
nehem

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 для вирішення вимоги ОП для доступу до вкладених диктів послідовно. Рішення електронною поштою не дозволило отримати доступ до вкладених диктів.

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.