<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Chen Yufei's blog &#187; generator</title>
	<atom:link href="http://chenyufei.info/blog/tag/generator/feed/" rel="self" type="application/rss+xml" />
	<link>http://chenyufei.info/blog</link>
	<description>Keep your head about you while all those are losing theirs</description>
	<lastBuildDate>Wed, 21 Jul 2010 05:30:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Python 的 iterator protocol 和 generator</title>
		<link>http://chenyufei.info/blog/2009-04-20/python-iterator-protocol-and-generator/</link>
		<comments>http://chenyufei.info/blog/2009-04-20/python-iterator-protocol-and-generator/#comments</comments>
		<pubDate>Mon, 20 Apr 2009 02:48:35 +0000</pubDate>
		<dc:creator>chenyufei</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[generator]]></category>
		<category><![CDATA[lazy evaluation]]></category>

		<guid isPermaLink="false">http://chenyufei.info/blog/?p=216</guid>
		<description><![CDATA[前一篇文章最后的代码效率很差。由于使用 list 保存临时的计算结果，所有文件内容会同时读入内存，其构建的管道类似下图所示： list 中的元素是先通过一个过滤器，存起来，再通过下一个过滤器。这样做的坏处是 浪费内存 浪费时间，来回的在临时 list 中进行存取需要时间，另外临时 list 的内存分配，垃圾收集都会增加时间开销 我们希望的管道其实如下： list 中的每个元素应该一次性通过所有的 filter，只在最后存在一个 list 中。更理想的情况下，一开始的 list 也不应该出现。 对于像 Haskell 这样默认使用 lazy evaluation 的语言来说，前面的函数连续调用默认行为就是如此。只有当管道末端需要一个元素时才会从管道头读取一个元素，经过所有过滤器处理以后得到结果，如果没有读取第二个元素，则管道头的其他元素根本不会被处理。 要在程序中使用管道的风格又必须要避免临时数据的产生，然而 python 中没有直接对 lazy evaluation 提供支持，好在 python 有 iterator protocol 和 generator。 首先介绍 python 的 iterator protocol，了解它才知道 for 语句进行遍历时究竟做了些什么。iterator protocol 使得我们可以对不同的容器（其实是任何支持遍历操作的对象，不一定是用来存放数据的容器）使用相同的方式（for 语句）进行遍历。容器可以是 list, dictionary 或者是其他用户定义的数据结构，它只需要实现 __iter__()方法，返回一个 iterator object 即可。真正关心如何遍历的是 iterator [...]]]></description>
			<content:encoded><![CDATA[<p>前一篇文章最后的代码效率很差。由于使用 list 保存临时的计算结果，所有文件内容会同时读入内存，其构建的管道类似下图所示：</p>
<p><img src="http://chenyufei.info/blog/wp-content/uploads/2009/04/list-pipe.gif" alt="list-pipe" title="list-pipe" width="667" height="59" class="aligncenter size-full wp-image-217" /></p>
<p>list 中的元素是先通过一个过滤器，存起来，再通过下一个过滤器。这样做的坏处是</p>
<ol>
<li>浪费内存</li>
<li>浪费时间，来回的在临时 list 中进行存取需要时间，另外临时 list 的内存分配，垃圾收集都会增加时间开销</li>
</ol>
<p>我们希望的管道其实如下：</p>
<p><img src="http://chenyufei.info/blog/wp-content/uploads/2009/04/generator-pipe.gif" alt="generator-pipe" title="generator-pipe" width="496" height="59" class="aligncenter size-full wp-image-222" /></p>
<p>list 中的每个元素应该一次性通过所有的 filter，只在最后存在一个 list 中。更理想的情况下，一开始的 list 也不应该出现。</p>
<p>对于像 Haskell 这样默认使用 lazy evaluation 的语言来说，前面的函数连续调用默认行为就是如此。只有当管道末端需要一个元素时才会从管道头读取一个元素，经过所有过滤器处理以后得到结果，如果没有读取第二个元素，则管道头的其他元素根本不会被处理。</p>
<p>要在程序中使用管道的风格又必须要避免临时数据的产生，然而 python 中没有直接对 lazy evaluation 提供支持，好在 python 有 iterator protocol 和 generator。</p>
<p>首先介绍 python 的 <em><a href="http://docs.python.org/library/stdtypes.html#iterator-types">iterator protocol</a></em>，了解它才知道 for 语句进行遍历时究竟做了些什么。iterator protocol 使得我们可以对不同的容器（其实是任何支持遍历操作的对象，不一定是用来存放数据的容器）使用相同的方式（for 语句）进行遍历。容器可以是 list, dictionary 或者是其他用户定义的数据结构，它只需要实现 <tt class="docutils literal"><span class="pre">__iter__()</span></tt>方法，返回一个 iterator object 即可。真正关心如何遍历的是 <strong>iterator object，它的两个方法构成了 iterator protocol</strong>。</p>
<ol>
<li><tt>__iter__()</tt> 返回自身</li>
<li><tt>next()</tt> 返回容器中的下一个对象，没有更多对象时应 raise<br />
StopIteration，一旦抛出此异常，后续的调用一定也必须抛出此异常。</li>
</ol>
<p>偷懒起见，直接拿 Beazley 的幻灯片里的例子来说明：</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"> <span style="color: #ff7700;font-weight:bold;">class</span> countdown<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;container&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, start<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">count</span> = start
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__iter__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> countdown_iterator<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">count</span><span style="color: black;">&#41;</span>
&nbsp;
 <span style="color: #ff7700;font-weight:bold;">class</span> countdown_iterator<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;iterator object&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, start<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">count</span> = start
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__iter__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> next<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">self</span>.<span style="color: black;">count</span> <span style="color: #66cc66;">&gt;</span>= <span style="color: #ff4500;">0</span>:
            <span style="color: #ff7700;font-weight:bold;">raise</span> <span style="color: #008000;">StopIteration</span>
        r = <span style="color: #008000;">self</span>.<span style="color: black;">count</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">count</span> -= <span style="color: #ff4500;">1</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> r</pre></div></div>

<p>for 语句首先对要遍历的对象调用 <tt class="docutils literal"><span class="pre">__iter__()</span></tt> 方法得到 iterator object，然后对 iterator object 不停调用 <tt class="docutils literal"><span class="pre">next()</span></tt> 方法直到遇到 &#8220;StopIteration&#8221; 异常为止。由此， 下面代码中的 for 语句和 while 循环等价：</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"> <span style="color: #dc143c;">cd</span> = countdown<span style="color: black;">&#40;</span><span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span>
&nbsp;
 <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">cd</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span> i
&nbsp;
 _iter = <span style="color: #dc143c;">cd</span>.<span style="color: #0000cd;">__iter__</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">cd</span><span style="color: black;">&#41;</span> <span style="color: #808080; font-style: italic;"># Get iterator object</span>
<span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #ff4500;">1</span>:
<span style="color: #ff7700;font-weight:bold;">try</span>:
    x = _iter.<span style="color: black;">next</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: #808080; font-style: italic;"># Get next item</span>
    <span style="color: #ff7700;font-weight:bold;">print</span> x
<span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">StopIteration</span>: <span style="color: #808080; font-style: italic;"># No more items</span>
    <span style="color: #ff7700;font-weight:bold;">break</span></pre></div></div>

<p>由于 iterator object 的 <tt class="docutils literal"><span class="pre">__iter__()</span></tt> 返回自身，因此 iterator object 也可以用在 for 语句中。<strong>实际上可以把 for 语句看成是一种语法糖，但它是一种重要的语法糖，因为它大大简化了程序的编写</strong>，看看上面例子比较下就知道了。</p>
<p>为了使得容器支持遍历操作而要另外定义一个 iterator 类，这跟 C++ 的 STL 有些类似。使用 <em><a href="http://docs.python.org/library/stdtypes.html#iterator-types">generator object</a></em> 可以不需要另外定义 iterator 类从而更方便的实现 iteration protocol。具体做法如下：</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">&nbsp;
 <span style="color: #ff7700;font-weight:bold;">class</span> countdown<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;container&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, start<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">count</span> = start
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__iter__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        it = <span style="color: #008000;">self</span>.<span style="color: black;">count</span>
        <span style="color: #ff7700;font-weight:bold;">while</span> it <span style="color: #66cc66;">&gt;</span> <span style="color: #ff4500;">0</span>:
            <span style="color: #ff7700;font-weight:bold;">yield</span> it
            it -= <span style="color: #ff4500;">1</span></pre></div></div>

<p>一旦在函数定义中使用 yield，这个函数就成为 <em>generator function</em> 而不是普通的函数。 generator function 被调用时会返回 <em>generator iterator</em>，简称 <em>generator</em>。调用 generator 的 next() 方法时，generator function 的函数体会执行，当遇到 yield 时，generator 的状态会冻结，而当前值被返回。</p>
<h4>lazy evaluation, generator 的其他用处</h4>
<p>利用 generator 可以模拟出 lazy evaluation 来。lazy evaluation 我觉得最有用的地方在于，</p>
<ol>
<li>使用管道风格的代码处理数据时，避免产生大量临时的结果，提高性能</li>
<li>处理无限长度的数据</li>
<li>避免不必要的求值</li>
</ol>
<p>但 generator 不光可以用来处理数据，Beazley 的幻灯片里提到 networking, threads, co-routines 等都可以用 generator 来处理。</p>
]]></content:encoded>
			<wfw:commentRss>http://chenyufei.info/blog/2009-04-20/python-iterator-protocol-and-generator/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
