коллекции python.defaultdict при назначении, но не при поиске - PullRequest
0 голосов
/ 08 января 2019

У меня есть следующий код:

from collections import *
nested_dict = lambda: defaultdict(nested_dict)
data = nested_dict()

, что позволяет мне записать любой новый «путь» в dict в виде однострочного:

data['A']['B']['C']=3

что я и хочу. Но я хочу получить исключение при запуске (для любого несуществующего пути):

var = data['A']['XXX']['C']

Я чувствую, что мне нужно defaultdict при записи, просто dict при чтении ...

Или есть простой способ проверить, существует ли «путь» в defaultdict без изменения его содержимого ...

Я пытался преобразовать defaultdict обратно в dict перед поиском, надеясь, что:

dict(data)['A']['XXX']['C']

вызовет исключение ... но он продолжал создавать недостающие ключи ...

Ответы [ 2 ]

0 голосов
/ 08 января 2019

Вы не можете различить поиск и запись здесь, потому что именно поиск создает вашу промежуточную структуру в назначении data['A']['B']['C'] = 3. Python выполняет операции индексирования data['A'], а затем ['B'], прежде чем назначить клавишу 'C'. Хукам __getitem__, __setitem__ и __missing__, задействованным для выполнения этой работы, не хватает контекста, достаточного для разграничения доступа, который затем приводит к назначению 'C' от только «чтения» 'XXX' во втором примере.

У вас действительно есть только 3 варианта:

  • Не используйте defaultdict. При написании явно создайте новые вложенные словари вместо dict.setdefault(); Вы можете при необходимости связать эти вызовы:

    var = {}
    var.setdefault('A', {}).setdefault('B', {})['C'] = 3
    

    или вы можете обернуть рекурсивное поведение несколькими функциями .

  • Создайте рекурсивную копию вашей структуры defaultdict, чтобы заменить ее структурой dict, когда вы закончите писать:

    def dd_to_d(dd):
        r = {}
        stack = [(r, dd)]
        while stack:
            target, dd = stack.pop()
            for key, value in dd.items():
                if isinstance(value, defaultdict):
                    sub = {}
                    stack.append((sub, value))
                    value = sub
                target[key] = value
        return r
    
    var = dd_to_d(var)
    
  • Установите все атрибуты default_factory на None, чтобы отключить создание новых значений для отсутствующих ключей:

    def disable_dd(dd):
        stack = [dd]
        while stack:
            dd = stack.pop()
            dd.default_factory = None
            for key, value in dd.items():
                if isinstance(value, defaultdict):
                    stack.append(value)
    
    disable_dd(var)
    
0 голосов
/ 08 января 2019

Очевидное решение - просто использовать простые диктовки с функцией, которая может «материализовать» промежуточные клавиши:

def write_path(d, path, value):
    for key in path[:-1]:
        d = d.setdefault(key, {})
    d[path[-1]] = value

d = {}

write_path(d, ['a', 'b', 'c'], 3)
print(d)
print(d['a']['b']['c'])
print(d['a']['b']['d'])

выходы

{'a': {'b': {'c': 3}}}
3
Traceback (most recent call last):
  File "writedefaultdict.py", line 11, in <module>
    print(d['a']['b']['d'])
KeyError: 'd'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...