迭代器的成员测试

3

请有人为我解释一下下面代码中最后3行的成员测试的行为,为什么是False?为什么迭代器和可迭代对象的成员测试不同?

c = [1,2,3,4,5,6,7,8,9,10,11,12]

print(3 in c)   # True
print(3 in c)   # True

d = iter(c)
print(2 in d)   # True
print(4 in d)   # True
print(4 in d)   # False  ???
print(6 in d)   # False  ???
print(10 in d)   # False  ???

5
第二次尝试查找数字4后,迭代器为空,因为它已经遍历了所有的值以寻找数字4。尝试在查找后将其转换为列表,看看会得到什么结果,你应该会得到[] - user3483203
3个回答

4

迭代器在使用时被消耗掉。我将以您的例子进行解释:

>>> c = [1,2,3,4,5,6,7,8,9,10,11,12]
>>> d = iter(c)
>>> print(list(d))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
>>> print(list(d))
[]

您可以将迭代器d看作是指向列表中第一个项目的指针。一旦读取其值,它就会指向第二个项目。当它到达末尾时,它指向空列表。
另请参见此内容:
>>> c = [1,2,3,4,5,6,7,8,9,10,11,12]
>>> d = iter(c)
>>> print(next(d))
1
>>> print(next(d))
2
>>> print(list(d))
[3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

检查某物是否在其中也会消耗其内容:

>>> c = [1,2,3,4,5,6,7,8,9,10,11,12]
>>> d = iter(c)
>>> 4 in d
True
>>> print(list(d))
[5, 6, 7, 8, 9, 10, 11, 12]

1
因此,在查找 4 时,它也调用 __next__,这会消耗迭代器。可以理解,但是对于成员资格测试和 in 操作符,是否应该以不消耗迭代器的方式实现,只是在其中搜索值呢? - Marko Savic
1
@MarkoSavic 迭代器只实现了一件事情:next。除此之外,没有其他方式可以获取任何东西。这里的问题是你是否应该使用迭代器... - zvone

0

这个问题看起来有些陈旧,但最近我正在使用自己的迭代器类进行一些实验,这让我对成员检查的内部工作原理有了一些见解。以下是说明正在发生的事情的代码。希望它有所帮助。

import logging
logging.basicConfig(format='%(asctime)s|%(levelname)s: %(message)s',
                    datefmt='%H:%M:%S, %d-%b-%Y', level=logging.INFO)
class Iterator:
    """Customise the Iterator with logging messages."""
    def __init__(self, max_value:int=20):
        """Initialise with a default state and a maximum value"""
        self.max_value:int=max_value
        self.value:int=-1
    def __iter__(self):
        """Part of the usual iterator protocol."""
        return self
    def __next__(self)->int:
        """Part of the usual iterator protocol."""
        logging.info(msg=f'Next was called with {self.value}')
        self.value+=1
        if self.value>self.max_value:raise StopIteration
        return self.value
sample_iterator=Iterator()        
3 in sample_iterator # Gives True
3 in sample_iterator # Gives False

第一次检查的日志输出

11:12:19, 19-May-2023|INFO: Next was called with -1
11:12:19, 19-May-2023|INFO: Next was called with 0
11:12:19, 19-May-2023|INFO: Next was called with 1
11:12:19, 19-May-2023|INFO: Next was called with 2

并输出下一个检查的结果

11:12:31, 19-May-2023|INFO: Next was called with 3
11:12:31, 19-May-2023|INFO: Next was called with 4
11:12:31, 19-May-2023|INFO: Next was called with 5
11:12:31, 19-May-2023|INFO: Next was called with 6
11:12:31, 19-May-2023|INFO: Next was called with 7
11:12:31, 19-May-2023|INFO: Next was called with 8
11:12:31, 19-May-2023|INFO: Next was called with 9
11:12:31, 19-May-2023|INFO: Next was called with 10
11:12:31, 19-May-2023|INFO: Next was called with 11
11:12:31, 19-May-2023|INFO: Next was called with 12
11:12:31, 19-May-2023|INFO: Next was called with 13
11:12:31, 19-May-2023|INFO: Next was called with 14
11:12:31, 19-May-2023|INFO: Next was called with 15
11:12:31, 19-May-2023|INFO: Next was called with 16
11:12:31, 19-May-2023|INFO: Next was called with 17
11:12:31, 19-May-2023|INFO: Next was called with 18
11:12:31, 19-May-2023|INFO: Next was called with 19
11:12:31, 19-May-2023|INFO: Next was called with 20

这是我的结论。当您请求对元素进行成员身份检查时,迭代器会从其当前状态开始计算next,直到它获得:

  • 正在搜索的元素,当它返回True
  • StopIteration,当它返回False
  • 继续一直到时间结束,直到发生上述情况之一

更具体地说,

val:bool=elem in sample_iterator

等同于

val:bool
while True:
    try:
        current=next(sample_iterator)
    except StopIteration:
        val=False
        break
    if elem==current:
        val=True
        break

总之,成员测试是对迭代器进行的一种侵入式测试。不要在迭代器或任何你知道检查本身会改变内部状态的类上执行此测试。


-1

因为迭代器具有状态 - 在您的情况下是指向当前元素的指针。

两个连续检查之间的区别

print(4 in d)   # True
print(4 in d)   # False  ???

是迭代器的状态。在第一次检查后,迭代器的指针设置为4后面的下一个元素。


这怎么算是一个答案? - DYZ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接