普通函数和 generator 函数的区别
假如要创建一个返回奇数数列的函数,普通函数的做法如下:
def odd_numbers(n):
odd_num_list = []
for i in range(n):
if (i % 2) == 1:
odd_num_list.append(i)
return odd_num_list
for j in odd_numbers(10):
print(j)
输出
1
3
5
7
9
当将上述函数改成如下形式时,普通函数便变成了一个 generator 函数。这时候调用 odd_numbers(10)
不会执行 odd_numbers
函数,而是返回一个 generator 对象。
def odd_numbers(n):
for x in range(n):
if (x % 2) == 0:
yield x
num = odd_numbers(10)
print(num)
输出
<generator object odd_numbers at 0x7f9d77a095f0>
从 generator对象获取值
为了从 generator 对象获取值,我们可以使用 for loop
、next()
或者 list()
方法。
for loop
方法:
def odd_numbers(n):
for x in range(n):
if (x % 2) == 1:
yield x
num = odd_numbers(10)
for i in num:
print(i)
输出
1
3
5
7
9
next()
方法:
def odd_numbers(n):
for x in range(n):
if (x % 2) == 1:
yield x
num = odd_numbers(10)
print(next(num))
print(next(num))
print(next(num))
print(next(num))
print(next(num))
输出
1
3
5
7
9
list()
方法
def odd_numbers(n):
for x in range(n):
if (x % 2) == 1:
yield x
num = odd_numbers(10)
for i in list(num):
print(i)
输出
1
3
5
7
9
当我们使用 for loop
、next()
或者 list()
方法获取 generator 对象的值时,每次循环都会执行 odd_numbers()
函数的代码,执行到 yield x
时,就会返回一个值,下一次执行时,从 yield x
的下一条语句继续执行,函数的状态和上次中断执行前是一样的,于是继续执行,直到再次遇到 yield x
。
generator 对象只能被遍历一次
generator 对象只能被遍历一次,当我们再次使用时, generator 对象为空。
def odd_numbers(n):
for x in range(n):
if (x % 2) == 1:
yield x
num = odd_numbers(10)
for i in num:
print(i)
# 再次使用 num
print(list(num))
输出
1
3
5
7
9
[]
从输出中可以看出,当再次使用 num 时,num 变成了空。
yield vs. return
包含 yield 的函数在被调用时,返回一个 generator 对象给调用者,只有在遍历对象时,函数的代码才会被执行。而函数中的 return 返回一个值给调用者。
yield | return |
包含 yield 的函数在被调用时,返回一个 generator 对象给调用者,只有在遍历对象时,函数的代码才会被执行 | return 返回一个值给调用者 |
不占用内存 | 需要给返回的值分配内存 |
适合处理数据量比较大的情况 | 当处理的数据量较小时,比较方便 |
当数据量较大时,性能较好 | 当数据量较大时,由于需要使用大量内存,会拖慢性能。 |
总结
- 和 return 关键字返回一个值给调用者不同的是,包含 yield 关键字的函数返回一个 generator 对象给调用者。
- 返回的 generator 对象在使用过一次后便不再可以使用,如果要获取值,需要遍历 generator 对象。
- 可以使用
for loop
、next()
或者list()
方法从 generator 对象获取值。 - yield 和 return 的最大区别是,yield 返回一个 generator 给调用者,而 return 返回一个值给调用者。
- 使用 yield 时,不会将值存储在内存中,这在处理的数据量很大时,比较有优势。