迭代器
什么是迭代器
迭代器就是用来迭代取值的工具
什么是迭代
迭代就是一个重复的过程,但是每次重复都是基于上一次的结果进行的
# 单纯的重复不叫迭代
n = 0
while True:
n += 1
# 下面才是迭代的过程
n = 0
while n < 5:
print(n)
n += 1
为何要用迭代器
迭代器的优点:
1.通用的迭代取值方案-->for循环
2.惰性计算,节省内存
# 惰性计算:不含有值,被需要时生成值
迭代器的缺点:
1.不能取指定的值,只能通过next往下取值
2.迭代器对象是一次性的:一个迭代器对象值取干净了就不能继续取了
如何用迭代器
粗略的使用迭代器
可迭代对象
内置有__iter__方法的对象都是可迭代对象
参数.__iter__()
通过此方法可以将参数转换为迭代器对象
迭代器对象
内置有__next__方法的对象都是迭代器对象
x = 可迭代对象.__iter__()
x.__next__()
迭代器每次运行都只按照顺序取出可迭代对象的一个值
实际上此时如果y = x.__next__(),会发现,y的可使用内置函数里也有y.__iter__(),迭代器对象同样也是可迭代对象,这个具体原因涉及到非常使用的内置循环函数for循环如何实现
带有__函数的正确使用方法
带有__的内置方法都不是给人用的
例如:需要查看'abcdefg'的长度,使用方法len('abcdefg'),实际上使用的方法是'abcdefg'.__len__()
所以可以将__iter__方法变成iter(),__next__方法变成next()
如何基础实现for循环
l = ['egon','jason','jack','tom','milu',]
# 将l列表内容系数打印
推导过程1
l_names = iter(l)
next(l_names)
next(l_names)
next(l_names)
next(l_names)
next(l_names)
next(l_names)
当迭代次数多于列表内容时,python会报出提示StopIteration
同时写了太多重复代码,有冗余
推导过程2
l_names = iter(l)
while True:
try:
print(next(l_names))
except StopIteration
break
结论
for循环工作原理类似如上代码
for i in l:
print(i)
1.调用l的__iter__方法,得到一个迭代器对象iter_l
2.x=next(iter_l),然后运行循环体代码
3.重复步骤2,直到取完值,抛出异常,for循环会自动捕捉异常结束循环
拓展
文件不仅是可迭代对象,同时也是迭代器对象,主要是因为文件通常过于庞大,在py开发初期考虑到了这个问题,如果在程序内打开文件内容,会直接将内存撑爆,所以直接将其导入时变成迭代器对象,减少计算机负担
既然文件是迭代器对象,那么在for循环中是否有必要添加一个判断,判断目标内容是否是迭代器对象,如果是则不用运行iter()代码?
没有必要,这是明显的代码冗余,py开发时直接将iter(迭代器)=迭代器,就可以省下这步操作,使for循环代码更加精简
生产器(自定义迭代器对象)
yield
yield 对比 return
相同点:都可以返回任意类型、任意个数的值
不同点:return只能返回值一次,函数立即结束
yield可以返回多次值,返回值时暂停函数的运行,下一次调用函数时从暂停处继续运行
def func():
print('1')
yield 0
print('2')
yield 5
print('3')
yield 10
函数内但凡出现yield关键字,哪怕是没有运行到,调用函数时就不会立即执行函数体代码,而是会返回一个生成器对象
生成器对象本质就是自定义的迭代器,需要时yield返回值同时暂停函数,下一次再调用函数从暂停处继续运行
函数内出现yield,此时函数并不是生成器对象,需要调用函数后才会得到生成器对象
如何使用生成器
如何使用迭代器就如何使用生成器,在next生成器对象后,函数才会开始执行代码,直到出现yield关键字后暂停运行,并返回yield的值
# 生成器的简单使用
def func():
res = 0
while True:
yield res
res += 1
g = func()
for i in g:
print(i)
# 无限打印在上一个数的基础上增加1的值
生成器用途
购物软件
在购物时页面不会直接生成全部的商品,在用户需要时才一个一个输出,发挥生成器惰性运算的优点
函数递归
什么是函数递归
函数递归是函数嵌套调用的一种特殊格式
即在调用一个函数时,在其内部又调用了自己
# python中不会允许无限死循环的函数递归
def foo():
print('hello world!')
foo()
foo()
# 函数在运行时会持续运行到出现return或者函数末尾,以上代码在运行到foo()时
函数的递归应该分为两个阶段
1.回溯:一层一层往下调用
2.递推:一层一层向上推出结果
为何要用函数递归
函数递归提供了一种基于函数实现的新的循环机制
如何用函数递归
例子1:
l = [11,[22,[33,[44,[55,[66,[77,[88]]]]]]]]
# 需求:将列表l内的所有值遍历出来
for item in l:
if type(item) is list:
pass # 将此处再次运行整行代码,进行无限套娃
else:
print(item)
# 修改后
def func(i):
for item in i:
if type(item) is list:
func(item)
else:
print(item)
func(l)
例子2:递归实现二分法
# 需求:以可控的效率在数据中查找到需要找到的值
二分法:从数据的中间开始查找,同时进行判断需要的值与当前值的大小,再向需要的值的方向继续从中间开始查找,循环往复
nums = [1,5,8,16,42,66,74,88,90,99]
def search(nums,find_num):
if len(nums) == 0:
print('not find')
return
mid = len(nums)//2
if find_num > nums[mid]:
search(nums[mid+1:],find_num)
elif finde_num < nums[mid]:
search(nums[:mid],find_num)
else:
print('find it')
拓展知识
三元表达式
x = 10
y = 20
res = x if x > y else y
条件成立返回x,条件不成立返回y
大概流程:条件成立返回的值 if 条件 else 条件不成立返回的值
列表生成式
l = []
for i in rango(10):
if i > 3:
l.append(i)
l = [i for i in range(10)]
将i遍历range(10),将每个i放进l列表
大概流程:需要放进列表的值 for循环
l = [i for i in range(10) if i > 3]
将i遍历range(10),每次运行for循环进行if判断,成立后将每个i放进l列表
大概流程:需要放进列表的值 for循环 if条件
最多只能添加if判断,不能再添加else
字典生成式(不常用)
了解即可
{'k%s'%i:i for i in range(3)}
将i遍历range(3),将每个i放进字典
集合生成式同理
{i for i in range(3)}
将i遍历range(3),将每个i放进集合
sum
sum(可迭代对象)
sum可以将里面的值全部加在一起
print(sum([1,2,3,4])) # 输出10
生成器表达式
res = (i for i in range(10))
res是一个生成器,运行时它不会产生任何值,只有在你需要它时才会依次生成值
例如:next(res),sum(res)
例子:
# 需求:数出文件内有多少个值
with open('a.txt',mode='rt',encoding='utf-8')as f:
res = sum((len(i) for i in f))
print(res)