Is there a label/goto in Python?

2019-01-01 07:35发布

问题:

Is there a goto or any equivalent in Python to be able to jump to a specific line of code?

回答1:

No, Python does not support labels and goto, if that is what you\'re after. It\'s a (highly) structured programming language.



回答2:

Python offers you the ability to do some of the things you could do with a goto using first class functions. For example:

void somefunc(int a)
{
    if (a == 1)
        goto label1;
    if (a == 2)
        goto label2;

    label1:
        ...
    label2:
        ...
}

Could be done in python like this:

def func1():
    ...

def func2():
    ...

funcmap = {1 : func1, 2 : func2}

def somefunc(a):
    funcmap[a]()  #Ugly!  But it works.

Granted, that isn\'t the best way to substitute for goto. But without knowing exactly what you\'re trying to do with the goto, it\'s hard to give specific advice.

@ascobol:

Your best bet is to either enclose it in a function or use an exception. For the function:

def loopfunc():
    while 1:
        while 1:
            if condition:
                return

For the exception:

try:
    while 1:
        while 1:
            raise BreakoutException #Not a real exception, invent your own
except BreakoutException:
    pass

Using exceptions to do stuff like this may feel a bit awkward if you come from another programming language. But I would argue that if you dislike using exceptions, Python isn\'t the language for you. :-)



回答3:

I recently wrote a function decorator that enables goto in Python, just like that:

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

I\'m not sure why one would like to do something like that though. That said, I\'m not too serious about it. But I\'d like to point out that this kind of meta programming is actual possible in Python, at least in CPython and PyPy, and not only by misusing the debugger API as that other guy did. You have to mess with the bytecode though.



回答4:

To answer the @ascobol\'s question using @bobince\'s suggestion from the comments:

for i in range(5000):
    for j in range(3000):
        if should_terminate_the_loop:
           break
    else: 
        continue # no break encountered
    break

The indent for the else block is correct. The code uses obscure else after a loop Python syntax. See Why does python use \'else\' after for and while loops?



回答5:

I found this in the official python Design and History FAQ.

Why is there no goto?

You can use exceptions to provide a “structured goto” that even works across function calls. Many feel that exceptions can conveniently emulate all reasonable uses of the “go” or “goto” constructs of C, Fortran, and other languages. For example:

class label(Exception): pass  # declare a label

try:
    ...
    if condition: raise label()  # goto label
    ...
except label:  # where to goto
    pass
... 

This doesn’t allow you to jump into the middle of a loop, but that’s usually considered an abuse of goto anyway. Use sparingly.

It\'s very nice that this is even mentioned in the official FAQ, and that a nice solution sample is provided. I really like python because its community is treating even goto like this ;)



回答6:

A working version has been made: http://entrian.com/goto/.

Note: It was offered as an April Fool\'s joke. (working though)

# 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\"

Needless to say. Yes its funny, but DONT use it.



回答7:

Labels for break and continue were proposed in PEP 3136 back in 2007, but it was rejected. The Motivation section of the proposal illustrates several common (if inelegant) methods for imitating labeled break in Python.



回答8:

It is technically feasible to add a \'goto\' like statement to python with some work. We will use the \"dis\" and \"new\" modules, both very useful for scanning and modifying python byte code.

The main idea behind the implementation is to first mark a block of code as using \"goto\" and \"label\" statements. A special \"@goto\" decorator will be used for the purpose of marking \"goto\" functions. Afterwards we scan that code for these two statements and apply the necessary modifications to the underlying byte code. This all happens at source code compile time.

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()

Hope this answers the question.



回答9:

I was looking for some thing similar to

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;

So my approach was to use a boolean to help breaking out from the nested for loops:

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


回答10:

you can use User-defined Exceptions to emulate goto

example:

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()


回答11:

I wanted the same answer and I didnt want to use goto. So I used the following example (from 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)


回答12:

There is now. goto

I think this might be useful for what you are looking for.



回答13:

I have my own way of doing gotos. I use separate python scripts.

If I want to loop:

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\")

(NOTE: This technique only works on Python 2.x versions)



回答14:

In Python goto statement is supported with goto .<label_name> and label .<label_name>. For more info please go through this link.

def func():
    resp = call_something()
    if resp != 0: # Failure case
        goto .failure_handler

    ....
    ....
    resp = ....
    if resp != 0: # Failure case
        goto .failure_handler

    ....
    ....    

    label .failure_handler
    cleanup
    return -1   

Generally in all over planet, people recommend to avoid using goto statement. But a failure handling in a function can be handled well with only goto statement. Even I dont prefer usage of goto other than failure handler.



回答15:

For a forward Goto, you could just add:

while True:
  if some condition:
    break
  #... extra code
  break # force code to exit. Needed at end of while loop
#... continues here

This only helps for simple scenarios though (i.e. nesting these would get you into a mess)



标签: python goto