第三十篇 迭代器、三元表达式与列表推导式、字典生成式、生成器、递归
一、迭代器
须知
迭代器协议:一个对象支持以下两个方法就是迭代器:1.返回迭代器本身;2.返回下一个元素
1、可迭代对象(Iterable)
1.但凡有__iter__()
方法的对象,都是可迭代对象
s = str() # 字符串
s.__iter__()
l = list() # 列表
l.__iter__()
t = tuple() # 元组
t.__iter__()
d = dict() # 字典
d.__iter__()
se = set() # 集合
s.__iter__()
with open('x.txt','a',encoding='utf8') as f:
f.__iter__() # 文件
2.可迭代对象使用__iter__()
方法,返回的是一个迭代器
3.凡是可作用于for
循环的对象都是可迭代对象(Iterable)
4.迭代器(Iterator)一定是可迭代对象,可迭代对象不一定是迭代器。文件既是可迭代对象又是迭代器
5.只有字符串和列表是依赖索引取值的,而其他的可迭代对象都无法依赖索引取值,因此我们需要找到一个方法能让其他的可迭代对象不依赖索引取值
2、迭代器对象(Iterator)
1.可迭代对象执行__iter__()
方法得到的返回值,就是迭代器对象
2.迭代器内置有__next__()
方法,执行该方法会拿到迭代器对象中的一个值
3.迭代器对象通过使用__iter__()
方法得到的返回值就是它本身
4.迭代器的特点:
- 1.(惰性)省空间。迭代器并不是把所有的元素提前计算出来,而是在需要的时候才计算返回
- 2.支持无限个元素,而列表等可迭代对象就没法容纳无限个元素
- 3.迭代器对象表示的是一个数据流,它可以不断使用
__next__()
方法连续返回下一个数据,直到没有数据时抛出StopIteration
错误。可以把这个数据流看作是一个我们无法预知其长度的有序序列
5.迭代器的缺点:
- 1.取值麻烦,只能一个一个取,并且只能往后取,值取了就没了
- 2.无法使用len()方法获取长度
3、for循环原理
1.for 循环称为迭代器循环,in后面必须是可迭代对象
2.因为迭代器使用__iter__()
后还是迭代器本身,因此for循环也可以用于迭代器
3.总结:for循环本质是将可迭代对象变成迭代器,然后用__next__()
方法将容器中的值一个一个取出,直到没有值然后捕捉异常,终止循环
二、三元表达式与列表推导式
1.三元表达式
格式:条件成立时返回值 if 条件 else 条件不成立时的返回值
x = 1
print(f'x if x == 0 else 666)
2. 列表推导式
格式:[i for i in 可迭代对象] 最前面的i是表达式,中间的i是逐个元素
三、字典生成式
1.格式:{ i:i*2 for i in range(8) }
2.zip()方法:
- 1.zip()函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表
- 2.格式:zip([iterable,......]) 一个或多个可迭代对象
keys = ['name','age','gender']
values = ['liu',20,'male']
res = zip(keys,values)
print(type(res)) # zip
for i in res:
print(i)
'''
('name', 'liu')
('age', 20)
('gender', 'male')
'''
l1 = ['name','age','gender']
l2 = ['liu',20,'male']
dic = dict(zip(l1,l2))
四、生成器(Generator)
1.通过列表生成式,我们可以直接创建一个列表,但受内存限制,列表容量肯定是有限的,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素,而不必创建完整的列表,节省空间,在python中,这种一边循环一边计算的机制,称为生成器
2.yield的意思是生产,在函数中但凡出现yield关键字,再调用函数,就不会继续执行函数体代码,而是返回一个值
def f():
yield 1
yield 2
f_iterator = f() # 用yield接收返回值的函数,在调用时会变成生成器
for i in iterator:
print(item) # 1 2
3,yield:1.yield提供了一种自定义迭代器的方法,2.yield可以暂停住函数,并提供当前的返回值
def my_range(*args):
start = 0
step = 1
if (len(args)) == 1:
end = args[0]
elif len(args) == 2:
start = args[0]
end = args[1]
elif len(args) == 3:
start = args[0]
end = args[1]
step = args[2]
else:
raise('超出参数个数')
while start < end:
print(start)
start += step
4.生成器的本质就是迭代器,但又不限于迭代器,且生成器提供了非常方便的自定义迭代器的途径
5.要创建一个生成器,有多种方法:
- 1.生成器表达式。把一个列表生成式中的 [] 换成 () 即可
l = [x*x for x in rang(10)] # 一筐鸡蛋
print(type(l))
G = (x*x for x in rang(10)) # 一只老母鸡
print(type(G))
- 2.用yield返回函数的值(如果一个函数定义中包含yield关键字,那么这个函数就不是一个普通的函数,而是一个生成器)
def fib(n):
c,a,b = 0,0,1
while c < n:
yield b
a,b = b,a+b
n += 1
6.yield和return的异同
- 相同:两者都是在函数内部使用,都可以返回值,并且没有类型和个数限制
- 不同:return只能返回一次,yield可以返回多次
五、递归
1.递归是一种特殊的嵌套使用,它在调用函数的过程中,会直接或间接的调用自己
2.递归:直接调用或间接调用自身
- 1.直接调用
def f(n):
if n < 2:
print(n)
else:
f(n-1)
- 2.间接调用
def f1():
f2()
def f2():
f1()
3.递归必须要有两个明确的阶段:
- 1.递推:一层一层递归调用下去,进入下一层递归的问题规模将会减小
- 2.回溯:递归必须要有一个明确的结束条件,在满足该条件后开始一层一层回溯
递归的精髓在于通过不断的重复逼近一个最终的结果