python基础:迭代器、生成器(yield)详细解读

1. 迭代器

迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。

1.1 使用迭代器的优点

对于原生支持随机访问的数据结构(如tuple、list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值)。但对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式。

另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。

迭代器更大的功劳是提供了一个统一的访问集合的接口,只要定义了__iter__()方法对象,就可以使用迭代器访问。

迭代器有两个基本的方法

  • next方法:返回迭代器的下一个元素
  • __iter__方法:返回迭代器对象本身

下面用生成斐波那契数列为例子,说明为何用迭代器

代码1

 def fab(max):
    n, a, b = 0, 0, 1
    while n < max:
        print b
        a, b = b, a + b
        n = n + 1

直接在函数fab(max)中用print打印会导致函数的可复用性变差,因为fab返回None。其他函数无法获得fab函数返回的数列。

代码2

 def fab(max):
    L = []
    n, a, b = 0, 0, 1
    while n < max:
        L.append(b)
        a, b = b, a + b
        n = n + 1
    return L

代码2满足了可复用性的需求,但是占用了内存空间,最好不要。

代码3

对比

 for i in range(1000): pass
 for i in xrange(1000): pass

前一个返回1000个元素的列表,而后一个在每次迭代中返回一个元素,因此可以使用迭代器来解决复用可占空间的问题

 class Fab(object):
    def __init__(self, max):
        self.max = max
        self.n, self.a, self.b = 0, 0, 1 

    def __iter__(self):
        return self 

    def next(self):
        if self.n < self.max:
            r = self.b
            self.a, self.b = self.b, self.a + self.b
            self.n = self.n + 1
            return r
        raise StopIteration()

执行


1

2

3

4

5

6

7

8

9

>>> for key in Fabs(5):

    print key

    

1

1

2

3

5

Fabs 类通过 next() 不断返回数列的下一个数,内存占用始终为常数  

1.2 使用迭代器

使用内建的工厂函数iter(iterable)可以获取迭代器对象:


1

2

3

4

>>> lst = range(5)

>>> it = iter(lst)

>>> it

<listiterator object at 0x01A63110>

使用next()方法可以访问下一个元素:


1

2

3

4

5

6

>>> it.next()

0

>>> it.next()

1

>>> it.next()

2

python处理迭代器越界是抛出StopIteration异常


1

2

3

4

5

6

7

8

9

10

11

12

>>> it.next()

3

>>> it.next

<method-wrapper ‘next‘ of listiterator object at 0x01A63110>

>>> it.next()

4

>>> it.next()

Traceback (most recent call last):

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

    it.next()

StopIteration

了解了StopIteration,可以使用迭代器进行遍历了

lst = range(5)
it = iter(lst)
try:
    while True:
        val = it.next()
        print val
except StopIteration:
    pass

结果


1

2

3

4

5

6

>>>

0

1

2

3

4

事实上,因为迭代器如此普遍,python专门为for关键字做了迭代器的语法糖。在for循环中,Python将自动调用工厂函数iter()获得迭代器,自动调用next()获取元素,还完成了检查StopIteration异常的工作。如下

>>> a = (1, 2, 3, 4)
>>> for key in a:
    print key

1
2
3
4

首先python对关键字in后的对象调用iter函数迭代器,然后调用迭代器的next方法获得元素,直到抛出StopIteration异常。

1.3 定义迭代器

下面一个例子——斐波那契数列

# -*- coding: cp936 -*-
class Fabs(object):
    def __init__(self,max):
        self.max = max
        self.n, self.a, self.b = 0, 0, 1  #特别指出:第0项是0,第1项是第一个1.整个数列从1开始
    def __iter__(self):
        return self
    def next(self):
        if self.n < self.max:
            r = self.b
            self.a, self.b = self.b, self.a + self.b
            self.n = self.n + 1
            return r
        raise StopIteration()

print Fabs(5)
for key in Fabs(5):
    print key
    

结果


1

2

3

4

5

6

<__main__.Fabs object at 0x01A63090>

1

1

2

3

5

2. 生成器

带有 yield 的函数在 Python 中被称之为 generator(生成器),几个例子说明下(还是用生成斐波那契数列说明)

可以看出代码3远没有代码1简洁,生成器(yield)既可以保持代码1的简洁性,又可以保持代码3的效果

代码4 

def fab(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1

执行


1

2

3

4

5

6

7

8

9

>>> for n in fab(5):

    print n

    

1

1

2

3

5

简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

也可以手动调用 fab(5) 的 next() 方法(因为 fab(5) 是一个 generator 对象,该对象具有 next() 方法),这样我们就可以更清楚地看到 fab 的执行流程:


1

2

3

4

5

6

7

8

9

10

11

12

13

>>> f = fab(3)

>>> f.next()

1

>>> f.next()

1

>>> f.next()

2

>>> f.next()

Traceback (most recent call last):

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

    f.next()

StopIteration

return作用

在一个生成器中,如果没有return,则默认执行到函数完毕;如果遇到return,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。例如


1

2

3

4

5

6

7

8

9

>>> s = fab(5)

>>> s.next()

1

>>> s.next()

Traceback (most recent call last):

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

    s.next()

StopIteration

代码5  文件读取

 def read_file(fpath):
    BLOCK_SIZE = 1024
    with open(fpath, ‘rb‘) as f:
        while True:
            block = f.read(BLOCK_SIZE)
            if block:
                yield block
            else:
                return

如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取。

 3.列表生成式→生成器

只要把一个列表生成式的[]改成(),就创建了一个generator:

1 #列表生成式
2 li=[x*x for x in range(10) if x%2==0]  #占用内存
3
4
5 #生成器
6 g=(x*x for x in range(10) if x%2==0)
7 #用for来循环访问,不用考虑异常溢出问题
8 for i in g:
9     print(i)

4.迭代器读取文件

使用迭代器一个显而易见的好处就是:每次只从对象中读取一条数据,不会造成内存的过大开销。

比如要逐行读取一个文件的内容,利用readlines()方法,我们可以这么写:

12
for line in open("test.txt").readlines():    print line

这样虽然可以工作,但不是最好的方法。因为他实际上是把文件一次加载到内存中,然后逐行打印。当文件很大时,这个方法的内存开销就很大了。

利用file的迭代器,我们可以这样写:

12
for line in open("test.txt"):   #use file iterators    print line

这是最简单也是运行速度最快的写法,他并没显式的读取文件,而是利用迭代器每次读取下一行。

时间: 2024-10-25 07:33:04

python基础:迭代器、生成器(yield)详细解读的相关文章

python 【迭代器 生成器 列表推导式】

python [迭代器  生成器  列表推导式] 一.迭代器 1.迭代器如何从列表.字典中取值的 index索引 ,key for循环凡是可以使用for循环取值的都是可迭代的可迭代协议 :内部含有__iter__方法的都是可迭代的迭代器协议 :内部含有__iter__方法和__next__方法的都是迭代器 print(dir([1,2,3])) lst_iter = [1,2,3].__iter__() print(lst_iter.__next__()) print(lst_iter.__ne

python学习之生成器yield

python学习之生成器yield **yield的作用是使函数生成一个结果序列而不是一个值,任何使用yield的函数都称为生成器,调用生成器会创建一个对象,该对象通过连续调用next()或者__next__()方法生成结果序列** 一般情况 >>> def count(n,m): >>> print('这是一个循环外部测试') >>> while n>0: >>> print('这是一个循环内部测试') >>>

python基础-迭代器和生成器

一.递归和迭代 1.递归:(问路示例) 递归算法是一种直接或者间接地调用自身算法的过程.在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于理解. 2.迭代:简单理解为更新换代( 儿子生孙子的故事) 二.迭代器协议 1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退) 2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter_

【python基础】生成器&amp;迭代器

一.生成器:generator 按照规则去生成一定的数据 1.列表推导式和生成器的区别 列表推导式: 一次性生成所有满足条件的数据 生成器: 你要一个数据, 我生成出来给你一个 2.生成器表达式 生成器对象 = (表达式 for item in 容器) 生成器对象 = (表达式 for item in 容器 if 条件) 生成器对象 = (表达式 for item in 容器 for item2 in 容器2) 3.通过生成器对象获取数据 (1)next(g) (2)for in 依次获取生成器

python之迭代器生成器和内置函数,匿名函数

今天学习了迭代器生成器以及内置函数和匿名函数,说实话有些懵圈,有些难度了. 一.迭代器和生成器 1.如何从列表.字典中取值的: index索引 for循环 凡是可以使用for循环取值的都是可迭代的 (1)可迭代协议:内部含有__iter__方法的都是可迭代的 (2)迭代器协议:内部含有__iter__方法和__next__方法的都是迭代器 什么是可迭代的:内部含有__iter__方法的都是可迭代的 什么是迭代器:迭代器=iter(可迭代的),自带一个__next__方法 可迭代最大的优势:节省内

python中的生成器yield

生成器yield:使用yield语句 可以让函数生成一个结果序列而不仅仅是一个值 def  countdow(n):      print("Start!");      while n>0:           yield n;           n -= 1; c = countdow(5); print(c.__next__())  print(c.__next__()) 输出结果: Start! 54__next__()方法使生成器函数一直运行到下一条yield语句为止

python基础-------迭代器,生成器,协程函数

1,迭代器协议: 1.1 迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退) 1.2. 可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法) 1.3. 协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象 2,迭代器: 1.1:为什么要用迭代器: 1.2 优点:迭代器提

python基础----迭代器、生成器、协程函数

一.什么是迭代器协议 1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退) 2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法) 3.协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象. 二,为什么要用迭代器 优点: 1:迭代器提供了一种不依赖于索引的取值方式,

Python基础06 - 生成器、迭代器

@@@文章内容参照老男孩教育 Alex金角大王,武Sir银角大王@@@ 一.生成器 列表生成式 1 a = [i * 2 for i in range(10)] 2 print(a) 3 # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] 生成器 通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的,而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大数元素占用的空间都是浪费的. 所以,

(转)python基础学习-----生成器和迭代器

在Python中,很多对象都是可以通过for语句来直接遍历的,例如list.string.dict等等,这些对象都可以被称为可迭代对象.至于说哪些对象是可以被迭代访问的,就要了解一下迭代器相关的知识了. 迭代器 迭代器对象要求支持迭代器协议的对象,在Python中,支持迭代器协议就是实现对象的__iter__()和next()方法.其中__iter__()方法返回迭代器对象本身:next()方法返回容器的下一个元素,在结尾时引发StopIteration异常. __iter__()和next()