Czy w Pythonie jest etykieta / goto?

178

Czy gotow Pythonie istnieje odpowiednik lub jego odpowiednik, który umożliwia przejście do określonej linii kodu?

user46646
źródło
2
Etykieta jest dość niejasna - czy możesz bardziej precyzyjnie określić, czego szukasz?
Dana
7
import goto
wim
9
Mój przyjaciel zaimplementował gotow Pythonie, kiedy tłumaczył kod w języku Fortran na Python. Nienawidził siebie za to.
Cody Piersall
3
github.com/cdjc/goto (to znacznie szybciej niż entrian realizacji)
cdjc
„Etykieta jest dość niejasna”, żadna etykieta nie jest inteligentna, struktura działa jak maszyna
datdinhquoc

Odpowiedzi:

118

Nie, Python nie obsługuje etykiet i gotowe, jeśli o to ci chodzi. Jest to (wysoce) ustrukturyzowany język programowania.

rozwijać
źródło
36
Funkcje @rejinacm?
UnkwnTech
79

Python oferuje możliwość zrobienia niektórych rzeczy, które można zrobić za pomocą goto, używając funkcji pierwszej klasy. Na przykład:

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

    label1:
        ...
    label2:
        ...
}

Można to zrobić w Pythonie w ten sposób:

def func1():
    ...

def func2():
    ...

funcmap = {1 : func1, 2 : func2}

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

To prawda, że ​​nie jest to najlepszy sposób na zastąpienie goto. Ale nie wiedząc dokładnie, co próbujesz zrobić z goto, trudno jest udzielić konkretnej porady.

@ ascobol :

Najlepszym rozwiązaniem jest umieszczenie go w funkcji lub użycie wyjątku. Dla funkcji:

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

Z wyjątkiem:

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

Używanie wyjątków do robienia takich rzeczy może wydawać się nieco niezręczne, jeśli pochodzisz z innego języka programowania. Ale argumentowałbym, że jeśli nie lubisz używać wyjątków, Python nie jest dla ciebie językiem. :-)

Jason Baker
źródło
Używaj go rozsądnie. Wyjątki w Pythonie są szybsze niż w większości innych języków. Ale nadal są powolne, jeśli oszalejesz z nimi.
Jason Baker
Tylko uwaga: loopfuncgeneralnie będzie wymagał wkładu i trochę więcej wysiłku, aby go wdrożyć, ale w większości przypadków jest to najlepszy sposób.
kon psych
60

Niedawno napisałem dekorator funkcji, który włącza gotow Pythonie, tak po prostu:

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

Nie jestem jednak pewien, dlaczego chciałoby się coś takiego zrobić. To powiedziawszy, nie podchodzę do tego poważnie. Chciałbym jednak zwrócić uwagę, że tego rodzaju metaprogramowanie jest w rzeczywistości możliwe w Pythonie, przynajmniej w CPython i PyPy, i nie tylko przez niewłaściwe użycie interfejsu API debuggera, tak jak zrobił to tamten facet . Musisz jednak zadzierać z kodem bajtowym.

Sebastian Noack
źródło
3
Świetny dekorator, który zrobiłeś! Niesamowite, jak możesz
bawić się kodem
Myślę, że to powinna być przyjęta odpowiedź na to pytanie. Może to być przydatne w przypadku wielu zagnieżdżonych pętli, dlaczego nie?
PiMathCLanguage
Czy to obsługuje tylko .begini .endetykiety?
Alexej Magura
29

Znalazłem to w oficjalnym Pythonie FAQ Design and History .

Dlaczego nie ma goto?

Możesz użyć wyjątków, aby zapewnić „strukturalne goto”, które działa nawet w przypadku wywołań funkcji. Wielu uważa, że ​​wyjątki mogą wygodnie naśladować wszystkie rozsądne zastosowania konstrukcji „go” lub „goto” w językach C, Fortran i innych. Na przykład:

class label(Exception): pass  # declare a label

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

Nie pozwala to wskoczyć w środek pętli, ale i tak jest to zwykle uważane za nadużycie goto. Używaj oszczędnie.

To bardzo miłe, że jest to nawet wspomniane w oficjalnym FAQ i że dostarczono fajną próbkę rozwiązania. Bardzo lubię Pythona, ponieważ jego społeczność traktuje nawet w gototen sposób;)

klaas
źródło
1
Z gotopewnością nadużywanie jest poważnym foux pasem programistycznym, ale nadużywanie wyjątków przez IMO w celu emulacji gotojest tylko nieznacznie lepsze i nadal powinno być mocno krytykowane. Wolałbym, żeby twórcy Pythona włączyli gotodo języka kilka sytuacji, w których faktycznie jest to użyteczne, niż zablokować go, ponieważ „to jest złe, chłopaki”, a następnie zalecić nadużywanie wyjątków, aby uzyskać tę samą funkcjonalność (i ten sam kod spaghetti).
Abion47
15

Aby odpowiedzieć na @ascobolpytanie użytkownika, korzystając @bobincez sugestii z komentarzy:

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

Wcięcie elsebloku jest poprawne. Kod używa niejasnej elseskładni Pythona po pętli. Zobacz Dlaczego python używa „else” po pętli for i while?

jfs
źródło
Poprawiłem wcięcie w innym bloku, co doprowadziło do ciekawego odkrycia :
Braden Best,
3
@ B1KMusic: wcięcie jest poprawne bez zmian. Jest to specjalna składnia Pythona. elsejest wykonywany po pętli, jeśli breaknie został napotkany. Efektem jest to, że should_terminate_the_loopzamyka obie pętle wewnętrzne i zewnętrzne.
jfs
1
Powinienem był sprecyzować, że dokonałem tego odkrycia dopiero po dokonaniu edycji. Wcześniej wydawało mi się, że znalazłem błąd w tłumaczu, więc stworzyłem kilka przypadków testowych i przeprowadziłem badania, aby zrozumieć, co się dzieje. Przepraszam za to.
Braden Best
1
Teraz, gdy rozumiem, co się dzieje, zgadzam się, że jest to jakiś ezoteryczny kod, który można by zrobić o wiele łatwiej przy użyciu bardziej tradycyjnych metod
Braden Best,
1
@ B1KMusic: Nie. Powielanie kodu w celu obejścia ignorancji nie jest dobrym rozwiązaniem. Tak. return sugerowane przez @Jason Baker to dobra alternatywa dla wyrwania się z głęboko zagnieżdżonych pętli.
jfs
12

Wykonano działającą wersję: http://entrian.com/goto/ .

Uwaga: był oferowany jako żart primaaprilisowy. (choć działa)

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

Nie trzeba dodawać że. Tak, to zabawne, ale NIE używaj go.

harmv
źródło
1
wygląda dla mnie lepiej niż 3 przerwy ... oczywiście są też inne sposoby, aby to zapisać.
Nick
1
@Nick Użycie funkcji z powrotem wygląda jeszcze lepiej.
Erik Šťastný
7

Etykiety breaki continuezostały zaproponowane w PEP 3136 w 2007 roku, ale zostały odrzucone. Sekcja Motywacja propozycji ilustruje kilka powszechnych (jeśli nieeleganckich) metod naśladowania oznaczonych breakw Pythonie.

Bill the Lizard
źródło
7

Jest technicznie wykonalne, aby dodać do Pythona instrukcję podobną do „goto”. Będziemy używać modułów „dis” i „new”, które są bardzo przydatne do skanowania i modyfikowania kodu bajtowego Pythona.

Główną ideą implementacji jest najpierw oznaczenie bloku kodu za pomocą instrukcji „goto” i „label”. Specjalny dekorator „@goto” będzie używany do oznaczania funkcji „goto”. Następnie skanujemy ten kod w poszukiwaniu tych dwóch instrukcji i wprowadzamy niezbędne modyfikacje do bazowego kodu bajtowego. To wszystko dzieje się w czasie kompilacji kodu źródłowego.

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

Mam nadzieję, że to odpowiada na pytanie.

Rabih Kodeih
źródło
5

do emulacji można użyć wyjątków zdefiniowanych przez użytkownikagoto

przykład:

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
źródło
Niesamowita metoda, ale czy możemy wyciszyć metodę str. Wyjątek m
Anonimowy
@ Anonimowy, który wyjątek? używasz python3?
xavierskip
5

Python 2 i 3

pip3 install goto-statement

Testowane na Pythonie 2.6 do 3.6 i PyPy.

Link: instrukcja goto


foo.py

from goto import with_goto

@with_goto
def bar():

    label .bar_begin

    ...

    goto .bar_begin
Marco DG
źródło
3

Szukałem czegoś podobnego do

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;

Więc moje podejście polegało na użyciu wartości logicznej, aby pomóc wyrwać się z zagnieżdżonych pętli for:

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
źródło
2

Jest teraz. iść do

Myślę, że może to być przydatne w przypadku tego, czego szukasz.

ancho
źródło
1

Chciałem tej samej odpowiedzi i nie chciałem używać goto. Więc użyłem następującego przykładu (z 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)
Piasek1512
źródło
1

Mam swój własny sposób robienia rzeczy gotowych. Używam osobnych skryptów Pythona.

Jeśli chcę zapętlić:

plik1.py

print("test test")
execfile("file2.py")
a = a + 1

plik2.py

print(a)
if a == 10:
   execfile("file3.py")
else:
   execfile("file1.py")

plik3.py

print(a + " equals 10")

( UWAGA: ta technika działa tylko w wersjach Python 2.x)

Anonaguy
źródło
1

W przypadku forward Goto możesz po prostu dodać:

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

Pomaga to jednak tylko w prostych scenariuszach (np. Zagnieżdżenie ich doprowadziłoby do bałaganu)

JGFMK
źródło
1

Zamiast odpowiednika goto w Pythonie używam instrukcji break w następujący sposób do szybkich testów mojego kodu. Zakłada się, że masz strukturalną podstawę kodu. Zmienna testowa jest inicjowana na początku twojej funkcji i po prostu przenoszę blok "If test: break" na koniec zagnieżdżonego bloku lub pętli if-then, które chcę przetestować, modyfikując zmienną zwracaną na końcu kodu aby odzwierciedlić testowaną zmienną bloku lub pętli.

def x:
  test = True
  If y:
     # some code
     If test:
            break
  return something
Chris Rogan
źródło
1

Chociaż nie ma żadnego kodu odpowiadającego temu goto/labelw Pythonie, nadal możesz uzyskać taką funkcjonalnośćgoto/label przy użyciu pętli.

Weźmy przykładowy kod pokazany poniżej, w którym goto/labelmożna go użyć w dowolnym języku innym niż 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...')

Teraz tę samą funkcjonalność powyższego przykładu kodu można osiągnąć w Pythonie za pomocą whilepętli, jak pokazano poniżej.

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
źródło
0

nie, istnieje alternatywny sposób implementacji instrukcji 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()  
Eksperci Pythona
źródło