借鉴:https://www.cnblogs.com/lovezwfjc/p/9300031.html

     https://www.cnblogs.com/jianguo221/p/8977465.html

1.递归和迭代的区别

https://www.zhihu.com/question/20278387

递归:自身调用自身的迭代就是递归。

迭代:重复一定的算法,达到想要的目的。数学上二分法,牛顿法是很好的迭代例子

2.什么是迭代器协议

1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代(只能往后走不能往前退)

2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)

3.协议是一种约定, 可迭代对象实现了迭代器协议, Python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象

3.python中强大的for循环机制

for循环的本质:循环所有对象,全部都是使用迭代器协议。

for循环就是基于迭代器协议提供了一个统一的可以遍历所有对象的方法,即在遍历之前,先调用对象的__iter__方法将其转换成一个迭代器,然后使用迭代器协议去实现循环访问,这样所有的对象就都可以通过for循环来遍历了

字符串,列表,元组,字典,集合,文件对象等本质上来说都不是可迭代对象,在使用for循环的时候内部是先调用他们内部的__iter__方法,使他们变成了可迭代对象,然后再使用可迭代对象的__next__方法依次循环元素,当元素循环完时,会触发StopIteration异常,for循环会捕捉到这种异常,终止迭代

访问方式常见的有下标方式访问,迭代器方式访问,for循环访问

# 1.下标访问方式
li = [1,2,3,4]
print(li[0])#下标访问

# 2.迭代器协议访问
li = [1,2,3,4]
f = li.__iter__()#第一步,先通过内部的_iter_方法,先把对象变成可迭代对象
print(f.__next__())#对可迭代对象用_next_方法取值
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())#StopIteration,超出边界会报错

# 3.for循环访问
# for循环l本质就是遵循迭代器协议的访问方式,先调用diedai_l=l.__iter__()方法,或者直接diedai_l=iter(l),然后依次执行diedai_l.next(),直到for循环捕捉到StopIteration终止循环
li = [1,2,3,4]
for i in li:#li_iter = li._iter_()
    print(i)#li_iter._next_
# 4.用while去模拟for循环做的事情
l=[1,2,3,4,5]
diedai_l=l.__iter__()
while True:
    try:
        print(diedai_l.__next__())
    except StopIteration:
        # print('迭代完毕了,循环终止了')
        break

练习

print(next(iter_l)) #next()---->iter_l.__next__(),next()是python内置,iter_l.__next__()是数据类型内置

s={1,2,3}
# for i in s:
#     print(i)
# iter_s=s.__iter__()#遵循迭代器协议,生成可迭代对象
# print(iter_s)
# print(iter_s.__next__())
# print(iter_s.__next__())
# print(iter_s.__next__())
# print(iter_s.__next__())

# dic={'a':1,'b':2}
# iter_d=dic.__iter__()
# print(iter_d.__next__())

# f=open('test.txt','r+')
# # for i in f:
# iter_f=f.__iter__()
# print(iter_f)
# print(iter_f.__next__(),end='')
# print(iter_f.__next__(),end='')
# l=[1,2,3,4,5]
# diedai_l=l.__iter__()
# while True:
#     try:
#         print(diedai_l.__next__())
#     except StopIteration:
#         # print('迭代完毕了,循环终止了')
#         break

# l=['die','erzi','sunzi','chongsunzi']
# iter_l=l.__iter__()
# print(iter_l)
# print(iter_l.__next__())
# print(iter_l.__next__())
# print(iter_l.__next__())
# print(iter_l.__next__())
# print(iter_l.__next__())
# print(next(iter_l)) #next()---->iter_l.__next__()

4.为什么要有for循环

 

 

 5.生成器

什么是生成器?
可以理解为一种数据类型,这神数据类型自动实现了送代器协议(其他的数据奖型需要调用自己内置的iter方法),所以生成器就是可选代对象

生成器分类及在python中的表现形式:(python有两种不同的方式提供生成器)
1.生成器函数:常规函数定义,但是,使用yield语句而不是使用return语句返回结果.yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行
2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表,按需取出对象
为何使用生成器和生成器的优点

python使用生成器对延迟操作提供了支持.所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果.这也是生成器的主要好处,节省内存

生成器小结:
1.是可达代对象
2.实现了延迟计算省内存
3.生成器本质和其他的数据类型一样,都是实现了选代器协议,只不过生成器附加了一个延迟计算省内存的好处,其余的可达代对象可没有这点好处。

6.生成器表达式和列表解析

三元表达式:  值1  if  条件  else  值2

def test():
    yield 1
    yield 2
    yield 3
g=test()
print('来自函数',g)
# print(g.__next__())
# print(g.__next__())

#三元表达式
# name='ray'
# name='raitorei'
# res='SB' if name == 'ray' else '帅哥'
# print(res)


#列表解析
# egg_list=[]
# for i in range(10):
#     egg_list.append('鸡蛋%s' %i)
# print(egg_list)

l=['鸡蛋%s' %i for i in range(10)]
# l1=['鸡蛋%s' %i for i in range(10) if i > 5 ]
# # l1=['鸡蛋%s' %i for i in range(10) if i > 5 else i] #没有四元表达式
# l2=['鸡蛋%s' %i for i in range(10) if i < 5] #没有四元表达式

print(l)
# print(l1)
# print(l2)

laomuji=('鸡蛋%s' %i for i in range(10)) #生成器表达式
print(laomuji)
print(laomuji.__next__())
print(laomuji.__next__())
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
print(next(laomuji))
# print(next(laomuji))

7.生成器函数(母鸡下蛋)

# def xiadan():
#     ret = []
#     for i in range(100):
#         ret.append('鸡蛋%s' %i)
#     return ret
# print(xiadan())
# 缺点 :
    # 1.占空间大
    # 2.效率低

    
# 生成器函数版本
def xiadan():
    for i in range(100):
        yield '鸡蛋%s' %i
xd = xiadan()
print('取鸡蛋',xd.__next__())

8.生成器总结(生成器只能遍历一次!)

综上已经对生成器有了一定的认识,下面我们以生成器函数为例进行总结

  • 语法上和函数类似:生成器函数和常规函数几乎是一样的。它们都是使用def语句进行定义,差别在于,生成器使用yield语句返回一个值,而常规函数使用return语句返回一个值
  • 自动实现迭代器协议:对于生成器,Python会自动实现迭代器协议,以便应用到迭代背景中(如for循环,sum函数)。由于生成器自动实现了迭代器协议,所以,我们可以调用它的next方法,并且,在没有值可以返回的时候,生成器自动产生StopIteration异常
  • 状态挂起:生成器使用yield语句返回一个值。yield语句挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行

优点一:生成器的好处是延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据量处理,将会非常有用。

#列表解析
sum([i for i in range(100000000)])#内存占用大,机器容易卡死

#生成器表达式
sum(i for i in range(100000000))#几乎不占内存

优点二:生成器还能有效提高代码可读性

不使用版本:

 

缺点 :
  1.占空间大
  2.效率低

  因为要一次性把所有的数据都给做好,然后再使用

 


 

 

def xiadan():
    ret = []
    for i in range(1,100):
        ret.append('鸡蛋%s' %i)
    return ret
print(xiadan())

 

"E:\Program Files\JetBrains\PycharmProjects\python_demo\venv\Scripts\python.exe" "E:/Program Files/JetBrains/PycharmProjects/python_demo/day19/下鸡蛋.py"
['鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9', '鸡蛋10', '鸡蛋11', '鸡蛋12', '鸡蛋13', '鸡蛋14', '鸡蛋15', '鸡蛋16', '鸡蛋17', '鸡蛋18', '鸡蛋19', '鸡蛋20', '鸡蛋21', '鸡蛋22', '鸡蛋23', '鸡蛋24', '鸡蛋25', '鸡蛋26', '鸡蛋27', '鸡蛋28', '鸡蛋29', '鸡蛋30', '鸡蛋31', '鸡蛋32', '鸡蛋33', '鸡蛋34', '鸡蛋35', '鸡蛋36', '鸡蛋37', '鸡蛋38', '鸡蛋39', '鸡蛋40', '鸡蛋41', '鸡蛋42', '鸡蛋43', '鸡蛋44', '鸡蛋45', '鸡蛋46', '鸡蛋47', '鸡蛋48', '鸡蛋49', '鸡蛋50', '鸡蛋51', '鸡蛋52', '鸡蛋53', '鸡蛋54', '鸡蛋55', '鸡蛋56', '鸡蛋57', '鸡蛋58', '鸡蛋59', '鸡蛋60', '鸡蛋61', '鸡蛋62', '鸡蛋63', '鸡蛋64', '鸡蛋65', '鸡蛋66', '鸡蛋67', '鸡蛋68', '鸡蛋69', '鸡蛋70', '鸡蛋71', '鸡蛋72', '鸡蛋73', '鸡蛋74', '鸡蛋75', '鸡蛋76', '鸡蛋77', '鸡蛋78', '鸡蛋79', '鸡蛋80', '鸡蛋81', '鸡蛋82', '鸡蛋83', '鸡蛋84', '鸡蛋85', '鸡蛋86', '鸡蛋87', '鸡蛋88', '鸡蛋89', '鸡蛋90', '鸡蛋91', '鸡蛋92', '鸡蛋93', '鸡蛋94', '鸡蛋95', '鸡蛋96', '鸡蛋97', '鸡蛋98', '鸡蛋99']

Process finished with exit code 0

使用版本:

def xiadan():
    for i in range(1,5):
        yield '鸡蛋%s' %i
xd = xiadan()
print(xd.__next__())
print(xd.__next__())
print(xd.__next__())
print(xd.__next__())
"E:\Program Files\JetBrains\PycharmProjects\python_demo\venv\Scripts\python.exe" "E:/Program Files/JetBrains/PycharmProjects/python_demo/day19/下鸡蛋.py"
鸡蛋1
鸡蛋2
鸡蛋3
鸡蛋4

Process finished with exit code 0

 

 

这里,至少有两个充分的理由说明,使用生成器比不使用代码更加清晰:
    1、使用生成器以后,代码行数更少。大家要记住,如果想要把代码写的pythonic在保证代码可读性的前提下,代码行数越少越好。
    2、不使用生成器的时候,对于每次结果,我们首先看到的是result append(index),其次,才是index。也就是说,我们每次看到的是一个列表的append的操作      的,知识append的是我们想要的结果。使用生成器时候,直接yield index,少了列表append操作的干扰,我们一眼就能够看出,代码是要返回index。
    
    上面的例子充分说明了,合理使用生成器,能够有效提高代码可读性,只要大家完全接收了生成器的概念,理解了yield语句和return语句一样,业绩是返回一个值。
    那么,就能够理解为什么使用生成器比不适用生成器要好,能够 理解使用生成器真的可以让代码变得清晰易懂。

9.计算省市人数demo

 文本文件

{'name':'河南','population':5}
{'name':'山西','population':5}
{'name':'上海','population':5}
{'name':'台湾','population':5}
def get_p():
    # with open('人口普查', 'r', encoding='utf-8') as f:
    #     for i in f:
    #         yield i
    f = open('人口普查','r',encoding='utf-8')
    for i in  f:
        yield i
g = get_p()
# print(g.__next__(),end='')
# print(next(g))

# 获取城市的人口
s = eval(g.__next__())
print(s['population'])# 5


# for p in g:
#     p_dic = eval(p)
#     print(p_dic['population'])

# 获取总数
all_p=sum(eval(i)['population'] for i in g)
print(all_p)#15  上面已经取过一次

 

 

 

posted on 2019-11-24 22:05  月零Ray  阅读(290)  评论(0)    收藏  举报