Python进阶-V 迭代器(Iterator)、生成器(Generator)函数

一、迭代器

1、可循环的有哪些,即可用for语句或者while语句的数据类型有哪些?
  字符串(str)、列表(list)、元组(tuple)、字典(dic)、集合(set)、枚举类(enumerate)
  还有哪些非数据类型,但是可以循环的?
  range(10),   文件句柄:f = open('filename',mode='r',enconding='utf-8')
2、查看这些可循环的数据类型或者函数或者文件句柄
  都有哪些共同的东西:(求交集,想到集合(set)的操作了)。
  还要引入一个内置函数dir()
print(dir())
print(dir(' '))
  由返回结果可以看到,dir带参数时,返回该参数的属性、方法列表。
  不带参数时,返回当前范围内的变量、方法和定义的类型列表;
 1 # 先可以求list和str的交集方法了
 2 str_method_li = set(dir(''))
 3 list_method_li = set(dir([]))
 4 res = str_method_li & list_method_li
 5 print(res)
 6 # 还是由很多共有的方法,加大力度
 7 tuple_m_li = set(dir(()))
 8 dic_m_li = set(dir({}))
 9 range_m_li = set(dir(range(5)))
10 set_m_li = set(dir(set({1, 2, 3, 4})))
11 res = res & tuple_m_li & dic_m_li & range_m_li & set_m_li
12 print(res)
13 #  还是由很多,我们还是看一看交集中一个双下方法名:__iter__
14 # 补充说明一下“双下方法”:方法名中开头和结果都带有两个下滑线的,即叫双下方法!它是python中用C实现的方法!
15 # 这个__iter__, 跟可迭代对应的英文iterable很像!
我们可以得出结论:
只要是能被for循环的数据类型,就一定拥有__iter__方法!
print([].__iter__())
# 运行结果为:<list_iterator object at 0x000001C2AD37B4A8>
# 即得到list的迭代器对象,因此我们可以判断,该方法返回的是一个迭代器(iterator)!
iterator = 'lov'.__iter__()
print(dir(iterator))
# 看到迭代器中的方法中,有一个双下方法:__next__,我们来看看它得到的是什么?
print(iterator.__next__())
# 返回的是字符串中的第一字母,再执行一次!
print(iterator.__next__())
# 返回的是字符串中的第二字母,再执行一次!
print(iterator.__next__())
# 返回的是字符串中的第三字母,再执行一次!
#print(iterator.__next__()) # 报错 StopIteration
# 这说明迭代器可以一个一个取集合中的元素,只到没有时,抛出异常!
总结一下:
#Iterable 可迭代的 -- > __iter__ #只要含有__iter__方法的都是可迭代的
[].__iter__() 迭代器 -- > __next__ #通过next就可以从迭代器中一个一个的取值

延申一下:
只要含有__iter__方法的都是可迭代的 —— 可迭代协议

自定义一个类型,可以用来迭代
 1 from collections import Iterable
 2 from collections import Iterator
 3 
 4 class A:
 5     def __next__(self):pass
 6     def __iter__(self):pass
 7 
 8 a = A()
 9 print(isinstance(a, Iterable))
10 print(isinstance(a, Iterator))
结果都为True,说明该类即可被迭代,也是迭代器!

3、迭代器的概念
迭代器协议:内部含有__next__方法和__iter__方法的就是迭代器
小结一下:
迭代器协议和可迭代协议
可以被for循环的都是可迭代的
可迭代的内部都有__iter__方法
只要是迭代器 一定可迭代
可迭代的.__iter__()方法就可以得到一个迭代器
迭代器中的__next__()方法可以一个一个的获取值

我们可以推导一下, for循环其实就是在使用迭代器
  只有是可迭代对象的时候 才能用for
  当我们遇到一个新的变量,不确定能不能for循环的时候,就判断它是否可迭代
4、迭代器的好处
  从容器中一个一个取值,会将所有的值取到;
  节省内存空间:
 迭代器不会在内存中再占用一大块内存;
  每次next,都会给我们一个新的

二、生成器函数

1、生成器的引入场景

#如果想要一个包含2百万个ILOVEU的字符串,如何取得?如此取只会得到最后一个def func():
    for i in  range(2000000):
        i = 'ILOVEU%s'%i
    return i

print(func())

改进一下:

def generate_str():
    li = []
    for i in range(200):
        s = 'ILOVEU No' + str(i) + ' '
        # print(s)
        li.append(s)
    return ','.join(li)

print(generate_str())
如此做的确可以生成一个包含200万个’ILOVEU No%d‘的字符串,
但是如此使用迭代器无优势!因为它一次性在内存中生成,浪费内存空间,而迭代器的优点恰恰是节省内存!

总结:

理清迭代器的原理,可以理解for循环的是如何工作的,另外可以对了解生成器做铺垫!
迭代器虽然有很多优点,如节省内存空间,但是不能满足我们的所有需求;
如果我们平时在写代码过程中,要产生大量的数据,又不希望一次性在内存中生成,而且还要处处使用它,基于此种情况,
必须有我们自己写的迭代器!它就是生成器!

2、生成器有两种表现形式:

  1)、生成器函数 --- 本质上就是我们自己写的函数
  2)、生成器表达式

3、生成器函数的定义:

    只要含有yield关键字的函数都是生成器函数
 1 ef generator():
 2     print(1)
 3     #return 'a'
 4     yield 'b' # yield不能和return共用且需要写在函数内
 5 
 6 res = generator()  # 生成器函数 : 执行之后会得到一个生成器作为返回值
 7 print(res)  # <generator object generator at 0x0000019C239AD4C0>
 8 
 9 def generator_full():
10     print(1)
11     yield 'a'  # yield不会结束函数
12     print(2)
13     yield 'b'
14     yield 'c'
15 g = generator_full()
16 print(dir(g))  # 它有'__iter__'
17 
18 ret = g.__next__()
19 print(ret)  # 执行步骤:1)执行g.__next__()双下函数;2)调用generator_full函数;3) print(1);4)执行:yield 'a' 返回a,但不退出函数,等待
20 ret = g.__next__()
21 print(ret)# 执行步骤:1)执行g.__next__()双下函数;2)调用generator_full函数;3) print(2); 4)yield 'b' 返回b,但不退出函数,等待
22 # ret = g.__next__()
23 # print(ret)# 执行步骤:1)执行g.__next__()双下函数;2)调用generator_full函数;3)yield 'c' 返回c,但不退出函数,等待
24 # ret = g.__next__()
25 # print(ret)# 执行步骤:1)执行g.__next__()双下函数;2)调用generator_full函数;3)发现没有yield,报错StopIteration
26 
27 for i in g:  # 接着上面的继续迭代剩下的元素
28     print(i)
29 
30 # 用for循环迭代后,无法再迭代了!
31 # ret = g.__next__()
32 # print(ret)
View Code

4、使用生成器函数来生成200万个“ILOVEU No%d”

def s_generator():
    for i in range(200):
        yield 'ILOVEU No%d'%i

s_g = s_generator()
# for i in s_g:
#     print(i)

继续深入

 1 #需求:我要取出前50个ILOVEU
 2 for i in range(50):
 3     print(s_g.__next__())
 4 
 5 i = 0
 6 # 发现是按序号接着取元素,从ILOVEU No50开始取
 7 while i < 50:
 8     i += 1
 9     print('----',s_g.__next__())
10 # 生成器与迭代器一样,会记录当前取到元素的位置,以及下一个元素的位置,随时都可以得到下一个元素!
11 
12 # 对比for循环列表(list,不是迭代器,但是可以迭代)
13 li = [2, 4, 6, 8, 10]
14 for i in li:
15     print(i)
16     if i == 6:
17         break
18 
19 for j in li:
20     print(j)
21 # 我们发现,没有从停止的位置的下一个位置取元素,而是重新取出所有元素!
22 # 原因不是list不是迭代器,for循环时,已经将其转换为迭代器,
23 # 而是,两次for的时候的迭代器不是同一个!
24 
25 #我们再来看生成器s_generator的例子
26 g1 = s_generator()
27 g2 = s_generator()
28 print(g1.__next__())
29 print(g2.__next__())
30 # 结果都是ILOVEU No0,说明同一个生成器(同时也是迭代器),__next__时才会记住元素位置
View Code

 

5、监听文件输入的内容,实时打印到命令行中

#普通方法:
def trip(file_path):
    f = open(file_path, encoding='utf-8')
    while 1:
        line = f.readline() # 每次读一行
        if line.strip():
            print(line.strip()) # 缺点:不能返回line,如果return,循环就终止了

#trip('../day12_func/user_info')

def monitor(file_path):
    f  = open(file_path, mode='r', encoding='utf-8')
    while 1:
        line = f.readline()
        if line.strip():
            yield line.strip()

g = monitor('../day12_func/user_info')
for i in g:
    if 'python' in i:
        print('*****', i)

##修改文件后,按ctrl+s才能有效果!打印结果!
posted @ 2019-11-28 11:59  四方游览  阅读(296)  评论(0)    收藏  举报