C'è un'etichetta / goto in Python?


178

C'è un gotoo qualche equivalente in Python per essere in grado di passare a una specifica riga di codice?


2
L'etichetta è piuttosto vaga: puoi essere più specifico su ciò che stai cercando?
Dana,


9
Un mio amico implementato gotoin Python mentre traduceva un codice Fortran in Python. Si odiava per questo.
Cody Piersall,

3
github.com/cdjc/gotomolto più veloce dell'implementazione di entrian)
cdjc

"L'etichetta è piuttosto vaga", nessuna etichetta è intelligente, strutturata funziona come una macchina
datdinhquoc

Risposte:


118

No, Python non supporta etichette e goto, se è quello che stai cercando. È un linguaggio di programmazione (altamente) strutturato.


36
Funzioni @rejinacm?
UnkwnTech,

79

Python ti offre la possibilità di fare alcune delle cose che potresti fare con un goto usando funzioni di prima classe. Per esempio:

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

    label1:
        ...
    label2:
        ...
}

Potrebbe essere fatto in Python in questo modo:

def func1():
    ...

def func2():
    ...

funcmap = {1 : func1, 2 : func2}

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

Certo, non è il modo migliore per sostituire Goto. Ma senza sapere esattamente cosa stai cercando di fare con il goto, è difficile dare consigli specifici.

@ ascobol :

La soluzione migliore è racchiuderla in una funzione o utilizzare un'eccezione. Per la funzione:

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

Per l'eccezione:

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

Usare le eccezioni per fare cose del genere può sembrare un po 'imbarazzante se vieni da un altro linguaggio di programmazione. Ma direi che se non ti piace usare le eccezioni, Python non è la lingua che fa per te. :-)


Usalo con giudizio. Le eccezioni in Python sono più veloci della maggior parte delle altre lingue. Ma sono ancora lenti se impazzisci con loro.
Jason Baker,

Solo un avviso: loopfuncgeneralmente richiederà input e qualche sforzo in più da implementare, ma è il modo migliore nella maggior parte dei casi penso.
kon psych,

60

Di recente ho scritto un decoratore di funzioni che abilita gotoin Python, proprio così:

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

Non sono sicuro del motivo per cui uno vorrebbe fare qualcosa del genere però. Detto questo, non sono troppo serio. Ma vorrei sottolineare che questo tipo di meta-programmazione è effettivamente possibile in Python, almeno in CPython e PyPy, e non solo abusando dell'API di debugger come faceva l' altro ragazzo . Devi pasticciare con il bytecode però.


3
Ottimo decoratore che hai realizzato! Fantastico come armeggiare con il bytecode :-)
K.Mulier

Penso che questa dovrebbe essere la risposta accettata per questa domanda. Questo potrebbe essere utile per molti loop nidificati, perché no?
PiMathCLanguage

Questo supporta solo .begined .endetichette?
Alexej Magura,

29

L'ho trovato nelle FAQ ufficiali su Design e Storia di Python .

Perché non c'è goto?

È possibile utilizzare le eccezioni per fornire un "goto strutturato" che funzioni anche attraverso le chiamate di funzione. Molti ritengono che le eccezioni possano facilmente emulare tutti gli usi ragionevoli dei costrutti "go" o "goto" di C, Fortran e altre lingue. Per esempio:

class label(Exception): pass  # declare a label

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

Questo non ti consente di saltare nel mezzo di un loop, ma di solito è considerato un abuso di goto. Usa con parsimonia.

È molto bello che questo sia anche menzionato nelle FAQ ufficiali e che sia fornito un esempio di soluzione piacevole. Mi piace molto Python perché la sua community sta trattando anche in gotoquesto modo;)


1
L'abuso gotoè un importante passo in avanti per la programmazione, ma le eccezioni dell'OMO che abusano di emulare gotoè solo leggermente migliore e dovrebbe essere ancora fortemente disapprovato. Preferirei che i creatori di Python includessero gotonel linguaggio per le poche occasioni in cui in realtà è utile che non consentirlo perché "è un male, ragazzi" e quindi raccomandare di abusare delle eccezioni per ottenere la stessa funzionalità (e lo stesso spaghettificazione del codice).
Abion47

15

Per rispondere alla @ascoboldomanda usando @bobinceil suggerimento dei commenti:

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

Il trattino per il else blocco è corretto. Il codice usa oscuro elsedopo una sintassi Python in loop. Vedi Perché python usa 'else' dopo e per i cicli while?


Ho corretto il rientro del blocco degli altri, che ha portato a una scoperta interessante :
Braden Best

3
@ B1KMusic: il rientro è corretto così com'è. È una sintassi speciale di Python. elseviene eseguito dopo il ciclo se breaknon è stato riscontrato. L'effetto è che should_terminate_the_looptermina entrambi i cicli interni ed esterni.
jfs,

1
Avrei dovuto specificare che ho fatto quella scoperta solo dopo aver effettuato la modifica. Prima di allora, pensavo di aver scoperto un bug nell'interprete, quindi ho fatto un sacco di casi di test e fatto alcune ricerche per capire cosa stava succedendo. Mi dispiace per quello.
Braden Best

1
Ora che capisco cosa sta succedendo, sono d'accordo, che è un codice esoterico che verrebbe fatto molto più facilmente con metodi più tradizionali
Braden Best

1
@ B1KMusic: No. La duplicazione del codice per aggirare la tua ignoranza non è una buona soluzione. Sì. return suggerito da @Jason Baker è una buona alternativa per uscire da anelli profondamente annidati.
jfs,

12

È stata realizzata una versione funzionante: http://entrian.com/goto/ .

Nota: è stato offerto come uno scherzo del pesce d'aprile. (funzionando però)

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

Inutile dire che. Sì, è divertente, ma NON usarlo.


1
mi sembra meglio dell'uso di 3 pause ... ovviamente ci sono anche altri modi per scriverlo.
Nick,

1
@Nick L'uso della funzione con il ritorno è ancora migliore.
Erik Šťastný,

7

Le etichette per breake continuesono state proposte in PEP 3136 nel 2007, ma è stata respinta. La sezione Motivazione della proposta illustra diversi metodi comuni (se non eleganti) per l'imitazione etichettati breakin Python.


7

È tecnicamente possibile aggiungere un'istruzione simile a "goto" su Python con qualche lavoro. Useremo i moduli "dis" e "new", entrambi molto utili per la scansione e la modifica del codice byte python.

L'idea principale dietro l'implementazione è di contrassegnare innanzitutto un blocco di codice come usando le istruzioni "goto" e "label". Uno speciale decoratore "@goto" verrà utilizzato per contrassegnare le funzioni "goto". Successivamente scansioniamo quel codice per queste due istruzioni e applichiamo le modifiche necessarie al codice byte sottostante. Tutto ciò accade al momento della compilazione del codice sorgente.

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

Spero che questo risponda alla domanda.


5

è possibile utilizzare le eccezioni definite dall'utente per emularegoto

esempio:

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

Metodo fantastico ma possiamo escludere il metodo str eccezione m
Anonimo

@Anonimo quale eccezione? usi python3?
Xavierskip,

5

Python 2 e 3

pip3 install goto-statement

Testato su Python da 2.6 a 3.6 e PyPy.

Link: goto-statement


foo.py

from goto import with_goto

@with_goto
def bar():

    label .bar_begin

    ...

    goto .bar_begin

3

Stavo cercando qualcosa di simile a

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;

Quindi il mio approccio è stato quello di usare un booleano per evadere dai loop nidificati:

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

2

C'è adesso. vai a

Penso che questo potrebbe essere utile per quello che stai cercando.


1

Volevo la stessa risposta e non volevo usare goto. Quindi ho usato il seguente esempio (da 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)

1

Ho il mio modo di fare goto. Uso script Python separati.

Se voglio fare un ciclo:

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

( NOTA: questa tecnica funziona solo su versioni Python 2.x)


1

Per andare avanti, puoi semplicemente aggiungere:

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

Questo aiuta solo per scenari semplici (ad esempio, nidificarli potrebbe farti confondere)


1

Al posto di un equivalente di Python Goto, utilizzo la seguente dichiarazione di interruzione nel modo seguente per test rapidi del mio codice. Ciò presuppone che tu abbia una base di codice strutturata. La variabile test viene inizializzata all'inizio della tua funzione e sposto semplicemente il blocco "If test: break" alla fine del blocco if-then nidificato che voglio testare, modificando la variabile return alla fine del codice per riflettere la variabile di blocco o loop che sto testando.

def x:
  test = True
  If y:
     # some code
     If test:
            break
  return something

1

Sebbene non ci sia un codice equivalente a goto/labelin Python, potresti comunque ottenere tale funzionalità goto/labelusando i loop.

Consente di prendere un esempio di codice mostrato di seguito in cui goto/labelpuò essere utilizzato in un linguaggio arbitrario diverso da 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...')

Ora la stessa funzionalità dell'esempio di codice sopra può essere ottenuta in Python usando un whileloop come mostrato di seguito.

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...')

0

no esiste un modo alternativo per implementare la dichiarazione 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()  
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.