迭代器,生成器

一.认识迭代器:  

迭代器:
器:工具
迭代:重复(更新换代)的过程,这种过程每次迭代都是基于上一次的结果
迭代器:迭代取值的工具

为什么用迭代器:
迭代提供了一种不依赖于索引取值的方法

优点;
1.可以不依赖索引取值
2.值占用一份内存空间,不会内存溢出 (python3与python2区别)
缺点:
1.不能去指定的元素
2.取完值会报错 StopIteration
 
n = 0
while True:
    print(n)
不算迭代器 没有迭代

n = 0
while True:
    print(n)
    n += 1
是迭代器 有迭代 + 重复

 

二.可迭代对象与迭代器对象

可迭代对象:
内置有__iter__方法的都是可迭代对象 . __iter__ 也可以写成 iter()
str,list,tuple,dict,set,file
迭代器对象:
内置有__iter__方法,__next__方法是迭代器对象. __next__ 迭代器取值
file

可迭代对象使用__iter__方法就得到该对象的迭代器对象
迭代器对象一定是可迭代对象,可迭代对象不一定是迭代器对象
 
可迭代对象转迭代器对象 __iter__

s = 'hello'
res = s.__iter__()
print(res)  # 得到的是迭代器对象的内存地址
# >>> <str_iterator object at 0x0000027734C7E908>


调用 __next__ 迭代取值
l = [1,2,3]
res = l.__iter__()
print(res.__next__())  # >>> 1
print(res.__next__())  # >>> 2
print(res.__next__())  # >>> 3
print(res.__next__())  # >>> 值取完,报错 StopIteration

d = {'name':'waller','age':20,'hobby':'read'}
res = d.__iter__()
print(res.__next__())  # >>> name
print(res.__next__())  # >>> age
print(res.__next__())  # >>> hobby
print(res.__next__())  # >>> 值取完,报错 StopIteration

print(d.__iter__().__next__())  # >>> name
print(d.__iter__().__next__())  # >>> name
print(d.__iter__().__next__())  # >>> name
# 可迭代对象转成迭代器对象后取值,又重新把可迭代对象转成迭代器对象再取值,所以取的都是第一个值

# 文件本身就是迭代器对象,无论调用多少个__iter__ 都是一样的结果
with open('file.txt','r',encoding='utf-8') as f:
    pass
f_iter = f.__iter__()
print(f_iter is f)  # True 文件对象调用__iter__方法后的内存地址和调用前一样
f.__next__()  # 迭代器对象用__next__取文件里每行的内容


异常处理
d = {'name':'waller','age':20,'hobby':'read'}
res = d.__iter__()

while True:
    try:
        print(res.__next__())
    except StopIteration:
        print('值已取完')
        break
# 对迭代器对象循环取值,通过try + except StopIteration 截获到住取完时的报错信息,结束循环,就不会报错
 

 

 三.for循环的本质

for循环本质:
1.现将 in 后面的可迭代对象调用__iter__方法转成迭代器对象
2.再调用__next__方法 迭代取值
3.内部有异常捕获,当值取完,出现 StopIteration ,自动结束循环
迭代取值的本质就是for循环
问: 文件本身就是迭代器对象,为什么还会有__iter__方法?
因为迭代取值的本质是for循环,for循环第一步是将in后面的可迭代对象调用__iter__方法转成迭代器对象,
文件也是通过for循环取值,要经理for循环取值的第一步,所以文件有__iter__方法,但不会改变文件
l = [1,2,3]
for i in l:
    print(i)

res = l.__iter__()
print(res.__next__())
print(res.__next__())
print(res.__next__())

 

五.生成器:

生成器:
用户自定义的迭代器
表达式:
  
res =(值 for i in 可迭代对象 if 条件) (*****)
  生成器不会主动执行任何一行代码,必须通过__next__触发代码执行

函数版生成器:
def func(参数...):
...
yield 值
...
g = func()
关于yield:
1.函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行,会将函数初始化成迭代器
2.yield后面跟的值就是调用迭代器__next__方法你能得到的值
3.yield会将函数暂停住
4.yield既可以返回一个值也可以返回多个值 并且多个值也是按照元组的形式返回
5.yield支持外界为其传参(了解)

与return之间异同点
相同点:都可以返回值,并且都可以返回多个
不同点:
yield可以返回多次值,而return只能返回一次函数立即结束
yield还可以接受外部传入的值

生成器表达式:
例1:
res = (i for i in range(1,10) if i != 4) print(res) # >>> <generator object <genexpr> at 0x000002272C5B0780> print(res.__next__()) print(res.__next__()) print(res.__next__()) print(res.__next__()) 例2: #占内存 f = open('xxx.txt','r',encoding='utf-8') data = f.read() print(len(data)) f.close() with open('xxx.txt','r',encoding='utf-8') as f: # n = 0 # for line in f: # n += len(line) # print(n) g = (len(line) for line in f) # print(g.__next__()) # print(g.__next__()) # print(g.__next__()) # print(g.__next__()) print(sum(g))

函数版生成器:
def
func(): print('--第一次--') yield 1 print('--第二次--') yield 2 print('--第三次--') yield 3 print('--第四次--') yield res = func() # 初始化成迭代器 print(res) print(res.__next__()) print(res.__next__()) print(res.__next__()) print(res.__next__()) ''' <generator object func at 0x000001CDAAB0A938> --第一次-- 1 --第二次-- 2 --第三次-- 3 --第四次-- None '''

 

range()原理

for i in range(1,8,2):
    print(i) # >>> 1 3 5 7
def my_range(x,y,z=1): while x < y: yield x x += z res = my_range(1,4) # 把my_range 初始化成迭代器 print(res) # >>> <generator object my_range at 0x00000297384B0780> print(res.__next__()) # >>> 1 print(res.__next__()) # >>> 2 print(res.__next__()) # >>> 3 for i in my_range(1,8,2): print(i,end=' ') # >>> 1 3 5 7

 

def dog(name):
    print('%s 准备开吃'%name)
    while True:
        food = yield
        print('%s 吃了 %s'%(name,food))
g = dog('xx')
g.__next__()  # 必须先将代码运行至yield 才能够为其传值
g.send('狗不理包子')  # 给yield左边的变量传参  触发了__next__方法
g.send('饺子')
'''
xx 准备开吃
xx 吃了 狗不理包子
xx 吃了 饺子

'''
yield支持外界为其传参(了解)

题:

def add(n,i):
    return n+i
def test():
    for i in range(4):
        yield i
g=test()  # 函数test初始化成迭代器 (不运行)

for n in [1,10]:
    g=(add(n,i) for i in g)
    # 第一次for循环g=(add(n,i) for i in test()) 此时n = 1

    # 第二次for循环g=(add(n,i) for i in (add(n,i) for i in test())) 此时 n = 10
# print(n)
res=list(g)  

"""
1.list是基于for循环,for循环内有__next__ 触发 g 的内部代码运行
2.g 中的 (add(n,i) for i in test()) 先运行变成  (10+0 ,10+1,10+2,10+3) 
3.此时变成 g = (add(n,i) for i in (10,11,12,13)  最终是 10+10,10+21,10+22,10+23
"""

#A. res=[10,11,12,13]
#B. res=[11,12,13,14]
#C. res=[20,21,22,23]  答案
#D. res=[21,22,23,24]

 











posted @ 2019-07-15 16:51  waller  阅读(157)  评论(0编辑  收藏  举报