Python迭代器、生成器及yield关键字

一、迭代

  1. 迭代:从对象中,逐个获取元素的过程。

  2. 可迭代对象
    理解:可以进行迭代操作的对象;
    定义:含有 __iter__() 方法或 __getitem__() 方法的对象;
    常见可迭代对象:字典(dict)、元组(tuple)、集合(set)和字符串等;
    可迭代是对象的一种特性,可迭代对象,都可以进行for循环进行遍历;
    可以使用函数hasattr()isinstance()判断一个对象是否可迭代。

from collections import Iterable

a = [1, 2, 3]
b = 'python'

print(hasattr(a, '__iter__'))
True

print(isinstance(b, Iterable))
True


二、迭代器

  1. 定义:指遵循迭代器协议(iterator protocol)的对象。

  2. 迭代器协议(iterator protocol):实现__iter__()方法和__next__()方法。__iter__()方法返回迭代器对象本身,__next__()方法返回容器的下一个元素,没有后续元素时抛出 StopIteration 异常。

  3. 迭代器是访问序列内元素的一种方式,提供了一种遍历序列对象的方法。

  4. 可以使用函数hasattr()isinstance()判断一个对象是否是迭代器。

from collections import Iterable, Iterator 

a = [1, 2, 3]
b = 'python'

print(hasattr(a, '__iter__'))
True

print(hasattr(a, '__next__'))   #有 __iter__ 方法但是没有 __next__ 方法,不是迭代器
False

print(isinstance(a, Iterator))
False
  1. 虽然字符串、元组、列表和字典等对象都是可迭代的,但它们却不是迭代器。这些可迭代对象,可以使用 Python 内置的 iter() 函数获得它们的迭代器对象。
from collections import Iterable, Iterator 

a = [1, 2, 3]
b = 'python'

print(isinstance(iter(a), Iterator))
True

print(isinstance(iter(b), Iterator))
True
  1. 对Python中的可迭代对象,进行 for 循环遍历时,就是先通过内置函数 iter() 获得一个迭代器,然后再不断调用 __next__() 函数进行迭代。

三、生成器

  1. 生成器(generator)是一个返回迭代器(iterator)的函数。

  2. 生成器只能用于迭代操作,且只能迭代一次,因为值是在迭代的过程生成的,而所有的值并没有保存在内存中。

  3. 简单点理解,生成器就是一个特殊的迭代器,每次迭代时返回一个值,直到抛出 StopIteration 异常。

  4. 构造生成器的方法

  • 生成器表达式
    列表推导式 的定义类似,生成器表达式使用圆括号() 而不是方括号[],比如:
nums = (x for x in range(4))

for num in nums:
    print(num, end = ' ')

0 1 2 3 
  • 生成器函数(yield)
    调用含有 yield 关键字的函时,会返回一个生成器;
    实际上,yield 仅能用于定义生成器函数。
  1. 生成器在Python中是一个非常强大的编程结构,可以用更少地中间变量写流式代码,更能节省内存和CPU,同时可以用更少的代码来实现相似的功能。

  2. 生成器小结
    是可迭代对象;
    延迟计算,一次返回一个结果,节省内存;
    本质上和其他的数据类型一样,都是实现了迭代器协议。

四、生成器函数(yield)

  1. 生成器函数,不使用 return 语句返回值,而使用关键字 yield,后跟一个表达式或值,调用时返回该表达式或值。

  2. 在生成器函数中,关键字 yield会在内部自动创建__iter__()__next__()方法,而且在没有数据时,也会抛出 StopIteration 异常,非常简单高效地获得一个迭代器。

  3. 执行过程
    调用生成器函数时,不会立即执行代码,而是返回一个生成器对象;
    __next__()方法作用于返回的生成器对象时,函数开始执行;
    每次遇到 yield 时,函数会暂停并保存当前所有的运行信息,返回 yield 表达式的值;
    当下一次执行 __next__() 方法时,函数会从原来暂停的地方继续执行;
    直到没有 yield 时,抛出异常;
    简而言之,就是 __next__() 使函数执行,yield 使函数暂停。

def generator_function():
    print('Hello 1')
    yield 1
    print('Hello 2')
    yield 2
    print('Hello 3')

g = generator_function()

print(g)    # 函数没有立即执行,而是返回了一个生成器,当然也是一个迭代器


print(g.__next__())     # 当使用__next()__方法,或使用 next(g) 时开始执行,遇到 yield 暂停
Hello 1
1

print(g.__next__())     # 从原来暂停的地方继续执行
Hello 2
2

print(g.__next__())     # 从原来暂停的地方继续执行,没有 yield,抛出异常
Hello 3
Traceback (most recent call last):
  File "", line 1, in 
StopIteration
  1. Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的,可以直接对迭代器进行函数操作,比如sum()list()等。
a = [1, 5, 2, 1, 9, 1, 5, 10]

def dedupe(items):
    seen = set()
    for item in items:
        if item not in seen:
            yield item
            seen.add(item)

# 返回生成器对象
print(type(dedupe(a)))


# 不同值求和
print(sum(dedupe(a)))
27

# 删除序列中相同元素,并保持原来的顺序
print(list(dedupe(a)))
[1, 5, 2, 9, 10]
  1. 注意事项
    迭代器函数可以多次调用(见上述4中的示例);
    但一个生成器对象只能遍历一次;
    如下示例中,执行sum()函数时,遍历了生成器d;执行list()函数,再次遍历生成器d时,将不会有任何记录,结果为空。
a = [1, 5, 2, 1, 9, 1, 5, 10]

def dedupe(items):
    seen = set()
    for item in items:
        if item not in seen:
            yield item
            seen.add(item)

d = dedupe(a)    # d 为一个生成器对象

# d 为生成器对象
print(type(d))


# 不同值求和
print(sum(d))
27

# 删除序列中相同元素,并保持原来的顺序:结果为空
print(list(d))
[]
  1. 示例:生成一个Fibonacci 数列
  • 自定义迭代器
class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1

    def __iter__(self):
        return self

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        return self.a

f = Fib()
for item in f:
    if item > 10:
        break
    print(item, end=' ')

1 1 2 3 5 8 
  • 生成器函数
def fib():
    a, b = 0, 1
    while True:
        a, b = b, a + b
        yield a 

f = fib()
for item in f:
    if item > 10:
        break
    print(item, end=' ')

1 1 2 3 5 8 

可以看到,使用生成器函数的方法非常简洁,不用自定义 __iter__()__next__() 方法。

  1. 形象理解生成器函数
    如果实在还是不太能理解生成器函数,大致上可以这样去分步理解:
    在函数开始处,假设 result = list()
    将每个 yield expr(表达式), 理解为 result.append(expr)
    在函数末尾处,假设 return result

你可能感兴趣的:(Python迭代器、生成器及yield关键字)