﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-思想比知识更重要 成长比成功更重要-随笔分类-Python</title><link>http://www.blogjava.net/renyangok/category/20450.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 21 Dec 2007 16:03:29 GMT</lastBuildDate><pubDate>Fri, 21 Dec 2007 16:03:29 GMT</pubDate><ttl>60</ttl><item><title>转：浅谈base64编码</title><link>http://www.blogjava.net/renyangok/archive/2007/11/19/161606.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Mon, 19 Nov 2007 06:30:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/11/19/161606.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/161606.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/11/19/161606.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/161606.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/161606.html</trackback:ping><description><![CDATA[		我打赌当你见到Base64这个词的时候你会觉得在哪里见过，因为在你能够上网看到这篇文章的时候你已经在后台使用它了。如果您对二进制数有所了解，你就可以开始读它了。<br />
<br />
打开一封Email，查看其原始信息（您可以通过收取、导出该邮件用文本编辑器查看）。你会看到类似这样的一个效果： <br />
<br />
Date: Thu, 25 Dec 2003 06:33:07 +0800 <br />
From: "eSX?!" &lt;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#37;&#51;&#67;&#97;&#37;&#50;&#48;&#104;&#114;&#101;&#102;&#61;"><font color="#0000ff">snaix@yeah.net</font></a>'&gt;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#110;&#97;&#105;&#120;&#64;&#121;&#101;&#97;&#104;&#46;&#110;&#101;&#116;"><font color="#0000ff">snaix@yeah.net</font></a>'&gt;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#37;&#51;&#67;&#97;&#37;&#50;&#48;&#104;&#114;&#101;&#102;&#61;"><font color="#0000ff">snaix@yeah.net</font></a>'&gt;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#110;&#97;&#105;&#120;&#64;&#121;&#101;&#97;&#104;&#46;&#110;&#101;&#116;"><font color="#0000ff">snaix@yeah.net</font></a>&gt; <br />
Reply-To: <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#37;&#51;&#67;&#97;&#37;&#50;&#48;&#104;&#114;&#101;&#102;&#61;"><font color="#0000ff">snaix@yeah.net</font></a>'&gt;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#110;&#97;&#105;&#120;&#64;&#121;&#101;&#97;&#104;&#46;&#110;&#101;&#116;"><font color="#0000ff">snaix@yeah.net</font></a>'&gt;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#37;&#51;&#67;&#97;&#37;&#50;&#48;&#104;&#114;&#101;&#102;&#61;"><font color="#0000ff">snaix@yeah.net</font></a>'&gt;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#110;&#97;&#105;&#120;&#64;&#121;&#101;&#97;&#104;&#46;&#110;&#101;&#116;"><font color="#0000ff">snaix@yeah.net</font></a><br />
To: "snaix" &lt;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#37;&#51;&#67;&#97;&#37;&#50;&#48;&#104;&#114;&#101;&#102;&#61;"><font color="#0000ff">snaix@126.com</font></a>'&gt;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#110;&#97;&#105;&#120;&#64;&#49;&#50;&#54;&#46;&#99;&#111;&#109;"><font color="#0000ff">snaix@126.com</font></a>&gt; <br />
Subject: <br />
X-mailer: Foxmail 5.0 beta2 [cn] <br />
Mime-Version: 1.0 <br />
Content-Type: text/plain; <br />
charset="gb2312" <br />
Content-Transfer-Encoding: base64 <br />
<br />
xOO6w6OsU25haVgNCg0KoaGhodXiysfSu7j2QmFzZTY0tcSy4srU08q8/qOhDQoNCkJlc3QgV2lz <br />
aGVzIQ0KIAkJCQkNCqGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaEgICAgICAgICAgICAgICBl <br />
U1g/IQ0KoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoSAgICAgICAgICAgICAgIHNuYWl4QHll <br />
YWgubmV0DQqhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhICAgICAgICAgMjAwMy0x <br />
Mi0yNQ0K <br />
<br />
是否看到了&#8220;base64&#8221;标记？是否看到了标记下面的一行乱码？也许你会恍然大悟，对！这就是Base64编码。 <br />
<br />
<strong>什么是Base64？</strong> <br />
<br />
按
照RFC2045的定义，Base64被定义为：Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。（The
Base64 Content-Transfer-Encoding is designed to represent arbitrary
sequences of octets in a form that need not be humanly readable.） <br />
<br />
<strong>为什么要使用Base64？</strong><br />
<br />
在设计这个编码的时候，我想设计人员最主要考虑了3个问题： <br />
1.是否加密？ <br />
2.加密算法复杂程度和效率 <br />
3.如何处理传输？ <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;加密是肯定的，但是加密的目的不是让用户发送非常安全的Email。这种加密方式主要就是&#8220;防君子不防小人&#8221;。即达到一眼望去完全看不出内容即可。 <br />
基
于这个目的加密算法的复杂程度和效率也就不能太大和太低。和上一个理由类似，MIME协议等用于发送Email的协议解决的是如何收发Email，而并不
是如何安全的收发Email。因此算法的复杂程度要小，效率要高，否则因为发送Email而大量占用资源，路就有点走歪了。 <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;
但是，如果是基于以上两点，那么我们使用最简单的恺撒法即可，为什么Base64看起来要比恺撒法复杂呢？这是因为在Email的传送过程中，由于历史原
因，Email只被允许传送ASCII字符，即一个8位字节的低7位。因此，如果您发送了一封带有非ASCII字符（即字节的最高位是1）的Email通
过有&#8220;历史问题&#8221;的网关时就可能会出现问题。网关可能会把最高位置为0！很明显，问题就这样产生了！因此，为了能够正常的传送Email，这个问题就必须
考虑！所以，单单靠改变字母的位置的恺撒之类的方案也就不行了。关于这一点可以参考RFC2046。 <br />
基于以上的一些主要原因产生了Base64编码。 <br />
<br />
<strong>算法详解</strong> <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;Base64编码要求把3个8位字节（3*8=24）转化为4个6位的字节（4*6=24），之后在6位的前面补两个0，形成8位一个字节的形式。 <br />
具体转化形式间下图： <br />
字符串&#8220;张3&#8221; <br />
11010101 11000101 00110011 <br />
<br />
00110101 00011100 00010100 00110011 <br />
表1 <br />
<br />
可以这么考虑：把8位的字节连成一串110101011100010100110011 <br />
然后每次顺序选6个出来之后再把这6二进制数前面再添加两个0，就成了一个新的字节。之后再选出6个来，再添加0，依此类推，直到24个二进制数全部被选完。 <br />
让我们来看看实际结果： <br />
<br />
字符串&#8220;张3&#8221; <br />
11010101 HEX:D5 11000101 HEX:C5 00110011 HEX:33 <br />
<br />
00110101 00011100 00010100 00110011 <br />
字符&#8217;5&#8217; 字符&#8217;^"&#8217; 字符&#8217;^T&#8217; 字符&#8217;3&#8217; <br />
十进制53 十进制34 十进制20 十进制51 <br />
表2 <br />
<br />
这样&#8220;张3 &#8221;这个字符串就被Base64表示为&#8221;5^"^T3&#8221;了么？。错！ <br />
Base64编码方式并不是单纯利用转化完的内容进行编码。像&#8217;^"&#8217;字符是控制字符，并不能通过计算机显示出来，在某些场合就不能使用了。Base64有其自身的编码表： <br />
<br />
Table 1: The Base64 Alphabet <br />
Value Encoding Value Encoding Value Encoding Value Encoding <br />
0 A 17 R 34 i 51 z <br />
1 B 18 S 35 j 52 0 <br />
2 C 19 T 36 k 53 1 <br />
3 D 20 U 37 l 54 2 <br />
4 E 21 V 38 m 55 3 <br />
5 F 22 W 39 n 56 4 <br />
6 G 23 X 40 o 57 5 <br />
7 H 24 Y 41 p 58 6 <br />
8 I 25 Z 42 q 59 7 <br />
9 J 26 a 43 r 60 8 <br />
10 K 27 b 44 s 61 9 <br />
11 L 28 c 45 t 62 + <br />
12 M 29 d 46 u 63 / <br />
13 N 30 e 47 v (pad) = <br />
14 O 31 f 48 w <br />
15 P 32 g 49 x <br />
16 Q 33 h 50 y <br />
表3 <br />
<br />
这
也是Base64名称的由来，而Base64编码的结果不是根据算法把编码变为高两位是0而低6为代表数据，而是变为了上表的形式，如&#8221;A&#8221;就有7位，
而&#8221;a&#8221;就只有6位。表中，编码的编号对应的是得出的新字节的十进制值。因此，从表2可以得到对应的Base64编码： <br />
<br />
字符串&#8220;张3&#8221; <br />
11010101 HEX:D5 11000101 HEX:C5 00110011 HEX:33 <br />
<br />
00110101 00011100 00010100 00110011 <br />
字符&#8217;5&#8217; 字符&#8217;^"&#8217; 字符&#8217;^T&#8217; 字符&#8217;3&#8217; <br />
十进制53 十进制34 十进制20 十进制51 <br />
字符&#8217;1&#8217; 字符&#8217;i&#8217; 字符&#8217;U&#8217; 字符&#8217;z&#8217; <br />
表4 <br />
<br />
这样，字符串&#8220;张3&#8221;经过编码后就成了字符串&#8220;1iUz&#8221;了。 <br />
Base64将3个字节转变为4个字节，因此，编码后的代码量（以字节为单位，下同）约比编码前的代码量多了1/3。之所以说是&#8220;约&#8221;，是因为如果代码量正好是3的整数倍，那么自然是多了1/3。但如果不是呢？ <br />
细心的人可能已经注意到了，在The Base64 Alphabet中的最后一个有一个(pad) =字符。这个字符的目的就是用来处理这个问题的。 <br />
当代码量不是3的整数倍时，代码量/3的余数自然就是2或者1。转换的时候，结果不够6位的用0来补上相应的位置，之后再在6位的前面补两个0。转换完空出的结果就用就用&#8220;=&#8221;来补位。譬如结果若最后余下的为2个字节的&#8220;张&#8221;： <br />
<br />
字符串&#8220;张&#8221; <br />
11010101 HEX:D5 11000101 HEX:C5 <br />
<br />
00110101 00011100 00010100 <br />
十进制53 十进制34 十进制20 pad <br />
字符&#8217;1&#8217; 字符&#8217;i&#8217; 字符&#8217;U&#8217; 字符&#8217;=&#8217; <br />
表6 <br />
<br />
这样，最后的2个字节被整理成了&#8220;1iU=&#8221;。 <br />
同理，若原代码只剩下一个字节，那么将会添加两个&#8220;=&#8221;。只有这两种情况，所以，Base64的编码最多会在编码结尾有两个&#8220;=&#8221; <br />
至于将Base64的解码，只是一个简单的编码的逆过程，读者可以自己探讨。我将在文章的最后给出解码算法。 <br />
<br />
<strong>算法实现</strong> <br />
其实在算法详解的时候基本上已经说的很清楚了。用于程序上，除去约束判断，大概可以分为如下几步几步： <br />
读取数据3字节用AND取前6位，放入新的变量中右移两位，高两位清0AND取第一个字节的后2位和第二个字节的前4位移位放入新变量中右移两位，清0&#8230;&#8230;依此类推。 <br />
解码的类C语言实现的算法： <br />
BYTE LMoveBit(int base, int MoveNum) <br />
{ <br />
BYTE result=base; <br />
if(MoveNum==0)return 1; <br />
if(MoveNum==1)return MoveNum; <br />
result=base&lt;&lt;(MoveNum-1); <br />
return result; <br />
} <br />
<br />
char base64_alphabet[]= <br />
{'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', <br />
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', <br />
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', <br />
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/','='}; <br />
BYTE Base64Decode(char *base64code, DWORD base64length) <br />
{ <br />
char buf[4]; <br />
int i,j; <br />
int k; <br />
int l=0; <br />
BYTE temp1[4],temp2; <br />
BYTE *Buffer=new BYTE[base64length*3/4]; <br />
DWORD base64a=(base64length/4)-1; <br />
DWORD base64b=0; <br />
for(;base64b&lt;base64a+1;base64b++) <br />
{ <br />
for(i=0;i&lt;4;i++) <br />
{ <br />
buf[i]=*(base64code+(base64b*4)+i); <br />
for(j=0;j&lt;65;j++) <br />
{ <br />
if(buf[i]==base64_alphabet[j]) <br />
{ <br />
temp1[i]=j; <br />
break; <br />
} <br />
} <br />
} <br />
i--; <br />
for(k=1;k&lt;4;k++) <br />
{ <br />
if(temp1[i-(k-1)]==64){m_padnum++; continue;} <br />
temp1[i-(k-1)]=temp1[i-(k-1)]/LMoveBit(2,(k-1)*2); <br />
temp2=temp1[i-k]; <br />
temp2=temp2&amp;(LMoveBit(2,k*2)-1); <br />
temp2*=LMoveBit(2,8-(2*k));//move 4 <br />
temp1[i-(k-1)]=temp1[i-(k-1)]+temp2; <br />
Buffer[base64b*3+(3-k)]=temp1[i-(k-1)]; <br />
} <br />
} <br />
return Buffer; <br />
} <br />
<br />
根据这段算法，文章最开始给出的Email内容，可以解码为： <br />
你好，SnaiX <br />
<br />
这是一个Base64的测试邮件！ <br />
<br />
Best Wishes! <br />
eSX?! <br />
<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#37;&#51;&#67;&#97;&#37;&#50;&#48;&#104;&#114;&#101;&#102;&#61;"><font color="#0000ff">snaix@yeah.net</font></a>'&gt;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#110;&#97;&#105;&#120;&#64;&#121;&#101;&#97;&#104;&#46;&#110;&#101;&#116;"><font color="#0000ff">snaix@yeah.net</font></a>'&gt;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#37;&#51;&#67;&#97;&#37;&#50;&#48;&#104;&#114;&#101;&#102;&#61;"><font color="#0000ff">snaix@yeah.net</font></a>'&gt;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#110;&#97;&#105;&#120;&#64;&#121;&#101;&#97;&#104;&#46;&#110;&#101;&#116;"><font color="#0000ff">snaix@yeah.net</font></a> <br />
2003-12-25 <br />
<br />
如文章有问题恳请指出并与我联系：<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#37;&#51;&#67;&#97;&#37;&#50;&#48;&#104;&#114;&#101;&#102;&#61;"><font color="#0000ff">snaix@126.com</font></a>'&gt;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#110;&#97;&#105;&#120;&#64;&#49;&#50;&#54;&#46;&#99;&#111;&#109;"><font color="#0000ff">snaix@126.com</font></a> <br />
<br />
<strong>主要参考资料：</strong> <br />
RFC2045 <br />
RFC2046 <br />
《奇妙的Base64编码》，罗聪 <br />
以及一些来自互联网上的其他资料 <br />
<img src ="http://www.blogjava.net/renyangok/aggbug/161606.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-11-19 14:30 <a href="http://www.blogjava.net/renyangok/archive/2007/11/19/161606.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>pythonchallenge参考答案</title><link>http://www.blogjava.net/renyangok/archive/2007/11/02/157804.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Fri, 02 Nov 2007 09:56:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/11/02/157804.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/157804.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/11/02/157804.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/157804.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/157804.html</trackback:ping><description><![CDATA[-------------------------------------------------------------------------------------------------------------------------------<br />
Level 7:&nbsp; http://www.pythonchallenge.com/pc/def/oxygen.html<br />
<p>The squares in the middle look suspicious so you think that the answer is encoded within it.
</p>
<p>Unfortunately, Python does not have a built-in Image library so
you'll have to search for an external module. I've chosen to use <a href="http://www.pythonware.com/products/pil/" class="external" title="http://www.pythonware.com/products/pil/" rel="nofollow">PIL</a><span class="urlexpansion">&nbsp;(<em>http://www.pythonware.com/products/pil/</em>)</span>, which I had pre-installed.
</p>
<pre>&gt;&gt;&gt; import Image, urllib, StringIO<br />
&gt;&gt;&gt; img = urllib.urlopen('http://www.pythonchallenge.com/pc/def/oxygen.png').read()<br />
&gt;&gt;&gt; i = Image.open(StringIO.StringIO(img))  # Image.open requires a file-like object<br />
&gt;&gt;&gt; i.size<br />
(629, 95)<br />
&gt;&gt;&gt; [i.getpixel((x,45)) for x in range(629)]<br />
[(115, 115, 115, 255), (115, 115, 115, 255), (115, 115, 115, 255), ...<br />
</pre>
<p>There seem to be a lot of data here, but it is noticable that the grey squares have R=G=B, and it has a period of 7 pixels.
</p>
<pre># Extract every 7th pixel from row 45.<br />
&gt;&gt;&gt; row = [i.getpixel((x, 45)) for x in range(0, i.size[0], 7)]<br />
# Take the R value from all with equal R, G, and B values.<br />
&gt;&gt;&gt; ords = [r for r, g, b, a in row if r == g == b]<br />
# And convert to characters.<br />
&gt;&gt;&gt; print "".join(map(chr, ords))<br />
smart guy, you made it. the next level is [105, 110, 116, 101, 103, 114, 105, 116, 121]<br />
</pre>
<p>Applying the hint:
</p>
<pre>&gt;&gt;&gt; "".join(map(chr,[105, 110, 116, 101, 103, 114, 105, 116, 121]))<br />
'integrity'<br />
<br />
-------------------------------------------------------------------------------------------------------------------------------<br />
<br />
Level 8: http://www.pythonchallenge.com/pc/def/integrity.html<br />
</pre>
<img src ="http://www.blogjava.net/renyangok/aggbug/157804.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-11-02 17:56 <a href="http://www.blogjava.net/renyangok/archive/2007/11/02/157804.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>方法和函数的区别（method &amp; function）</title><link>http://www.blogjava.net/renyangok/archive/2007/11/02/157759.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Fri, 02 Nov 2007 07:26:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/11/02/157759.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/157759.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/11/02/157759.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/157759.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/157759.html</trackback:ping><description><![CDATA[Not a whole lot.<br />
<br />
I believe the technical difference is that a function is not in a class, whereas a method is.<br />
<br />
You'll see method and member function used interchangeably which is
fine (a function that is a member of a class, get it? Correct term when
discussing C++). Function and method shouldn't be used interchangeably,
but often will be.<br />
<br />
I'd suggest you don't sweat it, they both represent blocks of code that do something and might return a value.
<img src ="http://www.blogjava.net/renyangok/aggbug/157759.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-11-02 15:26 <a href="http://www.blogjava.net/renyangok/archive/2007/11/02/157759.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux编程之序列化存储Python对象</title><link>http://www.blogjava.net/renyangok/archive/2007/11/02/157739.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Fri, 02 Nov 2007 06:10:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/11/02/157739.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/157739.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/11/02/157739.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/157739.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/157739.html</trackback:ping><description><![CDATA[<p style="text-indent: 2em;"><strong>(转自：</strong></p>
<p style="text-indent: 2em;"><strong>上——http://www.linuxdby.com/html/linux/program/20070412/1753.html<br />
</strong></p>
<p style="text-indent: 2em;"><strong>下——http://www.linuxdby.com/html/linux/program/20070412/1754.html)<br />
</strong></p>
<p style="text-indent: 2em;"><strong>什么是持久性？</strong>
</p>
<p style="text-indent: 2em;">持久性的基本思想很简单。假定有一个 Python
程序，它可能是一个管理日常待办事项的程序，您希望在多次执行这个程序之间可以保存应用程序对象（待办事项）。换句话说，您希望把对象存储在磁盘上，便于
以后检索。这就是持久性。要达到这个目的，有几种方法，每一种方法都有其优缺点。
</p>
<p style="text-indent: 2em;">例如，可以把对象数据存储在某种格式的文本文件中，譬如 CSV 文件。或者可以用关系数据库，譬如 Gadfly、MySQL、PostgreSQL 或者 DB2。这些文件格式和数据库都非常优秀，对于所有这些存储机制，Python 都有健壮的接口。
</p>
<p style="text-indent: 2em;">这些存储机制都有一个共同点：存储的数据是独立于对这些数据进行操作的对象和程序。
这样做的好处是，数据可以作为共享的资源，供其它应用程序使用。缺点是，用这种方式，可以允许其它程序访问对象的数据，这违背了面向对象的封装性原则
— 即对象的数据只能通过这个对象自身的公共（public）接口来访问。
</p>
<p style="text-indent: 2em;">另外，对于某些应用程序，关系数据库方法可能不是很理想。尤其是，关系数据库不理
解对象。相反，关系数据库会强行使用自己的类型系统和关系数据模型（表），每张表包含一组元组（行），每行包含具有固定数目的静态类型字段（列）。如果应
用程序的对象模型不能够方便地转换到关系模型，那么在把对象映射到元组以及把元组映射回对象方面，会碰到一定难度。这种困难常被称为阻碍性不匹配
（impedence-mismatch）问题。
</p>
<p style="text-indent: 2em;"><strong>对象持久性</strong>
</p>
<p style="text-indent: 2em;">如果希望透明地存储 Python
对象，而不丢失其身份和类型等信息，则需要某种形式的对象序列化：它是一个把任意复杂的对象转成对象的文本或二进制表示的过程。同样，必须能够把对象经过
序列化后的形式恢复到原有的对象。在 Python 中，这种序列化过程称为 pickle，可以把对象 pickle
成字符串、磁盘上的文件或者任何类似于文件的对象，也可以把这些字符串、文件或任何类似于文件的对象 unpickle
成原来的对象。我们把在本文后面详细讨论 pickle。
</p>
<p style="text-indent: 2em;">假定您喜欢把任何事物都保存成对象，而且希望避免把对象转换成某种基于非对象存储
的开销；那么 pickle 文件可以提供这些好处，但有时可能需要比这种简单的 pickle 文件更健壮以及更具有可伸缩性的事物。例如，只用
pickle 不能解决命名和查找 pickle 文件这样的问题，另外，它也不能支持并发地访问持久性对象。如果需要这些方面的功能，则要求助类似于
ZODB（针对 Python 的 Z 对象数据库）这类数据库。ZODB
是一个健壮的、多用户的和面向对象的数据库系统，它能够存储和管理任意复杂的 Python
对象，并支持事务操作和并发控制。（请参阅参考资料，以下载 ZODB。）令人足够感兴趣的是，甚至 ZODB 也依靠 Python
的本机序列化能力，而且要有效地使用 ZODB，必须充分了解 pickle。
</p>
<p style="text-indent: 2em;">另一种令人感兴趣的解决持久性问题的方法是 Prevayler，它最初是用
Java 实现的（有关 Prevaylor 方面的 developerWorks 文章，请参阅参考资料）。最近，一群 Python 程序员把
Prevayler 移植到了 Python 上，另起名为 PyPerSyst，由 SourceForge 托管（有关至 PyPerSyst
项目的链接，请参阅参考资料）。Prevayler/PyPerSyst 概念也是建立在 Java 和 Python
语言的本机序列化能力之上。PyPerSyst 把整个对象系统保存在内存中，并通过不时地把系统快照 pickle
到磁盘以及维护一个命令日志（通过此日志可以重新应用最新的快照）来提供灾难恢复。所以，尽管使用 PyPerSyst
的应用程序受到可用内存的限制，但好处是本机对象系统可以完全装入到内存中，因而速度极快，而且实现起来要比如 ZODB 这样的数据库简单，ZODB
允许对象的数目比同时在能内存中所保持的对象要多。
</p>
既然我们已经简要讨论了存储持久对象的各种方法，那么现在该详细探讨
pickle 过程了。虽然我们主要感兴趣的是探索以各种方式来保存 Python
对象，而不必把其转换成某种其它格式，但我们仍然还有一些需要关注的地方，譬如：怎么样有效地 pickle 和 unpickle
简单对象以及复杂对象，包括定制类的实例；怎么样维护对象的引用，包括循环引用和递归引用；以及怎么样处理类定义发生的变化，从而使用以前经过
pickle 的实例时不会发生问题。我们把在随后关于 Python 的 pickle 能力探讨中涉及所有这些问题。<br />
<p style="text-indent: 2em;"><strong>一些经过 pickle 的 Python</strong>
</p>
<p style="text-indent: 2em;">pickle 模块及其同类模块 cPickle 向 Python 提供了
pickle 支持。后者是用 C 编码的，它具有更好的性能，对于大多数应用程序，推荐使用该模块。我们把继续讨论
pickle，但本文的示例实际是利用了 cPickle。由于其中大多数示例要用 Python shell 来显示，所以先展示一下怎么样导入
cPickle，并可以作为 pickle 来引用它：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>  &gt;&gt;&gt; import cPickle as pickle</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<p style="text-indent: 2em;">现在已经导入了该模块，接下来让我们看一下 pickle 接口。pickle
模块提供了以下函数对：dumps(object) 返回一个字符串，它包含一个 pickle 格式的对象；loads(string) 返回包含在
pickle 字符串中的对象；dump(object, file)
把对象写到文件，这个文件可以是实际的物理文件，但也可以是任何类似于文件的对象，这个对象具有 write()
方法，可以接受单个的字符串参数；load(file) 返回包含在 pickle 文件中的对象。
</p>
<p style="text-indent: 2em;">缺省情况下，dumps() 和 dump() 使用可打印的 ASCII
表示来创建 pickle。两者都有一个 final 参数（可选），如果为 True，则该参数指定用更快以及更小的二进制表示来创建
pickle。loads() 和 load() 函数自动检测 pickle 是二进制格式还是文本格式。
</p>
<p style="text-indent: 2em;">清单 1 显示了一个交互式会话，这里使用了刚才所描述的 dumps() 和 loads() 函数：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>  Welcome To PyCrust 0.7.2 - The Flakiest Python Shell<br />
            Sponsored by Orbtech - Your source for Python programming expertise.<br />
            Python 2.2.1 (#1, Aug 27 2002, 10:22:32)<br />
            [GCC 3.2 (Mandrake Linux 9.0 3.2-1mdk)] on linux-i386<br />
            Type "copyright", "credits" or "license" for more information.<br />
            &gt;&gt;&gt; import cPickle as pickle<br />
            &gt;&gt;&gt; t1 = ('this is a string', 42, [1, 2, 3], None)<br />
            &gt;&gt;&gt; t1<br />
            ('this is a string', 42, [1, 2, 3], None)<br />
            &gt;&gt;&gt; p1 = pickle.dumps(t1)<br />
            &gt;&gt;&gt; p1<br />
            "(S'this is a string'\nI42\n(lp1\nI1\naI2\naI3\naNtp2\n."<br />
            &gt;&gt;&gt; print p1<br />
            (S'this is a string'<br />
            I42<br />
            (lp1<br />
            I1<br />
            aI2<br />
            aI3<br />
            aNtp2<br />
            .<br />
            &gt;&gt;&gt; t2 = pickle.loads(p1)<br />
            &gt;&gt;&gt; t2<br />
            ('this is a string', 42, [1, 2, 3], None)<br />
            &gt;&gt;&gt; p2 = pickle.dumps(t1, True)<br />
            &gt;&gt;&gt; p2<br />
            '(U\x10this is a stringK*]q\x01(K\x01K\x02K\x03eNtq\x02.'<br />
            &gt;&gt;&gt; t3 = pickle.loads(p2)<br />
            &gt;&gt;&gt; t3<br />
            ('this is a string', 42, [1, 2, 3], None)</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 1. dumps() 和 loads() 的演示</center>
<p style="text-indent: 2em;">注：该文本 pickle 格式很简单，这里就不解释了。事实上，在 pickle
模块中记录了所有使用的约定。我们还应该指出，在我们的示例中使用的都是简单对象，因此使用二进制 pickle
格式不会在节省空间上显示出太大的效率。然而，在实际使用复杂对象的系统中，您会看到，使用二进制格式可以在大小和速度方面带来显著的改进。
</p>
<p style="text-indent: 2em;">接下来，我们看一些示例，这些示例用到了 dump() 和
load()，它们使用文件和类似文件的对象。这些函数的操作非常类似于我们刚才所看到的 dumps() 和
loads()，区别在于它们还有另一种能力 — dump() 函数能一个接着一个地把几个对象转储到同一个文件。随后调用 load()
来以同样的顺序检索这些对象。清单 2 显示了这种能力的实际应用：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>  &gt;&gt;&gt; a1 = 'apple'<br />
            &gt;&gt;&gt; b1 = {1: 'One', 2: 'Two', 3: 'Three'}<br />
            &gt;&gt;&gt; c1 = ['fee', 'fie', 'foe', 'fum']<br />
            &gt;&gt;&gt; f1 = file('temp.pkl', 'wb')<br />
            &gt;&gt;&gt; pickle.dump(a1, f1, True)<br />
            &gt;&gt;&gt; pickle.dump(b1, f1, True)<br />
            &gt;&gt;&gt; pickle.dump(c1, f1, True)<br />
            &gt;&gt;&gt; f1.close()<br />
            &gt;&gt;&gt; f2 = file('temp.pkl', 'rb')<br />
            &gt;&gt;&gt; a2 = pickle.load(f2)<br />
            &gt;&gt;&gt; a2<br />
            'apple'<br />
            &gt;&gt;&gt; b2 = pickle.load(f2)<br />
            &gt;&gt;&gt; b2<br />
            {1: 'One', 2: 'Two', 3: 'Three'}<br />
            &gt;&gt;&gt; c2 = pickle.load(f2)<br />
            &gt;&gt;&gt; c2<br />
            ['fee', 'fie', 'foe', 'fum']<br />
            &gt;&gt;&gt; f2.close()<br />
            </ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 2. dump() 和 load() 示例<br />
</center><br />
<p style="text-indent: 2em;"><strong>Pickle 的威力</strong>
</p>
<p style="text-indent: 2em;">到目前为止，我们讲述了关于 pickle 方面的基本知识。在这一节，把讨论一些高级问题，当您开始 pickle 复杂对象时，会遇到这些问题，其中包括定制类的实例。幸运的是，Python 可以很容易地处理这种情形。
</p>
<p style="text-indent: 2em;"><strong>可移植性</strong>
</p>
<p style="text-indent: 2em;">从空间和时间上说，Pickle 是可移植的。换句话说，pickle
文件格式独立于机器的体系结构，这意味着，例如，可以在 Linux 下创建一个 pickle，然后把它发送到在 Windows 或 Mac OS
下运行的 Python 程序。并且，当升级到更新版本的 Python 时，不必担心可能要废弃已有的 pickle。Python
开发人员已经保证 pickle 格式把可以向后兼容 Python 各个版本。事实上，在 pickle
模块中提供了有关目前以及所支持的格式方面的详细信息：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>  &gt;&gt;&gt; pickle.format_version<br />
            '1.3'<br />
            &gt;&gt;&gt; pickle.compatible_formats<br />
            ['1.0', '1.1', '1.2']</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 3. 检索所支持的格式</center>
<p style="text-indent: 2em;"><strong>多个引用，同一对象</strong>
</p>
<p style="text-indent: 2em;">在 Python 中，变量是对象的引用。同时，也可以用多个变量引用同一个对象。经证明，Python 在用经过 pickle 的对象维护这种行为方面丝毫没有困难，如清单 4 所示：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>  &gt;&gt;&gt; a = [1, 2, 3]<br />
            &gt;&gt;&gt; b = a<br />
            &gt;&gt;&gt; a<br />
            [1, 2, 3]<br />
            &gt;&gt;&gt; b<br />
            [1, 2, 3]<br />
            &gt;&gt;&gt; a.append(4)<br />
            &gt;&gt;&gt; a<br />
            [1, 2, 3, 4]<br />
            &gt;&gt;&gt; b<br />
            [1, 2, 3, 4]<br />
            &gt;&gt;&gt; c = pickle.dumps((a, b))<br />
            &gt;&gt;&gt; d, e = pickle.loads(c)<br />
            &gt;&gt;&gt; d<br />
            [1, 2, 3, 4]<br />
            &gt;&gt;&gt; e<br />
            [1, 2, 3, 4]<br />
            &gt;&gt;&gt; d.append(5)<br />
            &gt;&gt;&gt; d<br />
            [1, 2, 3, 4, 5]<br />
            &gt;&gt;&gt; e<br />
            [1, 2, 3, 4, 5]</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 4. 对象引用的维护</center>
<p style="text-indent: 2em;"><strong>循环引用和递归引用</strong>
</p>
<p style="text-indent: 2em;">可以把刚才演示过的对象引用支持扩展到循环引用（两个对象各自包含对对方的引用）和递归引用（一个对象包含对其自身的引用）。下面两个清单着重显示这种能力。我们先看一下递归引用：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>  &gt;&gt;&gt; l = [1, 2, 3]<br />
            &gt;&gt;&gt; l.append(l)<br />
            &gt;&gt;&gt; l<br />
            [1, 2, 3, [...]]<br />
            &gt;&gt;&gt; l[3]<br />
            [1, 2, 3, [...]]<br />
            &gt;&gt;&gt; l[3][3]<br />
            [1, 2, 3, [...]]<br />
            &gt;&gt;&gt; p = pickle.dumps(l)<br />
            &gt;&gt;&gt; l2 = pickle.loads(p)<br />
            &gt;&gt;&gt; l2<br />
            [1, 2, 3, [...]]<br />
            &gt;&gt;&gt; l2[3]<br />
            [1, 2, 3, [...]]<br />
            &gt;&gt;&gt; l2[3][3]<br />
            [1, 2, 3, [...]]</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 5. 递归引用</center>
<p style="text-indent: 2em;">现在，看一个循环引用的示例：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>  &gt;&gt;&gt; a = [1, 2]<br />
            &gt;&gt;&gt; b = [3, 4]<br />
            &gt;&gt;&gt; a.append(b)<br />
            &gt;&gt;&gt; a<br />
            [1, 2, [3, 4]]<br />
            &gt;&gt;&gt; b.append(a)<br />
            &gt;&gt;&gt; a<br />
            [1, 2, [3, 4, [...]]]<br />
            &gt;&gt;&gt; b<br />
            [3, 4, [1, 2, [...]]]<br />
            &gt;&gt;&gt; a[2]<br />
            [3, 4, [1, 2, [...]]]<br />
            &gt;&gt;&gt; b[2]<br />
            [1, 2, [3, 4, [...]]]<br />
            &gt;&gt;&gt; a[2] is b<br />
            1<br />
            &gt;&gt;&gt; b[2] is a<br />
            1<br />
            &gt;&gt;&gt; f = file('temp.pkl', 'w')<br />
            &gt;&gt;&gt; pickle.dump((a, b), f)<br />
            &gt;&gt;&gt; f.close()<br />
            &gt;&gt;&gt; f = file('temp.pkl', 'r')<br />
            &gt;&gt;&gt; c, d = pickle.load(f)<br />
            &gt;&gt;&gt; f.close()<br />
            &gt;&gt;&gt; c<br />
            [1, 2, [3, 4, [...]]]<br />
            &gt;&gt;&gt; d<br />
            [3, 4, [1, 2, [...]]]<br />
            &gt;&gt;&gt; c[2]<br />
            [3, 4, [1, 2, [...]]]<br />
            &gt;&gt;&gt; d[2]<br />
            [1, 2, [3, 4, [...]]]<br />
            &gt;&gt;&gt; c[2] is d<br />
            1<br />
            &gt;&gt;&gt; d[2] is c<br />
            1</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 6. 循环引用</center>
<p style="text-indent: 2em;">注意，如果分别 pickle 每个对象，而不是在一个元组中一起 pickle 所有对象，会得到略微不同（但很重要）的结果，如清单 7 所示：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>  &gt;&gt;&gt; f = file('temp.pkl', 'w')<br />
            &gt;&gt;&gt; pickle.dump(a, f)<br />
            &gt;&gt;&gt; pickle.dump(b, f)<br />
            &gt;&gt;&gt; f.close()<br />
            &gt;&gt;&gt; f = file('temp.pkl', 'r')<br />
            &gt;&gt;&gt; c = pickle.load(f)<br />
            &gt;&gt;&gt; d = pickle.load(f)<br />
            &gt;&gt;&gt; f.close()<br />
            &gt;&gt;&gt; c<br />
            [1, 2, [3, 4, [...]]]<br />
            &gt;&gt;&gt; d<br />
            [3, 4, [1, 2, [...]]]<br />
            &gt;&gt;&gt; c[2]<br />
            [3, 4, [1, 2, [...]]]<br />
            &gt;&gt;&gt; d[2]<br />
            [1, 2, [3, 4, [...]]]<br />
            &gt;&gt;&gt; c[2] is d<br />
            0<br />
            &gt;&gt;&gt; d[2] is c<br />
            0</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 7. 分别 pickle vs. 在一个元组中一起 pickle</center>
<br />
<p style="text-indent: 2em;"><strong>相等，但并不总是相同</strong>
</p>
<p style="text-indent: 2em;">正如在上一个示例所暗示的，只有在这些对象引用内存中同一个对象时，它们才是相同的。在 pickle 情形中，每个对象被恢复到一个与原来对象相等的对象，但不是同一个对象。换句话说，每个 pickle 都是原来对象的一个副本：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>  &gt;&gt;&gt; j = [1, 2, 3]<br />
            &gt;&gt;&gt; k = j<br />
            &gt;&gt;&gt; k is j<br />
            1<br />
            &gt;&gt;&gt; x = pickle.dumps(k)<br />
            &gt;&gt;&gt; y = pickle.loads(x)<br />
            &gt;&gt;&gt; y<br />
            [1, 2, 3]<br />
            &gt;&gt;&gt; y == k<br />
            1<br />
            &gt;&gt;&gt; y is k<br />
            0<br />
            &gt;&gt;&gt; y is j<br />
            0<br />
            &gt;&gt;&gt; k is j<br />
            1</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 8. 作为原来对象副本的被恢复的对象</center>
<p style="text-indent: 2em;">同时，我们看到 Python 能够维护对象之间的引用，这些对象是作为一个单元进行
pickle 的。然而，我们还看到分别调用 dump() 会使 Python 无法维护对在该单元外部进行 pickle
的对象的引用。相反，Python 复制了被引用对象，并把副本和被 pickle 的对象存储在一起。对于 pickle
和恢复单个对象层次结构的应用程序，这是没有问题的。但要意识到还有其它情形。
</p>
<p style="text-indent: 2em;">值得指出的是，有一个选项确实允许分别 pickle
对象，并维护相互之间的引用，只要这些对象都是 pickle 到同一文件即可。pickle 和 cPickle 模块提供了一个
Pickler（与此相对应是 Unpickler），它能够跟踪已经被 pickle 的对象。通过使用这个
Pickler，把会通过引用而不是通过值来 pickle 共享和循环引用：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>  &gt;&gt;&gt; f = file('temp.pkl', 'w')<br />
            &gt;&gt;&gt; pickler = pickle.Pickler(f)<br />
            &gt;&gt;&gt; pickler.dump(a)<br />
            <br />
            &gt;&gt;&gt; pickler.dump(b)<br />
            <br />
            &gt;&gt;&gt; f.close()<br />
            &gt;&gt;&gt; f = file('temp.pkl', 'r')<br />
            &gt;&gt;&gt; unpickler = pickle.Unpickler(f)<br />
            &gt;&gt;&gt; c = unpickler.load()<br />
            &gt;&gt;&gt; d = unpickler.load()<br />
            &gt;&gt;&gt; c[2]<br />
            [3, 4, [1, 2, [...]]]<br />
            &gt;&gt;&gt; d[2]<br />
            [1, 2, [3, 4, [...]]]<br />
            &gt;&gt;&gt; c[2] is d<br />
            1<br />
            &gt;&gt;&gt; d[2] is c<br />
            1</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 9. 维护分别 pickle 的对象间的引用</center>
<p style="text-indent: 2em;"><strong>不可 pickle 的对象</strong>
</p>
<p style="text-indent: 2em;">一些对象类型是不可 pickle 的。例如，Python 不能 pickle
文件对象（或者任何带有对文件对象引用的对象），因为 Python 在 unpickle
时不能保证它可以重建该文件的状态（另一个示例比较难懂，在这类文章中不值得提出来）。试图 pickle 文件对象会导致以下错误：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>  &gt;&gt;&gt; f = file('temp.pkl', 'w')<br />
            &gt;&gt;&gt; p = pickle.dumps(f)<br />
            Traceback (most recent call last):<br />
            File "", line 1, in ?<br />
            File "/usr/lib/python2.2/copy_reg.py", line 57, in _reduce<br />
            raise TypeError, "can't pickle ％s objects" ％ base.__name__<br />
            TypeError: can't pickle file objects</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 10. 试图 pickle 文件对象的结果<br />
<div align="left">
<p style="text-indent: 2em;"><strong>类实例</strong>
</p>
<p style="text-indent: 2em;">与 pickle 简单对象类型相比，pickle
类实例要多加留意。这主要由于 Python 会 pickle 实例数据（通常是 _dict_ 属性）和类的名称，而不会 pickle
类的代码。当 Python unpickle 类的实例时，它会试图使用在 pickle
该实例时的确切的类名称和模块名称（包括任何包的路径前缀）导入包含该类定义的模块。另外要注意，类定义必须出现在模块的最顶层，这意味着它们不能是嵌套
的类（在其它类或函数中定义的类）。
</p>
<p style="text-indent: 2em;">当 unpickle 类的实例时，通常不会再调用它们的 _init_() 方法。相反，Python 创建一个通用类实例，并应用已进行过 pickle 的实例属性，同时设置该实例的 _class_ 属性，使其指向原来的类。
</p>
<p style="text-indent: 2em;">对 Python 2.2 中引入的新型类进行 unpickle 的机制与原来的略有不同。虽然处理的结果实际上与对旧型类处理的结果相同，但 Python 使用 copy_reg 模块的 _reconstructor() 函数来恢复新型类的实例。
</p>
<p style="text-indent: 2em;">如果希望对新型或旧型类的实例修改缺省的 pickle
行为，则可以定义特殊的类的方法 _getstate_() 和 _setstate_()，在保存和恢复类实例的状态信息期间，Python
会调用这些方法。在以下几节中，我们会看到一些示例利用了这些特殊的方法。
</p>
<p style="text-indent: 2em;">现在，我们看一个简单的类实例。首先，创建一个 persist.py 的 Python 模块，它包含以下新型类的定义：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>　　class Foo(object):<br />
            <br />
            def __init__(self, value):<br />
            self.value = value</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 11. 新型类的定义</center>
<p style="text-indent: 2em;">现在可以 pickle Foo 实例，并看一下它的表示：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>　　&gt;&gt;&gt; import cPickle as pickle<br />
            &gt;&gt;&gt; from Orbtech.examples.persist import Foo<br />
            &gt;&gt;&gt; foo = Foo('What is a Foo?')<br />
            &gt;&gt;&gt; p = pickle.dumps(foo)<br />
            &gt;&gt;&gt; print p<br />
            ccopy_reg<br />
            _reconstructor<br />
            p1<br />
            (cOrbtech.examples.persist<br />
            Foo<br />
            p2<br />
            c__builtin__<br />
            object<br />
            p3<br />
            NtRp4<br />
            (dp5<br />
            S'value'<br />
            p6<br />
            S'What is a Foo?'<br />
            sb.<br />
            &gt;&gt;&gt;</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 12. pickle Foo 实例</center>
<p style="text-indent: 2em;">可以看到这个类的名称 Foo 和全限定的模块名称
Orbtech.examples.persist 都存储在 pickle 中。如果把这个实例 pickle 成一个文件，稍后再
unpickle 它或在另一台机器上 unpickle，则 Python 会试图导入 Orbtech.examples.persist
模块，如果不能导入，则会抛出异常。如果重命名该类和该模块或者把该模块移到另一个目录，则也会发生类似的错误。
</p>
<p style="text-indent: 2em;">这里有一个 Python 发出错误消息的示例，当我们重命名 Foo 类，然后试图装入先前进行过 pickle 的 Foo 实例时会发生该错误：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>　　&gt;&gt;&gt; import cPickle as pickle<br />
            &gt;&gt;&gt; f = file('temp.pkl', 'r')<br />
            &gt;&gt;&gt; foo = pickle.load(f)<br />
            Traceback (most recent call last):<br />
            File "", line 1, in ?<br />
            AttributeError: 'module' object has no attribute 'Foo'</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 13. 试图装入一个被重命名的 Foo 类的经过 pickle 的实例</center>
<p style="text-indent: 2em;">在重命名 persist.py 模块之后，也会发生类似的错误：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>　　&gt;&gt;&gt; import cPickle as pickle<br />
            &gt;&gt;&gt; f = file('temp.pkl', 'r')<br />
            &gt;&gt;&gt; foo = pickle.load(f)<br />
            Traceback (most recent call last):<br />
            File "", line 1, in ?<br />
            ImportError: No module named persist</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 14. 试图装入一个被重命名的 persist.py 模块的经过 pickle 的实例</center>
我们会在下面模式改进这一节提供一些技术来管理这类更改，而不会破坏现有的 pickle。<br />
<p style="text-indent: 2em;"><strong>特殊的状态方法</strong>
</p>
<p style="text-indent: 2em;">前面提到对一些对象类型（譬如，文件对象）不能进行 pickle。处理这种不能
pickle 的对象的实例属性时可以使用特殊的方法（_getstate_() 和 _setstate_()）来修改类实例的状态。这里有一个
Foo 类的示例，我们已经对它进行了修改以处理文件对象属性：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>　　class Foo(object):<br />
            <br />
            def __init__(self, value, filename):<br />
            self.value = value<br />
            self.logfile = file(filename, 'w')<br />
            <br />
            def __getstate__(self):<br />
            """Return state values to be pickled."""<br />
            f = self.logfile<br />
            return (self.value, f.name, f.tell())<br />
            <br />
            def __setstate__(self, state):<br />
            """Restore state from the unpickled state values."""<br />
            self.value, name, position = state<br />
            f = file(name, 'w')<br />
            f.seek(position)<br />
            self.logfile = f</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 15. 处理不能 pickle 的实例属性</center>
<p style="text-indent: 2em;">pickle Foo 的实例时，Python 把只 pickle 当它调用该实例的
_getstate_() 方法时返回给它的值。类似的，在 unpickle 时，Python 把提供经过 unpickle
的值作为参数传递给实例的 _setstate_() 方法。在 _setstate_() 方法内，可以根据经过 pickle
的名称和位置信息来重建文件对象，并把该文件对象分配给这个实例的 logfile 属性。
</p>
<p style="text-indent: 2em;"><strong>模式改进</strong>
</p>
<p style="text-indent: 2em;">随着时间的推移，您会发现自己必须要更改类的定义。如果已经对某个类实例进行了
pickle，而现在又需要更改这个类，则您可能要检索和更新那些实例，以便它们能在新的类定义下继续正常工作。而我们已经看到在对类或模块进行某些更改
时，会出现一些错误。幸运的是，pickle 和 unpickle 过程提供了一些 hook，我们可以用它们来支持这种模式改进的需要。
</p>
在这一节，我们把探讨一些方法来预测常见问题以及怎么样解决这些问题。由于不能
pickle 类实例代码，因此可以添加、更改和除去方法，而不会影响现有的经过 pickle
的实例。出于同样的原因，可以不必担心类的属性。您必须确保包含类定义的代码模块在 unpickle 环境中可用。同时还必须为这些可能导致
unpickle 问题的更改做好规划，这些更改包括：更改类名、添加或除去实例的属性以及改变类定义模块的名称或位置。 <br />
<p style="text-indent: 2em;"><strong>类名的更改</strong>
</p>
<p style="text-indent: 2em;">要更改类名，而不破坏先前经过 pickle
的实例，请遵循以下步骤。首先，确保原来的类的定义没有被更改，以便在 unpickle
现有实例时可以找到它。不要更改原来的名称，而是在与原来类定义所在的同一个模块中，创建该类定义的一个副本，同时给它一个新的类名。然后使用实际的新类
名来替代 NewClassName，把以下方法添加到原来类的定义中：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>　　def __setstate__(self, state):<br />
            self.__dict__.update(state)<br />
            self.__class__ = NewClassName</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 16. 更改类名：添加到原来类定义的方法</center>
<p style="text-indent: 2em;">当 unpickle 现有实例时，Python 把查找原来类的定义，并调用实例的
_setstate_() 方法，同时把给新的类定义重新分配该实例的 _class_ 属性。一旦确定所有现有的实例都已经
unpickle、更新和重新 pickle 后，可以从源代码模块中除去旧的类定义。
</p>
<p style="text-indent: 2em;"><strong>属性的添加和删除</strong>
</p>
<p style="text-indent: 2em;">这些特殊的状态方法 _getstate_() 和 _setstate_() 再一次使我们能控制每个实例的状态，并使我们有机会处理实例属性中的更改。让我们看一个简单的类的定义，我们把向其添加和除去一些属性。这是是最初的定义：
</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>　class Person(object):<br />
            <br />
            def __init__(self, firstname, lastname):<br />
            self.firstname = firstname<br />
            self.lastname = lastname</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 17. 最初的类定义</center>
<p style="text-indent: 2em;">假定已经创建并 pickle 了 Person 的实例，现在我们决定真的只想存储一个名称属性，而不是分别存储姓和名。这里有一种方式可以更改类的定义，它把先前经过 pickle 的实例迁移到新的定义：
</p>
<p style="text-indent: 2em;">　</p>
<center><ccid_nobr>
</ccid_nobr>
<table bordercolorlight="black" bordercolordark="#FFFFFF" align="center" border="1" cellpadding="2" cellspacing="0" width="400">
    <tbody>
        <tr>
            <td class="code" style="font-size: 9pt;" bgcolor="#e6e6e6">
            <pre><ccid_code>　　class Person(object):<br />
            <br />
            def __init__(self, fullname):<br />
            self.fullname = fullname<br />
            <br />
            def __setstate__(self, state):<br />
            if 'fullname' not in state:<br />
            first = '<br />
            last = '<br />
            if 'firstname' in state:<br />
            first = state['firstname']<br />
            del state['firstname']<br />
            if 'lastname' in state:<br />
            last = state['lastname']<br />
            del state['lastname']<br />
            self.fullname = " ".join([first, last]).strip()<br />
            self.__dict__.update(state)</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</center>
<center>清单 18. 新的类定义</center>
<p style="text-indent: 2em;">在这个示例，我们添加了一个新的属性 fullname，并除去了两个现有的属性
firstname 和 lastname。当对先前进行过 pickle 的实例执行 unpickle 时，其先前进行过 pickle
的状态会作为字典传递给 _setstate_()，它把包括 firstname 和 lastname
属性的值。接下来，把这两个值组合起来，并把它们分配给新属性 fullname。在这个过程中，我们删除了状态字典中旧的属性。更新和重新
pickle 先前进行过 pickle 的所有实例之后，现在可以从类定义中除去 _setstate_() 方法。
</p>
<p style="text-indent: 2em;"><strong>模块的修改</strong>
</p>
<p style="text-indent: 2em;">在概念上，模块的名称或位置的改变类似于类名称的改变，但处理方式却完全不同。那是
因为模块的信息存储在 pickle 中，而不是通过标准的 pickle 接口就可以修改的属性。事实上，改变模块信息的唯一办法是对实际的
pickle
文件本身执行查找和替换操作。至于怎么样确切地去做，这取决于具体的操作系统和可使用的工具。很显然，在这种情况下，您会想备份您的文件，以免发生错误。
但这种改动应该非常简单，并且对二进制 pickle 格式进行更改与对文本 pickle 格式进行更改应该一样有效。
</p>
<p style="text-indent: 2em;"><strong>结束语</strong>
</p>
<p style="text-indent: 2em;">对象持久性依赖于底层编程语言的对象序列化能力。对于 Python
对象即意味着 pickle。Python 的 pickle 为 Python
对象有效的持久性管理提供了健壮的和可靠的基础。在下面的参考资料中，您把会找到有关建立在 Python pickle 能力之上的系统的信息。
</p>
<br />
</div>
</center>
<img src ="http://www.blogjava.net/renyangok/aggbug/157739.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-11-02 14:10 <a href="http://www.blogjava.net/renyangok/archive/2007/11/02/157739.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>python学习笔记</title><link>http://www.blogjava.net/renyangok/archive/2007/09/02/142080.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Sun, 02 Sep 2007 08:18:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/09/02/142080.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/142080.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/09/02/142080.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/142080.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/142080.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Python基础篇整理：Jims of 肥肥世家&lt;jims.yang@gmail.com&gt;Copyright &#169; 2004，2005，2006 本文遵从GNU 的自由文档许可证(Free Document License)的条款，欢迎转载、修改、散布。发布时间：2004年07月10日更新时间：20...&nbsp;&nbsp;<a href='http://www.blogjava.net/renyangok/archive/2007/09/02/142080.html'>阅读全文</a><img src ="http://www.blogjava.net/renyangok/aggbug/142080.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-09-02 16:18 <a href="http://www.blogjava.net/renyangok/archive/2007/09/02/142080.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>python中的unittest实例</title><link>http://www.blogjava.net/renyangok/archive/2007/06/20/125259.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Wed, 20 Jun 2007 02:38:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/06/20/125259.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/125259.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/06/20/125259.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/125259.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/125259.html</trackback:ping><description><![CDATA[test 1:<br><br>import unittest<br><br>def suite():<br>&nbsp;&nbsp;&nbsp; suite = unittest.TestSuite()<br>&nbsp;&nbsp;&nbsp; suite.addTest(JustinTest('test1'))<br>&nbsp;&nbsp;&nbsp; suite.addTest(JustinTest('test2'))<br>&nbsp;&nbsp;&nbsp; return suite<br><br>class JustinTest(unittest.TestCase):<br>&nbsp;&nbsp;&nbsp; def setUp(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print 'setUp'<br><br>&nbsp;&nbsp;&nbsp; def tearDown(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print 'tearDown'<br><br>&nbsp;&nbsp;&nbsp; def test1(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print 'test1'<br><br>&nbsp;&nbsp;&nbsp; def test2(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print 'test2'<br><br>if __name__ == '__main__':<br>&nbsp;&nbsp;&nbsp; unittest.main(defaultTest='suite')<br><br>____________________________________________________________________________________<br>test 2:<br><br>import unittest<br><br>class JustinTest(unittest.TestCase):<br>&nbsp;&nbsp;&nbsp; def setUp(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print 'setUp'<br><br>&nbsp;&nbsp;&nbsp; def tearDown(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print 'tearDown'<br><br>&nbsp;&nbsp;&nbsp; def test1(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print 'test1'<br><br>&nbsp;&nbsp;&nbsp; def test2(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print 'test2'<br><br>class JustinTest2(unittest.TestCase):<br>&nbsp;&nbsp;&nbsp; def setUp(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print '2setUp'<br><br>&nbsp;&nbsp;&nbsp; def tearDown(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print '2tearDown'<br><br>&nbsp;&nbsp;&nbsp; def test1(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print '2test1'<br><br>&nbsp;&nbsp;&nbsp; def test2(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print '2test2'<br><br>if __name__ == '__main__':<br>&nbsp;&nbsp;&nbsp; unittest.main()<br> <img src ="http://www.blogjava.net/renyangok/aggbug/125259.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-06-20 10:38 <a href="http://www.blogjava.net/renyangok/archive/2007/06/20/125259.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Exoweb's study plan</title><link>http://www.blogjava.net/renyangok/archive/2007/04/18/111676.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Wed, 18 Apr 2007 08:49:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/04/18/111676.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/111676.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/04/18/111676.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/111676.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/111676.html</trackback:ping><description><![CDATA[step1<br><br>Python<br>&nbsp;&nbsp; http://docs.python.org/tut/tut.html<br>&nbsp;&nbsp; [http://www.woodpecker.org.cn/diveintopython/toc/index.html(dive into python Chinese Edition)]<br>&nbsp;&nbsp; http://www.python.org/doc/2.3.5/lib/lib.html(Python lib: sections 1, 4, 5.3, 11.4, 11.5, 11.6)<br>&nbsp;&nbsp; http://www.cublog.cn/u/12592/showart.php?id=143640(regular expression)<br>&nbsp;&nbsp; [http://wiki.woodpecker.org.cn/moin/PythonCodingRule(Python coding rule)]<br><br>Agile development<br>&nbsp;&nbsp; http://agilemanifesto.org/principles.html<br>&nbsp;&nbsp; http://www.martinfowler.com/articles/newMethodology.html<br>&nbsp;&nbsp; [http://www.aka.org.cn/Docs/latest/newMethodology_Chinese.html#N301]<br>&nbsp;&nbsp; http://www.controlchaos.com/<br><br>Linux<br>&nbsp;&nbsp; http://www.debian.org/doc/manuals/users-guide/users-guide.en.html<br>&nbsp;&nbsp; http://www.debian.org/doc/manuals/reference/reference.en.html(chapters 1-11)<br>&nbsp;&nbsp; http://www.debian.org/doc/manuals/apt-howto/index.en.html<br>&nbsp;&nbsp; [http://ukdebian.mirror.anlx.net/doc/user-manuals.zh-cn.html(Chinese Edition)]<br>&nbsp;&nbsp; [http://vcd.gro.clinux.org/(vim document Chinese Edition)]<br><br>Postgresql<br>&nbsp;&nbsp; http://www.postgresql.org/docs/8.1/static/index.html (chapters 1-3)<br>&nbsp;&nbsp; [http://www.itworld21.com/docs/db/postgresql8.1/(Chinese Edition)]<br><br>HTML/CSShttp://www.china-pub.com/computers/common/info.asp?id=32815<br>&nbsp;&nbsp; http://www.w3schools.com/html/ http://www.w3schools.com/css/(html and css)<br>&nbsp;&nbsp; http://www.sitepronews.com/archives/2005/july/20.html(The 10 Best Resources for CSS)<br>&nbsp;&nbsp; http://www.csszengarden.com (try implementing one of their designs)<br><br>Subversion<br>&nbsp;&nbsp; http://svnbook.red-bean.com/ (chapters 1-4) <br><br>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br>step 2<br><br>Twisted (version 1.3 for now)<br>&nbsp;http://twistedmatrix.com/trac/<br><br>Clearsilver (templating system)<br>&nbsp;http://www.clearsilver.net/<br><br>Trac (task/bug tracking system)<br>&nbsp;http://www.edgewall.com/trac/<br><br>For twisted, we use only the http portions (it is our webserver base)&nbsp; and use the threaded model in most parts, except for the newest&nbsp; portions, which work asynchronously (twisted's greatest strength).<br><br>Once you feel familiar with these technologies, feel free to&nbsp; check out the source code for the Nordicbet project and install it on&nbsp; your system. Familiarize yourself with the system and when you are&nbsp; ready, with your mentor's assistance, pick a small task from trac to&nbsp; work on. <br>     <img src ="http://www.blogjava.net/renyangok/aggbug/111676.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-04-18 16:49 <a href="http://www.blogjava.net/renyangok/archive/2007/04/18/111676.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《Twisted网络编程必备》翻译1－4章</title><link>http://www.blogjava.net/renyangok/archive/2007/04/18/111631.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Wed, 18 Apr 2007 06:38:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/04/18/111631.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/111631.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/04/18/111631.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/111631.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/111631.html</trackback:ping><description><![CDATA[感谢翻译者<br><br>前言：http://blog.csdn.net/gashero/archive/2007/01/19/1487463.aspx<br>第0章：http://blog.csdn.net/gashero/archive/2007/01/19/1487630.aspx<br>第1章：http://blog.csdn.net/gashero/archive/2007/01/19/1487664.aspx<br>第2章：http://blog.csdn.net/gashero/archive/2007/03/02/1519032.aspx<br>第3章：http://blog.csdn.net/gashero/archive/2007/03/02/1519035.aspx<br>第4章：http://blog.csdn.net/gashero/archive/2007/03/02/1519045.aspx<br><img src ="http://www.blogjava.net/renyangok/aggbug/111631.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-04-18 14:38 <a href="http://www.blogjava.net/renyangok/archive/2007/04/18/111631.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Python开发编码规范</title><link>http://www.blogjava.net/renyangok/archive/2007/04/08/109214.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Sun, 08 Apr 2007 03:48:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/04/08/109214.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/109214.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/04/08/109214.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/109214.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/109214.html</trackback:ping><description><![CDATA[<p><span>这篇文档所给出的编码约定适用于在主要的</span><span>Python</span><span>发布版本中组成标准库的</span><span>Python<span>&nbsp;&nbsp;&nbsp; </span></span><span>代码，请查阅相关的关于在</span><span>Python</span><span>的</span><span>C</span><span>实现中</span><span>C</span><span>代码风格指南的描述。</span></p>
<p><span>这篇文档改编自</span><span>Guido</span><span>最初的《</span><span>Python</span><span>风格指南》一文，并从《</span><span>Barry's style guide</span><span>》中添加了部分内容。在有冲突的地方，</span><span>Guide</span><span>的风格规则应该是符合本</span><span>PEP</span><span>的意图</span><span>(</span><span>译注：指当有冲突时，应以</span><span>Guido</span><span>风格为准</span><span>)</span><span>。这篇</span><span>PEP</span><span>仍然尚未完成</span><span>(</span><span>实际上，它可能永远都不会完成</span><span>)</span><span>。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>在这篇风格指导中的一致性是重要的。在一个项目内的一致性更重要。在一个模块或函数内的一致性最重要。但最重要的是：知道何时会不一致——有时只是没有实施风格指导。当出现疑惑时，运用你的最佳判断，看看别的例子，然后决定怎样看起来更好。并且要不耻下问！</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>打破一条既定规则的两个好理由：</span></p>
<p><span><span>(1)<span>&nbsp;&nbsp;&nbsp; </span></span></span><span>当应用这个规则是将导致代码可读性下降，即便对某人来说，他已经习惯于按这条规则来阅读代码了。</span></p>
<p><span><span>(2)<span>&nbsp;&nbsp;&nbsp; </span></span></span><span>为了和周围的代码保持一致而打破规则</span><span>(</span><span>也许是历史原因</span><span>)</span><span>，虽然这也是个清除其它混乱的好机会</span><span>(</span><span>真正的</span><span>XP</span><span>风格</span><span>)</span><span>。</span></p>
<p>&nbsp;</p>
<p><strong><span>代码的布局</span></strong><strong></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><strong><span>缩进</span></strong></p>
<p><span>使用</span><span>Emacs</span><span>的</span><span>Python-mode</span><span>的默认值：</span><span>4</span><span>个空格一个缩进层次。对于确实古老的代码，你不希望产生混乱，可以继续使用</span><span>8</span><span>空格的制表符</span><span>(8-space tabs)</span><span>。</span><span>Emacs Python-mode</span><span>自动发现文件中主要的缩进层次，依此设定缩进参数。</span></p>
<p>&nbsp;</p>
<p><strong><span>制表符还是空格</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>永远不要混用制表符和空格。最流行的</span><span>Python</span><span>缩进方式是仅使用空格，其次是仅使用制表符，混合着制表符和空格缩进的代码将被转换成仅使用空格。</span><span>(</span><span>在</span><span>Emacs</span><span>中，选中整个缓冲区，按</span><span>ESC-x</span><span>去除制表符。</span><span>)</span><span>调用</span><span>Python</span><span>命令行解释器时使用</span><span>-t</span><span>选项，可对代码中不合法得混合制表符和空格发出警告，使用</span><span>-tt</span><span>时警告将变成错误。这些选项是被高度推荐的。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>对于新的项目，强烈推荐仅使用空格而不是制表符。许多编辑器拥有使之易于实现的功能</span><span>(</span><span>在</span><span>Emacs</span><span>中，确认</span><span>indent-tabs-mode</span><span>是</span><span>nil)</span><span>。</span></p>
<p>&nbsp;</p>
<p><strong><span>行的最大长度</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>周围仍然有许多设备被限制在每行</span><span>80</span><span>字符：而且，窗口限制在</span><span>80</span><span>个字符。使将多个窗口并排放置成为可能。在这些设备上使用默认的折叠方式看起来有点丑陋。因此，请将所有行限制在最大</span><span>79</span><span>字符</span><span>(Emacs</span><span>准确得将行限制为长</span><span>80</span><span>字符</span><span>)</span><span>，对顺序排放的大块文本</span><span>(</span><span>文档字符串或注释</span><span>)</span><span>，推荐将长度限制在</span><span>72</span><span>字符。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>折叠长行的首选方法是使用</span><span>Pyhon</span><span>支持的圆括号，方括号和花括号内的行延续。如果需要，你可以在表达式周围增加一对额外的圆括号，但是有时使用反斜杠看起来更好，确认恰当得缩进了延续的行。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>Emacs</span><span>的</span><span>Python-mode</span><span>正确得完成了这些。一些例子：</span></p>
<p><span>#!Python</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>class Rectangle(Blob)</span><span>：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>def __init__(self</span><span>，</span><span>width</span><span>，</span><span>height</span><span>，</span><span>color='black'</span><span>，</span><span>emphasis=None</span><span>，</span><span>highlight=0)</span><span>：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if width == 0 and height == 0 and \</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>color == 'red' and emphasis == 'strong' or \</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>highlight &gt; 100</span><span>：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>raise ValueError</span><span>，</span><span> "sorry</span><span>，</span><span> you lose"</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if width == 0 and height == 0 and (color == 'red' or</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>emphasis is None)</span><span>：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>raise ValueError</span><span>，</span><span>"I don't think so"</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Blob.__init__(self</span><span>，</span><span>width</span><span>，</span><span>height</span><span>，</span><span>color</span><span>，</span><span>emphasis</span><span>，</span><span>highlight)</span></p>
<p>&nbsp;</p>
<p><strong><span>空行</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>用两行空行分割顶层函数和类的定义，类内方法的定义用单个空行分割，额外的空行可被用于</span><span>(</span><span>保守的</span><span>)</span><span>分割相关函数组成的群，在一组相关的单句中间可以省略空行。</span><span>(</span><span>例如：一组哑元素</span><span>)</span><span>。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>当空行用于分割方法的定义时，在&#8216;</span><span>class</span><span>&#8217;行和第一个方法定义之间也要有一个空行。在函数中使用空行时，请谨慎的用于表示一个逻辑段落。</span><span>Python</span><span>接受</span><span>contol-L(</span><span>即</span><span>^L)</span><span>换页符作为空格：</span><span>Emacs(</span><span>和一些打印工具</span><span>)</span><span>，视这个字符为页面分割符，因此在你的文件中，可以用他们来为相关片段分页。</span></p>
<p>&nbsp;</p>
<p><strong><span>编码</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>Python</span><span>核心发布中的代码必须始终使用</span><span>ASCII</span><span>或</span><span>Latin-1</span><span>编码</span><span>(</span><span>又名</span><span> ISO-8859-1)</span><span>，使用</span><span>ASCII</span><span>的文件不必有编码</span><span>cookie</span><span>，</span><span>Latin-1</span><span>仅当注释或文档字符串涉及作者名字需要</span><span>Latin-1</span><span>时才被使用：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>另外使用</span><span>\x</span><span>转义字符是在字符串中包含非</span><span>ASCII(non-ASCII)</span><span>数据的首选方法。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>作为</span><span>PEP 263</span><span>实现代码的测试套件的部分文件是个例外。</span></p>
<p>&nbsp;</p>
<p><strong><span>导入</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>通常应该在单独的行中导入</span><span>(Imports)</span><span>，例如：</span></p>
<p><span>No</span><span>：</span><span>import sys</span><span>，</span><span> os</span></p>
<p><span>Yes</span><span>：</span><span>import sys</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>import os</span></p>
<p><span>&nbsp;<span>&nbsp;&nbsp;</span></span><span>但是这样也是可以的：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>from types import StringType</span><span>，</span><span> ListType</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>Imports </span><span>通常被放置在文件的顶部，仅在模块注释和文档字符串之后，在模块的全局变量和常量之前。</span><span>Imports</span><span>应该有顺序地成组安放：</span></p>
<p><span>1</span><span>、标准库的导入</span><span>(Imports )</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>2</span><span>、相关的主包</span><span>(major package)</span><span>的导入</span><span>(</span><span>即，所有的</span><span>email</span><span>包在随后导入</span><span>)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>3</span><span>、特定应用的导入</span><span>(imports)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>你应该在每组导入之间放置一个空行，对于内部包的导入是不推荐使用相对导入的，对所有导入都要使用包的绝对路径。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>从一个包含类的模块中导入类时，通常可以写成这样：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>from MyClass import MyClass</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>from foo.bar.YourClass import YourClass</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>如果这样写导致了本地名字冲突，那么就这样写</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>import MyClass</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;<span>&nbsp;&nbsp;</span>import foo.bar.YourClass</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>即使用</span><span>"MyClass.MyClass"</span><span>和</span><span>"foo.bar.YourClass.YourClass"</span></p>
<p>&nbsp;</p>
<p><strong><span>表达式和语句中的空格</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>Guido</span><span>不喜欢在以下地方出现空格：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>紧挨着圆括号，方括号和花括号的，如：</span><span>"spam( ham[ 1 ]</span><span>，</span><span>{ eggs</span><span>：</span><span>2 } )"</span><span>。要始终将它写成</span><span>"spam(ham[1]</span><span>，</span><span>{eggs</span><span>：</span><span> 2})"</span><span>。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>紧贴在逗号，分号或冒号前的，如：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>"if x == 4</span><span>：</span><span>print x</span><span>，</span><span>y</span><span>：</span><span>x</span><span>，</span><span>y = y</span><span>，</span><span>x"</span><span>。要始终将它写成</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>"if x == 4</span><span>：</span><span>print x</span><span>，</span><span>y</span><span>：</span><span>x</span><span>，</span><span>y = y</span><span>，</span><span>x"</span><span>。</span></p>
<p><span>紧贴着函数调用的参数列表前开式括号</span><span>(open parenthesis )</span><span>的，如</span><span>"spam (1)"</span><span>。要始终将它写成</span><span>"spam(1)"</span><span>。</span></p>
<p><span>紧贴在索引或切片，开始的开式括号前的，如：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>"dict ['key'] = list [index]"</span><span>。要始终将它写成</span><span>"dict['key'] = list[index]"</span><span>。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>在赋值</span><span>(</span><span>或其它</span><span>)</span><span>运算符周围的用于和其它并排的一个以上的空格，如：</span></p>
<p><span>#!Python</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>x= 1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>y= 2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>long_variable = 3</span></p>
<p><span>要始终将它写成</span></p>
<p><span>#!Python</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>x = 1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>y = 2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>long_variable = 3</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>(</span><span>不要对以上任意一条和他争论——</span><span>Guido </span><span>养成这样的风格超过</span><span>20</span><span>年了。</span><span>)</span></p>
<p>&nbsp;</p>
<p><strong><span>其它建议</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>始终在这些二元运算符两边放置一个空格：赋值</span><span>(=)</span><span>，</span> <span>比较</span><span>(==</span><span>，</span><span>&lt;</span><span>，</span><span>&gt;</span><span>，</span><span>!=</span><span>，</span><span>&lt;&gt;</span><span>，</span><span>&lt;=</span><span>，</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&gt;=</span><span>，</span><span>in</span><span>，</span><span>not in</span><span>，</span><span>is</span><span>，</span><span>is not)</span><span>，布尔运算</span><span> (and</span><span>，</span><span>or</span><span>，</span><span>not)</span><span>。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>按你的看法在算术运算符周围插入空格。</span> <span>始终保持二元运算符两边空格的一致。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>一些例子：</span></p>
<p><span>#!Python</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>i = i+1</span></p>
<p><span><span>&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>submitted = submitted + 1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>x = x*2 - 1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>hypot2 = x*x + y*y</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>c = (a+b) * (a-b)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>c = (a + b) * (a - b)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>不要在用于指定关键字参数或默认参数值的</span><span>'='</span><span>号周围使用空格，例如：</span></p>
<p><span>#!Python</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>def complex(real</span><span>，</span><span> imag=0</span><span>。</span><span>0)</span><span>：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return magic(r=real</span><span>，</span><span> i=imag)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>不要将多条语句写在同一行上：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>No</span><span>：</span><span>&nbsp;if foo == 'blah'</span><span>：</span><span>do_blah_thing()</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Yes</span><span>：</span><span>if foo == 'blah'</span><span>：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>do_blah_thing()</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>No</span><span>：</span><span>do_one()</span><span>：</span><span>do_two()</span><span>：</span><span>do_three()</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Yes</span><span>：</span><span> do_one()</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>do_two()</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;do_three()</span></p>
<p>&nbsp;</p>
<p><strong><span>注释</span></strong></p>
<p><span>同代码不一致的注释比没注释更差。当代码修改时，始终优先更新注释</span><span>!</span><span>注释应该是完整的句子，如果注释是一个短语或句子，首字母应该大写，除非他是一个以小写字母开头的标识符</span><span>(</span><span>永远不要修改标识符的大小写</span><span>)</span><span>。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>如果注释很短，最好省略末尾的句号。注释块通常由一个或多个由完整句子构成的段落组成，每个句子应该以句号结尾。你应该在句末，句号后使用两个空格，以便使</span><span>Emacs</span><span>的断行和填充工作协调一致。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>用英语书写时，断词和空格是可用的。非英语国家的</span><span>Python</span><span>程序员：请用英语书写你的注释，除非你</span><span>120%</span><span>的确信这些代码不会被不懂你的语言的人阅读。</span></p>
<p>&nbsp;</p>
<p><strong><span>注释块</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>注释块通常应用于跟随着一些</span><span>(</span><span>或者全部</span><span>)</span><span>代码并和这些代码有着相同的缩进层次。注释块中每行以&#8216;</span><span>#</span><span>&#8217;和一个空格开始</span><span>(</span><span>除非他是注释内的缩进文本</span><span>)</span><span>。注释块内的段落以仅含单个&#8216;</span><span>#</span><span>&#8217;的行分割。注释块上下方最好有一空行包围</span><span>(</span><span>或上方两行下方一行，对一个新函数定义段的注释</span><span>)</span><span>。</span></p>
<p>&nbsp;</p>
<p><strong><span>行内注释</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>一个行内注释是和语句在同一行的注释，行内注释应该谨慎适用，行内注释应该至少用两个空格和语句分开，它们应该以</span><span>'#'</span><span>和单个空格开始。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>x = x+1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span># Increment x</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>如果语意是很明了的，那么行内注释是不必要的，事实上是应该被移除的。不要这样写：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>x = x+1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span># Increment x</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>x = x+1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span># Compensate for border</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>但是有时，这样是有益的：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>x = x+1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span># Compensate for border</span></p>
<p>&nbsp;</p>
<p><strong><span>文档字符串</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>应该一直遵守编写好的文档字符串的约定</span><span>PEP 257 [3]</span><span>。为所有公共模块，函数，类和方法编写文档字符串。文档字符串对非公开的方法不是必要的，但你应该有一个描述这个方法做什么的注释。这个注释应该在</span><span>"def"</span><span>这行后。</span></p>
<p><span>PEP 257 </span><span>描述了好的文档字符串的约定。一定注意，多行文档字符串结尾的</span><span>"""</span><span>应该单独成行，例如：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>"""Return a foobang</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Optional plotz says to frobnicate the bizbaz first</span><span>。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;"""</span></p>
<p><span>对单行的文档字符串，结尾的</span><span>"""</span><span>在同一行也可以。</span></p>
<p><strong><span>版本注记</span></strong></p>
<p><span>如果你要将</span><span>RCS</span><span>或</span><span>CVS</span><span>的杂项</span><span>(crud)</span><span>包含在你的源文件中，按如下做。</span></p>
<p><span>#!Python</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>__version__ = "$Revision</span><span>：</span><span> 1</span><span>。</span><span>4 $"</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span># $Source</span><span>：</span><span> E</span><span>：</span><span>/cvsroot/Python_doc/pep8</span><span>。</span><span>txt</span><span>，</span><span>v $</span></p>
<p><span>这个行应该包含在模块的文档字符串之后，所有代码之前，上下用一个空行分割。</span></p>
<p>&nbsp;</p>
<p><strong><span>命名约定</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>Python</span><span>库的命名约定有点混乱，所以我们将永远不能使之变得完全一致，不过还是有公认的命名规范的。新的模块和包</span><span>(</span><span>包括第三方的框架</span><span>)</span><span>必须符合这些标准，但对已有的库存在不同风格的，保持内部的一致性是首选的。</span></p>
<p>&nbsp;</p>
<p><strong><span>描述：命名风格</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>有许多不同的命名风格。以下的有助于辨认正在使用的命名风格，独立于它们的作用。</span><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>以下的命名风格是众所周知的：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>b (</span><span>单个小写字母</span><span>)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>B (</span><span>单个大写字母</span><span>)</span></p>
<p><span><span>&nbsp;&nbsp; </span>&nbsp;Lowercase</span><span>（小写）</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>lower_case_with_underscores</span><span>（有下划线的小写）</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>UPPERCASE</span><span>（大写）</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>UPPER_CASE_WITH_UNDERSCORES</span><span>（有下划线的大写）</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>CapitalizedWords (</span><span>或</span><span> CapWords</span><span>，</span><span>CamelCase</span><span>这样命名是因为可从字母的大小写分出单词。这有时也被当作</span><span>StudlyCaps</span><span>。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>mixedCase (</span><span>与</span><span>CapitalizedWords</span><span>的不同在于首字母小写</span><span>!)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>Capitalized_Words_With_Underscores</span><span>（有下划线的首字母大写）</span><span> (</span><span>丑陋</span><span>!)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>还有用短的特别前缀将相关的名字聚合在一起的风格。这在</span><span>Python</span><span>中不常用，但是出于完整性要提一下，例如，</span><span>os.stat()</span><span>函数返回一个元组，他的元素传统上说名如</span><span>st_mode</span><span>，</span><span> st_size</span><span>，</span><span>st_mtime</span><span>等等。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>X11</span><span>库的所有公开函数以</span><span>X</span><span>开头。</span><span>(</span><span>在</span><span>Python</span><span>中，这个风格通常认为是不必要的，因为属性和方法名以对象作前缀，而函数名以模块名作前缀。</span><span>)</span></p>
<p><span>另外，以下用下划线作前导或结尾的特殊形式是被公认的</span><span>(</span><span>这些通常可以和任何习惯组合</span><span>)</span><span>：</span></p>
<p><span>_single_leading_underscore(</span><span>单个下划线作前导</span><span>)</span><span>：弱的&#8220;内部使用</span><span>(internal use)</span><span>&#8221;标志。</span><span> (</span><span>例如，&#8220;</span><span>from M import *</span><span>&#8221;不会导入以下划线开头的对象</span><span>)</span><span>。</span></p>
<p><span>single_trailing_underscore_(</span><span>单个下划线结尾</span><span>)</span><span>：</span> <span>用于避免与</span><span>Python</span><span>关键词的冲突，例如：&#8220;</span><span>Tkinter.Toplevel(master</span><span>，</span><span>class_='ClassName')</span><span>&#8221;。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>_double_leading_underscore(</span><span>双下划线</span><span>)</span><span>：从</span><span>Python 1.4</span><span>起为类私有名。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>_double_leading_and_trailing_underscore_</span><span>：&#8220;</span><span>magic</span><span>&#8221;对象或属性，存在于用户控制的</span><span>(user-controlled)</span><span>名字空间，例如：</span><span>_init_</span><span>，</span><span> _import_ </span><span>或</span><span>_file_</span><span>。有时它们被用户定义用于触发某个魔法行为</span><span>(</span><span>例如：运算符重载</span><span>)</span><span>：有时被构造器插入，以便自己使用或为了调试。因此，在未来的版本中，构造器</span><span>(</span><span>松散得定义为</span><span>Python</span><span>解释器和标准库</span><span>)</span><span>可能打算建立自己的魔法属性列表，用户代码通常应该限制将这种约定作为己用。欲成为构造器的一部分的用户代码可以在下滑线中结合使用短前缀，例如：</span></p>
<p><span>_bobo_magic_attr__</span><span>。</span></p>
<p>&nbsp;</p>
<p><strong><span>说明：命名约定</span></strong></p>
<p><span>应避免的名字。永远不要用字符&#8216;</span><span>l</span><span>&#8217;</span><span>(</span><span>小写字母</span><span>el(</span><span>就是读音，下同</span><span>))</span><span>，&#8216;</span><span>O</span><span>&#8217;</span><span>(</span><span>大写字母</span><span>oh)</span><span>，或&#8216;</span><span>I</span><span>&#8217;</span><span>(</span><span>大写字母</span><span>eye)</span><span>作为单字符的变量名。在某些字体中这些字符不能与数字</span><span>1</span><span>和</span><span>0</span><span>分辨。试着在使用&#8216;</span><span>l</span><span>&#8217;时用&#8216;</span><span>L</span><span>&#8217;代替。</span></p>
<p>&nbsp;</p>
<p><strong><span>模块名</span></strong></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>模块应该是不含下划线的，简短的，小写的名字。因为模块名被映射到文件名，有些文件系统大小写不敏感并且截短长名字，模块名被选为相当短是重要的，这在</span><span>Unix</span><span>上不是问题，但当代码传到</span><span>Mac</span><span>或</span><span>Windows</span><span>上就可能是个问题了。</span></p>
<p><span>&nbsp;</span><span>当用</span><span>C</span><span>或</span><span>C++</span><span>编写的扩展模块有一个伴随</span><span>Python</span><span>模块提供高层</span><span>(</span><span>例如进一步的面向对象</span><span>)</span><span>接口时，</span><span>C/C++</span><span>模块有下划线前导</span><span>(</span><span>如：</span><span>_socket)</span><span>。</span><span>Python</span><span>包应该是不含下划线的，简短的，全小写的名字。</span></p>
<p>&nbsp;</p>
<p><strong><span>类名</span></strong></p>
<p><span>几乎不出意料，类名使用</span><span>CapWords</span><span>约定。内部使用的类外加一个前导下划线。</span></p>
<p>&nbsp;</p>
<p><strong><span>异常名</span></strong></p>
<p><span>如果模块对所有情况定义了单个异常，它通常被叫做&#8220;</span><span>error</span><span>&#8221;或&#8220;</span><span>Error</span><span>&#8221;。似乎内建</span><span>(</span><span>扩展</span><span>)</span><span>的模块使用&#8220;</span><span>error</span><span>&#8221;</span><span>(</span><span>例如：</span><span>os.error)</span><span>，而</span><span>Python</span><span>模块通常用&#8220;</span><span>Error</span><span>&#8221;</span><span> (</span><span>例如：</span><span>xdrlib.Error)</span><span>。趋势似乎是倾向使用</span><span>CapWords</span><span>异常名。</span></p>
<p>&nbsp;</p>
<p><strong><span>全局变量名</span></strong></p>
<p><span>(</span><span>让我们祈祷这些变量仅在一个模块的内部有意义</span><span>)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span></span><span>这些约定和在函数中的一样。模块是被设计为通过&#8220;</span><span>from M import *</span><span>&#8221;来使用的，必须用一个下划线作全局变量</span><span>(</span><span>及内部函数和类</span><span>)</span><span>的前缀防止其被导出</span><span>(exporting)</span><span>。</span></p>
<p>&nbsp;</p>
<p><strong><span>函数名</span></strong></p>
<p><span>函数名应该为小写，可能用下划线风格单词以增加可读性。</span><span>mixedCase</span><span>仅被允许用于这种风格已经占优势的上下文</span><span>(</span><span>如：</span><span>threading.py)</span><span>，以便保持向后兼容。</span></p>
<p>&nbsp;</p>
<p><strong><span>方法名和实例变量</span></strong></p>
<p><span>这段大体上和函数相同：通常使用小写单词，必要时用下划线分隔增加可读性。仅为不打算作为类的公共界面的内部方法和实例使用一个前导下划线，</span><span>Python</span><span>不强制要求这样：它取决于程序员是否遵守这个约定。</span></p>
<p><span>使用两个前导下划线以表示类私有的名字，</span><span>Python</span><span>将这些名字和类名连接在一起：</span></p>
<p><span>如果类</span><span>Foo</span><span>有一个属性名为</span><span>_a</span><span>，它不能以</span><span>Foo._a</span><span>访问。</span><span>(</span><span>固执的用户还是可以通过</span><span>Foo._Foo__a</span><span>得到访问权。</span><span>)</span></p>
<p><span>通常双前导下划线仅被用于避免含子类的类中的属性名的名字冲突。</span></p>
<p>&nbsp;</p>
<p><strong><span>继承的设计</span></strong></p>
<p><span>始终要确定一个类中的方法和实例变量是否要被公开。通常，永远不要将数据变量公开，除非你实现的本质上只是记录，人们几乎总是更喜欢代之给出一个函数作为类的界面</span><span>(Python 2.2 </span><span>的一些开发者在这点上做得非常漂亮</span><span>)</span><span>。</span></p>
<p><span>同样，确定你的属性是否应为私有的。私有和非私有的区别在于模板将永远不会对原有的类</span><span>(</span><span>导出类</span><span>)</span><span>有效，而后者可以。你应该在大脑中就用继承设计好了你的类，私有属性必须有两个前导下划线，无后置下划线，非公有属性必须有一个前导下划线，无后置下划线，公共属性没有前导和后置下划线，除非它们与保留字冲突，在此情况下，单个后置下划线比前置或混乱的拼写要好，例如：</span><span>class_</span><span>优于</span><span>klass</span><span>。</span></p>
<p><span>最后一点有些争议：如果相比</span><span>class_</span><span>你更喜欢</span><span>klass</span><span>，那么这只是一致性问题。</span></p>
<p>&nbsp;</p>
<p><strong><span>设计建议</span></strong></p>
<p><span>单个元素</span><span>(singletons)</span><span>的比较，如</span><span>None </span><span>应该永远用：&#8216;</span><span>is</span><span>&#8217;或&#8216;</span><span>is not</span><span>&#8217;来做。当你本意是&#8220;</span><span>if x is not None</span><span>&#8221;时，对写成&#8220;</span><span>if x</span><span>&#8221;要小心。例如当你测试一个默认为</span><span>None</span><span>的变量或参数是否被设置为其它值时，这个值也许在布尔上下文</span><span>(Boolean context)</span><span>中是</span><span>false</span><span>！</span></p>
<p><span>基于类的异常总是好过基于字符串的异常。模块和包应该定义它们自己的域内特定的基异常类，基类应该是内建的</span><span>Exception</span><span>类的子类。还始终包含一个类的文档字符串。例如：</span></p>
<p><span>#!Python</span></p>
<p><span><span>&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>class MessageError(Exception)</span><span>：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>"""Base class for errors in the email package</span><span>。</span><span>"""</span></p>
<p><span>使用字符串方法</span><span>(methods)</span><span>代替字符串模块，除非必须向后兼容</span><span>Python 2.0</span><span>以前的版本。字符串方法总是非常快，而且和</span><span>unicode</span><span>字符串共用同样的</span><span>API(</span><span>应用程序接口</span><span>)</span><span>在检查前缀或后缀时避免对字符串进行切片。用</span><span>startswith()</span><span>和</span><span>endswith()</span><span>代替，因为它们是明确的并且错误更少。例如：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;</span>No</span><span>：</span><span>&nbsp;if foo[</span><span>：</span><span>3] == 'bar'</span><span>：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Yes</span><span>：</span><span> if foo</span><span>。</span><span>startswith('bar')</span><span>：</span></p>
<p><span>例外是如果你的代码必须工作在</span><span>Python 1.5.2 (</span><span>但是我们希望它不会发生！</span><span>)</span><span>，对象类型的比较应该始终用</span><span>isinstance()</span><span>代替直接比较类型，例如：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>No</span><span>：</span><span>&nbsp;if type(obj) is type(1)</span><span>：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Yes</span><span>：</span><span> if isinstance(obj</span><span>，</span><span> int)</span><span>：</span></p>
<p><span>检查一个对象是否是字符串时，紧记它也可能是</span><span>unicode</span><span>字符串！在</span><span>Python 2.3</span><span>，</span><span>str</span><span>和</span><span>unicode</span><span>有公共的基类，</span><span>basestring</span><span>，所以你可以这样做：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if isinstance(obj</span><span>，</span><span> basestring)</span><span>：</span></p>
<p><span>在</span><span>Python 2.2</span><span>类型模块为此定义了</span><span>StringTypes</span><span>类型，例如：</span></p>
<p><span>#!Python</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>from types import StringTypes</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if isinstance(obj</span><span>，</span><span> StringTypes)</span><span>：</span></p>
<p><span>在</span><span>Python 2.0</span><span>和</span><span>2.1</span><span>，你应该这样做：</span></p>
<p><span>#!Python</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;</span>from types import StringType</span><span>，</span><span> UnicodeType</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if isinstance(obj</span><span>，</span><span> StringType) or \</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>isinstance(obj</span><span>，</span><span> UnicodeType) </span><span>：</span></p>
<p><span>对序列，</span><span>(</span><span>字符串，列表，元组</span><span>)</span><span>，使用空列表是</span><span>false</span><span>这个事实，因此&#8220;</span><span>if not seq</span><span>&#8221;或&#8220;</span><span>if seq</span><span>&#8221;比&#8220;</span><span>if len(seq)</span><span>&#8221;或&#8220;</span><span>if not len(seq)</span><span>&#8221;好。书写字符串文字时不要依赖于有意义的后置空格。这种后置空格在视觉上是不可辨别的，并且有些编辑器</span><span>(</span><span>特别是近来，</span><span>reindent.py)</span><span>会将它们修整掉。不要用</span><span>==</span><span>来比较布尔型的值以确定是</span><span>True</span><span>或</span><span>False(</span><span>布尔型是</span><span>Pythn 2.3</span><span>中新增的</span><span>)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>No</span><span>：</span><span>&nbsp;if greeting == True</span><span>：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Yes</span><span>：</span><span> if greeting</span><span>：</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>No</span><span>：</span><span>&nbsp;if greeting == True</span><span>：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Yes</span><span>：</span><span> if greeting</span><span>：</span></p>
<img src ="http://www.blogjava.net/renyangok/aggbug/109214.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-04-08 11:48 <a href="http://www.blogjava.net/renyangok/archive/2007/04/08/109214.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Python正则表达式操作指南</title><link>http://www.blogjava.net/renyangok/archive/2007/04/05/108806.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Thu, 05 Apr 2007 14:36:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/04/05/108806.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/108806.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/04/05/108806.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/108806.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/108806.html</trackback:ping><description><![CDATA[<p>循序渐进，通俗易懂，很不错<a href="http://www.leshou.com/show/248"><br><br></a></p>
<p><a href="http://www.leshou.com/show/248">http://net.pku.edu.cn/%7Eyhf/tao_regexps_zh.html<br></a></p><img src ="http://www.blogjava.net/renyangok/aggbug/108806.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-04-05 22:36 <a href="http://www.blogjava.net/renyangok/archive/2007/04/05/108806.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>robbin眼中的python</title><link>http://www.blogjava.net/renyangok/archive/2007/03/12/103304.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Mon, 12 Mar 2007 06:02:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/03/12/103304.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/103304.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/03/12/103304.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/103304.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/103304.html</trackback:ping><description><![CDATA[
		<p>
				<font color="#0000ff">    以后工作要用到python，之前只知道是种成熟的动态语言，开发效率高，仅此而已。今天看到robbin发表的这个帖子，让我对python兴趣大增，也决心把这门语言搞懂，努力吧！<br /></font>
				<br />以下转自：<a href="http://www.javaeye.com/article/7414">http://www.javaeye.com/article/7414</a><br /><br />我眼中的Python</p>
		<p>python这种编程语言我很早就听说它了，早在1998年，我在玩Linux的时候，就接触过它，但是我对python的印象仅仅停留在它是一种流行的面向对象的脚本语言的认识上。</p>
		<p>zope，基于python的app server，早在2000年我就已经对它如雷贯耳了，但是我对zope的印象仅仅停留在它是一种类似Apache HTTPD Server，AOL Server之类的web server上。</p>
		<p>今年ozzzzzz多次向我提到python和zope，听得我耳朵都起了老茧了，dlee又建议我开设python版面，虽然我没有答应，但是架不住朋友们的左劝右劝，终究对python这门语言有了好奇心。</p>
		<p>几天前，当我漫不经心的浏览了python和zope的网站之后，我突然有了一种“众里寻它千百度，那人却在灯火阑珊处”的感觉，悔不迭错过了那么多次相逢的机会。</p>
		<p>对于软件开发，我和ozzzzzz有一个共识，就是脚本解释弱类型语言在开发效率上远远超过编译强类型语言，从软件开发角度来说，脚本语言具备天然的开发效率上的优势，这是由语言的内在属性决定的。</p>
		<p>例如我们可以比较一下Web编程使用PHP和Servlet/JSP，比较一下Windows桌面应用使用VB和VC/Delphi，比较一下Unix环境下面Shell和C/Java，比较一下数据库环境下面的PL/SQL和JDBC/SQLJ。</p>
		<p>我虽然使用Java开发软件已经有5年的历史了，但是能不用Java的时候，我一定不会用Java的。如果搭建一个小型的网站，我一定选择PHP而不是Java；如果针对数据库的小型编程，我一定使用PL/SQL而不是Java；如果是桌面应用，我一定选择VB而不是Java；如果是Unix环境我宁愿选择shell，perl甚至PHP，而不是Java。</p>
		<p>做为一种严谨的，编译式的，面向对象语言，Java总是给我一种须正襟危坐，须一板一眼的按照OOAD的原则编程，才敢在键盘上敲下字符的感觉。即使编写一个最小规模的程序，我也不能够接受把所有的code塞到main里面的做法。Java似乎以不怒自威的威严使我不敢随意编码，不敢玷污Java的严谨。于是我即使写一个很简单的JDBC程序，也要一板一眼的try catch finally，一层层的处理Connection，PreparedStatement和ResultSet。</p>
		<p>诚然，如果开发规模比较大的项目，或者开发自有的软件产品，必须应该按照严谨的方式，此时Java也是最适合的语言。但是我不想活的那么累，很多人也不想活的那么累，于是大家都怀念起来脚本语言的好来。对于小规模的应用，使用脚本语言快速简单完成的事情，当你使用Java的时候，你陷入了过多的层层代码包围中去。于是groovy出现了，bean shell出现了。大家终于明白，编译语言不是软件开发的全部，脚本语言才是最适合程序员的语言。</p>
		<p>我喜欢脚本语言，喜欢的没边，Java是我的职业，但是我从来都没有在内心深处喜欢过它的语法，我更加讨厌C++变本加厉的复杂。我钟爱的语言包括小学就接触的BASIC和Logo，毕业以后才掌握的Unix Shell和PHP，Perl，PL/SQL。几乎我接触过的每种脚本语言，我都有浓厚的兴趣和感情，除了VBA是一个例外。</p>
		<p>看看Java里面长长的对象，方法和属性命名，看看Java编程冗长的调用语句和愚蠢的对象赋值和经常长达几十行的getter/setter，我会时不时从心底泛起恶心的感觉，虽然我经常也是这种恶心感觉的制造者。但是恶心归恶心，我知道Java有它不可取代的作用，PHP有它无法弥补的缺陷。大部分脚本语言，包括perl，PHP，PL/SQL，VBA都不是真正意义上的面向对象编程语言，即使包括了部分面向对象语言的特性，这注定了脚本语言不能够承担大型项目的开发，甚至也不能够充当良好的可复用的组件存在。</p>
		<p>所以我很遗憾，我欣赏PHP脚本语言的开发效率，我也欣赏Java的面向对象的能力，我欣赏PHP的低部署成本高可靠运行，我也欣赏Java App Server带来的开发复杂运算的强大能力，但是鱼与熊掌不可得兼。</p>
		<p>直到我看到了python和zope，我终于找到了梦寐以求的东西，兼有脚本语言开发的高效率，兼有低部署成本的易用性，同时又有完备的面向对象的强大支撑能力，同时又具备完善的强大的app server支持。最令我生气的还是plone，这个运行在zope之上的软件，你可以称之为portal，或者称之为cms，或者其他的什么名词，但是我知道它几乎可以实现任何网站想要实现的功能。默认安装下，这个东西很像confluence，一个Java的商业的cms，但是比confluence功能强大的太多，可定制性，可开发性又强的太多了。plone在默认安装情况下你就可以把它当做cms来用，比较类似***nuke类软件(PHPNuke, PostNuke, JBossNuke,...)，通过插件的扩展，你可以让plone里面集成了forum，blog，wiki的功能，再加上plone本来就支持的WebDAV，功能强大的基于文档的权限控制，多用户多组的管理，你可以在很短的时间内实现一个全功能的门户网站。可笑的是，我从去年到今年一直在考虑把JavaEye建设成为一个集成forum，blog，wiki，cms功能的网站，甚至雄心壮志的想要做一个这么的软件产品来，但是现在我发现plone已经漂亮的实现了这一切，最令我沮丧的是，ozzzzzz在听过我的软件产品计划之后提出一个用关键词来组织网站内容的设想，结果我发现plone已经这样做了，我能说的只有惭愧！</p>
		<p>我只能感叹自己没有在一年之前就发现plone，否则的话现在的JavaEye将完整的使用plone来架设。更令我惭愧的是，当我刚刚意识到zope/plone的价值的时候，上海已经有人成立了专业的zope/plone解决方案的软件厂商，并且拿下了好几个大型的客户，(http://www.zopechina.com)。有时候我们真的不能把眼睛盲目的盯着大厂商强行推广的标准了，应该好好的审视自己真正需要的是什么，并且围绕它构建自己的核心竞争力，否则我们只有永远做IT行业食物链最底层的命运。</p>
		<p>ozzzzzz曾经对我说，zope是B/S应用中的VB，快速原型开发中小型企业应用的最佳武器，我现在真切的理解了他的话。我觉得我们确实不能够眼睛光盯着Java/C#不放，也应该了解一下zope/plone，它应该成为中小型企业应用，特别是基于文档管理的企业应用的最重要的解决方案。</p>
		<p>zope/plone虽然很好，但是我知道它不会如Java/J2EE，C#/dotnet那样在国内成为一种主流的软件开发解决方案。原因就在于国内的软件开发行业长期处于国际分工产业链的底层，缺乏创新意识和开拓的精神，而往往满足于跟随跨国公司的标准，啃啃人家剩下的肉骨头。位于食物链顶层的厂商，例如MS，IBM，Sun，BEA，CA，Oracle等等，争夺的是标准。他们是规则的制订者，制订好了规则，放大家进场，他们负责收费，钱收的差不多了的时候，他们又重新竞争，制订新一轮的规则，继续坐地收费。很多时候，市场的真实需求并没有被真实的体现，真实的需求被顶级厂商掩盖了。他们创造了一个市场需求，有了市场需求，就有钱赚，赚的差不多了，就摧毁这个市场，然后创造下一个市场需求。因此本质上来说，IT行业就是几个寡头在博弈，不管谁赢谁输，我们都是输家，我们只是人家的筹码。大家可以回顾一下这些年软件技术发展的历程，寡头厂商制造了EJB市场赚大发了，寡头厂商了制造了ERP市场赚大发了，寡头厂商继续在制造着SOA，等着继续收钱。我们不能够被满天飞的技术迷惑了，我们不能被人卖了还替人家数钱。</p>
		<p>所以多多关注一下我们真正需要的技术吧。</p>
<img src ="http://www.blogjava.net/renyangok/aggbug/103304.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-03-12 14:02 <a href="http://www.blogjava.net/renyangok/archive/2007/03/12/103304.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Python的历史</title><link>http://www.blogjava.net/renyangok/archive/2007/03/08/102549.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Thu, 08 Mar 2007 02:19:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/03/08/102549.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/102549.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/03/08/102549.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/102549.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/102549.html</trackback:ping><description><![CDATA[Python的创始人为Guido van Rossum。1989年圣诞节期间，在阿姆斯特丹，Guido为了打发圣诞节的无趣，决心开发一个新的脚本解释程序，做为ABC语言的一种继承。之所以选中Python（大蟒蛇的意思）作为程序的名字，是因为他是一个Monty 大蟒蛇飞行马戏团的爱好者。 <br /><br />ABC是由Guido参加设计的一种教学语言（没听说过）。就Guido本人看来，ABC这种语言非常优美和强大，是专门为非专业程序员设计的。但是ABC语言并没有成功，究其原因，Guido认为是非开放造成的。Guido决心在Python中避免这一错误（的确如此，Python与其它的语言如C,C++和Java结合的非常好）。同时，他还想实现在ABC中闪现过但未曾实现的东西。 <br /><br />就这样，Python在Guido手中诞生了（真要感谢他）。实际上，第一个实现是在Mac机上。可以说，Python是从ABC发展起来，主要受到了Modula-3（另一种相当优美且强大的语言，为小型团体所设计的）的影响。并且结合了Unix Shell和C的习惯。 <br /><br />Python的特点 <br />Python是一种脚本语言，它的语法表达优美易读。它具有很多优秀的脚本语言的特点：解释的，面向对象的，内建的高级数据结构，支持模块和包，支持多种平台，可扩展。而且它还支持交互式方式运行，图形方式运行。它的语法有很多与众不同的特性。下面我分别说明一下： <br /><br />运行方式 <br />Python可以以命令行方式运行，也可以交互式方式运行，还具有图形集成环境，这样开发Python就相当方便。现在已经出现了许多用Python编写的可视化编程软件，用于实现象Delphi一样的功能。 <br />面向对象 <br />Python是一个真正的面向对象语言。它甚至支持异常的处理。如果学过Java，应该对这个不陌生。但其它的脚本语言，如PHP，好象就没有。这使得程序的编写更加清晰，而不需要许多的错误检查了。 <br />模块和包 <br />这一点更象是Java。对于Java的支持，大家可以了解JPython。JPython是用Java写的Python，它完全支持Java，在这个环境下使用Python可以随意地使用Java的类库。 <br />语言扩展 <br />可以用C、C++或Java为Python编写新的新言模块，如函数。或者与Python直接编译在一起，或者采用动态库装入方式实现。也专门有人编写了一个工具，可以实现为Python自动实现函数接口封装，这就是SWIG（Simplified Wrapper and Interface Generator)，或称做简单封装和接口生成器(可以在 <a title="http://www.cs.utah.edu/~beazley/SWIG" href="http://www.cs.utah.edu/~beazley/SWIG" target="_blank">http://www.cs.utah.edu/~beazley/SWIG</a> 自由获得)。 <br />有趣的语法 <br />Guido认为Python的语法是非常优美的。其中一点就是，块语句的表示不是C语言常用的{}对，或其它符号对，而是采用缩近表示法！有趣吧。就这一点来说，Guido的解释是：首先，使用缩近表示法减少了视觉上的混乱，并且使程序变短，这样就减少了需要对基本代码单元注意的范围；其次，它减少了程序员的自由度，更有利于统一风格，使得阅读别人的程序更容易。感觉还是不错的，就C语言来说，在if语句后面大括号的写法就好几种，不同的人喜欢不同的样子，还不如统一起来，都不会看得别扭。 <br /><br />在每个类或函数的定义后面，第一行可以是说明语句，根本不需要注释符标记。对于后面跟块语句的语句，后面应跟上一个冒号(:)。一行语句不能太长，因为没有行结束符，如果超长则要使用续行符(\)。还有一些有趣的比如说，象下面的一个比较处理，用C语言为： <br /><br />if (2&lt;a &amp;&amp; a&lt;5) <br /><br />用Python可以表示为 <br /><br />if (2&lt;a&lt;5) : <br /><br />当然不一定会有什么好处，但是它所宣称的优美的语言毕竟不是虚的。其它的大家可以通过学习自行了解。 <br />Python可以做什么？ <br />那么Python可以做什么呢？我感觉，什么都可以。如果在Windows下，如果想做一个图形界面程序，那么Python可以胜任。可以去wxPython.org看一下，它是一个Python的扩展库，可以用来实现跨平台的图形编程，它支持windows和unix/linux。在那个网站上有一些演示，可以学习一下。Python可以做CGI。有些主页提供了对Python的CGI支持。现在还有一些用Python做的游戏。如果想了解更多的Python可以做什么，可以去SourceForge上查找有关Python的项目，上面有很多东西。 <br /><br />结束 <br />在中国Python还处于起步，学习研究它的人很少，希望感兴趣的同志加入这一行列。 <br /><br />参考文献：什么是Python？<br /><br />官方网站 <a title="http://www.python.org" href="http://www.python.org/" target="_blank">http://www.python.org/</a><img src ="http://www.blogjava.net/renyangok/aggbug/102549.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-03-08 10:19 <a href="http://www.blogjava.net/renyangok/archive/2007/03/08/102549.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>