มีป้ายกำกับ / goto ใน Python หรือไม่


178

gotoPython มีหรือเทียบเท่าใน Python เพื่อให้สามารถข้ามไปยังบรรทัดของรหัสที่ระบุได้หรือไม่?


2
ป้ายกำกับค่อนข้างคลุมเครือ - คุณเจาะจงเจาะจงมากขึ้นเกี่ยวกับสิ่งที่คุณกำลังมองหาหรือไม่?
Dana


9
เพื่อนของฉันติดตั้งgotoใน Python เมื่อเขาแปลรหัส Fortran ไปเป็น Python เขาเกลียดตัวเอง
Cody Piersall

3
github.com/cdjc/goto (มันมากเร็วกว่าการดำเนิน entrian)
cdjc

"ฉลากค่อนข้างคลุมเครือ" ไม่มีฉลากอัจฉริยะโครงสร้างทำงานได้เหมือนเครื่องจักร
datdinhquoc

คำตอบ:


118

ไม่ Python ไม่รองรับฉลากและข้ามไปหากเป็นสิ่งที่คุณต้องการ มันเป็นภาษาการเขียนโปรแกรมที่มีโครงสร้างสูง


36
@rejinacm ฟังก์ชั่น?
UnkwnTech

79

Python เสนอความสามารถในการทำบางสิ่งที่คุณสามารถทำได้ด้วยการข้ามไปใช้ฟังก์ชั่นชั้นหนึ่ง ตัวอย่างเช่น:

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 นั้นยากที่จะให้คำแนะนำเฉพาะ

@ 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

ฉันไม่แน่ใจว่าทำไมคนเราถึงอยากทำอะไรแบบนั้น ที่กล่าวว่าฉันไม่จริงจังกับมัน แต่ผมอยากจะชี้ให้เห็นว่าชนิดของการเขียนโปรแกรมเมตานี้เป็นจริงที่เป็นไปได้ในหลามอย่างน้อยใน CPython และ PyPy และไม่เพียง แต่โดย misusing ดีบัก API กับที่คนอื่นทำ คุณต้องยุ่งกับ bytecode ว่า


3
มัณฑนากรที่ยอดเยี่ยมที่คุณทำ! ยอดเยี่ยมว่าคุณสามารถเล่นซอกับ bytecode :-)
K.Mulier

ฉันคิดว่านี่ควรเป็นคำตอบที่ยอมรับสำหรับคำถามนี้ สิ่งนี้อาจมีประโยชน์สำหรับลูปซ้อนกันหลายแห่งทำไมล่ะ?
PiMathCLanguage

สิ่งนี้รองรับ.beginและติด.endป้ายกำกับเท่านั้นหรือไม่
Alexej Magura

29

ฉันพบนี้ในหลามอย่างเป็นทางการออกแบบและประวัติศาสตร์คำถามที่พบบ่อย

ทำไมถึงไม่มี goto?

คุณสามารถใช้ข้อยกเว้นเพื่อจัดทำ "goto 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หลังจากวนลูปไวยากรณ์ ดูทำไมงูหลามจึงใช้ 'else' หลังจากนั้นและในขณะที่ลูป


ฉันแก้ไขการเยื้องบล็อกอื่นของคุณซึ่งนำไปสู่การค้นพบที่น่าสนใจ :
Braden ที่ดีที่สุด

3
@ B1KMusic: เยื้องถูกต้องตามที่เป็นอยู่ มันเป็นไวยากรณ์ Python พิเศษ elseถูกดำเนินการหลังจากวนรอบหากbreakไม่พบ ผลที่ได้คือshould_terminate_the_loopยุติทั้งด้านในและด้านนอกของลูป
jfs

1
ฉันควรระบุว่าฉันได้ค้นพบนั้นหลังจากทำการแก้ไขเท่านั้น ก่อนหน้านั้นฉันคิดว่าฉันค้นพบข้อผิดพลาดในล่ามดังนั้นฉันจึงทำกรณีทดสอบและทำการวิจัยเพื่อทำความเข้าใจว่าเกิดอะไรขึ้น ขอโทษด้วยกับเรื่องนั้น.
Braden ที่ดีที่สุด

1
ตอนนี้ฉันเข้าใจแล้วว่าเกิดอะไรขึ้นฉันเห็นด้วยนั่นคือรหัสลับที่สามารถทำได้ง่ายขึ้น
Braden Best

1
@ B1KMusic: ไม่การทำสำเนารหัสเพื่อแก้ปัญหาความไม่รู้ของคุณไม่ใช่วิธีแก้ปัญหาที่ดี ใช่. return แนะนำโดย @Jason Bakerเป็นทางเลือกที่ดีในการแยกลูปที่ซ้อนกันอย่างล้ำลึก
jfs

12

รุ่นที่ทำงานได้รับการทำ: http://entrian.com/goto/

หมายเหตุ: มันเป็นเรื่องตลกของ April Fool (ทำงานแม้ว่า)

# 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 ครั้ง ... แน่นอนว่ามีวิธีอื่นในการเขียนเช่นกัน
Nick

1
@Nick ใช้ฟังก์ชั่นที่มีผลตอบแทนดูดียิ่งขึ้น
Erik Šťastný

7

ป้ายกำกับbreakและcontinueถูกเสนอในPEP 3136ย้อนกลับไปในปี 2550 แต่ถูกปฏิเสธ แรงจูงใจในส่วนของข้อเสนอแสดงให้เห็นถึงพื้นฐานหลายประการ (ถ้าไม่งดงาม) วิธีการเลียนแบบที่มีข้อความbreakในหลาม


7

มีความเป็นไปได้ทางเทคนิคที่จะเพิ่มคำสั่ง like 'goto' ใน python กับงานบางอย่าง เราจะใช้โมดูล "dis" และ "new" ซึ่งมีประโยชน์อย่างมากสำหรับการสแกนและการแก้ไขโค้ดไบต์หลาม

แนวคิดหลักที่อยู่เบื้องหลังการใช้งานคือการทำเครื่องหมายบล็อกของรหัสก่อนโดยใช้คำสั่ง "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()

วิธีการที่ยอดเยี่ยม แต่เราสามารถปิดเสียงวิธียกเว้นข้อ
ไม่เปิดเผยตัว

@ ไม่ระบุชื่อข้อยกเว้นใด? คุณใช้ python3 หรือเปล่า
xavierskip


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ฉันต้องการคำตอบเดียวกันและผมไม่ต้องการที่จะใช้ ดังนั้นฉันใช้ตัวอย่างต่อไปนี้ (จาก 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 ของตัวเอง ฉันใช้สคริปต์หลามแยกต่างหาก

ถ้าฉันต้องการวนรอบ:

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 block หรือ loop ที่ฉันต้องการทดสอบปรับเปลี่ยนตัวแปร return ที่ท้ายรหัส เพื่อสะท้อนถึงบล็อกหรือตัวแปรลูปฉันกำลังทดสอบ

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

1

แม้ว่าจะไม่มีรหัสใดเทียบเท่ากับgoto/labelใน Python คุณยังสามารถรับฟังก์ชั่นดังกล่าวได้goto/labelใช้ใช้ลูปได้

ให้นำตัวอย่างโค้ดที่แสดงด้านล่างซึ่งgoto/labelสามารถใช้ในภาษาใดก็ได้ที่ไม่ใช่ภาษาไพ ธ อน

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...')

ตอนนี้การทำงานที่เหมือนกันของตัวอย่างโค้ดข้างต้นสามารถทำได้ในไพ ธ อนโดยใช้การ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()  
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.