【问题标题】:Testing for an empty iterator in a Python for... loop在 Python for... 循环中测试空迭代器
【发布时间】:2010-12-02 07:03:09
【问题描述】:

以下代码基于this recipe。然而,秘诀的关键点——如果迭代器为空,它提供了一种在迭代器上中断迭代的方法——似乎在这里不起作用,而是以以下不受欢迎的方式表现:

  1. 如果 get_yes_no_answer() == False 并且迭代器中还有两个或更多项目,则跳过 next_choice,而不是在下一次迭代中被选中。
  2. 如果 get_yes_no_answer() == False 并且迭代器中剩余的项目少于两个,则 my_func() 返回 None。

我如何确保:

  • 如果 get_yes_no_answer() == False 并且迭代器中还有两个或更多项目,next_choice不会跳过?李>
  • 如果 get_yes_no_answer() == False 并且迭代器中还剩下一项,my_func() 打印它并调用 get_yes_no_answer()?
  • 如果 get_yes_no_answer() == False 并且迭代器中没有剩余项目,是否会触发 except StopIteration 子句?

代码如下:

def my_func(choice_pattern, input):
# Search in input for some things to choose from.
choice_iterator = choice_pattern.finditer(input, re.M)
if not choice_iterator:
    print "No choices. Exiting..."
    sys.exit()
else:
    # Show choices to the user. For each one, ask user for a yes/no response. If
    # choice accepted, return a result. Otherwise show user next choice. If no
    # choices accepted by user, quit.
    for choice in choice_iterator:
        print choice.group()
        # get_yes_no_answer() returns True or False depending on user response.
        if get_yes_no_answer():
            return choice
        else:
            # Check if iterator is empty. If so, quit; if not, do something else.
            try:
                next_choice = choice_iterator.next()
            except StopIteration:
                print "No matches. Exiting..."
                sys.exit()
            else:
                choice_iterator = itertools.chain([next_choice], choice_iterator)

【问题讨论】:

    标签: python exception iterator conditional


    【解决方案1】:

    你为什么要这样做呢?为什么不只是:

    def get_choice(pattern, inpt):
        choices = pattern.finditer(inpt, re.M)
        if not choices:
            sys.exit('No choices')
        for choice in choices:
            print(choice.group(0))
            if get_yes_no_answer():
                return choice
        sys.exit('No matches')
    

    我不知道你输入的长度是多少,但我怀疑这是否值得。

    【讨论】:

    • 我使用 finditer() 而不是 findall() 因为我需要知道比赛的开始和结束位置。如果我没记错的话,我将无法对 findall() 的结果调用 start() 和 end() 方法,因为它们不是 MatchObjects。
    • 那么,为什么您的问题中没有这些信息?请添加它。当您print choice 时,这是非常具有误导性的,因为用户不希望看到这样的内容:<_sre.SRE_Match object at 0x0104AFA8>
    • 对此表示歉意。当我粘贴到代码块中时,我实际上在
      printchoice
      之后有一个
      .group()
      并且我在尝试编辑代码的过程中显然意外删除了它在撰写我的问题时,为了清楚起见。我现在把它放回去了,我对你的评论表示感谢。
    【解决方案2】:

    您不需要检查迭代器是否为空。 for 循环将为您执行此操作,并在迭代器为空时停止。就这么简单。

    此外,您不需要在 sys.exit() 或 return 之后使用 else

    完成后,您的代码如下所示:

    def my_func(choice_pattern, input):
        # Search in input for some things to choose from.
        choice_iterator = choice_pattern.finditer(input, re.M)
        if not choice_iterator:
            print "No choices. Exiting..."
            sys.exit()
    
        # Show choices to the user. For each one, ask user for a yes/no response. If
        # choice accepted, return a result. Otherwise show user next choice. If no
        # choices accepted by user, quit.
        for choice in choice_iterator:
            print choice
            # get_yes_no_answer() returns True or False depending on user response.
            if get_yes_no_answer():
                return choice
        # Loop exited without matches.
        print "No matches. Exiting..."
        sys.exit()
    

    就是这样!

    发生的事情是你在循环中,得到下一个项目。结果是您实际上只显示第二个答案。

    其实你可以更简化:

    def my_func(choice_pattern, input):
        choice_iterator = choice_pattern.finditer(input, re.M)
        if choice_iterator:
            for choice in choice_iterator:
                print choice
                if get_yes_no_answer():
                    return choice
        # If there is no choices or no matches, you end up here:
        print "No matches. Exiting..."
        sys.exit()
    

    迭代器的使用几乎与任何序列类型一样。您无需将其与列表区别对待。

    【讨论】:

    • 宾果游戏!我很接近,但没有雪茄。谢谢你:)
    • 你提出的第二种方案,我不太喜欢。区分没有匹配项和没有选择是很重要的。不过还是谢谢。
    【解决方案3】:

    查看来自this question 的成对迭代器。然后您可以像这样检查最后一项:

    MISSING = object()
    for choice, next_choice in pairwise(chain(choice_iterator, [MISSING])):
        print(choice.group())
        if get_yes_no_answer():
            return choice.group()
        if next_choice is MISSING:
            print("No matches. Exiting...")
            sys.exit()
    

    在您展示的示例中,这似乎没有必要。您不需要检查 finditer 是否返回了迭代器,因为它总是这样。如果你没有找到你想要的,你可以直接通过 for 循环:

    def my_func(choice_pattern, input):
        """Search in input for some things to choose from."""
        for choice in choice_pattern.finditer(input, re.M):
            print(choice.group())
            if get_yes_no_answer():
                return choice.group()
        else:
            print("No choices. Exiting...")
            sys.exit()
    

    【讨论】:

    • else 如果迭代器为空或在for 循环中没有返回任何内容,则将到达。
    • 我使用 else 纯粹是为了可读性,但作为额外的奖励,您可以在返回之前在同一个函数中进行选择并做一些事情。
    【解决方案4】:

    为什么这么复杂的方法?

    我认为这应该做的差不多:

    def my_func(pattern, data):
      choices = pattern.findall(data, re.M)
      while(len(choices)>1):
        choice = choices.pop(0)
        if get_yes_no_answer():
          return choice
        else:
          choices.pop(0)
      else:
        return None
    

    【讨论】:

    猜你喜欢
    • 2021-10-08
    • 2018-11-21
    • 2012-06-27
    • 2023-03-05
    • 1970-01-01
    • 2013-03-09
    • 2016-01-18
    • 2015-06-06
    • 2016-08-03
    相关资源
    最近更新 更多