цикл `for` с использованием генераторов, возвращает другое значение, если исчерпан - PullRequest
0 голосов
/ 02 декабря 2018

Я зацикливаюсь на объекте itertools.permutation, и для эффективности цикл обрывается, когда элемент найден.Я понимаю, что, поскольку я использовал цикл for, я не могу отловить ошибку StopIteration, когда генератор исчерпан, а элемент не найден.

До сих пор я установил флаг found,но это кажется довольно хакерским.

from itertools import permutations

def verify_string(name, compare):
    name = name.lower().split()
    compare = compare.lower().split()
    p = permutations(compare, len(compare))
    found = 0

    for i in p:
        if list(i) == name:
            print(f'Found: {compare} in {name}')
            found = 1
            break

    if not found:
        print('Not Found')

name = 'guido van rossum'
compare = 'van guido rossum'

verify_string(name, compare)
>>Found: ['van', 'guido', 'rossum'] in ['guido', 'van', 'rossum']

Я также подумал о проверке if not next(p, ''), чтобы увидеть, исчерпан ли он, но этот предмет может быть найден в последнем элементе генератора и в любом случае вернет True.

С точки зрения Pythonic, есть ли способ управлять циклом над генератором, который останавливается и возвращается, когда элемент найден и возвращает другое значение только тогда, когда генератористощены.

Ответы [ 3 ]

0 голосов
/ 02 декабря 2018

Pythonic - это использование петли for-else.

from itertools import permutations

def verify_string(name, compare):
    name = name.lower().split()
    compare = compare.lower().split()    
    for i in permutations(compare, len(compare)):
        if list(i) == name:
            print(f'Found: {compare} in {name}')
            break
    else:  # Raymond Hettinger calls this "if no break" condition
        # If we did not break out of the "for loop", execute this.
        print('Not Found')

name = 'guido van rossum'
compare = 'van guido rossum'

verify_string(name, compare)
>>> Found: ['van', 'guido', 'rossum'] in ['guido', 'van', 'rossum']

Edit

Мой первый ответ был о том, как избежать использования флага found, а я не былобращая внимание на то, что вы на самом деле пытались сделать.Конструкция for-else также является очень полезной и часто игнорируемой языковой конструкцией, которую я хотел выделить.

Однако, если вы просто хотите проверить, является ли набор строк перестановкойдругой, то почему бы просто не

match = sorted(name.lower().split()) == sorted(compare.lower().split())

Это избавляет от необходимости проходить все возможные перестановки слов в строке.

0 голосов
/ 02 декабря 2018

С точки зрения Pythonic, существует ли способ управления циклированием по генератору, который останавливается и возвращается, когда элемент найден, и возвращает другое значение только тогда, когда генератор исчерпан.

Поскольку вы на самом деле ничего не возвращаете - если вы исправите эту часть своей функции, вы вернете None, если совпадений нет:

from itertools import permutations

def verify_string(name, compare):
    name = name.lower().split()
    compare = compare.lower().split()    
    for i in permutations(compare, len(compare)):
        if list(i) == name:
            return True

name = 'guido van rossum'
compare = 'van guido rossum'

if verify_string(name, compare):
  print(f'Found: {compare} in {name}')
else:
  print('Not found')
0 голосов
/ 02 декабря 2018

Есть два подхода.Одним из них является проверка членства в итераторе:

from itertools import permutations
from collections import Counter


def verify_string(name, compare):
    if tuple(compare.lower().split()) in permutations(name.lower().split()):
        print(f"Found: {compare} in {name}")
    else:
        print("No match found")

Мы также можем полностью избежать функции с помощью троичного оператора:

val if val in itr else other_val

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

def verify_string_fast(name, compare):
    if not Counter(compare.lower.split()) - Counter(name.lower.split()):
        print(f"Found: {compare} in {name}")
    else:
        print("No match found")

В целом, если у нас есть условие теста, отличное от членства, мы можем выполнить:

def verify_general(val, itr):
    if any(compare(val, x) for x in itr):
        print("Success")
    else:
        print("Failure")

Примечание о членстве в итераторе.Согласно официальной документации ,

Для пользовательских классов, которые не определяют , содержат (), но определяют iter (), x в y - это True, если какое-то значение z с x == z получается при итерации по y.Если во время итерации возникает исключение, оно как если бы вызывало это исключение.

Поскольку permutations имеет метод __iter__, тестирование на членство возможно.

Например,

assert (3, 2, 1) in permutations([1, 2, 3])
...