假如我们有一个列表 items=[1,2,3].我们要遍历这个列表我们会用下面的方式
For i in items:
  Print i
首先介绍几个概念:容器,可迭代对象,迭代器
容器是一种存储数据的数据结构,容器将所有数据保存在内存中,典型的容器有列表,集合,字典,字符数组等。如items就是一个列表容器。
 
可迭代对象:这个对象是否可迭代。如items也是一个可迭代对象。简单来说如果可以用for循环的对象都称为可迭代对象。如果要判断是否是一个可迭代的对象。可以用print isinstance(items,Iterable)
,如果是True,则证明是一个可迭代对象。
 
迭代器:任何具有__next__()方法的对象都是迭代器。对迭代器调用next方法可以获取下一个值。所以迭代器本质上是一个产生值的工厂。通俗点说就好比我们写C代码遍历数组的作用。
__next__方法可以用下面的C代码来表示:
for(i=0;i<length;i++)
   return list[i]
 
介绍完几个概念,我们重新来看下面的这个代码发生了什么
for i in items:
    print i
首先items是一个可迭代对象,用for .. in ..的方式其实就是对一个可迭代对象不停调用迭代器的过程。我们将代码改成如下形式来看下:
items=[1,2,3]
L=items.__iter__()  #返回列表为一个可迭代的对象
print L.next()   #调用next方法来获取下一个值
print L.next()
print L.next()
print L.next()
输出如下,可以看到输出了1,2,3.但是还有一个报错StopIteration。这是因为我们使用了4个next方法。但是只有3个元素。在第4个元素的时候。没有可迭代的值了所以抛出了异常。
通过这个我们其实用下面的图来解释for的用法。在for..in的结构中其实是将一个列表传入一个迭代器。然后不停的调用next方法输出元素值。当找不到元素值的时候,则抛出异常

通过上面代码可以看到items.__iter__()可以返回一个可迭代的对象。现在我们来将一个类变成一个可迭代的对象,也就是重写类的__iter__实现方法。
class Node1():
    def __init__(self,value):
        self._value=value
        self._child=[]
    def __repr__(self):
        return 'Node%s' % self._value
    def add_child(self,node):
        self._child.append(node)
    def __iter__(self):
        return iter(self._child)
下面Node1这个类,首先初始化_value和_child两个变量。然后add_child将每个节点加入到_child列表中。最后__iter__返回一个可迭代的对象
下面首先生成root这个根节点,然后生成2个子节点。将这2个子节点加入到root节点中去,最后用for..in的方式调用root.此时
root=Node1(0)
child1=Node1(1)
child2=Node1(2)
root.add_child(child1)
root.add_child(child2)
for ch in root:
    print ch
最后得到结果如下:

可以看到最终结果遍历了_child这个列表。在上述的代码中,__iter__将迭代请求传递给了_child属性。
 
生成器:
前面介绍了迭代器的用法,现在介绍一个更简洁的迭代用法,就是生成器。
我们首先来看下这样的一种应用。我们想实现一个函数,这个函数返回值是得到100之内的所有数的平方值,我们根据这个返回值然后对各个值进行处理。一般来说我们会这样实现:
def data_generate(value):
    number=[]
    for i in range(value):
        num=i*i
        number.append(num)
    return number
首先定义一个列表,然后将value内的值全部取平方。然后加入到number中去。等所有的数都生成后直接用return返回。这里看上去没啥问题。但是如果我们设置的value是10000或者是更大的数。那么对应的列表number也会变得更大。这样就需要更多的内存来存储值。如果这个value足够大,仅仅为了存储这些值就得耗尽所有的内存,哪该怎么办呢。有没有一种方法每当生成一个数的时候,就返回这个值,这样就不需要专门定义一个列表来存储了。
但是return语句每当调用的时候整个函数就停止了。无法满足我们的诉求。不用急,python中的生成器完全我们的需求。而且用法很简单
代码改造成如下
def data_generate(value):
    for i in range(value):
        num=i*i
        yield num
如下调用
for i in data_generate(100):
    print i
 
通过代码可以看到我们去掉了number列表以及return语句。添加了yield num语句。并用调用迭代器的方式调用data_generate函数。最终也达到了我们要的效果。而且最重要的是在函数中我们不需要申请一个占用内存的列表。完美的实现了我们的诉求。
这里介绍yield的用法:yield就是生成器的意思。其实作用就像一个增强版的return语句。每当执行到yield的时候,函数会自动停止,并保存所有的变量。相当于执行了一个中断,然后会返回一个当前的值。然后代码从yield num的下一条语句继续执行。
我们来看下代码的单步执行结果:
执行第一次循环的时候,i=0,num=0,value=100

调用yield num后返回0值,得到输出结果如下

接着进入第二次循环,i=1,在上一次的基础上加1,num=1

最终输出结果如下:

进入第三次循环,i=2,num=4.

输出结果如下

从上面的单步调用的结果可以很直观的看出yield的用法。其实yield就是一个增强版的迭代器。我们可以也将代码改成如下。可以看到ret是一个迭代器,然后我们不停的调用next就可以得到每次每次调用的值。
ret=data_generate(100)
print ret.next()
print ret.next()
print ret.next()
得到输出结果如下:

我们来看下yield在类中的应用。代码如下:
class Node:
    def __init__(self,value):
        self._value=value
        self._child=[]
    def __repr__(self):
        return "Node%s" % self._value
    def add_child(self,node):
        self._child.append(node)
    def __iter__(self):
        return iter(self._child)
    def depth_first(self):
        yield self
        for c in self:
            yield c
            for last in c:
                yield last
 
 
def iter_function():
    root=Node(0)
    child1=Node(1)
    child2=Node(2)
    root.add_child(child1)
    root.add_child(child2)
    child1.add_child(Node(3))
    child1.add_child(Node(4))
    child2.add_child(Node(5))
    child2.add_child(Node(6))
    for ch in root.depth_first():
        print ch
在iter_function1中root是根节点。下面有child1和child2 2个子节点。其中child1和child2下面各自有3,4/5,6节点。我们做要实现一个节点的深度遍历。期望得到的结果是Node0,Node1,Node3,Node4,Node2,Node5,Node6.在depth_first中用到了3个yield语句。其中yield self是返回当前的根节点。yield c是返回根节点下的子节点. yield last是继续在上一步的基础上返回上一层的子节点。由于在__iter__中返回的是_child的迭代对象。因此上面的功能也就是不停地遍历各个节点下的_child对象。
执行结果如下:和我们预想的结果一致

我们来看下单步调试的结果:
首先进入Node0节点

打印出Node0

开始遍历Node0的子节点,第一个是Node1

此时ch=Node1

打印出Node1

Ch=Node3

打印出Node3

Node1的下一个子节点Node4
打印出Node4

Node1遍历完后,开始遍历Node2

首先打印出Node2

开始遍历node2,第一个子节点是node5

最后一个是node6

反向遍历列表。有一个列表a=[1,2,3,4].想从列表末尾开始遍历。可以用reversed 来实现这个效果
a=[1,2,3,4]
for x in reversed(a):
    print x
发现迭代只有在对象实现了__reversed__方法且对象大小是确定的情况下才起作用。如果不符合上述条件,必须先将对象转换成列表。
我们可以自定义__reversed__来实现反向迭代。
class CountDown():
    def __init__(self,start):
        self.start=start
    def __iter__(self):
        n=self.start
        while n > 0:
            yield n
            n-=1
    def __reversed__(self):
        n=1
        while n<=self.start:
            yield n
            n+=1
 
for x in CountDown(5):
    print x
print 'reversed result:\n'
for
x in reversed(CountDown(5)):
    print x
 
结果如下:

迭代器切片:
假设有如下的代码,count函数实现从n开始的加一操作
def count(n):
    while True:
        yield n
        n+=1
for c in count(5):
    print c
当开始遍历的时候。会一直打印从5开始的加一结果。但是我们只是想得到其中一部分的结果。比如第10到15个生成结果
c=count(5)
print c[10:15]
报如下错误,无法进行数据切片

如果要对生成器进行切片,要用到itertools.islice功能,代码改造如下:
c=count(5)
for x in itertools.islice(c,10,15):
    print x
结果如下,得到了我们想要的结果

如果我们想得到一个集合中元素的所有的组合或者排列。这里可以用到itertools.permutations 这个函数的作用是生成一个排列。
def iter_combinations():
    items=['a','b','c']
    for p in permutations(items):
        print p
 结果如下:

如果我只是想得到指定长度的排列。也可以指定长度permutations(items,2):

如果我们只想得到组合呢。这里解释下组合和排列的区别。这个是概率论上的概念。这个主要是看是否和顺序有关,不考虑顺序就是排列。比如a,b,c和a,c,b是两种不同的排列。但是组合就要考虑顺序,例如a,b,c和a.c.b就是同一个组合。要得的所有的组合,这里要用到itertools.combinations.代码改成如下:
def iter_combinations():
    items=['a','b','c']
    for p in combinations(items,3):
        print p
可以看到只有一个组合。

但是如果选择2个元素的话(combinations(items,2)),则有多个组合。

打开一个文件的时候,是对文件逐行的扫描。很多时候我们都期望同时得到文件的行号以及内容。这里可以用到enumerate
f=open(r'E:\py_prj\README.TXT','rb')
for line,content in enumerate(f):
    print line,content
返回的结果中带有行号的索引。Enumerate返回的是包含一个计数和一个值的元组

如果我有两个序列,我们想得到两者的一一对应关系。比如
a=[1,2,3]   b=[a,b,c]
想得到如下对应关系
1,a
2,b
3,c
这里可以用到zip函数。
a=[1,2,3]
b=['a','b','c']
for i in zip(a,b):
    print i
结果如下:

但如果数组是如下的样式,长度不一致。这个时候该如何对应呢
a=[1,2,3]
b=['a','b','c','d']
可以用izip_longest函数
a=[1,2,3]
b=['a','b','c','d']
for i in izip_longest(a,b):
    print i
可以看到缺失的项用None补充的

如果想自己定义缺失项的名称。可以对fillvalue进行赋值:
for i in izip_longest(a,b,fillvalue='null'):

上述返回的对应的元组。我们可以在这个基础上将其变成一个字典。
a=[1,2,3]
b=['a','b','c','d']
result=dict(izip_longest(a,b))
print result


















 
















 

python cookbook第三版学习笔记六:迭代器与生成器的更多相关文章

  1. python cookbook第三版学习笔记十:类和对象(一)

    类和对象: 我们经常会对打印一个对象来得到对象的某些信息. class pair:     def __init__(self,x,y):         self.x=x         self. ...

  2. python cookbook第三版学习笔记十六:抽象基类

    假设一个工程中有多个类,每个类都通过__init__来初始化参数.但是可能有很多高度重复且样式相同的__init__.为了减少代码.我们可以将初始化数据结构的步骤归纳到一个单独的__init__函数中 ...

  3. python cookbook第三版学习笔记 一

    数据结构 假设有M个元素的列表,需要从中分解出N个对象,N<M,这会导致分解的值过多的异常.如下: record=['zhf','zhf@163.com','775-555-1212','847 ...

  4. python cookbook第三版学习笔记十三:类和对象(三)描述器

    __get__以及__set__:假设T是一个类,t是他的实例,d是它的一个描述器属性.读取属性的时候T.d返回的是d.__get__(None,T),t.d返回的是d.__get__(t,T).说法 ...

  5. python cookbook第三版学习笔记二十:可自定义属性的装饰器

    在开始本节之前,首先介绍下偏函数partial.首先借助help来看下partial的定义 首先来说下第一行解释的意思: partial 一共有三个部分: (1)第一部分也就是第一个参数,是一个函数, ...

  6. python cookbook第三版学习笔记十五:property和描述

    8.5 私有属性: 在python中,如果想将私有数据封装到类的实例上,有两种方法:1 单下划线.2 双下划线 1 单下划线一般认为是内部实现,但是如果想从外部访问的话也是可以的 2 双下划线是则无法 ...

  7. python cookbook第三版学习笔记七:python解析csv,json,xml文件

    CSV文件读取: Csv文件格式如下:分别有2行三列. 访问代码如下: f=open(r'E:\py_prj\test.csv','rb') f_csv=csv.reader(f) for f in ...

  8. python cookbook第三版学习笔记十三:类和对象(四)描述器

    __get__以及__set__:假设T是一个类,t是他的实例,d是它的一个描述器属性.读取属性的时候T.d返回的是d.__get__(None,T),t.d返回的是d.__get__(t,T).说法 ...

  9. python cookbook第三版学习笔记十一:类和对象(二)调用父类的方法

    在子类中调用父类的方法,可以下面的A.spam(self)的方法. class A(object):     def spam(self):         print 'A.spam' class ...

随机推荐

  1. Mysql乱码问题解决历程

    可能是因为看了太多网上的关于这个问题的解决办法,可能当时是我自己没有看明白也或许是情况不一样,反正都没有解决我当初遇到的问题,现在想想可能是自己当初太无知了,第二个原因是原来大多数情况下是在windo ...

  2. linux svn co 重新迁出

    在linux环境下,使用svn co (即svn checkout) 报svn: Authorization failed错误, 使用svn co svn://localhost/temp.cc /d ...

  3. mfoc安装编译

    系统环境 UBUNTU16.04,要安装好各种基础的编译环境,这就不说了,如果不知道需要什么,那就编译时出错时看到什么安装什么吧 下载源码包libnfc, mfoc, mfcuk,都是github上, ...

  4. sql server book

    http://www.sqlpassion.at/blog/ http://www.sqlservercentral.com/Books/

  5. 2017.2.9 开涛shiro教程-第十章-会话管理(一)

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第十章 会话管理(一) 10.1 会话 shiro提供的会话可以用 ...

  6. Performing User-Managed Database-18.7、Performing Complete User-Managed Media Recovery

    18.7.Performing Complete User-Managed Media Recovery 完毕一致性备份,把数据库恢复到当前的scn是最好的结果.能够恢复整个数据库.恢复单个表空间.或 ...

  7. grep 精确匹配

    使用grep实现精确过滤的五种方法 (1)当被过滤的内容占据一行时 [root@MySQL scripts]# cat oldboy.log        200 0200 2000 [root@My ...

  8. 怎样通过Html网页调用本地安卓app

    怎样使用html网页和本地app进行传递数据呢?经过研究.发现还是有方法的,总结了一下,大致有一下几种方式 一.通过html页面打开Android本地的app 1.首先在编写一个简单的html页面 & ...

  9. UNP学习笔记(第二十六章 线程)

    线程有时称为轻权进程(lightweight process) 同一进程内的所有线程共享相同的全局内存.这使得线程之间易于共享信息,然后这样也会带来同步的问题 同一进程内的所有线程处理共享全局变量外还 ...

  10. java:可变参数(转载)

    http://12477787.blog.51cto.com/12467787/1887843 Java在1.5之后允许方法使用可变参数,可变参数的好处在于:它允许传递0个或者多个参数.比如原来有一段 ...