Вот семейство рекурсивных генераторов, которые можно использовать для поиска объекта, состоящего из диктов и списков. find_key
дает кортеж, содержащий список ключей словаря и индексы списка, которые приводят к ключу, который вы передаете; кортеж также содержит значение, связанное с этим ключом.
def find_key(obj, key):
if isinstance(obj, dict):
yield from iter_dict(obj, key, [])
elif isinstance(obj, list):
yield from iter_list(obj, key, [])
def iter_dict(d, key, indices):
for k, v in d.items():
if k == key:
yield indices + [k], v
if isinstance(v, dict):
yield from iter_dict(v, key, indices + [k])
elif isinstance(v, list):
yield from iter_list(v, key, indices + [k])
def iter_list(seq, key, indices):
for k, v in enumerate(seq):
if isinstance(v, dict):
yield from iter_dict(v, key, indices + [k])
elif isinstance(v, list):
yield from iter_list(v, key, indices + [k])
# test
data = {
'1_data': {
'4_data': [
{'5_data': 'hooray'},
{'3_data': 'hooray2'}
],
'2_data': []
}
}
for t in find_key(data, '3_data'):
print(t)
output
(['1_data', '4_data', 1, '3_data'], 'hooray2')
Чтобы получить один ключ, если он является генератором, он найдет все соответствующие ключи. вы можете передать find_key
в функцию next
. И если вы хотите использовать список ключей для извлечения связанного значения, вы можете использовать простой цикл for
.
seq, val = next(find_key(data, '3_data'))
print('seq:', seq, 'val:', val)
obj = data
for k in seq:
obj = obj[k]
print('obj:', obj, obj == val)
output
seq: ['1_data', '4_data', 1, '3_data'] val: hooray2
obj: hooray2 True
Если ключ может быть отсутствует, затем дайте next
соответствующий кортеж по умолчанию. Например:
seq, val = next(find_key(data, '6_data'), ([], None))
print('seq:', seq, 'val:', val)
if seq:
obj = data
for k in seq:
obj = obj[k]
print('obj:', obj, obj == val)
output
seq: [] val: None
Обратите внимание, что этот код предназначен для Python 3. Чтобы запустить его на Python 2, вам необходимо заменить все yield from
например, заменить
yield from iter_dict(obj, key, [])
на
for u in iter_dict(obj, key, []):
yield u
Чтобы понять, как работает этот код, вам нужно быть знакомым с рекурсия и с генераторами Python . Вы также можете найти эту страницу полезной: Понимание генераторов в Python ; существуют также различные обучающие программы для генераторов Python, доступные в Интернете.
Объект Python, возвращенный json.load
или json.loads
, обычно является dict, но также может быть списком. Мы передаем этот объект генератору find_key
в качестве obj
arg, а также строку key
, которую хотим найти. find_key
затем вызывает либо iter_dict
, либо iter_list
, в случае необходимости, передавая им объект, ключ и пустой список indices
, который используется для сбора ключей dict и индексов списка, которые приводят к ключу, который мы want.
iter_dict
выполняет итерацию по каждой (k, v) паре на верхнем уровне своего d
dict arg. Если k
соответствует клавише, которую мы ищем, то текущий indices
список будет добавлен с добавленным к нему k
вместе со связанным значением. Поскольку iter_dict
рекурсивно, полученные пары (индексы список, значение) передаются до предыдущего уровня рекурсии, в конечном итоге доходя до find_key
, а затем кода, который называется find_key
. Обратите внимание, что это «базовый случай» нашей рекурсии: это часть кода, которая определяет, ведет ли этот путь рекурсии к нужному ключу. Если путь рекурсии никогда не находит ключ, соответствующий ключевому слову, который мы ищем, тогда этот путь рекурсии ничего не добавит к indices
, и он завершится без каких-либо изменений.
Если текущий v
является dict, тогда нам нужно изучить все пары (ключ, значение), которые он содержит. Мы делаем это путем рекурсивного вызова iter_dict
, передавая, что v
является его стартовым объектом и текущим indices
списком. Если текущий v
является списком, мы вместо этого вызываем iter_list
, передавая ему одни и те же аргументы.
iter_list
работает аналогично iter_dict
, за исключением того, что в списке нет никаких ключей, это только содержит значения, поэтому мы не выполняем тест k == key
, мы просто рекурсируем в любые dicts или списки, которые содержит исходный список.
Конечным результатом этого процесса является то, что когда мы перебираем find_key
мы получаем пары (индексы, значение), где каждый список indices
представляет собой последовательность ключей dict и индексов списка, которые успешно завершаются в элементе dict с помощью нашего желаемого ключа, а value
- это значение, связанное с этим конкретным ключом .
Если вы хотите увидеть некоторые другие примеры использования этого кода, см. , как изменить ключ вложенных Json и . Как я могу выбрать глубоко вложенный ключ: значения из словаря в python .
> sub("(.*)_.*$", "\\1", group)
[1] "amount_bin" "fico_bin" "cltv_bin" "p_region"