Python Iterator and Generator

Python Iterator and Generator

Iterator

? 迭代器(Iterator)和可迭代对象(Iterable)往往是绑定的。可迭代对象就是我们平时经常用的list ,string, tuple这种。事实上迭代器的概念会比可迭代对象宽泛很多,一会举几个例子就能明白。

? 在使用list这种数据类型的时候,我们经常会使用下面这种迭代方式:

# eg 1
mylist = [1,2,3,4]
for x in mylist:
    print(x)
>>>1
>>>2
>>>3
>>>4

? 有时候会很奇怪for循环为什么可以这么用,列表的索引竟然会自动往后一个一个走,走到结尾了,还会自动停下来,不会有list out of range的报错。神奇。其实for循环做的事情不止这么简单。要说明这个问题,得先说迭代器具体怎么做,是什么。

? 要创建一个迭代器,就必须要实现两个方法,分别是__iter__()__next__(),以及实现异常机制StopIteration。请看下面的例子:

# eg 2
class PowTwo:
    """Class to implement an iterator of powers of two"""

    def __init__(self, max = 0):
        self.max = max

    def __iter__(self):
        self.n = 0
        return self

    def __next__(self):
        if self.n <= self.max:
            result = 2 ** self.n
            self.n += 1
            return result
        else:
            raise StopIteration

? 可以看到,迭代器是通过写一个类来定义的,类里面实现了刚刚所说的__iter__()__next__()方法。这个迭代器是用来产生一系列2的指数次方的数字的,具体用法看下面:

a = PowTwo(4)
i = iter(a)         # attention please
next(i)
>>>1
next(i)
>>>2
next(i)
>>>4
next(i)
>>>8
next(i)
>>>16
next(i)
>>>Traceback (most recent call last):
    ...
    StopIteration

? 仔细看哦,第一行代码用4创建了一个实例a,设置了这个迭代器的迭代上限,然后并不是直接用这个实例就可以了,还得调用iter()函数去把a彻底进化成一个Iterator,进而有了接下来next()函数的闪亮登场。其实我也蛮奇怪,直接用这个类不好么,比如下面的代码:

a = PowTwo(4)
a.__iter__()
a.__next__()
>>>1
a.__next__()
>>>2
a.__next__()
>>>4
a.__next__()
>>>8
a.__next__()
>>>16
next(i)
>>>Traceback (most recent call last):
    ...
    StopIteration

? 完全没问题,但是你自己比较一下二者的代码,哪个美一点毋庸置疑。。。再说了,跟装饰器一样,一切为了简洁优美而生嘛。

? OK,可以回到最初的起点——for循环了。示例1中的代码,for循环到底做了什么呢,答案是,for循环其实是先把mylist变成迭代器,然后用while循环去迭代:

iter_obj = iter(mylist)
while True:
    try:
        element = next(iter_obj)
    except StopIteration:
        break

? 这样一路解释过来,应该就不难理解迭代器了。总结一下就是:

  1. 如何已经有一个可迭代对象,那么直接用iter()函数去初始化它,然后疯狂next()即可;
  2. 如果没有现成的可迭代对象,那就自己写一个类,类里面记得实现__iter__()__next__()方法,以及异常机制StopIteration,然后操作同1;
  3. 如果你想要一个无限迭代器,不要实现异常机制StopIteration即可。



Generator

? 这玩意儿就比迭代器复杂点了,所以还得分几个小点,逐个击破。

1. 生成器是啥

? 生成器也是Python中面向一系列需要迭代的问题,常用的解决方案。既然有了迭代器,可以解决很多迭代的问题,那为啥子还要生成器勒?

? 主要的原因是迭代器的开销太大了。对于一些小问题还好,大的问题需要迭代的元素很庞大的时候,迭代器就使不上劲儿了。而且,创建一个迭代器,说实话也还挺麻烦的,看看上面的小总结的第二点,你得实现这些方法和手动处理异常。

? 而且迭代器要写类,其实一个函数可以搞定的事情,何必那么复杂。正是应了这个景,生成器也就是在函数对象上搞事情的。

? 这个原因点到即止,先把生成器讲清楚了,自然就通透了。先看一个小例子,写一个生成器函数(Generator Function):

# eg 1
def my_gen():
    n = 1
    print('This is printed first')
    yield n

    n += 1
    print('This is printed second')
    yield n

    n += 1
    print('This is printed at last')
    yield n

? 上面就是一个简单的生成器函数,多了一种关键字yield,细心看会发现这个函数竟然没有return!再细看代码,没错,yield替代了return。那怎么用呢,有两种用法如下:

# usage 1
a = my_gen()
next(a)
>>>This is printed first
    1
next(a)
>>>This is printed second
    2
next(a)
>>>This is printed at last
    3
next(a)
>>>Traceback (most recent call last):
    ...
    StopIteration
# usage 2
for item in my_gen():
    print(item)
>>>
This is printed first
1
This is printed second
2
This is printed at last
3

? 对于用法1,把函数赋值给了a,然乎疯狂next()即可。你会发现,我们并没有像迭代器那样实现__iter__()__next__()方法,以及异常机制StopIteration,只是用了一个yield关键字,这个生成器函数却达到了迭代器一样的效果。

? 对于用法2,更牛皮了,甚至不用赋值的操作,直接for这个生成器函数。。。

2. 循环机制生成器

? 刚刚那个小函数,就是一个最普通的例子,那问题是如果有多个n想要玩,岂不是来多少手动写多少?那当然还有循环的玩法,带有循环机制的生成器。下面是一个逆序输出的小例子:

# eg 2
def rev_str(my_str):
    length = len(my_str)
    for i in range(length - 1,-1,-1):
        yield my_str[i]

for char in rev_str("hello"):
     print(char)
>>>o
>>>l
>>>l
>>>e
>>>h

? 没错,真无聊,犯得着逆序输出的程序还得上生成器么,犯不着,但是,只是想给出这个循环机制生成器的概念。如果你发现这个rev_str()函数和普通的逆序函数完全一样,只是return换成了yield,那就万事大吉,要理解的就是这个。就这样一记简单的替换操作,你就得到了一个生成器,是不是比迭代器省事儿多了。

3. 生成器表达式(Generator Expression)

? 不知道你有没有用过列表生成式,没用过也应该看到过,这类似于匿名函数,语法简洁,比如:

# eg 3
my_list = [1, 3, 6, 10]

[x**2 for x in my_list]
>>>[1, 9, 36, 100]

? 生成器表达式和这个几乎一样,不信你看:

# eg 4
my_list = [1, 3, 6, 10]

a = (x**2 for x in my_list)
next(a)
>>>1
next(a)
>>>9
next(a)
>>>36
next(a)
>>>100

? 把列表生成式的[]直接改成(),就得到了一个生成器。

4. Why Generator???

(1). 简洁

? 回到最开始迭代器的那个类的例子,用生成器咋写呢?

def PowTwoGen(max = 0):
    n = 0
    while n < max:
        yield 2 ** n
        n += 1

? 简洁吧,然后你就可以用这个生成器函数遨游了。

(2). 开销小

? 同样的一个需要迭代的功能,如果用普通函数写,一旦需要迭代的元素特别多,在使用的时候,普通函数需要等所有的元素计算出来了,然后把返回值给你。生成器就不是了,它一次计算出一个,用的时候就取一个,并且它还会记住位置,下次用就计算下一个,这样对空间的开销也是很小的。

(3). 无限

? 看下面的函数:

def all_even():
    n = 0
    while True:
        yield n
        n += 2

? 写普通函数,你必然做不到写出一个可以无限操作的函数,生成器却可以。(迭代器也可以,就是麻烦点儿)

5. 再给一个例子

# 利用yield生成一个斐波那契数列的生成器
def fib(max):
    n,a,b=0,0,1
    while n<max:
        yield b
        a,b=b,a+b
        n+=1
    return 'done'   # 要不要这句话都行
f=fib(6)
next(f)             # 疯狂next()它
>>>
1
1
2
3
5
8
Traceback (most recent call last):
...
StopIteration: done

原文地址:https://www.cnblogs.com/machine-lyc/p/11332358.html

时间: 2024-08-28 11:11:43

Python Iterator and Generator的相关文章

【Python笔记】如何理解python中的generator functions和yield表达式

本篇笔记记录自己对Python的generator functions和yield表达式的理解. 1. Generator Functions Python支持的generator functions语法允许我们定义一个行为与iterator类似的函数,它可以被用在需要循环调用的场合.与普通函数相比,generator functions只是在函数定义中多了1个yield表达式,除此之外,没有其它特别之处. 当generator函数被创建时,python解释器会自动为它实现iteration p

Iterator and Generator

# Iterator 一个对象,代表了遗传数据流,使用__next__()方法或内置函数next() # 返回连续的对象,没有数据返回时,抛出StopIteration异常 # iterable 一个对象,能每次返回数据组中的一个成员 for 循环中每次返回一个值 或 # 内置函数iter()传入参数返回iterator对象 # generator 使用了yield或者生成器表达式,申城iterator对象 用一种方便的 # 方法实现了iterator,在for循环取数据或使用next()取数据

python——iterator迭代器

Iterator(迭代器)模式又称Cursor(游标)模式,用于提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示.或者这样说可能更容易理解:Iterator模式是运用于聚合对象的一种模式,通过运用该模式,使得我们可以在不知道对象内部表示的情况下,按照一定顺序(由iterator提供的方法)访问聚合对象中的各个元素. A. 一个类里面,如果实现了  __iter__ 这个函数, 那这个类就是 “可迭代的” B. 如果它还实现了 next ( self ) ,那它就称为  

Python之生成器(generator)和迭代器(Iterator)

generator 生成器generator:一边循环一边计算的机制. 生成器是一个特殊的程序,可以被用于控制循环的迭代行为.python中的生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,可以使用next()函数和send()函数恢复生成器. 生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用.但是,不同于一般函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这个消耗的内存数量将大大减小.因此,生成器看起来像是一个函数,但是表现得像迭代

Python生成器(generator)和迭代器(Iterator)

列表生成式 a = [i+1 for i in range(10)] print(a) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 这就是列表生成式 生成器(generator) 通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的. 如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的    list,从而节省大量的空间.在Python中,这种一边循环一边计算的机制,称为生成器(gene

python中的generator, iterator, iterabel

先来看看如果遇到一个对象,如何判断其是否是这三种类型: 1 from types import GeneratorType 2 from collectiuons import Iterable, Iterator 3 4 isinstance( xx, GeneratorType ) 5 isinstance( xx, Iterable ) 6 isinstance( xx, Iterator ) 生成器对象: 生成器是一个通过yield关键字构建的函数,其返回一个generator对象,同时

python中的generator(coroutine)浅析和应用

背景知识: 在Python中一个function要运行起来,它在python VM中需要三个东西. PyCodeObject,这个保存了函数的代码 PyFunctionObject,这个代表一个虚拟机中的一个函数对象 PyFrameObject,这个代表了函数运行时的调用链和堆栈 Python正是通过这三样东西模拟0x86的函数调用的 在python中 coroutine(协程)被称为的generator,这两个东西在python其实是同一个东东,之所以如此称呼是因为它有迭代器的功能,但是又可以

python之生成器-generator

在跟着廖雪峰的博客学习python,看到生成器这一章节的时候,首先提到了generator .yield,然后在搜索资料的时候,又查到了协程这一概念,这篇文章总结一下这几个概念. generator  从字面上理解,就是生成器,它的实现方式有两种: 1.不同于列表生成器([]),而是用 ()来表示.(原来这种叫做生成器表达式哦,哈哈) 访问方式,可以用for 循环来访问,也可以用 .next 来访问. N = ['Hello', 'World', 18, 'Apple', 'None'] hh

python学习--生成器Generator

生成器函数:在函数中如果出现了yield关键字,那么这个函数就是生成器函数,yield的作用就是生成一个generator,生成器函数返回一个生成器. 实现一个generator:1.把列表的[ ]换成(),就创建了一个generator. >>> L = [x * x for x in range(10)] >>> L [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> g = (x * x for x in range