使用生成器的`for`循环,如果耗尽则返回不同的值

时间:2018-12-02 05:31:09

标签: python python-3.x for-loop generator

我正在遍历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

从Python的角度来看,有一种方法可以管理生成器上的循环,该生成器在找到项目时停止并返回,而仅当生成器为精疲力尽。

3 个答案:

答案 0 :(得分:5)

Python方式是使用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']

编辑

我的最初答复是如何避免使用found标志,而我并没有注意您实际上试图做什么。 for-else构造也是我要强调的非常有用且经常被忽略的语言构造。

但是,如果您只是要检查字符串集是否是另一个字符串的排列,那么为什么不

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

这避免了对字符串中所有单词进行所有可能排列的需要。

答案 1 :(得分:1)

有两种方法。一种是测试迭代器的成员资格:

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

关于迭代器中成员资格的说明。据官方documentation称,

  

对于未定义包含()但可以定义的用户定义的类   定义 iter (),如果x == z的某个值z为   在y上迭代时产生。如果在   迭代,就好像引发了该异常。

由于permutations具有__iter__方法,因此可以测试成员资格。

例如,

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

答案 2 :(得分:1)

  

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