Existe um label / goto em Python?


178

Existe gotoalgum equivalente em Python para poder pular para uma linha específica de código?


2
A etiqueta é bastante vaga - você pode ser mais específico sobre o que está procurando?
Dana


9
Um amigo meu implementou gotoem Python quando ele estava traduzindo algum código Fortran para Python. Ele se odiava por isso.
Cody Piersall

3
github.com/cdjc/gotomuito mais rápido do que a implementação entrian)
cdjc

"O rótulo é bastante vago", nenhum rótulo é inteligente, estruturado está funcionando como uma máquina
datdinhquoc

Respostas:


118

Não, o Python não suporta rótulos e vai, se é isso que você procura. É uma linguagem de programação (altamente) estruturada.


36
funções @rejinacm?
Unkwntech

79

O Python oferece a capacidade de fazer algumas das coisas que você poderia fazer com um goto usando funções de primeira classe. Por exemplo:

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

    label1:
        ...
    label2:
        ...
}

Pode ser feito em python assim:

def func1():
    ...

def func2():
    ...

funcmap = {1 : func1, 2 : func2}

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

É verdade que essa não é a melhor maneira de substituir o goto. Mas sem saber exatamente o que você está tentando fazer com o ir, é difícil dar conselhos específicos.

@ ascobol :

Sua melhor aposta é incluí-la em uma função ou usar uma exceção. Para a função:

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

Para a exceção:

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

Usar exceções para fazer coisas como essa pode parecer um pouco estranho se você vier de outra linguagem de programação. Mas eu argumentaria que, se você não gosta de usar exceções, o Python não é a linguagem para você. :-)


Use-o criteriosamente. Exceções no Python são mais rápidas que a maioria das outras linguagens. Mas eles ainda são lentos se você enlouquecer com eles.
Jason Baker

Apenas um aviso: loopfuncgeralmente exigirá contribuições e mais algum esforço para implementar, mas é o melhor caminho na maioria dos casos, eu acho.
Kon psych

60

Recentemente, escrevi um decorador de funções que habilita gotono Python, assim:

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

Não sei ao certo por que alguém gostaria de fazer algo assim. Dito isto, eu não sou muito sério sobre isso. Mas eu gostaria de ressaltar que esse tipo de meta programação é realmente possível no Python, pelo menos no CPython e PyPy, e não apenas usando a API do depurador como o outro cara fez. Você precisa mexer com o bytecode.


3
Ótimo decorador que você fez! Impressionante como você pode mexer com o bytecode :-)
K.Mulier

Eu acho que essa deve ser a resposta aceita para essa pergunta. Isso pode ser útil para muitos loops aninhados, por que não?
PiMathCLanguage

Isso suporta apenas .begine .endrótulos?
Alexej Magura

29

Encontrei isso nas perguntas frequentes oficiais sobre Design e História do python .

Por que não há ir?

Você pode usar exceções para fornecer um "goto estruturado" que funciona mesmo em chamadas de função. Muitos acham que as exceções podem emular convenientemente todos os usos razoáveis ​​das construções "go" ou "goto" de C, Fortran e outros idiomas. Por exemplo:

class label(Exception): pass  # declare a label

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

Isso não permite que você pule no meio de um loop, mas isso geralmente é considerado um abuso de goto. Use moderadamente.

É muito bom que isso seja mencionado na FAQ oficial e que é fornecida uma amostra de solução legal. Eu realmente gosto de python porque sua comunidade está tratando gotoassim mesmo ;)


1
O abuso gotoé um dos principais foux pas de programação, com certeza, mas a OMI abusa de exceções para emulargoto é apenas um pouco melhor e ainda deve ser desaprovado. Eu preferiria que os criadores do Python incluíssem gotona linguagem nas poucas ocasiões em que é realmente útil que não a proibisse porque "é ruim, pessoal" e recomendem o abuso de exceções para obter a mesma funcionalidade (e o mesmo spaghettification de código).
Abion47 9/07

15

Para responder à @ascobolpergunta usando @bobinceas sugestões dos comentários:

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

O recuo do elsebloco está correto. O código usa obscuro elseapós uma sintaxe Python de loop. Consulte Por que o python usa 'else' após loops for e while?


Corrigi o recuo do outro bloco, o que levou a uma descoberta interessante :
Braden Best

3
@ B1KMusic: o recuo está correto como está. É uma sintaxe especial do Python. elseé executado após o loop se breaknão tiver sido encontrado. O efeito é que should_terminate_the_looptermina os loops internos e externos.
jfs

1
Eu deveria ter especificado que só fiz essa descoberta depois de fazer a edição. Antes disso, pensei que havia descoberto um bug no intérprete, então fiz vários casos de teste e fiz algumas pesquisas para entender o que estava acontecendo. Me desculpe por isso.
Braden Best

1
Agora que eu entendo o que está acontecendo, eu concordo, que é um código de esotérico que seria feito muito mais facilmente com métodos mais tradicionais
Braden Melhor

1
@ B1KMusic: Não. Duplicar código para solucionar sua ignorância não é uma boa solução. Sim. return sugerido por Jason Baker é uma boa alternativa para romper loops profundamente aninhados.
jfs

12

Uma versão de trabalho foi criada: http://entrian.com/goto/ .

Nota: Foi oferecido como uma piada do primeiro de abril. (trabalhando embora)

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

Escusado será dizer que. Sim, é engraçado, mas NÃO o use.


1
parece melhor para mim do que usar três pausas ... é claro que existem outras maneiras de escrevê-lo também.
Nick

1
@ Nick O uso da função com retorno parece ainda melhor.
Erik Šťastný

7

Os rótulos para breake continueforam propostos no PEP 3136 em 2007, mas foram rejeitados. A seção Motivação da proposta ilustra vários métodos comuns (se deselegantes) para imitar rotulados breakem Python.


7

É tecnicamente possível adicionar uma declaração semelhante a 'goto' ao python com algum trabalho. Usaremos os módulos "dis" e "new", ambos muito úteis para varrer e modificar o código de bytes python.

A principal idéia por trás da implementação é marcar primeiro um bloco de código como usando as instruções "goto" e "label". Um decorador especial "@goto" será usado para marcar as funções "goto". Depois, varremos esse código para essas duas instruções e aplicamos as modificações necessárias no código de bytes subjacente. Isso tudo acontece no tempo de compilação do código fonte.

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 isso responda à pergunta.


5

você pode usar exceções definidas pelo usuário para emulargoto

exemplo:

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 impressionante, mas podemos silenciar o método de exceção str m
Anônimo

@ Anônimo, que exceção? você usa python3?
Xavierskip

5

Python 2 e 3

pip3 install goto-statement

Testado em Python 2.6 a 3.6 e PyPy.

Link: declaração-goto


foo.py

from goto import with_goto

@with_goto
def bar():

    label .bar_begin

    ...

    goto .bar_begin

3

Eu estava procurando por algo parecido com

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;

Portanto, minha abordagem foi usar um booleano para ajudar a sair do loop aninhado para:

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

Existe agora. vamos para

Eu acho que isso pode ser útil para o que você está procurando.


1

Eu queria a mesma resposta e não queria usar goto. Então, usei o seguinte exemplo (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

Eu tenho meu próprio jeito de fazer gotos. Eu uso scripts python separados.

Se eu quiser fazer um loop:

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: Essa técnica funciona apenas nas versões do Python 2.x)


1

Para avançar, você pode adicionar:

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

Porém, isso só ajuda em cenários simples (ou seja, aninhar esses itens causaria uma confusão)


1

Em vez de um equivalente python goto, uso a instrução break da seguinte maneira para testes rápidos do meu código. Isso pressupõe que você tenha uma base de código estruturada. A variável de teste é inicializada no início de sua função e apenas movo o bloco "If test: break" para o final do bloco ou loop aninhado if-then que desejo testar, modificando a variável de retorno no final do código para refletir a variável de bloco ou loop que estou testando.

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

1

Embora não exista nenhum código equivalente ao goto/labelPython, você ainda pode obter essa funcionalidade degoto/label usando loops.

Vamos pegar um exemplo de código mostrado abaixo, onde goto/labelpode ser usado em uma linguagem arbitrária que não seja 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...')

Agora, a mesma funcionalidade do exemplo de código acima pode ser alcançada em python usando um whileloop, como mostrado abaixo.

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

não, existe uma maneira alternativa de implementar a instrução 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()  
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.