Python迭代和生成器三篇好文章

来源:互联网 发布:二战网络射击游戏 编辑:程序博客网 时间:2024/06/05 18:42

1,李木头的Python学习]Iterator 和 Generator的学习心得

    把递归处理变成线性处理

Iterator是迭代器的意思,它的作用是一次产生一个数据项,直到没有为止。这样在 for 循环中就可以对它进行循环处理了。那么它与一般的序列类型(list, tuple等)有什么区别呢?它一次只返回一个数据项,占用更少的内存。但它需要记住当前的状态,以便返回下一数据项。它是一个有着next()方法的对象。而序列类型则保存了所有的数据项,它们的访问是通过索引进行的。

使用Iterator的好处除了节省内存外,还有一个好处就是可以把非线性化的处理转换成线性化的方式来进行处理。如对一棵树的访问,传统的方法可以使用递归函数来处理,下面是对树的一个中序遍历的示例:

例1:

def deal_tree(node):
    if not node:
        return
    if node.leftnode:
        deal_tree(node.leftnode)
    process(node)
    if node.rightnode:
        deal_tree(node.rightnode)

deal_tree(root)

可以看出,对结点的处理函数与递归函数是混在一起的,不是很清晰。使用Iterator的方式改写后为:

例2:

1    def walk_tree(node):
2        if not node:
3            return
4        if node.leftnode:
5            for i in walk_tree(node.leftnode):
6                yield i
7        yield node
8        if node.rightnode:
9            for i in walk_tree(node.rightnode):
10               yield i
11
12   for node in wald_tree(root):
13       process(node)

生成结点的过程仍然是一个递归过程,但对于返回后的结点的处理就变成了线性化的处理,结构上要清晰多了。第5-6,9-10行要特别注意,如果不这样处理直接调用walk_tree的话,其实返回的是一个Iterator对象,而不是想要的元素。

象上面的walk_tree函数在 Python 中可以叫作Generator--产生器,它的作用是生成一个Iterator的对象。那么它主要是将一个函数过程进行封装,转化为Iterator对象,每执行到yield语句时,函数的状态,数据都保存起来,然后返回相应的值。取下一个值的时候,再从上次运行的地方继续运行,如果遇上yield语句,则再次保存状态,返回结果,如果不存在值了,则自动引发一个异常StopIteration,从而Iterator不再产生新的值。从此处我们可以了解,这里的Iterator只可以遍历一次,但并非所有的都是这样,你完全可以对其进行控制。

下面我再介绍一下如何构造自已的Iterator。很简单,创建一个类,满足Iterator的协议,也就是要定义__iter__方法,它返回一个Iterator对象,这个对象必须有next方法,因此我们可以总结出两种对象模式:

class A:
    def __iter__(self):
        return self

    def next(self):
        if has_next_value(self):
            return next_value
        else:
            raise StopIteration

class B:
    def __iter__(self):
        return iterator_obj

A,B分别为两种对象模式(都是示例代码)。模式A表示,在A中定义了next方法,因此__iter__简单地返回自身即可。当不存在下一个值时,引发StopIteration异常。模式B表示,它使用了其它的Iterator对象,因此只需要定义__iter__即可,next不需要定义,因为返回的Iterator对象已经含有next方法了。如果是自已实现next方法,那么在返回值之前需要记住当前的状态,以便下一次运行时,可以取下一个值。

第2个例子好象与这里讲的不一样啊。这就是前面讲的Generator,它的作用就是把一个函数转换成一个Iterator,它自动保存状态,中间数据,引发异常,全部是自动化了。而且它只可以遍历一次。如果想再次遍历,只有重新生成新的Iterator对象才可以。

在最新的 Python 2.4 版中新增了Genetaor Expression方式,它是用来生成简单的,在函数调用需要序列参数时的一种Iterator写法,语法就象是list comprehension的格式,如:

>>> sum(i*i for i in range(10))                 # sum of squares
285

不过这种写法必须要在小括号对中,因此它的使用是有限的。它的目的主要是想更好的使用内存。

前面我们提到不是所有的Iterator只可以遍历一次(使用Generator生成的只能遍历一次),你完全可以控制它重新遍历。比如我们可以在Iterator对象中增加一个复位方法,用来将内部的计数恢复到开始状态,这样我们就可以重新遍历了。

下面我们总结一下:

Iterator对象:具有__iter__方法,和next方法。当没有新值时引发StopIteration异常。

Iterator的好处:在某些情况下可以使程序结构清晰,如将递归等非线性处理转为线性处理。可以减少内存的占用。

Generator:将一个函数转化成Iterator对象的方法。使用它只需要在函数中需要返回值的时候调用yield语句。它是生成Iterator对象的简单方法,只适用于函数。

以上是我学习Iterator和Genetator的一点心得,如有不对的地方,欢迎交流。

2,深入了解Python暂缓列表生成器

3,http://wiki.woodpecker.org.cn/moin/PyCkBk-4-8

1.1. 问题 Problem

You need to do something to every word in a file, similar to theforeach function ofcsh.

需要处理文件中每个单词, 类似于cshforeach功能。

1.2. 解决 Solution

This is best handled by two nested loops, one on lines and one on the words in each line:

最佳方法是使用2层嵌套循环,对文件的各行循环和对每行内的单词循环:

   1 for line in open(thefilepath).xreadlines(  ):                  #方法1   2     for word in line.split(  ):   3         dosomethingwith(word)

This implicitly defines words as sequences of nonspaces separated by sequences of spaces (just as the Unix program wc does).

代码中隐含单词定义是:被空白符号分开的非空白符号的系列(同Unix程序wc一样)。

For other definitions of words, you can use regular expressions. For example:

对于单词的其它定义,可以使用正则表达式,比如:

   1 import re   2 re_word = re.compile(r'[\w-]+')   3    4 for line in open(thefilepath).xreadlines(  ):   5     for word in re_word.findall(line):   6         dosomethingwith(word)

In this case, a word is defined as a maximal sequence of alphanumerics and hyphens.

此处,单词的定义是:由字母数字-的组成的最长序列(#译注:贪婪查找?)

1.3. 讨论 Discussion

For other definitions of words you will obviously need different regular expressions. The outer loop, on all lines in the file, can of course be done in many ways. The xreadlines method is good, but you can also use the list obtained by the readlines method, the standard library module fileinput, or, inPython 2.2, even just:

对于单词的其它定义,显然需要不同的正则表达式。对文件每行进行的外层循环,可以以多种方式进行。上面使用xreadlines不错,也可以使用由readlines获得的list对象,或则标准模块fileinput, 进一步在Python 2.2种,可以用:

   1 for line in open(thefilepath):

which is simplest and fastest.

这样最简单最快。

In Python 2.2, it's often a good idea to wrap iterations as iterator objects, most commonly by simplegenerators:

Python 2.2及高版本中,用iterator对象封装迭代是个好主意。一般由简单generator产生,代码如下:

   1 from _ _future_ _ import generators   2    3 def words_of_file(thefilepath):   4     for line in open(thefilepath):   5         for word in line.split(  ):   6             yield word   7    8 for word in words_of_file(thefilepath):   9     dosomethingwith(word)

This approach lets you separate, cleanly and effectively, two different concerns: how to iterate over all items (in this case, words in a file) and what to do with each item in the iteration.

generatoriterator的使用,可以干净有效的分离2个不同的Concern(#译注:AOP中seperation of concerns):1,如何迭代所有元素; 2,对每个元素的处理。

Once you have cleanly encapsulated iteration concerns in an iterator object (often, as here, a generator), most of your uses of iteration become simple for statements.

只要将迭代Concern封装于iterator对象(这里是generator)内一次,那么几乎所有后期迭代代码就可以使用简单的for循环了。

You can often reuse the iterator in many spots in your program, and if maintenance is ever needed, you can then perform it in just one place梩he definition of the iterator梤ather than having to hunt for all uses.

可以在程序中多处使用这个iteraotor, 如果需要维护,那么在一处的维护就够了。维护仅仅需要修改iterator定义处的代码,而不是搜索修改所有应用iterator的程序片断。

The advantages are thus very similar to those you obtain, in any programming language, by appropriately defining and using functions rather than copying and pasting pieces of code all over the place.

如此,正确定义并使用函数,而不是复制粘贴代码片断到程序的各个角落,在Python中与在所有编程语言中一样,获得的好处是很明显的。

With Python 2.2's iterators, you can get these advantages for looping control structures, too.

使用Python 2.2的iterator处理循环结构,也可以获得这种好处。(#译注:理解的不知道对不对?)