迭代器
1.迭代器及相关概念
迭代器:是用来迭代取值的工具
迭代:是一个重复的过程,但每次的重复都是基于上一次的结果进行的,单纯的重复不叫迭代,如例1,类似于例2才是迭代的过程
# 例1
while True:
print(1+1)
# 例2
count = 1
while True:
print(count)
count += 1
可迭代对象:内置有__iter__方法的对象都是可迭代对象,例如字符串、列表、元组、字典、集合以及文件,都有内置__iter__方法,都为可迭代对象,整型和浮点型不是可迭代对象
'richer'.__iter__()
[1, 2, 3].__iter__()
(1, 2, 3).__iter__()
{'x':1, 'y':2}.__iter__()
{1, 2, 3}.__iter__()
open('a.txt',mode='r').__iter__()
迭代器对象:调用可迭代对象的__iter__()会得到一个返回值,该返回值便是迭代器对象
特点:内置有__iter__()和__next__()方法,例如文件,既是可迭代对象,又是迭代器对象
ps:可迭代对象不一定是迭代器对象,但迭代器对象一定是可迭代对象
open('a.txt',mode='r').__iter__()
open('a.txt',mode='r').__next__()
2.为什么要用迭代器
names = ['richer', 'lili', 'jack', 'kiki', 'tom']
# 需求,将列表中的元素逐个取出,不用for循环的情况下
i = 0
while i < len(names):
print(names[i])
i += 1
# 但while True迭代取值只适用于索引类型取值,不适用于字典、集合等非索引类型
因此使用迭代器取值
优点:
1)是一种通用的迭代取值的方案 -> 同for循环,又称迭代循环
for 循环的工作原理
names = ['richer', 'lili', 'jack', 'kiki', 'tom']
# names = {"x":1,"y":2,'z':3} # 字典迭代取的值是key
# names.__len__()可以写成len(names),同理names.__iter__() -> iter(names)
iter_names = iter(names) # 可迭代对象转换未迭代器对象
while True:
# 单纯while True取完值后会报错,避免没有匹配到异常,又中断程序,用try/except
try: # 捕获程序在执行时遇到的异常情况,即一些报错。
print(next(iter_names)) # iter_names.__next__()->next(iter_names)
except StopIteration: # 发现StopIteration错误时继续向下执行
break
i.调用in后的对象__iter__方法,得到一个迭代对象的iter_names
ii.next(iter_names)赋值打印,然后运行循环体代码
iii.重复步骤2,直到取完值,抛出异常,for循环会帮我们捕捉异常结束循环
2)惰性计算,节省内存,例如python3种的range
缺点:
1)不能取指定的值,只能往后next
s = "abc"
iter_s = s.__iter__() # iter_s = iter(s) # 可迭代对象转换未迭代器对象
print(iter_s.__next__()) # print(next(ites_s)) a
print(iter_s.__next__()) # print(next(ites_s)) b
print(iter_s.__next__()) # print(next(ites_s)) c
print(iter_s.__next__()) # 报错 StopIteration
dic = {"x":1,"y":2}
iter_dic = ter(dic) # 可迭代对象转换未迭代器对象
print(next(iter_dic)) # x
print(next(iter_dic)) # y
2)迭代器对象是一次性的:一个迭代器对象值去干净后,不能继续取,继续取,报错如1或不出值(for循环)
names = ['richer', 'lili', 'jack', 'kiki', 'tom']
iter_names = iter(names) # 可迭代对象转换未迭代器对象
for x in iter_names :
print(x)
print('='*50)
for x in iter_names :
print(x)
输出结果:
egon
tom
jack
lili
ttt
==================================================
3、如何用迭代器
1)使用__iter__()或iter(),将可迭代对象转换成迭代器对象
2)使用__next__()或next(),运行代码
生成器
生成器:自定义迭代器
l=[1,2,3]
res=iter(l)
print(next(res)) # 1
print(res) # <list_iterator object at 0x000001BA60D46BB0> 返回iterator迭代器内存地址 iterable 可迭代对象
在函数中迭代取值需要返回值时
def func():
print('one')
return 1
print('two')
return 2
print('three')
return 3
res = func()
print(res) # one 1 return只运行一次就结束了函数
为了返回多次值,用到yeild
def func():
print('one')
yeild 1
print('two')
yeild 2
print('three')
yeild 3
print('four')
函数内但凡出现yeild关键字,再调用函数不会立即执行函数代码体,而是会返回一个生成器对象,生成器对象本质就是一个自定义的迭代器对象
调用函数,得到返回操作如下:
g = func()
# g.__iter__()
# g.__next__()
print(g) # one <generator object func at 0x000002145B28D820> generator生成器
res = next(g)
print(res) # one 1
res = next(g)
print(res) # two 2
res = next(g)
print(res) # three 3
next(g) # four 报错 StopIteration,会继续执行代码,但无返回值
函数递归
1.什么是函数递归
函数递归是函数嵌套调用的一种特殊格式,即再调用一个函数时,在其内部又调用了自己,即循环
def foo():
print('from foo')
foo()
# 循环调用是有层数限制的,限制为1000层,可以修改,但不建议
# import sys
# print(sys.getrecursionlimit()) # 1000
# sys.setrecursionlimit(2000)
foo()
递归分为两个阶段
1)回溯:溯源,寻根问底,一层一层往下调
2)递推:得到源头后,一层一层往上推出结果
2.为何要用函数递归
为循环机制提供了一种基于函数实现的新的方式
3.如何用函数递归
# 用函数得出age(5)的结果
# age(5)=age(4)+10
# age(4)=age(3)+10
# age(3)=age(2)+10
# age(2)=age(1)+10
# age(1)=18
# 进一步思考
# n > 1: age(n) = age(n-1)
# n == 1: age(1) = 18
# 函数实现
def age(n):
if n > 1:
return age(n-1) + 10
# else:
# return 18
return 18 # 两行成一行
res = age(5)
print(res) # 58
# 回溯
# 第一次:n=5
# age(5) = age(4) + 10
# 第二次:n=4
# age(4) = age(3) + 10
# 第三次:n=3
# age(3) = age(2) + 10
# 第四次:n=2
# age(2) = age(1) + 10
# 第五次:n=1
# age(1) = 18
# 例1
# 需求:取出非列表和元组的值
l = [1,[2,[3,(4,(5,[6,[7,(8,(9,[10]))]]))]]]
def func(l):
for i in l:
if type(i) is list:
func(i)
elif type(i) is tuple:
func(i)
else:
print(i)
func(l)
# 例2:递归实现二分法
nums = [3, 7, 9, 13, 21, 33, 57, 63, 98]
def catch(nums, find_num):
print(nums)
if len(nums) == 0: # 要找的值没在列表中,则二分到最后为空列表
print('Blank')
return
mid = len(nums) // 2 # // 二分
if find_num > nums[mid]:
# 找右半部分
catch(nums[mid+1:], find_num)
elif find_num < nums[mid]:
# 找左半部分
catch(nums[:mid],find_num) # [:mid] 顾头不顾尾
else:
print('find it')
catch(nums, 33)
输出结果:
[3, 7, 9, 13, 21, 33, 57, 63, 78, 99]
find it
catch(nums, 35)
[3, 7, 9, 13, 21, 33, 57, 63, 78, 99]
[57, 63, 78, 99]
[57, 63]
[57]
[]
not exists
三元表达式
# 可以将多条代码写成一条,简化代码,也不丧失可读性,但只能是if else这种双分支结构或两个条件的简单代码,复杂的不建议写成多元表达式
书写方式
变量名 = 条件成立时的返回值 if 条件 else 条件不成立时的返回值
res = 'ok' if 'a' != 'b' else 'no'
# 例子:将下列代码写成三元表达式
x = 10
y = 20
if x > y:
return x
else:
return y
# 实现
res = x if x > y else y
print(res)
生成式
1.列表生成式:映射新列表,根据条件筛选出新列表
l = []
# for i in range(10):
# if i > 3:
# l.append(i)
l = [i for i in range(10) if i > 3]
print(l) # [4, 5, 6, 7, 8, 9]
names = ['Zhangsan', 'Lisi', 'Wangmazi', 'Zhangquandan', 'Zhangdapao']
# 需求:将姓Zhang的提取出来
res = [name for name in names if name.startswith('Zhang')]
print(res) # ['Zhangsan', 'Zhangquandan', 'Zhangdapao']
# 需求:将names 这个列表中的元素都加上'_NB'
res = [name + '_NB' for name in names]
print(res)
输出结果:
['Zhangsan_NB', 'Lisi_NB', 'Wangmazi_NB', 'Zhangquandan_NB', 'Zhangdapao_NB']
2.字典生成式
dic = {f'{i}': i for i in range(3)}
print(dic) # {'0': 0, '1': 1, '2': 2}
3.集合生成式
set = {i for i in range(3)}
print(set) # {0, 1, 2}
4.生成器表达式
res = (i for i in range(3))
print(res) #<generator object <genexpr> at 0x000001FBA78ED820> 输出的是生成器,而非元组
print(next(res)) # 0
print(next(res)) # 1
print(next(res)) # 2
# 若求文件中字符总数
with open('a.txt', mode='rt', encoding='utf8') as f:
res = f.read() # 如果文件小,可以用这种方法一次性读出来
print(len(res))
# 如果文件较大,不适宜一次性读出,应当一行一行读出再求和
res = 0
for line in f:
res += len(line) # 每一行字符累加
将上面三行代码一行搞定
# res = sum([len(line) for line in f])
# 如果文件很大,有多行,该法也就不合适了,因此将中括号改为小括号,将它做成生成器
# res = sum((len(line) for line in f)) # python 语法sum可以将(len(line) for line in f)默认为生成器,因此可以去掉一层括号
res = sum(len(line) for line in f)