<?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>apt-blog.net    IT民工养成计划    PT博客 &#187; Python</title>
	<atom:link href="http://apt-blog.net/category/tech/programming/python-programming-tech/feed" rel="self" type="application/rss+xml" />
	<link>http://apt-blog.net</link>
	<description>以我多年种田的经验来看，什么都略懂一点，生活才多彩一点……猪葛亮说的……</description>
	<lastBuildDate>Mon, 23 Aug 2010 10:45:28 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>An improved Vimpress</title>
		<link>http://apt-blog.net/an-improved-vimpress</link>
		<comments>http://apt-blog.net/an-improved-vimpress#comments</comments>
		<pubDate>Sat, 03 Jul 2010 11:14:34 +0000</pubDate>
		<dc:creator>PT</dc:creator>
				<category><![CDATA[Blogger Tech]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Vim]]></category>
		<category><![CDATA[vimpress]]></category>
		<guid isPermaLink="false">http://apt-blog.net/an-improved-vimpress</guid>
		<description><![CDATA[Vim has been so graceful that I like to have any text editing work done in her, especially coding and blogging. As a Firefox user, I previously use scribefire to write to my wordpress blog, actually I was working with HTML, I don't like WYSIWYG editing, which always creates dirty codes. However, scribefire is not [...]]]></description>
			<content:encoded><![CDATA[<p>Vim has been so graceful that I like to have any text editing work done in her, especially coding and blogging. </p>
<p>As a Firefox user, I previously use <a href="http://www.scribefire.com/">scribefire</a> to write to my wordpress blog, actually I was working with HTML, I don't like WYSIWYG editing, which always creates dirty codes. However, scribefire is not good enought to me, many of my blogs includes codes, they needed to be surround by <code>&lt;pre&gt;</code> element to be syntax high-lighted, scribefire doesn't always works that well with code symbols, sometimes got translated into codes that hardly understand, I have to login into my blog to correct those lines.</p>
<p>What's more importantly, scribefire isn't good enough as an editor.</p>
<p>Then I found <a href="http://www.vim.org/scripts/script.php?script_id=1953">vimpress</a>, and sadly found it was totaly broken, which haven't updated since 2007-07-13. Vimpress's official page is also <a href="http://www.friggeri.net/projets/vimblog/">broken</a>. I googled for it, found that vimpress was noticed by many other blogger, they improved it, and hosting working versions of vimpress in their own repository. </p>
<p>The version by pigeond, <a href="http://pigeond.net/blog/2009/05/07/vimpress-again/">he posted a blog about his improvement</a>, this's first working version I got.</p>
<p>Then the other post by Erik, he had vimpress in another git repository, and <a href="http://www.ralree.com/2009/02/28/test-post-using-vimpress/">discussed about his using about vimpress</a>, what's worth another mention here is, vimpress is communicating to your wordpress blog through the XML-RPC protocol, unless your blog uses HTTPS(then use https url in vimpress), or you password would be transfered in plain text.</p>
<p>When I found vimpress is written in Python, I feel eager to improve it. Firstly it didn't support for wp-slug, which is called as 'postname' in wordpress, this is supposed to be a url friendly pattern, won't hurt if people write posts in English, wordpress simply translate spaces in Title into hyphen. But in other language like Chinese, such transfer would be ugly, I often use an English postname for every post.</p>
<p>And then the image upload. Scribefire allow me to upload images to blog and insert the img element to the edit. I added this feature to vimpress.</p>
<p>I added more friendly notifications for exceptions proccess, and many functions rewritten.</p>
<p>Now vimpress is mature enough to fulfill my blogging works. I firstly write my blog in <a href="http://daringfireball.net/projects/markdown/">Markdown</a>, save as a temp file, then open a new buffer, type :BlogNew, then :r !markdown my_tmp.mkd , my post will translate into pretty HTML. Then fillin those title meta data, then :BlogSave publish, done. Pretty, Simple.</p>
<p>I still think I should have markdown ingranted to support with Markdown. </p>
<p>My version of vimpress could be retrived in my google code repository:</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">svn</span> checkout http:<span style="color: #000000; font-weight: bold;">//</span>ptcoding.googlecode.com<span style="color: #000000; font-weight: bold;">/</span>svn<span style="color: #000000; font-weight: bold;">/</span>trunk<span style="color: #000000; font-weight: bold;">/</span>vimpress</pre></td></tr></table></div>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/an-improved-vimpress/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>PDB远程调试Python多进程子程序</title>
		<link>http://apt-blog.net/rm_pdb_module_for_debugging_multiprocessing</link>
		<comments>http://apt-blog.net/rm_pdb_module_for_debugging_multiprocessing#comments</comments>
		<pubDate>Sun, 14 Mar 2010 13:30:48 +0000</pubDate>
		<dc:creator>PT</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[debug]]></category>
		<category><![CDATA[pdb]]></category>
		<category><![CDATA[process]]></category>
		<category><![CDATA[多进程]]></category>
		<category><![CDATA[远程调试]]></category>
		<guid isPermaLink="false">http://apt-blog.net/rm_pdb_module_for_debugging_multiprocessing</guid>
		<description><![CDATA[此前文章《最简单方法远程调试Python多进程子程序》利用了Unix管道文件以及简单的bash来配合调试多进程子程序，但也因此没法跨平台支持windows下的子进程调试，这次简单使用socket接口写了个模块，利用类文件对象传给Pdb的构造，因此不仅可以跨平台，甚至跨机器，跨网络调试都没问题（通常不会这么BT的）。 使用方法，用回之前的例子： 先在终端运行调试服务端： python -c &#34;import rm_pdb; rm_pdb.server()&#34; 在另外的终端运行这个文件： multiproces_debug.py 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #!/usr/bin/python &#160; import multiprocessing import pdb import rm_pdb &#160; def child_process&#40;&#41;: print &#34;Child-Process&#34; rm_pdb.pdb&#40;&#41;.set_trace&#40;&#41; var = &#34;debug me!&#34; &#160; def main_process&#40;&#41;: print &#34;Parent-Process&#34; p [...]]]></description>
			<content:encoded><![CDATA[<p>此前文章<a href="http://apt-blog.net/debugging_python_multiprocessing">《最简单方法远程调试Python多进程子程序》</a>利用了Unix管道文件以及简单的bash来配合调试多进程子程序，但也因此没法跨平台支持windows下的子进程调试，这次简单使用socket接口写了个模块，利用类文件对象传给Pdb的构造，因此不仅可以跨平台，甚至跨机器，跨网络调试都没问题（通常不会这么BT的）。</p>
<p>使用方法，用回之前的例子：</p>
<p>先在终端运行调试服务端：</p>
<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">python <span style="color: #660033;">-c</span> <span style="color: #ff0000;">&quot;import rm_pdb; rm_pdb.server()&quot;</span></pre></div></div>
<p>在另外的终端运行这个文件：<br />
<strong>multiproces_debug.py</strong></p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/usr/bin/python</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> multiprocessing
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">pdb</span>
<span style="color: #ff7700;font-weight:bold;">import</span> rm_pdb
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> child_process<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;Child-Process&quot;</span>
    rm_pdb.<span style="color: #dc143c;">pdb</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">set_trace</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    var = <span style="color: #483d8b;">&quot;debug me!&quot;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> main_process<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;Parent-Process&quot;</span>
    p = multiprocessing.<span style="color: black;">Process</span><span style="color: black;">&#40;</span>target = child_process<span style="color: black;">&#41;</span>
    p.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">pdb</span>.<span style="color: black;">set_trace</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    var = <span style="color: #483d8b;">&quot;debug me!&quot;</span>
    p.<span style="color: black;">join</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">&quot;__main__&quot;</span>:
    main_process<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<p>完成，即可同时调试父子进程。另外pdb()和server()是可以接受addr、port参数的，默认用了addr='localhost', port=18964地址端口来通讯，如果需要同时调试N多个子进程，或者跨网络调试，需要使调试端跟服务端的参数一致。</p>
<p>模块代码放到Google Code：<br />
<a href="http://code.google.com/p/ptcoding/source/browse/trunk/rm_pdb/rm_pdb.py" target="_blank">http://code.google.com/p/ptcoding/source/browse/trunk/rm_pdb/rm_pdb.py</a></p>
<p>可直接下载文件放到需要调试的代码同一目录使用，或者使用easy_install直接安装到python模块内（需要svn支持）：</p>
<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">easy_install http:<span style="color: #000000; font-weight: bold;">//</span>ptcoding.googlecode.com<span style="color: #000000; font-weight: bold;">/</span>svn<span style="color: #000000; font-weight: bold;">/</span>trunk<span style="color: #000000; font-weight: bold;">/</span>rm_pdb</pre></div></div>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/rm_pdb_module_for_debugging_multiprocessing/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>最简单方法远程调试Python多进程子程序</title>
		<link>http://apt-blog.net/debugging_python_multiprocessing</link>
		<comments>http://apt-blog.net/debugging_python_multiprocessing#comments</comments>
		<pubDate>Fri, 05 Mar 2010 15:00:12 +0000</pubDate>
		<dc:creator>PT</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[debug]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[pdb]]></category>
		<category><![CDATA[pipe]]></category>
		<category><![CDATA[process]]></category>
		<category><![CDATA[多进程]]></category>
		<category><![CDATA[远程调试]]></category>
		<guid isPermaLink="false">http://apt-blog.net/debugging_python_multiprocessing</guid>
		<description><![CDATA[Python 2.6新增的multiprocessing，即多进程，给子进程代码调试有点困难，比如python自带的pdb如果直接在子进程代码里面启动会抛出一堆异常，原因是子进程的stdin/out/err等文件都已关闭，pdb无法调用。据闻winpdb、Wing IDE的调试器能够支持这样的远程调试，但似乎过于重量级（好吧前者比后者要轻多了，但一样要wxPython的环境，再说pdb的灵活可靠它们难以比拟）。 其实只需稍作改动即可用pdb继续调试子进程的代码，思路来自这个博客：子进程的stdin/out/err关闭了，那可以自己重新按/dev/stdout的名称打开来用。当然这指*nix下，win下要麻烦一些，后面再说。 pdb支持自定义输出输入的文件，我再稍作改动，使用fifo管道(Named Pipe)来完成pdb的输出输入的重定向，这样的好处是，可以同时对父子进程调试！ multiproces_debug.py 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #!/usr/bin/python &#160; import multiprocessing import pdb &#160; def child_process&#40;&#41;: print &#34;Child-Process&#34; pdb.Pdb&#40;stdin=open&#40;'p_in', 'r+'&#41;, stdout=open&#40;'p_out', 'w+'&#41;&#41;.set_trace&#40;&#41; var = &#34;debug me!&#34; &#160; def main_process&#40;&#41;: print &#34;Parent-Process&#34; p = multiprocessing.Process&#40;target = [...]]]></description>
			<content:encoded><![CDATA[<p>Python 2.6新增的multiprocessing，即多进程，给子进程代码调试有点困难，比如python自带的pdb如果直接在子进程代码里面启动会抛出一堆异常，原因是子进程的stdin/out/err等文件都已关闭，pdb无法调用。据闻winpdb、Wing IDE的调试器能够支持这样的远程调试，但似乎过于重量级（好吧前者比后者要轻多了，但一样要wxPython的环境，再说pdb的灵活可靠它们难以比拟）。</p>
<p>其实只需稍作改动即可用pdb继续调试子进程的代码，思路来自<a href="http://almirkaric.com/2010/01/07/debugging-python-multiprocessing/" target="_blank">这个博客</a>：子进程的stdin/out/err关闭了，那可以自己重新按/dev/stdout的名称打开来用。当然这指*nix下，win下要麻烦一些，后面再说。</p>
<p>pdb支持自定义输出输入的文件，我再稍作改动，使用fifo管道(Named Pipe)来完成pdb的输出输入的重定向，这样的好处是，可以同时对父子进程调试！</p>
<p><span style="font-weight: bold;">multiproces_debug.py</span></p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/usr/bin/python</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> multiprocessing
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">pdb</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> child_process<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;Child-Process&quot;</span>
    <span style="color: #dc143c;">pdb</span>.<span style="color: black;">Pdb</span><span style="color: black;">&#40;</span>stdin=<span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'p_in'</span>, <span style="color: #483d8b;">'r+'</span><span style="color: black;">&#41;</span>, stdout=<span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'p_out'</span>, <span style="color: #483d8b;">'w+'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>.<span style="color: black;">set_trace</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    var = <span style="color: #483d8b;">&quot;debug me!&quot;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> main_process<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;Parent-Process&quot;</span>
    p = multiprocessing.<span style="color: black;">Process</span><span style="color: black;">&#40;</span>target = child_process<span style="color: black;">&#41;</span>
    p.<span style="color: black;">start</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">pdb</span>.<span style="color: black;">set_trace</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    var = <span style="color: #483d8b;">&quot;debug me!&quot;</span>
    p.<span style="color: black;">join</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">&quot;__main__&quot;</span>:
    main_process<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<p>只需要给pdb的构造参数传入stdin/stdout的文件对象，调试过程的输出输入就自然以传入的文件为方向了。这里需要两个管道文件p_in、p_out，运行脚本之前，使用命令<span style="font-weight: bold;">mkfifo p_in p_out</span>同时建立。这还未完成，还需要个外部程序来跟管道交互：</p>
<p><span style="font-weight: bold;">debug_cmd.sh</span></p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
</pre></td><td class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">#!/bin/bash</span>
&nbsp;
<span style="color: #c20cb9; font-weight: bold;">cat</span> p_out <span style="color: #000000; font-weight: bold;">&amp;</span>
<span style="color: #000000; font-weight: bold;">while</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #7a0874; font-weight: bold;">&#91;</span> <span style="color: #000000;">1</span> <span style="color: #7a0874; font-weight: bold;">&#93;</span><span style="color: #7a0874; font-weight: bold;">&#93;</span>; <span style="color: #000000; font-weight: bold;">do</span>
    <span style="color: #c20cb9; font-weight: bold;">read</span> <span style="color: #660033;">-e</span> cmd
    <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #007800;">$cmd</span><span style="color: #000000; font-weight: bold;">&gt;</span>p_in
<span style="color: #000000; font-weight: bold;">done</span></pre></td></tr></table></div>
<p>很简单的bash。因为fifo管道在写入端未传入数据时，读取端是阻塞的（反之亦然），所以cat的显示挂在后台，当调试的程序结束后，管道传出EOF，cat就自动退出了。</p>
<p>实验开始：先在一个终端运行debug_cmd.sh（其实顺序无关），其光标停在新的一行，再在另外一个终端运行multiproces_debug.py，可见到两个终端同时出现了(Pdb)的指示符，可以同时对父子进程调试了！</p>
<p style="text-align: center;"><img class="aligncenter" style="max-width: 800px;" src="http://apt-blog.net/wp-content/uploads/2010/03/python-remote-debug.png" alt="" width="406" height="240" /></p>
<p>在Windows下使用管道就没这么方便了，因为没有实体的管道文件支持，可以考虑使用socket的类文件对象传给pdb。但这样要写的python代码就多一点，以及要另外用做个交互程序；不过依然用不了多少代码，可以写成一个模块专门用做远程调试，import即用。暂未实现，以后有空弄好放代码出来。</p>
<p>Update: 专用调试模块见<a href="http://apt-blog.net/rm_pdb_module_for_debugging_multiprocessing">《PDB远程调试Python多进程子程序》</a></p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/debugging_python_multiprocessing/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Python, C-Python, Cython代码与GIL的交互</title>
		<link>http://apt-blog.net/python_cpython_cython_and_gil</link>
		<comments>http://apt-blog.net/python_cpython_cython_and_gil#comments</comments>
		<pubDate>Wed, 06 Jan 2010 17:08:47 +0000</pubDate>
		<dc:creator>PT</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[CPython]]></category>
		<category><![CDATA[Cython]]></category>
		<category><![CDATA[GIL]]></category>
		<category><![CDATA[Global Interpreter Lock]]></category>
		<category><![CDATA[mutithreading]]></category>
		<category><![CDATA[thread]]></category>
		<guid isPermaLink="false">http://apt-blog.net/python_cpython_cython_and_gil</guid>
		<description><![CDATA[这篇笔记相对Python来说，有点底层，先来解释几个名词： C-Python: 或者CPython，指C实现的Python虚拟机的基础API。最通用的Python就是是基于C实现的，它的底层API称为C-Python API，所有Python代码的最终变成这些API以及数据结构的调用，才有了Python世界的精彩； Cython：准确说Cython是单独的一门语言，专门用来写在Python里面import用的扩展库。实际上Cython的语法基本上跟Python一致，而Cython有专门的“编译器”先将Cython代码转变成C（自动加入了一大堆的C-Python API），然后使用C编译器编译出最终的Python可调用的模块。 GIL：Global Interpreter Lock，是Python虚拟机的多线程机制的核心机制，翻译为：全局解释器锁。其实Python线程是操作系统级别的线程，在不同平台有不同的底层实现（如win下就用win32_thread, posix下就用pthread等），Python解释器为了使所有对象的操作是线程安全的，使用了一个全局锁（GIL）来同步所有的线程，所以造成“一个时刻只有一个Python线程运行”的伪线程假象。GIL是个颗粒度很大的锁，它的实现跟性能问题多年来也引起过争议，但到今天它还是经受起了考验，即使它让Python在多核平台下CPU得不到最大发挥。 GIL的作用很简单，任何一个线程除非获得锁，否则都在睡眠，而如果获得锁的线程一刻不释放锁，别的线程就永远睡眠下去。对于纯Python线程，这个问题不大，Python代码会通过解释器实时转换成微指令，而解释器给他们算着，每个线程执行了一定的指令数后就要把机会让给别的线程。这个过程中操作系统的调度作用比较微妙，不管操作系统怎么调度，即使把有锁线程挂起到后台，尝试唤醒没锁的，解释器也不给他任何执行机会，所以Python对象很安全。 所以一般来说，做纯Python的编程不需要考虑到GIL，它们是不同层面的东西，但是模块级别的C-Python、Cython等C层面的代码，跟Python虚拟机是平起平坐的，所以GIL很可能需要考虑，特别那些代码涉及IO阻塞、长时间运算、休眠等情况的时候（否则整个Python都在等这个耗时操作的返回，因为他们没获得锁，急也没办法）。 想体现这个过程，很简单，考虑下面的代码，一段纯Python和一段纯C的循环，每次print一段文字就睡眠一秒。 1 2 3 4 5 6 7 void _c_loop &#40; void &#41; &#123; while&#40;1&#41; &#123; printf&#40;&#34;Print from C loop\n&#34;&#41;; sleep&#40;1&#41;; &#125; &#125; 1 2 3 4 def _py_loop&#40;&#41;: while True: print &#34;Print from Python loop&#34; time.sleep&#40;1&#41; 先不管他们是如何揉合到同一Python进程里面，两个进程分别执行了这两个函数后，他们应该以大概相互间隔着输出文字；但实际情况是，Print from Python loop这句出现了一次之后（先启动了纯Python线程，否则它连启动的机会都没），剩下的输出全都是Print from C [...]]]></description>
			<content:encoded><![CDATA[<p>这篇笔记相对Python来说，有点底层，先来解释几个名词：</p>
<p>C-Python: 或者CPython，指<a target="_blank" href="http://docs.python.org/dev/c-api/">C实现的Python虚拟机的基础API</a>。最通用的Python就是是基于C实现的，它的底层API称为C-Python API，所有Python代码的最终变成这些API以及数据结构的调用，才有了Python世界的精彩；</p>
<p>Cython：<a target="_blank" href="http://www.cython.org/">准确说Cython是单独的一门语言</a>，专门用来写在Python里面import用的扩展库。实际上Cython的语法基本上跟Python一致，而Cython有专门的“编译器”先将Cython代码转变成C（自动加入了一大堆的C-Python API），然后使用C编译器编译出最终的Python可调用的模块。</p>
<p>GIL：Global Interpreter Lock，是Python虚拟机的多线程机制的核心机制，翻译为：全局解释器锁。其实Python线程是操作系统级别的线程，在不同平台有不同的底层实现（如win下就用win32_thread, posix下就用pthread等），Python解释器为了使所有对象的操作是线程安全的，使用了一个全局锁（GIL）来同步所有的线程，所以造成“一个时刻只有一个Python线程运行”的伪线程假象。GIL是个颗粒度很大的锁，它的实现跟性能问题多年来也引起过争议，但到今天它还是经受起了考验，即使它让Python在多核平台下CPU得不到最大发挥。</p>
<p>GIL的作用很简单，任何一个线程除非获得锁，否则都在睡眠，而如果获得锁的线程一刻不释放锁，别的线程就永远睡眠下去。对于纯Python线程，这个问题不大，Python代码会通过解释器实时转换成微指令，而解释器给他们算着，每个线程执行了一定的指令数后就要把机会让给别的线程。这个过程中操作系统的调度作用比较微妙，不管操作系统怎么调度，即使把有锁线程挂起到后台，尝试唤醒没锁的，解释器也不给他任何执行机会，所以Python对象很安全。</p>
<p>所以一般来说，做纯Python的编程不需要考虑到GIL，它们是不同层面的东西，但是模块级别的C-Python、Cython等C层面的代码，跟Python虚拟机是平起平坐的，所以GIL很可能需要考虑，特别那些代码涉及IO阻塞、长时间运算、休眠等情况的时候（否则整个Python都在等这个耗时操作的返回，因为他们没获得锁，急也没办法）。</p>
<p>想体现这个过程，很简单，考虑下面的代码，一段纯Python和一段纯C的循环，每次print一段文字就睡眠一秒。</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">void</span> _c_loop <span style="color: #009900;">&#40;</span> <span style="color: #993333;">void</span> <span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">while</span><span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000066;">printf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;Print from C loop<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        sleep<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> _py_loop<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">True</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;Print from Python loop&quot;</span>
        <span style="color: #dc143c;">time</span>.<span style="color: black;">sleep</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<p>先不管他们是如何揉合到同一Python进程里面，两个进程分别执行了这两个函数后，他们应该以大概相互间隔着输出文字；但实际情况是，Print from Python loop这句出现了一次之后（先启动了纯Python线程，否则它连启动的机会都没），剩下的输出全都是Print from C loop，不断的输出，按Ctrl + C都没反应（因为响应信号的只有住线程，终止信号是发出了，说不定主线程也收到了，但是解释器不给机会），只好从另外的控制台kill了整个python进程。</p>
<p>显然问题在于，运行C那段程序的进程获取了GIL，但是这是个死循环是永远都出不去的，所以GIL永远都在这个进程手里，别的进程只能睡觉。考虑到这个C程序的情况，因为循环里面只有printf跟sleep两个操作系统的调用，完全威胁不到Python对象的安全，所以GIL完全没必要插手进来。解决办法有两种：</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">void</span> _c_loop_1 <span style="color: #009900;">&#40;</span> <span style="color: #993333;">void</span> <span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    Py_BEGIN_ALLOW_THREADS
    <span style="color: #b1b100;">while</span><span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000066;">printf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;Print from C loop<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        sleep<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    Py_END_ALLOW_THREADS
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #993333;">void</span> _c_loop_2 <span style="color: #009900;">&#40;</span> <span style="color: #993333;">void</span> <span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">while</span><span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        Py_BEGIN_ALLOW_THREADS
        <span style="color: #000066;">printf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;Print from C loop<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        sleep<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        Py_END_ALLOW_THREADS
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>
<p><a target="_blank" href="http://docs.python.org/dev/c-api/init.html#Py_BEGIN_ALLOW_THREADS">Py_BEGIN_ALLOW_THREADS、Py_END_ALLOW_THREADS</a>是定义在Python的C-API头文件里面的宏，需要给头文件包含上#include
<python2.x python.h="">。</p>
<p>两个解决办法看起来差不多，效果也差不多，但意义不大一样。</p>
<p>_c_loop_1是在进入永远都不会停的循环之前，把GIL交出去（还有保存自己的线程状态，但现在的例子这个没有意义），然后自己继续运行，这时候_c_loop_1是跟python虚拟机是互不相关的线程了，井水不犯河水，解释器的GIL爱给谁给谁。这时候“Python只能有一个线程运行”的传说就被破除迷信了。</p>
<p>_c_loop_2则是每次进行printf/sleep的调用前交出GIL，之后又申请要回来，然后循环回去马上又交出去（=。=）……</p>
<p>这个例子来说，当然是前者有效率了（=。=反正都是无用功，何来效率），只是为了在复杂的情况（C代码会影响Python对象，调用Python的函数等）下灵活运用。<a target="_blank" href="http://docs.python.org/dev/c-api/init.html#Py_BEGIN_ALLOW_THREADS">Py_BEGIN_ALLOW_THREADS、Py_END_ALLOW_THREADS</a>其实只是两句语句的宏包含，Python手册里面有详细说明，甚至直接打开Python的include就可以看到。其中BEGIN那句调用了PyEval_SaveThread()，而打开Python源码，PyEval_SaveThread函数里面毅然有PyThread_release_lock()的调用。</p>
<p>上面这些，在<a target="_blank" href="http://www.douban.com/subject/3117898/">《Python源码剖析——深度探索动态语言核心技术》</a>一书，<a target="_blank" href="http://book.51cto.com/art/200807/82538.htm">第15章Python多线程机制 15.4.2阻塞调度</a>一节讲解得比较详细，书里面是直接拿出time.sleep/raw_input这些Python本身的代码来举例和分析。当然咯，本文加入了很多PT自己的发散性理解。</p>
<p>Cython在旁边说，怎么还没到我出场。恩，马上就来。</p>
<p>Cython其实是很纠结的东西，因为他用的是Python一样的语法，但经常不得不时时刻刻提醒自己，有时候是在写C，有时候在写Python（=。=）。C是强类型的，Python是弱类型的，所以写Cython的时候有时候是强类型的，有时候是弱类型的，一般变量还好，你可能会想一下类型，像我刚入门，经常把漏写参数类型，不过还好Cython的编译器是有提示的，当熟悉了规则后，用Python写C啊……通过Cython，不仅可以在Python调用C，还可以在C调用Python……Python的上天入地就靠他了。</p>
<p>Cython代码最终都是会编译成C的，上面例子那段py_loop，改个名字叫cy_loop吧，直接放入Cython里面编译即可，他完全会变成C，然后变得像C一样霸道，执行后拿了GIL永远不还回去。</p>
<p>显然，只有纯Python的代码需要在执行时候经过解释器解释的，才会“自动的”交回GIL，而Cython是编译成C的代码，执行不经过解释器，如果他不交回GIL，就拿它没办法的了。</p>
<p>Cython是留有GIL的交互接口，在<a target="_blank" href="http://docs.cython.org/src/userguide/external_C_code.html#acquiring-and-releasing-the-gil">Cython手册里面加起来才半页的说明</a>，语焉不详地介绍了Cython下跟GIL的交互方法，如果没理解上面GIL的工作方式，像我那样把这两页翻烂了都看不懂的。像刚才的例子，cy_loop可以写成这样：</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> cy_loop<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">with</span> nogil:
        <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">True</span>:
            printf<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Print from Cython loop<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: black;">&#41;</span>
            sleep<span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
<p>就是用with nogil块把循环围起来，对，就加一行。如果打开Cython转换而成的C代码，发现Cython用Py_UNBLOCK_THREADS和Py_BLOCK_THREADS把这段循环括起来了，查看Python C-API，Py_UNBLOCK_THREADS跟<a target="_blank" href="http://docs.python.org/dev/c-api/init.html#Py_BEGIN_ALLOW_THREADS">Py_BEGIN_ALLOW_THREADS</a>其实是一样的，只差一个花括号。不过Cython还会盯着你在nogil块里面的代码，里面不能引用Python对象，不能调用Python的函数，要画清界线！啊我只是调用个printf跟sleep，人格保证这是C库的函数！可是这样Cython还是不让过，说在nogil里面不能调用需要gil的函数，恩，</python2.x>printf跟sleep是通过cdef预定义的，Cython可不知道这些函数是哪里的，就默认它们需要gil了。所以要在cdef引入的时候，在后面加上nogil的声明：</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">cdef extern <span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #66cc66;">*</span>:
    unsigned <span style="color: #008000;">int</span> sleep<span style="color: black;">&#40;</span>unsigned <span style="color: #008000;">int</span> seconds<span style="color: black;">&#41;</span> nogil
    <span style="color: #008000;">int</span> printf<span style="color: black;">&#40;</span>char <span style="color: #66cc66;">*</span>format, ...<span style="color: black;">&#41;</span> nogil</pre></td></tr></table></div>
<p>嗯，就这样，Cython编译器终于高兴了。</p>
<p>Cython里面关于GIL的另外一个接口是：</p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
</pre></td><td class="code"><pre class="python" style="font-family:monospace;">cdef void callback_func<span style="color: black;">&#40;</span>void<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">with</span> gil:
    ...</pre></td></tr></table></div>
<p>Cython会生成PyGILState_Ensure()的调用，来保证这个函数在线程获得锁的时候才运行。这个情况一般是用于C的回调函数，因为回调运行是不知道什么时候的，如果这个函数里面有对Python的引用，就需要保证获得了GIL才操作。又或者你在C代码里面又生成了新的线程，而且也会引用Python的东西（纠结……）。</p>
<p>本文<a target="_blank" href="http://ptcoding.googlecode.com/files/cython_gil.tar.gz">例子代码包可这里下载</a>，或<a target="_blank" href="http://code.google.com/p/ptcoding/source/browse/trunk/cython_gil/">Google Code在线查看</a>；例子里面是分别是Python、Cython、C三个循环，因为有Cython的GIL接口，C代码里面就没有使用API的宏了（注释掉）。例子里面添加了全局变量end，主线程进入pause后按Ctrl + C触发使其为真，然后三个线程会相继退出。例子在linux下编译，如在win下编译可能要修改下C代码里面的sleep，以及signal.pause()。</p>
<p>例子代码运行效果：<br />
<img src="http://apt-blog.net/wp-content/uploads/2010/01/cython_loop.png" /></p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/python_cpython_cython_and_gil/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>什么是pythonic？</title>
		<link>http://apt-blog.net/what_is_pythonic</link>
		<comments>http://apt-blog.net/what_is_pythonic#comments</comments>
		<pubDate>Sun, 20 Sep 2009 08:55:13 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[pythonic]]></category>
		<category><![CDATA[编程风格]]></category>
		<guid isPermaLink="false">http://apt-blog.net/archives/563.html</guid>
		<description><![CDATA[昨天的技术沙龙上，清风大妈Zoom.Quiet给大家提了个“很基础很基本”的问题，什么是pythonic？ 自己没特地做过功课，但pythonic这个词不陌生，应该见过几次，但是要说具体是什么，确实很空白。说起python，我首先想起来的是其很让人愉悦的编码体验，一些很常用的封装让人感觉很“惊艳”，比如说，for line in open('file'):ooxxooxx，替代了C++、Java等里面的readline，更不会让人产生在C里面打开文件时候那种恐惧感；还有一些很贴心的细节就是，高度对象化，比如说gtk的TreeViewModel，可以直接送给SQL cursor的execute，直接把用户界面的数据写入到数据库里面去……因为都是list。 像这样的“愉悦感”，就是我理解的pythonic。昨天会场上有人的答案是：“简洁，优雅，高效”，获得比较多人的认可（得到了奖品带大妈签名的《可爱的Python》一本）。 回来后看了看华蟒用户组的首页，呃，很明显写着嘛：申明 pythonic == "大道至简"，也八九不离十了。 这两天也是这个邮件组里面有人讨论实现小时候铅笔盒上的九九乘法表，最获得大家认可的答案是： print &#34;&#34;.join&#40;&#91;&#40;'%s*%s=%s%s' % &#40;y,x,x*y,'\n' if x==y else '\t'&#41;&#41; for x in range&#40;1,10&#41; for y in range&#40;1,10&#41; if x &#62;= y&#93;&#41; 看了犯晕，这很明显跟python给人“惊艳感”的做法背道而驰的，但怎么会这么多人认可呢……同时想起咋们群主“逆”说一直没搞懂的一句算素数的python: print reduce&#40;lambda l,y:not 0 in map&#40;lambda x:y % x, l&#41; and l+&#91;y&#93; or l,xrange&#40;2,1000&#41;, &#91;&#93; &#41; 这叫做pythonic？既不简洁，又不优雅，更不高效，充其量作为一个脑力游戏，还不如玩玩数独！ 玩小聪明一直都是天朝人的传统，人家孔乙己还会“茴”字的N个写法呢，但这类小聪明，着实应该远离python。]]></description>
			<content:encoded><![CDATA[<p>昨天的技术沙龙上，清风大妈Zoom.Quiet给大家提了个“很基础很基本”的问题，什么是pythonic？</p>
<p>自己没特地做过功课，但pythonic这个词不陌生，应该见过几次，但是要说具体是什么，确实很空白。说起python，我首先想起来的是其很让人愉悦的编码体验，一些很常用的封装让人感觉很“惊艳”，比如说，for line in open('file'):ooxxooxx，替代了C++、Java等里面的readline，更不会让人产生在C里面打开文件时候那种恐惧感；还有一些很贴心的细节就是，高度对象化，比如说gtk的TreeViewModel，可以直接送给SQL cursor的execute，直接把用户界面的数据写入到数据库里面去……因为都是list。</p>
<p>像这样的“愉悦感”，就是我理解的pythonic。昨天会场上有人的答案是：“简洁，优雅，高效”，获得比较多人的认可（得到了奖品带大妈签名的《可爱的Python》一本）。</p>
<p>回来后看了看<a target="_blank" href="https://groups.google.com/group/python-cn?hl=zh-CN">华蟒用户组</a>的首页，呃，很明显写着嘛：<b style="color: rgb(255, 153, 0);">申明</b> <font style="font-family: georgia;" size="4"><b><span style="color: rgb(0, 153, 0);">pythonic</span></b></font> == "<b style="color: rgb(204, 0, 0);"><font size="4">大道至简</font></b>"，也八九不离十了。</p>
<p>这两天也是这个邮件组里面有人讨论<a target="_blank" href="https://groups.google.com/group/python-cn/browse_thread/thread/cf6878dc203c02b4/47c82bd808b51b62?hl=zh-CN#47c82bd808b51b62">实现小时候铅笔盒上的九九乘法表</a>，最获得大家认可的答案是：</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: #483d8b;">&quot;&quot;</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span><span style="color: black;">&#91;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'%s*%s=%s%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>y,x,x<span style="color: #66cc66;">*</span>y,<span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\n</span>'</span> <span style="color: #ff7700;font-weight:bold;">if</span> x==y <span style="color: #ff7700;font-weight:bold;">else</span> <span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\t</span>'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span>,<span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> y <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span>,<span style="color: #ff4500;">10</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">if</span> x <span style="color: #66cc66;">&gt;</span>= y<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span></pre></div></div>
<p>看了犯晕，这很明显跟python给人“惊艳感”的做法背道而驰的，但怎么会这么多人认可呢……<a target="_blank" href="http://dcy.is-programmer.com/posts/11069.html">同时想起咋们群主“逆”说一直没搞懂的一句算素数的python</a>:</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: #008000;">reduce</span><span style="color: black;">&#40;</span><span style="color: #ff7700;font-weight:bold;">lambda</span> l,y:<span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #ff4500;">0</span> <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">map</span><span style="color: black;">&#40;</span><span style="color: #ff7700;font-weight:bold;">lambda</span> x:y <span style="color: #66cc66;">%</span> x, l<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">and</span> l+<span style="color: black;">&#91;</span>y<span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">or</span> l,<span style="color: #008000;">xrange</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">2</span>,<span style="color: #ff4500;">1000</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span> <span style="color: black;">&#41;</span></pre></div></div>
<p>这叫做pythonic？既不简洁，又不优雅，更不高效，充其量作为一个脑力游戏，还不如玩玩数独！</p>
<p>玩小聪明一直都是天朝人的传统，人家孔乙己还会“茴”字的N个写法呢，但这类小聪明，着实应该远离python。</p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/what_is_pythonic/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>通过python实现mutipart/form发送数据到paste.ubuntu.org.cn</title>
		<link>http://apt-blog.net/python_script_mutipart_upload</link>
		<comments>http://apt-blog.net/python_script_mutipart_upload#comments</comments>
		<pubDate>Wed, 16 Sep 2009 10:41:42 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[paste. mutipart]]></category>
		<category><![CDATA[upload]]></category>
		<guid isPermaLink="false">http://apt-blog.net/archives/553.html</guid>
		<description><![CDATA[paste.ubuntu.org.cn是国内很多linuxer喜爱的“在线剪贴板”，在跟网友交流时把代码、截图等发在这里，然后把网址发送给对方即可，而且对多种常见代码支持语法高亮，功能简单贴心。（不用像某网友在这个博客上篇帖子里面那样，在留言里面贴一大堆乱哄哄的代码……=。=） 虽说方便，但平时要发送文件时候还是要打开浏览器，再贴代码或者选择文件，多少有点繁琐，所以打算用python写个上传脚本，跟nautilous结合的话，上传截图就方便多了。 首先问球猫要了个ubpaste的perl的脚本，虽然我不懂perl，但发现上传部分只有10来行代码嘛……看来挺简单的，可能用urllib随便弄一下就可以了……结果发现，不行！paste.ubuntu.org.cn用的是mutipart/form协议方式的上传，而python标准库里面没有直接支持这种协议（perl却有……而且自动支持……所以几行代码搞定=。=）…… 查了一下资料后自己写了个class来实现mutipart的boundary，才知道用http来发送文件，特别是上传大文件是这么麻烦的事情……不过还好，不算很复杂，但是整个脚本下来居然有150行代码了……=。= 现在还不能直接拿来当nautilous script用，因为第一个参数是读入文字而不是文件，还在犹豫用bash来重新封装（使用curl一行就搞定上面所说的上传了）……[懒ing]]]></description>
			<content:encoded><![CDATA[<p><a target="_blank" href="http://paste.ubuntu.org.cn/">paste.ubuntu.org.cn</a>是国内很多linuxer喜爱的“在线剪贴板”，在跟网友交流时把代码、截图等发在这里，然后把网址发送给对方即可，而且对多种常见代码支持语法高亮，功能简单贴心。（不用像某网友在这个博客上篇帖子里面那样，在留言里面贴一大堆乱哄哄的代码……=。=）</p>
<p>虽说方便，但平时要发送文件时候还是要打开浏览器，再贴代码或者选择文件，多少有点繁琐，所以打算用python写个上传脚本，跟nautilous结合的话，上传截图就方便多了。</p>
<p>首先<a target="_blank" href="http://code.google.com/p/ptcoding/source/browse/trunk/ubpaste.pl">问球猫要了个ubpaste的perl的脚本</a>，虽然我不懂perl，但发现上传部分只有10来行代码嘛……看来挺简单的，可能用urllib随便弄一下就可以了……结果发现，不行！<a target="_blank" href="http://paste.ubuntu.org.cn/">paste.ubuntu.org.cn</a>用的是<a target="_blank" href="http://yefeng.javaeye.com/blog/315847">mutipart/form协议方式</a>的上传，而python标准库里面没有直接支持这种协议（perl却有……而且自动支持……所以几行代码搞定=。=）……</p>
<p><a target="_blank" href="http://code.google.com/p/ptcoding/source/browse/trunk/ubpaste.py">查了一下资料后自己写了个class来实现mutipart的boundary</a>，才知道用http来发送文件，特别是上传大文件是这么麻烦的事情……不过还好，不算很复杂，但是整个脚本下来居然有150行代码了……=。=</p>
<p>现在还不能直接拿来当nautilous script用，因为第一个参数是读入文字而不是文件，还在犹豫用bash来重新封装（使用curl一行就搞定上面所说的上传了）……[懒ing]</p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/python_script_mutipart_upload/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>错误生涯：Warning: unable to set property `editable&#039; of type `gboolean&#039; from value of type `gchararray&#039;</title>
		<link>http://apt-blog.net/error_of_gchararray</link>
		<comments>http://apt-blog.net/error_of_gchararray#comments</comments>
		<pubDate>Mon, 04 May 2009 06:04:00 +0000</pubDate>
		<dc:creator>PT</dc:creator>
				<category><![CDATA[GTK +]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Pygtk]]></category>
		<category><![CDATA[错误生涯]]></category>
		<guid isPermaLink="false">http://apt-blog.net/archives/356.html</guid>
		<description><![CDATA[Warning: unable to set property `editable' of type `gboolean' from value of type `gchararray' 做GTK编程的时候，使用TreeView控件时出现这个警告，也就是无法使单元格变为“editable”，原因出在这里： column = gtk.TreeViewColumn(columName[columnNum], renderer, editable=True) 原理解TreeViewColumn的构造函数接受的参数里面可以接受设置Cellrenderer的属性，就直接给editable设True，于是就得到以上警告。 换用add_attribute、set_attributes，均是如此。 Google上搜到同样错误警告，但是他的原因是设错值类型，我明明设了True啊…… 自己观察pygtk-demo的代码和手册，突然发现在构造函数里面给出的属性设置值不应该是直接的值，而是对应Model里面的相应column的值！看手册的描述： Zero or more attribute=column pairs may be added to specify from which tree model column to retrieve the attribute value. 呃，果然是看手册时候大意了，这么多年来还是让英语介词搞的晕头转向。如果在构造函数里面设True这个值，就会被解析为1，去对应Model里面第二栏的类型，是字符串的gchararray，当然对不上了。 解决办法是renderer.set_property("editable", True)，调用继承自GObject的set_property方法来设置对象属性。]]></description>
			<content:encoded><![CDATA[<blockquote><p>Warning: unable to set property `editable' of type `gboolean' from value of type `gchararray'</p></blockquote>
<p>做GTK编程的时候，使用TreeView控件时出现这个警告，也就是无法使单元格变为“editable”，原因出在这里：</p>
<p>column = gtk.TreeViewColumn(columName[columnNum], renderer, editable=True)</p>
<p>原理解TreeViewColumn的构造函数接受的参数里面可以接受设置Cellrenderer的属性，就直接给editable设True，于是就得到以上警告。</p>
<p>换用add_attribute、set_attributes，均是如此。</p>
<p>Google上搜到<a target="_blank" href="http://errorjournal.blogspot.com/2008/11/unable-to-set-property-editable-of-type.html">同样错误警告</a>，但是他的原因是设错值类型，我明明设了True啊……</p>
<p>自己观察pygtk-demo的代码和手册，突然发现在构造函数里面给出的属性设置值不应该是直接的值，而是对应Model里面的相应column的值！看手册的描述：<br />
<blockquote>Zero or more attribute=column pairs may be added to specify from which tree model column to retrieve the attribute value.</p></blockquote>
<p>呃，果然是看手册时候大意了，这么多年来还是让英语介词搞的晕头转向。如果在构造函数里面设True这个值，就会被解析为1，去对应Model里面第二栏的类型，是字符串的gchararray，当然对不上了。</p>
<p>解决办法是renderer.set_property("editable", True)，调用继承自GObject的set_property方法来设置对象属性。</p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/error_of_gchararray/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ibus数据库高频词错误修正脚本</title>
		<link>http://apt-blog.net/a_script_fixing_errors_in_ibus_pinyin</link>
		<comments>http://apt-blog.net/a_script_fixing_errors_in_ibus_pinyin#comments</comments>
		<pubDate>Fri, 24 Apr 2009 11:24:18 +0000</pubDate>
		<dc:creator>PT</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[ibus]]></category>
		<category><![CDATA[数据库]]></category>
		<category><![CDATA[输入法]]></category>
		<guid isPermaLink="false">http://apt-blog.net/archives/299.html</guid>
		<description><![CDATA[使用ibus时间长了，常常突然发现有些本来常驻的首选或者常用字词突然掉到后面，甚至到了第二页，并不是被其他词挤掉，而是可能ibus的用户数据库出现错乱了。 不知道这是ibus程序的bug，还是ibus所用的SQLite数据库系统本身的问题，本来当用户输入一个拼音，ibus从用户数据库里面提出对应字的用户输入频数，决定字词的位置；如果用户第一次选择输入某个字，那么该字的记录就添加到用户数据库中，下次输入时便以此记录来提前该字的位置。理论上，在用户数据库里面一个词条的记录最多只能出现一次（多音字算多个字），然而，在实际的使用中，有时不知什么原因，某个本来常用的字被当作第一次输入再次加入到数据库当中，下次输入时，该字便作为低频字来排序，导致位置变得很后，带来不少不便。 这个Python脚本就是把这样的词条找出来，并把后来加入的记录删掉，把词条频数还原。 脚本下载：http://code.google.com/p/ptcoding/source/browse/trunk/ibus_fix (svn目录内的ibux_db_fix.py，其他的两个是测试脚本) 程序功能： 自动备份用户词库 检出用户数据库中出现了两次，但不是多音字词的词条 将后加入的词条删除 检出错词的SQL： SELECT * FROM py_phrase WHERE phrase IN (SELECT phrase FROM py_phrase GROUP BY phrase HAVING COUNT(*) = 2) 尚存缺陷： 如果同一个词条的记录出现了3次或以上，程序不能鉴别（极少可能出现，可修改脚本内的SQL语句来查询出来） 如果一个字本身是多音字，其中一个音节出现了上述情况，程序不能鉴别（貌似概率也挺低的） 如果两个记录中的用户输入频数相同，两条记录都会被删掉（倒不是坏事，影响不大） Python源码： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [...]]]></description>
			<content:encoded><![CDATA[<p>使用ibus时间长了，常常突然发现有些本来常驻的首选或者常用字词突然掉到后面，甚至到了第二页，并不是被其他词挤掉，而是可能ibus的用户数据库出现错乱了。</p>
<p>不知道这是ibus程序的bug，还是ibus所用的SQLite数据库系统本身的问题，本来当用户输入一个拼音，ibus从用户数据库里面提出对应字的用户输入频数，决定字词的位置；如果用户第一次选择输入某个字，那么该字的记录就添加到用户数据库中，下次输入时便以此记录来提前该字的位置。理论上，在用户数据库里面一个词条的记录最多只能出现一次（多音字算多个字），然而，在实际的使用中，有时不知什么原因，某个本来常用的字被当作第一次输入再次加入到数据库当中，下次输入时，该字便作为低频字来排序，导致位置变得很后，带来不少不便。</p>
<p>这个Python脚本就是把这样的词条找出来，并把后来加入的记录删掉，把词条频数还原。<br />
<img src="http://apt-blog.net/wp-content/uploads/2009/04/ibusdbfix.png" /></p>
<p>脚本下载：<a href="http://code.google.com/p/ptcoding/source/browse/trunk/ibus_fix">http://code.google.com/p/ptcoding/source/browse/trunk/ibus_fix</a><br />
(svn目录内的ibux_db_fix.py，其他的两个是测试脚本)</p>
<p>程序功能：</p>
<ol>
<li>自动备份用户词库</li>
<li>检出用户数据库中出现了两次，但不是多音字词的词条
</li>
<li>将后加入的词条删除</li>
</ol>
<p>检出错词的SQL：</p>
<blockquote><p>SELECT * FROM py_phrase<br />
            WHERE phrase IN<br />
                (SELECT phrase<br />
                    FROM py_phrase<br />
                    GROUP BY phrase<br />
                    HAVING COUNT(*) = 2)</p></blockquote>
<p>尚存缺陷：</p>
<ol>
<li>如果同一个词条的记录出现了3次或以上，程序不能鉴别（极少可能出现，可修改脚本内的SQL语句来查询出来）</li>
<li>如果一个字本身是多音字，其中一个音节出现了上述情况，程序不能鉴别（貌似概率也挺低的）</li>
<li>如果两个记录中的用户输入频数相同，两条记录都会被删掉（倒不是坏事，影响不大）</li>
</ol>
<p>Python源码：<br />
<span id="more-299"></span></p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/usr/bin/python</span>
<span style="color: #808080; font-style: italic;"># -*- coding: utf-8 -*-</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">import</span> sqlite3
&nbsp;
DB = <span style="color: #dc143c;">os</span>.<span style="color: black;">getenv</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;HOME&quot;</span><span style="color: black;">&#41;</span> + <span style="color: #483d8b;">&quot;/.ibus/pinyin/user.db&quot;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">exists</span><span style="color: black;">&#40;</span>DB<span style="color: black;">&#41;</span>:
     <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;囧……似乎没有安装ibus……PT发来贺电……&quot;</span>
     exit<span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># ------ Backup database file --------</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">time</span>
nowtime = <span style="color: #dc143c;">time</span>.<span style="color: black;">strftime</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;_%Y-%m-%d-%H_%M_%S&quot;</span>, <span style="color: #dc143c;">time</span>.<span style="color: black;">localtime</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
DB_BK = DB + nowtime
execute = <span style="color: #483d8b;">&quot;cp -v %s %s&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>DB, DB_BK<span style="color: black;">&#41;</span>
<span style="color: #dc143c;">os</span>.<span style="color: black;">system</span><span style="color: black;">&#40;</span>execute<span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;ibus用户数据库已备份到&quot;</span>,DB_BK
&nbsp;
&nbsp;
<span style="color: #808080; font-style: italic;"># ------ Connect to Database ---------</span>
con = sqlite3.<span style="color: black;">connect</span><span style="color: black;">&#40;</span>DB<span style="color: black;">&#41;</span>
c = con.<span style="color: black;">cursor</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
c.<span style="color: black;">execute</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;&quot;&quot;SELECT * FROM py_phrase
            WHERE phrase IN
                (SELECT phrase
                    FROM py_phrase
                    GROUP BY phrase
                    HAVING COUNT(*) = 2)&quot;&quot;&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
rows = c.<span style="color: black;">fetchall</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
badphrase = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># ------ Detemine bad phrases -------</span>
<span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span>, <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>rows<span style="color: black;">&#41;</span>, <span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span>:
    flag = <span style="color: #008000;">True</span>
    phrase = rows<span style="color: black;">&#91;</span>i:i+<span style="color: #ff4500;">2</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> j <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">5</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">if</span> phrase<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span>j<span style="color: black;">&#93;</span> <span style="color: #66cc66;">!</span>= phrase<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#91;</span>j<span style="color: black;">&#93;</span>:
            flag = <span style="color: #008000;">False</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> flag:
        badphrase.<span style="color: black;">append</span><span style="color: black;">&#40;</span>phrase<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;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>badphrase<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;没有发现错误词条……PT发来贺电…… http://apt-blog.net&quot;</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;发现以下错误词条, 共%d个：&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>badphrase<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">for</span> row <span style="color: #ff7700;font-weight:bold;">in</span> badphrase:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;**[%s]**&quot;</span> <span style="color: #66cc66;">%</span> row<span style="color: black;">&#91;</span>-<span style="color: #ff4500;">3</span><span style="color: black;">&#93;</span>
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>执行优化清理……&quot;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># ------  Clean work to Database</span>
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        <span style="color: #ff7700;font-weight:bold;">for</span> row <span style="color: #ff7700;font-weight:bold;">in</span> badphrase:
            sql = <span style="color: #483d8b;">&quot;DELETE FROM py_phrase WHERE phrase = <span style="color: #000099; font-weight: bold;">\&quot;</span>%s<span style="color: #000099; font-weight: bold;">\&quot;</span> AND user_freq = %s&quot;</span> <span style="color: #66cc66;">%</span> <span style="color: black;">&#40;</span>row<span style="color: black;">&#91;</span>-<span style="color: #ff4500;">3</span><span style="color: black;">&#93;</span>, row<span style="color: black;">&#91;</span>-<span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
            <span style="color: #808080; font-style: italic;">#print sql</span>
            c.<span style="color: black;">execute</span><span style="color: black;">&#40;</span>sql<span style="color: black;">&#41;</span>
&nbsp;
        con.<span style="color: black;">commit</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;清理完成……PT发来贺电…… http://apt-blog.net&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> sqlite3.<span style="color: black;">OperationalError</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;清理无法完成，请先退出ibus...&quot;</span>
&nbsp;
con.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/a_script_fixing_errors_in_ibus_pinyin/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>测试ibus输入法默认词库的流行词覆盖度</title>
		<link>http://apt-blog.net/testing_ibus_pinyin</link>
		<comments>http://apt-blog.net/testing_ibus_pinyin#comments</comments>
		<pubDate>Tue, 31 Mar 2009 16:03:37 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[ibus]]></category>
		<category><![CDATA[词库]]></category>
		<category><![CDATA[输入法]]></category>
		<guid isPermaLink="false">http://apt-blog.net/archives/214.html</guid>
		<description><![CDATA[这些天一直在想怎么扩充ibus输入法的词库，虽然一般使用感觉还好。在网上找到sogou提供了一个“互联网词库”，里面是搜索引擎分析出来的15万多词语，本想拿来导入到ibus，先用python测试了一下有多少词语已经在ibus的默认词库中，最后发现15万流行词中只有200多不在默认词库中，ibus词库确实挺优秀。 程序输出：（测试代码见后） seached: 157200 times. 215 phrases not in the database, written in file 'notexist' 查看notexist文件，发现除了后半部分一大堆频度为1的成语之外，只有20多个大频率词没在默认词库： （- -｜原来连“裸体”都没有？太和谐了！建议广滇驹推荐ibus为国家首选输入法） 乾坤 3561275 N, 乾隆 3088184 N, 乾净 1533219 夥伴 1052393 瞭望 984469 宏碁 979267 乾脆 953204 乾燥 624377 清乾隆 480337 乾隆皇帝 380252 N, 阿房宫 235461 乾隆年间 214986 定乾坤 210477 乾隆帝 149133 乾坤袋 143966 著色 111072 萧乾 84647 [...]]]></description>
			<content:encoded><![CDATA[<p>这些天一直在想怎么扩充ibus输入法的词库，虽然一般使用感觉还好。在网上找到sogou提供了一个“互联网词库”，里面是搜索引擎分析出来的15万多词语，本想拿来导入到ibus，先用python测试了一下有多少词语已经在ibus的默认词库中，最后发现15万流行词中只有200多不在默认词库中，ibus词库确实挺优秀。</p>
<p>程序输出：（测试代码见后）</p>
<p>seached: 157200 times. 215 phrases not in the database,<br />
 written in file 'notexist'</p>
<p>查看notexist文件，发现除了后半部分一大堆频度为1的成语之外，只有20多个大频率词没在默认词库：<br />
（- -｜原来连“裸体”都没有？太和谐了！建议广滇驹推荐ibus为国家首选输入法）</p>
<blockquote><p>乾坤	3561275	N,<br />
乾隆	3088184	N,<br />
乾净	1533219<br />
夥伴	1052393<br />
瞭望	984469<br />
宏碁	979267<br />
乾脆	953204<br />
乾燥	624377<br />
清乾隆	480337<br />
乾隆皇帝	380252	N,<br />
阿房宫	235461<br />
乾隆年间	214986<br />
定乾坤	210477<br />
乾隆帝	149133<br />
乾坤袋	143966<br />
著色	111072<br />
萧乾	84647<br />
小夥子	79076<br />
瞭望台	71630<br />
寒伧	50780	V,ADJ,<br />
祼体	46797
</p></blockquote>
<p>其实ibus词库不用再怎么扩充了，呵呵，当然<big>萌萌的草泥马</big>、雅篾蝶、法克鱿之类的新新词汇，还得用户自己敲一下咯，或者能找到专用的神兽词库……<br />
<span id="more-214"></span></p>
<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/usr/bin/python</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> sqlite3
&nbsp;
con = sqlite3.<span style="color: black;">connect</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/usr/share/ibus-pinyin/engine/py.db'</span><span style="color: black;">&#41;</span>
c = con.<span style="color: black;">cursor</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
diclib = <span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Freq/SogouLabDic.dic&quot;</span>,<span style="color: #483d8b;">'r'</span><span style="color: black;">&#41;</span>
rec_notexist = <span style="color: #008000;">open</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'notexist'</span>,<span style="color: #483d8b;">'w'</span><span style="color: black;">&#41;</span>
&nbsp;
seachCounter = <span style="color: #ff4500;">0</span>
notExistCounter = <span style="color: #ff4500;">0</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #ff4500;">1</span>:
    line = diclib.<span style="color: #dc143c;">readline</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>line<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">break</span>
    data = line.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\t</span>'</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">try</span>:
        phrase = data<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>.<span style="color: black;">decode</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;gbk&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
        c.<span style="color: black;">execute</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;select *<span style="color: #000099; font-weight: bold;">\</span>
            from py_phrase<span style="color: #000099; font-weight: bold;">\</span>
            where phrase = ?&quot;</span>, <span style="color: black;">&#91;</span>phrase.<span style="color: black;">encode</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'utf-8'</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>
&nbsp;
        rows = c.<span style="color: black;">fetchall</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
        seachCounter += <span style="color: #ff4500;">1</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">UnicodeDecodeError</span>, e:
        <span style="color: #ff7700;font-weight:bold;">print</span> e
        <span style="color: #ff7700;font-weight:bold;">print</span> data<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
        rec_notexist.<span style="color: black;">writelines</span><span style="color: black;">&#40;</span>line<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">continue</span>
    <span style="color: #ff7700;font-weight:bold;">except</span> BaseException, e:
        <span style="color: #ff7700;font-weight:bold;">print</span> e
        <span style="color: #ff7700;font-weight:bold;">break</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> rows:
        notExistCounter += <span style="color: #ff4500;">1</span>
        rec_notexist.<span style="color: black;">writelines</span><span style="color: black;">&#40;</span>line<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">print</span> phrase
&nbsp;
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;Seached: %d times. %d phrases not in the database, <span style="color: #000099; font-weight: bold;">\n</span> <span style="color: #000099; font-weight: bold;">\</span>
written in file 'notexist'&quot;</span> <span style="color: #66cc66;">%</span><span style="color: black;">&#40;</span>seachCounter, notExistCounter<span style="color: black;">&#41;</span>
&nbsp;
rec_notexist.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
diclib.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
con.<span style="color: black;">close</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></pre></td></tr></table></div>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/testing_ibus_pinyin/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PyETGO 专辑下载脚本 Python练习作品</title>
		<link>http://apt-blog.net/pyetgo-an-script-for-downing-music-album</link>
		<comments>http://apt-blog.net/pyetgo-an-script-for-downing-music-album#comments</comments>
		<pubDate>Tue, 10 Mar 2009 15:44:24 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Download]]></category>
		<category><![CDATA[Music]]></category>
		<category><![CDATA[脚本]]></category>
		<guid isPermaLink="false">http://apt-blog.net/archives/114.html</guid>
		<description><![CDATA[PyETGO 0.3 更新版下载：http://code.google.com/p/ptcoding/source/browse/trunk/PyETGO 本脚本可跨平台使用，在Win下需要wget for Windows， 把wget.exe放在脚本所在目录即可。 Wget下载：http://www.interlog.com/~tcharron/wgetwin-1_5_3_1-binary.zip 感谢Twitter上的好友mengzehe关于Win下使用的测试和提醒。 感谢Ubuntu论坛上网友的测试。 v0.3 2009.03.16 &#160;&#160;&#160; -对获取的XML列表中存在的非法字符进行过滤（解决曲名含"&#38;"等不规则字符导致无法下载） v0.2更新 2009.03.11&#160; -为在Win下使用本脚本，全部使用Unicode的字符串来提示（0.1有部分乱码） -修改写入Intro.txt文件的方法，使用writelines，会根据操作系统不同写入不同换行符，减少乱码 -写入文件名前过滤Win下的非法文件名字符/\:&#60;&#62;?&#124;等 大概实现这样的功能： 下载http://music.etgo.cn/上的任意专辑的音乐文件 专辑的存放目录命名为“歌手名 - 专辑名” 多CD的专辑，音乐文件命名为“CD号-轨号_歌手 - 歌名”，单CD则为“轨号_歌手 - 歌名” 下载专辑的封面和封底文件cover.jpg、coverback.jpg 从页面中抽出的专辑信息和介绍文字写入Intro.txt 首次使用需要用-u和-p输入一个ETGO账户以获取Cookie，以后下载只需用-a指定一个专辑的页面 下载时可随时使用Ctrl + C中断，重新下载时自动从断点续传。 花了好几天的时间在这个脚本上，基本把Python的特性摸熟了。 ETGO是国内一个娱乐网站，有电影、Mp3等，资源不算新，格式也就192～256K那样，没太大特色，但其有自己的服务器，运营稳定，这几年来我偶尔都从那里Down些专辑，基本上浏览器的嗅探+序号批量下载就可搞定，而且速度不赖。网站的免费试听使用的是Flash的播放器，这个脚本则模拟了播放器读取列表的功能，骗回所有Mp3的原始地址，然后调用wget下载。 稍微记录下开发过程。 首先还是逆向路线，打开Wireshark在嗅探，从浏览器点击一首歌的试听，浏览器弹出窗口开始播放。从嗅探结果看，在本地发出GET MP3文件的那个报文之前，服务器必然已经把真实地址发回给Flash播放器，于是检查前一个请求：GET /mp3player.php。从其相应报文类型HTTP/XML就可以判断出这是一个返回XML信息的请求，打开分析，果然。 但这个mp3player.php根据什么来知道用户的点歌呢？查看专辑页的源码，只见每个“试听”链接具有这样的属性： onClick="MM_openBrWindow('/player.php?fid=90388','MusicPlayer','width=446,height=340')" 不难猜到fid=54664就是每首歌的“身份ID”，player.php是带Flash控件的那个页面，fid应该是送给Flash的参数，而mp3player.php才是返回列表XML，而mp3player.php没有任何GET参数，说明请求还是藏在报文内。 回到Wireshark查看GET /mp3player.php那个报文，展开其请求，一眼看不出什么猫腻，就见到那个长长的Cookie，Wireshark没有给出完整的分析，直接看下面的原始数据： 抓到你了！竟然藏在Cookie的结尾。 1.getPlayListXML模块 有了之前写CETQuery的经验，模拟它发个这样的报文很轻松，稍作调试就收到完整的XML歌曲列表。原来ETGO整个网站对用户的验证都是通过这样的Cookie，里面甚至包含了明文的用户名和密码，都算的上是世界上最不安全的网站之一了。都几年了没点技术更新，难怪我骗起来那么容易（嘿嘿）。 一次得一首歌的效率我可是不满意的，页面里面不是有个连播么，一次往列表里面添加所有歌。同上道理嗅探下来，原来只需把藏在Cookie后面那个参数用%7C分隔，连上一串uid即可： playlist=20274%7C20275%7C20276%7C20277%7C20278...... 剩下的是解析XML的工作，这可是Python的强项，我选择了minidom。《Dive in Python》Scripts and [...]]]></description>
			<content:encoded><![CDATA[<p><img style="max-width: 800px;" src="http://apt-blog.net/wp-content/uploads/2009/03/pyetgo04.png" alt="" /><br />
PyETGO 0.3 更新版下载：<a href="http://code.google.com/p/ptcoding/source/browse/trunk/PyETGO">http://code.google.com/p/ptcoding/source/browse/trunk/PyETGO</a></p>
<p>本脚本可跨平台使用，在Win下需要wget for Windows， 把wget.exe放在脚本所在目录即可。<br />
Wget下载：<a target="_blank" href="http://www.interlog.com/%7Etcharron/wgetwin-1_5_3_1-binary.zip">http://www.interlog.com/~tcharron/wgetwin-1_5_3_1-binary.zip</a></p>
<p>感谢Twitter上的好友mengzehe关于Win下使用的测试和提醒。<br />
<a target="_blank" href="http://forum.ubuntu.org.cn/viewtopic.php?f=63&amp;t=188514&amp;sid=9c3fa102f17b8186faec9cead7abebcc">感谢Ubuntu论坛上网友的测试。</a><br />
v0.3<br />
2009.03.16<br />
&nbsp;&nbsp;&nbsp; -对获取的XML列表中存在的非法字符进行过滤（解决曲名含"&amp;"等不规则字符导致无法下载）</p>
<p>v0.2更新</p>
<ul>
<li>2009.03.11&nbsp; </li>
<li>-为在Win下使用本脚本，全部使用Unicode的字符串来提示（0.1有部分乱码）</li>
<li>-修改写入Intro.txt文件的方法，使用writelines，会根据操作系统不同写入不同换行符，减少乱码</li>
<li>-写入文件名前过滤Win下的非法文件名字符/\:&lt;&gt;?|等</li>
</ul>
<p>大概实现这样的功能：</p>
<ol>
<li>下载http://music.etgo.cn/上的任意专辑的音乐文件</li>
<li>专辑的存放目录命名为“歌手名 - 专辑名”</li>
<li>多CD的专辑，音乐文件命名为“CD号-轨号_歌手 - 歌名”，单CD则为“轨号_歌手 - 歌名”</li>
<li>下载专辑的封面和封底文件cover.jpg、coverback.jpg</li>
<li>从页面中抽出的专辑信息和介绍文字写入Intro.txt</li>
<li>首次使用需要用-u和-p输入一个ETGO账户以获取Cookie，以后下载只需用-a指定一个专辑的页面</li>
<li>下载时可随时使用Ctrl + C中断，重新下载时自动从断点续传。</li>
</ol>
<p>花了好几天的时间在这个脚本上，基本把Python的特性摸熟了。</p>
<p>ETGO是国内一个娱乐网站，有电影、Mp3等，资源不算新，格式也就192～256K那样，没太大特色，但其有自己的服务器，运营稳定，这几年来我偶尔都从那里Down些专辑，基本上浏览器的嗅探+序号批量下载就可搞定，而且速度不赖。网站的免费试听使用的是Flash的播放器，这个脚本则模拟了播放器读取列表的功能，骗回所有Mp3的原始地址，然后调用wget下载。</p>
<p>稍微记录下开发过程。<br />
<span id="more-114"></span></p>
<p><img style="max-width: 800px;" src="http://apt-blog.net/wp-content/uploads/2009/03/pyetgo01.png" alt="" height="105" width="446" /></p>
<p>首先还是逆向路线，打开Wireshark在嗅探，从浏览器点击一首歌的试听，浏览器弹出窗口开始播放。从嗅探结果看，在本地发出GET MP3文件的那个报文之前，服务器必然已经把真实地址发回给Flash播放器，于是检查前一个请求：GET /mp3player.php。从其相应报文类型HTTP/XML就可以判断出这是一个返回XML信息的请求，打开分析，果然。</p>
<p>但这个mp3player.php根据什么来知道用户的点歌呢？查看专辑页的源码，只见每个“试听”链接具有这样的属性：</p>
<blockquote><p>onClick="MM_openBrWindow('/player.php?fid=90388','MusicPlayer','width=446,height=340')"</p></blockquote>
<p>不难猜到fid=54664就是每首歌的“身份ID”，player.php是带Flash控件的那个页面，fid应该是送给Flash的参数，而mp3player.php才是返回列表XML，而mp3player.php没有任何GET参数，说明请求还是藏在报文内。</p>
<p><img style="max-width: 800px;" src="http://apt-blog.net/wp-content/uploads/2009/03/pyetgo02.png" alt="" /></p>
<p>回到Wireshark查看GET /mp3player.php那个报文，展开其请求，一眼看不出什么猫腻，就见到那个长长的Cookie，Wireshark没有给出完整的分析，直接看下面的原始数据：</p>
<p><img style="max-width: 800px;" src="http://apt-blog.net/wp-content/uploads/2009/03/pyetgo03.png" alt="" height="42" width="445" /></p>
<p>抓到你了！竟然藏在Cookie的结尾。</p>
<h4>1.getPlayListXML模块</h4>
<p>有了之前写CETQuery的经验，模拟它发个这样的报文很轻松，稍作调试就收到完整的XML歌曲列表。原来ETGO整个网站对用户的验证都是通过这样的Cookie，里面甚至包含了明文的用户名和密码，都算的上是世界上最不安全的网站之一了。都几年了没点技术更新，难怪我骗起来那么容易（嘿嘿）。</p>
<p>一次得一首歌的效率我可是不满意的，页面里面不是有个连播么，一次往列表里面添加所有歌。同上道理嗅探下来，原来只需把藏在Cookie后面那个参数用%7C分隔，连上一串uid即可：</p>
<blockquote><p>playlist=20274%7C20275%7C20276%7C20277%7C20278......</p></blockquote>
<p>剩下的是解析XML的工作，这可是Python的强项，我选择了minidom。《Dive in Python》Scripts and Streams一章中示范了直接把urlopen的流对象直接塞给minidom的parse，帅呆了。操作方法和Javascript的DOM很类似，用了一个getElementsByTagName把所有个song元素调出，再组织一下属性值，轻松过关。</p>
<h4>2.Download File 方法</h4>
<p>如何下载Mp3文件呢？直接下载是不行的，服务器有HTTP头检测。wget --help看了一下，果然不愧是UNIX下的老牌工具，很强大，--header可定义下载时的HTTP头。依旧在Wireshark看其连接Mp3地址时的报文，看不出有什么特别的，也是那个Cookie。</p>
<h4>3.getCookie 模块</h4>
<p>要获得Cookie，需要在页面登录。页面登录经典的老牌方法，把用户名密码等post到http://member.etgo.cn/member.php，然后读取相应头部里面的'set-cookie'段，然后干脆把Cookie写入文件，以后要下载专辑都不用麻烦连服务器了，直接读取即可。</p>
<h4>4.getAlbum 模块</h4>
<p>如果仅仅是下载，这时已经完成了，但为了提高自动化程度，还得想办法自动获取每首歌的uid，这需要解释专辑的HTML页面。《Dive in Python》中的例子用的是SGMLParser，但是从文档看到说SGMLParser在2.6版本后就过时了，《Python网络编程基础》里面则用了HTMLParser，两个模块大同小异，选择了后者。</p>
<p>HTMLParser的设计和很多Python模块一样，预置了接口，只需要实现所用的部分即可（似乎就是传说中的抽象工厂模式）。为了下载的时候能够把文件名编得完美点，从网页中解析出完整的歌手、专辑名，用了一堆正则表达式来过滤。</p>
<h4>5.模块内聚</h4>
<p>以上的几个模块都实现完成后，虽说什么功能都不缺了，但还是耗了不少时间。一是文件名，对于多CD的专辑，在文件名前标上CD号，而单CD的，只标轨号。而CD号和轨号都是要从URL里面析取的，一晚上下来把正则表达式用得相当熟手了。其次是如何组织目录，检查文件，os模块内的几种系统调用，还有要响应强行终止的任务，处理命令行参数……</p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/pyetgo-an-script-for-downing-music-album/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
