yield语句来返回值,而不是return。
简单来说,生成器就像一个“按需分配”的机器。传统的函数一次性把所有结果算好,放到内存里,等你来拿。如果结果太大,内存就爆了。生成器不一样,它在你需要的时候才计算结果,然后给你,用完就扔,很省地方。
你可能要问了,这跟"函数"有啥区别?记住!生成器是一个包含了yield关键字的函数。
咱们看个例子:
defmy_generator(n):
for i inrange(n):
yield i
# 使用生成器
gen = my_generator(5)
print(next(gen)) # 输出 0
print(next(gen)) # 输出 1
print(next(gen)) # 输出 2
print(next(gen)) # 输出 3
print(next(gen)) # 输出 4
#print(next(gen)) # 抛出 StopIteration 异常
这段代码里,my_generator就是一个生成器函数。每次调用next(gen),它会执行到yield语句,返回一个值,然后暂停。下次调用next(gen),它会从上次暂停的地方继续执行。直到没有更多的值可以yield,就抛出一个StopIteration异常。
温馨提示:生成器只能用一次!用完了就没了,想再用,得重新创建一个。
除了生成器函数,Python还有一种更简洁的生成器写法:生成器表达式。它长得有点像列表推导式,但是用圆括号()括起来。
# 列表推导式
squares_list = [x*x for x inrange(5)]
print(squares_list) # 输出 [0, 1, 4, 9, 16]
# 生成器表达式
squares_generator = (x*x for x inrange(5))
print(squares_generator) # 输出 <generator object <genexpr> at 0x...>
print(next(squares_generator)) # 输出 0
print(next(squares_generator)) # 输出 1
生成器表达式和列表推导式的区别在于:列表推导式会立即计算所有元素,生成一个列表;而生成器表达式只创建一个生成器对象,只有在你需要的时候才会计算。
生成器最大的优势就是节省内存。想象一下,你要处理一个10GB的文件,如果一次性把所有数据读到内存里,电脑可能直接卡死。但如果用生成器,每次只读取一小部分数据,处理完再读取下一部分,内存占用就非常小。
举个例子,计算一个超大文件中所有数字的和:
defprocess_large_file(filename):
withopen(filename, 'r') as f:
for line in f:
try:
num = int(line.strip()) # 去掉首尾空格,并转换为整数
yield num
except ValueError:
# 处理非数字的行,比如忽略掉
pass
# 创建一个包含大量数据的虚拟文件
withopen("large_file.txt", "w") as f:
for i inrange(100000):
f.write(str(i) + "\n")
# 使用生成器处理文件
numbers = process_large_file("large_file.txt")
total = sum(numbers) # sum() 函数可以接受生成器作为参数
print(f"The sum is: {total}")
在这个例子里,process_large_file函数就是一个生成器。它逐行读取文件,把每一行转换成数字,然后yield出去。sum()函数可以接受一个生成器作为参数,它会遍历生成器,把所有数字加起来。
如果没有生成器,你得先把所有数字读到内存里,放到一个列表里,然后再计算总和。文件很大的话,内存肯定吃不消。
生成器在很多场景下都很有用:
一个生成斐波那契数列的例子:
deffibonacci_generator():
a, b = 0, 1
whileTrue: # 无限生成
yield a
a, b = b, a + b
# 使用生成器
fib = fibonacci_generator()
for i inrange(10):
print(next(fib)) # 输出前10个斐波那契数
温馨提示:由于上面的while循环是死循环,因此不加break语句会一直执行下去,或者你可以使用迭代器,显示声明循环次数。
生成器其实就是一种特殊的迭代器。迭代器是一个实现了__iter__()和__next__()方法的对象。__iter__()方法返回迭代器自身,__next__()方法返回下一个值,如果没有更多值,就抛出StopIteration异常。
生成器自动实现了这两个方法,所以你可以像使用迭代器一样使用生成器。
# 迭代器
classMyIterator:
def__init__(self, data):
self.data = data
self.index = 0
def__iter__(self):
return self
def__next__(self):
if self.index >= len(self.data):
raise StopIteration
value = self.data[self.index]
self.index += 1
return value
# 使用迭代器
my_list = [1, 2, 3]
my_iterator = MyIterator(my_list)
for item in my_iterator:
print(item)
这段代码手动实现了一个迭代器。可以看到,要写很多代码才能实现迭代器的功能。而生成器只需要一个yield语句就搞定了,简洁明了。
生成器是个好东西,特别是在处理大数据的时候。它可以帮你节省内存,提高程序效率。记住它的几个要点:
yield语句返回值。()括起来。这次就先聊到这,下次有机会再深入研究生成器的更多用法。