python中的迭代器

Python中有一类工具叫做迭代工具,它能从左至右扫描对象。这包括了for循环、列表解析、in成员关系测试以及map内置函数等。可以用在上述迭代工具环境中,通过一次次迭代不断产生结果的对象称为可迭代对象,即是Iterable。

实际上可迭代对象分为两大类,一种是实际保存的序列,即列表、元组,字符串;另一种就是 “不一次性产生所有结果列表,而是可以在for循环中按需一次产生一个结果的对象”。如:range函数返回值、zip函数返回值、enumerate函数返回值等等,与实际序列相对应,这个叫做按需产生对象的虚拟序列。

而迭代器是一个含有 next() 方法的对象,可以被next()函数不断调用并返回下一个值(迭代器从对象的第一个元素开始访问),直到所有的元素被遍历后结束,超出范围后能够抛出StopIteration的错误停止迭代。

迭代器可以使你迭代不是序列而表现出序列行为的对象,如字典的键、一个文件的行等。当你使用循环迭代一个对象条目时,几乎分不出它是迭代器还是序列,也不必去关心这些,python可以让它像一个序列那样操作。

>>> from collections import Iterable

>>> isinstance([],Iterable)

True

>>> dir([])

['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

对象里面包含__iter()__方法的实现,但它没有next方法,不是一个迭代器。

>>> from collections import Iterator

>>> isinstance([],Iterator)

False

>>> isinstance({},Iterator)

False

>>> isinstance(str,Iterator)

False

list、dict、str虽然是Iterable,却不是Iterator,把list、dict、str等Iterable变成Iterator需要调用对象中的iter方法,调用成功后会返回一个迭代器,里面包含具体数据获取的实现。迭代器为类序列对象提供了一个类序列的接口(只要是实现了 __iter__() 方法的对象,就可以使用迭代器来进行访问)。

>>> isinstance(iter([]),Iterator)

True

>>>

iter()函数实现将一个可迭代对象转换为迭代器,然后调用next()方法就可以按需取出元素。

>>> alist=[1,2,4,5,'a']

>>> iter_list=iter(alist)          # Invokes alist.__iter__()

>>> next(iter_list)              # Invokes iter_list.__next__()

1

>>> next(iter_list)

2

>>>

一般我们会使用for循环语句用来遍历一个可迭代对象,因此迭代器也可以作用于for循环语句,这样可以将迭代器中元素依次取出

>>> alist=[1,2,4,5,'a']

>>> for i in alist:

...      print(i,end=' ')

...

1 2 4 5 a

>>>

>>>iter_list= iter(alist)          # Invokes alist.__iter__()

>>> for i in iter_list:

...      print(i,end=' ')

...

1 2 4 5 a

>>> next(iter_list)              #迭代完毕,抛出异常

Traceback (most recent call last):

  File "<pyshell#14>", line 1, in <module>

    next(iter_list)

StopIteration

>>>

python的for循环机制本质上就是需要下一项时通过不断调用next()函数实现的,实际上完全等价于:

# 首先获得Iterator对象:

it = iter(alist)                # Invokes alist.__iter__()

# 循环:

while True:

    try:

        # 依次获得下一个值:

        x = next(it)            # Run the iterator, Invokes it.__next__()

    except StopIteration:

        # 遇到StopIteration就退出循环

        break

for这个语法背后的是首先获取可迭代对象返回的迭代器对象,然后不断调用迭代器对象的next方法获取每个值,在获取值的过程中随时检测边界-也就是检查是否抛出了StopIteration这样的异常,如果迭代器对象抛出错误则迭代停止,异常只是告诉外部调用者,迭代完成。

或者当任何可迭代对象传入到for循环或其他迭代工具(如list)中进行遍历时,迭代工具都是先通过iter函数获得与可迭代对象对应的迭代器,然后再对迭代器调用next函数,不断的依次获取元素,并在捕捉到StopIteration异常时确定完成迭代,这就是完整的迭代过程。这也称之为“迭代协议”。

使用迭代器的理由

首先看那么为什么list、dict、str等数据类型不是Iterator?

这是因为python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

它与列表的区别在于,构建迭代器的时候,不像列表把所有元素一次性加载到内存中,而是一种延时计算方式返回元素,如列表中含有一亿个数据(假如整型占4个字节),需要内存400M,而迭代器只需要几十个字节的空间,这是因为它没有把所有元素加载到内存中,而是等待调用next方法才返回该元素(按需调用call by need的方式)。

1、“流式”数据处理方式减少内存消耗:

比如处理文件,一下猛地把全部数据全部取出来放到内存里面进行处理会导致程序消耗大量内存,有时甚至没法做到,一般我们会一部分一部分的对文件内容进行处理:

for text_line in open("xx.txt"):

  print text_line

open("xx.txt")返回的文件对象是迭代器,所以可以渐进式地对文件的内容进行处理,即按行来读取文件(自动调用readline()方法),并进行处理,而不是直接把全部文件一下加载到内存中。

如下读取一个大文件,要求输出相同时间戳内状态为200的平均连接数

#序号 时间戳 连接数 转态码

[root@localhost newtest]# cat cookies.txt

1 12345 12 200

2 12345 11 100

3 12345 19 200

4 12346 11 200

5 12346 19 200

6 12347 11 200

7 12348 10 200

8 12348 11 100

9 12348 11 200

10 12348 12 200

统计脚本

def linknumpersecond(filename):


    fp=open(filename,'r')


    fronttimestamp=0                      #初始状态

    persecondlinknum=0

    cnt=0


    for line in fp:


        linelist = line.split(' ')                  #当前处理结果

        status = linelist[3].strip('\n')

        timestamp = linelist[1]

        linknum=int(linelist[2])


        if timestamp==fronttimestamp and status=='200':

            persecondlinknum+=linknum

            cnt+=1


        else:

            if timestamp !=fronttimestamp:          #如果和上一次不一样,就输出上一次的统计结果

                print(fronttimestamp,persecondlinknum,cnt)

                if status=='200':

                    persecondlinknum=linknum              #重新计算新的时间戳对应的连接数

                    fronttimestamp=timestamp


    print(fronttimestamp,persecondlinknum,cnt)      #最后的时间戳

测试用例

>>> linknumpersecond('/newtest/cookies.txt')

(0, 0, 0)

('12345', 31, 1)

('12346', 30, 2)

('12347', 11, 2)

('12348', 33, 4)

>>>

2、支持方便用for语句对数据进行处理

python内置的一些常见的类型像数组、列表甚至字符串等都是可迭代类型,这样我们就能方便for语句这个语法方便对数据进行消费,不需要自己记录索引位置。

1、 在字典和列表中迭代会带来性能上的提升

2、 迭代非序列集合(如映射、文件)时,代码更简洁可读

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

推荐阅读更多精彩内容