老男孩python DAY13-14 迭代器,生成器笔记

1. 主要内容

- 迭代器

- 生成器

2. 迭代器

- 可迭代协议:

  • 只要含有双下划线iter方法的都是可迭代的
  • 可以被for循环的都是可迭代的,for循环其实就是在使用迭代器

- 迭代器协议:

  • 内部含有__iter__和__next__方法的就是迭代器
  • 只要是迭代器,就一定可以迭代
  • 可迭代的.__iter__()方法就可以得到一个迭代器
  • 迭代器中的.__next__()方法可以一个一个的获取值
  • 惰性运算

- 迭代器的好处

  • 从容器类型中一个一个的获取值
  • 节省内存空间(需要时才会执行,每次__next__()生成一个值)

- 判断可迭代和迭代器的一种方法

1
2
3
4
5
6
7
8
9
10
11
12
from collections import Iterable
from collections import Iterator
# print(isinstance([],Iterator))
# print(isinstance([],Iterable))

# class A:
# # def __iter__(self):pass
# def __next__(self):pass
#
# a = A()
# print(isinstance(a,Iterator))
# print(isinstance(a,Iterable))

3. 生成器

- 生成器的本质:迭代器

- 生成器的表现形式:

  • 生成器函数
  • 生成器表达式

- 生成器函数:

  • 只要含有yield关键字的函数都是生成器函数
  • yield不能和return共用,且需要写在函数内部
  • 调用生成器函数会得到一个生成器

- 生成器运行方式:

  • 通过__next__执行
  • 通过for循环执行
  • 数据类型强制转换
  • 通过send执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def generator():
print(1)
yield 'a'
print(2)
yield 'b'
# 生成器函数:执行之后会得到一个生成器作为返回值
g = generator()

# __next__方法运行生成器
ret = g.__next__()
print(ret)
ret = g.__next__()
print(ret)
# for 循环方法运行生成器
g = generator()
for i in g:
print(i)
# 数据类型强行转换
g = generator()
print(list(g))

- send的使用方法:

  • 利用send执行的过程中,可以给上一个yield的地方传值
  • 第一次使用生成器的时候,利用next获取下一个值
  • 最后一个yield不能接收外部的值
1
2
3
4
5
6
7
8
9
10
11
12
def generator():
print(123)
content = yield 1
print('======', content)
print(456)
yield 2

g = generator() #得到生成器
ret = g.__next__() # 执行到第一个yield,打印123,返回1
print('****', ret) # 打印**** 1
ret = g.send('hello') # 传值给content, 执行到第二个yield,打印456, 返回2
print('****',ret) # 打印**** 2

- 生成器表达式:

  • 列表推导式—返回列表

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    egg_list = ['egg%s' % (i*i) for i in range(10)]
    print(egg_list)
    ret = [i*i for i in range(30) if i%3 == 0]
    print(ret)
    #根据条件筛选查找嵌套列表并返回
    names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
    ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]

    ret = [j for i in names for j in i if j.count('e') == 2]
    print(ret)
  • 字典推导式

    1
    2
    3
    4
    5
    6
    7
    8
    #字典key,value对调
    mcase = {'a': 10, 'b': 34}
    ret = {mcase[i]:i for i in mcase.keys()}
    print(ret)
    # 例二:合并大小写对应的value值,将k统一成小写
    mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
    ret = {k.lower():(mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0))for k in mcase.keys()}
    print(ret)
  • 集合推导式

    1
    2
    3
    # 计算列表中每个值的平方,自带去重功能
    squared = {x**2 for x in [1, -1, 2]}
    print(squared)
  • 生成器表达式—返回生成器

    1
    2
    3
    g = ('egg%s' % (i*i) for i in range(10))
    for i in g:
    print(i)

4. 例题

- 利用生成器监听文件输入

利用生成器的yield特性,不需要一次返回所有的值,只是将功能提供给用户,用户需要时进行循环输出或选择性输出。

1
2
3
4
5
6
7
8
9
10
11
def tail(filename):
f = open(filename,encoding='utf-8')
while True:
line = f.readline()
if line.strip():
yield line.strip()

g = tail('file')
for i in g:
if 'python' in i:
print('***',i)

- 获取移动平均值

  • 利用yield和send配合
  • 第一步的生成器__next__初始化通过装饰器来预激活
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def init(func):
def inner(*args, **kwargs):
g = func(*args, **kwargs)
g.__next__()
return g
return inner

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


avg_g = average()
avg1 = avg_g.send(10)
print(avg1)

4. 练习题

1
2
3
4
5
6
7
8
9
10
11
# 练习题:
lst = ['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe']
# 例1: 过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母
new_lst = [i.upper() for i in lst if len(i)>=3]
print(new_lst)
# 例2: 求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元祖列表
lst = [(i, j) for i in range(0,5,2) for j in range(1,6,2)]
print(lst)
# 例3: 求M中3,6,9组成的列表M = [[1,2,3],[4,5,6],[7,8,9]]
M = [[1,2,3],[4,5,6],[7,8,9]]
print([row[2] for row in M])