Gibt es in Python ein Label / goto?

177

Gibt es gotoin Python ein oder ein gleichwertiges Element, um zu einer bestimmten Codezeile springen zu können?

user46646
quelle
2
Das Etikett ist ziemlich vage - können Sie genauer sagen, wonach Sie suchen?
Dana
7
import goto
wim
9
Ein Freund von mir wurde gotoin Python implementiert, als er Fortran-Code in Python übersetzte. Er hasste sich dafür.
Cody Piersall
3
github.com/cdjc/goto (es ist viel schneller als die entrian Implementierung)
cdjc
"Label ist ziemlich vage", kein Label ist intelligent, strukturiert funktioniert wie eine Maschine
datdinhquoc

Antworten:

116

Nein, Python unterstützt keine Labels und goto, wenn Sie danach suchen. Es ist eine (stark) strukturierte Programmiersprache.

entspannen
quelle
36
@ rejinacm Funktionen?
UnkwnTech
79

Python bietet Ihnen die Möglichkeit, einige der Dinge zu tun, die Sie mit einem goto mit erstklassigen Funktionen tun können. Beispielsweise:

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

    label1:
        ...
    label2:
        ...
}

Könnte in Python so gemacht werden:

def func1():
    ...

def func2():
    ...

funcmap = {1 : func1, 2 : func2}

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

Zugegeben, das ist nicht der beste Weg, um goto zu ersetzen. Aber ohne genau zu wissen, was Sie mit dem goto machen wollen, ist es schwierig, spezifische Ratschläge zu geben.

@ ascobol :

Am besten schließen Sie es entweder in eine Funktion ein oder verwenden eine Ausnahme. Für die Funktion:

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

Für die Ausnahme:

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

Die Verwendung von Ausnahmen für solche Aufgaben kann sich etwas unangenehm anfühlen, wenn Sie aus einer anderen Programmiersprache stammen. Aber ich würde argumentieren, dass Python nicht die Sprache für Sie ist, wenn Sie keine Ausnahmen verwenden möchten. :-)

Jason Baker
quelle
Verwenden Sie es mit Bedacht. Ausnahmen in Python sind schneller als in den meisten anderen Sprachen. Aber sie sind immer noch langsam, wenn Sie verrückt nach ihnen werden.
Jason Baker
Nur ein Hinweis: Die loopfuncImplementierung erfordert im Allgemeinen Eingaben und etwas mehr Aufwand, ist aber in den meisten Fällen meiner Meinung nach der beste Weg.
Kon Psych
60

Ich habe kürzlich einen Funktionsdekorator geschrieben , der gotoin Python einfach Folgendes ermöglicht :

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

Ich bin mir nicht sicher, warum man so etwas machen möchte. Das heißt, ich meine es nicht zu ernst. Aber ich möchte darauf hinweisen, dass diese Art der Metaprogrammierung in Python tatsächlich möglich ist, zumindest in CPython und PyPy, und nicht nur durch den Missbrauch der Debugger-API wie dieser andere Typ . Sie müssen sich jedoch mit dem Bytecode herumschlagen.

Sebastian Noack
quelle
3
Toller Dekorateur, den du gemacht hast! Genial, wie man mit dem Bytecode
herumspielen
Ich denke, dies sollte die akzeptierte Antwort auf diese Frage sein. Dies kann für viele verschachtelte Schleifen nützlich sein. Warum nicht?
PiMathCLanguage
Unterstützt .beginund .endbeschriftet dies nur ?
Alexej Magura
29

Ich fand dies in den offiziellen Python Design and History FAQ .

Warum gibt es kein goto?

Sie können Ausnahmen verwenden, um ein „strukturiertes Goto“ bereitzustellen, das sogar über Funktionsaufrufe hinweg funktioniert. Viele sind der Meinung, dass Ausnahmen alle vernünftigen Verwendungen der Konstrukte "go" oder "goto" von C, Fortran und anderen Sprachen bequem emulieren können. Beispielsweise:

class label(Exception): pass  # declare a label

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

Dies erlaubt Ihnen nicht, in die Mitte einer Schleife zu springen, aber das wird normalerweise sowieso als Missbrauch von goto angesehen. Sparsam verwenden.

Es ist sehr schön, dass dies sogar in den offiziellen FAQ erwähnt wird und dass ein schönes Lösungsbeispiel bereitgestellt wird. Ich mag Python wirklich, weil seine Community sogar gotoso behandelt ;)

klaas
quelle
15

So beantworten Sie die @ascobolFrage mit @bobincedem Vorschlag aus den Kommentaren:

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

Der Einzug für den elseBlock ist korrekt. Der Code verwendet obskur elsenach einer Python-Schleifensyntax. Siehe Warum verwendet Python 'else' nach for- und while-Schleifen?

jfs
quelle
Ich habe Ihren else-Blockeinzug korrigiert, was zu einer interessanten Entdeckung führte :
Braden Best
3
@ B1KMusic: Der Einzug ist so wie er ist korrekt. Es ist eine spezielle Python-Syntax. elsewird nach der Schleife ausgeführt, wenn breaksie nicht gefunden wurde. Der Effekt ist , dass should_terminate_the_loopendet beide inneren und äußeren Schleifen.
JFS
1
Ich hätte angeben sollen, dass ich diese Entdeckung erst gemacht habe, nachdem ich die Bearbeitung vorgenommen habe. Vorher dachte ich, ich hätte einen Fehler im Interpreter entdeckt, also machte ich eine Reihe von Testfällen und recherchierte, um zu verstehen, was los war. Das tut mir leid.
Braden Best
1
Jetzt, da ich verstehe, was los ist, stimme ich zu, dass dies ein esoterischer Code ist, der mit traditionelleren Methoden
Braden Best
1
@ B1KMusic: Nein. Das Duplizieren von Code zur Umgehung Ihrer Unwissenheit ist keine gute Lösung. Ja. return vorgeschlagen von @Jason Baker ist eine gute Alternative, um aus tief verschachtelten Schleifen auszubrechen.
JFS
12

Eine funktionierende Version wurde erstellt: http://entrian.com/goto/ .

Hinweis: Es wurde als Aprilscherz angeboten. (arbeitet aber)

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

Unnötig zu erwähnen. Ja, es ist lustig, aber benutze es NICHT.

harmv
quelle
1
sieht für mich besser aus als 3 Pausen ... natürlich gibt es auch andere Möglichkeiten, es zu schreiben.
Nick
1
@Nick Die Verwendung der Funktion mit Rückgabe sieht noch viel besser aus.
Erik Šťastný
7

Etiketten für breakund continuewurden bereits 2007 in PEP 3136 vorgeschlagen , aber abgelehnt. Der Abschnitt Motivation des Vorschlags zeigt einige gängige (wenn auch unelegante) Methoden zur Nachahmung von breakin Python beschrifteten .

Bill die Eidechse
quelle
7

Es ist technisch machbar, Python mit etwas Arbeit eine 'goto'-ähnliche Anweisung hinzuzufügen. Wir werden die Module "dis" und "new" verwenden, die beide sehr nützlich sind, um Python-Byte-Code zu scannen und zu ändern.

Die Hauptidee hinter der Implementierung besteht darin, zunächst einen Codeblock mit den Anweisungen "goto" und "label" zu markieren. Ein spezieller "@goto" -Dekorator wird zum Markieren von "goto" -Funktionen verwendet. Anschließend scannen wir diesen Code nach diesen beiden Anweisungen und wenden die erforderlichen Änderungen auf den zugrunde liegenden Bytecode an. Dies alles geschieht zum Zeitpunkt der Kompilierung des Quellcodes.

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

Hoffe das beantwortet die Frage.

Rabih Kodeih
quelle
5

Sie können benutzerdefinierte Ausnahmen zum Emulieren verwendengoto

Beispiel:

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()
xavierskip
quelle
Super Methode, aber können wir die str Ausnahme m Methode stumm schalten
Anonym
@ Anonym welche Ausnahme? Sie verwenden Python3?
Xavierskip
5

Python 2 & 3

pip3 install goto-statement

Getestet auf Python 2.6 bis 3.6 und PyPy.

Link: goto-Anweisung


foo.py.

from goto import with_goto

@with_goto
def bar():

    label .bar_begin

    ...

    goto .bar_begin
Marco DG
quelle
3

Ich suchte nach etwas Ähnlichem

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;

Mein Ansatz war es also, einen Booleschen Wert zu verwenden, um aus den verschachtelten for-Schleifen auszubrechen:

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
yaitloutou
quelle
2

Das gibt es jetzt. gehe zu

Ich denke, dies könnte nützlich sein für das, was Sie suchen.

Ancho
quelle
1

Ich wollte die gleiche Antwort und wollte sie nicht verwenden goto. Also habe ich das folgende Beispiel verwendet (von 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)
Sand1512
quelle
1

Ich habe meine eigene Art, Gotos zu machen. Ich benutze separate Python-Skripte.

Wenn ich eine Schleife machen möchte:

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

( HINWEIS: Diese Technik funktioniert nur in Python 2.x-Versionen.)

Anonaguy
quelle
1

Für ein Forward Goto können Sie einfach hinzufügen:

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

Dies hilft jedoch nur bei einfachen Szenarien (dh wenn Sie diese verschachteln, geraten Sie in Unordnung).

JGFMK
quelle
1

Anstelle eines Python-Goto-Äquivalents verwende ich die break-Anweisung folgendermaßen, um meinen Code schnell zu testen. Dies setzt voraus, dass Sie eine strukturierte Codebasis haben. Die Testvariable wird zu Beginn Ihrer Funktion initialisiert und ich verschiebe einfach den Block "If test: break" an das Ende des verschachtelten if-then-Blocks oder der verschachtelten Schleife, die ich testen möchte, und ändere die Rückgabevariable am Ende des Codes um die Block- oder Schleifenvariable wiederzugeben, die ich teste.

def x:
  test = True
  If y:
     # some code
     If test:
            break
  return something
Chris Rogan
quelle
1

Obwohl es keinen Code gibt, der goto/labelPython entspricht, könnten Sie dennoch solche Funktionen für die goto/labelVerwendung von Schleifen erhalten.

Nehmen goto/labelwir ein unten gezeigtes Codebeispiel , das in einer anderen Sprache als Python verwendet werden kann.

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

Jetzt kann die gleiche Funktionalität des obigen Codebeispiels in Python mithilfe einer whileSchleife wie unten gezeigt erreicht werden.

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...')
Somanna
quelle
0

Nein, es gibt eine alternative Möglichkeit, die goto-Anweisung zu implementieren

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()  
Python-Experten
quelle