我试着写一个Python sys.excepthook
其中,除了打印出堆栈跟踪代码,你写的,还打印出每个评估值的repr
。
例如,如果我跑了下面的代码:
def greeting():
return 'Hello'
def name():
return
greeting() + name()
而不是只打印出:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
greeting() + name()
TypeError: cannot concatenate 'str' and 'NoneType' objects
它也将打印出来'Hello' + None
所以我马上就可以看到它的值是无效的,并知道代码的右侧区域寻找在(显然这是一个非常简单的例子)。
我知道,CPU需要这些中间值存储在一些临时寄存器......我怀疑是内部Python有做类似的东西,我希望有一些方法可以让我访问这些临时值,可能通过inspect
模块或相似的东西。
由sys.exceptionhook()
被调用的时候,你不能让那些中介值更多,因为他们都已经走了。是的,分量表达式的中间结果是由Python的地方保存。您无法访问“的地方”直接的时候,也不是当发生异常时的周围保持在所有。
在CPython的,标准的Python实现,那某处'是附接至执行的当前帧的堆栈(每个有源函数有一个)。 Python代码被编译为字节码,并评估循环随后执行的字节码,并在individual bytecode instructions字节码该堆栈上的操作。
您可以使用dis.dis()
function来查看所使用的字节码的例子表达:
>>> import dis
>>> dis.dis("greeting() + name()")
1 0 LOAD_NAME 0 (greeting)
2 CALL_FUNCTION 0
4 LOAD_NAME 1 (name)
6 CALL_FUNCTION 0
8 BINARY_ADD
10 RETURN_VALUE
再看看那些什么字节码指令做:
LOAD_NAME 0
发现命名greeting
对象,并把该对堆(TOS)的顶部。CALL_FUNCTION 0
从栈中删除0个元素是参数为一个呼叫,则采取的下一个对象从栈是可调用对象,调用与该论点,即对象,并把结果作为新的TOS。BINARY_ADD
采取顶部的两个元素从堆栈中,增加了他们,并把结果返回的TOS。所以综合起来,LOAD_NAME
和CALL_FUNCTION
执行调用一个命名对象,堆栈的顶部最终引用两个结果,在name()
结果的顶部greeting()
结果。然后将BINARY_ADD
指令替代栈与添加在一起的结果关于这两个结果。
您不必从内部的Python访问该堆栈,因为它是执行Python字节码,使Python的工作摆在首位的非常行为。可能访问堆栈中的任何代码必须处理这种堆栈当前正在使用来执行Python代码的事实!
但你有一个更大的问题。如果你看看CPython的源代码,你可以搜索在ceval.c
评价循环指令的名称。当你在看BINARY_ADD
instruction implementation,你可以看到两个输入值从堆栈相加,然后删除之前:
TARGET(BINARY_ADD) {
PyObject *right = POP();
PyObject *left = TOP();
PyObject *sum;
// code to set sum as the result of addibg left to right
SET_TOP(sum);
if (sum == NULL)
goto error;
DISPATCH();
}
如果BINARY_ADD
失败并抛出异常,sum == NULL
是真实的,并执行goto error
放松一下调用堆栈和传播异常一起到第一try
块,或做不到这一点,最终调用sys.excepthook()
功能。在这一点上,中介结果从堆不见了。在上述块的局部right
和left
指针也长,长了(C使用块范围,并且当被执行时goto error
范围退出所以变量都将丢失)。
使用Python的try/except
块:
g = greeting()
n = name()
try:
g + n
except:
raise ValueError('g: %s, n: %s' % (g, n))
对于@LukasGraf,关于“适当的Python编程实践”阅读列表: