Python迭代器&生成器

可迭代对象

概念

内部实现了__iter__()方法

验证方法

  • 使用dir()方法查看数据类型中的方法是否包含__iter__()

作用

  • 可迭代对象执行__iter__()方法后会生成一个迭代器对象

标识

  • 拥有__iter__()方法的对象

示例

1
2
3
4
5
6
7
s = 'abc' # 可迭代对象
it = iter(s) # 调用迭代方法,等同于 s.__iter__()

print(it)

# 输出
<str_iterator object at 0x7f0234b10eb8>

迭代器

概念

迭代器是一个有状态的对象,它能在调用next()方法的时候返回容器中的下一个值,任何实现了__iter__()__next__()方法的对象都是迭代器,__iter__返回迭代器自身,__next__返回容器中的下一个值,如果容器中没有下一个值则抛出StopIteration异常。

迭代器 vs 可迭代对象区别

  • 迭代器拥有__length_hint__: 获取迭代器中元素的长度

  • 迭代器拥有__setstate__: 根据索引值指定从哪里开始迭代

  • 迭代器拥有__next__: 获取迭代器对象的下一个值

标识

  • 拥有__iter__()__next__()方法的对象

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
s = 'abc'
it = iter(s)
print(it) # 输出迭代器对象

print(next(it)) # 输出'a'
print(next(it)) # 输出'b'
print(next(it)) # 输出'c'
print(next(it)) # 没有下一个值,抛出StopIteration异常

# 输出
<str_iterator object at 0x7fbe85382f60>
a
b
c
Traceback (most recent call last):
File "main.py", line 9, in <module>
print(next(it))
StopIteration

生成器(本质是迭代器)

概念

生成器是一种特殊的迭代器,它用于创建迭代器的简单而强大的工具。 它们的写法类似标准的函数,但当它们要返回数据时会使用yield语句。 每次对生成器调用next()时,它会从上次离开位置恢复执行(它会记住上次执行语句时的所有数据值)。

特点

  • 惰性运算,开发者自定义
  • 本质是迭代器

应用

  • 生成器函数
    一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于returnreturn的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。

  • 生成器函数进阶:send()

    • send和next的作用相同
    • 第一次不能用send
    • 函数中的最后一个yield不能接受新的值
  • 生成器表达式
    类似于列表推导,但是生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表。

示例

  • 简单应用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import time
def genrator_fun1():
a = 1
print('现在定义了a变量')
yield a
b = 2
print('现在又定义了b变量')
yield b

g1 = genrator_fun1()
print('g1 : ',g1) #打印g1可以发现g1就是一个生成器
print('-'*20) #我是华丽的分割线
print(next(g1))
time.sleep(1) #sleep一秒看清执行过程
print(next(g1))
  • 避免一次性读取数据到内存中导致内存溢出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import time
def tail(filename):
f = open(filename)
f.seek(0, 2) #从文件末尾算起
while True:
line = f.readline() # 读取文件中新的文本行
if not line:
time.sleep(0.1)
continue
yield line

tail_g = tail('tmp')
for line in tail_g:
print(line)
  • yield from
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def gen1():
for c in 'AB':
yield c
for i in range(3):
yield i
print(list(gen1()))

以上是将生成器中的数据以列表的方式打印
以下的执行效果与上面相同,运用了yield from 语法
def gen2():
yield from 'AB'
yield from range(3)

print(list(gen2()))
  • 生成器函数进阶:send()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 计算移动平均值的例子
def average():
sum = 0
count = 0
avg = 0
while True:
num = yield avg
sum += num
count += 1
avg = sum / count

avg_g = average()
avg_g.__next__()

print(avg_g.send(20))
print(avg_g.send(40))
print(avg_g.send(60))
  • 预激生成器的装饰器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 预激生成器的装饰器——在装饰器中首先调用了__next__方法,方便用户直接进行调用
def init(func):
def wrapper(*args,**kwargs):
g = func(*args,**kwargs)
g.__next__()
return g
return wrapper

@init
def average():
sum,avg,count = 0,0,0
while True:
num = yield avg
sum += num
count += 1
avg = sum/count

avg_g = average()
print(avg_g.send(10))
print(avg_g.send(20))
print(avg_g.send(30))
  • 推导式汇总
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# -------------列表推导-------------
'''
[每一个元素或者是和元素相关的操作 for 元素 in 可迭代数据类型] #遍历之后挨个处理
[满足条件的元素相关的操作 for 元素 in 可迭代数据类型 if 元素相关的条件] #筛选功能
'''
# -------------简单的列表推导-------------
l = [i ** 3 for i in range(10)]
print(l)
>>> [0, 1, 8, 27, 64, 125, 216, 343, 512, 729]

# -------------带筛选的列表推导-------------
l = [i ** 3 for i in range(10) if i % 3 == 0]
print(l)
>>> [0, 27, 216, 729]

# -------------多层列表推导-------------
double_l = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
l = [name for lst in double_l for name in lst if name.count('e') >= 2]
print(l)
>>> ['Jefferson', 'Wesley', 'Steven', 'Jennifer']


# -------------字典推导-------------

# 例一:将一个字典的key和value对调
mcase = {'a': 10, 'b': 34}
mcase_frequency = {mcase[k]: k for k in mcase}
print(mcase_frequency)
>>> {34: 'b', 10: 'a'}

# 例二:合并大小写对应的value值,将k统一成小写
mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
mcase_frequency = {k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase.keys()}
print(mcase_frequency)
>>> {'b': 34, 'a': 17, 'z': 3}


# -------------集合推导-------------

# 计算列表中每个值的平方,自带去重功能
squared = {x ** 2 for x in [1, -1, 2]}
print(squared)
>>> {1, 4}

关系图