##########迭代器、生成器和面向过程编程##########
一、迭代器
迭代器是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值;
l = [1,2,3] count = 0 while count < len(l): print(l[count]) count+=1
为何要有迭代器?
对于序列类型:字符串,列表,元组,可以通过使用索引的方式迭代取出其包含的元素,但是对于字典,集合文件等类型是没有索引的,若还想取出其内部包含的元素,
就必须使用不依赖于索引的迭代方式---迭代器。
什么是可迭代对象?
可迭代的对象指的是内置有__iter__方法的对象,即obj.__iter__,如下:
'hello'.__iter__ (1,2,3).__iter__ [1,2,3].__iter__ {'a':1}.__iter__ {'a','b'}.__iter__ open('a.txt').__iter__
什么是迭代器对象?
可迭代对象执行obj.__iter__()得到的结果就是迭代器对象
而迭代器器对象指的是内置有__iter__又内置有__next__方法的对象
文件类型是迭代器对象:
open('a.txt').__iter__() open('b.txt').__next__()
注意:
迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象。
迭代器对象的使用
dic={'a':1,'b':2,'c':3} # iter_dic = dic.__iter__() #得到迭代器对象,迭代器对象既有__iter__又有__next__。 # print(iter_dic.__iter__() is iter_dic) #True 迭代器.__iter__()得到的仍然是迭代器本身 # print(iter_dic.__next__()) #等同于next(iter_dic) # print(iter_dic.__next__()) # print(iter_dic.__next__()) # print(iter_dic.__next__()) #元素迭代完成,会抛出异常StopIteration #有了迭代器,就可以不依赖索引迭代取值了 需要自己捕捉异常,控制next。 iter_dic = dic.__iter__() print(iter_dic) while 1: try: k = next(iter_dic) print(dic[k]) except StopIteration: break
for循环
dic={'a':1,'b':2,'c':3} for key in dic: print(key,dic[key])
#基于for循环,可以不再依赖索引取值
#for循环的工作原理:
1. 执行in后对象的dic.__iter__(),得到一个迭代器对象iter_dic
2. 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码
3. 重复过程2,直到捕捉到异常StopIteration
迭代器的优缺点:
优点:
- 提供一种统一的不依赖于索引的迭代方式
- 惰性计算,节省内存
缺点:
- 无法获取长度(只有在next完毕才知道到底有几个值)
- 一次性的,只能往后走,不能往前退
二、生成器
什么是生成器?
只要函数内部包含有yield关键字,那么函数名()得到的结果就是生成器,并且不会执行函数内部代码。
def func(): print('====>first') yield 1 print('====>second') yield 2 print('====>third') yield 3 print('====>end') print(func()) # g=func() # print(g) # print(next(g)) # print(next(g)) # print(next(g)) # print(next(g))
生成器就是迭代器
g.__iter__
g.__next__
res = next(g)
print(res)
练习题:1、自定义函数模拟range(1,7,2)
#1、自定义函数模拟range(1,7,2) def my_range(start,stop,step=1): while True: if start < stop: yield start start+=step g = my_range(1,7,2) #g为生成器 生成器就是迭代器,可以通过迭代器迭代取值 print(next(g)) print(next(g)) print(next(g)) # for i in my_range(1,7,2): #也可以通过for循环取值 # print(i)
2、模拟管道,实现功能:tail -f access.log | grep '404'
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(parttern,lines): for line in lines: line = line.decode('utf-8') if parttern in line: yield line for line in grep('404',tail('access.log')): print(line,end='')
三、yield总结
#1、把函数做成迭代器
#2、对比return,可以返回多次值,可以挂起/保存函数的运行状态
四、面向过程编程
#1、强调:面向过程编程绝对不是用函数编程,面向过程是一种编程思想、思路,而编程思路是不依赖于具体的语言或者语法的。也可以说即使我们不依赖于函数,也可以基于面向过程的思想编写程序
#2、定义
面向过程的核心是过程,过程指的是解决问题的步骤,即先干什么后干什么。
基于面向过程设计程序就好比是设计一条流水线,是一种机械式的思维方式
#3、优点
复杂的问题流程化,进而简单化
#4、缺点
可扩展性差,修改流水线的任意一个阶段,都会牵一发而动全身
#5、应用
扩展性要求不高的场景,典型案例Linux内核
##########三元表达式、列表推导式、生成器表达式、递归、匿名函数、内置函数##########
一、三元表达式、列表推导式、生成器表达式
1、三元表达式
name = input('姓名:> ').strip() res = 'NB' if name == 'alex' else 'TNB' print(res)
2、列表推导式
# egg_list = [] # for i in range(1,10): # egg_list.append('鸡蛋%s' %i) # print(egg_list) egg_list = ['鸡蛋%s' %i for i in range(1,10)] print(egg_list)
3、生成器表达式
#1、把列表推导式的[]换成()就是生成器表达式
chicken = ('鸡蛋%s' %i for i in range(1,5)) print(chicken) #chicken = <generator object <genexpr> at 0x00000000021768E0> # print(next(chicken)) #鸡蛋1 # print(next(chicken)) #鸡蛋2 # print(next(chicken)) #鸡蛋3 print(list(chicken)) #['鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', ] 因为chicken可迭代,因而可以转换成列表
#2、优点:省内存,一次只产生一个值在内存中
二、声明式编程练习题
#1、将names=['egon','alex_sb','wupeiqi','yuanhao']中的名字全部变大写 names=['egon','alex_sb','wupeiqi','yuanhao'] res = [name.upper() for name in names] print(res) #['EGON', 'ALEX_SB', 'WUPEIQI', 'YUANHAO']
#2、将names=['egon','alex_sb','wupeiqi','yuanhao']中以sb结尾的名字过滤掉,然后保存剩下的名字长度 names=['egon','alex_sb','wupeiqi','yuanhao'] # res = [name for name in names if 'sb' not in name] #以sb结尾的名字过滤掉 res = ['egon', 'wupeiqi', 'yuanhao'] res = [len(name) for name in names if 'sb' not in name] #[4, 7, 7] print(res)
#3、求文件a.txt中最长的行的长度(长度按字符个数算,需要使用max函数) #笨方法 # num_list=[] # with open('a.txt','r',encoding='utf-8') as f: # lines = f.readlines() # for line in lines: # num_list.append(len(line)) # print(max(num_list)) #声明式方法 with open('a.txt',encoding='utf-8') as f: print(max(len(line)for line in f))
三、内置函数
现阶段需要掌握的:
divmod
python divmod()函数把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a // b,a % b)
>>> divmod(7,2) (3, 1) >>> divmod(8,2) (4, 0)
enumerate
enumerate是用来遍历可迭代容器中的元素,同时通过一个计数器变量记录当前元素所对应的索引值。
#示例: names = ['Bob', 'Alice', 'Guido'] for index, value in enumerate(names): print(f'{index}: {value}') #输入如下内容: 0: Bob 1: Alice 2: Guido
这个循环遍历了name列表的所有元素,并通过增加从零开始的计数器变量为每个元素生成索引。
enumerate()函数允许为循环自定义起始索引值。enumerate()函数中接收一个可选参数,该参数允许为本次循环中的计数器变量设置初始值。
names = ['Bob', 'Alice', 'Guido'] for index, value in enumerate(names, 1): print(f'{index}: {value}')
1: Bob
2: Alice
3: Guido
enumerate是Python的一个内置函数。你应该充分利用它通过循环迭代自动生成的索引变量。 索引值默认从0开始,但也可以将其设置为任何整数。 enumerate函数是从2.3版本开始被添加到Python中的,详情见PEP279。 Python的enumerate函数可以帮助你编写出更加Pythonic和地道的循环结构,避免使用笨重且容易出错的手动生成索引。 为了充分利用enumerate的特性,一定要研究Python的迭代器和数据结构解包功能。 作者:vimiix 链接:https://juejin.im/post/5a31146251882503eb4b4755 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
eval
功能:将字符串str当成有效的表达式来求值并返回结果。
参数:
source : 一个python表达式或者函数compile()返回的代码对象
globals : 可选,必须是dictionary
locals : 可选,任意map对象
可以把list,tuple,dict和string相互转化。 ################################################# 字符串转换成列表 >>>a = "[[1,2], [3,4], [5,6], [7,8], [9,0]]" >>>type(a) <type 'str'> >>> b = eval(a) >>> print b [[1, 2], [3, 4], [5, 6], [7, 8], [9, 0]] >>> type(b) <type 'list'> ################################################# 字符串转换成字典 >>> a = "{1: 'a', 2: 'b'}" >>> type(a) <type 'str'> >>> b = eval(a) >>> print b {1: 'a', 2: 'b'} >>> type(b) <type 'dict'> ################################################# 字符串转换成元组 >>> a = "([1,2], [3,4], [5,6], [7,8], (9,0))" >>> type(a) <type 'str'> >>> b = eval(a) >>> print b ([1, 2], [3, 4], [5, 6], [7, 8], (9, 0)) >>> type(b) <type 'tuple'>