一、迭代器
1、什么是迭代
1.1 迭代是一个重复的过程,每一次重复都是基于上一次的结果而进行的。
1.2 单纯的重复并不是迭代
2、为什么要用迭代器
1、列表,元组,字符串都可以依靠索引把值取出来,而一些数据类型没有索引,那么如何取出里面的值呢? --这里就要用到迭代器!
2、迭代器就是一种可以不依赖索引的迭代取值方式!
二、迭代器分为:可迭代对象和不可迭代对象
2.1、可迭代的对象(数据类型)如下: 在python中,但凡带内置有__iter__()方法的数据类型(或者对象),都是可迭代的对象!
1.列表类型
2.元组类型
3.集合类型
4.字典类型
5.文本类型(文本类型本身就是迭代器对象"具有__next__()"方法)
2.2、不可迭代的对象(数据类型)如下:
1.整数类型
2.浮点数类型
二、迭代器对象 (通过调用可迭代对象拿到迭代器对象)
2.1 如何拿到一个迭代器对象?
执行可迭代对象下的__iter__()方法,得到的返回值就是一个迭代器对象!
c=['a','b'] #列表类型
res=c.__iter__()
print(res)
>>:<list_iterator object at 0x0000017A993592E8>
三、使用迭代器对象 迭代器对象有,内置的__next__()方法 ,同时具有__iter__()方法 (执行该方法得到的仍然是迭代器本身)
3.1、使用迭代器对象的__next__()方法,就可以取出列表内的值,这就是通过迭代器取值。(不依赖索引取值!!)
例子1:使用迭代器取出字典里面的key和value
dic={'aaa':123,'bbb':456}
res_dic=dic.__iter__()
迭代获取字典里面的key
print(next(res_dic)) #使用迭代器对象内置__next__方法,可以取出迭代器的值(取第1个key)
print(next(res_dic)) #使用迭代器对象内置__next__方法,可以取出迭代器的值(取第2个key)
aaa
bbb
迭代获取字典里面的value
print(dic[next(res_dic)]) #使用迭代器对象内置__next__方法,可以取出迭代器的值(取第1个value)
print(dic[next(res_dic)]) #使用迭代器对象内置__next__方法,可以取出迭代器的值(取第2个value)
123
456
例子2:使用迭代器取出文件里面的内容
f=open('a.txt','rt',encoding='utf-8')
iter_obj=f.__iter__()
print(iter_obj.__next__(),end='') #end='' 表示设置换行符为空
print(iter_obj.__next__(),end='') #end='' 表示设置换行符为空
print(iter_obj.__next__(),end='') #end='' 表示设置换行符为空
print(iter_obj.__next__(),end='') #end='' 表示设置换行符为空
>>:111
>>:22222
>>:33333333
>>:44444444444
3.2、 当字典内的值被取光了,就会报错"StopIteration" (这是一个结束信号,表示迭代器的值被取完了),如何解决?
1、使用try+except StopIteration的方式
dic={'k1':1,'k2':1,'k3':1}
res=dic.__iter__()
while True:
try:
print(res.__next__()) #使用迭代器对象内置__next__方法,可以取出迭代器的值(取第1个)
except StopIteration:
break
k1
k2
k3
四、总结迭代器对象
1、可迭代对象不一定是迭代器对象!
1.列表类型 2.元组类型 3.集合类型 4.字典类型 都不具备__next__()方法。
2、迭代器对象一定是可迭代对象!
1.文本类型(文本类型本身就是迭代器对象"具有__next__()"方法)具备__iter__()方法,同时也具备__next__()方法。
3、总结迭代器的优缺点
缺点:1、只能往后取值,不能往前取值,是一次性的,值取干净后无法再次取值,除非重新得到新的迭代器对象。
优点:1、提供了一种不依赖于索引的迭代取值方式 2、节省内存
五、for 循环的底层运行机制 for循环也叫做迭代器循环
5.1、能被for循环,循环的对象,它一定是可迭代对象!
dic={'k1':1,'k2':1,'k3':1}
for k in dic:
print(k)
for循环的机制:
1、先调用in后那个对象的__iter__()方法,得到该对象的迭代器对象: ### iter_obj=dic.__iter__()
2、执行迭代器对象的__next__()方法,将得到的返回值赋值给in前面的变量名,然后执行一次循环体代码: ### iter_obj.__next__()
3、循环往复,直到把dic里面的值全部取完,然后自动捕捉"StopIteration"这个异常,结束循环。
六、生成器 (学习生成器是为了掌握一种自定义迭代器的方式)
6.1、什么是生成器 ?
1、在函数内但凡有yield关键字,在调用函数时就不会执行函数体内的任何代码,得到的返回值就是一个生成器对象[一串内存地址](生成器的本质就是迭代器)。
2、生成器本身就是迭代器。
def func():
print('11111')
yield 1
print('22222')
print('33333')
res=func()
print(res)
>>:<generator object func at 0x00000204A8F51B48>
6.2、使用生成器 -- 用yield来控制返回值(yield和函数内的return很相似),取到想要的值
nxet(res)过程:
会触发生成器res所对应的函数体代码的运行,直到遇到yield关键字,把yield后的返回值当做本次next操作的结果返回
def func():
print('11111')
yield 1 # 函数体代码运行到此处时会暂停,把yield之前运行的代码以及yield后面的1作为返回值返回
print('22222')
yield 2 # 函数体代码运行到此处时会暂停,把yield之前运行的代码以及yield后面的2作为返回值返回
print('33333')
res=func()
print(next(res)) # 执行next(res)获取到的值为"11111",然后继续执行代码遇到了yield,那么就把yield后面的1作为本次结果的返回值
11111
1
print(next(res)) # 执行next(res)获取到的值为"22222",然后继续执行代码遇到了yield,那么就把yield后面的2作为本次结果的返回值
22222
2
print(next(res))
33333
StopIteration # 由于函数内的"print('33333')"这行代码之后无yield关键字,那么next(res)在取到'33333'这个值之后,没有其他值可以取了,就会抛出一个StopIteration异常。
for n in res:
print(n)
11111 # 这是执行函数体代码的结果
1 # 这是for循环变量n的值
22222 # 这是执行函数体代码的结果
2 # 这是for循环变量n的值
33333 # 这是执行函数体代码的结果,由于后面并没有yield关键字,就会抛出一个StopIteration异常。
过程解析:
执行for循环相当于执行了:res.__next__() == next(res):
1、 首先next(res)会执行遇到yield之前的代码print('11111'),然后拿到"yield 1"把1最为本次结果的返回值赋值给n,然后打印n,那么第一次循环结束。
2、然后执行next(res),执行函数内"yield 1"之后的代码,直至遇到下一个"yield"关键字。
小例子:自定义函数模拟range(1,10,2)
def my_range(start,stop,step=1):
print('开始')
while start < stop:
yield start
start+=step
print('结束')
for n in my_range(1,10,2):
print(n)
开始
1
3
5
7
9
结束
6.3、生成器如何运行?
1、首先要明白生成器本身就是迭代器,那么运行生成器就可以按照迭代器的方式去运行!
2、运行生成器就会触发迭代器所对应的代码,并开始运行迭代器的代码。
3、运行迭代器的代码时,只要碰到yield,代码就会停止运行,并拿到yield 的返回值。
6.4、yield关键字的另外一种使用形式:表达式形式的yield应用
1、先让函数运行起来,然后暂停到某一处,然后传一个值运行一次代码,传一个值运行一次代码。
通过:生成器.send的方法给yield 传值,例子如下:[执行g.send就相当于给yield传值,同时执行一次next(g)]
def dog(dog_name):
print('狗准备开吃!')
food_list=[]
while True:
print(1)
food=yield food_list # food=yield='骨头'
print('狗:%s,吃了%s' %(dog_name,food))
food_list.append(food)
print(2)
g=dog('doudou') #拿到一个迭代器
next(g) #next(g)==g.send(None),开始运行函数体代码,next(res)==res.__next__()
#强调:对于表达式形式的yield的生成器,在使用前必须先用next(g)或者g.send(None)初始化一次。
res2=g.send('骨头')
print(res2)
res3=g.send('骨头1')
print(res3)
狗准备开吃
1
[]
dog: doudou 吃了 骨头
2
1
['骨头']
dog: doudou 吃了 骨头1
2
1
['骨头', '骨头1']
注释:
1、yield后面的 food_list 在没有没传值的情况下,默认为None。
2、执行一次g.send('骨头')后,会把'骨头'传值给 yield ,并执行一次"yield"关键字之后的函数体代码,直到遇见下一个“yield”为止。
3、如上例子,当g.send('骨头')后,循环一遍结束,直到遇见一个新的yield为止。此时 food_list 的值就为None(默认值)。
七、面向过程编程
1、什么是面向过程编程?
核心是过程二字,过程指的是解决问题的步骤,即先干什么,在干什么,在干什么。。。。
基于面向过程的思想,编写程序就好比在设计一条流水线,是一种机械式思维方式。
优点:将复杂的问题流程化,进而简单化。
缺点:修改了某一个阶段,其他相关联的阶段都有可能受到影响,牵一发而动全身,扩展性极差。
应用:应用于对扩展性要求不高的场景。
面向过程编程小例子:把要做的事情拆分为一件件小的事情,然后完成。
def login():
uname=input('pls input uname>>:').strip()
pwd=input('pls input pwd>>:').strip()
return uname,pwd
def auth(uname,pwd):
if uname == 'szq' and pwd == '123':
return True,uname
else:
return False,uname
def index():
if res[0]:
print('欢迎%s登录' %res[1])
else:
print('%s登录失败' % res[1])
uname,pwd=login()
res=auth(uname,pwd)
index()
八、三元表达式
x=10
y=30
res=x if x >y else y #如果x的值大于y的值,那么返回x,否则返回y的值。
print(res)
30
九、列表生成式,字典生成式,生成器表达式
当数据量不多的时候,用列表表达式
1、列表生成式 -- 直接将产生的结果放到列表里面
list=[n for n in range(10)] # 每一次for循环取到的值,都会赋值给给左边的n,然后放在列表list里面,然后接着下一次循环
print(list)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for 循环后接if判断
list=[n for n in range(10) if n > 3] # 每一次for循环取到的值(然后执行右边的if判断,符合要求的值),都会赋值给给左边的n,然后放在列表list里面,然后接着下一次循环
print(list)
[4, 5, 6, 7, 8, 9]
例子1:将小写全部改为大写
names=['aaa','bbb','ccc','ddd']
names=[name.upper() for name in names]
print(names)
['AAA', 'BBB', 'CCC', 'DDD']
例子2:将列表内以'sb'结尾的字符去除,并统计剩下字符的长度
names=['aaa','bbb_sb','ccc_sb','ddd']
names=[len(name) for name in names if not name.endswith('sb')]
print(names)
[3, 3]
2、字典生成式
例子:将集合里面的元组改为key:value的形式
dic={('aaa','123'),('bbb','223'),('ccc','323')}
dic={k:y for k,y in dic}
print(dic)
{'aaa': '123', 'ccc': '323', 'bbb': '223'}
3、生成器表达式 -- 拿到一个生成器对象,需要next(g)一次取一次值。
当数据量特别多的时候用,用生成器表达式
l=(n for n in range(10))
print(next(l))
print(next(l))
print(next(l))
0
1
2