Top.Mail.Ru
Ответы

Рекурсия в Python

Имеется многоуровневый словарь.
Вводные данные, искомый ключ.
Цель, найти ключ путем рекурсии.

В общем и целом, что такое рекурсия я понимаю, но я не понимаю почему то что я пишу, не работает так как задумано мною.

Имеется код:

12345678910111213141516
 def search(site, search_key, deep=0): 
    if deep != -1: 
        print('ГЛУБИНА РЕКУРСИИ', deep) 
        for i_key, i_value in site.items(): 
            print('\t\t\t\t', i_key) 
            print('\t\t\t\t', i_value) 
            if search_key == i_key: 
                return 'Искомый ключ', i_key 
 
            elif isinstance(i_value, dict): 
                print(isinstance(i_value, dict), i_key, 'is dict') 
                search(site[i_key], search_key, deep + 1) 
 
            else: 
                print(isinstance(i_value, dict), i_key, 'is not dict')
 

deep в данном случае по назначению не используется*

Иду циклом по объектам словаря.
Если значение ключа это словарь(isinstance(i_value, dict), то он его проверяет, это сделано чтобы он не проверял побуквенно значения ключей которые являются просто строкой.
Если он циклом не нашел искомый ключ он идет соответственно в блок elif: где вызывает рекурсию словаря от ключа. По факту цикл не закончился, а прервался(заморозился) из-за рекурсии на первом же ключе(т.е. при возврате он должен продолжить его).

И вот тут меня вынесло на 2 дня. Если я вызываю рекурсию с return как положено, то он стопарится в первом же блоке ключа. Т.е у меня словарь состоит из 2 ключей на 1-м уровне, он проверяет первый, а на второй забивает даже на обратном пути. И выдает None, ибо то что на тот момент я ищу, находится во втором ключе.

1234567891011121314
 ГЛУБИНА РЕКУРСИИ 0 
				 html 
				 {'head': {'title': 'Мой сайт'}, 'body': {'h2': 'Здесь будет мой заголовок', 'div': 'Тут, наверное, какой-то блок', 'p': 'А вот здесь новый абзац'}} 
True html is dict 
ГЛУБИНА РЕКУРСИИ 1 
				 head 
				 {'title': 'Мой сайт'} 
True head is dict 
ГЛУБИНА РЕКУРСИИ 2 
				 title 
				 Мой сайт 
False title is not dict 
None 
 



Если же, в рамках эксперимента, убрать return, то как мною и задумано, после окончания рекурсии для первого ключа, он идет во второй ключ, ииии?) Он находит искомое мною значение, но выдает None, потому что что? А потому что функция на тот момент ничего не возвращает, ибо return нету.

1234567891011121314151617181920212223
 ГЛУБИНА РЕКУРСИИ 0 
				 html 
				 {'head': {'title': 'Мой сайт'}, 'body': {'h2': 'Здесь будет мой заголовок', 'div': 'Тут, наверное, какой-то блок', 'p': 'А вот здесь новый абзац'}} 
True html is dict 
ГЛУБИНА РЕКУРСИИ 1 
				 head 
				 {'title': 'Мой сайт'} 
True head is dict 
ГЛУБИНА РЕКУРСИИ 2 
				 title 
				 Мой сайт 
False title is not dict 
				 body 
				 {'h2': 'Здесь будет мой заголовок', 'div': 'Тут, наверное, какой-то блок', 'p': 'А вот здесь новый абзац'} 
True body is dict 
ГЛУБИНА РЕКУРСИИ 2 
				 h2 
				 Здесь будет мой заголовок 
False h2 is not dict 
				 div 
				 Тут, наверное, какой-то блок 
None 
 


Я целиком и полностью, просто не могу понять что тут не так.
Почему при вызове рекурсии с return он зависает на первом ключе и не идет во второй?


*на этот момент искомым ключом был div, и в варианте без return последним блоком выходит он в паре с None

Дополнен

Имеющийся словарь пришлось добавить в качестве дополнения.

По дате
По Рейтингу
Аватар пользователя
Просветленный

Проблема с вашим кодом
Возврат из рекурсии: Когда вы вызываете рекурсию, вам нужно обрабатывать результат рекурсивного вызова. Если рекурсивный вызов нашел ключ, вам нужно прекратить дальнейшее выполнение и вернуть результат.
Отсутствие возврата в цикле: Если рекурсивный вызов ничего не возвращает, текущий вызов функции тоже должен ничего не возвращать. Нужно корректно обрабатывать результат внутри цикла.

12345678910111213141516171819202122232425262728293031323334
 def search(site, search_key, deep=0): 
    if deep != -1: 
        print('ГЛУБИНА РЕКУРСИИ', deep) 
        for i_key, i_value in site.items(): 
            print('\t\t\t\t', i_key) 
            print('\t\t\t\t', i_value) 
            if search_key == i_key: 
                return 'Искомый ключ', i_key, i_value 
             
            elif isinstance(i_value, dict): 
                print(isinstance(i_value, dict), i_key, 'is dict') 
                result = search(i_value, search_key, deep + 1) 
                if result: 
                    return result 
 
            else: 
                print(isinstance(i_value, dict), i_key, 'is not dict') 
    return None 
 
site = { 
    'html': { 
        'head': { 
            'title': 'Мой сайт' 
        }, 
        'body': { 
            'h2': 'Здесь будет мой заголовок', 
            'div': 'Тут, наверное, какой-то блок', 
            'p': 'А вот здесь новый абзац' 
        } 
    } 
} 
 
result = search(site, 'div') 
print(result)  

Объяснение исправлений
Рекурсивный вызов с обработкой результата:

result = search(i_value, search_key, deep + 1) — сохраняем результат рекурсивного вызова.
if result: — если результат не None, возвращаем его.
Возврат результата в случае нахождения ключа:

return 'Искомый ключ', i_key, i_value — возвращаем ключ и значение при нахождении.
Возврат None, если ключ не найден:

Добавлен возврат None в конце функции, если ключ не найден на всех уровнях.