python 迭代器和生成器

1、迭代器

迭代和可迭代协议

迭代器的定义:

我们可以对一个列表进行for循环,而不能对一个整型数据进行for循环。
for i in [1,2,3,4]:  
    print(i)  #1,2,3,4

  

for i in 1234
    print(i) 

结果:
Traceback (most recent call last):
  File "test.py", line 4, in <module>
    for i in 1234:
TypeError: 'int' object is not iterable

报错的结果中出现了一个单词iterable,中文翻译为可迭代的

字符串、列表、元组、字典、集合都可以被for循环,说明他们都是可迭代的。如何证明呢?

from collections import Iterable
                             
l = [1,2,3,4]                
t = (1,2,3,4)                
d = {1:2,3:4}                
s = {1,2,3,4}                
                             
print(isinstance(l,Iterable))
print(isinstance(t,Iterable))
print(isinstance(d,Iterable))
print(isinstance(s,Iterable))
结果都是True

综合以上:可以将某个数据集内的数据“一个挨着一个的取出来”,就叫做迭代

 

可迭代协议

  可以被迭代要满足的要求
就叫做可迭代协议可迭代协议的定义非常简单,就是内部实现了__iter__方法。

使用dir可以查看不同数据类型有哪些方法:


print(dir([]))  #告诉我列表拥有的所有方法
print(dir({}))  #告诉我字典拥有的所有方法
print(dir(''))  #告诉我字符串拥有的所有方法
ret=set(dir([]))&set(dir({}))&set(dir(''))&set(dir(range(10)))
print(ret)

所有的结果中都有__iter__

得出结论:可以被for循环的都是可迭代的,要想可迭代,内部必须有一个__iter__方法。只要是能被for循环的数据类型,就一定拥有__iter__方法

print([1,2].__iter__())

结果
<list_iterator object at 0x1024784a8>

执行了list([1,2])的__iter__方法,我们好像得到了一个list_iterator,现在我们又得到了一个新名词——iterator。中文名称:迭代器。

迭代器

  

'''
dir([1,2].__iter__())是列表迭代器中实现的所有方法,dir([1,2])是列表中实现的所有方法,都是以列表的形式返回给我们的,为了看的更清楚,我们分别把他们转换成集合,
然后取差集。
'''
#print(dir([1,2].__iter__()))
#print(dir([1,2]))
print(set(dir([1,2].__iter__()))-set(dir([1,2])))

结果:
{'__length_hint__', '__next__', '__setstate__'}

我们看到在列表迭代器中多了三个方法,那么这三个方法都分别做了什么事呢?


iter_l = [1,2,3,4,5,6].__iter__()
#获取迭代器中元素的长度
print(iter_l.__length_hint__())
#根据索引值指定从哪里开始迭代
print('*',iter_l.__setstate__(4))
#一个一个的取值
print('**',iter_l.__next__())
print('***',iter_l.__next__())

我们使用__next__方法来获取值。

在for循环中,就是在内部调用了__next__方法才能取到一个一个的值。

那接下来我们就用迭代器的next方法来写一个不依赖for的遍历。


l=[1,2,3]
iterator = l.__iter__()
print(iterator.__next__())    #1
print(iterator.__next__())    #2
print(iterator.__next__())    #3
那现在我们就使用while循环实现了原本for循环做的事情,我们是从谁那儿获取一个一个的值呀?是不是就是l_iter?好了,这个l_iter就是一个迭代器
迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。

2、生成器
迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象通过执行

Python中提供的生成器:

  1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行

  2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

 

生成器函数

  一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。


import time
def genrator_fun1():
    a = 1
    print('现在定义了a变量')
    yield a
    b = 2
    print('现在又定义了b变量')
    yield b

g1 = genrator_fun1()
print('g1 : ',g1)       #打印g1可以发现g1就是一个生成器
print('-'*20)   #我是华丽的分割线
print(next(g1))
time.sleep(1)   #sleep一秒看清执行过程
print(next(g1))
生成器2:
def wahaha():
    '''
    wahaha自增2000000
    :return:
    '''
    for i in range(2000000):
        yield '哇哈哈%s'%i
ret = wahaha()
print(ret)
print(ret.__next__())   #打印哇哈哈0
print(ret.__next__())   #打印哇哈哈1
print(ret.__next__())   #打印哇哈哈2
print(ret.__next__())   #打印哇哈哈3
print(ret.__next__())   #打印哇哈哈4
print(ret.__next__())   #打印哇哈哈5

一次打印完所有哇哈哈(0-1999999)

def wahaha():
    '''
    wahaha自增20000000
    :return:
    '''
    for i in range(20000000):
        yield '哇哈哈%s'%i
ret = wahaha()

for i in ret:
    print(i)

 send方法:

def generator():
    print(12)
    content = yield 1
    print('+++++',content)
    print(23)
    ret = yield 2
    print("====",ret)
    print(45)
    yield 3

g = generator()
res = g.__next__()
print(res)
res = g.send('amy')    #send的效果和next一样
print(res)
res = g.send('zzm')  #send的效果和next一样
print(res) #运行结果: 12 1 +++++ amy 23 2 ==== zzm 45 3

 

更多应用:

import time


def tail(filename):
    f = open(filename)
    f.seek(0, 2) #从文件末尾算起
    while True:
        line = f.readline()  # 读取文件中新的文本行
        if not line:
            time.sleep(0.1)
            continue
        yield line

tail_g = tail('tmp')
for line in tail_g:
    print(line)
生成器监听文件输入
def average():
    sum = 0
    count = 0
    avg = 0
    while True:
        num =yield avg
        sum += num
        count += 1
        avg = sum/count

avg_g = average()
avg_g.__next__()
avg1 = avg_g.send(1)   #发送一个数:1
avg1 = avg_g.send(2)    #发送一个数:2
avg1 = avg_g.send(3)    #发送一个数:3
avg1 = avg_g.send(4)    #发送一个数:4
avg1 = avg_g.send(5)    #发送一个数:5
print(avg1)
移动平均数
def oter(func):
    def inner(*args,**kwargs):
        g = func(*args,**kwargs)
        g.__next__()
        return g
    return inner
@oter
def generator():
    sum = 0
    count = 0
    avg = 0
    while True:
        num = yield avg
        sum += num
        count +=1
        avg = sum/count
avg_g = generator()
ret = avg_g.send(10)
print(ret)
ret = avg_g.send(20)
print(ret)
计算移动平均值的预激协程的装饰器

yield from

yield from可以从一个容器类中依次取值

优化前:
def generater():
    l=[3,334,54,54,3,5,2,4]
    s='2345dsdfghbvc'
    for i in l:
        yield i
    for i in s:
        yield i
g = generater()      #得到生成器g
for i in g:
    print(i

优化后:
def generater():
    l=[3,334,54,54,3,5,2,4]
    s='2345dsdfghbvc'
    yield from l    #从l中循环取值
    yield from s    #从2中循环取值
g = generater()      #生成器g
for i in g:
    print(i)

 

列表推导式:

g = (i for i in range(10))
for i in g:
    print(i)
生成器表达式
g = (i for i in range(10))    #10以内依次打印
print(i)

lis = [i*i for i in range(10)]  #10以内的值平方
print(lis)

li=[i*i for i in range(30) if i%3==0]     #有条件的 能整除3的值得平方
print(li)
列表推导式
# mcase={'a':10,'b':34}
# mcase_frequency = {mcase[k]:k for k in mcase}
# print(mcase_frequency)

对字典中的键值对进行翻转

# mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
# mcase_new = {k.lower():mcase.get(k.lower(),0)+mcase.get(k.upper(),0) for k in mcase}
# print(mcase_new)

对字典中的所有键值对,合并大小写对应的value值,将k统一成小写
字典推导式
计算列表中每个值的平方,自带去重功能
squared = {x**2 for x in [1, -1, 2]}
print(squared)
# Output: set([1, 4])
集合推导式

 


 

 
 
posted @ 2018-01-02 20:28  amyleell  阅读(99)  评论(0)    收藏  举报