Как перебрать вложенную словарную структуру и оперировать каждой строкой? - PullRequest
0 голосов
/ 12 ноября 2018

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

Этот вопрос близок к тому, что я ищу, но мне не удалось применить это решение к моему.

Мне нужно применить функцию os.path.expanduser() к каждой строке в следующем словаре:

x = dict(
    dir = dict(
        wd = '~/Desktop/WD',
        pymodule = [
            '~/Documents/PythonModule',
            '/Users/Username/Documents/PythonModule2'
        ],
        album = '~/Desktop/Album'
    ),
    file = dict(
        XML = '~/Downloads/data.xml',
        CSV = '~/Downloads/data.csv'
    )
)

В идеале я хотел бы определить класс, который при вызове в обычном словаре будет применять os.path.expanduser() к каждому строковому элементу этого словаря.

class MyDict:
    def __init__(d):
        self.d = d
        # some operation to apply os.path.expanduser() on each string element of 'd'

Как мне этого добиться?

Ответы [ 3 ]

0 голосов
/ 12 ноября 2018

Вот функция, которая принимает вложенную структуру x в качестве входных данных и возвращает аналогично вложенную структуру, в которой все строки были развернуты:

def expand(x):
    if isinstance(x, str):
        return os.path.expanduser(x)
    if isinstance(x, dict):
        return { key : expand(x[key]) for key in x }
    if isinstance(x, list):
        return [ expand(elem) for elem in x ]
    return x

Так, например вызывая его с

expand({1: '~/Hello', 2: ['~/World', '~/foo']})

вернется

{1: '/home/hkoehler/Hello', 2: ['/home/hkoehler/World', '/home/hkoehler/foo']}
0 голосов
/ 12 ноября 2018

Вот функция, которая сделает это:

import json
import os

x = dict(
    dir = dict(
        wd = '~/Desktop/WD',
        pymodule = [
            '~/Documents/PythonModule',
            '/Users/Username/Documents/PythonModule2'
        ],
        album = '~/Desktop/Album'
    ),
    file = dict(
        XML = '~/Downloads/data.xml',
        CSV = '~/Downloads/data.csv'
    )
)

def func(d):
    for key, value in d.items():
        if isinstance(value, dict):
            func(value)
        elif isinstance(value, str):
            d[key] = os.path.expanduser(value)
        elif isinstance(value, list):
            for i, element in enumerate(value):
                if isinstance(element, str):
                    value[i] = os.path.expanduser(element)

func(x)
print(json.dumps(x, indent=4))

Выход:

{
    "dir": {
        "wd": "C:\\Users\\martineau/Desktop/WD",
        "pymodule": [
            "C:\\Users\\martineau/Documents/PythonModule",
            "/Users/Username/Documents/PythonModule2"
        ],
        "album": "C:\\Users\\martineau/Desktop/Album"
    },
    "file": {
        "XML": "C:\\Users\\martineau/Downloads/data.xml",
        "CSV": "C:\\Users\\martineau/Downloads/data.csv"
    }
}
0 голосов
/ 12 ноября 2018

Это легко сделать с помощью рекурсивной функции. Давайте посмотрим на пример реализации. Здесь мы отобразим все строки в данном контейнере для данной функции, мы также будем использовать Понимания списка и Понимания словаря для имитации исходной вложенной структуры. Кроме того, встроенная функция isinstance используется для проверки типа заданного параметра:

def convert(func, d):
  if (isinstance(d, str)):
    return func(d)
  elif (isinstance(d, dict)):
    return {key : convert(func, d[key]) for key in d}
  elif (isinstance(d, list)):
    return [convert(func, val) for val in d]

Применяется func к каждой строке в контейнере. Проверьте это с помощью своего примера словаря и os.path.expanduser :

x = dict(
    dir = dict(
        wd = '~/Desktop/WD',
        pymodule = [
            '~/Documents/PythonModule',
            '/Users/Username/Documents/PythonModule2'
        ],
        album = '~/Desktop/Album'
    ),
    file = dict(
        XML = '~/Downloads/data.xml',
        CSV = '~/Downloads/data.csv'
    )
)


import os
x = convert(os.path.expanduser, x)
print(x)

И, конечно же, вы получите желаемый результат:

{'dir': {'wd': '/home/runner/Desktop/WD', 'pymodule': ['/home/runner/Documents/PythonModule', '/Users/Username/Documents/PythonModule2'], 'album': '/home/runner/Desktop/Album'}, 'file': {'XML': '/home/runner/Downloads/data.xml', 'CSV': '/home/runner/Downloads/data.csv'}}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...