¿Hay una etiqueta / goto en Python?


178

¿Hay un gotoo algún equivalente en Python para poder saltar a una línea de código específica?


2
La etiqueta es bastante vaga: ¿puede ser más específico sobre lo que está buscando?
Dana el


9
Un amigo mío lo implementó gotoen Python cuando estaba traduciendo un código Fortran a Python. Se odiaba por eso.
Cody Piersall

3
github.com/cdjc/goto (es mucho más rápido que la implementación de entrian)
cdjc

"La etiqueta es bastante vaga", ninguna etiqueta es inteligente, estructurada funciona como una máquina
datdinhquoc

Respuestas:


118

No, Python no admite etiquetas y goto, si eso es lo que buscas. Es un lenguaje de programación (altamente) estructurado.


36
@rejinacm funciones?
UnkwnTech

79

Python le ofrece la posibilidad de hacer algunas de las cosas que podría hacer con un goto utilizando funciones de primera clase. Por ejemplo:

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

    label1:
        ...
    label2:
        ...
}

Podría hacerse en Python de esta manera:

def func1():
    ...

def func2():
    ...

funcmap = {1 : func1, 2 : func2}

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

De acuerdo, esa no es la mejor manera de sustituir el goto. Pero sin saber exactamente qué está tratando de hacer con el goto, es difícil dar consejos específicos.

@ ascobol :

Su mejor opción es encerrarlo en una función o usar una excepción. Para la función:

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

Por la excepción:

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

Usar excepciones para hacer cosas como esta puede ser un poco incómodo si vienes de otro lenguaje de programación. Pero diría que si no te gusta usar excepciones, Python no es el lenguaje para ti. :-)


Úselo juiciosamente. Las excepciones en Python son más rápidas que la mayoría de los otros idiomas. Pero siguen siendo lentos si te vuelves loco con ellos.
Jason Baker

Solo un aviso: loopfuncgeneralmente requerirá entradas y un poco más de esfuerzo para implementar, pero creo que es la mejor manera en la mayoría de los casos.
kon psych

60

Recientemente escribí un decorador de funciones que habilita gotoen Python, así:

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

Sin embargo, no estoy seguro de por qué a uno le gustaría hacer algo así. Dicho esto, no soy demasiado serio al respecto. Pero me gustaría señalar que este tipo de meta programación es realmente posible en Python, al menos en CPython y PyPy, y no solo haciendo un mal uso de la API del depurador como lo hizo ese otro tipo . Sin embargo, debes meterte con el código de bytes.


3
Gran decorador que hiciste! Impresionante cómo puedes jugar con el
código de bytes

Creo que esta debería ser la respuesta aceptada para esta pregunta. Esto podría ser útil para muchos bucles anidados, ¿por qué no?
PiMathCLanguage

¿Esto solo es compatible .beginy las .endetiquetas?
Alexej Magura

29

Encontré esto en las preguntas frecuentes oficiales sobre diseño e historia de Python .

¿Por qué no hay goto?

Puede usar excepciones para proporcionar un "goto estructurado" que incluso funciona en llamadas a funciones. Muchos sienten que las excepciones pueden emular convenientemente todos los usos razonables de las construcciones "go" o "goto" de C, Fortran y otros lenguajes. Por ejemplo:

class label(Exception): pass  # declare a label

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

Esto no le permite saltar a la mitad de un bucle, pero de todos modos eso generalmente se considera un abuso de goto. Utilizar con moderación.

Es muy agradable que esto se mencione incluso en las preguntas frecuentes oficiales, y que se proporcione una buena muestra de solución. Realmente me gusta Python porque su comunidad está tratando incluso gotoasí;)


1
El abuso gotoes una gran falta de programación para asegurarse, pero las excepciones de abuso de la OMI para emular gotoson solo un poco mejores y aún deben ser mal vistas. Prefiero que los creadores de Python lo incluyan gotoen el lenguaje para las pocas ocasiones en que realmente es útil que no permitirlo porque "es malo, muchachos" y luego recomiendan abusar de las excepciones para obtener la misma funcionalidad (y la misma espaguetización de código).
Abion47

15

Para responder a la @ascobolpregunta usando @bobincela sugerencia de los comentarios:

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

La sangría para el elsebloque es correcta. El código usa oscuro elsedespués de una sintaxis de Python en bucle. Consulte ¿Por qué Python usa 'else' después de los bucles for y while?


Arreglé su sangría de bloque else, lo que condujo a un descubrimiento interesante :
Braden Best

3
@ B1KMusic: la sangría es correcta como está. Es una sintaxis especial de Python. elsese ejecuta después del ciclo si breakno se ha encontrado. El efecto es que should_terminate_the_looptermina los bucles internos y externos.
jfs

1
Debería haber especificado que solo hice ese descubrimiento después de hacer la edición. Antes de eso, pensé que había descubierto un error en el intérprete, así que hice un montón de casos de prueba e investigué un poco para entender lo que estaba sucediendo. Lo siento por eso.
Braden Best

1
Ahora que entiendo lo que está sucediendo, estoy de acuerdo, es un código esotérico que se haría mucho más fácilmente con métodos más tradicionales
Braden Best

1
@ B1KMusic: No. Duplicar código para solucionar su ignorancia no es una buena solución. Si. return sugerido por @Jason Baker es una buena alternativa para salir de los bucles profundamente anidados.
jfs

12

Se ha realizado una versión de trabajo: http://entrian.com/goto/ .

Nota: Se ofreció como una broma de April Fool. (aunque trabajando)

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

No hace falta decir que. Sí, es divertido, pero NO lo use.


1
me parece mejor que usar 3 descansos ... por supuesto, también hay otras formas de escribirlo.
Nick

1
@Nick El uso de la función con retorno se ve aún mucho mejor.
Erik Šťastný

7

Las etiquetas para breaky continuefueron propuestas en PEP 3136 en 2007, pero fueron rechazadas. La sección de Motivación de la propuesta ilustra varios métodos comunes (aunque poco elegantes) para imitar etiquetados breaken Python.


7

Es técnicamente factible agregar una declaración 'goto' como python con algo de trabajo. Utilizaremos los módulos "dis" y "nuevos", ambos muy útiles para escanear y modificar el código de bytes de Python.

La idea principal detrás de la implementación es marcar primero un bloque de código como el uso de declaraciones "goto" y "label". Se utilizará un decorador especial "@goto" para marcar las funciones "goto". Luego escaneamos ese código para estas dos declaraciones y aplicamos las modificaciones necesarias al código de bytes subyacente. Todo esto sucede en el tiempo de compilación del código fuente.

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

Espero que esto responda la pregunta.


5

puede usar Excepciones definidas por el usuario para emulargoto

ejemplo:

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

Método impresionante, pero ¿podemos silenciar el método str excepción m
Anónimo

@ ¿Anónimo qué excepción? usas python3?
xavierskip

5

Python 2 y 3

pip3 install goto-statement

Probado en Python 2.6 a 3.6 y PyPy.

Enlace: goto-declaración


foo.py

from goto import with_goto

@with_goto
def bar():

    label .bar_begin

    ...

    goto .bar_begin

3

Estaba buscando algo similar 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;

Entonces, mi enfoque fue usar un booleano para ayudar a salir de los bucles anidados:

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

Hay ahora. ir

Creo que esto podría ser útil para lo que estás buscando.


1

Quería la misma respuesta y no quería usarla goto. Así que usé el siguiente ejemplo (de 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

Tengo mi propia forma de hacer gotos. Yo uso scripts de python separados.

Si quiero hacer un bucle:

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: esta técnica solo funciona en las versiones de Python 2.x)


1

Para un Goto avanzado, simplemente puede agregar:

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

Sin embargo, esto solo ayuda para escenarios simples (es decir, anidarlos te metería en un lío)


1

En lugar de un python goto equivalente, uso la instrucción break de la siguiente manera para pruebas rápidas de mi código. Esto supone que tiene una base de código estructurado. La variable de prueba se inicializa al comienzo de su función y simplemente muevo el bloque "If test: break" al final del bloque o bucle anidado if-then que quiero probar, modificando la variable de retorno al final del código para reflejar la variable de bloque o bucle que estoy probando.

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

1

Aunque no hay ningún código equivalente a goto/labelPython, aún podría obtener tal funcionalidad de goto/labelusar bucles.

Tomemos un ejemplo de código que se muestra a continuación donde goto/labelse puede usar en un lenguaje arbitrario que no sea 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...')

Ahora se puede lograr la misma funcionalidad del ejemplo de código anterior en python usando un whilebucle como se muestra a continuación.

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, hay una forma alternativa de implementar la instrucción 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()  
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.