一、迭代
迭代:从对象中,逐个获取元素的过程。
可迭代对象
理解:可以进行迭代操作的对象;
定义:含有__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
二、迭代器
定义:指遵循迭代器协议
(iterator protocol)
的对象。迭代器协议
(iterator protocol)
:实现__iter__()
方法和__next__()
方法。__iter__()
方法返回迭代器对象本身,__next__()
方法返回容器的下一个元素,没有后续元素时抛出 StopIteration 异常。迭代器是访问序列内元素的一种方式,提供了一种遍历序列对象的方法。
可以使用函数
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
- 虽然字符串、元组、列表和字典等对象都是可迭代的,但它们却不是迭代器。这些可迭代对象,可以使用 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
- 对Python中的可迭代对象,进行
for
循环遍历时,就是先通过内置函数iter()
获得一个迭代器,然后再不断调用__next__()
函数进行迭代。
三、生成器
生成器
(generator)
是一个返回迭代器(iterator)
的函数。生成器只能用于迭代操作,且只能迭代一次,因为值是在迭代的过程生成的,而所有的值并没有保存在内存中。
简单点理解,生成器就是一个特殊的迭代器,每次迭代时返回一个值,直到抛出 StopIteration 异常。
构造生成器的方法
- 生成器表达式
和 列表推导式 的定义类似,生成器表达式使用圆括号()
而不是方括号[]
,比如:
nums = (x for x in range(4))
for num in nums:
print(num, end = ' ')
0 1 2 3
- 生成器函数(yield)
调用含有 yield 关键字的函时,会返回一个生成器;
实际上,yield 仅能用于定义生成器函数。
生成器在Python中是一个非常强大的编程结构,可以用更少地中间变量写流式代码,更能节省内存和CPU,同时可以用更少的代码来实现相似的功能。
生成器小结
是可迭代对象;
延迟计算,一次返回一个结果,节省内存;
本质上和其他的数据类型一样,都是实现了迭代器协议。
四、生成器函数(yield)
生成器函数,不使用
return
语句返回值,而使用关键字yield
,后跟一个表达式或值,调用时返回该表达式或值。在生成器函数中,关键字
yield
会在内部自动创建__iter__()
和__next__()
方法,而且在没有数据时,也会抛出 StopIteration 异常,非常简单高效地获得一个迭代器。执行过程
调用生成器函数时,不会立即执行代码,而是返回一个生成器对象;
当__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
- 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]
- 注意事项
迭代器函数可以多次调用(见上述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))
[]
- 示例:生成一个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__()
方法。
- 形象理解生成器函数
如果实在还是不太能理解生成器函数,大致上可以这样去分步理解:
在函数开始处,假设result = list()
;
将每个 yield expr(表达式), 理解为result.append(expr)
;
在函数末尾处,假设return result
。