Python для создания пути к ключам диктовки аналогично mkdir -p - PullRequest
1 голос
/ 23 марта 2020

Допустим, у нас есть синтаксический анализ, полученный из json, и мы читаем значения из него с ключей в виде пути к ключу path-to.my.keys

my_dict['path-to']['my']['keys']

В файловой системе у нас есть mkdir -p для создайте такой путь, если он не существует.

В python, есть ли у нас подобный синтаксис / функция для создания key path для dict aka по умолчанию пустой dict для отсутствующих ключей ? Мой поиск в Google результаты не очень полезны.

Ответы [ 4 ]

2 голосов
/ 23 марта 2020

TLDR

Вы можете использовать dict.setdefault или collections.defaultdict.

def make_path(d: dict, *paths: str) -> None:
    for key in paths:
        d = d.setdefault(key, {})

make_path(my_dict, 'path-to', 'my', 'keys')
assert my_dict['path-to']['my']['keys'] is not None

Полная информация

Решение 1. dict.setdefault:

my_dict.setdefault('path-to', {}).setdefault('my', {}).setdefault('keys', {})

Плюсы:

  • my_dict нормально dict
  • диктовка происходит только явно
  • Нет ограничений по глубине

Минусы:

  • Вы должны вызывать setdefault метод при каждом использовании.

Решение 2. collections.defaultdict:

from collections import defaultdict

my_dict = defaultdict(lambda: defaultdict(lambda: defaultdict(dict)))
my_dict['path-to']['my']['keys']

Плюсы:

  • Вам не нужно звонить проверять существование вообще.

Минусы:

  • Создание словаря происходит неявно.
  • my_dict не является чистым dict.
  • У вас есть ограничение по глубине по определению my_dict.

Решение 3. Усовершенствовано из решения 1: Создайте свою собственную функцию

def make_path(my_dict: dict, *paths: str) -> dict:
    while paths:
        key, *paths = paths
        my_dict = my_dict.setdefault(key, {})
    return my_dict


test = {'path-to': {'test': 1}}
print(test)

make_path(test, 'path-to', 'my', 'keys')['test2'] = 4
print(test)

print(make_path(test))  # It's okay even no paths passed

Вывод:

{'path-to': {'test': 1}}
{'path-to': {'test': 1, 'my': {'keys': {'test2': 4}}}}
{'path-to': {'test': 1, 'my': {'keys': {'test2': 4}}}}

Решение 4. Усовершенствовано из решения 2: Создайте собственный класс

class MyDefaultDict(dict):
    def __missing__(self, key):
        self[key] = MyDefaultDict()
        return self[key]


my_dict = MyDefaultDict()
print(my_dict)
my_dict['path-to']['my']['keys'] = 'hello'
print(my_dict)

вывод:

{}
{'path-to': {'my': {'keys': 'hello'}}}

Вывод

Я думаю, что решение 3 является больше всего соответствует вашим потребностям, но вы можете использовать любые другие варианты, если это соответствует вашему случаю.


Append

Как насчет решения 4, мы уже задали: d уже разобрали с json? Ваше решение начинается с типа MyDefaultDict (), а не с того, который возвращается из jsons.loads ()

Если вы можете редактировать json.loads деталь, попробуйте:

import json


class MyDefaultDict(dict):
    def __missing__(self, key):
        self[key] = MyDefaultDict()
        return self[key]


data = '{"path-to": {"my": {"keys": "hello"}}}'
my_dict = json.loads(data, object_pairs_hook=MyDefaultDict)
print(type(my_dict))

output:

<class '__main__.MyDefaultDict'>
1 голос
/ 23 марта 2020

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

import json
from collections import defaultdict

nested = lambda: defaultdict(nested)

d = nested()
d['path']['to']['nested']['key'] = 'value'

print(json.dumps(d))
# {"path": {"to": {"nested": {"key": "value"}}}}

Несуществующие ключи будут возвращать пустые defaultdicts.

1 голос
/ 23 марта 2020

В python есть ли у нас подобный синтаксис / функция для создания ключевого пути для dict? Мои результаты поиска в Google не очень полезны.

Python не имеет синтаксиса "keypath" в стиле clojure & friends no. Он может обрабатывать этот конкретный случай c при некоторых затратах времени выполнения для удобства, используя метод setdefault, хотя: dict.setdefault(key, default) вернет значение для ключа после его установки, если оно отсутствовало , так что my_dict.setdefault('path-to', {}).setdefault('my', {}).setdefault('keys', ???) получит доступ к указанному пути, установив параметры, где они отсутствуют.

0 голосов
/ 23 марта 2020

Ответ на ваш вопрос - ДА.

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

Вы можете выполнить ту же команду mkdir -p из python для создания вложенного каталога usinf subprocess.Popen.

Вот как это можно сделать:

import subprocess

# Create a string of nested directory path.
path_from_dict_keys = "dir1/dir2/dir3"

temp = subprocess.Popen(['mkdir', '-p', path_from_dict_keys], stdout = subprocess.PIPE) 

# we use the communicate function to fetch the output 
output = str(temp.communicate())

...