Python如何打印全栈,包括使用的魔术方法(dunder methods)?

我正在尝试调试 Python 内置类。我的调试使我进入了魔术方法(又名 dunder 方法)的领域。


我试图找出调用了哪些 dunder 方法,如果有的话。通常我会做这样的事情:


import sys

import traceback


# This would be located where the I'm currently debugging

traceback.print_stack(file=sys.stdout)

但是,traceback.print_stack并没有给我打印在其附近使用的 dunder 方法区域的详细程度。


有什么方法可以以非常详细的方式打印出代码块中实际发生的事情?


示例代码


#!/usr/bin/env python3.6


import sys

import traceback

from enum import Enum



class TestEnum(Enum):

    """Test enum."""


    A = "A"



def main():

    for enum_member in TestEnum:

        traceback.print_stack(file=sys.stdout)

        print(f"enum member = {enum_member}.")



if __name__ == "__main__":

    main()

我希望上面的示例代码打印出任何使用的 dunder 方法(例如:)__iter__。


目前它打印出调用的路径traceback.print_stack:


/path/to/venv/bin/python /path/to/file.py

  File "/path/to/file.py", line 56, in <module>

    main()

  File "/path/to/file.py", line 51, in main

    traceback.print_stack(file=sys.stdout)

enum member = TestEnum.A.

PS 我对进入 . 给出的字节码级别不感兴趣dis.dis。


暮色呼如
浏览 127回答 2
2回答

杨魅力

我认为,使用堆栈跟踪,您正在寻找错误的地方。当您print_stack从一个地方调用时,该方法仅在来自 dunder 方法时执行,该方法很好地包含在输出中。我试过这段代码来验证:import sysimport tracebackfrom enum import Enumclass TestEnum(Enum):&nbsp; &nbsp; """Test enum."""&nbsp; &nbsp; A = "A"class MyIter:&nbsp; &nbsp; def __init__(self):&nbsp; &nbsp; &nbsp; &nbsp; self.i = 0&nbsp; &nbsp; def __next__(self):&nbsp; &nbsp; &nbsp; &nbsp; self.i += 1&nbsp; &nbsp; &nbsp; &nbsp; if self.i <= 1:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; traceback.print_stack(file=sys.stdout)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return TestEnum.A&nbsp; &nbsp; &nbsp; &nbsp; raise StopIteration&nbsp; &nbsp; def __iter__(self):&nbsp; &nbsp; &nbsp; &nbsp; return selfdef main():&nbsp; &nbsp; for enum_member in MyIter():&nbsp; &nbsp; &nbsp; &nbsp; print(f"enum member = {enum_member}.")if __name__ == "__main__":&nbsp; &nbsp; main()堆栈跟踪的最后一行打印为File "/home/lydia/playground/demo.py", line 21, in __next__traceback.print_stack(file=sys.stdout)在您的原始代码中,您在所有 dunder 方法都已返回时获取堆栈跟踪。因此,它们已从堆栈中删除。所以我认为,你想看看调用图。我知道 IntelliJ / PyCharm 至少在付费版本中可以很好地做到这一点。您可能还想尝试其他工具。你觉得 pycallgraph怎么样?更新:Python 使得转储所有函数调用的简单列表实际上非常容易。基本上你需要做的就是import syssys.setprofile(tracefunc)tracefunc根据您的需要编写。在这个 SO 问题上找到一个工作示例:How do I print functions as they are called警告:我需要从外部 shell 启动脚本。通过在我的 IDE 中使用播放按钮启动它意味着脚本永远不会终止,而是编写越来越多的行。我认为它与我的 IDE 完成的内部分析相冲突。官方文档sys.setprofile:https ://docs.python.org/3/library/sys.html#sys.setprofile还有一个关于 Python 跟踪的随机教程:https ://pymotw.com/2/sys/tracing.html但是请注意,根据我的经验,您可以对“谁在给谁打电话?”这个问题获得最好的见解。或者“这个价值是从哪里来的?”&nbsp;通过使用普通的调试器。

蝴蝶刀刀

我还对该主题进行了一些研究,因为@LydiaVanDyke 的答案中的信息促进了更好的搜索。打印整个调用堆栈正如@LydiaVanDyke 指出的那样,IDE 调试器是一个非常好的方法。我使用 PyCharm,发现这是我最喜欢的解决方案,因为可以:遵循函数调用 + 代码中的确切行号阅读调用周围的代码,更好地理解打字跳过不想调查的电话另一种方法是 Python 的标准库的trace.&nbsp;它提供了命令行和可嵌入的方法来打印整个调用堆栈。还有一个是 Python 的内置调试器模块,pdb.&nbsp;这(通过调用pdb.set_trace())真的改变了我的游戏。Profiler 输出的可视化gprof2dot是另一个有用的分析器可视化工具。源代码有用的教程查找源代码由于我的 IDE 的存根文件 (PyCharm),我的其他问题之一实际上并没有看到真正的源代码。如何检索Python函数的源代码详述了实际打印源代码的两种方法使用所有这些工具,您会感到非常有能力!
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python