Python迭代器没有按预期工作

Posted

技术标签:

【中文标题】Python迭代器没有按预期工作【英文标题】:Python iterator not working as anticipated 【发布时间】:2015-07-10 20:50:44 【问题描述】:

我有以下代码,我正在尝试比较一些值并返回最高值:

def high(it):
    it = iter(it)
    returnlist = []
    try:
        while True:
            one = next(it)
            two = next(it) 
            three = next(it)
            if three <= two and one <= two:
                returnlist.append(two)
    except StopIteration:
        pass
    return returnlist

它工作了一半,但不正常:

>>high([0,1,-1,3,8,4,3,5,4,3,8]) #(this works)
[1, 8, 5] 
>>high([5,2,4,9,6,1,3,8,0,7]) #(this doesn't work, should return [9,8]
[8] 
>>high(int(is_prime(p)) for p in irange(1,20)) #(doesn't work, returns four 1's - should return 5)
[1, 1, 1, 1]

【问题讨论】:

second 未在您的行中定义return list.append(second) 我假设它是two 而不是second 单步执行你脑海中的代码,你会发现问题所在。 请记住,在循环的每次迭代中,迭代器都会前进三个位置。所以你只是在比较元素 [0,1,2]、[3,4,5]、[6,7,8] 等,而不是每组相邻的 3。 您正在做的事情可以由for one, two, three in zip(it, it, it): 完成。如果您需要遍历窗口而不是块,则需要以某种方式跟踪最后两个值,无论是显式还是其他方式。 (itertools 文档中的 pairwise 配方演示了一种简单的隐式方法。) @heinst 你是对的!改了,谢谢:-) 【参考方案1】:

您的代码正在做的是将“列表”分成 3 个块,并且只比较每个块中的数字。这意味着您仅将前 3 个数字相互比较,然后将接下来的 3 个数字相互比较。您要做的是使用滑动窗口,以便将每个数字与旁边的数字进行比较。您可以通过跟踪以前的值来做到这一点:

def high(lst):
    returnlist = []
    one = None
    two = None
    for three in lst:
        # If one is None here we haven't
        # reached the first set of numbers yet
        if not one is None: 
            if three <= two and one <= two:
                returnlist.append(two)
        # Update the sliding window
        one = two
        two = three
    return returnlist

【讨论】:

这绝对是我更喜欢的方法,但是,它会导致这个实例出现问题:high(int(is_prime(p)) for p in irange(1,20)) 应该等于 [1, 1, 1, 1, 1, 1] 但返回 [1, 1, 1, 1, 0, 1, 1, 0, 1, 1] - 你认为这可能是因为一些相等错误? 我不知道你从哪里得到 is_primeirange 方法,所以我无法为你测试这个案例 @unicornication 请参阅我回答的最后一段。我认为您的期望不正确,Raniz 解决方案正确返回 @Raniz 这是一个很好的解决方案,但是如果列表中可以有 None 值,则会出现错误。最好使用next before 循环初始化前三个值。【参考方案2】:

@Shashank 评论是正确的,因为您假设迭代器是独立的,而实际上它们不是。您可以使用tee 修补您的功能:

from itertools import tee

def high(it):

    it1,it2,it3 = tee(iter(it), 3)
    next(it2, None)
    next(it3, None); next(it3, None)

    returnlist = []
    try:
        while True:
            one = next(it1)
            two = next(it2) 
            three = next(it3)
            if three <= two and one <= two:
                returnlist.append(two)
    except StopIteration:
        pass
    return returnlist

我认为实现相同想法的更 Pythonic 方式:

from itertools import tee, izip

def threesome(iterable):
    "s -> (s0,s1,s2), (s1,s2,s3), (s2,s3,s4), ..."
    a, b, c = tee(iterable, 3)
    next(b, None)
    next(c, None); next(c, None)
    return izip(a, b, c)

def high(it):
    return [x2 for x1, x2, x3 in threesome(it) if x2 == max(x1, x2, x3)]

顺便说一句,我认为您对最后一种情况的预期输出不正确。您还应该在输出中看到零,因为只要您连续有三个合数(例如 8、9、10 将满足您的条件),就会发生这种情况。

【讨论】:

以上是关于Python迭代器没有按预期工作的主要内容,如果未能解决你的问题,请参考以下文章

返回 C++ 迭代器引用

python-迭代器协议和for循环工作机制

《Python学习之路 -- Python基础之迭代器及for循环工作原理》

Python 迭代器

25Python之迭代器

具有比较器的优先级队列未按预期工作