一、迭代器
1.迭代器定义
迭代是一个重复的过程,每次重复一次迭代,并且每次迭代的结果都是下一次迭代的初始值。
l = ["aaa","bbb","ccc"] count = 0 while count< len(l): #每次重复完成后count都是下一次的初始值 print(l[count]) count+=1
需要迭代器的原因:对于序列类型str、list、tuple可以依赖索引迭代取值,对于dict、set、文件需要提供不依赖索引取值的方式。
可迭代对象:具有内置__iter__方法的对象。字符串、列表、元组、字典、集合和文件均为可迭代对象。
迭代器对象:可迭代对象执行obj.__iter__()方法后得到的结果为迭代器对象。迭代器对象内置有__iter__和__next__方法。文件对象为迭代器对象。
总结:
1.可迭代对象不一定是迭代器对象。
2.迭代器对象一定是可迭代对象。
3.调用obj.__iter__()方法,可得到迭代器对象,若本身为迭代器对象,执行该方法得到的仍然是它本身。
2.迭代器的使用
可迭代对象调用obj.__iter__()方法得到迭代器对象后,可使用obj.__next__()迭代取值,迭代器对象可直接调用obj.__next__()取值。
obj.__next__()等同于next(obj),取完值后调用__next__会抛出StopIteration异常。
#每次调用__next__方法取一次值,取完之后再调用该方法会抛出StopIteration异常 l = ["aaa","bbb","ccc"] iter_list = l.__iter__() print(iter_list.__next__()) print(iter_list.__next__()) print(iter_list.__next__()) print(iter_list.__next__())
使用try...except捕获异常
d = {"a":212,"b":111,"c":222} iter_dict = d.__iter__() while True: try: print(next(iter_dict)) #字典迭代取到的为key except StopIteration: break
for循环原理
for k in obj
a)调用in后的obj__iter=obj.__iter__()得到一个迭代器对象。
b) 执行k=obj_iter.__next__()将取到的值赋给k,然后执行循环体。
c)重复过程2,直到捕获到StopInteration异常,结束循环。
3.迭代器的优缺点
优点:
a) 提供一种统一的、不依赖索引的取值方式,为for循环的实现提供了依据。
b) 迭代器同一时间在内存中只有一个值,更节省内存。
缺点:
a) 只能往后取值,为一次性的。
b) next执行完之前,不能统计值得个数,无获取长度。
二、生成器
1.生成器定义
只要函数内部包含有yield关键字,那么执行func()的到的就是生成器,不会执行函数内部代码,并且生成器就是迭代器。
def func(): print("1111111111") yield 1 print("2222222222") yield 2 print("3333333333") yield 3 g = func() #g为生成器,生成器就是迭代器 print(next(g)) #func()开始执行到第一个yield,并且打印yield返回值 print(next(g)) #func()执行到第二个yield,并且打印yield返回值 print(next(g))
yield的功能:
a) yield提供了一种自定义迭代器的方法
b) yield于return的区别:yield可以返回多次值,return只能返回一次值。函数暂停与再继续的状态有yield保存。
模拟管道,实现tail -f access.log | grep "404"
1 #tail -f access.log|grep "404" 2 import time 3 def tail(filepath): 4 with open(filepath,‘rb‘) as f: 5 f.seek(0,2) 6 while True: 7 line = f.readline() 8 if line: 9 yield line 10 else: 11 time.sleep(0.05) 12 13 def grep(lines,pattern): 14 for line in lines: #调用tail后得到生成器对象,可使用for来迭代其中每一行内容 15 line =line.decode(‘UTF-8‘) 16 if pattern in line: 17 yield line 18 19 res = grep(tail("access.log"),"404") #res也为生成器对象,可使用for来迭代取值 20 for line in res: 21 print(line)
2.协程函数
表达式形式的yield,在使用时,第一次必须穿None,g.send(None)等同于next(g)
1 def eater(name): 2 print("%s开始吃了"%name) 3 food_list = [] 4 while True: 5 food = yield food_list 6 print("%s吃了%s"%(name,food)) 7 food_list.append(food) 8 9 g = eater("xxx") #创建生成器g 10 res1 = g.send(None) #初始化yield,拿到返回的空列表 11 print(res1) 12 res2 = g.send("米饭") #发送"米饭"给yield,继续执行代码到下一个yield,返回列表给res2 13 print(res1) 14 g.close() #结束迭代 15 res3 = g.send("面条") #无法发送和获取值 16 print(res3)
实现功能:grep -rl ‘python‘ /etc
1 #从某个路径下搜索所有包含某个字符串的文件的路径 2 import os 3 4 def init(func): 5 def inner(*args,**kwargs): 6 res = func(*args,**kwargs) 7 next(res) 8 return res 9 return inner 10 11 #列出某个路径下所有文件的绝对路径 12 @init 13 def list_path(target): 14 while True: 15 file_path = yield 16 g = os.walk(file_path) 17 for pardir,_,files in g: 18 for file in files: 19 abs_path = "%s\%s"%(pardir,file) 20 target.send(abs_path) 21 22 #打开某个文件 23 @init 24 def openner(target): 25 while True: 26 abs_path = yield 27 with open(abs_path,"rb") as f: 28 target.send((abs_path,f)) 29 30 #读取文件中每一行 31 @init 32 def read_line(target): 33 while True: 34 abs_path,f = yield 35 for line in f: 36 flag = target.send((abs_path,line)) 37 if flag: 38 break 39 40 #从一行中查找某个字符 41 @init 42 def find_str(target,pattern): 43 flag = False #用于判断某个文件是否包含有重复的某个字符 44 pattern = pattern.encode("utf-8") 45 while True: 46 abs_path,line = yield flag 47 if pattern in line: 48 target.send(abs_path) 49 flag = True 50 51 #将结果打印出来 52 @init 53 def print_path(): 54 while True: 55 abs_path = yield 56 print(abs_path) 57 58 g = list_path(openner(read_line(find_str(print_path(),"abcdef")))) 59 g.send(r"D:\PycharmProjects")
3.yield总结
a) 可以把函数做成迭代器
b) 对比return,可以返回多次值,可以挂起/保存函数的运行状态
三、面向过程编程
面向过程是一种思路和思想,不依赖于具体的语言或语法,核心思想是过程二字,即按照流水线式分步骤解决问题。
优点是复杂问题流程化,分解为简单的小功能。
缺点是可扩展性差,修改流水线任一阶段,会影响到其他阶段。
适合扩展性要求不高的场景,入linux内核、git、httpd等。
四、三元表达式、列表推导式、生成器表达式
1.三元表达式
name = input(">>:").strip() res = "SB" if name=="xxx" else "NB" #满足if条件,则返回if前的值,不满足则返回else后的值 print(res)
2、列表推导式
l = [str(i)+"xxx" for i in range(1,10)] #为1-10所有数字添加后缀 print(l) l = [str(i)+"xxx" for i in range(1,20) if i%2==0] #1-20所有偶数加后缀 print(l)
3、生成器表达式
将列表推导式的[]换为()就是生成器表达式。
#生成老母鸡,需要使用时调用next下蛋 >>> chicken = ("鸡蛋%s"%i for i in range(1,10)) >>> chicken <generator object <genexpr> at 0x000001AFD64CDF68> >>> next(chicken) ‘鸡蛋1‘ >>> list(chicken) #第一个鸡蛋已经下过了,chicken可迭代,因此可转为列表 [‘鸡蛋2‘, ‘鸡蛋3‘, ‘鸡蛋4‘, ‘鸡蛋5‘, ‘鸡蛋6‘, ‘鸡蛋7‘, ‘鸡蛋8‘, ‘鸡蛋9‘]
4、声明式编程
a) 将l=[‘xxx‘,‘abc_sb‘,‘aaa‘,‘bbb‘]中的字母全部变大写
l=[‘xxx‘,‘abc_sb‘,‘aaa‘,‘bbb‘] l = [i.upper()for i in l] print(l)
b) 将l=[‘xxx‘,‘abc_sb‘,‘aaa‘,‘bbb‘]中以sb结尾的名字过滤掉,然后保存剩下的名字长度
l=[‘xxx‘,‘abc_sb‘,‘aaa‘,‘bbb‘] l = [len(i) for i in l if not i.endswith("sb")] print(l)
c) 求文件test.txt中最长的行的长度(长度按字符个数算,需要使用max函数)
with open("test.txt",‘r‘,encoding="utf-8") as f: res = max(len(line) for line in f) print(res)
d) 求文件test.txt中总共包含的字符个数?思考为何在第一次之后的n次sum求和得到的结果为0?(需要使用sum函数)
with open("test.txt",‘r‘,encoding="utf-8") as f: res = sum(len(line) for line in f) print(res)
生成器只能往后取值,已经取完一次值后,再对生成器做求和结果为0
五、递归和二分法
1.递归调用定义
在调用一个函数的过程中,直接或间接调用了该函数本身,称之为递归调用。
递归调用分为两个阶段:递推和回溯。
python中使用sys.gettrcursionlimit()查看可递归调用的层数。
import sys print(sys.getrecursionlimit()) #默认支持1000层递归调用 l=[1,[2,[3,[4,[5,[6,[7,]]]]]]] def func(l): for item in l: if type(item) is list: func(item) else: print(item) func(l)
2.python中递归调用特点
python中递归调用效率低,需要在进入下一次递归时保留当前状态。其他语言中有尾递归优化,python中没有,并且对层级做了限制。
递归使用的要求:
a) 必须有一个明确的结束条件
b) 每次进入更深一层递归时,问题规模比上一次有所减少
c) 递归效率不高,层数过多易导致栈溢出
3.二分法
1 l = [2,3,5,6,8,12,15,16,19,22,35,45,56,62,98,122,321] 2 def binary_search(l,num): 3 print(l) 4 mid_index = len(l)//2 5 if len(l) != 0: 6 if num < l[mid_index]: 7 binary_search(l[0:mid_index-1],num) 8 elif num > l[mid_index]: 9 binary_search(l[mid_index+1:],num) 10 else: 11 print("find it") 12 else: 13 print("can‘t find %s" % num) 14 binary_search(l,321)
六、匿名函数
匿名函数一次性使用,随时随需定义,可应用在max、min、sorted、map、reduce、filter等函数中。
#找出薪水最高的人 salaries = { ‘abc‘:3222, ‘def‘:111, ‘aaa‘:431, ‘xxx‘:4133 } #普通方法 g = zip(salaries.values(),salaries.keys()) #g为迭代器 print(max(g)) #max按照每次取值的第一个数来比较 #定义函数的方法,打印出薪水最高的人的名字 def func(k): return salaries[k] print(max(salaries,key=func)) #排序是按照key来排序,结果展示按照salaries默认结果展示 #使用匿名函数 print(max(salaries,key=lambda k:salaries[k]))
七、内置函数
sorted
#薪水排序,输出人名 salaries = { ‘abc‘:3222, ‘def‘:111, ‘aaa‘:431, ‘xxx‘:4133 } print(sorted(salaries,key=lambda k:salaries[k])) print(sorted(salaries,key=lambda k:salaries[k],reverse=True))
map
#给列表中所有元素批量添加后缀 #普通方法 names = [‘xxx‘,‘aaa‘,‘eee‘] l =[] for name in names: res = "%s_SB"%name l.append(res) print(l) #使用map添加 g = map(lambda name:‘%s_SB‘%name,names) print(g) #g为迭代器 print(list(g))
filter
#筛选出某个序列中特定元素 names = ["xxx_sb","aaa_sb","sss","ee_sb"] g = filter(lambda i:i.endswith("sb"),names) print(g) print(list(g))
reduce
#筛选出某个序列中特定元素 #某个序列中连续两个元素进行处理 from functools import reduce print(reduce(lambda x,y:x+y,range(1,101),100)) #最后的100为初始值,可以不添加。