Чи є ярлик / гото в Python?


178

Чи є gotoеквівалент у Python, щоб мати можливість перейти до певного рядка коду?


2
Мітка досить розпливчаста - чи можете ви бути більш конкретними щодо того, що шукаєте?
Дана


9
Мій друг, реалізований gotoу Python, коли він перекладав якийсь код Fortran на 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.

Зрозуміло, це не найкращий спосіб замінити гото. Але не знаючи, що саме ви намагаєтеся зробити з гото, важко дати конкретні поради.

@ 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як правило, потрібні додаткові вкладення та додаткові зусилля, але в більшості випадків це найкращий спосіб.
kon psych

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
Чудовий декоратор, який ви зробили! Дивовижно, як можна пограти з байт-
кодом

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

Чи означає це тільки підтримка .beginі .endетикетки?
Олексій Магура

29

Я знайшов це в офіційному FAQ-питання щодо дизайну та історії python .

Чому немає гото?

Винятки можна використовувати для надання "структурованого переходу", який працює навіть у функціональних викликах. Багато хто вважає, що винятки можуть зручно імітувати всі розумні способи використання конструкцій "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, і що приклад приємного рішення надається. Мені дуже подобається python, тому що його спільнота трактує навіть gotoтак;)


1
Зловживання goto- це головний програмовий фукс-папір, напевне, але IMO, який зловживає винятками для емуляції, gotoє лише дещо кращим, і його слід сильно нахмурити. Я вважаю за краще, щоб творці Python включали gotoв мову кілька випадків, коли це насправді корисно, ніж забороняти його, оскільки "це погано, хлопці", а потім рекомендую зловживати винятками, щоб отримати той самий функціонал (і той самий спагетіфікація коду).
Авіон47

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" після і для циклів?


Я виправив ваш інший блок відступ, що призвело до цікавого відкриття :
Бреден Best

3
@ B1KMusic: відступ правильний як є. Це особливий синтаксис Python. elseвиконується після циклу, якщо breakйого не зустрічалося. Ефект полягає в тому, що should_terminate_the_loopприпиняються як внутрішні, так і зовнішні петлі.
jfs

1
Я повинен був уточнити, що це відкриття я зробив лише після того, як я змінив його. До цього я думав, що виявив помилку в перекладачі, тому зробив купу тестових справ і зробив кілька досліджень, щоб зрозуміти, що відбувається. Вибач за це.
Бреден Кращий

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

1
@ B1KMusic: Ні. Копіювання коду для подолання вашого незнання не є хорошим рішенням. Так. return запропонований @ Джейсон Бейкер - хороша альтернатива, щоб вирватися з глибоко вкладених петель.
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 Використання функції із поверненням виглядає ще набагато краще.
Erik Šťastný

7

Етикетки для breakта continueбули запропоновані в PEP 3136 ще в 2007 році, але він був відхилений. Розділ Мотивація пропозиції ілюструє декілька поширених (якщо неелегантних) методів наслідування, позначених breakна Python.


7

Технічно доцільно додати python, як-небудь операцію, схожу на "goto". Ми будемо використовувати модулі "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 str m
Анонімний

@ Anonymous який виняток? ви використовуєте python3?
xavierskip

5

Python 2 & 3

pip3 install goto-statement

Тестували на Python 2.6 по 3.6 та PyPy.

Посилання: goto-заява


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 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. Тому я використав наступний приклад (from 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

У мене є свій спосіб робити гото. Я використовую окремі сценарії 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

Для переходу Goto ви можете просто додати:

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.