<?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</title>
	<atom:link href="http://chenyufei.info/blog/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>Thu, 31 Dec 2009 10:14:34 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Get the current module in Python</title>
		<link>http://chenyufei.info/blog/2009-12-31/get-the-current-module-in-python/</link>
		<comments>http://chenyufei.info/blog/2009-12-31/get-the-current-module-in-python/#comments</comments>
		<pubDate>Thu, 31 Dec 2009 02:47:37 +0000</pubDate>
		<dc:creator>chenyufei</dc:creator>
				<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://chenyufei.info/blog/?p=290</guid>
		<description><![CDATA[It&#8217;s sometimes useful to do introspection on the module itself you are writing. But python doesn&#8217;t provide any direct way to support this. In fact a PEP about this feature has been rejected.
A little google find a solution to this problem. Here&#8217;s the code

import sys
# get the current module's name
modname = globals&#40;&#41;&#91;'__name__'&#93;
# get the module
module [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s sometimes useful to do introspection on the module itself you are writing. But python doesn&#8217;t provide any direct way to support this. In fact a <a href="http://www.python.org/dev/peps/pep-3130/">PEP about this feature</a> has been rejected.</p>
<p>A little google find <a href="http://old.nabble.com/How-to-get-current-module-object-td15532853.html">a solution</a> to this problem. Here&#8217;s the code</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #808080; font-style: italic;"># get the current module's name</span>
modname = <span style="color: #008000;">globals</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'__name__'</span><span style="color: black;">&#93;</span>
<span style="color: #808080; font-style: italic;"># get the module</span>
module = <span style="color: #dc143c;">sys</span>.<span style="color: black;">modules</span><span style="color: black;">&#91;</span>modname<span style="color: black;">&#93;</span>
<span style="color: #808080; font-style: italic;"># or more simply as suggested by E.T</span>
module = <span style="color: #dc143c;">sys</span>.<span style="color: black;">modules</span><span style="color: black;">&#91;</span>__name__<span style="color: black;">&#93;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://chenyufei.info/blog/2009-12-31/get-the-current-module-in-python/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Reviewboard, PIL and virtualenv</title>
		<link>http://chenyufei.info/blog/2009-12-02/reviewboard-pil-and-virtualenv/</link>
		<comments>http://chenyufei.info/blog/2009-12-02/reviewboard-pil-and-virtualenv/#comments</comments>
		<pubDate>Wed, 02 Dec 2009 06:44:27 +0000</pubDate>
		<dc:creator>chenyufei</dc:creator>
				<category><![CDATA[未分类]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[reviewboard]]></category>
		<category><![CDATA[virtualenv]]></category>

		<guid isPermaLink="false">http://chenyufei.info/blog/2009-12-02/reviewboard-pil-and-virtualenv/</guid>
		<description><![CDATA[I created a separate no site-packages virtualenv directory, and use easy_install to install Reviewboard.
However, easy_install installs PIL version 1.1.7 which does not work with the latest stable Reviewboard version 1.0.5.1.
Using easy_install PIL==1.1.6 doesn&#8217;t work because of build error. The workaround is to manually download PIL and install it.
To make reviewboard work under virtualenv, additional steps [...]]]></description>
			<content:encoded><![CDATA[<p>I created a separate no site-packages virtualenv directory, and use easy_install to install Reviewboard.</p>
<p>However, easy_install installs PIL version 1.1.7 which does not work with the latest stable Reviewboard version 1.0.5.1.</p>
<p>Using easy_install PIL==1.1.6 doesn&#8217;t work because of build error. The workaround is to manually download PIL and install it.</p>
<p>To make reviewboard work under virtualenv, additional steps are needed, which in fact are steps for Django application to work under virtualenv.</p>
<ul>
<li>If you use mod_python and apache, <a href="http://mydjangoblog.com/2009/03/30/django-mod_python-and-virtualenv/">this article</a> maybe useful. This works for Reviewboard.</li>
<li>For mod_wsgi, <a href="http://www.danceric.net/2009/03/26/django-virtualenv-and-mod_wsgi/">look here</a>.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://chenyufei.info/blog/2009-12-02/reviewboard-pil-and-virtualenv/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>更新 zsh 的 command hash table</title>
		<link>http://chenyufei.info/blog/2009-11-14/updating-zsh-command-hash-table/</link>
		<comments>http://chenyufei.info/blog/2009-11-14/updating-zsh-command-hash-table/#comments</comments>
		<pubDate>Sat, 14 Nov 2009 15:26:42 +0000</pubDate>
		<dc:creator>chenyufei</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[tips]]></category>
		<category><![CDATA[zsh]]></category>

		<guid isPermaLink="false">http://chenyufei.info/blog/?p=282</guid>
		<description><![CDATA[zsh 下新装了一个软件，tab 补全时新装软件的命令不会出现。以前的解决办法是执行 export PATH=$PATH 让 zsh 去更新缓存。
今天在 Pylons 的 activate 脚本里看到了一条 builtin 命令 hash (man zshbuiltins)，这条命令可以直接修改 command hash table。$PATH 路径下的内容发生变化时可以用 hash -r 来更新。
]]></description>
			<content:encoded><![CDATA[<p>zsh 下新装了一个软件，tab 补全时新装软件的命令不会出现。以前的解决办法是执行 <code>export PATH=$PATH</code> 让 zsh 去更新缓存。</p>
<p>今天在 Pylons 的 activate 脚本里看到了一条 builtin 命令 <code>hash</code> (man zshbuiltins)，这条命令可以直接修改 command hash table。$PATH 路径下的内容发生变化时可以用 <code>hash -r</code> 来更新。</p>
]]></content:encoded>
			<wfw:commentRss>http://chenyufei.info/blog/2009-11-14/updating-zsh-command-hash-table/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>screen &amp; LD_LIBRARY_PATH</title>
		<link>http://chenyufei.info/blog/2009-11-13/screen-ld_library_path/</link>
		<comments>http://chenyufei.info/blog/2009-11-13/screen-ld_library_path/#comments</comments>
		<pubDate>Thu, 12 Nov 2009 16:45:24 +0000</pubDate>
		<dc:creator>chenyufei</dc:creator>
				<category><![CDATA[未分类]]></category>
		<category><![CDATA[glibc]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[screen]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://chenyufei.info/blog/?p=191</guid>
		<description><![CDATA[这篇 post 居然是在今年 1 月份的时候放到 draft 里，到现在才 publish……
因为用 intel 的编译器，所以设置了 LD_LIBRARY_PATH 这个环境变量，但是每次启动 screen 后这个环境本来都会被 unset。google 到的结果。
screen 可执行文件是 setuid 的（为了 share session，debian 的 screen 安装时默认没有 setuid），glibc 对这样的可执行文件会把那些“危险”的环境变量去掉，所以出现了上面的情况
解决办法两个
1. 把 setuid 位去掉，当然这样 screen 的 share session 就不能工作了，还好一般用不到
2. 把 LD_LIBRARY_PATH 的设置放到 .zshrc/.bashrc 之类的文件里去，这样每次启动 shell 的时候自然会把这个环境变量读入的

if [ -z $LD_LIBRARY_PATH ]; then
    export $LD_LIBRARY_PATH=XXX
else
    export [...]]]></description>
			<content:encoded><![CDATA[<p>这篇 post 居然是在今年 1 月份的时候放到 draft 里，到现在才 publish……</p>
<p>因为用 intel 的编译器，所以设置了 LD_LIBRARY_PATH 这个环境变量，但是每次启动 screen 后这个环境本来都会被 unset。<a href="http://www.mail-archive.com/screen-devel@gnu.org/msg00019.html">google 到的结果</a>。</p>
<p>screen 可执行文件是 setuid 的（为了 share session，debian 的 screen 安装时默认没有 setuid），glibc 对这样的可执行文件会把那些“危险”的环境变量去掉，所以出现了上面的情况</p>
<p>解决办法两个</p>
<p>1. 把 setuid 位去掉，当然这样 screen 的 share session 就不能工作了，还好一般用不到<br />
2. 把 LD_LIBRARY_PATH 的设置放到 .zshrc/.bashrc 之类的文件里去，这样每次启动 shell 的时候自然会把这个环境变量读入的</p>
<pre>
if [ -z $LD_LIBRARY_PATH ]; then
    export $LD_LIBRARY_PATH=XXX
else
    export $LD_LIBRARY_PATH=$LD_LIBRARY_PATH:XXX
fi
</pre>
<p>LD_LIBRARY_PATH 这个环境变量设置起来还真的挺麻烦的，系统的库目录和自己的库目录下有同名不同版本的库的时候，不是自己的程序有问题就是系统的程序有问题。</p>
]]></content:encoded>
			<wfw:commentRss>http://chenyufei.info/blog/2009-11-13/screen-ld_library_path/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>zsh + screen</title>
		<link>http://chenyufei.info/blog/2009-11-12/zsh-screen/</link>
		<comments>http://chenyufei.info/blog/2009-11-12/zsh-screen/#comments</comments>
		<pubDate>Thu, 12 Nov 2009 15:12:14 +0000</pubDate>
		<dc:creator>chenyufei</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[screen]]></category>
		<category><![CDATA[zsh]]></category>

		<guid isPermaLink="false">http://chenyufei.info/blog/?p=267</guid>
		<description><![CDATA[screen 提供多个 shell 来回切换是很方便，不过有时会忘记应该切换到哪个 window。如果可以根据执行的命令和当前目录来动态设置 window title 的话来回切换时就会可以方便的找到目标 window。其实 zsh-lovers 里就有说明。
screen 可以通过 echo 特殊的字符来设置 window 的 title，而 zsh 有两个特殊的函数 preexec 和 precmd，前者在用户输入命令按下回车但 zsh 还未执行命令前被调用，后者在 zsh 更新 prompt 前被调用（具体说明见 man zshmisc）。把 zsh 和 screen 的功能结合起来就可以在执行命令时把 screen window 的标题设置成当前执行的命令，而在 zsh 等待用户命令时，将 title 设置成当前目录。
先贴 zsh 的相关配置，PS1 的配置不喜欢可以去掉。

autoload colors
colors

case $TERM in
    screen*)
     [...]]]></description>
			<content:encoded><![CDATA[<p>screen 提供多个 shell 来回切换是很方便，不过有时会忘记应该切换到哪个 window。如果可以根据执行的命令和当前目录来动态设置 window title 的话来回切换时就会可以方便的找到目标 window。其实 zsh-lovers 里就有说明。</p>
<p>screen 可以通过 echo 特殊的字符来设置 window 的 title，而 zsh 有两个特殊的函数 preexec 和 precmd，前者在用户输入命令按下回车但 zsh 还未执行命令前被调用，后者在 zsh 更新 prompt 前被调用（具体说明见 man zshmisc）。把 zsh 和 screen 的功能结合起来就可以在<strong>执行命令时把 screen window 的标题设置成当前执行的命令，而在 zsh 等待用户命令时，将 title 设置成当前目录</strong>。</p>
<p>先贴 zsh 的相关配置，PS1 的配置不喜欢可以去掉。</p>
<pre>
autoload colors
colors

case $TERM in
    screen*)
        function sctitle() { print -Pn "\ek$1\e\\"}
        function precmd() { sctitle "%20< ..<%~%<<" }
        function preexec() { sctitle "%20>..>$1%< <" }
        export PS1="%{${fg[cyan]}%}[%D{%H:%M} %20<..<%~%<<]%{$reset_color%} "
    ;;
    *)
        export PS1="%{${fg[cyan]}%}[%D{%H:%M} %n@%m:%20<..<%~%<<]%{$reset_color%} "
    ;;
esac
</pre>
<p>有时命令或者当前目录很长，screen 的 status bar 宽度会不够，zsh 有内置的截短字符串的功能，用 %20< ..<%~%<< 限制目录最长为 20 个字符，把左边的多余字符去掉，截短后用两个点表示，%~ 表示当前目录，如果包含 HOME 目录则用波浪号表示，%<< 标志截短操作的结束。%20>..>$1%< < 类似，preexec 的第一个参数是完整的命令行输入，同样截短成 20 个字符，不过是去掉右边的多余字符。sctitle 就是打印特殊字符来设置 screen window title 的函数。</p>
<p>接下来是 .screenrc。</p>
<pre>
startup_message off
# use visual bell
vbell off
# replace Ctrl-A by `
escape ``
# set a big scrolling buffer
defscrollback 5000
# Set the caption on the bottom line
caption always '%{= kg}[%{G}%H%{g}][%= %{= kw}%?%-Lw%?%{+b r}(%{y}%n %t%?(%u)%?%{r})%{= w}%?%+Lw%?%?%= %{g}][%{B} %d/%m %{W}%c %{g}]'
# %{= kG} first set default color (=), back ground black (k), foreground green (G)
# [%{G}%H%{g}] color bright green, host name (%H), color green
# [%= ...] padding (%=), window left to the current focus window if exists,
#      (current focus window with color yellow), window to the right of the
#      focus window
# [%{B} %m/%d %{W}%c %{g}] color bright blue, month/date (%m%d), color bright white,
#      current time(%c), color green

# open several terminals at startup
screen 5
screen 4
screen 3
screen 2
screen 1
</pre>
<p>因为我用 vim，而且我觉得切换 window 时要按数字键，所以我把 escape 设置成 back tick。这个配置最关键的就是 caption 了，忘记从哪里 copy 过来的了（用 hardstatus 的话 screen 的消息也是在 hardstatus 上显示，所以我喜欢用 caption）。我直接在配置文件里加了点注释，语法很恶心，我都看晕了。关于  caption/hardstatus 里的转义字符说明见 man screen 的 string escape 一节。</p>
<p>试试看吧，这个配置还是很炫而且也蛮实用的。</p>
]]></content:encoded>
			<wfw:commentRss>http://chenyufei.info/blog/2009-11-12/zsh-screen/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>我也来推荐 bpython</title>
		<link>http://chenyufei.info/blog/2009-11-12/recommend-bpython/</link>
		<comments>http://chenyufei.info/blog/2009-11-12/recommend-bpython/#comments</comments>
		<pubDate>Thu, 12 Nov 2009 13:51:10 +0000</pubDate>
		<dc:creator>chenyufei</dc:creator>
				<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://chenyufei.info/blog/?p=262</guid>
		<description><![CDATA[在光华上看到 Zellux 推荐的，bpython。
bpython 对输入的代码有高亮显示，输入代码同时自动补全，不需要按 tab，调用函数时打完左括号文档就自动出现。
现在只是刚刚试了一下，印象不错。
有些 ipython 支持的功能 bpython 里没有，现在发现的是不支持直接执行 shell 命令。（文件名补全的话当前目录的要用 ./ 以后才会出来。）
]]></description>
			<content:encoded><![CDATA[<p>在光华上看到 <a href="http://techblog.iamzellux.com/">Zellux</a> 推荐的，<a href="http://www.bpython-interpreter.org/about/">bpython</a>。</p>
<p>bpython 对输入的代码有高亮显示，输入代码同时自动补全，不需要按 tab，调用函数时打完左括号文档就自动出现。</p>
<p>现在只是刚刚试了一下，印象不错。</p>
<p>有些 ipython 支持的功能 bpython 里没有，现在发现的是不支持直接执行 shell 命令。（文件名补全的话当前目录的要用 ./ 以后才会出来。）</p>
]]></content:encoded>
			<wfw:commentRss>http://chenyufei.info/blog/2009-11-12/recommend-bpython/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Ch &#8212; 一个 C/C++ 解释器</title>
		<link>http://chenyufei.info/blog/2009-11-05/ch-a-c-and-cplusplus-interprete/</link>
		<comments>http://chenyufei.info/blog/2009-11-05/ch-a-c-and-cplusplus-interprete/#comments</comments>
		<pubDate>Wed, 04 Nov 2009 17:14:08 +0000</pubDate>
		<dc:creator>chenyufei</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[c]]></category>
		<category><![CDATA[compiler]]></category>

		<guid isPermaLink="false">http://chenyufei.info/blog/?p=258</guid>
		<description><![CDATA[动态语言很重要的一个功能就是支持交互式的开发，用惯了 Python 有时候非常希望 C 也能有一个解释器来用，尤其是忘了 C 的某些语法想写个简单的例子来测试的时候。
很久以前就搜过 C 的解释器，搜到过 Ch，不记得当时为什么没有试用过。今天下了个免费版本的用了下，很不错，支持 C90 和 C99 的主要功能，C++ 支持不完全（不过 C++ 我基本不关心）。
以前想要测试 C 的某个语法功能时会写个文件，int main 什么的搞一堆，然后用 tcc (Tiny C Compiler) 来测试。tcc 可以把 C 代码的编译和执行放在一步完成，执行 tcc -run foo.c 就可以看到效果了，还算方便。
用 Ch 就更方便了。ch 命令出来个交互式的 shell，输入 C 代码马上执行，调 printf 直接看到效果，输入变量就可以看到它的值（struct 的话可以看到每个成员的值），做点小的测试就不需要写 int main 之类的了。另外 ch 还有函数名补全。
]]></description>
			<content:encoded><![CDATA[<p>动态语言很重要的一个功能就是支持交互式的开发，用惯了 Python 有时候非常希望 C 也能有一个解释器来用，尤其是忘了 C 的某些语法想写个简单的例子来测试的时候。</p>
<p>很久以前就搜过 C 的解释器，搜到过 <a href="http://www.softintegration.com/">Ch</a>，不记得当时为什么没有试用过。今天下了个免费版本的用了下，很不错，支持 C90 和 C99 的主要功能，C++ 支持不完全（不过 C++ 我基本不关心）。</p>
<p>以前想要测试 C 的某个语法功能时会写个文件，int main 什么的搞一堆，然后用 <a href="http://bellard.org/tcc/">tcc (Tiny C Compiler)</a> 来测试。tcc 可以把 C 代码的编译和执行放在一步完成，执行 <code>tcc -run foo.c</code> 就可以看到效果了，还算方便。</p>
<p>用 Ch 就更方便了。ch 命令出来个交互式的 shell，输入 C 代码马上执行，调 printf 直接看到效果，输入变量就可以看到它的值（struct 的话可以看到每个成员的值），做点小的测试就不需要写 int main 之类的了。另外 ch 还有函数名补全。</p>
]]></content:encoded>
			<wfw:commentRss>http://chenyufei.info/blog/2009-11-05/ch-a-c-and-cplusplus-interprete/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Shell like data processing in Python &#8212; using decorators</title>
		<link>http://chenyufei.info/blog/2009-10-03/shell-like-data-processing-in-python-using-decorators/</link>
		<comments>http://chenyufei.info/blog/2009-10-03/shell-like-data-processing-in-python-using-decorators/#comments</comments>
		<pubDate>Sat, 03 Oct 2009 15:31:29 +0000</pubDate>
		<dc:creator>chenyufei</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[decorator]]></category>
		<category><![CDATA[pipe]]></category>

		<guid isPermaLink="false">http://chenyufei.info/blog/?p=232</guid>
		<description><![CDATA[前面的文章展示了管道的好处，以及在 Python 程序中利用管道的思想。但是前面文章里的代码还有一点缺陷，看下面的 shell 脚本和 Python 代码的比较：

find logdir -name "access-log*" &#124; \
xargs cat &#124; \
grep '[^-]$' &#124; \
awk '{ total += $NF } END { print total }'


lines = grep&#40;'[^-]$', cat&#40;find&#40;'logdir', 'access-log*'&#41;&#41;&#41;
col = &#40;line.rsplit&#40;None, 1&#41;&#91;1&#93; for line in lines&#41;
print sum&#40;int&#40;c&#41; for c in col&#41;

Python 中 find, cat, grep 的调用用一层层的括号嵌套起来进行调用，执行顺序是从最内部的括号开始，可读性没有 shell 脚本好。可不可能在 Python 脚本中用类似 shell 脚本里的语法来提高可读性？用运算符重载就可以做到了。最初的想法来自这里，这个例子用的方法是重载或运算符 [...]]]></description>
			<content:encoded><![CDATA[<p>前面的文章展示了<a href="http://chenyufei.info/blog/2009-01-19/why-unix-pipe-is-a-good-thing/">管道的好处</a>，以及在 Python 程序中<a href="http://chenyufei.info/blog/2009-01-28/writing-unix-pipe-style-python-code/">利用管道的思想</a>。但是前面文章里的代码还有一点缺陷，看下面的 shell 脚本和 Python 代码的比较：</p>
<pre>
find logdir -name "access-log*" | \
xargs cat | \
grep '[^-]$' | \
awk '{ total += $NF } END { print total }'
</pre>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">lines = grep<span style="color: black;">&#40;</span><span style="color: #483d8b;">'[^-]$'</span>, cat<span style="color: black;">&#40;</span>find<span style="color: black;">&#40;</span><span style="color: #483d8b;">'logdir'</span>, <span style="color: #483d8b;">'access-log*'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
col = <span style="color: black;">&#40;</span>line.<span style="color: black;">rsplit</span><span style="color: black;">&#40;</span><span style="color: #008000;">None</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> lines<span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">sum</span><span style="color: black;">&#40;</span><span style="color: #008000;">int</span><span style="color: black;">&#40;</span>c<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> c <span style="color: #ff7700;font-weight:bold;">in</span> col<span style="color: black;">&#41;</span></pre></div></div>

<p>Python 中 find, cat, grep 的调用用一层层的括号嵌套起来进行调用，执行顺序是从最内部的括号开始，可读性没有 shell 脚本好。可不可能在 Python 脚本中用类似 shell 脚本里的语法来提高可读性？用运算符重载就可以做到了。最初的想法<a href="http://code.activestate.com/recipes/276960/">来自这里</a>，这个例子用的方法是重载或运算符 &#8220;|&#8221;。新的 Python 代码如下，我把这样的代码称为 pipe syntax (抽取最后一列并求和的代码不变。)</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">lines = find<span style="color: black;">&#40;</span><span style="color: #483d8b;">'logdir'</span>, <span style="color: #483d8b;">'access-log*'</span><span style="color: black;">&#41;</span> | cat | grep<span style="color: black;">&#40;</span><span style="color: #483d8b;">'[^-]$'</span><span style="color: black;">&#41;</span></pre></div></div>

<p>用运算符重载的方法把一个函数放在管道中必须自己定义一个类，在 override 的 <code>__ror__()</code> 函数中完成实际的工作。每次都要定义一个类我觉得不够方便，因此我用 decorator 来进行简化。</p>
<p><strong>简单来说 decorator 通过用其他对象替换原来的函数来改变函数的行为</strong>。对 decorator 的介绍可以参考 Bruce Eckel 的两篇文章，分别介绍了<a href="http://www.artima.com/weblogs/viewpost.jsp?thread=240808">无参数</a>和<a href="http://www.artima.com/weblogs/viewpost.jsp?thread=240845">有参数</a>的 decorator 如何创建，无参数的那篇文章还介绍了 decoractor 的作用。Bruce Eckel 认为 decorator 就像是宏一样，可以改变函数的语义。他还认为 Python 的 decorator 就像 Lisp 的宏一样 powerful。对这一点我不太赞同，两者差别还是很大的，decorator 其实只是提供了在运行时动态地替换函数的功能，而 Lisp 的宏是在编译时生成代码，要说 powerful 肯定还是 Lisp 的宏更强，但 decorator 更简单而且也已经足够 powerful 了。</p>
<p>使用 decorator 来定义 cat 的代码如下，函数前的 &#8220;@&#8221; 是为了支持 decorator 而引入的新的语法：</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">@pipeable
<span style="color: #ff7700;font-weight:bold;">def</span> grep<span style="color: black;">&#40;</span><span style="color: #008000;">iter</span>, match<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">callable</span><span style="color: black;">&#40;</span>match<span style="color: black;">&#41;</span>:
        fun = match
    <span style="color: #ff7700;font-weight:bold;">else</span>:
        fun = <span style="color: #dc143c;">re</span>.<span style="color: #008000;">compile</span><span style="color: black;">&#40;</span>match<span style="color: black;">&#41;</span>.<span style="color: black;">match</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> ifilter<span style="color: black;">&#40;</span>fun, <span style="color: #008000;">iter</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Without @pipeable before the function definition of grep,</span>
<span style="color: #808080; font-style: italic;"># we can use the following code to achieve the same effect.</span>
grep = pipeable<span style="color: black;">&#40;</span>grep<span style="color: black;">&#41;</span></pre></div></div>

<p>pipeable 其实只是一个普通的 Python 对象，可以是一个函数，也可以是一个类。如果是函数，那么 grep 就是给它的参数；如果是类，grep 是给它的初始化函数的参数。<strong>pipeable(grep) 必须返回一个能够调用的对象（含有 <code>__call__()</code> 方法）</strong>，除此之外没有其他要求。可见，decorator syntax 只是语法糖而已，但这个语法糖使得我们可以在函数之前加上修饰，而且从代码上一下就可以看出 grep 可以用在管道中。</p>
<p>下面首先描述 pipeable 修饰函数的要求和修饰后的行为，然后再看 pipeable 的实现。</p>
<p>可以用 pipeable 进行修饰的函数只有一个要求，第一个参数必须支持遍历操作。如果这个函数之后还有其他管道，那么函数的返回值也需要支持遍历。</p>
<p>被 pipeable 修饰之后，函数的行为如下：</p>
<ol>
<li>可以像没有修饰过时一样调用，函数行为不变</li>
<li>调用时给定除了第一个参数以外的所有参数，当函数对象出现在 &#8220;|&#8221; 右侧时，将左侧对象作为函数的第一个参数，与之前的参数一起完成函数的调用。</li>
</ol>
<p>用来实现 pipe syntax 的 decorator 如下（完整代码<a href='http://chenyufei.info/blog/wp-content/uploads/2009/10/shelike.py'>shelike.py</a>）。</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> functools <span style="color: #ff7700;font-weight:bold;">import</span> update_wrapper
<span style="color: #ff7700;font-weight:bold;">class</span> pipeable:
    <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>, method<span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">func</span> = method
        <span style="color: #008000;">self</span>.<span style="color: black;">args</span> = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">kwds</span> = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">reqlen</span> = <span style="color: #ff4500;">0</span>
        <span style="color: #808080; font-style: italic;"># Since we need to allow classes to be used in pipe, there are cases that</span>
        <span style="color: #808080; font-style: italic;"># method is not a function.</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #008000;">hasattr</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">func</span>, <span style="color: #483d8b;">'func_code'</span><span style="color: black;">&#41;</span>:
            <span style="color: #008000;">self</span>.<span style="color: black;">reqlen</span> = <span style="color: #008000;">self</span>.<span style="color: black;">func</span>.<span style="color: black;">func_code</span>.<span style="color: black;">co_argcount</span>
        <span style="color: #808080; font-style: italic;"># makes the wrapper object looks like the wrapped function</span>
        update_wrapper<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, method<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__call__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, <span style="color: #66cc66;">*</span>args, <span style="color: #66cc66;">**</span>kwds<span style="color: black;">&#41;</span>:
        curlen = <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>args<span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># An ugly hack to handle classes.</span>
        <span style="color: #ff7700;font-weight:bold;">if</span> curlen == <span style="color: #008000;">self</span>.<span style="color: black;">reqlen</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #ff4500;">0</span> == <span style="color: #008000;">self</span>.<span style="color: black;">reqlen</span>:
            <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">func</span><span style="color: black;">&#40;</span><span style="color: #66cc66;">*</span>args, <span style="color: #66cc66;">**</span>kwds<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">elif</span> curlen <span style="color: #66cc66;">!</span>= <span style="color: #008000;">self</span>.<span style="color: black;">reqlen</span> - <span style="color: #ff4500;">1</span>:
            <span style="color: #ff7700;font-weight:bold;">raise</span> <span style="color: #008000;">TypeError</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Arguments number wrong.'</span><span style="color: black;">&#41;</span>
&nbsp;
        cpy = deepcopy<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>
        cpy.<span style="color: black;">args</span> = args
        cpy.<span style="color: black;">kwds</span> = kwds
        <span style="color: #ff7700;font-weight:bold;">return</span> cpy
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__ror__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, <span style="color: #008000;">iter</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: black;">func</span><span style="color: black;">&#40;</span><span style="color: #008000;">iter</span>, <span style="color: #66cc66;">*</span><span style="color: #008000;">self</span>.<span style="color: black;">args</span>, <span style="color: #66cc66;">**</span><span style="color: #008000;">self</span>.<span style="color: black;">kwds</span><span style="color: black;">&#41;</span></pre></div></div>

<p>由于修改后的对象需要支持 &#8220;|&#8221; 运算符，pipeable 用类的形式实现更方便。（看了 Bruce Eckel 的文章后我也偏好用类来创建 decorator。）</p>
<ol>
<li>
<code>__init__()</code> 接受要修饰的函数，保存起来以备后面调用，这个函数在函数被修饰时执行，实际上就是创建一个pipeable 对象，把原先函数名赋给这个对象。最后的 update_wrapper 把被修饰函数的 <code>__name__, __doc__, __module__</code> 属性拷贝到创建的对象上，这样这个对象看起来就更加像原先的函数。（否则这些属性的值的都是这个对象的值。）
</li>
<li>
<code>__call__()</code> 在被修饰函数/替换的对象被调用时执行，这里根据参数个数直接调用原函数或者把参数保存起来</li>
<li>
<code>__ror__()</code> 就是实现 pipe syntax 的关键，把 &#8220;|&#8221; 左边的序列和其他参数组合起来完成被修饰函数的调用。有些 ugly hack 是为了处理 class 作为初始化参数的情况</li>
</ol>
<p>要把 shell 里的常用命令一个个用 Python 再实现一下也比较麻烦，因此我还实现了一个函数用来把 Python 里的数据转成字符串直接调用 shell 命令来处理，处理完再转成一行一行的 Python 字符串。比较下用 pipeable 和自己定义一个 class 的实现代码（现在的处理方式是把输入一次性全部转成字符串通过 OS 的管道传给外部程序，数据量大的话内存开销比较大，但我没有想到好的解决办法。）</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">@pipeable
<span style="color: #ff7700;font-weight:bold;">def</span> shell<span style="color: black;">&#40;</span><span style="color: #008000;">iter</span>, <span style="color: #dc143c;">cmd</span>=<span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:
    pipe = Popen<span style="color: black;">&#40;</span><span style="color: #dc143c;">cmd</span>, shell=<span style="color: #008000;">True</span>, stdin = PIPE, stdout = PIPE<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> pipe.<span style="color: black;">communicate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span><span style="color: #008000;">iter</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>.<span style="color: black;">splitlines</span><span style="color: black;">&#40;</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">class</span> shell:
    <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>, <span style="color: #dc143c;">cmd</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: #dc143c;">cmd</span> = <span style="color: #dc143c;">cmd</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__ror__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, <span style="color: #008000;">iter</span><span style="color: black;">&#41;</span>:
        pipe = Popen<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: #dc143c;">cmd</span>, shell=<span style="color: #008000;">True</span>, stdin = PIPE, stdout = PIPE<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> pipe.<span style="color: black;">communicate</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">''</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span><span style="color: #008000;">iter</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>.<span style="color: black;">splitlines</span><span style="color: black;">&#40;</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span></pre></div></div>

<p>有了这个管道，就可以把求和的任务直接用 awk 来完成了。</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">sumtmp = lines | shell<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;awk '{ total += $NF } END { print total }'&quot;</span><span style="color: black;">&#41;</span> | aslist
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">int</span><span style="color: black;">&#40;</span>sumtmp<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>当然，如果不想依赖外部程序，可以写一个提取字符串第 n 列/最后一列的函数，作为 tr 的参数放入管道（这个函数的名字不太好，其实就是 map，代码见<a href='http://chenyufei.info/blog/wp-content/uploads/2009/10/shelike.py'>shelike.py</a>），最后转成整数以后再用 sum 求和即可。</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: black;">&#40;</span>lines | tr<span style="color: black;">&#40;</span>last_column<span style="color: black;">&#41;</span> | tr<span style="color: black;">&#40;</span><span style="color: #008000;">int</span><span style="color: black;">&#41;</span> | <span style="color: #008000;">sum</span><span style="color: black;">&#41;</span></pre></div></div>

<p>我很喜欢看这样的代码，有点函数式的味道，而且可读性也很好 :)</p>
<p>有兴趣的话 <a href="http://pypi.python.org/pypi/decorator">decorator module</a> 和 <a href="http://wiki.python.org/moin/PythonDecoratorLibrary">PythonDecoratorLibrary</a> 提供了更多使用 decorator 的例子，推荐。</p>
]]></content:encoded>
			<wfw:commentRss>http://chenyufei.info/blog/2009-10-03/shell-like-data-processing-in-python-using-decorators/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<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 object，它的两个方法构成了 iterator protocol。

__iter__() 返回自身
next() 返回容器中的下一个对象，没有更多对象时应 raise
StopIteration，一旦抛出此异常，后续的调用一定也必须抛出此异常。

偷懒起见，直接拿 Beazley 的幻灯片里的例子来说明：

 [...]]]></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>
		<item>
		<title>编写 Unix 管道风格的 Python 代码</title>
		<link>http://chenyufei.info/blog/2009-01-28/writing-unix-pipe-style-python-code/</link>
		<comments>http://chenyufei.info/blog/2009-01-28/writing-unix-pipe-style-python-code/#comments</comments>
		<pubDate>Wed, 28 Jan 2009 14:15:07 +0000</pubDate>
		<dc:creator>chenyufei</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[pipe]]></category>

		<guid isPermaLink="false">http://chenyufei.info/blog/?p=201</guid>
		<description><![CDATA[先推荐一份幻灯片，David Beazley (&#34;Python essiential reference&#34;, PLY 的作者) 在 PyCon&#8217;2008 上报告的幻灯片，强烈推荐！！这篇文章的很多内容都来自或者受这份幻灯片的启发而来。
在上一篇文章里介绍了 Unix 管道的好处，那可不可以在写程序时也使用这样的思想呢？当然可以。看过 SICP 就知道，其实函数式编程中的 map, filter 都可以看作是管道思想的应用。但其实管道的思想不仅可以在函数式语言中使用，只要语言支持定义函数，有能够存放一组数据的数据结构，就可以使用管道的思想。
一个日志处理任务
这里直接以前面推荐的幻灯片里的例子来说明，应用场景如下：

某个目录及子目录下有一些 web 服务器的日志文件，日志文件名以 access-log 开头


日志格式如下
81.107.39.38 - ... "GET /ply/ply.html HTTP/1.1" 200 97238
81.107.39.38 - ... "GET /ply HTTP/1.1" 304 -

其中最后一列数字为发送的字节数，若为 &#8216;-&#8217; 则表示没有发送数据

目标是算出总共发送了多少字节的数据，实际上也就是要把日志记录的没一行的最后一列数值加起来


我不直接展示如何用 Unix 管道的风格来处理这个问题，而是先给出一些“不那么好”的代码，指出它们的问题，最后再展示管道风格的代码，并介绍如何使用 generator 来避免效率上的问题。想直接看管道风格的，点这里。
问题并不复杂，几个 for 循环就能搞定：

sum = 0
for path, dirlist, filelist in os.walk&#40;top&#41;:
    for name [...]]]></description>
			<content:encoded><![CDATA[<p>先推荐一份幻灯片，David Beazley (&quot;Python essiential reference&quot;, PLY 的作者) 在 PyCon&#8217;2008 上报告的<a class="reference external" href="http://www.dabeaz.com/generators/Generators.pdf">幻灯片</a>，强烈推荐！！这篇文章的很多内容都来自或者受这份幻灯片的启发而来。</p>
<p>在上一篇文章里介绍了 Unix 管道的好处，那可不可以在写程序时也使用这样的思想呢？当然可以。看过 SICP 就知道，其实函数式编程中的 map, filter 都可以看作是管道思想的应用。但其实管道的思想不仅可以在函数式语言中使用，只要语言支持定义函数，有能够存放一组数据的数据结构，就可以使用管道的思想。</p>
<h3>一个日志处理任务</h3>
<p>这里直接以前面推荐的幻灯片里的例子来说明，应用场景如下：</p>
<ul>
<li>某个目录及子目录下有一些 web 服务器的日志文件，日志文件名以 access-log 开头
</li>
<li>
日志格式如下</p>
<pre class="literal-block">81.107.39.38 - ... "GET /ply/ply.html HTTP/1.1" 200 97238
81.107.39.38 - ... "GET /ply HTTP/1.1" 304 -
</pre>
<p>其中最后一列数字为发送的字节数，若为 &#8216;-&#8217; 则表示没有发送数据
</li>
<li>目标是算出总共发送了多少字节的数据，实际上也就是要把日志记录的没一行的最后一列数值加起来
</li>
</ul>
<p>我不直接展示如何用 Unix 管道的风格来处理这个问题，而是先给出一些“不那么好”的代码，指出它们的问题，最后再展示管道风格的代码，并介绍如何使用 generator 来避免效率上的问题。想直接看管道风格的，点<a class="reference internal" href="#pipe-style">这里</a>。</p>
<p>问题并不复杂，几个 for 循环就能搞定：</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #008000;">sum</span> = <span style="color: #ff4500;">0</span>
<span style="color: #ff7700;font-weight:bold;">for</span> path, dirlist, filelist <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">walk</span><span style="color: black;">&#40;</span>top<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">for</span> name <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">fnmatch</span>.<span style="color: #008000;">filter</span><span style="color: black;">&#40;</span>filelist, <span style="color: #483d8b;">&quot;access-log*&quot;</span><span style="color: black;">&#41;</span>:
        <span style="color: #808080; font-style: italic;"># 对子目录中的每个日志文件进行处理</span>
        <span style="color: #ff7700;font-weight:bold;">with</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span>name<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> f:
            <span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> f:
                <span style="color: #ff7700;font-weight:bold;">if</span> line<span style="color: black;">&#91;</span>-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> == <span style="color: #483d8b;">'-'</span>:
                    <span style="color: #ff7700;font-weight:bold;">continue</span>
                <span style="color: #ff7700;font-weight:bold;">else</span>:
                    <span style="color: #008000;">sum</span> += <span style="color: #008000;">int</span><span style="color: black;">&#40;</span>line.<span style="color: black;">rsplit</span><span style="color: black;">&#40;</span><span style="color: #008000;">None</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>利用 <tt class="docutils literal"><span class="pre">os.walk</span></tt> 这个问题解决起来很方便，由此也可以看出 python 的 for 语句做遍历是多么的方便，不需要额外控制循环次数的变量，省去了设置初始值、更新、判断循环结束条件等工作，相比 C/C++/Java 这样的语言真是太方便了。看起来一切都很美好。</p>
<p>然而，设想以后有了新的统计任务，比如：</p>
<ol>
<li>统计某个特定页面的访问次数</li>
<li>处理另外的一些日志文件，日志文件名字以 error-log 开头</li>
</ol>
<p>完成这些任务直接拿上面的代码过来改改就可以了，文件名的 pattern 改一下，处理每个文件的代码改一下。其实每次任务的处理中，找到特定名字为特定 pattern 的文件的代码是一样的，直接修改之前的代码其实就引入了重复。</p>
<p>如果重复的代码量很大，我们很自然的会注意到。然而 python 的 for 循环实在太方便了，像这里找文件的代码一共就两行，哪怕重写一遍也不会觉得太麻烦。<strong>for 循环的方便使得我们会忽略这样简单代码的重复</strong>。然而，再怎么方便好用，<strong>for 循环无法重用，只有把它放到函数中才能进行重用</strong>。</p>
<p>(先考虑下是你会如何避免这里的代码的重复。下面马上出现的代码并不好，是“误导性”的代码，我会在之后再给出“更好”的代码。)</p>
<p>因此，我们把上面代码中<strong>不变的部分提取成一个通用的函数，可变的部分以参数的形式传入</strong>，得到下面的代码。</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> generic_process<span style="color: black;">&#40;</span>topdir, filepat, processfunc<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">for</span> path, dirlist, filelist <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">walk</span><span style="color: black;">&#40;</span>top<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">for</span> name <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">fnmatch</span>.<span style="color: #008000;">filter</span><span style="color: black;">&#40;</span>filelist, filepat<span style="color: black;">&#41;</span>:
            <span style="color: #ff7700;font-weight:bold;">with</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span>name<span style="color: black;">&#41;</span> f:
                processfunc<span style="color: black;">&#40;</span>f<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #008000;">sum</span> = <span style="color: #ff4500;">0</span>
<span style="color: #808080; font-style: italic;"># 很遗憾，python 对 closure 中的变量不能进行赋值操作，</span>
<span style="color: #808080; font-style: italic;"># 因此这里只能使用全局变量</span>
<span style="color: #ff7700;font-weight:bold;">def</span> add_count<span style="color: black;">&#40;</span>f<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">global</span> <span style="color: #008000;">sum</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> f:
        <span style="color: #ff7700;font-weight:bold;">if</span> line<span style="color: black;">&#91;</span>-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> == <span style="color: #483d8b;">'-'</span>:
            <span style="color: #ff7700;font-weight:bold;">continue</span>
        <span style="color: #ff7700;font-weight:bold;">else</span>:
            <span style="color: #008000;">sum</span> += <span style="color: #008000;">int</span><span style="color: black;">&#40;</span>line.<span style="color: black;">rsplit</span><span style="color: black;">&#40;</span><span style="color: #008000;">None</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
generic_process<span style="color: black;">&#40;</span><span style="color: #483d8b;">'logdir'</span>, <span style="color: #483d8b;">'access-log*'</span>, add_count<span style="color: black;">&#41;</span></pre></div></div>

<p>看起来不变和可变的部分分开了，然而 generic_process 的设计并不好。它除了寻找文件以外还调用了日志文件处理函数，因此在其他任务中很可能就无法使用。另外 add_count 的参数必须是 file like object，因此测试时不能简单的直接使用字符串。</p>
<h3><a id="pipe-style">管道风格的程序</a></h3>
<p>下面考虑用 Unix 的工具和管道我们会如何完成这个任务：</p>
<pre>
find logdir -name "access-log*" | \
xargs cat | \
grep '[^-]$' | \
awk '{ total += $NF } END { print total }'
</pre>
<p>find 根据文件名 pattern 找到文件，cat 把所有文件内容合并输出到 stdout，grep 从 stdin 读入，过滤掉行末为 &#8216;-&#8217; 的行，awk 提取每行最后一列，将数值相加，最后打印出结果。（省掉 cat 是可以的，但这样一来 grep 就需要直接读文件而不是只从标准输入读。）</p>
<p>我们可以在 python 代码中模拟这些工具，Unix 的工具通过文本来传递结果，在 python 中可以使用 list。</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> find<span style="color: black;">&#40;</span>topdir, filepat, processfunc<span style="color: black;">&#41;</span>:
    files = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> path, dirlist, filelist <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">walk</span><span style="color: black;">&#40;</span>top<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">for</span> name <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">fnmatch</span>.<span style="color: #008000;">filter</span><span style="color: black;">&#40;</span>filelist, filepat<span style="color: black;">&#41;</span>:
            files.<span style="color: black;">append</span><span style="color: black;">&#40;</span>name<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> files
&nbsp;
 <span style="color: #ff7700;font-weight:bold;">def</span> cat<span style="color: black;">&#40;</span>files<span style="color: black;">&#41;</span>:
    lines = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> <span style="color: #008000;">file</span> <span style="color: #ff7700;font-weight:bold;">in</span> files:
        <span style="color: #ff7700;font-weight:bold;">with</span> <span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #008000;">file</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> f:
            <span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> f:
                lines.<span style="color: black;">append</span><span style="color: black;">&#40;</span>line<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> lines
&nbsp;
 <span style="color: #ff7700;font-weight:bold;">def</span> grep<span style="color: black;">&#40;</span>pattern, lines<span style="color: black;">&#41;</span>:
    result = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">re</span>
    pat = <span style="color: #dc143c;">re</span>.<span style="color: #008000;">compile</span><span style="color: black;">&#40;</span>pattern<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> lines:
        <span style="color: #ff7700;font-weight:bold;">if</span> pat.<span style="color: black;">search</span><span style="color: black;">&#40;</span>line<span style="color: black;">&#41;</span>:
            result.<span style="color: black;">append</span><span style="color: black;">&#40;</span>line<span style="color: black;">&#41;</span>
    resurn result
&nbsp;
lines = grep<span style="color: black;">&#40;</span><span style="color: #483d8b;">'[^-]$'</span>, cat<span style="color: black;">&#40;</span>find<span style="color: black;">&#40;</span><span style="color: #483d8b;">'logdir'</span>, <span style="color: #483d8b;">'access-log*'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
col = <span style="color: black;">&#40;</span>line.<span style="color: black;">rsplit</span><span style="color: black;">&#40;</span><span style="color: #008000;">None</span>, <span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> lines<span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #008000;">sum</span><span style="color: black;">&#40;</span><span style="color: #008000;">int</span><span style="color: black;">&#40;</span>c<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> c <span style="color: #ff7700;font-weight:bold;">in</span> col<span style="color: black;">&#41;</span></pre></div></div>

<p>有了 <tt class="docutils literal"><span class="pre">find</span></tt>, <tt class="docutils literal"><span class="pre">cat</span></tt>, <tt class="docutils literal"><span class="pre">grep</span></tt> 这三个函数，只需要连续调用就可以像 Unix 的管道一样将这些函数组合起来。数据在管道中的变化如下图（简洁起见，过滤器直接标在箭头上 ）：</p>
<p><a href="http://chenyufei.info/blog/wp-content/uploads/2009/01/data-pipe.gif"><img src="http://chenyufei.info/blog/wp-content/uploads/2009/01/data-pipe-600x31.gif" alt="data-pipe" title="data-pipe" width="600" height="31" class="aligncenter size-medium wp-image-205" /></a></p>
<p>看起来现在的代码行数比最初直接用 for 循环的代码要多，但现在的代码就像 Unix 的那些小工具一样，每一个都更加可能被用到。我们可以把更多常用的 Unix 工具用 Python 来模拟，从而在 Python 代码中以 Unix 管道的风格来编写代码。</p>
<p>不过上面的代码性能很差，多个临时的 list 被创建。解决的办法是用 generator，因为篇幅比较长，具体做法放到下一篇文章中。</p>
]]></content:encoded>
			<wfw:commentRss>http://chenyufei.info/blog/2009-01-28/writing-unix-pipe-style-python-code/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
