迭代器和生成器

迭代器和生成器

1. 迭代

1.1 迭代的定义

在 Python 中,给定一个 list 或 tuple,我们可以通过 for 循环来遍历这个 list 或 tuple ,这种遍历就是迭代。

迭代案例
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
# -*- coding: UTF-8 -*-

# 1、for 循环迭代字符串
for char in 'liangdianshui' :
print ( char , end = ' ' )

print('\n') # l i a n g d i a n s h u i

# 2、for 循环迭代 list
list1 = [1,2,3,4,5]
for num1 in list1 :
print ( num1 , end = ' ' )

print('\n') # 1 2 3 4 5

# 3、for 循环也可以迭代 dict (字典)
dict1 = {'name':'两点水','age':'23','sex':'男'}

for key in dict1 : # 迭代 dict 中的 key
print ( key , end = ' ' )

print('\n') # name age sex

for value in dict1.values() : # 迭代 dict 中的 value
print ( value , end = ' ' )

print ('\n') # 两点水 23 男

# 如果 list 里面一个元素有两个变量,也是很容易迭代的
for x , y in [ (1,'a') , (2,'b') , (3,'c') ] :
print ( x , y ) # 1 a 2 b 3 c

1.2 迭代器

  • 迭代器 是一个可以记住遍历的位置的对象,从集合的第一个元素开始访问,直到所有的元素被访问完结束.
  • 迭代器有两个基本的方法:iter()next(), 且 字符串列表元组 对象都可用于创建迭代器,
  • 迭代器对象可以使用常规 for 语句进行遍历,也可以使用 next() 函数来遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1、字符创创建迭代器对象
str1 = 'liangdianshui'
iter1 = iter (str1)

# 2、list对象创建迭代器
list1 = [1,2,3,4]
iter2 = iter (list1)

# 3、tuple(元祖) 对象创建迭代器
tuple1 = (1,2,3,4)
iter3 = iter (tuple1)

# for 循环遍历迭代器对象
for x in iter1 :
print (x, end=' ') # l i a n g d i a n s h u i

# next() 函数遍历迭代器
while True :
try :
print (next (iter3)) # 1 2 3 4
except StopIteration :
break

2. list 生成式(列表生成式)

2.1 创建 list

1
2
3
4
# 生成一个有 30 个元素的 list ,里面的元素为 1 - 30
list1 = list(range(1,31))

print(list1) # [1, 2, 3, ... 28, 29, 30]
1
2
3
4
5
6
7
8
9
10
11
12
13
# 99 乘法表
print('\n'.join([' '.join(f'{x}*{y}={x * y}' for x in range(1, y+1)) for y in range(1, 10)]))

输出结果:
1*1=1
2*1=2 2*2=4
3*1=3 3*2=6 3*3=9
4*1=4 4*2=8 4*3=12 4*4=16
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64
9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81

2.2 列表生成式

list 生成式的语法格式:

  • 结果 for 元素 in 集合
    • 把集合中的元素依次提取出来, 然后在表达式中应用元素, 最后把表达式的结果添加到结果中
  • 结果 for 元素 in 集合 if 筛选条件
    • 把条件成立的元素从集合中提取出来, 然后在表达式中应用元素, 最后把表达式的结果添加到结果中
1
2
3
4
5
list1=[x * x for x in range(1, 11)]
print(list1) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

list1= [x * x for x in range(1, 11) if x % 2 == 0]
print(list1) # [4, 16, 36, 64, 100]
1
2
3
4
list1 = [(x, y) for x in range(2) for y in range(3)] 
print(list1)

[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]

3. 生成器(generator)

在 Python 中,这种一边循环一边计算的机制,称为生成器:generator
在 Python 中,使用了 yield 的函数被称为生成器(generator)
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值。并在下一次执行 next()方法时从当前位置继续运行

3.1 生成器创建

生成器创建 最简单的方法就是把一个列表生成式的 [] 改成 ()

1
2
3
4
5
6
# -*- coding: UTF-8 -*-
gen= (x * x for x in range(10))
print(gen)

输出的结果:
<generator object <genexpr> at 0x0000000002734A40>

创建 Listgenerator 的区别仅在于最外层的 []()
生成器并不真正创建数字列表,而是返回一个生成器
这个生成器在每次计算出一个条目后,把这个条目产生(yield)出来。

生成器表达式使用了 惰性计算 (lazy evaluation,也有翻译为“延迟求值”,我以为这种按需调用 call by need 的方式翻译为惰性更好一些),只有在检索时才被赋值(evaluated),所以在列表比较长的情况下使用内存上更有效。

3.2 遍历生成器的元素

1
2
3
4
5
6
7
8
9
10
gen = (x*x for x in range(10))

for i in gen:
print(i, end=' ')

while True:
try:
print(next(gen), end=' ')
except StopIteration:
break

3.3 以函数的形式实现生成器

1
2
3
4
5
6
7
8
9
10
11
12
def odd():
print('step 1')
yield 1
print('step 2')
yield 3
print('step 3')
yield 5

o = odd()
print(next(o)) # step 1 1
print(next(o)) # step 2 3
print(next(o)) # step 3 5

可以看到,odd 不是普通函数,而是 generator,在执行过程中,遇到 yield 就中断,下次又继续执行。
执行 3 次 yield 后,已经没有 yield 可以执行了,如果你继续打印 print(next(o)),就会报错的。
所以通常在 generator 函数中都要对错误进行捕获。

1
2
3
4
5
6
7
8
9
10
# 计算斐波那契数列的生成器
def fibon(n):
a = b = 1
for i in range(n):
yield a
a, b = b, a + b

# 引用函数
for x in fibon(10):
print(x , end = ' ') # 1 1 2 3 5 8 13 21 34 55

3.4 打印杨辉三角

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def triangles():  # 杨辉三角形
lists = [1]
while True:
yield lists
lists.append(0)
lists = [lists[i - 1] + lists[i] for i in range(len(lists))]

n = 0
for t in triangles(): # 直接修改函数名即可运行
print(t)
n = n + 1
if n == 5:
break

输出结果:
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]

4. 迭代器和生成器结合

4.1 反向迭代

反向迭代仅仅当对象的大小可 预先确定 或者对象实现了 __reversed__() 的特殊方法时才能生效。
如果两者都不符合,那你必须先将对象转换为一个列表才行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Countdown:
def __init__(self, start):
self.start = start

def __iter__(self):
n = self.start
while n > 0:
yield n
n -= 1

def __reversed__(self):
n = 1
while n <= self.start:
yield n
n += 1

for rr in reversed(Countdown(10)):
print(rr, end=' ') # 1 2 3 4 5 6 7 8 9 10

for rr in Countdown(10):
print(rr, end=' ') # 10 9 8 7 6 5 4 3 2 1

4.2 同时迭代多个序列

zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。

1
2
3
4
5
names = ['laingdianshui', 'twowater', '两点水']
ages = [18, 19, 20]

for name, age in zip(names, ages):
print(name, age, end=', ') # laingdianshui 18, twowater 19, 两点水 20,

利用zip()函数,我们还可把一个 key 列表和一个 value 列表生成一个 dict(字典)

1
2
3
4
5
6
names = ['laingdianshui', 'twowater', '两点水']
ages = [18, 19, 20]

dict1 = dict(zip(names, ages))

print(dict1) # {'laingdianshui': 18, 'twowater': 19, '两点水': 20}