暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Python 打印调试变量名和变量内容

生有可恋 2022-07-14
2060

当我们调试Python代码时,有时需要将变量或表达式的值打印出来观察表达式的内容。但当打印内容过多时搞不清楚哪个表达式对应到哪个值。


比较傻的办法是将表达式在格式字符串中输入两遍:

    import locale
    encoding = locale.getpreferredencoding()
    print("encoding = %s" % encoding)
    print("type(encoding) = %s" % type(encoding))


    ------输出-------------
    encoding = cp936
    type(encoding) = <class 'str'>



    当需要打印的内容变多时,打印函数会变得很长,比如:

      print("fname.encode(encoding).decode(encoding) = %s" %
      fname.encode(encoding).decode(encoding))


      遇到这个问题,我们的第一反应是在 Python 中能否获取变量名本身,比如:

        encoding = locale.getpreferredencoding()

        我们如果知道 encoding 这个变量名所代表的字符串就可以自定义打印函数,按我们的要求把变量名和内容打印出来。最好的结果就是传个变量进去,就把变量名和变量内容打印出来。比如下面这样:

          myprint(encoding)
          myprint(fname)


          ------输出-------------
          encoding = 'cp936'
          fname = 'd:\\python3'


          于是问题就变成了如何获取Python变量名。这个问题可以单独写一篇帖子,而且方法比较多。我们使用已有的方法,代码来源我贴在最后。

            import inspect
            import locale


            def retrieve_name(var):
            """
            Gets the name of var. Does it from the out most frame inner-wards.
            :param var: variable to get name from.
            :return: string
            """
            for fi in reversed(inspect.stack()):
            names = [var_name for var_name, var_val in fi.frame.f_locals.items() if var_val is var]
            if len(names) > 0:
            return names[0]




            encoding = locale.getpreferredencoding()
            print(retrieve_name(encoding))


            ----输出------------
            encoding

            以上是使用别人写好的函数,可以获取变量名,将变量名转换成字符串。方法比较多,也可以使用 locals() 函数获取局部变量名,对应 locals() 函数,还有一个获取全局变量名的函数 globals()。

              >>> import locale
              >>> encoding = locale.getpreferredencoding()
              >>> locals()
              {'__name__': '__main__',
              '__doc__': None,
              '__package__': None,
              'encoding': 'cp936'}


              >>> globals()
              {'__name__': '__main__',
              '__doc__': None,
              '__package__': None,
              'encoding''cp936'}
              >>>


              我们如果只是处理变量名,这些函数都还好。可以构造自己的调试函数把变量名和变量传给调试函数,然后格式化输出。比如:

                import inspect
                import locale


                def retrieve_name(var):
                """
                Gets the name of var. Does it from the out most frame inner-wards.
                :param var: variable to get name from.
                :return: string
                """
                for fi in reversed(inspect.stack()):
                names = [var_name for var_name, var_val in fi.frame.f_locals.items() if var_val is var]
                if len(names) > 0:
                return names[0]




                def myprint(var):
                fmt = retrieve_name(var) + " = {0}"
                print(fmt.format(var))




                encoding = locale.getpreferredencoding()
                s = "test"
                test_dir = r"D:\python3"


                myprint(encoding)
                myprint(s)
                myprint(test_dir)




                ------输出----------
                encoding = cp936
                s = test
                test_dir = D:\python3


                一切看起来很好,变量名被成功捕获到了,只用传一次参就可以把变量名和变量内容都打印出来。但是我们有时并不只打印变量,有时还要打印表达式。在处理表达式时,以上方法就失效了,比如下面这种表达式:

                  fname = r'D:\python3'
                  fname.encode()
                  fname.encode().decode() 
                  fname.encode(encoding)
                  fname.encode(encoding).decode(encoding)

                  像上面这种表达式,它们并不是变量,并且在传参前已经计算好了,所以上述处理方法都失效了。因为问题不再是获取变量名,问题变成如何获取表达式名了。


                  实际上我们已经把问题的导向搞偏了,我们刚开始只是想让打印调试信息简化一点。如果能写个函数把 print() 包一层,能输出变量名和变量内容就好。


                  对于调试信息的输出,在 Python 3.6 新引入了一种新的语法,即以 f 为前缀的格式字符串。这种新的语法适用我们这种打印调试信息的场景。如果想把变量或表达式打印出来,可以直接使用如下语法:

                    print(f"{encoding = }")
                    print(f"{fname = }")
                    print(f"{type(fname) = }")
                    print(f"{fname.encode() = }")
                    print(f"{fname.encode().decode() = }")
                    print(f"{fname.encode(encoding) = }")
                    print(f"{fname.encode(encoding).decode(encoding) = }")

                    格式字符串的格式为:f"{ var = }"  


                    就这么简单,支持变量、表达式、各种加减运算。最终打印效果如下:

                      ------输出-------------
                      encoding = 'cp936'
                      fname = 'd:\\python3'
                      type(fname) = <class 'str'>
                      fname.encode() = b'd:\\python3'
                      fname.encode().decode() = 'd:\\python3'
                      fname.encode(encoding) = b'd:\\python3'
                      fname.encode(encoding).decode(encoding) = 'd:\\python3'


                      为了控制调试开关,我们把 print 函数包装一下,在函数中加上调试状态判断,可以根据需要关闭或启用调试信息。

                        #! pyhton3
                        import locale


                        # 调试开关
                        debug = True


                        # 自定义打印函数
                        def pprint(var):
                        global debug
                        if debug:
                        print(var)




                        # 测试打印效果
                        encoding = locale.getpreferredencoding()
                        fname = r'D:\python3'
                        pprint(f"{encoding = }")
                        pprint(f"{fname = }")
                        pprint(f"{type(fname) = }")
                        pprint(f"{fname.encode() = }")
                        pprint(f"{fname.encode().decode() = }")
                        pprint(f"{fname.encode(encoding) = }")
                        pprint(f"{fname.encode(encoding).decode(encoding) = }")


                        关于Python3 的字符串前缀,我有一篇帖子做了总结:

                        Python3 字符串前缀 u、r、b、f

                        hyang0,公众号:生有可恋Python 字符串前缀


                        获取变量名的函数retrieve_name(),代码来源:

                        • https://stackoverflow.com/questions/18425225/getting-the-name-of-a-variable-as-a-string/40536047#40536047


                        参考:

                        • https://blog.csdn.net/jiangwei741/article/details/103801578

                        • https://www.zhihu.com/question/42768955

                        • https://zhuaxia.xyz/detail/10076


                        全文完。


                        如果转发本文,文末务必注明:“转自微信公众号:生有可恋”。


                        文章转载自生有可恋,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                        评论