Есть ли ярлык / goto в Python?


178

Есть ли gotoв Python или какой-либо эквивалент, чтобы можно было перейти к определенной строке кода?


2
Этикетка довольно расплывчата - можете ли вы быть более точным в отношении того, что вы ищете?
Дана


9
Мой друг реализовал gotoв Python, когда он переводил код на Фортране на Python. Он ненавидел себя за это.
Коди Пирсолл

3
github.com/cdjc/goto (это намного быстрее, чем реализация entrian)
cdjc

«Этикетка довольно расплывчата», ни один лейбл не является умным, структурированный работает как машина
datdinhquoc

Ответы:


118

Нет, Python не поддерживает метки и goto, если это то, что вам нужно. Это (высоко) структурированный язык программирования.


36
функции @rejinacm?
UnkwnTech

79

Python предлагает вам возможность делать некоторые вещи, которые вы можете сделать с помощью goto, используя функции первого класса. Например:

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

    label1:
        ...
    label2:
        ...
}

Может быть сделано в Python, как это:

def func1():
    ...

def func2():
    ...

funcmap = {1 : func1, 2 : func2}

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

Конечно, это не лучший способ заменить goto. Но не зная точно, что вы пытаетесь сделать с помощью goto, сложно дать конкретный совет.

@ ascobol :

Лучше всего заключить ее в функцию или использовать исключение. Для функции:

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

Для исключения:

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

Использование исключений для подобных вещей может показаться немного неловким, если вы пришли из другого языка программирования. Но я бы сказал, что если вам не нравится использовать исключения, Python не для вас. :-)


Используйте это разумно. Исключения в Python быстрее, чем в большинстве других языков. Но они все еще медленные, если вы сходите с ума от них.
Джейсон Бейкер

Просто замечание: loopfuncкак правило, потребует ввода и некоторых дополнительных усилий для реализации, но я думаю, что это лучший способ в большинстве случаев.
Кон Псих

60

Недавно я написал функцию-декоратор, которая включает gotoв Python, вот так:

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

Я не уверен, почему кто-то хотел бы сделать что-то подобное, хотя. Тем не менее, я не слишком серьезно об этом. Но я хотел бы отметить, что этот вид метапрограммирования действительно возможен в Python, по крайней мере, в CPython и PyPy, и не только путем неправильного использования API отладчика, как это делал другой парень . Вы должны возиться с байт-кодом, хотя.


3
Отличный декоратор ты сделал! Удивительно, как вы можете возиться с байт-кодом :-)
K.Mulier

Я думаю, это должен быть принятый ответ на этот вопрос. Это может быть полезно для многих вложенных циклов, почему бы и нет?
PiMathCLanguage

Это только поддержка .beginи .endметки?
Алексей Магура

29

Я нашел это в официальном FAQ по дизайну и истории Python .

Почему нет goto?

Вы можете использовать исключения для обеспечения «структурированного перехода», который работает даже при вызовах функций. Многие считают, что исключения могут удобно эмулировать все разумные варианты использования конструкций «go» или «goto» в C, Fortran и других языках. Например:

class label(Exception): pass  # declare a label

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

Это не позволяет вам перейти в середину цикла, но это обычно считается злоупотреблением goto. Используйте экономно.

Очень приятно, что об этом даже упоминается в официальном FAQ, и что предоставляется хороший пример решения. Мне действительно нравится питон, потому что его сообщество относится к этому даже gotoтак;)


1
Злоупотребление gotoявляется основной ошибкой в ​​программировании, но злоупотребление исключениями для эмуляции в IMO gotoлишь немного лучше, и его все равно следует осуждать. Я бы предпочел, чтобы создатели Python включали gotoв этот язык несколько случаев, когда это действительно полезно, чем запрещать его, потому что «это плохо, ребята», а затем рекомендовали бы использовать исключения для получения той же функциональности (и той же спагеттификации кода).
Abion47

15

Чтобы ответить на @ascobolвопрос, используя @bobinceпредложение из комментариев:

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

Отступ для elseблока правильный. Код использует неясный elseпосле цикла синтаксис Python. См. Почему Python использует «else» после циклов for и while?


Я исправил ваш еще один отступ блока, который привел к интересному открытию :
Брэден Бест

3
@ B1KMusic: отступ правильный, как есть. Это специальный синтаксис Python. elseвыполняется после цикла, если breakне было найдено. Эффект заключается в том, что should_terminate_the_loopзаканчивается как внутренние, так и внешние петли.
JFS

1
Я должен был указать, что я сделал это открытие только после того, как сделал редактирование. До этого я думал, что обнаружил ошибку в интерпретаторе, поэтому я сделал несколько тестовых случаев и провел некоторое исследование, чтобы понять, что происходит. Извини за это.
Брэден Бест

1
Теперь, когда я понимаю, что происходит, я согласен, что это некоторый эзотерический код, который можно было бы сделать намного проще с помощью более традиционных методов
Braden Best

1
@ B1KMusic: Нет. Дублирование кода для обхода вашего невежества не является хорошим решением. Да. return предложенный @Jason Baker - хорошая альтернатива для выхода из глубоко вложенных циклов.
JFS

12

Рабочая версия была сделана: http://entrian.com/goto/ .

Примечание: оно было предложено как первоапрельская шутка. (хотя работает)

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

Разумеется. Да, это смешно, но не используйте его.


1
выглядит лучше, чем использование 3 перерывов ... конечно, есть и другие способы написать это.
Ник

1
@Nick Использование функции с возвратом выглядит даже намного лучше.
Эрик Шастны

7

Метки для breakи continueбыли предложены в PEP 3136 еще в 2007 году, но они были отклонены. Раздел « Мотивация » предложения иллюстрирует несколько распространенных (если не элегантных) методов для имитации, помеченных breakв Python.


7

Технически выполнимо добавить оператор «goto» в python с некоторой работой. Мы будем использовать модули "dis" и "new", которые очень полезны для сканирования и изменения байт-кода Python.

Основная идея реализации заключается в том, чтобы сначала пометить блок кода как операторы «goto» и «label». Специальный декоратор "@goto" будет использоваться для обозначения функций "goto". После этого мы сканируем этот код для этих двух операторов и применяем необходимые модификации к базовому байтовому коду. Все это происходит во время компиляции исходного кода.

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

Надеюсь, что это отвечает на вопрос.


5

Вы можете использовать определяемые пользователем исключения для эмуляцииgoto

пример:

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

Потрясающий метод, но можем ли мы отключить метод исключения str m
Anonymous

@ Аноним, какое исключение? вы используете python3?
xavierskip

5

Python 2 и 3

pip3 install goto-statement

Протестировано на Python 2.6 до 3.6 и PyPy.

Ссылка: goto-Statement


foo.py

from goto import with_goto

@with_goto
def bar():

    label .bar_begin

    ...

    goto .bar_begin

3

Я искал что-то похожее на

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;

Поэтому мой подход состоял в том, чтобы использовать логическое значение, чтобы помочь вырваться из вложенных циклов 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


1

Я хотел тот же ответ, и я не хотел использовать goto. Поэтому я использовал следующий пример (из 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

У меня есть свой способ делать gotos. Я использую отдельные скрипты Python.

Если я хочу зациклить:

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

( ПРИМЕЧАНИЕ. Этот метод работает только в версиях Python 2.x)


1

Для форвард Гото, вы можете просто добавить:

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

Это помогает только для простых сценариев (то есть, их вложение приведет вас в беспорядок)


1

Вместо эквивалента Python Goto я использую оператор break следующим образом для быстрых тестов моего кода. Это предполагает, что у вас есть структурированная кодовая база. Тестовая переменная инициализируется в начале вашей функции, и я просто перемещаю блок «If test: break» в конец вложенного блока if-then или цикла, который я хочу протестировать, изменяя возвращаемую переменную в конце кода чтобы отразить блочную или циклическую переменную, которую я тестирую.

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

1

Хотя goto/labelв Python нет кода, эквивалентного таковому, вы все равно можете получить такую ​​функциональность, goto/labelиспользуя циклы.

Давайте возьмем приведенный ниже пример кода, который goto/labelможно использовать на любом языке, отличном от 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...')

Теперь та же функциональность приведенного выше примера кода может быть достигнута в Python с помощью whileцикла, как показано ниже.

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

нет, есть альтернативный способ реализации оператора 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()  
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.