【python学习笔记】迭代器和生成器

一,迭代器

1.迭代器的概念

#迭代器即迭代的工具,那什么是迭代呢?

#迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值

while True: #只是单纯地重复,因而不是迭代
    print('===>') 
    
l=[1,2,3]
count=0
while count < len(l): #迭代
    print(l[count])
    count+=1

2.为何要有迭代器?

对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器

3.可迭代对象

可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下
字符串:'hello'.__iter__
元组:(1,2,3).__iter__
列表:[1,2,3].__iter__
字典:{'a':1}.__iter__
集合:{'a','b'}.__iter__
文件:open('a.txt').__iter__

4.迭代器对象

可迭代对象执行obj.__iter__()得到的结果就是迭代器对象

而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象
例如:
文件类型是迭代器对象
open('a.txt').__iter__()

open('a.txt').__next__()

#列表
l = [1,2,3,4]
#for i in l:   #本质上是: i_l = l.__iter__()  i_l.__next__()
iter_l = l.__iter__() #调用内部的__iter__方法,得到一个迭代对象
print(iter_l.__next__())
print(iter_l.__next__())
print(iter_l.__next__())
print(iter_l.__next__())
print(iter_l.__next__()) #取值完毕后,最后再次调用next方法,会报错StopIteration
#字符串
x = 'hello'
#print(dir(x))
iter_s = x.__iter__() #调用内部的__iter__方法,得到一个迭代对象
print(type(iter_s))
print(iter_s.__next__())
print(iter_s.__next__())
print(iter_s.__next__())
print(iter_s.__next__())
print(iter_s.__next__())
print(iter_s.__next__())  #取值完毕后,最后再次调用next方法,会报错StopIteration

#字典
dic = {'a':1,'b':2}
iter_d = dic.__iter__() #调用内部的__iter__方法,得到一个迭代对象
print(iter_d.__next__())
print(iter_d.__next__())
print(iter_d.__next__()) #取值完毕后,最后再次调用next方法,会报错StopIteration

#文件
f = open('test.txt','w+')
#for i in f:
iter_f = f.__iter__() #调用内部的__iter__方法,得到一个迭代对象
print(iter_f.__next__())

iter.__next__()方法等同于next(iter):

#使用next()内置函数
l = ['美女','帅哥','基佬','百合']

iter_l = l.__iter__()
#print(iter_l.__next__())
#print(iter_l.__next__())
#print(iter_l.__next__())
#print(iter_l.__next__())
#print(iter_l.__next__()) #报错:StopIteration
print(next(iter_l))  #next()内置函数相当于 -->>iter_l.__next__()

注意:迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象

迭代器对象的使用:

dic={'a':1,'b':2,'c':3}
iter_dic=dic.__iter__() #得到迭代器对象,迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身
iter_dic.__iter__() is iter_dic #True

print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
# print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志

#有了迭代器,我们就可以不依赖索引迭代取值了-->>利用while循环遍历元素
iter_dic=dic.__iter__() #转换成迭代器对象
while True:
    try:
        k=next(iter_dic)
        print(dic[k])
    except StopIteration:
        break
diedai_l = l.__iter__() #转换成迭代器对象
while True:
    try:
	print(diedai_l.__next__())
    except StopIteration:   #捕捉异常
	print('迭代完毕,循环终止')
	break        
#利用while循环遍历元素,需要我们自己捕捉异常,控制next,变得很麻烦,不过可以用for循环解决

5.for循环

基于for循环,我们可以完全不再依赖索引去取值了

dic={'a':1,'b':2,'c':3}
for k in dic:
    print(dic[k])
for循环的工作原理
#1: 执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic
#2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码

#3: 重复过程2,直到捕捉到异常StopIteration,结束循环

6.迭代器的优缺点

①优点:
  - 提供一种统一的、不依赖于索引的迭代方式
  - 惰性计算,节省内存
②缺点:
  - 无法获取长度(只有在next完毕才知道到底有几个值)

  - 一次性的,只能往后走,不能往前退

二,生成器

1.生成器的概念

只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码

def func():
    print('====>first')
    yield 1
    print('====>second')
    yield 2
    print('====>third')
    yield 3
    print('====>end')

g=func()
print(g) #<generator object func at 0x0000000002184360>

其他的数据类型需要调用内置的__iter__方法,得到一个迭代器对象,而生成器不要,它就是迭代器

g.__iter__
g.__next__
#2、所以生成器就是迭代器,因此可以这么取值
res=next(g)
print(res)

2.生成器的两种表现形式

1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果,yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行

def test():
    yield 1
    yield 2
    yield 3

g = test()
print(g)  g为生成器
print(g.__next__()) #直接使用next方法
print(g.__next__())
print(g.__next__())
print(g.__next__())  #与迭代器一样,报错:StopIteration

生成器函数举例:

#正常实现:先计算共生产多少,再去考虑怎么卖
def product_baozi():
    ret = []
    for i in range(100):
	ret.append('包子%s' % i)
    return ret
baozi_list = product_baozi()
print(baozi_list)

#生成器实现:先不管有多少,生产一个实现卖出去一个
def product_baozi():
    for i in range(100):
	yield '包子%s' % i
pro_g = product_baozi()

baozi1 = pro_g.__next__()
print('吃%s' % baozi1)

baozi2 = pro_g.__next__()
print('吃%s' % baozi2)

#正常实现:下蛋
def xiadan():
    ret = []
    for i in range(100):
	ret.append('鸡蛋%s' % i)
    return ret
print(xiadan())
#缺点1:占空间大  #缺点2:效率低
#生成器实现:下蛋
def xiadan():
    for i in range(100):
	yield '鸡蛋%s' % i
alex_lmj = xiadan()

jidan1 = alex_lmj.__next__()
print('吃%s' % jidan1)

jidan2 = alex_lmj.__next__()
print('吃%s' % jidan2)

2.生成器表达式:类似于列表推导式,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

三元表达式,列表推导式,生成器表达式

① 三元表达式

name = 'alex'
res = 'gay' if name == 'alex' else 'shuaige'
print(res)

② 列表推导式

#正常实现
egg_list = []
for i in range(10):
    egg_list.append('苹果%s' % i)
print(egg_list)

#列表解析
l = ['苹果%s' % i for i in range(10)]
ll = ['苹果%s' % i for i in range(10) if i > 5] #三元表达式
lll = ['苹果%s' % i for i in range(10) if i < 5]
print(l)
print(ll)
print(lll)

③ 生成器推导式

1.列表解析的[]换成()得到的就是生成器表达式
2.生成器比列表解析更节省内存

示例:生一筐鸡蛋变成给你一只老母鸡,用的时候就下蛋,这也是生成器的特性
>>> chicken=('鸡蛋%s' %i for i in range(5))
>>> chicken
<generator object <genexpr> at 0x10143f200>
>>> next(chicken)
'鸡蛋0'
>>> list(chicken) #因chicken可迭代,因而可以转成列表
['鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4',]

优点:省内存,一次只产生一个值在内存中

④其他内置函数表达

#sum()函数
#使用列表解析
sum_l = sum([x ** 2 for x in range(10)])
print(sum_l)
#没必要使用列表解析,可以去掉[],直接写表达式
sum_l = sum(x ** 2 for x in range(9))
print(sum_l)

#max()函数
max_1 = max(i for i in [1,9,8,10,100,25])
print(max_1)

#min()函数
min_1 = min(i for i in [1,9,8,10,100,25])
print(min_1)

3.yield关键字的另外一种使用形式

#正常实现:吃包子
import time

def producer():
    ret = []
    for i in range(10000):
        time.sleep(0.01)
	ret.append('包子%s' % i)
    return ret

def consumer(res):
    for index,baozi in enumerate(res):
	time.sleep(0.01)
	print('第%s人,吃了%s' % (index,baozi)) 
res = producer()
consumer(res)
def test():
    print('开始了')
    first=yield #first = yield的另一个特性,接受send传过来的值,赋值给first
    print('第一次',first)
    yield 2
    print('第二次')

t = test()
res = t.__next__()  #next(t)
print(res)
#t.__next__()
#res = t.send(None)
res = t.send('函数停留在first那个位置,我就给first赋值')
print(res)
#吃包子-->>增加send方法
import time

def consumer(name):
    print('我是%s,我在吃包子' % name)
    while True:
	baozi = yield
	time.sleep(1)
	print('%s高兴地把【%s】吃掉了' % (name,baozi))

def producer():
    c1 = consumer('laowang')
    c2 = consumer('youzong')
    c1.__next__()
    c2.__next__()
    for i in range(10):
	time.sleep(1)
	c1.send('包子 %s' % i)
	c2.send('包子 %s' % i)
producer()

4.生成器特点

①语法上和函数类似:生成器函数与常规函数几乎一样,都是用def语句定义,差别在于使用yield语句返回一个值,而常规函数使用
return语句返回值
②自动实现迭代器协议:对于生成器,python会自动实现迭代器协议,由于生成器自动实现迭代器协议,我们可以调用它的next方法,并且,在没有值可以返回时,生成器自动产生stopiteration异常

③状态挂起:生成器使用yield语句返回一个值。yield语句挂起该生成器函数的状态,保留足够的信息,以便从它离开的地方继续执行

5.生成器的优点

①延迟计算,一次返回一个结果,也就是说,它不会一次生成所有的结果,这对大数据处理来说,将会非常有用

#列表解析
sum([i for i in range(100000)]) #内存占用大,机器容易卡死
#生成器表达式
sum(i for i in range(100000)) #几乎不占用内存

②生成器还能有效提高代码可读性

注意事项:生成器只能遍历一次,遍历完后就没有值可获取 )

作业题:

1.自定义函数模拟range(1,7,2)

#定义函数
def my_range(start,stop,step=1):
    while start < stop:
	yield start
	start+=step
#执行函数得到生成器,本质就是迭代器
obj = my_range(1,7,2)
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))  #StopIteration

#应用于for循环
for i in range(1,7,2):
	print(i)

2.编写装饰器,实现初始化协程函数的功能

def init(func):
    def wrapper(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return wrapper
@init
def eater(name):
    print('%s 准备开始吃饭啦' %name)
    food_list=[]
    while True:
        food=yield food_list
        print('%s 吃了 %s' % (name,food))
        food_list.append(food)

g=eater('egon')
g.send('蒸羊羔')

3.将names=['egon','alex_sb','wupeiqi','yuanhao']中的名字全部变大写

names=['egon','alex_sb','wupeiqi','yuanhao']
names = [i.upper() for i in names]
print(names)

4.将names=['egon','alex_sb','wupeiqi','yuanhao']中以sb结尾的名字过滤掉,然后保存剩下的名字长度

names=['egon','alex_sb','wupeiqi','yuanhao']
names = [len(i) for i in names if not i.endswith('sb')]
print(names)

5.求文件a.txt中最长的行的长度(长度按字符个数算,需要使用max函数)

with open('a.txt',encoding='utf-8') as f:
    print(max(len(i) for i in f))

6.求文件a.txt中总共包含的字符个数

with open('a.txt',encoding='utf-8') as f:
    print(sum(len(i) for i in f))
7.文件shopping.txt内容如下
    mac,20000,3
    lenovo,3000,10
    tesla,1000000,10
    chicken,200,1

~求总共花了多少钱?

with open('shopping.txt',encoding='utf-8') as f:
    info = [line.split() for line in f]
    cost = sum(float(unit_price)*int(count) for name,unit_price,count in info)
    print(cost)

~打印出所有商品的信息,格式为[{'name':'xxx','price':333,'count':3},...]

with open('shopping.txt',encoding='utf-8') as f:
    info = [{'name':line.split()[0],
	    'price':line.split()[1],
	    'count':line.split()[2]
	} for line in f]
    print(info)

~求单价大于10000的商品信息,格式同上

with open('shopping.txt',encoding='utf-8') as f:
    info = [{'name':line.split()[0],
	    'price':line.split()[1],
	    'count':line.split()[2]
	} for line in f if float(line.split()[1])>10000]
    print(info)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值