获取中间值在Python?

问题描述 投票:2回答:2

我试着写一个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模块或相似的东西。

python exception-handling python-internals
2个回答
3
投票

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_NAMECALL_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()功能。在这一点上,中介结果从堆不见了。在上述块的局部rightleft指针也长,长了(C使用块范围,并且当被执行时goto error范围退出所以变量都将丢失)。


-3
投票

使用Python的try/except块:

g = greeting()
n = name()
try:
    g + n
except:
    raise ValueError('g: %s, n: %s' % (g, n))

对于@LukasGraf,关于“适当的Python编程实践”阅读列表:

© www.soinside.com 2019 - 2024. All rights reserved.