迭代器 for循环的本质 生成器
迭代器
迭代:更新换代的过程,每次的迭代都必须基于上一次的结果
迭代器:迭代取值的工具
作用
迭代器提供了一种不依赖于索引取值的方式
根据以上对于迭代的描述,如果只是简单的重复,不算迭代,如下:
n = 0 while True: print(n) # 这只是简单重复,不算迭代
所以,迭代应该是“重复+每次迭代都是基于上一次的结果”
l = [1,2,3,4] s = 'hello' n = 0 while n < len(s): print(s[n]) n += 1
可迭代对象
内置有__iter__()方法的都叫做可迭代对象
# 对于这种 双下划线开头 双下划线结尾的方法
# 我们读作“双下+方法名”
基本数据类型中,是可迭代对象的有:字符串(str)、列表(list)、元组(tuple)、字典(dict)、集合(set)
另外 文件对象(file) 是可迭代对象,也是迭代器对象
str0 = 'bitten' str0.__iter__() print(type(str0)) # 字符串 list0=[1,2,3,] list0.__iter__() print(type(list0)) # 列表 tuple0 = (1,2,3) tuple0.__iter__() print(type(tuple0)) # 元组 dict0={'name':'bitten'} dict0.__iter__() print(type(dict0)) # 字典 set0={1,2,354,} set0.__iter__() print(type(set0)) # 集合
f1 = open('xxx.txt','w',encoding='utf-8') res = f1.__iter__().__iter__().__iter__().__iter__() print(f1 is res) # True # 文件对象(执行内置的__iter__之后还是本身 没有任何变化):文件对象本身就是迭代器对象
迭代器对象
可迭代对象执行内置的__iter__方法得到的就是该对象的迭代器对象
迭代器对象需要满足的条件:
1.内置有__iter__方法
2.内置有__next__方法
ps:迭代器一定是可迭代对象
而可迭代对象不一定是迭代器对象
迭代器对象取值方法:
必须调用__next__()方法
l = [1,2,3,4] # 生成一个迭代器对象 iter_l = l.__iter__() # 迭代器取值 调用__next__ print(iter_l.__next__()) # 1 print(iter_l.__next__()) # 2 print(iter_l.__next__()) # 3 print(iter_l.__next__()) # 4 print(iter_l.__next__()) # 如果取完了 直接报错StopIteration
异常处理 #把上面这五行print改成下面的形式 # while True: # try: # print(iter_l.__next__()) # except StopIteration: # # print('值已近取完啦') # break
迭代器对象无论执行多少次__iter__()方法得到的还是迭代器对象本身
ps:文件对象作为迭代器对象,还内置了__iter__()
因为for循环后面in(如果迭代器对象没有__iter__)
是因为只有使用了__iter__()方法才能继续用__next__()取值
迭代器取值的特点
1. 只能往后依次取 不能返回取前面的值
for循环的本质
d = {'name':'bitten','password':'123','hobby':'hiking'} for i in d: print(i) # for循环后面的in关键 跟的是一个可迭代对象
for循环内部的本质
1.将in后面的对象调用__iter__转换成可迭代对象
上面👆这条就可以解释为什么文件对象是迭代器对象但是还是内置有__iter__方法
因为如果没有的话,在执行上面👆👆的第1.步的时候就会报错。
2.将这个可迭代对象,作为迭代器对象,调用__next__迭代取值
3.内部有异常捕获StopIteration,当__next__报这个错 自动结束循环
迭代取值:
优点
1.不依赖于索引取值
2.内存中永远只占一份空间,不会导致内存溢出
缺点
1.不能够获取指定的元素
2.取完之后会报StopIteration错
生成器
即用户自定义的迭代器
def func(): print('first') yield 666 # 函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行 print('second') yield 777 print('third') yield 888 print('forth') yield yield # yield后面跟的值就是调用迭代器__next__方法你能得到的值 # yield既可以返回一个值也可以返回多个值 并且多个值也是按照元组的形式返回
def func(): print('first') yield 666 # 函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行 print('second') yield 777 print('third') yield 888 print('forth') yield yield # yield后面跟的值就是调用迭代器__next__方法你能得到的值 # yield既可以返回一个值也可以返回多个值 并且多个值也是按照元组的形式返回
g = func() # 生成器初始化:将函数变成迭代器 print(g) print(g.__next__()) print(g.__next__()) print(g.__next__()) print(g.__next__()) print(g.__next__())
用生成器写range函数
for i in range(1,10,2): print(i) #1 3 5 7 9
def my_range(start,end,step=1): while start < end: yield start start += step # 这只是完成了range的功能中的一部分 for j in my_range(1,100,2): print(j)
补充(yield的表达式形式)(了解)
def dog(name): print('%s 准备开吃'%name) while True: food = yield print('%s 吃了 %s'%(name,food)) # 当函数内有yield关键字的时候,调用该函数不会执行函数体代码 # 而是将函数变成生成器 g = dog('lucky') g.__next__() # 必须先将代码运行至yield 才能够为其传值 g.send('狗不理包子') # 给yield左边的变量传参 触发了__next__方法 g.send('饺子') g.send('肉夹馍') >>>>>> # lucky 准备开吃 # lucky 吃了 狗不理包子 # lucky 吃了 饺子 # lucky 吃了 肉夹馍
yield
1.帮你提供了一种自定义生成器方式(自定义生成器必须要有yield)
2.会帮你将函数的运行状态暂停住
3.可以返回值
与return之间异同点
相同点:都可以返回值,并且都可以返回多个
不同点:
yield可以返回多次值,而return只能返回一次函数立即结束
yield还可以接受外部传入的值
生成器表达式
列表生成式
res = [i for i in range(1,10) if i != 4] print(res)
>>>
[1, 2, 3, 5, 6, 7, 8, 9]
生成器表达式
res = (i for i in range(1,10) if i != 4) # 注意这里括号是()不是[] print(res) # <generator object <genexpr> at 0x101396eb8> # 老母鸡
# 生成器不会主动执行任何一行代码
# 必须通过__next__触发代码的运行
res = (i for i in range(1,10) if i != 4) # 注意这里括号是()不是[] print(res) # <generator object <genexpr> at 0x101396eb8> # 老母鸡 print(res.__next__()) # 1 print(res.__next__()) # 2 print(res.__next__()) # 3 print(res.__next__()) # 5
面试题1
def add(n,i): return n+i def test(): for i in range(4): yield i g=test() # 函数变成生成器 for n in [1,10]: # 遍历集合中1和10两个元素,执行两次 g=(add(n,i) for i in g) # 生成器表达式,只有调用__next__方法才能执行代码 # 第一次for循环g=(add(n,i) for i in test()) # g 生成器,不执行 # 第二次for循环g=(add(n,i) for i in (add(n,i) for i in test())) # g 生成器,不执行 print(n) res=list(g) # list的使用原理是for循环,for循环会调用__next__迭代取值,会触发生成器执行代码 # 执行第一次for循环 g=(add(n,i) for i in test()) # 循环一次后g中的值[10,11,12,13],g是一个生成器 # 执行第二次for循环g=(add(n,i) for i in g) # 循环两次后g中的值[20,21,22,23],g是一个生成器
#A. res=[10,11,12,13] #B. res=[11,12,13,14] #C. res=[20,21,22,23] 答案 #D. res=[21,22,23,24]
面试题2
def multipliers(): return [lambda x : i*x for i in range(4)] # 修改成这样就能输出想要的: return [lambda x, i=i: i*x for i in range(4)] # 0, 1, 2, 3 # [func(x): return 0*x, / func(x): return 1*x, / func(x): return 2*x, / func(x): return 3*x, ] print([m(2) for m in multipliers()]) # [func(x): return 0*2, / func(x): return 1*2, / func(x): return 2*2, / func(x): return 3*2, ] # 理想结果是 [0, 2, 4, 6] # 但现实输出的是 [6, 6, 6, 6] # 闭包函数的延迟绑定 # 在内层函数执行时才会绑定变量i
# 可以改成这样的形式,就容易理解了
def multipliers2(): list1 = [] for i in range(4): def func(x): #应该这么修改:def func(x, i=i): return x * i list1.append(func) return list1 print([m(2) for m in multipliers2()]) # 这里会给multipliers传参为2并执行
# 修改前:[6, 6, 6, 6] # 修改后:[0, 2, 4, 6]
常用的内置方法part2
################abs#######################
print(abs(-13.24)) # abs 求绝对值 ###################all和any########## l = [1,1,0] print(all(l)) # all 存在一个为False就返回False print(any(l)) # any 任意一位为True就返回True ##############local和globals############# def index(): username = '我是局部名称空间里面的username' print(locals()) # locals 返回当前所在区域存储的所有的名字 print(globals()) # globals 无论在哪 返回的都是全局名称空间 index() #################进制转换################### print(bin(10)) # bin 十进制转2进制 print(oct(10)) # oct 十进制转8进制 print(hex(10)) # hex 十进制转16进制 print(int('0b1010',2)) # 其他进制转十进制 ##############bool################### print(bool(1)) print(bool(0)) # bool判断真假 ######################encode################ s = 'hello你好' print(s.encode('utf-8')) # encode 编码 print(bytes(s,encoding='utf-8')) # 字节流 ##################callable##################### def index(): pass print(callable(index)) # callable 可调用的(可以加括号执行相应功能的) ###########chr和ord################### print(chr(65)) # chr 将数字转换成ascii码表对应的字符 print(ord('A')) # ord 将字符按照ascii表转成对应的数字 ###############dir########## print(dir(range(12))) # dir 获取当前对象名称空间里面的名字 ##################divmod################## print(divmod(101,10)) total_num,more = divmod(900,11) if more: total_num += 1 print('总页数:',total_num)# divmod 分页器 ###############enumerate#################### l = ['a','b','c','d'] for i,j in enumerate(l,1):# enumerate 枚举 print(i,j) ####################eval################ # eval exec 执行字符串表达式 s1 = """ print(1 + 2) for i in range(10): print(i) """ # eval(s1) exec(s1) name = 'jason' s2 = """ name """ print(eval(s2)) # eval不支持逻辑代码,只支持一些简单的python代码 #######################help############################# def login(): """ 一起嗨皮 :return: """ print(help(login)) # help 查看函数注释 ######################isinstance############################ print(isinstance("123",list)) # isinstance 判断对象是否属于某个数据类型 print(pow(2,3)) # 返回 x的y次方。 print(round(3.4)) # round 四舍五入,取整
面向对象编程简介
面向过程编程:就类似于设计一条流水线
好处:
将复杂的问题流程化 从而简单化
坏处:
可扩展性较差 一旦需要修改 整体都会受到影响