<?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>Some reminiscences, some memories &#187; Python</title>
	<atom:link href="http://www.mikespook.com/index.php/category/python/feed" rel="self" type="application/rss+xml" />
	<link>http://www.mikespook.com</link>
	<description>Just another boring day</description>
	<lastBuildDate>Tue, 10 Jan 2012 03:14:06 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>[翻译]Go 和 Python 的 Web 服务器性能对比</title>
		<link>http://www.mikespook.com/2011/02/%e7%bf%bb%e8%af%91go-%e5%92%8c-python-%e7%9a%84-web-%e6%9c%8d%e5%8a%a1%e5%99%a8%e6%80%a7%e8%83%bd%e5%af%b9%e6%af%94/</link>
		<comments>http://www.mikespook.com/2011/02/%e7%bf%bb%e8%af%91go-%e5%92%8c-python-%e7%9a%84-web-%e6%9c%8d%e5%8a%a1%e5%99%a8%e6%80%a7%e8%83%bd%e5%af%b9%e6%af%94/#comments</comments>
		<pubDate>Thu, 24 Feb 2011 04:46:27 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Stardy & Research]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[性能]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=857</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
原文在此：http://ziutek.github.com/web_bench/

由于是早上看到 鱼哥，在推上的推荐，我实在忍不住……这是中午的草率之举，所以 鱼哥 对本文的翻译负全责。

PS：别说我工作状态不饱满，我在等丫的程序执行完……

<span class="readmore"><a href="http://www.mikespook.com/2011/02/%e7%bf%bb%e8%af%91go-%e5%92%8c-python-%e7%9a%84-web-%e6%9c%8d%e5%8a%a1%e5%99%a8%e6%80%a7%e8%83%bd%e5%af%b9%e6%af%94/" title="[翻译]Go 和 Python 的 Web 服务器性能对比">阅读全文——共2501字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>原文在此：<a href="http://ziutek.github.com/web_bench/" target="_blank">http://ziutek.github.com/web_bench/</a></p>
<p>由于是早上看到 <a href="https://twitter.com/smallfish_xy" target="_blank">鱼哥</a>，在推上的推荐，我实在忍不住……这是中午的草率之举，所以 <a href="https://twitter.com/smallfish_xy" target="_blank">鱼哥</a> 对本文的翻译负全责。</p>
<p>PS：别说我工作状态不饱满，我在等丫的程序执行完……</p>
<p>&#8212;&#8212;&#8212;&#8212;翻译分割线&#8212;&#8212;&#8212;&#8212;</p>
<h1>Go 和 Python Web 服务器性能对比</h1>
<p>我通常使用 <a href="http://www.python.org/">Python</a> 来构建 Web 应用。一年前，在兴趣的驱使下，我开始学习 <a href="http://golang.org/">Go</a>。 在此期间，我重写了一些原本由 C 开发的 CGI 应用，包括运行于 <a href="http://en.wikipedia.org/wiki/Chroot">chroot</a> 环境下的同 <a href="http://www.acme.com/software/thttpd/">thttpd</a> 服务器一起的应用。我开始寻找可以开发易于 chroot、且内置 Web 服务器的独立 Web 应用的工具。那时，我开始玩 <a href="http://www.getwebgo.com/">web.go</a> 框架、<a href="https://github.com/hoisie/mustache.go">mustache.go</a> 模板、Go 原生 <a href="http://golang.org/pkg/http/">http</a> 包和 <a href="https://github.com/Philio/GoMySQL">GoMySQL</a> 数据库 API。我发现，有 http、mustache.go  GoMySQL 包的 Go 可以是我用来工作的不错的工具组合。因此，我决定使用 Go 编写我的应用。<br />
<span id="more-857"></span><br />
在工作过程中发现，我需要比 mustache.go 更加灵活，比 GoMySQL 更加成熟、没有那么多 Bug 的东西。最终，我使用 <a href="https://github.com/ziutek/kasia.go">Kasia.go</a> 模板和  <a href="https://github.com/ziutek/mymysql">MyMySQL</a> （为我的应用定制开发的包，不过我将其贡献给了 Go 社区）。重写的应用即便是在比以前的负载更高的运营环境下，也工作得很好。我开始思考这个问题：用 Go 实现独立 Web 应用比 Python 到底快了（或者是慢了）多少。我决定做一些各种框架和服务器不同的用途的测试。为了比较，我选择了下面的 Go 包：</p>
<ul>
<li>原始的 Go http包；</li>
<li>web.go 框架（它使用运行于独立模式[standalone mode] 的 http 包）；</li>
<li><a href="http://garyburd.github.com/twister/">twister</a> 框架 （它同样使用 http 包）。</li>
</ul>
<p>和下面的 Python Web服务器/框架：</p>
<ul>
<li>使用 <a href="http://www.cherrypy.org/">CherryPy</a> WSGI 服务器的<a href="http://webpy.org/"> web.py</a> 框架；</li>
<li>使用 <a href="http://trac.saddi.com/flup">flup</a> <a href="http://en.wikipedia.org/wiki/FastCGI">FastCGI</a> 做 <a href="http://wiki.nginx.org/Main">nginx</a> 服务器的后台处理的 web.py 框架；</li>
<li><a href="http://www.tornadoweb.org/">tornado</a> 异步服务器/框架；</li>
<li>nginx 做负载均衡的 tornado。</li>
</ul>
<p>每一个用例，我都编写了一个小应用，略微复杂一些的、传统的 Hello World 例子。任何应用都包括：</p>
<ul>
<li>使用正则表达式通过 URL 路径传递参数；</li>
<li>使用语句创建多行输出；</li>
<li>使用 printf 形式的格式化函数/表达式格式化输出。</li>
</ul>
<p>我想，这些都是在 Web 应用中常见的操作，所以应当包含在任何简易的性能对比测试中。所有测试应用的代码在下面的链接中：</p>
<ul>
<li> <a href="https://github.com/ziutek/web_bench/blob/master/http.go">Go http</a></li>
<li> <a href="https://github.com/ziutek/web_bench/blob/master/webgo.go">web.go</a></li>
<li> <a href="https://github.com/ziutek/web_bench/blob/master/twister.go">twister</a></li>
<li> <a href="https://github.com/ziutek/web_bench/blob/master/webpy.py">web.py</a></li>
<li> <a href="https://github.com/ziutek/web_bench/blob/master/trnado.py">tornado</a></li>
</ul>
<h2>测试环境</h2>
<p>测试环境包括两台 使用千兆以太网链接的PC （请求发起者和应用服务器）。</p>
<ul>
<li>请求发起者：2 x Xeon 2.6 GHz with hyperthreading, Debian SID,     kernel: 2.6.33.7.2-rt30-1-686 #1 SMP PREEMPT RT；</li>
<li>服务器: MSI Netbook with two core Intel U4100 1.30GHz, AC power     connected, 64-bit Ubuntu 10.10, kernel: 2.6.35-25-generic #44-Ubuntu SMP,     Python 2.6.6-2ubuntu2, web.py 0.34-2, flup 1.0.2-1, tornado 0.2-1, nginx     0.7.67-3ubuntu1；</li>
</ul>
<p>为了产生 HTTP 请求并且评估测试应用的性能，我使用 <a href="http://www.joedog.org/index/siege-home">siege</a> 性能测试工具。Siege 可以用多线程模拟多个用户。我使用了下面的命令产生请求：</p>
<pre class="brush: bash; title: ; notranslate">siege -c 200 -t 20s http://SERVER_ADDR:8080/Hello/100</pre>
<p>或者多个类似的命令，减少参数 -c 的量（在这个测试中，我同时运行了多个 Python 脚本）。它模拟了 200 用户的请求，并持续 20 秒。这个 URL 使得 Web 应用对每个请求都输出 100 行。Go 应用使用 Go 发布版 2011-02-01.1。</p>
<h2>结果</h2>
<p>GOMAXPROCS=1, 一个 Python 进程：</p>
<table style="border: 1px solid;">
<tbody>
<tr>
<th>框架</th>
<th colspan="2">请求速率 [1/sec]</th>
</tr>
<tr>
<td>Go http</td>
<td>1350</td>
<td></td>
</tr>
<tr>
<td>Twister</td>
<td>1324</td>
<td></td>
</tr>
<tr>
<td>Web.go</td>
<td>1141</td>
<td></td>
</tr>
<tr>
<td>Tornado</td>
<td>882</td>
<td></td>
</tr>
<tr>
<td>Tornado+nginx</td>
<td>862</td>
<td></td>
</tr>
<tr>
<td>Web.py+CheryPy</td>
<td>169</td>
<td></td>
</tr>
<tr>
<td>Web.py+nginx</td>
<td>114</td>
<td></td>
</tr>
</tbody>
</table>
<p>GOMAXPROCS=2, 两个 Python 并发进程：</p>
<table style="border: 1px solid;">
<tbody>
<tr>
<th>框架</th>
<th colspan="2">请求速率 [1/sec]</th>
</tr>
<tr>
<td>Go http</td>
<td>1768</td>
<td></td>
</tr>
<tr>
<td>Twister</td>
<td>1746</td>
<td></td>
</tr>
<tr>
<td>Tornado</td>
<td>1682</td>
<td></td>
</tr>
<tr>
<td>Web.go</td>
<td>1516</td>
<td></td>
</tr>
<tr>
<td>Tornado+nginx</td>
<td>1378</td>
<td></td>
</tr>
<tr>
<td>Web.py+CheryPy</td>
<td>308</td>
<td></td>
</tr>
<tr>
<td>Web.py+nginx</td>
<td>198</td>
<td></td>
</tr>
</tbody>
</table>
<p>GOMAXPROCS=4, 四个 Python 并发进程：</p>
<table style="border: 1px solid;">
<tbody>
<tr>
<th>框架</th>
<th colspan="2">请求速率 [1/sec]</th>
</tr>
<tr>
<td>Go http</td>
<td>2063</td>
<td></td>
</tr>
<tr>
<td>Twister</td>
<td>2020</td>
<td></td>
</tr>
<tr>
<td>Web.go</td>
<td>1753</td>
<td></td>
</tr>
<tr>
<td>Tornado</td>
<td>1662</td>
<td></td>
</tr>
<tr>
<td>Tornado+nginx</td>
<td>1364</td>
<td></td>
</tr>
<tr>
<td>Web.py+CheryPy</td>
<td>304</td>
<td></td>
</tr>
<tr>
<td>Web.py+nginx</td>
<td>211</td>
<td></td>
</tr>
</tbody>
</table>
<p>Web.py+nginx 工作的 flup FastCGI 选项：multiplexed=False, multithreaded=False。如果 multiplexed=True 它会运行得慢一些。如果 multithreaded=True 而只有一个进程服务于 nginx 服务器，会报下面的错误：</p>
<pre class="brush: bash; title: ; notranslate">[error] 18166#0: *66139 connect() to
unix:/home/michal/Programowanie/web_bench/socket failed (11: Resource
temporarily unavailable) while connecting to upstream</pre>
<p>FastCGI 的多进程由 <a href="http://redmine.lighttpd.net/projects/spawn-fcgi/wiki">spawn-fcgi</a> 产生。</p>
<h2>结论</h2>
<p>你可以看到 Go 赢得了几乎所有的测试用例。web.go 框架的那个不太理想的结果可能是由于它先尝试用指定的 URL 寻找静态页面，然后才会执行处理方法。让我惊讶的是 tornado Python 框架如此之高的性能，尤其是跟 web.py 框架相比而言。我同样对 CherryPy 服务器比 nginx+flup 快感到惊讶 (我使用 web.py+flup+nginx 跑几乎所有的 Python Web 应用)。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2011/02/%e7%bf%bb%e8%af%91go-%e5%92%8c-python-%e7%9a%84-web-%e6%9c%8d%e5%8a%a1%e5%99%a8%e6%80%a7%e8%83%bd%e5%af%b9%e6%af%94/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>python 自身的协程实现？</title>
		<link>http://www.mikespook.com/2010/03/python-%e8%87%aa%e8%ba%ab%e7%9a%84%e5%8d%8f%e7%a8%8b%e5%ae%9e%e7%8e%b0%ef%bc%9f/</link>
		<comments>http://www.mikespook.com/2010/03/python-%e8%87%aa%e8%ba%ab%e7%9a%84%e5%8d%8f%e7%a8%8b%e5%ae%9e%e7%8e%b0%ef%bc%9f/#comments</comments>
		<pubDate>Fri, 19 Mar 2010 16:09:15 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[yield]]></category>
		<category><![CDATA[协程]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=514</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
本来，想给这个随笔起个更眩的名字：《跟随赖神学协程——之一》，原因嘛，自然是因为赖神的协程三篇之一（协程初接触）。不过，怕赖神k我，所以标题党还是朴素一点吧。

至于标题里的问号，是我有意加上去的。原因是在推上赖神认为“python 语言和标准库是不支持协程的（3.x部分支持）”，并且如果 python 自身支持协程，“stackless py 该有多么惭愧啊,  ”（stackless python 的协程详情请看这里）。但是因为 PEP 0342 的描述，令我很迷惑。python 自身是否可以实现协程，这个值得商榷。我不确定，保守点好，给自己个后路走。加个问号吧……如果这不算协程，大家就当我实现了“伪协程”吧。嘿嘿……

<span class="readmore"><a href="http://www.mikespook.com/2010/03/python-%e8%87%aa%e8%ba%ab%e7%9a%84%e5%8d%8f%e7%a8%8b%e5%ae%9e%e7%8e%b0%ef%bc%9f/" title="python 自身的协程实现？">阅读全文——共1285字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>本来，想给这个随笔起个更眩的名字：《跟随赖神学协程——之一》，原因嘛，自然是因为赖神的<a href="http://blog.csdn.net/lanphaday/archive/2010/03/19/5397038.aspx" target="_blank">协程三篇之一（协程初接触）</a>。不过，怕赖神k我，所以标题党还是朴素一点吧。</p>
<p>至于标题里的问号，是我有意加上去的。原因是在推上赖神认为<strong>“python 语言和标准库是不支持协程的（3.x部分支持）”</strong>，并且如果 python 自身支持协程，<strong>“stackless py 该有多么惭愧啊, <img src='http://www.mikespook.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> ”</strong>（stackless python 的协程详情请看<a href="http://members.verizon.net/olsongt/stackless/why_stackless.html#coroutines" target="_blank">这里</a>）。但是因为 <a href="http://www.python.org/dev/peps/pep-0342/" target="_blank">PEP 0342</a> 的描述，令我很迷惑。python 自身是否可以实现协程，这个值得商榷。我不确定，保守点好，给自己个后路走。加个问号吧……如果这不算协程，大家就当我实现了“伪协程”吧。嘿嘿……<span id="more-514"></span></p>
<p>在赖神的博客里有对协程的一个简单的描述，为了说明下面的代码，特别摘抄于此：<strong>“协程是用户空间线程，操作系统其存在一无所知，所以需要用户自己去做调度，用来执行协作式多任务非常合适。”</strong></p>
<p>赖神博客中的例子应当是引用了维基百科对于<a href="http://zh.wikipedia.org/wiki/%E5%8D%8F%E7%A8%8B" target="_blank">协程</a>的说明。下面的代码正是基于这个伪代码实现的，同样的原因摘抄于此：<br />
生产者：</p>
<pre class="brush: python; title: ; notranslate">
   loop
       while q is not full
           create some new items
           add the items to q
       yield to consume
</pre>
<p>消费者</p>
<pre class="brush: python; title: ; notranslate">
   loop
       while q is not empty
           remove some items from q
           use the items
       yield to produce
</pre>
<p>这两段伪代码，赖神的博客上已经有说明，不再累述。看看 python 如何实现这个的吧：</p>
<pre class="brush: python; title: ; notranslate">
#!/usr/bin/evn python
# -*- coding:utf-8 -*-
import time
# 生产者
def produce(l):
	i = 0
	while True:
		i += 1
		l.append(i)
		time.sleep(2)
		# 暂时跳出当前方法
		yield i
		# 消费者 send 了以后，就又回到了这里继续执行
		pass

# 消费者
def consume(l):
	p = produce(l)
	while True:
		try:
			# 获取生产者的执行
			i = p.next()
			# 这里完全是为了匹配伪代码而写，实际上这个 list 在这种情况下，即不会多于一个 item，也不会为空
			while len(l) &gt; 0:
				print l.pop()
		except:
			# 生产者提供的所有的执行都处理完了，通过 send 返回生产者跳出的地方
			p.send(None)
l = []
consume(l)
</pre>
<p>这段代码是可以正常执行的，由生产者每秒产生一个数字放入 list，由消费者从 list 中取出并打印。<br />
大家对照赖神的说明和维基百科上的内容理解一下吧。</p>
<p>总结一下！我对于 python 的协程的观点是：<strong>“python 自身已经提供了实现协程的基础条件，是可以很容易的实现概念中的协程。但是由于栈的问题，只能由循环的方式来实现调用。而 stackless 在 python 原有的基础上，改进了 python 在协程上的实现方式，使其代码的表达更加的自然。”</strong></p>
<p>如有不对，尽请拍砖，期待赖神的协程系列二、三。继续品味协程……</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2010/03/python-%e8%87%aa%e8%ba%ab%e7%9a%84%e5%8d%8f%e7%a8%8b%e5%ae%9e%e7%8e%b0%ef%bc%9f/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Twisted 的 MemcacheProtocol 使用</title>
		<link>http://www.mikespook.com/2010/02/twisted-%e7%9a%84-memcacheprotocol-%e4%bd%bf%e7%94%a8/</link>
		<comments>http://www.mikespook.com/2010/02/twisted-%e7%9a%84-memcacheprotocol-%e4%bd%bf%e7%94%a8/#comments</comments>
		<pubDate>Mon, 08 Feb 2010 14:16:11 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[defer]]></category>
		<category><![CDATA[Demo]]></category>
		<category><![CDATA[memcache]]></category>
		<category><![CDATA[twisted]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=467</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
Twisted 的 defer 是个强大但是诡异的东西，遗憾的是，除了官方文档和这些文档的翻译，其他资料不多。而基于 defer 的客户端协议实现资料就更少了。Memcache 的协议实现也是利用 defer，但是除了一个单元测试的代码外，很难看到其他可以参考的实现。搞了几天，总算是有一个简单的封装，可以避开 defer，无须让所有参与开发的技术人员一定要弄懂 defer 才能用 MemCacheProtocol。

上代码吧，这样封装，应该是比较好理解的……

#!/usr/bin/evn python

<span class="readmore"><a href="http://www.mikespook.com/2010/02/twisted-%e7%9a%84-memcacheprotocol-%e4%bd%bf%e7%94%a8/" title="Twisted 的 MemcacheProtocol 使用">阅读全文——共3517字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>Twisted 的 defer 是个强大但是诡异的东西，遗憾的是，除了官方文档和这些文档的翻译，其他资料不多。而基于 defer 的客户端协议实现资料就更少了。Memcache 的协议实现也是利用 defer，但是除了一个单元测试的代码外，很难看到其他可以参考的实现。搞了几天，总算是有一个简单的封装，可以避开 defer，无须让所有参与开发的技术人员一定要弄懂 defer 才能用 MemCacheProtocol。<br />
上代码吧，这样封装，应该是比较好理解的……<span id="more-467"></span></p>
<pre lang="python">#!/usr/bin/evn python
# -*- coding:utf-8 -*-
'''
Created on 2010-2-7

@author: mikespook
'''
from twisted.internet import reactor, protocol
from twisted.protocols.memcache import MemCacheProtocol, DEFAULT_PORT

from pnp.log import log

class MemCache:
    '''
    memcache 调用接口
    >>>def printResult(result):
    >>>    print result
    >>>mc = MemCache()
    >>>mc.version(printResult)
    >>>reactor.callLater(1, reactor.stop)
    >>>reactor.run()
    '''
    # 客户端创建对象
    clientCreator = protocol.ClientCreator(reactor, MemCacheProtocol)
    def __init__(self, host, port=DEFAULT_PORT):
        '''
        初始化
        @param host: 主机名
        @param port: 端口号
        '''
        self.mcDefer = self.clientCreator.connectTCP(host, port)
        self.mcDefer.addErrback(self._onErr)

    def _onErr(self, err):
        '''
        错误回调
        '''
        log.err(err) 

    def version(self, callback):
        '''
        获取版本
        @param callback: 回调函数，结果将作为参数传递入该接口
        '''
        self.mcDefer.addCallback(self._mcVersion, callback)

    def _mcVersion(self, proto, callback):
        '''
        版本回调
        @param proto: twisted.p.mMemCacheProtocol
        @param callback: 回调函数返回结果
        '''
        d = proto.version()
        d.addErrback(self._onErr)
        d.addCallback(callback)
        return proto

    def increment(self, key, callback):
        '''
        增长
        @param key: 键
        @param callback: 回调函数
        '''
        self.mcDefer.addCallback(self._mcIncrement, key, callback)        

    def _mcIncrement(self, proto, key, callback):
        '''
        增长回调
        @param proto: twisted.p.mMemCacheProtocol
        @param key: 键
        @param callback: 回调函数返回结果
        '''
        d = proto.increment(key)
        d.addErrback(self._onErr)
        d.addCallback(callback)
        return proto

    def decrement(self, key, callback):
        '''
        减少
        @param key: 键
        @param callback: 回调函数
        '''
        self.mcDefer.addCallback(self._mcDecrement, key, callback)        

    def _mcDecrement(self, proto, key, callback):
        '''
        减少回调
        @param proto: twisted.p.mMemCacheProtocol
        @param key: 键
        @param callback: 回调函数返回结果
        '''
        d = proto.decrement(key)
        d.addErrback(self._onErr)
        d.addCallback(callback)
        return proto

    def get(self, key, callback):
        '''
        获取值
        @param key: 键
        @param callback: 回调函数
        '''
        self.mcDefer.addCallback(self._mcGet, key, callback)        

    def _mcGet(self, proto, key, callback):
        '''
        获取值回调
        @param proto: twisted.p.mMemCacheProtocol
        @param key: 键
        @param callback: 回调函数返回结果
        '''
        d = proto.get(key)
        d.addErrback(self._onErr)
        d.addCallback(callback)
        return proto

    def set(self, key, value, callback):
        '''
        设置值
        @param key: 键
        @param value: 值
        @param callback: 回调函数
        '''
        self.mcDefer.addCallback(self._mcSet, key, value, callback)        

    def _mcSet(self, proto, key, value, callback):
        '''
        设置值回调
        @param proto: twisted.p.mMemCacheProtocol
        @param key: 键
        @param value: 值
        @param callback: 回调函数返回结果
        '''
        d = proto.set(key, value)
        d.addErrback(self._onErr)
        d.addCallback(callback)
        return proto

    def delete(self, key, callback):
        '''
        删除
        @param key: 键
        @param value: 值
        @param callback: 回调函数
        '''
        self.mcDefer.addCallback(self._mcDelete, key, callback)        

    def _mcDelete(self, proto, key, value, callback):
        '''
        删除回调
        @param proto: twisted.p.mMemCacheProtocol
        @param key: 键
        @param value: 值
        @param callback: 回调函数返回结果
        '''
        d = proto.delete(key)
        d.addErrback(self._onErr)
        d.addCallback(callback)
        return proto

    def stats(self, callback):
        '''
        状态
        @param key: 键
        @param value: 值
        @param callback: 回调函数
        '''
        self.mcDefer.addCallback(self._mcStats, callback)        

    def _mcStats(self, proto, callback):
        '''
        状态回调
        @param proto: twisted.p.mMemCacheProtocol
        @param key: 键
        @param value: 值
        @param callback: 回调函数返回结果
        '''
        d = proto.stats()
        d.addErrback(self._onErr)
        d.addCallback(callback)
        return proto

    def flushAll(self, callback):
        '''
        刷新
        @param key: 键
        @param value: 值
        @param callback: 回调函数
        '''
        self.mcDefer.addCallback(self._mcFlushAll, callback)        

    def _mcFlushAll(self, proto, callback):
        '''
        刷新回调
        @param proto: twisted.p.mMemCacheProtocol
        @param key: 键
        @param value: 值
        @param callback: 回调函数返回结果
        '''
        d = proto.flushAll()
        d.addErrback(self._onErr)
        d.addCallback(callback)
        return proto</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2010/02/twisted-%e7%9a%84-memcacheprotocol-%e4%bd%bf%e7%94%a8/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Netbeans Python 调试，郁闷的包导入问题</title>
		<link>http://www.mikespook.com/2009/11/netbeans-python-%e8%b0%83%e8%af%95%ef%bc%8c%e9%83%81%e9%97%b7%e7%9a%84%e5%8c%85%e5%af%bc%e5%85%a5%e9%97%ae%e9%a2%98/</link>
		<comments>http://www.mikespook.com/2009/11/netbeans-python-%e8%b0%83%e8%af%95%ef%bc%8c%e9%83%81%e9%97%b7%e7%9a%84%e5%8c%85%e5%af%bc%e5%85%a5%e9%97%ae%e9%a2%98/#comments</comments>
		<pubDate>Tue, 10 Nov 2009 03:50:00 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[NetBeans]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[调试]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=419</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
这几天在帮忙开发一个 python 项目。使用 netbeans 的时候，遇到一个诡异的问题。

如果使用“运行”来跑项目，一切正常。而如果使用“调试”来跑项目，当执行到 import 第三方的库的时候，就会产生“ImportError: No module named xxxx”。郁闷不已，上网查了半天，没找到可用的信息。

毛主席教育我们“自己动手，丰衣足食”。于是就祭出“观察、归纳和总结”的科学的研究方法，对这个灵异现象进行了一番探索。

<span class="readmore"><a href="http://www.mikespook.com/2009/11/netbeans-python-%e8%b0%83%e8%af%95%ef%bc%8c%e9%83%81%e9%97%b7%e7%9a%84%e5%8c%85%e5%af%bc%e5%85%a5%e9%97%ae%e9%a2%98/" title="Netbeans Python 调试，郁闷的包导入问题">阅读全文——共1424字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>这几天在帮忙开发一个 python 项目。使用 netbeans 的时候，遇到一个诡异的问题。</p>
<p>如果使用“运行”来跑项目，一切正常。而如果使用“调试”来跑项目，当执行到 import 第三方的库的时候，就会产生“ImportError: No module named xxxx”。郁闷不已，上网查了半天，没找到可用的信息。</p>
<p>毛主席教育我们“自己动手，丰衣足食”。于是就祭出“观察、归纳和总结”的科学的研究方法，对这个灵异现象进行了一番探索。<span id="more-419"></span></p>
<p>出现导入问题的包都是用了 egg 打包的第三方包。在我的系统中，这几个包都是放在/usr/local/lib/python2.6/dist-packages/下。在命令行能正常执行，在 netbeans 中直接执行也无异常。说明包本身的安装没有问题。那么 netbeans 调试就出现找不到这个包，是不是 netbeans 的调试环境有什么特殊呢？看了一下 netbeans 的调试窗口，发现这个脚本~/.netbeans/6.7/config/nbPython/debug/nbpythondebug/jpydaemon.py。很明显，这个是 netbeans 为了集成调试功能而开发的一个 python 调试器。难道是这个netbeans自带的调试器的问题？祭出 sys.path 一看，果然正常执行的时候，dist-packages 下的 egg 包都被放到搜索路径里。而调试的时候，没有一个 egg 包在 sys.path 中。而是多了一些 netbeans 使用的目录而已。</p>
<p>郁闷啊，为什么调试器不能直接继承系统环境呢？</p>
<p>于是乎，将每个 egg 包都手工加入 Python Platforms 的 Python Path 里。问题解决了。可以带第三方 egg 包来调试项目了。</p>
<p>虽然问题解决了，不过还是不爽，如果我一个项目要是有 n 个 egg 包，那不是要每个都设置一下？</p>
<p>Hacking……</p>
<p>发现 jpydaemon.py 的 1588 行修改了sys.path，导致默认的路径信息丢失。</p>
<pre lang="python">pythonPath = dbgutils.PythonPathHandler(None)
pythonPath.getPyPathFromEnv()</pre>
<p>再检查 dbgutils.py ，原来 netbeans 的调试环境只读取了环境变量 PYTHONPATH，未添加默认的 sys.path。而我并没有使用 PYTHONPATH 环境变量，所以导致一些安装好的包无法导入。164 行直接覆盖了 sys.path。</p>
<pre lang="python">    def getPyPathFromEnv( self ):
        "PYTHONPATH env and set sys.path out of it "
        pyPath = os.environ["PYTHONPATH"]
        # cleanly take care of previous ';' convention
        if os.pathsep != ';':
            pyPath.replace(';' , os.pathsep)
        if pyPath.find(os.pathsep) != -1:
            sys.path = pyPath.split(os.pathsep)
            # remove empty nodes first
            for element in sys.path:
                if ( len(element.strip())==0 ):
                    sys.path.remove(element)</pre>
<p>修改 164 行</p>
<pre lang="python">sys.path = pyPath.split(os.pathsep) + sys.path</pre>
<p>这个世界又清爽了……</p>
<p>不知道会不会有什么问题。有问题，继续 fix 好了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2009/11/netbeans-python-%e8%b0%83%e8%af%95%ef%bc%8c%e9%83%81%e9%97%b7%e7%9a%84%e5%8c%85%e5%af%bc%e5%85%a5%e9%97%ae%e9%a2%98/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>脚本语言的配置文件</title>
		<link>http://www.mikespook.com/2009/09/%e8%84%9a%e6%9c%ac%e8%af%ad%e8%a8%80%e7%9a%84%e9%85%8d%e7%bd%ae%e6%96%87%e4%bb%b6/</link>
		<comments>http://www.mikespook.com/2009/09/%e8%84%9a%e6%9c%ac%e8%af%ad%e8%a8%80%e7%9a%84%e9%85%8d%e7%bd%ae%e6%96%87%e4%bb%b6/#comments</comments>
		<pubDate>Mon, 21 Sep 2009 02:30:26 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[xml]]></category>
		<category><![CDATA[配置文件]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=405</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
关于配置文件，在 PHP 的 Zend Framework 中我做过一些简单的关于性能的测试：http://www.mikespook.com/index.php/archives/36。将 ninnypro 的配置文件从 ini 修改为 xml ，并且声称能提高传说中的性能。

最近被调到另外一个在用 python 的组帮忙，阅读了他们的实现服务器端的 python 代码之，配置文件近二十余个，全是 xml 文件。为了使用着些配置文件，从 XMLFile 继承，实现了二十余个 Config 类。

<span class="readmore"><a href="http://www.mikespook.com/2009/09/%e8%84%9a%e6%9c%ac%e8%af%ad%e8%a8%80%e7%9a%84%e9%85%8d%e7%bd%ae%e6%96%87%e4%bb%b6/" title="脚本语言的配置文件">阅读全文——共1350字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>关于配置文件，在 PHP 的 Zend Framework 中我做过一些简单的关于性能的测试：<a href="http://www.mikespook.com/index.php/archives/36" target="_blank">http://www.mikespook.com/index.php/archives/36</a>。将 ninnypro 的配置文件从 ini 修改为 xml ，并且声称能提高传说中的性能。</p>
<p>最近被调到另外一个在用 python 的组帮忙，阅读了他们的实现服务器端的 python 代码之，配置文件近二十余个，全是 xml 文件。为了使用着些配置文件，从 XMLFile 继承，实现了二十余个 Config 类。</p>
<p><strong>这看起来似乎没什么问题。<span id="more-405"></span></strong></p>
<p>是的，一点问题也没有。</p>
<ol>
<li>XML是高度结构化的描述方式。</li>
<li>XML有大量丰富的包、库支持。</li>
<li>XML的跨语言能力也是相当优异。你可以轻松的用市面上常见的所有语言轻松的掌控 XML。</li>
</ol>
<p>这些都决定了 XML 作为配置文件，肯定是比 ini 或者自定义结构的配置文件更加优异的结构。同时，大量的实践也证明 XML 的优异性。例如 j2ee 的实践、SOA的实现、Jabber 的应用都说明 XML: is the lord of the configuration。</p>
<p><strong>但是真得一点问题都没有么？</strong></p>
<p>首先，来看一下 Zend Framework 的 Zend_Config。由于 PHP 自身的数据结构特点，Zend_Config_Xml 和 Zend_Config_Ini 两个类都会加载相应的配置文件，解析后作为数组传递到 Zend_Config 类中。也就是说，最终殊途同归，所有的配置都变成了 PHP 的数组。</p>
<p>然后，再来看一下那个项目中的配置文件。结构化的 XML 被 XMLFile 读取，然后转化为 XMLFile 对象。再统一放入 ConfigManagement 中，经过转换，将部分结构转为 dict 使用。</p>
<p>使用配置文件的原因，我猜测是因为以前，编译语言开发的程序经过编译后，不具有动态改变参数的能力。虽然可以从命令行通过传参来在每次运行时修改配置，但是无论是谁需要通过命令行传递成百上千的参数，都会疯掉。而且，命令行传参，不具有结构化的特征。在一些场景下，需要快速修改部分配置，就变得极为困难。</p>
<p>于是聪明的程序员发明了对人友好的、结构化的配置文件，通过读取配置文件来改变程序内部参数的使用。</p>
<p>这一习惯一直沿用至今。伴随 XML 的问世，配置文件的地位再次上升。更有甚者，在开发中不做编码，只写配置。</p>
<p><strong>看出问题了么？</strong></p>
<p>如果还是没有想法的话，那就再来看看脚本语言：</p>
<ol>
<li>脚本语言具有动态性。</li>
<li>脚本语言自身就是结构化的。</li>
<li>脚本语言可以增强编译语言的动态能力。</li>
</ol>
<p>在各个脚本语言应用 XML 作为配置文件的实践中，不论是 PHP 的 array，还是 Python 的 dict，无一例外的将配置文件转换为了脚本语言内部的一种数据结构。在多数情况下，这也是 XML 作为脚本语言配置文件唯一的解决方案。</p>
<p>再来看一个问题：在项目中使用 XML 作为配置文件的时候，是否有使用 DTD 作为 XML 的效验工具？</p>
<p>如果答案是否定的，那么 XML作为脚本语言就没有任何优势。没有应用 DTD 的 XML 就如同 PHP 的 array，Python 的 dict 一样。</p>
<p>那又何必要苦苦的追随 XML，不用脚本语言的数据结构来直接编写配置文件呢？</p>
<p>本来，早就像对这个话题写点什么。星期六（2009-09-19）的时候参加了广州技术沙龙，听了朱童鞋的 nginx 代码剖析后，突然有所顿悟：<strong></strong></p>
<p><strong>成功在于细节，性能在于细节。</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2009/09/%e8%84%9a%e6%9c%ac%e8%af%ad%e8%a8%80%e7%9a%84%e9%85%8d%e7%bd%ae%e6%96%87%e4%bb%b6/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Django 1.0.2 mod_python 方式报 TypeError 的解决方法</title>
		<link>http://www.mikespook.com/2009/05/django-102-mod_python-%e6%96%b9%e5%bc%8f%e6%8a%a5-typeerror-%e7%9a%84%e8%a7%a3%e5%86%b3%e6%96%b9%e6%b3%95/</link>
		<comments>http://www.mikespook.com/2009/05/django-102-mod_python-%e6%96%b9%e5%bc%8f%e6%8a%a5-typeerror-%e7%9a%84%e8%a7%a3%e5%86%b3%e6%96%b9%e6%b3%95/#comments</comments>
		<pubDate>Tue, 05 May 2009 03:07:07 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[mod_python]]></category>
		<category><![CDATA[TypeError]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=297</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
尝试用 Django 写了一个简单的企业站，部署过程中出了不少问题，不过还好都一一解决了。

“激动不已”的看到了首页，长出一口气：“总不会再出什么其他问题了吧！”随手点了一个内部链接：

&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;

<span class="readmore"><a href="http://www.mikespook.com/2009/05/django-102-mod_python-%e6%96%b9%e5%bc%8f%e6%8a%a5-typeerror-%e7%9a%84%e8%a7%a3%e5%86%b3%e6%96%b9%e6%b3%95/" title="Django 1.0.2 mod_python 方式报 TypeError 的解决方法">阅读全文——共1361字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>尝试用 Django 写了一个简单的企业站，部署过程中出了不少问题，不过还好都一一解决了。</p>
<p>“激动不已”的看到了首页，长出一口气：“总不会再出什么其他问题了吧！”随手点了一个内部链接：<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<h1>TypeError at about</h1>
<pre class="exception_value">'NoneType' object is not iterable</pre>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
这回可晕倒了，怎么会有这样一个错误？<br />
接着往下看，发现错误是由于丢失了一个反斜线引起的：</p>
<table class="meta" border="0">
<tbody>
<tr>
<th>Request Method:</th>
<td>GET</td>
</tr>
<tr>
<th>Request URL:</th>
<td>http://www.i-fang.comabout</td>
</tr>
<tr>
<th>Exception Type:</th>
<td>TypeError</td>
</tr>
<tr>
<th>Exception Value:</th>
<td>
<pre>'NoneType' object is not iterable</pre>
</td>
</tr>
<tr>
<th>Exception Location:</th>
<td>/usr/lib/python2.5/site-packages/django/core/handlers/base.py in get_response, line 77</td>
</tr>
<tr>
<th>Python Executable:</th>
<td>/usr/bin/python</td>
</tr>
<tr>
<th>Python Version:</th>
<td>2.5.2</td>
</tr>
<tr>
<th>Python Path:</th>
<td>['/home/www/www.i-fang.com/', '/usr/lib/python25.zip', '/usr/lib/python2.5', '/usr/lib/python2.5/plat-linux2', '/usr/lib/python2.5/lib-tk', '/usr/lib/python2.5/lib-dynload', '/usr/local/lib/python2.5/site-packages', '/usr/lib/python2.5/site-packages', '/var/lib/python-support/python2.5']</td>
</tr>
<tr>
<th>Server time:</th>
<td>Tue, 5 May 2009 10:25:17 +0800</td>
</tr>
</tbody>
</table>
<p>在 Request URL 中域名和路径中间的反斜线丢失了。<br />
是 urls.py 配置的问题？检查、试验……不是！<br />
是 apache 配置的问题？检查、试验……还不是！<br />
所有的内容跟 Django 手册上的完全一致，到底是怎么回事呢？<br />
看到<a href="http://groups.google.com/group/django-users/browse_thread/thread/de96ca3a50c372f3" target="_blank">这个</a>邮件列表的内容，才明白，有时手册是不能随便抄的：<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
Take a look at the django.root PythonOption described here:</p>
<p><a rel="nofollow" href="http://docs.djangoproject.com/en/dev/howto/deployment/modpython/?from=olddocs#basic-configuration" target="_blank">http://docs.djangoproject.com/en/dev/howto/deployment/modpython/?from&#8230;</a></p>
<p>Are you using that option? If, yes, remove that option entirely and<br />
try again.<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<p><span style="color: #ff0000;"><strong>去掉了 apache 配置文件中的“PythonOption django.root /”，重启 apache 后，一切访问正常了。</strong></span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2009/05/django-102-mod_python-%e6%96%b9%e5%bc%8f%e6%8a%a5-typeerror-%e7%9a%84%e8%a7%a3%e5%86%b3%e6%96%b9%e6%b3%95/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TurboGears 学习笔记－控制器</title>
		<link>http://www.mikespook.com/2008/08/turbogears-%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0%ef%bc%8d%e6%8e%a7%e5%88%b6%e5%99%a8/</link>
		<comments>http://www.mikespook.com/2008/08/turbogears-%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0%ef%bc%8d%e6%8e%a7%e5%88%b6%e5%99%a8/#comments</comments>
		<pubDate>Tue, 26 Aug 2008 10:28:09 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[TurobGears]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=177</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
这部分内容虽然不多，但是看了好多天。

在 tgadmin 自动生成的 appname/appname/controller.py 中有如下代码：

1 import turbogears as tg 

<span class="readmore"><a href="http://www.mikespook.com/2008/08/turbogears-%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0%ef%bc%8d%e6%8e%a7%e5%88%b6%e5%99%a8/" title="TurboGears 学习笔记－控制器">阅读全文——共1303字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>这部分内容虽然不多，但是看了好多天。</p>
<p>在 tgadmin 自动生成的 appname/appname/controller.py 中有如下代码：</p>
<div style="background:#000000;color:#cccccc"><span style="font-family: monospace;"><span style="color: #ffff00;">1 </span><span style="color: #ff80ff;">import</span> turbogears <span style="color: #ff80ff;">as</span> tg<span style="color: #ffff00;"> </span></span></div>
<div style="background:#000000;color:#cccccc"><span style="color: #ffff00;">2 </span><span style="color: #ff80ff;">from</span> turbogears <span style="color: #ff80ff;">import</span> controllers, expose, flash</div>
<div style="background:#000000;color:#cccccc"><span style="color: #ffff00;">3 </span><span style="color: #ff80ff;">from</span> cherrypy <span style="color: #ff80ff;">import</span> request</div>
<div style="background:#000000;color:#cccccc"><span style="color: #ffff00;">4 </span><span style="color: #80a0ff;"># from sandbox import model</span></div>
<div style="background:#000000;color:#cccccc"><span style="color: #ffff00;">5 </span><span style="color: #80a0ff;"># import logging</span></div>
<div style="background:#000000;color:#cccccc"><span style="color: #ffff00;">6 </span><span style="color: #80a0ff;"># log = logging.getLogger(“sandbox.controllers”)</span></div>
<div style="background:#000000;color:#cccccc"><span style="color: #ffff00;">7</span></div>
<div style="background:#000000;color:#cccccc"><span style="color: #ffff00;">8 </span><span style="color: #ffff00;">class</span> <span style="color: #40ffff;">Root</span>(controllers.RootController):</div>
<div style="background:#000000;color:#cccccc"><span style="color: #ffff00;">9 </span> <span style="color: #ff80ff;">@</span><span style="color: #40ffff;">expose</span>(template=<span style="background-color: #000000"><span style="color: #cccccc;">“</span></span><span style="color: #ffa0a0;">sandbox.templates.welcome</span><span style="background-color: #000000"><span style="color: #cccccc;">“</span></span>)</div>
<div style="background:#000000;color:#cccccc"><span style="color: #ffff00;">10 </span> <span style="color: #ffff00;">def</span> <span style="color: #40ffff;">index</span>(self):</div>
<div style="background:#000000;color:#cccccc"><span style="color: #ffff00;">11 </span> <span style="color: #ff80ff;">import</span> time</div>
<div style="background:#000000;color:#cccccc"><span style="color: #ffff00;">12 </span> <span style="color: #80a0ff;"># log.debug(“Happy TurboGears Controller Responding For Duty”)</span></div>
<div style="background:#000000;color:#cccccc"><span style="color: #ffff00;">13 </span> flash(<span style="background-color: #000000"><span style="color: #cccccc;">“</span></span><span style="color: #ffa0a0;">Your application is now running</span><span style="background-color: #000000"><span style="color: #cccccc;">“</span></span>)</div>
<div style="background:#000000;color:#cccccc"><span style="color: #ffff00;">14 </span> <span style="color: #ffff00;">return</span> dict(now=time.ctime())</div>
<p>Root.index 是 http://127.0.0.1:8080/index 的控制器。如果需要添加新的“页面”，只要在 Root 中添加新的方法即可：</p>
<div style="background:#000000;color:#cccccc"><span style="color: #ffff00;">16 </span> <span style="color: #ff80ff;">@</span><span style="color: #40ffff;">expose</span>()<br />
<span style="color: #ffff00;">17 </span> <span style="color: #ffff00;">def</span> <span style="color: #40ffff;">foobar</span>(self):<br />
<span style="color: #ffff00;">18 </span> <span style="color: #ffff00;">return</span> <span style="background-color: #000000"><span style="color: #cccccc;">&#8216;</span></span><span style="color: #ffa0a0;">Just another page</span><span style="background-color: #000000"><span style="color: #cccccc;">&#8216;</span></span></div>
<p>@expose() 是一定要加的，不加这个，无法定位控制器的方法。</p>
<p>我这里没有添加 template，所以框架将返回的内容直接作为字符串输出在页面上了。</p>
<p>如果想使用模板（默认为Kid），则指定模板名称 template=”appname.appname.foobar”，这个对应的是项目根开始的目录和模板文件名的结构。实际模板应保存在 appname/appname/foobar.kid 文件中。</p>
<p>控制器通过 expose 出来的方法的参数获得浏览器传递过来的变量，对上面的方法稍加改造即可：</p>
<div style="background:#000000;color:#cccccc"><span style="color: #ffff00;">16 </span> <span style="color: #ff80ff;">@</span><span style="color: #40ffff;">expose</span>()<br />
<span style="color: #ffff00;">17 </span> <span style="color: #ffff00;">def</span> <span style="color: #40ffff;">foobar</span>(self, *args, **kv):<br />
<span style="color: #ffff00;">18 </span> <span style="color: #ffff00;">if</span> (kv.has_key(<span style="background-color: #000000"><span style="color: #cccccc;">&#8216;</span></span><span style="color: #ffa0a0;">name</span><span style="background-color: #000000"><span style="color: #cccccc;">&#8216;</span></span>)):<br />
<span style="color: #ffff00;">19 </span> name = kv[<span style="background-color: #000000"><span style="color: #cccccc;">'</span></span><span style="color: #ffa0a0;">name</span><span style="background-color: #000000"><span style="color: #cccccc;">'</span></span>]<br />
<span style="color: #ffff00;">20 </span> <span style="color: #ffff00;">else</span>:<br />
<span style="color: #ffff00;">21 </span> name = <span style="background-color: #000000"><span style="color: #cccccc;">&#8216;</span></span><span style="color: #ffa0a0;">unknown</span><span style="background-color: #000000"><span style="color: #cccccc;">&#8216;</span></span><br />
<span style="color: #ffff00;">22 </span> <span style="color: #ffff00;">return</span> <span style="background-color: #000000"><span style="color: #cccccc;">&#8216;</span></span><span style="color: #ffa0a0;">Your name is </span><span style="background-color: #000000"><span style="color: #cccccc;">&#8216;</span></span> + name</div>
<p>当访问 http://127.0.0.1:8080/foobar 时，页面显示 Your name is unknown。当访问 http://127.0.0.1:8080/foobar?name=fool 时，页面显示 Your name is fool。</p>
<p>当提供一个名为 default 的方法 expose 时，所有未定义的访问都会转到这个方法上去。这时，args 参数包含了访问所用的名字。例如访问 http://127.0.0.1:8080/barfoo，而 barfoo 未定义，那么default 方法被调用。同时 args[0] = “barfoo“。</p>
<p>好像控制器最基本的使用就这么些东西了，有点类似 Zend Framework 的 Controller-&gt;Action 的形式，仅仅是形式。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2008/08/turbogears-%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0%ef%bc%8d%e6%8e%a7%e5%88%b6%e5%99%a8/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TurboGears 学习笔记－安装</title>
		<link>http://www.mikespook.com/2008/08/turbogears-%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0%ef%bc%8d%e5%ae%89%e8%a3%85/</link>
		<comments>http://www.mikespook.com/2008/08/turbogears-%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0%ef%bc%8d%e5%ae%89%e8%a3%85/#comments</comments>
		<pubDate>Tue, 19 Aug 2008 15:15:31 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[TurboGears]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=173</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
Zope3 很强大，但是一直徘徊在 python 2.4 不升级。学习的兴趣骤减。Django 版本一直停滞在 0.96 不前。据说 12 月会出 1.0，但是兼容性似乎不能保障。偶然的机会看到一篇 TurboGears 的文章，似乎停简单。于是乎，先学安装。

TurboGears 的安装已经简单到了及至：easy_install turbogears。

在 ubuntu 下使用 easy_install 这个命令需要安装 python-setuptools 这个包。

<span class="readmore"><a href="http://www.mikespook.com/2008/08/turbogears-%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0%ef%bc%8d%e5%ae%89%e8%a3%85/" title="TurboGears 学习笔记－安装">阅读全文——共655字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>Zope3 很强大，但是一直徘徊在 python 2.4 不升级。学习的兴趣骤减。Django 版本一直停滞在 0.96 不前。据说 12 月会出 1.0，但是兼容性似乎不能保障。偶然的机会看到一篇 TurboGears 的文章，似乎停简单。于是乎，先学安装。</p>
<p>TurboGears 的安装已经简单到了及至：easy_install turbogears。</p>
<p>在 ubuntu 下使用 easy_install 这个命令需要安装 python-setuptools 这个包。</p>
<p>在终端中执行 tg-admin 这个命令，一切正常，除了会有一个警告：UserWarning: Your installation of Python doesn&#8217;t have a profile module.原来还需要安装 python-profiler 这个包，使用 apt-get 安装之后，这个警告就不会再出现了。</p>
<p>使用 tg-admin quickstart 生成一个项目，比如 sandbox。进入自动生成的 sandbox 目录，执行命令 ./start-sandbox.py 一切 OK，只要 8080 端口没有被占用，TurboGears 就正常启动了。</p>
<p>在开发模式下，如果需要修改 8080 端口，找到 dev.cfg 文件中的 #server.socket_port=8080 行，去掉注释，修改端口，重新执行 ./start-sandbox.py 即可。</p>
<p>TurboGears 所具有的美感是 zend framework 所不具备的。连安装和部署都这么简单。希望 zend framework 能够取其之长，至少提供几个实用的自动化工具也好啊。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2008/08/turbogears-%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0%ef%bc%8d%e5%ae%89%e8%a3%85/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

