之(二) 生成器

一. 可迭代对象(Iterable) 和迭代器(Iterator)

Python中任意的对象, 只要它定义了可以返回一个迭代器的iter方法, 或者定义了可以支持下标索引的getitem方法, 那么它就是一个可迭代对象. 简单说, 可迭代对象就是能提供迭代器的任意对象. 那迭代器又是什么呢?

任意对象, 只要定义了next(Python2) 或者next方法, 它就是一个迭代器. 就这么简单.

现在我们来理解迭代(iteration). 当我们使用一个循环来遍历某个东西时, 这个过程本身就叫迭代.

二. 生成器(Generators)

1. 生成器基本概念

生成器也是一种迭代器, 但是你只能对其迭代一次. 这是因为它们并没有把所有的值存在内存中, 而是在运行时生成值. 你通过遍历来使用它们, 要么用一个“for”循环, 要么将它们传递给任意可以进行迭代的函数和结构. 大多数时候生成器是以函数来实现的. 然而, 它们并不返回一个值, 而是yield一个值. 这里有个生成器函数的简单例子:

def generator_function():
    for i in range(10):
        yield i

for item in generator_function():
    print(item)

输出:

0
1
2
3
4
5
6
7
8
9

这个案例并不是非常实用. 生成器最佳应用场景是:你不想同一时间将所有计算出来的大量结果集分配到内存当中, 特别是结果集里还包含循环. 这样做会消耗大量资源. 许多Python 2里的标准库函数都会返回列表, 而Python 3都修改成了返回生成器, 因为生成器占用更少的资源.

下面是一个计算斐波那契数列的生成器:

# generator version
def fibon(n):
    a = b = 1
    for i in range(n):
        yield a
        a, b = b, a + b

for x in fibon(100):
    print(x)

用这种方式, 我们可以不用担心它会使用大量资源. 然而, 之前如果我们这样来实现的话:

def fibon(n):
    a = b = 1
    result = []
    for i in range(n):
        result.append(a)
        a, b = b, a + b
    return result

2. 理解生成器的”生成”

在测试前你需要再知道一个Python内置函数:next(). 它允许我们获取一个序列的下一个元素. 那我们来验证下我们的理解:

def generator_function():
    for i in range(3):
        yield i

gen = generator_function()
print(next(gen))  # Output: 0
print(next(gen))  # Output: 1
print(next(gen))  # Output: 2
print(next(gen))  # Error

# Output: Traceback (most recent call last):
#            File "<stdin>", line 1, in <module>
#         StopIteration

我们可以看到, 在yield掉所有的值后, next()触发了一个StopIteration的异常. 基本上这个异常告诉我们, 所有的值都已经被yield完了. 你也许会奇怪, 为什么我们在使用for循环时没有这个异常呢?答案很简单. for循环会自动捕捉到这个异常并停止调用next().

3. 内置数据类型中的可迭代对象

my_string = "Yasoob"
print(next(my_string))

# Output: Traceback (most recent call last):
#      File "<stdin>", line 1, in <module>
#    TypeError: str object is not an iterator

这个异常说那个str对象不是一个迭代器. 对, 就是这样!它是一个可迭代对象, 而不是一个迭代器. 这意味着它支持迭代, 但我们不能直接对其进行迭代操作. 那我们怎样才能对它实施迭代呢?是时候学习下另一个内置函数, iter. 它将根据一个可迭代对象返回一个迭代器对象. 这里是我们如何使用它:

my_iter = iter("Yasoob")
print(next(my_iter))

# Output: 'Y'

参考文献

  1. Python进阶
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章