【问题标题】:"return" statement not exiting recursive function“return”语句不退出递归函数
【发布时间】:2020-04-01 20:19:52
【问题描述】:

如果已经有回答此问题的问题,但我没有找到任何帮助,我提前道歉。本质上,我有一个具有 n 级的嵌套字典,即,

nested_dict = {
    "lvl_1a": {
        "lvl_2a_1": {
            "lvl_3a_1": 1,
            "lvl_3b_1": 2,
            ...,
         },
        "lvl_2b_1": {
            "lvl_3a_2": 5,
            "lvl_3b_2": 6,
             ...,
        },
     "lvl_1b": {
         "lvl_2a_2": {
            "lvl_3a_3": 1,
            "lvl_3b_3": 2,
            ...,
          }
      }
  }

还有一个我想删除的键,比如“lvl_3a_3”。这就是我目前所拥有的

def remove_a_key(d, remove_key):
    if isinstance(d, dict) and d:
        for key in list(d.keys()):
            if key == remove_key:
                del d[key]
                return d

            elif not d[key]:
                del d[key]
                return remove_a_key(d, remove_key)

            else:
                return remove_a_key(d[key], remove_key)

modified_dictionary = remove_a_key(nested_dictionary, "lvl_3a_3")

在另一个人的帮助下 StackOverflow question。 但是一旦找到要删除的key,并且预期return d,它就会继续else-statement,而不是完全退出函数。事实上,它在完全退出之前运行了两次else-语句。我不确定发生了什么,除了我以某种方式引用了该函数,因此它没有正确返回。

【问题讨论】:

  • 请重复介绍,尤其是MRE。您发布的代码仅定义了一个函数并退出而不执行。
  • 请记住,return 返回 one 级别。外部堆栈帧获取执行;如果该层不选择返回,则预期执行其他代码。尽管在给定的代码中,remove_a_key 确实 的每个实例都会立即返回,但我真的很想看到一个真正的复制者,以便相信问题中给出的问题描述是准确的一个,而不是对所呈现的症状的误读。
  • 也就是说,我不知道您希望这个函数如何做任何有用的事情。如果您在列表的第一次迭代期间总是return(因此,当key 仅指 very first 键时),您如何期望该列表的其他成员(或者,在这个上下文,该字典中的其他键)要处理?
  • 我也不知道你为什么要在你成功删除后递归,在同样的情况下,删除后递归总是重新创建。这似乎是一条通往无限循环的捷径。

标签: python python-3.x dictionary recursion nested


【解决方案1】:

您的代码无法正常工作,因为您return 每次递归调用的结果,即使它没有找到所需的密钥并且没有进行任何更改。当您的函数第一次到达无效对象时(例如,对非 dict 值进行内部调用),它会返回 None(默认情况下),并将调用堆栈向上传播为顶部的最终结果-水平调用。

由于您正在就地修改字典,因此您真的不应该使用return 作为字典值。调用者已经引用了您正在处理的 dict,因此当我们找到所需的键时,您可以改为使用返回值发出信号:

def remove_a_key(d, remove_key):
    if isinstance(d, dict) and d:
        for key in list(d.keys()):
            if key == remove_key:
                del d[key]
                return True                          # first base case, we found the key

            else:                                    # recursive case
                if remove_a_key(d[key], remove_key): # only return if we were successfull 
                    if not d[key]:   # empty dict cleanup code
                        del d[key]
                    return True      
    return False                                     # third base case, failure

注意,我将删除空字典的代码位移动到递归案例中。这假设您没有任何希望在开始时清理的空字典,您只是不希望删除字典中的最后一个叶子条目而留下一堆孤立的父字典。

这样调用代码:

nested = {
          "a": {"aa": 1, "ab": 2},
          "b": {"ba": 3, "bb": 4},
         }
print(nested) # prints {'a': {'aa': 1, 'ab': 2}, 'b': {'ba': 3, 'bb': 4}}
remove_a_key(nested, "bb")
print(nested) # prints {'a': {'aa': 1, 'ab': 2}, 'b': {'ba': 3}}
remove_a_key(nested, "ba")
print(nested) # prints {'a': {'aa': 1, 'ab': 2}}

最后一点:此代码最多只能删除一个键。如果您希望它删除所有可能出现多次的键,则需要在递归情况下摆脱return。实际上,在这种情况下,您甚至根本不需要处理返回值,您只需始终无条件地扫描整个字典。

【讨论】:

    【解决方案2】:

    这是我找到的解决方案

    def remove_key(d = {}, q = ""):
      if isinstance(d, dict):            # <-- only attempt prune on dict
        if q in d:
          del d[q]                       # <-- remove key if it exists
        for (key, value) in d.items():
          d[key] = remove_key(value, q)  # <-- recur
      return d                           # <-- always return d
    

    【讨论】:

      猜你喜欢
      • 2017-07-28
      • 2019-03-09
      • 1970-01-01
      • 2015-10-08
      • 2014-06-25
      • 2017-02-09
      • 1970-01-01
      • 2018-04-05
      • 2019-01-25
      相关资源
      最近更新 更多