• Python递归中 return 代码陷阱


    最近写接口测试程序中,需要查询多层嵌套字典中某个Key的值,查到则返回,查不到返回None,查询的Key可以是任意层次的Key,如 Value本身也是多层字典,或者Key已经是叶子结点。

    思想:利用Python的递归思想,逐层深入遍历,最后返回结果值

    最终的成品代码参考了一下博客内容:

     
    尝试多次后发现参考代码已经无法再深入优化,只能照搬:
     1 #获取字典中的objkey对应的值,适用于字典嵌套
     2 #targetDict:要查找的字典;serchKey:要查找的目标key
     3 #ret:递归过程中,向外部(上层)传送 return值。被查找目标在第几层,则返回几次ret
     4 #default:查不到符合的serchKey,就返回默认值None
     5 def dict_getValue(targetDict,serchKey,default=None):
     6     for k,v in targetDict.items():
     7         if k == serchKey:
     8             return v                   
     9         elif isinstance(v,dict): 
    10             ret = dict_getValue(v,serchKey,default)
    11             if ret is not default: #ret与default=None不等,表示找到serchKey,则ret会作为返回值向上层返回。
    12                 return ret
    13     return default
    View Code

    测试数据,拼接在上面的代码里即可

    1 if __name__ == '__main__':
    2     targetDict ={"H": {"Ver": ["aaaa","bbbb"],"ACID": {'kkk':"aaaaa"},"CInf": [100,2,1000]},
    3                  "B": { "Login": {"Type": "LP","Name": "41SS676","Pwd": {'aaa':"123456"},"ForToken": 1}}}
    4     print (recursionSearch(targetDict,'Name'))
    直接拼装上面代码即可

    在成品之前,尝试过几种写法,都无法达到最终要求,进行了一些分析,现记录下来:

    1、查找的Key只能是叶子结点,非叶子结点的无法实现查找,代码如下:

    1 def recursionSearch(targetDict,serchKey):            #递归查找
    2     for k,v in targetDict.items():
    3         if isinstance(v,dict) :                      #值是字典元素,则递归处理
    4             recursionSearch(v,serchKey)
    5         elif k == serchKey:
    6             pp=targetDict[k]
    7             print (pp)
    8             return pp
    只考虑叶子结点是查询目标

    结果:

    print (recursionSearch(targetDict,'kkk')) 看到打印出来的叶子结点的值正是我想要查找的Key='kkk'的值‘aaaaaa’,

    print (recursionSearch(targetDict,'Name')) 换第二个分支里面的叶子结点,也能看到函数内打印结果41SS676是我想要的,应该是对的吧?

    Python输出:

    aaaaaa

    None                  <---函数输出结果

    41SS676
    None                  <---函数输出结果
    [Finished in 0.2s]

    但为毛函数结果是None呢???

    分析:

    (1).代码确实能查找叶子结点,但。。。函数返回还是有问题。

    (2).这代码遇到值为字典型的就会继续深入,如果目标Key的值恰好是字典数据,程序只会继续深入而不会就此停止。

    2、更换if条件,不会直接到叶子结点级别才开始查找

    1 def recursionSearch(targetDict,serchKey):            #递归查找
    2     for k,v in targetDict.items():
    3         if k == serchKey:
    4             return v
    5         elif isinstance(v,dict) :                      #值是字典元素,则递归处理
    6             recursionSearch(v,serchKey)
    任一层查找,但代码还是错误的

    分析:

      (2).这个程序最后一行只进行了递归调用,但是没有返回递归的值,导致一旦出现递归,则必然返回断档,结果必然是None。无return的函数返回值就是None,Python规定。

         参考《Python学习手册第4版》531页 “没有renturn语句的函数”

    3、那就把递归调用的返回值也return一下

    1 def recursionSearch(targetDict,serchKey):            #递归查找
    2     for k,v in targetDict.items():
    3         if k == serchKey:
    4             return v
    5         elif isinstance(v,dict) :                      #值是字典元素,则递归处理
    6             ret = recursionSearch(v,serchKey)
    7             return ret
    return递归调用的结果

    结果:这种代码只能按照第一个元素这条线深入递归下去,无论最终找到或者找不到目标值,都会结束递归。

    这种没脑子的增加return直接导致的是:

    (1).查找的Key在第一层第一个键值对的值中,且递归调用时,Key也在目标字典的第一个位置,能够返回正确值;

              如:Key='H',Key=‘ACID’,Key=‘kkk’都能返回正确值,如果Key=‘B’,Key=‘CInf’只会返回None

    (2).换句话说:for循环里只会使用第一对(k,v)

    分析:

    (1).如果在代码最后加一个else:return None呢?事实证明这样仍然会中断for循环,没有任何改进的作用。

    (2).必须增加一个处理方法,让程序能够在for循环中循环下去,不能只局限在第一对(k,v)中。

      主要就是用莫条件限制return ret是否执行,如果此return不执行,则for能继续循环下去

      如果ret是None就继续循环,如果ret不是None就证明找到目标,应该return ret,精简之后语句:if ret is not None: return ret

      到此就算结束了,已经全部修改完成,虽然和参考文章上的代码有些在default的区别,但功能已经完善了。经过如下测试:

    正向测试:

      print (recursionSearch(targetDict,'H'))                   #{'Ver': ['aaaa', 'bbbb'], 'ACID': {'kkk': 'aaaaa'}, 'CInf': [100, 2, 1000]}

      print (recursionSearch(targetDict,'ACID'))              #{'kkk': 'aaaaa'}

      print (recursionSearch(targetDict,'kkk‘’))                #aaaaa

      print (recursionSearch(targetDict,'Ver'))                 #['aaaa', 'bbbb']

      print (recursionSearch(targetDict,'B'))                    #{'Login': {'Type': 'LP', 'Name': '41SS676', 'Pwd': {'aaa': '123456'}, 'ForToken': 1}}

          print (recursionSearch(targetDict,'Login'))               #{'Type': 'LP', 'Name': '41SS676', 'Pwd': {'aaa': '123456'}, 'ForToken': 1}

      print (recursionSearch(targetDict,'Pwd'))                #{'aaa': '123456'}

      print (recursionSearch(targetDict,'aaa'))                 #123456

    以上均能返回正确目标键的值  --测试通过

    逆向测试:

      print (recursionSearch(targetDict,'aaaaaa'))  #None没有键属性是‘aaaaaa’的,只有一个键的值是‘aaaaaa’,测试函数是否是按键名查找

      print (recursionSearch(targetDict,'B111'))      #None

    以上均能争取返回None   --测试通过

    至此,从最初级错误程序,一步一步走到正确程序。

    我的同事和我讨论了一天,最终弄清楚了正确程序的原理,也一步一步分析清楚错误程序错在哪里,应该如何改进。

  • 相关阅读:
    (转)rename命令详解
    (转)CentOS 7系统详细开机启动流程和关机流程
    (转)Linux 开机引导和启动过程详解
    (转)Bash Shell常用快捷键
    (转)linux正则表达式详解
    (转)linux 中使用ls指定输出时间格式
    (转)用户管理 之 Linux 用户(user)和用户组(group)管理概述
    (转)Web服务器磁盘满故障深入解析
    详解MYSQL数据库密码的加密方式及破解方法
    php常见问题辨析(二)
  • 原文地址:https://www.cnblogs.com/kuzaman/p/7563141.html
Copyright © 2020-2023  润新知