面试题答案
一键面试生成器和迭代器的工作原理
- 迭代器:
- 迭代器是一个实现了
__iter__()
和__next__()
方法的对象。__iter__()
方法返回迭代器对象本身,__next__()
方法返回下一个元素。当没有更多元素可返回时,__next__()
抛出StopIteration
异常。 - 例如,当对列表使用
iter()
函数时,会返回一个列表迭代器对象,通过不断调用next()
函数可以逐个获取列表中的元素。
my_list = [1, 2, 3] my_iterator = iter(my_list) print(next(my_iterator)) # 输出 1 print(next(my_iterator)) # 输出 2 print(next(my_iterator)) # 输出 3 print(next(my_iterator)) # 抛出 StopIteration 异常
- 迭代器是一个实现了
- 生成器:
- 生成器是一种特殊的迭代器,它的创建更加简洁。生成器可以通过生成器函数或生成器表达式来创建。
- 生成器函数使用
yield
语句来暂停函数的执行并返回一个值。当再次调用next()
时,函数从暂停的地方继续执行,直到再次遇到yield
或函数结束。 - 例如:
def my_generator(): for i in range(3): yield i my_gen = my_generator() print(next(my_gen)) # 输出 0 print(next(my_gen)) # 输出 1 print(next(my_gen)) # 输出 2 print(next(my_gen)) # 抛出 StopIteration 异常
- 生成器表达式的语法类似于列表推导式,但使用圆括号而不是方括号。例如:
my_gen_exp = (i for i in range(3))
。
减少内存占用的方式
- 迭代器:
- 迭代器不需要一次性将所有数据加载到内存中。例如,对于一个非常大的文件,使用
open()
函数返回的文件对象本身就是一个迭代器。每次读取一行数据(通过next()
或在for
循环中隐式调用next()
),只有当前行的数据在内存中,而不是整个文件内容,从而大大减少了内存占用。
- 迭代器不需要一次性将所有数据加载到内存中。例如,对于一个非常大的文件,使用
- 生成器:
- 生成器同样按需生成数据。生成器函数在每次
yield
时暂停,只在调用next()
时才计算并返回下一个值。生成器表达式也是在需要时才生成值,而不是像列表推导式那样一次性生成整个列表。例如,要生成一个包含1000万个整数的序列,如果使用列表存储会占用大量内存:
# 列表占用大量内存 big_list = [i for i in range(10000000)]
- 而使用生成器表达式,只有在实际使用这些值时才会生成,几乎不占用额外内存:
big_generator = (i for i in range(10000000))
- 生成器同样按需生成数据。生成器函数在每次
实际应用场景中优化内存使用的例子
- 处理大文件:
- 假设要处理一个非常大的文本文件,统计其中单词出现的次数。如果一次性读取整个文件到内存,可能会导致内存不足。
word_count = {} with open('large_file.txt', 'r') as file: for line in file: # file 是迭代器,逐行读取,减少内存占用 words = line.split() for word in words: if word not in word_count: word_count[word] = 1 else: word_count[word] += 1
- 生成大量数据序列:
- 例如生成斐波那契数列,使用生成器可以在不占用大量内存的情况下生成无限的斐波那契数。
def fibonacci_generator(): a, b = 0, 1 while True: yield a a, b = b, a + b fib_gen = fibonacci_generator() for _ in range(10): # 生成前10个斐波那契数 print(next(fib_gen))
- 在这个例子中,生成器按需生成斐波那契数,而不是一次性生成所有数并存储在内存中。