Y a-t-il une étiquette / goto en Python?

178

Existe-t-il un gotoou un équivalent en Python pour pouvoir accéder à une ligne de code spécifique?

utilisateur46646
la source
2
L'étiquette est assez vague - pouvez-vous être plus précis sur ce que vous recherchez?
Dana
7
import goto
wim
9
Un de mes amis a implémenté gotoen Python quand il traduisait du code Fortran en Python. Il se détestait pour ça.
Cody Piersall
3
github.com/cdjc/goto (c'est beaucoup plus rapide que l'implémentation entrian)
cdjc
"L'étiquette est assez vague", aucune étiquette n'est intelligente, structurée fonctionne comme une machine
datdinhquoc

Réponses:

118

Non, Python ne prend pas en charge les étiquettes et goto, si c'est ce que vous recherchez. C'est un langage de programmation (hautement) structuré.

se détendre
la source
36
Fonctions @rejinacm?
UnkwnTech
79

Python vous offre la possibilité de faire certaines des choses que vous pourriez faire avec un goto en utilisant des fonctions de première classe. Par exemple:

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

    label1:
        ...
    label2:
        ...
}

Cela pourrait être fait en python comme ceci:

def func1():
    ...

def func2():
    ...

funcmap = {1 : func1, 2 : func2}

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

Certes, ce n'est pas la meilleure façon de remplacer goto. Mais sans savoir exactement ce que vous essayez de faire avec le goto, il est difficile de donner des conseils précis.

@ ascobol :

Votre meilleur pari est de l'enfermer dans une fonction ou d'utiliser une exception. Pour la fonction:

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

Pour l'exception:

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

Utiliser des exceptions pour faire des choses comme celle-ci peut sembler un peu gênant si vous venez d'un autre langage de programmation. Mais je dirais que si vous n'aimez pas utiliser les exceptions, Python n'est pas le langage qu'il vous faut. :-)

Jason Baker
la source
Utilisez-le judicieusement. Les exceptions en Python sont plus rapides que la plupart des autres langages. Mais ils sont encore lents si vous devenez fou avec eux.
Jason Baker
Juste un avis: loopfuncnécessitera généralement des contributions et un peu plus d'efforts à mettre en œuvre, mais c'est le meilleur moyen dans la plupart des cas, je pense.
kon psych
60

J'ai récemment écrit un décorateur de fonctions qui permet gotoen Python, comme ça:

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

Je ne sais pas pourquoi on aimerait faire quelque chose comme ça. Cela dit, je ne suis pas trop sérieux à ce sujet. Mais j'aimerais souligner que ce type de métaprogrammation est réellement possible en Python, au moins en CPython et PyPy, et pas seulement en abusant de l'API du débogueur comme l'a fait cet autre gars . Vous devez cependant jouer avec le bytecode.

Sébastien Noack
la source
3
Grand décorateur que vous avez fait! Génial comment vous pouvez jouer avec le bytecode :-)
K.Mulier
Je pense que cela devrait être la réponse acceptée à cette question. Cela pourrait être utile pour de nombreuses boucles imbriquées, pourquoi pas?
PiMathCLanguage
Est-ce que cela prend uniquement en charge .beginet les .endétiquettes?
Alexej Magura
29

J'ai trouvé cela dans la FAQ officielle sur la conception et l'histoire de python .

Pourquoi n'y a-t-il pas de goto?

Vous pouvez utiliser des exceptions pour fournir un «goto structuré» qui fonctionne même entre les appels de fonction. Beaucoup pensent que les exceptions peuvent facilement émuler toutes les utilisations raisonnables des constructions «go» ou «goto» de C, Fortran et d'autres langages. Par exemple:

class label(Exception): pass  # declare a label

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

Cela ne vous permet pas de sauter au milieu d'une boucle, mais c'est généralement considéré comme un abus de goto de toute façon. Utiliser avec parcimonie.

C'est très bien que cela soit même mentionné dans la FAQ officielle et qu'un bel exemple de solution soit fourni. J'aime vraiment python parce que sa communauté traite même gotocomme ça;)

klaas
la source
1
Abuser gotoest un problème majeur de programmation, bien sûr, mais l'OMI abuser des exceptions pour émuler goton'est que légèrement mieux et devrait encore être fortement désapprouvé. J'aurais préféré que les créateurs de Python incluent gotodans le langage pour les rares occasions où cela est réellement utile que de l'interdire parce que "c'est mauvais, les gars" et puis recommander d'abuser des exceptions pour obtenir la même fonctionnalité (et la même spaghettification du code).
Abion47 le
15

Pour répondre à la @ascobolquestion de en utilisant @bobincela suggestion de dans les commentaires:

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

Le retrait du elsebloc est correct. Le code utilise obscure elseaprès une syntaxe Python en boucle. Voir Pourquoi python utilise-t-il 'else' après les boucles for et while?

jfs
la source
J'ai corrigé votre retrait de bloc else, ce qui a conduit à une découverte intéressante :
Braden Best
3
@ B1KMusic: le retrait est correct tel quel. C'est une syntaxe Python spéciale. elseest exécuté après la boucle s'il breakn'a pas été rencontré. L'effet est que should_terminate_the_looptermine les boucles internes et externes.
jfs
1
J'aurais dû préciser que je n'ai fait cette découverte qu'après avoir fait l'édition. Avant cela, je pensais avoir découvert un bogue dans l'interpréteur, alors j'ai fait un tas de cas de test et fait des recherches pour comprendre ce qui se passait. Désolé pour ça.
Braden Best
1
Maintenant que je comprends ce qui se passe, je suis d'accord, c'est un code ésotérique qui serait fait beaucoup plus facilement avec des méthodes plus traditionnelles
Braden Best
1
@ B1KMusic: Non. Dupliquer du code pour contourner votre ignorance n'est pas une bonne solution. Oui. return suggéré par @Jason Baker est une bonne alternative pour sortir de boucles profondément imbriquées.
jfs
12

Une version fonctionnelle a été créée: http://entrian.com/goto/ .

Remarque: il s'agissait d'une blague du poisson d'avril. (fonctionne bien)

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

Il va sans dire. Oui c'est drôle, mais ne l'utilisez pas.

mal
la source
1
me semble mieux que d'utiliser 3 pauses ... bien sûr, il y a d'autres façons de l'écrire aussi.
Nick
1
@Nick L'utilisation de la fonction avec retour est encore bien meilleure.
Erik Šťastný
7

Des étiquettes pour breaket continueont été proposées dans PEP 3136 en 2007, mais elles ont été rejetées. La section Motivation de la proposition illustre plusieurs méthodes courantes (si inélégantes) pour imiter étiquetées breaken Python.

Bill le lézard
la source
7

Il est techniquement possible d'ajouter une instruction de type «goto» à python avec un peu de travail. Nous utiliserons les modules "dis" et "new", tous deux très utiles pour scanner et modifier le code octet python.

L'idée principale derrière l'implémentation est de marquer d'abord un bloc de code comme utilisant des instructions "goto" et "label". Un décorateur spécial "@goto" sera utilisé pour marquer les fonctions "goto". Ensuite, nous analysons ce code pour ces deux instructions et appliquons les modifications nécessaires au code d'octet sous-jacent. Tout cela se produit au moment de la compilation du code source.

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

J'espère que cela répond à la question.

Rabih Kodeih
la source
5

vous pouvez utiliser des exceptions définies par l'utilisateur pour émulergoto

exemple:

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
la source
Méthode géniale mais pouvons-nous désactiver la méthode str exception m
Anonyme
@Anonymous quelle exception? vous utilisez python3?
xavierskip
5

Python 2 et 3

pip3 install goto-statement

Testé sur Python 2.6 à 3.6 et PyPy.

Lien: goto-statement


foo.py

from goto import with_goto

@with_goto
def bar():

    label .bar_begin

    ...

    goto .bar_begin
Marco DG
la source
3

Je cherchais quelque chose de similaire à

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;

Mon approche a donc été d'utiliser un booléen pour aider à sortir des boucles for imbriquées:

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
la source
2

Il y a maintenant. aller à

Je pense que cela pourrait être utile pour ce que vous recherchez.

ancho
la source
1

Je voulais la même réponse et je ne voulais pas l'utiliser goto. J'ai donc utilisé l'exemple suivant (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)
Sable1512
la source
1

J'ai ma propre façon de faire des gotos. J'utilise des scripts python séparés.

Si je veux faire une boucle:

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

( REMARQUE: cette technique ne fonctionne que sur les versions Python 2.x)

Anonaguy
la source
1

Pour un aller en avant, vous pouvez simplement ajouter:

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

Cela n'aide cependant que pour des scénarios simples (c'est-à-dire que leur imbrication vous mettrait dans le désordre)

JGFMK
la source
1

Au lieu d'un équivalent python goto, j'utilise l'instruction break de la manière suivante pour des tests rapides de mon code. Cela suppose que vous disposez d'une base de code structurée. La variable de test est initialisée au début de votre fonction et je déplace simplement le bloc "If test: break" à la fin du bloc ou de la boucle imbriquée if-then que je veux tester, en modifiant la variable de retour à la fin du code pour refléter la variable de bloc ou de boucle que je teste.

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

Bien qu'il n'y ait pas de code équivalent à goto/labelPython, vous pouvez toujours obtenir une telle fonctionnalité en goto/labelutilisant des boucles.

Prenons un exemple de code ci-dessous où goto/labelpeut être utilisé dans un langage arbitraire autre que 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...')

Maintenant, la même fonctionnalité de l'exemple de code ci-dessus peut être obtenue en python en utilisant une whileboucle comme indiqué ci-dessous.

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
la source
0

non il existe une autre façon d'implémenter l'instruction 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()  
Experts Python
la source