在Python中是否有goto或任何等价的东西能够跳转到特定的代码行?
当前回答
通过一些工作,在python中添加类似“goto”的语句在技术上是可行的。我们将使用"dis"和"new"模块,这两个模块对于扫描和修改python字节代码都非常有用。
实现背后的主要思想是首先将代码块标记为使用“goto”和“label”语句。一个特殊的“@goto”装饰器将用于标记“goto”函数。然后,我们扫描这两个语句的代码,并对底层字节代码应用必要的修改。这一切都发生在源代码编译时。
import dis, new
def goto(fn):
"""
A function decorator to add the goto command for a function.
Specify labels like so:
label .foo
Goto labels like so:
goto .foo
Note: you can write a goto statement before the correspnding label statement
"""
labels = {}
gotos = {}
globalName = None
index = 0
end = len(fn.func_code.co_code)
i = 0
# scan through the byte codes to find the labels and gotos
while i < end:
op = ord(fn.func_code.co_code[i])
i += 1
name = dis.opname[op]
if op > dis.HAVE_ARGUMENT:
b1 = ord(fn.func_code.co_code[i])
b2 = ord(fn.func_code.co_code[i+1])
num = b2 * 256 + b1
if name == 'LOAD_GLOBAL':
globalName = fn.func_code.co_names[num]
index = i - 1
i += 2
continue
if name == 'LOAD_ATTR':
if globalName == 'label':
labels[fn.func_code.co_names[num]] = index
elif globalName == 'goto':
gotos[fn.func_code.co_names[num]] = index
name = None
i += 2
# no-op the labels
ilist = list(fn.func_code.co_code)
for label,index in labels.items():
ilist[index:index+7] = [chr(dis.opmap['NOP'])]*7
# change gotos to jumps
for label,index in gotos.items():
if label not in labels:
raise Exception("Missing label: %s"%label)
target = labels[label] + 7 # skip NOPs
ilist[index] = chr(dis.opmap['JUMP_ABSOLUTE'])
ilist[index + 1] = chr(target & 255)
ilist[index + 2] = chr(target >> 8)
# create new function from existing function
c = fn.func_code
newcode = new.code(c.co_argcount,
c.co_nlocals,
c.co_stacksize,
c.co_flags,
''.join(ilist),
c.co_consts,
c.co_names,
c.co_varnames,
c.co_filename,
c.co_name,
c.co_firstlineno,
c.co_lnotab)
newfn = new.function(newcode,fn.func_globals)
return newfn
if __name__ == '__main__':
@goto
def test1():
print 'Hello'
goto .the_end
print 'world'
label .the_end
print 'the end'
test1()
希望这回答了问题。
其他回答
早在2007年,PEP 3136就提出了中断和继续的标签,但被拒绝了。提案的动机部分说明了在Python中模仿带标签的break的几种常见方法(虽然不太优雅)。
使用评论中@bobince的建议来回答@ascobol的问题:
for i in range(5000):
for j in range(3000):
if should_terminate_the_loop:
break
else:
continue # no break encountered
break
else块的缩进是正确的。代码在循环Python语法后使用模糊的else。参见为什么python在for和while循环之后使用'else' ?
我最近写了一个函数装饰器,在Python中启用goto,就像这样:
from goto import with_goto
@with_goto
def range(start, stop):
i = start
result = []
label .begin
if i == stop:
goto .end
result.append(i)
i += 1
goto .begin
label .end
return result
我不知道为什么有人想做这样的事情。也就是说,我并不是很认真。但我想指出的是,这种元编程在Python中实际上是可能的,至少在CPython和PyPy中是可能的,而不仅仅是像其他人那样误用调试器API。不过,您必须修改字节码。
虽然在Python中没有任何等同于goto/label的代码,但您仍然可以使用循环来获得goto/label的这种功能。
让我们以下面所示的代码示例为例,其中goto/label可以在python以外的任意语言中使用。
String str1 = 'BACK'
label1:
print('Hello, this program contains goto code\n')
print('Now type BACK if you want the program to go back to the above line of code. Or press the ENTER key if you want the program to continue with further lines of code')
str1 = input()
if str1 == 'BACK'
{
GoTo label1
}
print('Program will continue\nBla bla bla...\nBla bla bla...\nBla bla bla...')
现在,通过使用下面所示的while循环,可以在python中实现上述代码示例的相同功能。
str1 = 'BACK'
while str1 == 'BACK':
print('Hello, this is a python program containing python equivalent code for goto code\n')
print('Now type BACK if you want the program to go back to the above line of code. Or press the ENTER key if you want the program to continue with further lines of code')
str1 = input()
print('Program will continue\nBla bla bla...\nBla bla bla...\nBla bla bla...')
Python为您提供了使用第一类函数可以用goto完成的一些事情的能力。例如:
void somefunc(int a)
{
if (a == 1)
goto label1;
if (a == 2)
goto label2;
label1:
...
label2:
...
}
在Python中可以这样做:
def func1():
...
def func2():
...
funcmap = {1 : func1, 2 : func2}
def somefunc(a):
funcmap[a]() #Ugly! But it works.
当然,这并不是代替goto的最佳方式。但是如果你不知道你想用去做什么,就很难给出具体的建议。
@ascobol:
最好的方法是将其包含在函数中或使用异常。对于函数:
def loopfunc():
while 1:
while 1:
if condition:
return
对于例外情况:
try:
while 1:
while 1:
raise BreakoutException #Not a real exception, invent your own
except BreakoutException:
pass
如果您来自另一种编程语言,使用异常来做这样的事情可能会感觉有点尴尬。但我认为,如果您不喜欢使用异常,Python并不适合您。: -)
推荐文章
- Numpy Max vs amax vs maximum
- 我应该在.gitignore文件中添加Django迁移文件吗?
- 每n行有熊猫
- 实例属性attribute_name定义在__init__之外
- 如何获取在Python中捕获的异常的名称?
- 第一次出现的值大于现有值的Numpy
- 如何从Python函数中返回两个值?
- 前一个月的Python日期
- Python中方括号括起来的列表和圆括号括起来的列表有什么区别?
- Python日志记录不输出任何东西
- 每n秒运行特定代码
- SQLAlchemy是否有与Django的get_or_create等价的函数?
- 如何将python datetime转换为字符串,具有可读格式的日期?
- 美丽的汤和提取div及其内容的ID
- 在Python中重置生成器对象