# 1.生成器Generator
在2001创建的PEP255 (opens new window)中引入的,生成器函数是返回懒加载迭代器的函数,使用上像list
,但与list
不同的时,生成器中保存的不是元素的值而是生成元素的逻辑关系,所以使用生成器能够节省内存。
使用生成器的几个例子:
- 读取大文件:
csv_gen = csv_reader("some_csv.txt")
row_count = 0
for row in csv_gen:
row_count += 1
print(f"Row count is {row_count}")
通过普通的方式定义csv_reader
函数时会因为读取的文件过大导致内存耗尽:
def csv_reader(file_name):
file = open(file_name)
result = file.read().split("\n")
return result
因为要把csv
文件的每行都读取到result
中,内存不够时将会导致报错:
Traceback (most recent call last):
File "ex1_naive.py", line 22, in <module>
main()
File "ex1_naive.py", line 13, in main
csv_gen = csv_reader("file.txt")
File "ex1_naive.py", line 6, in csv_reader
result = file.read().split("\n")
MemoryError
通过使用生成器,能够避免这种问题,定义csv_reader
函数如下:
def csv_reader(file_name):
for row in open(file_name, "r"):
yield row
生成器中会保留读取的文件句柄,不会重复的open
文件,又不会加载文件中的内容,因此需要的内存也比较少。
- 生成一个无穷序列
def infinite_sequence():
num = 0
while True:
yield num
num += 1
gen = infinite_sequence()
for i in range(3):
print(next(gen))
# 0
# 1
# 2
- 生成器表达式
通过圆括号表达式生成生成器。
import sys
nums_squared_lc = [num**2 for num in range(100)]
print(sys.getsizeof(nums_squared_lc))
# 920
nums_squared_gc = (num**2 for num in range(100))
print(sys.getsizeof(nums_squared_gc))
# 112
可以看到生成器所需要的内存比普通迭代器容器需要的少的多。
# 2.yield
语句
yield
语句用于生成器函数中,其功能有些类似于普通函数中的return
语句,主要用来控制生成器函数的逻辑流。
使用yield
语句的函数返回的是一个生成器,当每次使用next
的函数去获取生成器中的值时,每次生成器函数都会执行到yield
的语句,只到再次迭代或next
生成器。
当执行到yield
语句时,函数返回yield
的值,并将函数的状态挂起,会将生成器函数相关的任何局部变量保存起来,包括指针,函数的栈,和异常,如前面读取文件的上下文句柄。