【问题标题】:How to return a specific point after an error in 'while' loop'while'循环中的错误后如何返回特定点
【发布时间】:2016-01-02 20:34:50
【问题描述】:

我正在尝试编写一个包含while 循环的程序,在这个循环中,如果出现问题,我会收到一条错误消息。有点像这样;

while True:

    questionx = input("....")
    if x =="SomethingWrongabout questionX":
        print ("Something went wrong.")
        continue
    other codes...

    questiony = input("....")
    if y == "SomethingWrongabout questionY":
        print ("Something went wrong.")
        continue

    other codes...

    questionz = input("....")
    if z == "SomethingWrongabout questionZ":
       print ("Something went wrong.")
       continue

    other codes..

问题如下:当questionX之后出现错误时,程序进入开始。它从头开始,而不是从yz。但是x没有问题,所以程序应该从yz开始提问,因为问题发生在yz

如何使程序从特定点开始,例如如果仅在yquestion 出现错误,则程序必须从y 开始提问,或者如果仅在z,程序必须从@ 开始987654334@,不是开始-不是x

我应该为此使用多个while 循环,还是有什么东西可以使这个只在一个循环中工作?

【问题讨论】:

  • 你为什么要在一个while循环中实现这个,或者这个特定的代码结构?如果您想重新启动完整的程序以记住最后一个正确回答的问题,为什么不使用循环外可用的变量,无论是在内存中还是在持久存储中?

标签: python loops python-3.x while-loop continue


【解决方案1】:

一个简单的解决方案是使用一个计数器变量来解决这个问题。像这样:

counter = 0
while True:
    if counter == 0:
        questionx = input("....")
        if x =="SomethingWrongabout questionX":
            print ("Something went wrong.")
            continue
        else:
            counter = counter + 1
         other codes...

    if counter <= 1:
        questiony = input("....")
        if y == "SomethingWrongabout questionY":
            print ("Something went wrong.")
            continue
        else:
            counter = counter + 1
        other codes...

    if counter <= 2:
         questionz = input("....")
         if z == "SomethingWrongabout questionZ":
             print ("Something went wrong.")
             continue
         else:
             counter = counter + 1
        other codes..

这个想法是每次事情顺利时增加计数器。计数器递增后,它不会执行其他条件,而是直接跳转到出错的代码块

【讨论】:

  • 最后一次计数器检查应该是if counter &lt;= 2:吗?
  • 是的!我的错。对于每个条件块,它应该加一。感谢您指出。
【解决方案2】:

[从生成器编辑到函数]

你可以试试一个函数:

def check_answer(question, answer):
    while True:
        current_answer = input(question)
        if current_answer == answer:
            break
        print "Something wrong with question {}".format(question)
    return current_answer

answerX = check_answer("Question about X?\n", "TrueX")
answerY = check_answer("Question about Y?\n", "TrueY")
answerZ = check_answer("Question about Z?\n", "TrueZ")

不确定是否要保留这些值,但如果您需要调整它,这应该会给您提示。

结果:

Question about X?
"blah"
Something wrong with question Question about X?

Question about X?
"blah"
Something wrong with question Question about X?

Question about X?
"TrueX"
Question about Y?
"TrueY"
Question about Z?
"blah"
Something wrong with question Question about Z?

Question about Z?
"blah"
Something wrong with question Question about Z?

Question about Z?
"TrueZ"

根据评论编辑:

def check_answer(question, answers):
    while True:
        current_answer = input(question)
        if current_answer in answers:
            break
        print "Something wrong with question {}".format(question)
    return current_answer

answerX = check_answer("Question about X?\n", ("TrueX", "TrueY")

【讨论】:

  • 你的答案似乎是我想要的,问题是我不习惯使用发电机,我必须检查不止一次的情况。喜欢;如果答案与“G”、“g”、“B”、“b”、“M”、“m”不同,我想提出一个错误句子,然后再次提问。我试图把你的答案变成我想要的但做不到,你能像这样编辑你的答案吗?如果答案与上述不同,则适用于多种变体。
  • 只需将答案替换为答案列表 ["answer1","answer2","answer3", ...] 并检查“if current_answer in answers”。我会把它打出来,但 cmets 并不真正允许这样做。创建一个单独的答案太多了,因为如果这是您想要的,我希望您接受@salparadise 答案。他可能会更新他的答案以反映这一点。
  • @GLHF 如果提供的答案在可能性列表中,您想重新提出问题吗?如果是这样,只需列出一个列表,并确保它不像 bastjin 提到的那样存在。我用这个版本编辑过。
  • 生成器是不必要的,并且没有提供简单函数的价值。
  • 我同意@EthanFurman。考虑编辑以删除生成器,以免误导未来的访问者。
【解决方案3】:

只需使用迭代器迭代问题,在获得所需输出之前不要在迭代器上调用 next:

questions = iter(("who is foo", "who is bar", "who is foobar"))
def ask(questions):
    quest = next(questions)
    while quest:
        inp = input(quest)
        if inp != "whatever":
            print("some error")
        else:
            print("all good")
            quest = next(quest, "")

如果您有问题和答案,请将它们压缩在一起:

def ask(questions, answers):
    zipped = zip(questions,answers) # itertools.izip python2
    quest,ans = next(zipped)
    while quest:
        inp = input(quest)
        if inp != ans:
            print("Wrong")
        else:
            print("all good")
            quest, ans = next(zipped, ("",""))

【讨论】:

  • 这些都不是生成器。
  • 不,这只是一个tuple。第二个代码 sn-p 有效,因为您 zipped 了它,但第一个代码失败了。
  • @EthanFurman,是的,意思是 ( q for q in ("who is foo", "who is bar", "who is foobar")),但 iter 会完成这项工作
【解决方案4】:

我会这样做:

qa = (
    ('Question X', 'Answer X'),
    ('Question Y', 'Answer Y'),
    ('Question Z', 'Answer Z'),
)

for item in enumerate(qa):
    question = item[1][0]
    answer = item[1][1]
    while True:
        usr = input("What is the answer to %s: " % question)
        if usr == answer:
            break

这会导致:

$ python qa.py
What is the answer to Question X: Answer X
What is the answer to Question Y: Answer Y
What is the answer to Question Z: Answer X
What is the answer to Question Z: Answer Z

Process finished with exit code 0

【讨论】:

    【解决方案5】:

    这个问题将通过多个 while 循环来解决。这些循环是全部在一个地方,还是分解为函数/生成器/等,由您选择。

    如果是我,我会将提问代码分解成一个函数,该函数接受问题本身,加上验证代码来验证答案——该函数会一直询问问题,直到验证通过:

    def ask_question(question, validate):
        while "not valid":
            answer = input(question)
            if validate(answer):
                return answer
            else:
                print(" invalid response, try again")
    
    while True:
    
        x = ask_question("....", lambda a: a=="SomethingWrongabout questionX")
    
        ...other codes...
    
        y = ask_questiony("....", lambda a: a== "SomethingWrongabout questionY")
    
        ...other codes...
    
        z = ask_questionz("....", lambda a: a=="SomethingWrongabout questionZ")
    

    【讨论】:

    • 可能这里不需要 True?
    • 取决于:问题之一可能是“再试一次?”
    【解决方案6】:

    问题是程序凝聚力之一。如果您有具有特定验证的特定问题,您应该为它们编写函数..

    def getX():
       while True:
          response = input("...")
          if response == "something wrong with x":
             print("Something went wrong with x")
          else:
             return response
    
    def getY():
       ...
    

    然后在你的代码中你只是

    x = getX()
    y = getY()
    z = getZ()
    

    这些函数中的每一个都可以以不同的方式验证输入。如果您的许多验证属于特定模式,您也可以尝试概括它们。例如

    def getInt(name, range_start, range_end):
       prompt = "Enter a number for {} between {} and {}".format(name,
                                                                 range_start, 
                                                                 range_end)
       while True:
          try:
              response = int(input(prompt))
          raise ValueError:
              print("That's not a number")
              continue
          if response not in range(range_start, range_end+1):
              print(response, 'is not in the range')
          else:
              return response
    

    【讨论】:

      【解决方案7】:

      在进入循环之前将xyz 设置为None。然后用if 保护每个问题,并在continue 之前再次将有问题的变量设置为None

      x = y = z = None
      while True:
      
          if x is None:
              questionx = input("....")
              if x =="SomethingWrongabout questionX":
                  print ("Something went wrong.")
                  x = None
                  continue
      
              other codes...
      
          if y is None:
              questiony = input("....")
              if y == "SomethingWrongabout questionY":
                  print ("Something went wrong.")
                  y = None
                  continue
      
              other codes...
      
          if z is None:
              questionz = input("....")
              if z == "SomethingWrongabout questionZ":
                 print ("Something went wrong.")
                  z = None
                 continue
      
              other codes..  
      

      【讨论】:

        【解决方案8】:

        我认为这里有两个非常简单、优雅的解决方案。

        这个想法是有一个要问的问题列表。只要问题仍然存在,两种实现都会继续询问。一个会使用itertools.dropwhile() 方法从列表中删除元素,只要问题的答案是正确的,另一个会做一些不同的事情 - 见下文。

        在这个示例实现中,神奇的答案“foo”是任何问题的错误答案。您可以在 Python 中运行它以检查它是否会在您回答“foo”的问题处重新开始询问(剩余的)问题。

        修改ask_question()函数应该很容易适应你的情况。

        import itertools
        
        input = lambda x: raw_input("what is your "+x+"? ")
        
        # returns true or false; wether or not the question was answered 
        # correctly
        def ask_question(question):
            answer = input(question)
            # could be any test involving answer
            return answer != "foo"
        
        # assume we have a list of questions to ask
        questions = [ "age", "height", "dog's name" ]
        
        # keep on looping until there are questions
        while questions:
            questions = list(itertools.dropwhile(ask_question, questions))
        

        编辑 所以,在幕后,仍然有两个 while 循环(takewhile() 是一个赠品:-))。通过一些开箱即用的思考,它甚至可以在没有一个 while 循环的情况下完成:

        递归就是这个词!

        def ask_more_questions(question_list):
            # no more questions? then we're done
            if not question_list:
                return
            # ask the first question in the list ...
            if ask_question(question_list[0]):
                # ok, this one was answered fine, continue with the remainder
                ask_more_questions(question_list[1:])
            else:
                # Incorrect answer, try again with the same list of questions
                ask_more_questions(question_list)
        

        如果你喜欢,可以压缩成:

        def ask(q_list):
            if qlist:
                ask(q_list[1:]) if ask_question(q_list[0]) else ask(q_list)
        

        【讨论】:

          【解决方案9】:

          是否可以将您的代码放入函数中?知道问题遵循任意顺序,如果答案不符合您的标准,您可以使用 try/except 块,并保留已回答问题的列表。

          假设我们有一个全局列表:

          answered_questions = []
          

          还有一个帮助函数,让我根据之前列表的长度检查问题是否已被回答:

          def is_it_answered(index):
              """
              Ckecks whether the question number "index" has already been answered.
              :param index: Number of question inside answered_questions
              :return: True if the question was already asked
              """
              # Checking for the index value to be True may not be necessary, but it's just for safety
              if len(answered_questions) >= index + 1 and answered_questions[index]:
                  return True
          

          现在,您在 main 函数中所要做的就是将与每个问题对应的代码放入每个套件中。如果输入了您不想要的答案,请提出异常,而不是在完成该问题背后的逻辑之前做任何您想做的事情。

          def ask_questions():
          
              if not is_it_answered(0):
                  try:
                      answered_questions.append(True)
                      questionx = input("...")
          
                      # Whatever is supposed to make Question X wrong goes here
                      if questionx == "not what i want":
                          raise Exception
          
                  except Exception:
                      print "Something went wrong in question x"
                      # do whatever you want to do regarding questionx being wrong
                      ask_questions()
          
                  # Rest of Code for Question X if everything goes right
          
              if not is_it_answered(1):
                  try:
                      answered_questions.append(True)
                      questiony = input("...")
          
                      # Whatever is supposed to make Question Y wrong goes here
                      if questiony == "not what i want":
                          raise Exception
          
                  except Exception:
                      print("Something went wrong")
                      # do whatever you want to do regarding questionxy being wrong
                      ask_questions()
          
                  # Rest of Code for Question Y if everything goes right
          
              if not is_it_answered(2):
                  try:
                      answered_questions.append(True)
                      questionz = input("...")
          
                      # Whatever is supposed to make Question Z wrong goes here
                      if questionz == "not what i want":
                          raise Exception
          
                  except Exception:
                      print("Something went wrong")
                      ask_questions()
          
                  # Rest of Code for Question Z
          
                  # If this is the last question, you can now call other function or end
          
          if __name__ == "__main__":
              ask_questions()
          

          在这段代码中,键入“not what i want”将引发异常,并且在 except 块内,您的函数将被再次调用。请注意,任何未在 if 条件中缩进的代码都将重复提出问题的次数,以防万一。

          【讨论】:

          • 我会在我可以使用我的电脑后尽快查看您的答案,我现在正在打电话。但是,是的,可以在我的程序中使用函数,这些基本上是简单的问答代码。我只想知道如果发生我不想在程序中发生的事情,是否有任何方式程序不会从头开始。问题正如我所说,“继续”使程序从头开始。
          【解决方案10】:

          是的,除了通过循环之外,没有办法在执行后返回到代码的前一行。 根本不可能

          Python 和许多现代编程语言以这种方式工作,不支持“goto”行。

          因此,这样做的唯一方法是通过某种形式的多个 while 循环重复执行一个语句,直到收到您想要的结果(嵌套循环,或者通过将 while 循环拉出到函数中作为由 salparadise 建议)。

          【讨论】:

            【解决方案11】:

            您误解了使用 continue 的方式, continue 移动到循环的下一个迭代。要解决此问题,只需删除 continue

            根据评论进行编辑::

            我只使用while True 值,因为我对您的系统一无所知

            while True:
                while True:
                    questionx = input("....")
                    if x =="SomethingWrongabout questionX":
                        print ("Something went wrong.")
                        continue
                    else:
                        break;
            

            使用break 将帮助你实现你想要的

            【讨论】:

            • 但我希望程序在出错后删除记录。所以我需要“继续”?因为我保存了问题的记录,如果有任何问题有错误,我想回去再问一次。我不想保留它们,所以我需要使用“继续”之类的东西。
            • 例如,如果“y”问题有问题,我想回去只问“y”问题。但是那个“继续”使程序从乞​​求开始,程序再次询问“x”。
            • 听起来你想要的是循环中的另一个循环。我将编辑我的答案以反映我的意思
            • 好吧,如果没有办法在一个循环中做到这一点,那么是的,我需要更多的“while”循环
            • 我实际上会使用 1 个循环,直到一些答案没有给出这个,我会接受你的作为正确的。
            猜你喜欢
            • 1970-01-01
            • 2020-02-19
            • 2017-11-21
            • 2017-03-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-05-08
            相关资源
            最近更新 更多