迭代器,生成器,面向过程编程思想
今日内容:
一、迭代器
二、生成器
三、面向过程的编程思想
一、迭代器:
迭代器基础:
#1.迭代是个什么概念: #迭代器重点在迭代二字,器是工具的意思, #迭代是一个重复的过程,每次执行重复过程就是一次迭代,并且每次迭代的结#果都是下一迭代的初始值 #有人说重复的过程不就可以用,while或者for循环来搞定么? #while循环只是单纯的循环,因而不是迭代 #而for循环其实底层是用的迭代的方式去实现for循环的功能 2. 为什么要用迭代器 对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器 3. 怎么用迭代器 可迭代的对象? 在python中,但凡内置有__iter__方法的对象,都是可迭代的对象 迭代器对象? 执行可迭代对象下__iter__方法得到的返回值就是一个迭代器对象 迭代器对象内置__next__方法, # l = ['a', 'b', 'c', 'b'] # # iter_obj = l.__iter__() # print (iter_obj) 也内置__iter__方法,执行该方法得到的仍然是迭代器本身,之所以有个iter方法是为了顺应for循环的工作机制 f=open('aa.py', rb) 文件类型本身就是一个迭代器对象 注意: 迭代器对象一定是可迭代的对象 可迭代对象不一定是迭代器对象 总结迭代器的特点 1. 是一次性的,值取干净后就无法再次取值,除非重新得到新的迭代器对象 不如按照索引取值的方式灵活 值取不干净,永远无法预测迭代器的长度 2. 提供了一种不依赖索引的迭代取值方式 3. 更加的节省内存
4.迭代器的优缺点:
#优点: - 提供一种统一的、不依赖于索引的迭代方式 - 惰性计算,节省内存 #缺点: - 无法获取长度(只有在next完毕才知道到底有几个值) - 一次性的,只能往后走,不能往前退
迭代对象的使用:
dic={'a':1,'b':2,'c':3}
iter_dic=dic.__iter__() #得到迭代器对象,迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身,这个是为了符合for的工作机制,for循环其实是把循环体执行__iter__转化为迭代器对象,万一循环的是一个迭代器对象呢,所以迭代器也有__iter__方法
iter_dic.__iter__() is iter_dic #True
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
# print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志
#有了迭代器,我们就可以不依赖索引迭代取值了
dic = {'k1': 1, 'k2': 2, 'k3': 3}
iter_obj = dic.__iter__()
while True:
try:
print(next(iter_obj))
except StopIteration:
break
for循环机制介绍:
#基于for循环,我们可以完全不再依赖索引去取值了 dic={'a':1,'b':2,'c':3} for k in dic: print(dic[k]) #for循环的工作原理 #1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic #2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码 #3: 重复过程2,直到捕捉到异常StopIteration,结束循环
二、生成器:
生成器基本概念:
1. 什么是生成器 在函数内但凡有yield关键字,在调用函数就不会执行函数体代码,得到返回值就是一个生成器对象 def func(): print('====>first') yield 1 print('====>second') yield 2 print('====>third') yield 3 print('====>end') g=func() print(g) #<generator object func at 0x0000000002184360> 强调:生成器本身就是自定义的迭代器 g.__iter__ g.__next__ #所以生成器就是迭代器,因此可以这么取值 res=next(g) print(res) next(g)的过程: 会触发生成器g所对应的函数的执行,知道遇到yield才停下来,然后把yield后的返回值当做本次next操作结果,如果没有返回值默认返回None 2. 为何要用生成器: 学习生成器是为了掌握一种自定义迭代器的方式 3. 总结yield: 1. yield提供一种自定义迭代器的方式 2. 与return对比,都能返回值,都能返回多个值,都没有类型限制,而return只能返回一次值,yield可以返回多次值,(yield可以帮我们保存函数的执行状态) 4. yield关键字表达式形式的应用 x = yield
生成器练习:
1.自定义函数模拟range(1.7.2)
2.模拟管道,实现功能:tailf access.log | grep '404'
#题目一: def my_range(start,stop,step=1): while start < stop: yield start start+=step #执行函数得到生成器,本质就是迭代器 obj=my_range(1,7,2) #1 3 5 print(next(obj)) print(next(obj)) print(next(obj)) print(next(obj)) #StopIteration #应用于for循环 for i in my_range(1,7,2): print(i) #题目二 import time def tail(filepath): with open(filepath,'rb') as f: f.seek(0,2) while True: line=f.readline() if line: yield line else: time.sleep(0.2) def grep(pattern,lines): for line in lines: line=line.decode('utf-8') if pattern in line: yield line for line in grep('404',tail('access.log')): print(line,end='') #测试 with open('access.log','a',encoding='utf-8') as f: f.write('出错啦404\n')
send方法,实现协程函数:
def dog(name): print('狗[%s]准备开吃了' %name) food_list = [] while True: food = yield food_list# food=yield='骨头' print('狗哥[%s]吃了:%s' %(name, food)) food_list.append(food) # print (food_list) dg = dog('alex') next(dg) #print(dg1) #send方法有两个功能: #1. 为当前暂停位置的yield赋值 #2. 与next的效果一样
#强调:针对表达式形式的yield,在使用生成器时必先send(None),相当于先完成一个初始化操作
dg2 = dg.send('骨头')
print(dg2)
dg3 = next(dg)
print(dg3)
生成器表达式:
''' 生成器表达式 三元表达式外面换位()小括号,并不是元组的生成式,生成的是一个生成器(迭代器对象) ''' names = ['gr', 'zc', 'xx', 'kk'] g = (name.upper() for name in names if name != 'gr') print(g) #打印出一个生成器对象内存地址
三、面向过程的编程思想
#1、首先强调:面向过程编程绝对不是用函数编程这么简单,面向过程是一种编程思路、思想,而编程思路是不依赖于具体的语言或语法的。言外之意是即使我们不依赖于函数,也可以基于面向过程的思想编写程序 #2、定义 面向过程的核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么 基于面向过程设计程序就好比在设计一条流水线,是一种机械式的思维方式 #3、优点:复杂的问题流程化,进而简单化 #4、缺点:可扩展性差,修改流水线的任意一个阶段,都会牵一发而动全身 #5、应用:扩展性要求不高的场景,典型案例如linux内核,git,httpd #6、举例 流水线1: 用户输入用户名、密码--->用户验证--->欢迎界面 流水线2: 用户输入sql--->sql解析--->执行功能
ps: 函数的参数传入,是函数接收了这个参数,而函数return的返回值,是函数给出的结果,面向过程的思路就是把程序的执行当做一串首尾相连的功能,该功能可以是函数的形式,然后一个函数return值,另个一个函数接收这个值,然后数据处理后又return一个值,下一个函数又接收处理。。。。以此类推

浙公网安备 33010602011771号