Python 如何打印完整堆栈,包括使用的魔法方法(dunder methods)?
Python how to print full stack, including magic methods (dunder methods) used?
我正在尝试调试 Python 内置 class。我的调试使我进入了魔术方法(又名 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()
我希望上面的示例代码能够打印出任何使用的双打方法(例如:__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.
P.S。我对进入 dis.dis
.
给出的字节码级别不感兴趣
我认为,通过堆栈跟踪,您看错了地方。当你从一个地方调用 print_stack
时,只有当来自 dunder 方法时才会执行,这个方法很好地包含在输出中。
我试过这段代码来验证:
import sys
import traceback
from enum import Enum
class TestEnum(Enum):
"""Test enum."""
A = "A"
class MyIter:
def __init__(self):
self.i = 0
def __next__(self):
self.i += 1
if self.i <= 1:
traceback.print_stack(file=sys.stdout)
return TestEnum.A
raise StopIteration
def __iter__(self):
return self
def main():
for enum_member in MyIter():
print(f"enum member = {enum_member}.")
if __name__ == "__main__":
main()
堆栈跟踪的最后一行打印为
File "/home/lydia/playground/demo.py", line 21, in __next__
traceback.print_stack(file=sys.stdout)
在您的原始代码中,您在所有 dunder 方法都已返回时获取堆栈跟踪。因此它们已从堆栈中移除。
所以我想,您想看看调用图。我知道 IntelliJ / PyCharm 至少在付费版本中可以很好地做到这一点。
您可能还想尝试其他工具。 pycallgraph 你觉得如何?
更新:
Python 实际上很容易转储所有函数调用的简单列表。
基本上你需要做的就是
import sys
sys.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
但是请注意,根据我的经验,您可以通过使用普通的调试器获得对问题 "who is calling whom?" 或 "where does this value even come from?" 的最佳见解。
我还对该主题进行了一些研究,因为@LydiaVanDyke 的回答中的信息推动了更好的搜索。
打印整个调用堆栈
正如@LydiaVanDyke 指出的那样,IDE 调试器是一种非常好的方法。我使用 PyCharm,发现这是我最喜欢的解决方案,因为可以:
- 跟随函数调用 + 代码中的确切行号
- 阅读调用代码,更好地理解输入
- 跳过那些不关心调查的电话
另一种方式是Python的标准库trace
。它提供命令行和可嵌入的方法来打印整个调用堆栈。
还有一个是 Python 的内置调试器模块 pdb
。这(通过 pdb.set_trace()
调用)真的改变了我的游戏。
Profiler 输出的可视化
gprof2dot
是另一个有用的分析器可视化工具。
查找源代码
由于我的 IDE 存根文件 (PyCharm),我的另一个问题是实际上没有看到真正的源代码。
How to retrieve source code of Python functions详解两种实际打印源码的方法
使用所有这些工具,让人感觉充满力量!
我正在尝试调试 Python 内置 class。我的调试使我进入了魔术方法(又名 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()
我希望上面的示例代码能够打印出任何使用的双打方法(例如:__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.
P.S。我对进入 dis.dis
.
我认为,通过堆栈跟踪,您看错了地方。当你从一个地方调用 print_stack
时,只有当来自 dunder 方法时才会执行,这个方法很好地包含在输出中。
我试过这段代码来验证:
import sys
import traceback
from enum import Enum
class TestEnum(Enum):
"""Test enum."""
A = "A"
class MyIter:
def __init__(self):
self.i = 0
def __next__(self):
self.i += 1
if self.i <= 1:
traceback.print_stack(file=sys.stdout)
return TestEnum.A
raise StopIteration
def __iter__(self):
return self
def main():
for enum_member in MyIter():
print(f"enum member = {enum_member}.")
if __name__ == "__main__":
main()
堆栈跟踪的最后一行打印为
File "/home/lydia/playground/demo.py", line 21, in __next__
traceback.print_stack(file=sys.stdout)
在您的原始代码中,您在所有 dunder 方法都已返回时获取堆栈跟踪。因此它们已从堆栈中移除。
所以我想,您想看看调用图。我知道 IntelliJ / PyCharm 至少在付费版本中可以很好地做到这一点。
您可能还想尝试其他工具。 pycallgraph 你觉得如何?
更新:
Python 实际上很容易转储所有函数调用的简单列表。
基本上你需要做的就是
import sys
sys.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
但是请注意,根据我的经验,您可以通过使用普通的调试器获得对问题 "who is calling whom?" 或 "where does this value even come from?" 的最佳见解。
我还对该主题进行了一些研究,因为@LydiaVanDyke 的回答中的信息推动了更好的搜索。
打印整个调用堆栈
正如@LydiaVanDyke 指出的那样,IDE 调试器是一种非常好的方法。我使用 PyCharm,发现这是我最喜欢的解决方案,因为可以:
- 跟随函数调用 + 代码中的确切行号
- 阅读调用代码,更好地理解输入
- 跳过那些不关心调查的电话
另一种方式是Python的标准库trace
。它提供命令行和可嵌入的方法来打印整个调用堆栈。
还有一个是 Python 的内置调试器模块 pdb
。这(通过 pdb.set_trace()
调用)真的改变了我的游戏。
Profiler 输出的可视化
gprof2dot
是另一个有用的分析器可视化工具。
查找源代码
由于我的 IDE 存根文件 (PyCharm),我的另一个问题是实际上没有看到真正的源代码。
How to retrieve source code of Python functions详解两种实际打印源码的方法
使用所有这些工具,让人感觉充满力量!