美文网首页
一周一个Python语法糖:(二)迭代器与生成器

一周一个Python语法糖:(二)迭代器与生成器

作者: 寒夏凉秋 | 来源:发表于2017-04-10 22:03 被阅读0次

迭代器(iterator):

  • 遍历类序列对象的方法
  • 迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问一遍后结束。
  • 迭代器不能回退,只能往前进行迭代
  • 迭代器也不是线程安全的,在多线程环境中对可变集合使用迭代器是一个危险的操作。
  • 迭代器的另一个优点就是它不要求你事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代至某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。这个特点被称为延迟计算或惰性求值(Lazy evaluation)。

惰性计算想像成水龙头,需要的时候打开,接完水了关掉,这时候数据流就暂停了,再需要的时候再打开水龙头,这时候数据仍是接着输出,不需要从头开始循环

1. 迭代器的生成

iter()函数 构造一个迭代器,接受的参数是列表,字典,string 等可迭代的参数

iter1=iter(range(4))
iter1.next()#python2
next(iter1)#python3
工作流程

总不能每次都调用next 去获取值吧(

itertest=iter(range(4))
try:
    while True:
        print(itertest.next())
except StopIteration:
    pass

语法糖:for in

itertest=iter(range(4))
for i in itertest:
    print(i)

当需要知道迭代的索引(通俗讲就是’数组下标‘)的时候,可以用python内置的enmumerate():

for index,value in enumerate(iter1):
        #doSomething()

但是,为何不直接用 :

for i in range(4):
    print(i)

这只是简单的迭代器,当你去做一个无法用列表表达式生成的复杂迭代器的时候,你就需要用自定义的迭代器,然后进行迭代了

2. 自定义迭代器:

迭代器的实现需要实现迭代器协议:

  • 实现了方法 __iter__(),返回一个迭代对象,这个对象有一个next()方法
  • 实现__next()__方法,返回当前元素,并指向下一个元素的位置,
    当前位置已经没有元素的时候,抛出StopIteration异常。
class Iter(object):
    def __init__(self,n):
        self.i=0
        self.n=n
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.i< self.n:
            self.i+=1
            return self.i
        else:
            raise StopIteration()
iter1=Iter(5)
print(next(iter1))
print(next(iter1))
print(next(iter1))
#out  1
#out  2
#out  3

生成器与yield语法

(迭代器这么简单,算语法糖吗?!!)
不急,这才是我们的重头菜!
普通的函数:

函数只有一次返回结果的机会,因而必须一次返回所有的结果。

当我们遇到异步io等问题的时候,我们面临的场景:

  • 网络阻塞,cpu一直卡在这(多线程可以等一会儿)
  • 不阻塞,直接得到结果

结果二当然是我们理想的,结果一好像也可以用多线程去解决
但是,多线程wait的时候进程蹦了呢~,那我们就一无所获了
我可以不可以:
网络io的时候得到一部分就返回一部分内容呢?

这就需要生成器了!!

一个生成器函数的定义很像一个普通的函数,除了当它要生成一个值的时候,使用yield关键字而不是return。如果一个def的主体包含yield,这个函数会自动变成一个生成器(即使它包含一个return)。
生成器函数返回生成器的迭代器

定义生成器:

def simple_generator_function():
    yield '1'
    yield '2'
    yield '3'

为了从生成器获取下一个值,我们使用next()函数,就像对付迭代器一样

#我们在解释器中进行这段代码方便看next()值
>>> our_generator = simple_generator_function()
>>> next(our_generator)
'1'
>>> next(our_generator)
'2'
>>> next(our_generator)
'3'

生成器执行流程:

  • 当调用生成器函数的时候,函数只是返回了一个生成器对象,并没有 执行。
  • 当next()方法第一次被调用的时候,生成器函数才开始执行,执行到yield语句处停止
  • next()方法的返回值就是yield语句处的参数(yielded value)
  • 当继续调用next()方法的时候,函数将接着上一次停止的yield语句处继续执行,并到下一个yield处停止;如果后面没有yield就抛出StopIteration异常

生成器是可以迭代的,但是你只可以读取它一次,因为它并不把所有的值放到内存,而是实时生成的数据

我们来看个简单的列子吧:
当我们需要知道斐波那契数列:
我们通常通过迭代关系获取整个列表(我学C语言的时候就是开整个大数组再去迭代,或者直接递归得到)

def fiblist(n):
    a = b = 1
    result = []
    for i in range(n):
        result.append(a)
        a, b = b, a + b
    return result

对,没毛病~
但是,以下代码不妨一试:(记得开内存查看器)

for x in fiblist(100000):
    print(x)

(我只试了下100000~~~~~炸得飞起)

来来来,优化下:
我们用生成器来做一下:斐波那契数列

def fib(n):
    a = b = 1
    for i in range(n):
        yield a
        a, b = b, a + b

瞬间 就优雅起来~~~~~ 起码我的内存消耗降下来了~

相关文章

网友评论

      本文标题:一周一个Python语法糖:(二)迭代器与生成器

      本文链接:https://www.haomeiwen.com/subject/uqgdattx.html