python的迭代器与生成器实例详解

本文以实例详解了python的迭代器与生成器,具体如下所示:

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()

执行

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

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

1.2 使用迭代器

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

>>> lst = range(5)
>>> it = iter(lst)
>>> it
<listiterator object at 0x01A63110>

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

>>> it.next()

>>> it.next()

>>> it.next()

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

>>> it.next()

>>> it.next
<method-wrapper 'next' of listiterator object at 0x01A63110>
>>> it.next()

>>> 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

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

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

首先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

结果

<__main__.Fabs object at 0x01A63090>

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

执行

>>> for n in fab(5):
  print n

简单地讲,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 的执行流程:

>>> 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 终止迭代。例如

>>> 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,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取。

(0)

相关推荐

  • 分享Python文本生成二维码实例

    本文实例分享了Python文本生成二维码的详细代码,供大家参考,具体内容如下 测试一:将文本生成白底黑字的二维码图片 测试二:将文本生成带logo的二维码图片 #coding:utf-8 ''' Python生成二维码 v1.0 主要将文本生成二维码图片 测试一:将文本生成白底黑字的二维码图片 测试二:将文本生成带logo的二维码图片 ''' __author__ = 'Xue' import qrcode from PIL import Image import os #生成二维码图片 def

  • Python基于QRCode实现生成二维码的方法【下载,安装,调用等】

    本文实例讲述了Python基于QRCode实现生成二维码的方法.分享给大家供大家参考,具体如下: QR码是一种矩阵码,或二维空间的条码,1994年由日本Denso-Wave公司发明.QR是英文Quick Response的缩写,即快速反应的意思,源自发明者希望QR码可让其内容快速被解码.QR码常见於日本,并为目前日本最流行的二维空间条码.QR码比普通条码可储存更多资料,亦无需像普通条码般在扫描时需直线对准扫描器. qrcode是Python的第三方模块,依赖于Python 图像库:PIL(Pyt

  • Python MD5文件生成码

    import md5 import sys def sumfile(fobj): m = md5.new() while True: d = fobj.read(8096) if not d: break m.update(d) return m.hexdigest() def md5sum(fname): if fname == '-': ret = sumfile(sys.stdin) else: try: f = file(fname, 'rb') except: return 'Fail

  • Python生成随机数的方法

    如果你对在Python生成随机数与random模块中最常用的几个函数的关系与不懂之处,下面的文章就是对Python生成随机数与random模块中最常用的几个函数的关系,希望你会有所收获,以下就是这篇文章的介绍. random.random()用于生成 用于生成一个指定范围内的随机符点数,两个参数其中一个是上限,一个是下限.如果a > b,则生成随机数 n: a <= n <= b.如果 a <b, 则 b <= n <= a. print random.uniform(

  • Python使用QRCode模块生成二维码实例详解

    Python使用QRCode模块生成二维码 QRCode官网 https://pypi.python.org/pypi/qrcode/5.1 简介 python-qrcode是个用来生成二维码图片的第三方模块,依赖于 PIL 模块和 qrcode 库. 简单用法 import qrcode img = qrcode.make('hello, qrcode') img.save('test.png') 高级用法 import qrcode qr = qrcode.QRCode( version=1

  • Python二维码生成库qrcode安装和使用示例

    二维码简称 QR Code(Quick Response Code),学名为快速响应矩阵码,是二维条码的一种,由日本的 Denso Wave公司于 1994 年发明.现随着智能手机的普及,已广泛应用于平常生活中,例如商品信息查询.社交好友互动.网络地址访问等等. 安装 Python 的二维码库 -- qrcode 由于生成 qrcode 图片需要依赖 Python 的图像库,所以需要先安装 Python 图像库 PIL(Python Imaging Library),不然会遇到 "ImportE

  • Python生成pdf文件的方法

    本文实例演示了Python生成pdf文件的方法,是比较实用的功能,主要包含2个文件.具体实现方法如下: pdf.py文件如下: #!/usr/bin/python from reportlab.pdfgen import canvas def hello(): c = canvas.Canvas("helloworld.pdf") c.drawString(100,100,"Hello,World") c.showPage() c.save() hello() di

  • Python实现将文本生成二维码的方法示例

    本文实例讲述了Python实现将文本生成二维码的方法.分享给大家供大家参考,具体如下: #coding:utf-8 ''' Python生成二维码 v1.0 主要将文本生成二维码图片 测试一:将文本生成白底黑字的二维码图片 测试二:将文本生成带logo的二维码图片 ''' __author__ = 'Xue' import qrcode from PIL import Image import os #生成二维码图片 def make_qr(str,save): qr=qrcode.QRCode

  • Python实现的二维码生成小软件

    前几天,我估摸着做一个能生成QR Code小程序,并能用wxPython在屏幕上显示出来.当然,我想用纯Python实现,观望了一会后,我找到了三个候选: github 上的 python-qrcode sourceforge上的 pyqrcode Goolge code 上的 pyqrnative 我尝试了python-qrcode以及pyqrnative,因为它们能够运行在Windows/Mac/Linux.也不需要依赖额外的其他库除了Python图像库.pyqrcode项目需要其他一些先决

  • 使用python调用zxing库生成二维码图片详解

    (1)安装Jpype 用Python调用jar包需要安装jpype扩展,在Ubuntu上可以直接使用apt-get安装jpype扩展 $ sudo apt-get install python-jpype (2) 得到zxing  jar包 使用zxing第三方库生成二维码图片,关于zxing的介绍可以看其github地址:https://github.com/zxing/zxing/. 我们可以下载源码编译安装包,也可以直接在网上下载jar包,我的jar包是直接下载的,如下: $ wget h

  • python实现二维码扫码自动登录淘宝

    一个小项目自动登录淘宝联盟抓取数据,由于之前在Github上看过类似用Python写的代码因此选择用Python来写,第一次用Python正式写程序还是被其"简单"所震撼,当然用的时候还是对其(2.7版)编码.迁移环境等问题所困扰,还好后来都解决了. 言归正传,抓取淘宝联盟的数据首先要解决的就是登录的问题,之前一般会碰到验证码的困扰,现在支持二维码扫码登录反而简单了,以下是登录的Python代码,主要是获取二维码打印,然后不断的检查扫码状态,如果过期了重新请求二维码(主要看逻辑,由于有

随机推荐