普通函数和 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 loopnext() 或者 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 loopnext() 或者 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 loopnext() 或者 list() 方法从 generator 对象获取值。
  • yieldreturn 的最大区别是,yield 返回一个 generator 给调用者,而 return 返回一个值给调用者。
  • 使用 yield 时,不会将值存储在内存中,这在处理的数据量很大时,比较有优势。