为什么我的 python 代码在调试器中如预期的那样 运行 而不是?
Why does my python code run as expected in the debugger but not otherwise?
我在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()
这个例子很简单;效果应该是将字符串中的所有数字相加,包括括号中的数字。
tokenize() 函数生成令牌,parse_tok() 函数解析令牌流。如果它遇到一个左括号,它会递归(将 OpenBracket 推到令牌流上),这应该具有将括号中的数字视为单独表达式的效果,对其进行解析并添加结果到 result 堆栈。
当我解析代码时,例如在表达式“1(2)3”上,它在右括号后立即结束,返回 3 实际上令牌流似乎已经结束。
当我 运行 它使用 pdb 但是,并在 parse_tok 的循环内设置断点时,我可以在它处理 ')' 和正确处理程序时小心操作 returns 6.
我认为该错误与从 push_on_stream() 中的令牌流产生有关。
这是解释器中的错误吗?如果是这样,有什么好的解决方法吗?
我是为 python-3.6 编写的,但我也在 python-3.7 上在另一台机器上测试过,结果相同。
问题正是您描述的地方:
elif tok == ')':
if not leading_brak:
raise SyntaxError("Very bad ')'.")
break
一旦你击中右括号,你就会中止配对循环,异常或显式中断。只需删除 break
。您期望这里有什么功能?
测试代码:
test("1(2)3")
test()
test("1(2(4)8)5")
输出值:
6
[]
13
[]
20
[]
假设
当 break
语句离开循环时,会引发一个 GeneratorExit
异常,该异常会通过生成器传播。 pdb
修改了它的传播方式,这正是我希望它引入的那种微妙的错误,导致它不会耗尽 push_on_stream
来自 yield
的生成器。
测试
如果我们将 push_on_stream
更改为:
def push_on_stream(obj, stream):
yield obj
yield from stream
至:
def push_on_stream(obj, stream):
yield obj
stream = iter(stream)
while True:
yield next(stream)
那么这将对其产生足够的影响以保证两种情况下的正确行为。
结果
错误已修复!
说明
提供的更好。 基本上、yield from
并不像您认为的那样工作;当生成器由于 break
语句退出时,yield from
会导致您正在迭代的生成器将其自身标记为耗尽。 (pdb
打断了这个,因为它有点小问题。)这会导致您的解析器在第一个 )
处终止,因为当第一个 break
语句运行时底层迭代器停止。
您的 push_on_stream
并不像您认为的那样有效。
看到,当 push_on_stream
生成器被回收时,Python 调用生成器上的 close
,它向生成器抛出一个 GeneratorExit
以确保任何 finally
块和 __exit__
方法 运行。由于 push_on_stream
在底层生成器上使用 yield from
,如果 push_on_stream
在 yield from
中暂停,这会在底层 GeneratorExit
=21=]生成器.
这会立即终止令牌流。在 pdb 中,某些东西导致 push_on_stream
生成器未被收集,从而阻止了这种效果。
我在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()
这个例子很简单;效果应该是将字符串中的所有数字相加,包括括号中的数字。
tokenize() 函数生成令牌,parse_tok() 函数解析令牌流。如果它遇到一个左括号,它会递归(将 OpenBracket 推到令牌流上),这应该具有将括号中的数字视为单独表达式的效果,对其进行解析并添加结果到 result 堆栈。
当我解析代码时,例如在表达式“1(2)3”上,它在右括号后立即结束,返回 3 实际上令牌流似乎已经结束。
当我 运行 它使用 pdb 但是,并在 parse_tok 的循环内设置断点时,我可以在它处理 ')' 和正确处理程序时小心操作 returns 6.
我认为该错误与从 push_on_stream() 中的令牌流产生有关。
这是解释器中的错误吗?如果是这样,有什么好的解决方法吗?
我是为 python-3.6 编写的,但我也在 python-3.7 上在另一台机器上测试过,结果相同。
问题正是您描述的地方:
elif tok == ')':
if not leading_brak:
raise SyntaxError("Very bad ')'.")
break
一旦你击中右括号,你就会中止配对循环,异常或显式中断。只需删除 break
。您期望这里有什么功能?
测试代码:
test("1(2)3")
test()
test("1(2(4)8)5")
输出值:
6
[]
13
[]
20
[]
假设
当 break
语句离开循环时,会引发一个 GeneratorExit
异常,该异常会通过生成器传播。 pdb
修改了它的传播方式,这正是我希望它引入的那种微妙的错误,导致它不会耗尽 push_on_stream
来自 yield
的生成器。
测试
如果我们将 push_on_stream
更改为:
def push_on_stream(obj, stream):
yield obj
yield from stream
至:
def push_on_stream(obj, stream):
yield obj
stream = iter(stream)
while True:
yield next(stream)
那么这将对其产生足够的影响以保证两种情况下的正确行为。
结果
错误已修复!
说明
yield from
并不像您认为的那样工作;当生成器由于 break
语句退出时,yield from
会导致您正在迭代的生成器将其自身标记为耗尽。 (pdb
打断了这个,因为它有点小问题。)这会导致您的解析器在第一个 )
处终止,因为当第一个 break
语句运行时底层迭代器停止。
您的 push_on_stream
并不像您认为的那样有效。
看到,当 push_on_stream
生成器被回收时,Python 调用生成器上的 close
,它向生成器抛出一个 GeneratorExit
以确保任何 finally
块和 __exit__
方法 运行。由于 push_on_stream
在底层生成器上使用 yield from
,如果 push_on_stream
在 yield from
中暂停,这会在底层 GeneratorExit
=21=]生成器.
这会立即终止令牌流。在 pdb 中,某些东西导致 push_on_stream
生成器未被收集,从而阻止了这种效果。