Skip to main content
 首页 » 编程设计

Python:检查生成器中是否存在对象

2025年05月04日15wuhuacong

我有一个 bug,它是由检查“if x in generator”的变化结果引起的

def primes(upper_limit): 
    for n in range(2, upper_limit): 
        if all(n % i > 0 for i in range(2, n)): 
            yield n 
 
first_hundred_primes = primes(100) 
 
print(5 in first_hundred_primes) 
print(5 in first_hundred_primes) 
print(5 in first_hundred_primes) 
print(5 in first_hundred_primes) 
print(5 in first_hundred_primes) 

这给出了输出:

True 
False 
False 
False 
False 

我假设它不是为了检查生成器中是否存在对象,但如果是这种情况,为什么它不抛出一些错误,为什么它会起作用?

>>> hundred_generator = range(1,100) 
>>> 50 in hundred_generator 
True 
>>> 50 in hundred_generator 
True 
>>> 50 in hundred_generator 
True 

我通常在检查生成器中是否存在某些对象(以加快检查速度)之前将生成器变成一个集合,这解决了问题,但我非常想知道这里发生了什么?

请您参考如下方法:

当您迭代生成器的元素时,您消耗它们。

试试这个:

len(list(first_hundred_primes)) > 0 
=> True 
len(list(first_hundred_primes)) > 0 
=> False 

即第一次使用 in(遍历它们)或 at 列出最多 5 个元素时,您已经完成了对元素的消耗,因此生成器不会在此之后再次生成 5。第二次后,它不再生成任何东西。

您的选择:

  1. 在使用之前将生成器转换为列表(或集合):first_hundred_primes = list(first_hundred_primes)
  2. 每次创建一个新的生成器:5 in primes(100); 5 个素数 (100); ...
  3. 使用itertools.tee

编辑:

关于您关于 range 的问题:range 不是生成器。

在 python2 中,它只是返回一个列表。没问题。

在 python3 中,它返回一个看起来像集合的特殊对象。它不必实际存储范围内的所有数字,它只是根据定义范围的规则实现列表操作。例如。 len 实现为 stop-start。由于它代表一个集合,而不是一个生成器,您可以多次迭代它,而无需“消耗”元素。