2009-09-29 2 views
1

Код на основании ниже основан на this recipe. Однако ключевой момент рецепта - то, что он дает возможность вырваться из итерации на итераторе, если итератор пуст, не работает здесь, вместо этого ведет себя следующим образом:Тестирование пустого итератора в Python for ... loop

  1. Если get_yes_no_answer() == False и есть два или более элементов, оставшихся в итераторе, next_choice пропускается, а не выбирается на следующей итерации.
  2. Если get_yes_no_answer() == Ложные и есть менее двух элементов, оставшихся в итератора, my_func() возвращает None.

Как я могу гарантировать, что:

  • If get_yes_no_answer() == Ложные и есть два или более элементы остались в итераторе, next_choice является не пропустила?
  • Если get_yes_no_answer() == Ложные и есть один элемент остается в итератора, my_func() печатает его и называет get_yes_no_answer()?
  • Если get_yes_no_answer() == Ложных и там не остались в итераторе пунктов, то кроме 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) 

ответ

1

Вам не нужно, чтобы проверить, если итератор пуст. Цикл for сделает это для вас и остановится, когда итератор пуст. Это так просто.

Кроме того, после sys.exit() или возврата вам не нужен 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() 

итераторы используются в значительной степени, как и любой тип последовательности. Вам не нужно относиться к нему иначе, чем к списку.

+0

Бинго! Я был рядом, но не сигары. Спасибо :) – sampablokuper

+0

Второе решение, которое вы предложили, мне не очень нравится. Важно различать отсутствие совпадений и отсутствие выбора. Но спасибо. – sampablokuper

3

почему вы делаете это так вообще? почему не просто:

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

Я не знаю, какова ваша длина ввода, но я сомневаюсь, что это стоит того.

+0

Я использую finditer() вместо findall(), потому что мне нужно знать начальное и конечное местоположения совпадения. Если я не ошибаюсь, мне не удастся вызвать методы start() и end() по результатам findall(), потому что они не являются MatchObjects. – sampablokuper

+1

так, почему этой информации нет в вашем вопросе? добавьте его. Когда вы печатаете выбор, это очень вводит в заблуждение, так как пользователь не ожидает увидеть что-то вроде этого: '<_sre.SRE_Match object at 0x0104AFA8>'. – SilentGhost

+0

Извинения за это. У меня на самом деле был

.group()
после
print choice
, когда я вставлял в блок кода, и я ясно удалил его случайно, пытаясь в течение некоторого времени отредактировать код, составляя мой вопрос. Я вернул его обратно, и я проголосовал за ваш комментарий в благодарность. – sampablokuper

0

См. Попарный итератор от 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() 
+0

'else' будет достигнут, если либо итератор пуст, либо ничего не было возвращено из цикла' for'. – SilentGhost

+0

Я использовал еще чисто для удобочитаемости, но в качестве дополнительного бонуса вы можете вырваться и сделать что-то с выбором в пределах одной функции перед возвратом. –

0

Почему такой сложный подход?

Я думаю, что это нужно сделать почти то же самое:

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 
+0

См. Мой ответ на ответ SilentGhost: http://stackoverflow.com/questions/1491957/testing-for-an-empty-iterator-in-a-python-for-loop/1492039#1492039 – sampablokuper