`goto` en Python

Debo usar goto en Python. Encontré a los entrantes goto pero mi implementación de Python (CPython 2.7.1 en Mac) no tiene este módulo, por lo que no parece ser portátil. Al menos debería funcionar en todas las implementaciones de Python que admiten el código de bytes de CPython (especialmente. Me importa CPython y PyPy). Luego está esta pregunta relacionada , y goto de cdjc . Y las dadas por respuestas a continuación.

Podría ir y construir el JUMP_ABSOLUTE manualmente (es decir, escribir mi propio comstackdor de Python) porque existe una instrucción de este tipo ( JUMP_ABSOLUTE y amigos). Pero me pregunto si hay una manera más fácil. ¿Es posible, a través de inspect llamar a una sola instrucción de código de bytes? También pensé en comstackr a través de Python y luego parchear automáticamente el código de bytes de Python generado.


Por supuesto, la gente preguntará por qué y no me dará ninguna respuesta útil si no explico por qué realmente necesito esto. En resumen, mi caso de uso: estoy traduciendo un C AST a Python AST y comstackndo esto. Puedo mapear cada flujo lógico (todos los bucles y otras cosas) de alguna manera al código de Python equivalente. Todo excepto goto . Proyectos relacionados: PyCParser (ver interpreter.py ), PyCPython , PyLua .

Sé lo que todos están pensando:

xkcd GOTO

Sin embargo, puede haber algunos casos didácticos en los que realmente necesitas un goto .

Esta receta de python proporciona el comando goto como un decorador de funciones.

El decorador goto ( receta de Python por Carl Cerecke )

Esta es la receta para usted si está harto de la velocidad lenta del módulo goto existente http://entrian.com/goto/ . El goto en esta receta es aproximadamente sys.settrace más rápido y también es más limpio (abusar de sys.settrace parece difícilmente pythonic). Debido a que este es un decorador, alerta al lector qué funciones usan goto . No implementa el comando comefrom, aunque no es difícil extenderlo para hacerlo (ejercicio para el lector). Además, los gotos computados no son compatibles; No son pythonicos.

  • Utilice dis.dis(fn) para mostrar el desassembly del bytecode de una función.
  • Se accede a los fn.func_code.co_code de fn.func_code.co_code de una función mediante fn.func_code.co_code . Esto es solo así:
  • La función decorada se crea exactamente igual que la anterior, pero con el bytecode actualizado para obedecer los comandos goto .
  • Esto es sólo 2.x; El nuevo módulo no está en Python 3.x (¡otro ejercicio para el lector!)

Uso

 @goto def test1(n): s = 0 label .myLoop if n <= 0: return s s += n n -= 1 goto .myLoop >>> test1(10) 55 

Actualizar

Aquí hay dos implementaciones adicionales compatibles con Python 3:

Es posible que tenga el único caso de uso válido que he visto por necesitar goto a Python. 🙂

La forma más directa de emular goto hacia adelante en Python es mediante excepciones, ya que pueden saltar desde cualquier profundidad de las estructuras de control anidadas.

 class Goto(Exception): pass try: if foo = "bar": raise Goto print "foo is not bar" except Goto: print "foo is bar" 

Esto se complica si necesita admitir más de un destino, pero creo que podría hacerse utilizando estructuras de try/except anidadas y varias clases de excepción, una para cada destino. Ya que C limita el goto a una sola función, al menos no tendrá que preocuparse por cómo hacer que esto funcione en todas las funciones. 🙂 Por supuesto, no funciona para goto inversa.

Otra cosa a tener en cuenta es que las excepciones en Python, si bien son rápidas en comparación con algunos idiomas, son aún más lentas que las estructuras de control de flujo normales, como por ejemplo, while y for .

Esto podría ser una gran cantidad de trabajo (aunque quizás no más de lo que ya está en), pero si pudiera generar el código de bytes de Python en lugar de la fuente de Python, no tendría ningún problema en implementar goto , porque el código de bytes de Python -languages) tiene un código de operación JUMP_ABSOLUTE perfectamente JUMP_ABSOLUTE .

He actualizado mi decorador de Python Goto para Python 3. Puedes obtenerlo en https://github.com/cdjc/goto . Usar goto en lugar de funciones puede hacer que una máquina de estados sea 5 veces más rápida.

La versión para python 2 todavía está disponible en http://code.activestate.com/recipes/576944-the-goto-decorator/ pero tiene una serie de errores corregidos en la versión de python 3.

Habrá algunos patrones comunes que el código que usa goto probablemente seguirá.

En la mayoría de los casos, sospecho que todas las declaraciones de goto saltarán a una ubicación que sea más adelante y en un bloque más cerrado; Si el cuerpo de una función sigue este patrón a la perfección, transforma los goto en excepciones, con las tags como bloques a excepción.

Otros casos de goto saltando de un lugar a otro en el mismo bloque, como se utilizarían en una máquina de estados. Esto probablemente se puede traducir en un bucle de despacho; cada región entre una etiqueta y la siguiente se convierte en una función; los goto’s son reemplazados por next_state = 'labelname'; return next_state = 'labelname'; return

El último caso, que no es ninguno de los anteriores y posiblemente no trivial, es cuando el salto es en un cuerpo de bucle. Todavía no tengo una respuesta para eso.

Esto no es exactamente lo que estás buscando pero escúchame.

Hace muchos años, mi hijo y yo escribimos un juego de “Aventura” en BASIC. Cada ubicación en el juego subterráneo era un número de línea. Cuando dejó una ubicación a través del túnel hacia el norte, por ejemplo, llegó a otra ubicación.

La encoding fue algo así como if response == 'N' GOTO 2400 . Así que los jugadores terminaron yendo por todos lados usando GOTOs.

Me pregunté cómo podría hacerse esto en Python y se me ocurrió esto.

Tal vez una técnica de este tipo podría usarse para otras aplicaciones donde se necesita algo como un GOTO. Si divides tu progtwig en partes que son funciones, la siguiente encoding “un poco tonta” haría el truco.

 """ Simple, short and unfinished 'Adventure' game to show how a program such as this with 'locations' (where each location is handled by a function) can simulate 'GOTO's through use of the eval() function. Each time the player chooses an exit from his current location, where he goes to will depend on the exit chosen and achieved using eval() on the last line. This saves having to code a series of 'if's at each location which call the correct function the player goes to. Also, because the code always comes back to the eval line at the botton each time, one location's function doesn't call the next location's function, with possible risk of stack overflow if the program is radically extended. The program uses randint() to determine if characters are there and what they are doing. This is just a taster. Dtwigtic improvements could be made if the player could collect and use artefacts found during his travels. For instance if there was a key somewhere and it was collected, it could be used to unlock the door, but the key can't be picked up unless the troll isn't there etc. The program needs to be able to parse (understand) simple natural language (English) commands such as 'take key' or 'unlock door' or 'give food to troll' There will also need to be some global variables so each function can behave and do stuff dependent on these variables. The program needs to be able to respond to players' commands, such as after a player has indicated which direction he wants to go, the program responds, 'You can't go that way. the Ork is blocking your path'. You get the picture. The program also needs to be able to save variables in a dictionary (which is then pickled into a file) so players can close the game and save it and pick up where they left off next time. People new to this sort of game should realise by the way, that just because a tunnel (or other route) leaves one location northwards, that doesn't mean that it arrives at the next location from the south. The tunnels twist and turn all over the place.""" def l0(): #print('L0') print("You're south of a forbidding-looking cave") go = input('n or q > ') if go == 'n': return('1') if go == 'q': return('q') else: return 'q' def l1(): #print('L1') print("You're in a large, dark cave. Bats are hanging from the ceiling.") print("Tunnels lead north, east and west. The entrance is south of you.") go = input('nsew > ') if go == 'n': return('3') # Leaving L1 northwards takes you to L3 if go == 's': return('0') # Leaving L1 southwards takes you to L0 if go == 'e': return('3') # Leaving L1 eastwards also takes you to L3 if go == 'w': return('2') # Leaving L1 westwards takes you to L2 else: return 'q' def l2(): #print('L2') print("You've come to a bridge running east across a chasm") print("On the other side the only way to go is through a tunnel") print("This side of the chasm, a tunnel leads north and a path leads south") go = input('nes > ') if go == 'n': return('1') # As per L! but applicable to L2 etc. if go == 'e': return('4') if go == 's': return('3') else: return 'q' def l3(): #print('L3') print("You've come to a hot and humid cavern") print("Tunnels run north, east and west. A path leads south.") print("There's a dragon here.") dstate = randint(1,5) if dstate == 1: print("The dragon seems to be asleep") if dstate == 2: print("The dragon looks sleepy") if dstate == 3: print("The dragon is awake") if dstate == 4: print("The dragon looks angry") if dstate == 5: print("The dragon is breathing fire and very angry!") go = input('nsew > ') if go == 'n': return('1') if go == 's': return('2') if go == 'e': return('4') if go == 'w': return('1') else: return 'q' def l4(): #print('L4') print("You've arrived at a grotto. There are jewels here!") tstate = randint(1,4) if tstate > 1: print("There's a troll here wielding a cudgel") print("Tunnels lead east, west and south from here") go = input('sew > ') if go == 's': return('5') if go == 'e': return('2') if go == 'w': return('3') else: return 'q' def l5(): #print('L5') print("The tunnel ends at a door leading to a small room") print("Through a grille in the door, you can see there is no way out") print("The only way is back, south along the tunnel") print("But there's gold in the room!") print("The door is locked.") go = input('s > ') if go == 's': return('4') else: return 'q' ### ********************* Main Program Start *************************** import random from random import randint go = l0() # That's call L zero (location zero), not ten! while go != 'q': print() go = eval("l"+go+"()") # Program always returns here to sort out where to # go next. Player doesn't of course! 

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

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

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

Excusado es decir que. Sí, es gracioso, pero NO lo uses.