Python中的`goto`

29 投票
6 回答
28352 浏览
提问于 2025-04-16 22:58

我必须在Python中使用goto。我找到了一些资料,比如entrian的goto,但我的Python版本(Mac上的CPython 2.7.1)没有这个模块,所以看起来不太通用。理论上,它应该能在所有支持CPython字节码的Python实现中工作(尤其是我关心的CPython和PyPy)。还有这个相关问题,以及cdjc的goto,还有下面回答中提到的其他方法。

我可以手动构建字节码(也就是自己写一个Python编译器),因为有这样的指令(JUMP_ABSOLUTE等)。但我在想有没有更简单的方法。是否可以通过inspect之类的工具调用单个字节码指令?我还考虑过通过Python编译,然后自动修补生成的Python字节码。


当然,人们会问我为什么,如果我不解释我真的非常需要这个,可能就得不到任何有用的回答。简单来说,我的使用场景是:我正在把一个C语言的抽象语法树(AST)翻译成Python的抽象语法树,并进行编译。我可以把每个逻辑流程(所有的循环和其他东西)以某种方式映射到等效的Python代码。除了goto以外,其他的都可以。相关项目有:PyCParser(见interpreter.py),PyCPythonPyLua

6 个回答

5

我更新了我的 Python goto 装饰器,适用于 Python 3。你可以在这里找到它:https://github.com/cdjc/goto。使用 goto 代替函数可以让状态机的运行速度快大约五倍。

Python 2 的版本仍然可以在这里找到:http://code.activestate.com/recipes/576944-the-goto-decorator/,不过这个版本有一些错误,而这些错误在 Python 3 的版本中已经修复了。

9

你可能是我见过的唯一一个需要在Python中使用goto的合理例子。:-)

在Python中模拟前向goto最简单的方法是使用异常,因为异常可以跳出任何深度的嵌套控制结构。

class Goto(Exception):
    pass

try:
    if foo = "bar":
        raise Goto
    print "foo is not bar"
except Goto:
    print "foo is bar"

如果你需要支持多个目标,这就变得复杂了,不过我觉得可以通过嵌套的try/except结构和多种异常类来实现,每个目标对应一种异常。由于C语言将goto限制在单个函数的范围内,至少你不用担心如何在不同函数之间使用它。:-) 当然,这种方法不适用于反向goto

另外要注意的是,虽然Python中的异常处理比某些语言快,但仍然比正常的控制结构(比如whilefor)要慢。

这可能会需要很多工作(不过也许不比你现在要做的更多),但如果你能生成Python字节码而不是Python源代码,那么实现goto就没问题了,因为Python字节码(就像大多数伪机器语言一样)有一个非常合适的JUMP_ABSOLUTE操作码。

44

我知道大家在想什么:

xkcd GOTO

不过,有些情况下你可能真的需要用到 goto

这个 Python 的小技巧提供了一个将 goto 命令作为函数装饰器的方式。

goto 装饰器 (这是 Carl Cerecke 的 Python 小技巧)

如果你对现有的 goto 模块的速度感到厌烦,这个小技巧就是为你准备的。这个小技巧中的 goto 快了大约 60 倍,而且代码也更简洁(使用 sys.settrace 看起来不太符合 Python 的风格)。因为这是一个装饰器,它能让读者知道哪些函数使用了 goto。它没有实现 comefrom 命令,虽然扩展它来实现这个功能并不难(留给读者自己去做)。另外,计算得到的 goto 也不支持;这在 Python 中不太常见。

  • 使用 dis.dis(fn) 可以显示一个函数的字节码反汇编。
  • 函数的字节码可以通过 fn.func_code.co_code 访问。这是只读的,所以:
  • 被装饰的函数和旧的函数完全一样,但字节码会更新以遵循 goto 命令。
  • 这个小技巧只适用于 Python 2.x;在 Python 3.x 中没有这个新模块(另一个留给读者的练习!)

用法

@goto
def test1(n):
    s = 0

    label .myLoop

    if n <= 0:
        return s
    s += n
    n -= 1

    goto .myLoop

>>> test1(10)
55

更新

这里有两个与 Python 3 兼容的额外实现:

撰写回答