Python中有标签/转到吗?
Posted
技术标签:
【中文标题】Python中有标签/转到吗?【英文标题】:Is there a label/goto in Python? 【发布时间】:2010-10-01 03:08:32 【问题描述】:是否有 goto
或 Python 中的任何等效项能够跳转到特定的代码行?
【问题讨论】:
import goto 我的一个朋友在将一些 Fortran 代码翻译成 Python 时,用 Python 实现了goto
。他恨自己。
github.com/cdjc/goto(它比 entrian 实现快得多)
上下文中的“goto”标签对于任何有经验的程序员来说都很清楚
【参考方案1】:
不,Python 不支持标签和 goto。它是一种(高度)结构化的编程语言。
【讨论】:
【参考方案2】:在实现“goto”时,首先要问什么是goto。虽然看起来很明显,但大多数人并没有考虑 goto 与函数堆栈的关系。
如果您在函数内部执行“goto”,实际上就是放弃了函数调用堆栈。这被认为是不好的做法,因为函数堆栈的设计期望您在委派中间任务后从中断的地方继续。这就是为什么goto用于异常,异常可以用来模拟goto,这个我会解释。
有限状态机可能是 goto 的最佳用例,大多数情况下它是通过循环和 switch 语句以笨拙的方式实现的,但我相信“***” goto 是最干净、最语义化的方式实现有限状态机。在这种情况下,您要确保,如果您有更多变量,它们是全局变量,并且不需要封装。确保首先对变量状态空间进行建模(可能不同于执行状态,即有限状态机)。
我相信使用 goto 有合理的设计理由,异常处理是一种特殊情况,将 goto 与函数混合是有意义的。但是,在大多数情况下,您希望将自己限制为“***” goto,因此您永远不会在函数内调用 goto,而只能在全局范围内调用。
在现代语言中模拟*** goto 的最简单方法是实现*** goto,只需要全局变量和一个空的调用堆栈。因此,为了保持调用堆栈为空,每次调用新函数时都会返回。这是打印前 n 个斐波那契数的示例:
a = 0
b = 1
n = 100
def A():
global a, b
a = a + b
n -= 1
print(a)
return B() if n > 0 else 0
def B():
global a, b
b = a + b
n -= 1
print(b)
return A() if n > 0 else 0
A()
虽然这个例子可能比循环实现更冗长,但它也更加强大和灵活,并且不需要特殊情况。它让您拥有一个完整的有限状态机。您也可以使用 goto 运行器对其进行修改。
def goto(target):
while(target) target = target()
def A():
global a, b
a = a + b
print(a)
return B
def B():
global a, b
b = a + b
print(b)
return A
goto(A)
要强制执行“返回”部分,您可以编写一个 goto 函数,该函数在完成时简单地抛出一个异常。
def goto(target):
target()
throw ArgumentError("goto finished.")
def A():
global a, b
a = a + b
print(a)
goto(B)
def B()
global a, b
b = a + b
print(b)
goto(A)
goto(A)
所以你看,很多这都是过度思考,而一个调用函数然后抛出错误的辅助函数就是你所需要的。您可以进一步将其包装在“开始”函数中,以便捕获错误,但我认为这不是绝对必要的。虽然其中一些实现可能会用完您的调用堆栈,但第一个运行器示例将其保持为空,如果编译器可以进行尾调用优化,那也会有所帮助。
【讨论】:
【参考方案3】:你可以使用python内部的嵌套方法来实现它
def func1():
print("inside func1")
def inline():
print("im inside")
inline()
func1()
【讨论】:
【参考方案4】:我认为 while 循环可以替代“goto_Statement”。因为在 3.6 之后 goto 循环不再工作了。我还写了一个while循环的例子。
str1 = "stop"
while str1 == "back":
var1 = int(input(" Enter Ist Number: "))
var2 = int(input(" Enter 2nd Number: "))
var3 = print(""" What is your next operation
For Addition Press And Enter : 'A'
For Muliplt Press And Enter : 'M'
For Division Press And Enter : 'D'
For Subtaction Press And Enter : 'S' """)
var4 = str(input("For operation press any number : "))
if(var1 == 45) and (var2 == 3):
print("555")
elif(var1 == 56) and (var2 == 9):
print("77")
elif(var1 == 56) and (var2 == 6):
print("4")
else:
if(var4 == "A" or "a"):
print(var1 + var2)
if(var4 == "M" or "m"):
print(var1 * var2)
if(var4 == "D" or "d"):
print(var1 / var2)
if(var4 == "S" or "s"):
print(var1 - var2)
print("if you want to continue then type 'stop'")
str1 = input()
print("Strt again")
【讨论】:
【参考方案5】:Python 2 和 3
pip3 install goto-statement
在 Python 2.6 到 3.6 和 PyPy 上测试。
链接:goto-statement
foo.py
from goto import with_goto
@with_goto
def bar():
label .bar_begin
...
goto .bar_begin
【讨论】:
【参考方案6】:虽然在 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...')
【讨论】:
【参考方案7】:没有实现 goto 语句的替代方法
class id:
def data1(self):
name=[]
age=[]
n=1
while n>0:
print("1. for enter data")
print("2. update list")
print("3. show data")
print("choose what you want to do ?")
ch=int(input("enter your choice"))
if ch==1:
n=int(input("how many elemet you want to enter="))
for i in range(n):
name.append(input("NAME "))
age.append(int(input("age ")))
elif ch==2:
name.append(input("NAME "))
age.append(int(input("age ")))
elif ch==3:
try:
if name==None:
print("empty list")
else:
print("name \t age")
for i in range(n):
print(name[i]," \t ",age[i])
break
except:
print("list is empty")
print("do want to continue y or n")
ch1=input()
if ch1=="y":
n=n+1
else:
print("name \t age")
for i in range(n):
print(name[i]," \t ",age[i])
n=-1
p1=id()
p1.data1()
【讨论】:
【参考方案8】:代替 python goto 等效项,我以以下方式使用 break 语句来快速测试我的代码。这假设您有结构化的代码库。测试变量在函数开始时初始化,我只是将“If test: break”块移动到我要测试的嵌套 if-then 块或循环的末尾,在代码末尾修改返回变量反映我正在测试的块或循环变量。
def x:
test = True
If y:
# some code
If test:
break
return something
【讨论】:
【参考方案9】:对于前向 Goto,您只需添加:
while True:
if some condition:
break
#... extra code
break # force code to exit. Needed at end of while loop
#... continues here
这仅对简单的场景有帮助(即嵌套这些会让你陷入混乱)
【讨论】:
【参考方案10】:我有自己的 goto 方式。 我使用单独的 python 脚本。
如果我想循环:
file1.py
print("test test")
execfile("file2.py")
a = a + 1
file2.py
print(a)
if a == 10:
execfile("file3.py")
else:
execfile("file1.py")
file3.py
print(a + " equals 10")
(注意:此技术仅适用于 Python 2.x 版本)
【讨论】:
【参考方案11】:使用来自 cmets 的@bobince
的建议来回答@ascobol
's question:
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
。见Why does python use 'else' after for and while loops?
【讨论】:
@B1KMusic:缩进是正确的。它是一种特殊的 Python 语法。如果没有遇到break
,则在循环之后执行else
。效果是should_terminate_the_loop
终止 内循环和外循环。
我应该指定我只是在进行编辑后才发现。在此之前,我以为我在解释器中发现了一个错误,所以我发了a bunch of test cases 和did some research 以了解发生了什么。对此感到抱歉。
现在我明白发生了什么,我同意,这是一些深奥的代码,使用more traditional methods 会更容易完成
@B1KMusic:您建议的方法(复制粘贴条件两次)更糟。通常,可以使用found
、done
布尔变量代替。
@B1KMusic:不。复制代码来解决你的无知并不是一个好的解决方案。是的。 return
suggested by @Jason Baker 是跳出深度嵌套循环的好选择。【参考方案12】:
我在official python Design and History FAQ 中找到了这个。
为什么没有 goto?
您可以使用异常来提供甚至可以工作的“结构化 goto” 跨函数调用。很多人觉得异常可以方便 模拟 C 的“go”或“goto”结构的所有合理使用, Fortran 和其他语言。例如:
class label(Exception): pass # declare a label
try:
...
if condition: raise label() # goto label
...
except label: # where to goto
pass
...
这不允许你跳到循环的中间,但那是 无论如何,通常被认为是对 goto 的滥用。谨慎使用。
很高兴官方常见问题解答中甚至提到了这一点,并提供了一个很好的解决方案示例。我真的很喜欢 python,因为它的社区甚至像这样对待goto
;)
【讨论】:
滥用goto
可以肯定是一个主要的编程错误,但IMO 滥用异常来模仿goto
只是稍微好一点,仍然应该被严重反对。我宁愿 Python 的创建者在语言中包含 goto
在它实际上有用的少数场合而不是因为“这很糟糕,伙计们”而禁止它,然后建议滥用异常来获得相同的功能(和相同的代码意大利面条)。
这是常见问题解答中有趣的部分。常见问题解答中的标题有点误导,因为它实际上并没有回答“为什么”部分。
@np8:我认为它确实在说......“你可以使用异常来提供'结构化的goto'”......并且......“许多人认为异常可以方便地模拟所有合理的'go' 或 'goto' 的用法”。因此,我将其解释为“python 中没有专门的 goto,因为使用该语言的其他机制可以“轻松”实现功能甚至更多功能。但我也同意没有真正的理由不添加它作为“语法糖”。恕我直言,我认为重点是“结构化”,而不是 goto,它可以在代码中的任何地方跳来跳去。
大声笑,我在想我是第一个使用try...except...
模拟 goto 的人(因为几乎所有来自互联网的答案都建议使用第三部分 python-goto 库)。所以试图在这里回答,直到看到这个答案。很高兴看到官方文档中提到了这一点。【参考方案13】:
我正在寻找类似的东西
for a in xrange(1,10):
A_LOOP
for b in xrange(1,5):
for c in xrange(1,5):
for d in xrange(1,5):
# do some stuff
if(condition(e)):
goto B_LOOP;
所以我的方法是使用布尔值来帮助摆脱嵌套的 for 循环:
for a in xrange(1,10):
get_out = False
for b in xrange(1,5):
if(get_out): break
for c in xrange(1,5):
if(get_out): break
for d in xrange(1,5):
# do some stuff
if(condition(e)):
get_out = True
break
【讨论】:
【参考方案14】:现在有。 goto
我认为这可能对您正在寻找的内容有用。
【讨论】:
【参考方案15】:我最近wrote a function decorator 在 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 中是可能的,而不仅仅是像 other guy 那样滥用调试器 API。不过,您必须弄乱字节码。
【讨论】:
你做的很棒的装饰器!太棒了,你如何摆弄字节码:-) 我认为,这应该是这个问题的公认答案。这可能对许多嵌套循环很有用,为什么不呢? 这只支持.begin
和.end
标签吗?
我写了一个简单的程序来测试你的模块。使用 Python 3.9。并且在 goto.py
模块中的 line-num 175
内行 return _make_code(code, buf.tostring())
出现异常,异常是 AttributeError: 'array.array' object has no attribute 'tostring'
。无论如何感谢伟大的模块!如果您设法修复此错误,期待再次测试您的模块。
关于我之前的评论 - 我发现 .tostring()
已被弃用(从 Python 3.2 开始猜测)并在 Python 3.9 中删除,您必须改用 .tobytes()
。但即使有了这个修复,我在goto.py
行53
代码return types.CodeType(*args)
错误TypeError: an integer is required (got type bytes)
中遇到另一个错误。看起来_make_code()
内的args
数组有错误的参数数量或其类型,在您放置bytes()
的代码Python 的地方需要int
。【参考方案16】:
我想要相同的答案,但我不想使用goto
。所以我使用了以下示例(来自 learnpythonthehardway)
def sample():
print "This room is full of gold how much do you want?"
choice = raw_input("> ")
how_much = int(choice)
if "0" in choice or "1" in choice:
check(how_much)
else:
print "Enter a number with 0 or 1"
sample()
def check(n):
if n < 150:
print "You are not greedy, you win"
exit(0)
else:
print "You are nuts!"
exit(0)
【讨论】:
【参考方案17】:通过一些工作向 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()
希望这能回答问题。
【讨论】:
很棒的实用功能! (goto()
) 对我来说效果很好。但仅在 Python 2 中。在 Python 3 中使用时有很多例外情况(例如,new
模块被 Py 3 替换为 types
,.func_code.
替换为 .__code__.
等)。如果您可以将此功能应用于 Python 3,那就太好了。【参考方案18】:
已经制作了一个工作版本:http://entrian.com/goto/。
注意:它是作为愚人节的玩笑提供的。 (虽然工作)
# Example 1: Breaking out from a deeply nested loop:
from goto import goto, label
for i in range(1, 10):
for j in range(1, 20):
for k in range(1, 30):
print i, j, k
if k == 3:
goto .end
label .end
print "Finished\n"
不用说。是的,它很有趣,但不要使用它。
【讨论】:
对我来说看起来比使用 3 个休息点更好......当然还有其他方法来编写它。 @Nick 使用带有 return 的函数看起来更好。【参考方案19】:您可以使用User-defined Exceptions 模拟goto
示例:
class goto1(Exception):
pass
class goto2(Exception):
pass
class goto3(Exception):
pass
def loop():
print 'start'
num = input()
try:
if num<=0:
raise goto1
elif num<=2:
raise goto2
elif num<=4:
raise goto3
elif num<=6:
raise goto1
else:
print 'end'
return 0
except goto1 as e:
print 'goto1'
loop()
except goto2 as e:
print 'goto2'
loop()
except goto3 as e:
print 'goto3'
loop()
【讨论】:
很棒的方法,但我们可以静音 str 异常 m 方法 @Anonymous 哪个例外?你用python3?【参考方案20】:break
和 continue
的标签早在 2007 年就在 PEP 3136 中提出,但被拒绝了。提案的Motivation 部分说明了几种在 Python 中模仿标记为 break
的常见(如果不优雅)方法。
【讨论】:
【参考方案21】: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 的最佳方式。但如果不确切知道您想用 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 不适合你。 :-)
【讨论】:
明智地使用它。 Python 中的异常比大多数其他语言更快。但如果你对它们发疯,它们仍然很慢。 请注意:loopfunc
通常需要投入和更多努力才能实施,但在大多数情况下,我认为这是最好的方式。
抱歉,不应该使用异常来控制程序流。以上是关于Python中有标签/转到吗?的主要内容,如果未能解决你的问题,请参考以下文章
如何让wordpress自带的TinyMCE编辑器识别更多html标签
用 Python 生成的 Tensorflow 数据集在 Tensorflow Java API(标签图像)中有不同的读数