python迭代器+生成器基础

一、迭代器iterator

1.简介:

    迭代器是访问集合内元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问一遍后结束。

  • 优点:

    i.对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式。

    ii.迭代器不要求你事先准备好整个迭代过程的所有的元素,它仅仅是在迭代至某一个元素时才计算该元素。在这之前或者之后,元素可以不存在或者被销毁。适合巨大的或者无限的集合。

    iii.迭代器提供了一个统一的访问集合的接口。只要实现了iter()方法的对象,就可以使用迭代器进行访问。

  • 缺点

    i.迭代器无法回退,只能向前进行迭代。

    ii.迭代器不是线程安全的,在多线程环境中对可变集合使用迭代器是一个危险的操作。

2.简单使用:

使用内建函数iter可以从可迭代的对象中获得迭代器。

>>it = iter([1, 2, 3])
>>it.next()
1
>>it.next()
2

上述的it.next()可以使用next(it)替换。

如果迭代到最后一位后,再调用it.next()就会出现StopIteration的异常。但是我们可以通过检测到异常信息,来退出迭代器。

使用迭代器的循环可以避开索引,但有时候我们还是需要索引来进行一些操作的。这时候内建函数enumerate就派上用场咯,它能在iter函数的结果前加上索引,以元组返回,用起来就像这样:

>>lst = range(3)
>>for idx, ele in enumerate(lst):
>>    print idx, ele
0 0
1 1
2 2

Tips:使用list的构造方法显式地将迭代器转化为列表、

二、生成器(Generator)

1.简介

    生成器是一种用普通函数语法定义的迭代器。它主要依赖于yield关键字,任何包含yield语句的函数都称为生成器。

    生成器和函数的主要区别在于函数return a value,生成器yield a value同时标记且记忆point of the yield 以便于在下次调用时从标记点恢复执行。

2.示例学习

2.1创建生成器

我们创建一个函数,用来展开列表的列表的值顺序打印。

In [35]: my_list = [[1, 2], [3, 4], [5]]

In [36]: def un_fold(the_list):
    ...:     for _list in the_list:
    ...:         for element in _list:
    ...:             yield element
    ...:             

In [37]: list(un_fold(my_list))
Out[37]: [1, 2, 3, 4, 5]

在这里,就是一个生成器,如果我们将yield element换成print element那么就是一个普通的函数。区别就在于,生成器并没有return一个值,而是产生多个值。每次产生一个值(即使用yield语句),函数就被冻结:重新唤醒后,就会延续刚才运行的状态继续。

2.2递归生成器

Demo:

In [38]: def flatten(the_list):
    ...:     try:
    ...:         for sublist in the_list:
    ...:             for element in flatten(sublist):
    ...:                 yield element
    ...:     except TypeError:
    ...:         yield the_list
    ...: 
In [39]: list(flatten([[[1],2], 3,4, [5, [6, 7]], 8]))
Out[39]: [1, 2, 3, 4, 5, 6, 7, 8]

在函数flatten被调用时存在两种可能性:

  • 1.函数被告知展开一个元素时(如一个数字),这时,for循环会出现一个TypeError,生成器会产生一个元素。

  • 2.如果展开的是一个列表(或者其他迭代器对象),那么程序就会遍历所有的子列表(一些也可能不是列表),并对他们调用flatten函数。然后用另一个flatten函数展开子列表中的所有元素。

但是上述的例子由一个问题。如果the_list是一个类似字符串的对象。那么它就是一个序列,不会引发TypeError,但是我们不想对这样的对象进行迭代。
所以我们可以在生成器的开始添加一个检查语句。如下

# demo2
In [6]: def flatten(the_list):
   ...:     try:
   ...:         try: the_list + ''
   ...:         except TypeError: pass
   ...:         else: raise TypeError
   ...:         for sublist in the_list:
   ...:             for element in flatten(sublist):
   ...:                 yield element
   ...:     except TypeError:
   ...:         yield the_list
   ...:         

In [7]: list(flatten(['foo', ['bar', ['baz']]]))
Out[7]: ['foo', 'bar', 'baz']
2.3通用生成器

当生成器被调用时,在函数体的代码不会被执行,而会返回一个迭代器。每次请求一个值,就会执行生成器的代码,直到遇到一个yield或者return语句。yield语句意味着应该生成一个值,return语句意味着生成器要停止运行(不在生成任何东西,return语句只有在一个生成器中使用时才能 进行无参数调用).

换句话说,生成器由两部分构成:生成器的函数和生成器的迭代器。生成器的函数是用def定义的,包含yield的部分。生成器的迭代器是这个函数返回的部分。

# demo
In [1]: def simple_generator():
   ...:     yield 1
   ...:     

In [2]: simple_generator
Out[2]: <function __main__.simple_generator>

In [3]: simple_generator()
Out[3]: <generator object simple_generator at 0x7f2b98d66dc0>

Tips: 在生成器中返回的迭代器,可以像其他的迭代器一样使用。

2.4生成器方法

1.send()方法

  • 在外部作用域访问生成器的send()方法就像访问next()方法一样,只不过前者使用一个参数(即要发送的消息---任意对象)

  • 在内部则挂起生成器,yield现在作为表达式而不是语句来用,也就是说,在生成器重新运行时,yield函数返回一个值,也就是外部通过send方法发送的值,如果next方法被使用,那么yield返回None

注意在使用send方法,只有在生成器挂起的时候,才会有意义,否则就要提供更多信息。

# demo 说明这种机制
In [4]: def repeater(value):
   ...:     while True:
   ...:         new = (yield value)
   ...:         if new is not None:
   ...:             value = new
   ...:             

In [5]: r = repeater(100)

In [6]: r.next()
Out[6]: 100

In [7]: r.send(101)
Out[7]: 101

In [8]: r.send("Alex")
Out[8]: 'Alex'

2.throw方法:用于在生成器内引发一个异常(在yield表达式中)

3.close方法:调用时不需要参数, 用于停止生成器。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容