为什么我的python代码在调试器中按预期运行但不是这样?

Sil*_*ker 4 python debugging parsing python-3.x pdb

我在python3.6中编写了一个解析器; 我仍在尽可能地简化它,同时仍然产生错误:

def tokenize(expr):
    for i in expr:
        try:
            yield int(i)
        except ValueError:
            yield i


def push_on_stream(obj, stream):
    yield obj
    yield from stream


class OpenBracket:
    "just a token value, could have used Ellipsis"
    pass


def parse_toks(tokstream):
    result = []
    leading_brak = False
    for tok in tokstream:
        if tok == OpenBracket:
            leading_brak = True
        elif tok == '(':
            result.append(parse_toks(
                push_on_stream(OpenBracket, tokstream)))
        elif tok == ')':
            if not leading_brak:
                raise SyntaxError("Very bad ')'.")
            break
        else:
            result.append(tok)
    return sum(result)


def test(expr="12(34)21"):
    tokens = tokenize(expr)
    print( parse_toks(tokens) )
    print(list(tokens))

test()
Run Code Online (Sandbox Code Playgroud)

这个例子很简单; 效果应该是添加字符串中的所有数字,包括括号中的数字.

tokenize()函数产生令牌,parse_tok()函数解析令牌流.如果它遇到一个开括号,它会递归(将OpenBracket推送到令牌流),这应该具有将括号中的数字作为单独的表达式处理,解析它并将结果添加到结果堆栈的效果.

当我解析代码时,例如在表达式"1(2)3"上,它立即在close括号后结束,返回3,实际上令牌流似乎已经结束.

然而,当我使用pdb运行它,并在parse_tok中的循环内设置断点时,我可以在处理')'时仔细步骤,程序正确返回6.

我认为这个bug与push_on_stream()中令牌流的屈服有关.

这是解释器中的错误吗?如果是这样有一个很好的解决方法?

我为python-3.6编写了它,但我也在python-3.7上在不同的机器上测试了它,结果相同.

use*_*ica 5

你的push_on_stream工作方式并不像你认为的那样.

看看,当push_on_stream回收生成器时,Python会调用close生成器,它会GeneratorExit生成一个生成器,以确保运行任何finally块和__exit__方法.由于push_on_stream使用yield from对底层发生器,如果push_on_stream是悬浮在yield from,这将引发一个GeneratorExit 在底层tokenize发生器.

这会立即终止令牌流.在pdb中,某些东西导致push_on_stream生成器无法收集,从而阻止了这种影响.

  • @ wizzwizz4:呃,你以为是"StopIteration".`StopIteration`不是那样的. (2认同)