python之迭代器、生成器

一、迭代器

1、什么是迭代器

迭代器就是迭代取值的工具,什么是迭代呢?

迭代是一个重复的过程,但是每一次重复都是基于上一次的结果而来,这就是迭代

# 循环重复,但是每一次没有关联,不是迭代
while True:
    print("111")
    
# 循环重复,但是每次循环基于上次,是迭代
x = 0
while True:
    print(x)
    x += 1

2、为什么要用迭代器

我们之前有讲过,列表、元组等可以通过索引取值,字典可以通过key取值,但是这些都是他们自身的特性,其他数据类型无法使用,那有没有一种统一的方法进行取值呢?

这就要用到迭代器,基于以下两个原因:

为了能找到一种新的、统一的取值方式(可以不依赖与索引与key取值)
惰性计算,不耗费内存(当数据很大时,列表、字典等会占据很大的内存)
3、如何使用迭代器

1)可迭代对象

从语法形式上讲,内置有__iter__方法的对象都是可迭代对象,字符串、列表、元组、字典、集合、打开的文件都是可迭代对象:

# 带有内置方法__iter__(),都是可迭代对象
[1, 2, 3, 4, 5].__iter__()
"safdsdsf".__iter__()
{"name": "zhang"}.__iter__()
{1, 2, 3, 4, 5, 6}.__iter__()

2)迭代器对象

任何可迭代对象调用iter()方法后,都会生成一个迭代器对象,迭代器对象内置有__iter__与__next__方法,当调用__iter__方法后,返回的还是迭代器自身,而调用__next__方法后,迭代器会根据迭代对象生成以一个值,再调用__next__,就会生成下一个值,从第一个开始顺序取值,一旦值取完就会报错,过程不可逆不可改变顺序,并且取完值后,迭代器无法再次取值。

通俗说法:迭代器就像一只能下蛋的母鸡,我们想要的是母鸡肚子里的蛋,我们调用一下__next__,母鸡就下一个蛋,当母鸡把蛋下完,老母鸡炖汤,就在也无法得到蛋了,除非再造一只老母鸡

x = [1,2,3,4,5].__iter__() # 使用__iter__()方法会得到一个返回值:迭代器对象
# 一般情况下,我们使用iter()得到迭代器对象而不是__iter__()
x = iter([1,2,3,4,5])
# y = x.__next__() # 生成第一个值,并且返回该值
y1 = next(x) # 与__next__一致,但是常用next()
y2 = next(x) # 取得第二个数
y3 = next(x) # 取得第三个数
y4 = next(x) # 取得第四个数
y5 = next(x) # 取得第五个数
print(y1,y2,y3,y4,y5)
y6 = next(x) # 取第六个数,但是不存在,报错

3)for循环原理:for循环的本质,就是调用了iter()方法得到一个迭代器对象,然后调用next()方法逐个取值,知道取完为止

#使用for循环实现遍历取值
str1 = [1, 2, 3, 4, 5]
str_iter = iter(str1)
while True:
    try:
        x = next(str_iter)
        print(x)
    except StopIteration:
        break

# for循环等价于上诉的过程
for i in str1:
    print(i)

4)如果使用for循环读迭代器对象取值,结果如何?

str1 = [1,2,3,4] # 可迭代对象
str_iter = iter(str1) # 迭代器对象

# 使用for循环对迭代器对象取值
for i in str_iter:
    print(i)
for j in str_iter:
    print(j)

print("*"*20)

# 使用for询函对可迭代对象取值
for i in str1:
    print(i)
for j in str1:
    print(j)

运行结果如下:
在这里插入图片描述
 可以看到,如果使用迭代器对象,只能遍历取值1次,下一次就无法取到任何值,但是使用可迭代对象,确实可以取值多次。这是因为迭代器对象是“一次性”,取完一次就无法取      值,而使用可迭代对象,for循环每次都会造一个迭代器对象,所以可以多次取值。

5)迭代器总结

优点:

1、为序列和非序列类型提供了一种统一的迭代取值方式。

2、惰性计算:迭代器对象表示的是一个数据流,可以只在需要时才去调用next来计算出一个值,就迭代器本身来说,同一时刻在内存中只有一个值,因而可以存放无限大的数据流,而对于其他容器类型,如列表,需要把所有的元素都存放于内存中,受内存大小的限制,可以存放的值的个数是有限的。

缺点:

1、除非取尽,否则无法获取迭代器的长度

2、只能取下一个值,不能回到开始,更像是‘一次性的’,迭代器产生后的唯一目标就是重复执行next方法直到值取尽,否则就会停留在某个位置,等待下一次调用next;若是要再次迭代同个对象,你只能重新调用iter方法去创建一个新的迭代器对象,如果有两个或者多个循环使用同一个迭代器,必然只会有一个循环能取到值

二、生成器

1、什么生成器?

1)如果函数体内出现了yield关键字,那么当在调用函数时,函数不会执行函数体代码,而是生成一个生成器。

生成器的实质就是一种自定义的迭代器

# 生成器
def f1(start, end):
    while start < end:
        yield start  # 当函数运行到此处就停止运行,等待调用next(),逐个取值,直到值取完报错
        start += 1


x = f1(0, 4) # 得到生成器对象
print(f1(0, 4))

print(next(x)) # 调用next方法取值
print(next(x))
print(next(x))
print(next(x))
# print(next(x)) # 超过了四个,这里会报错

2)生成器与for循环:作为一种迭代器对象,生成器也是可以使用for循环遍历,

def f1(start, end):
    while start < end:
        yield start  # 当函数运行到此处就停止运行,等待调用next(),逐个取值,直到值取完报错
        start += 1

f = f1(0,5) # 得到迭代器对象

for i in f: # for循环遍历
    print(i)

print("*"*20) # 分隔

for j in f: # 再次遍历上述迭代器对象
    print(j)

注意这里是迭代器对象,只能被for循环取值1次

3)生成器与send()方法

send()方法可以拿到函数的生成器对象持续为函数体send值

def eater():
    print('Ready to eat')
    while True:
        food = yield "start"
        print('get the food: %s, and start to eat' % food)

g = eater() # 这里拿到生成器对象
print(g)

next(g) # 在我们传值之前,需要初始化生成器对象,让生成器在food = yiel处挂起,等待传值
g.send("狗屎") # 将"狗屎”赋值到food,然后打印,下次循环到ood = yiel处挂起,等待传值
g.send("猿粪")# 同上层

表达式形式的yield也可以用于返回多次值,即 变量名 = yield 值 的形式

def cooker():
    list1 = []
    while True:
        list2 = yield list1
        list1.append(list2)
        print(list1)

a = cooker() # 获得生成器对象
next(a) # 初始化生成器

a.send("西瓜汁")
a.send("苹果汁")

运行过程

通过赋值,得到生成器对象a
next(a),初始化生成器,运行到list2 = yield list1处,返回list1(初始值[]),等待next(),或send()传值
通过a.send(“西瓜汁”)方法,向生成器对象传值,此时,"西瓜汁”被传入生成器,并且赋值给list2,运行列表增加,打印list1(添加了西瓜汁),进入下一次循环
第二次循环,再次返回list1([“西瓜汁”]),等待next(),或send()传值;
通过a.send(“苹果汁”)方法,向生成器对象传值,此时,"苹果汁”被传入生成器,并且赋值给list2,运行列表增加,打印list1(添加了西瓜汁、苹果汁),进入下一次循环
…如此循环
二、三元表达式

三元表达式是python为我们提供的一种简化代码的解决方案,语法如下

res = 条件成立时返回的值 if 条件 else 条件不成立时返回的值

# 三元表达式
x = 5
y = 8

if x < y:
    print(x)
else:
    print(y)

# res = 条件成立时返回的值 if 条件 else 条件不成立时返回的值
res = print(x) if x < y else print(y) # 等价于上述if判断语句

三、列表生成式

列表生成式是python为我们提供的一种简化代码的解决方案,用来快速生成列表,语法如下

[expression for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
...
for itemN in iterableN if conditionN
]

#类似于
res=[]
for item1 in iterable1:
    if condition1:
        for item2 in iterable2:
            if condition2
                ...
                for itemN in iterableN:
                    if conditionN:
                        res.append(expression)

实例:

list1 = []

for i in range(0,10):
    if i % 2 == 1:
        list1.append(i)

print(list1)

list1 = [i for i in range(0,10) if i % 2 == 1] # 等价于上述for循环
print(list1)

四、字典生成式

dic1 = {}

for i in range(1,10):
    if i < 6:
        dic1["number"] = i

print(dic1)

# 等价于上述for循环
dic1 = {"number":i for i in range(1,10) if i < 6}
print(dic1)

五、生成器表达式

产生生成器的两种方式:

函数+yiel
生成器表达式:()

(expression for item in iterable if condition)

实例:

f1 = (i for i in range(0,5) if i > 1)
print(f1)
print(next(f1))

结果如下:

C:\Users\Python\Python35-32\python.exe E:/python/保存文件/next1/作业/迭代器.py
<generator object <genexpr> at 0x00638CF0>
2

读取大文件的正取打开方式:使用生成器表达式,节省内存空间

with open("hhhh.txt","rt",encoding="utf-8") as f:
    lines = (i for i in f.readlines())

x1 = next(lines)
x2 = next(lines)
x3 = next(lines)
print(x1)
print(x2)
print(x3)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值