Python学习笔记四(迭代器、生成器、内置函数)

一、迭代器

  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)
View Code

   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)
View Code

  实现功能: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")
View Code

  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为初始值,可以不添加。

 

posted @ 2018-01-11 23:14  n1ghtwatcher  阅读(282)  评论(0编辑  收藏  举报