Changeset 858

Show
Ignore:
Timestamp:
Sun Mar 12 17:22:19 2006
Author:
osmond
Message:

译毕,待校

Files:

Legend:

Unmodified
Added
Removed
Modified
  • zh-translations/branches/diveintopython-zh-5.4/zh-cn/xml/kgp.xml

    r779 r858  
    2 2 <chapter id="kgp">  
    3 3 <?dbhtml filename="xml_processing/index.html"?>  
    4   <title>&xml; Processing</title>  
    5   <titleabbrev id="kgp.numberonly">第九章</titleabbrev>  
      4 <title>&xml; 处理</title>  
      5 <titleabbrev id="kgp.numberonly">第 9 章</titleabbrev>  
    6 6 <section id="kgp.divein">  
    7 7 <title>接触</title>  
    8   <para>下面两章是关于 &python; 中 &xml; 处理的。如果你已经知道一个 &xml; 文档的样子,比如它是由结构化标记构成的,这些标记形成了层次模型的元素,等等这些知识都是有帮助的。如果你不明白这些,这里有<ulink url="&url_xmltutorial;">很多 &xml; 教程</ulink> 能够解释这些基础知识。</para>  
      8 <para>下面两章是关于 &python; 中 &xml; 处理的。如果你已经知道一个 &xml; 文档的样子,比如它是由结构化标记构成的,这些标记形成了层次模型的元素,等等这些知识都是有帮助的。如果你不明白这些,这里有 <ulink url="&url_xmltutorial;">很多 &xml; 教程</ulink> 能够解释这些基础知识。</para>  
    8 8 <para>如果你对XML不是很感兴趣,你还是应该读一下这些章节,它们涵盖了不少重要的主题比如 &python; 包,Unicode,命令行参数以及如何使用 &getattr; 进行方法分发。</para>  
    9 9 <para>Being a philosophy major is not required, although if you have ever had the misfortune of being subjected to the writings of Immanuel Kant, you will appreciate the example program a lot more than if you majored in something useful, like computer science.</para>  
    847 847 <!-- You can only enter the same stream once. -->  
    848 848 <title>Scripts and Streams</title>  
    849   <titleabbrev id="streams.numberonly">第章</titleabbrev>  
      849 <titleabbrev id="streams.numberonly">第 10 章</titleabbrev>  
    849 849 <section id="kgp.openanything">  
    850 850 <?dbhtml filename="scripts_and_streams/input_sources.html"?>  
  • zh-translations/branches/diveintopython-zh-5.4/zh-cn/xml/fileinfo.xml

    r798 r858  
    3 3 <?dbhtml filename="object_oriented_framework/index.html"?>  
    4 4 <title>对象和面向对象</title>  
    5   <titleabbrev id="fileinfo.numberonly">Chapter 5</titleabbrev>  
      5 <titleabbrev id="fileinfo.numberonly">第 5 章</titleabbrev>  
    5 5 <abstract>  
    6 6 <title/>  
     
    415 415 </callout>  
    416 416 <callout arearefs="fileinfo.userdict.1.4">  
    417   <para>[todo]The &update; method is a dictionary duplicator: it copies all the keys and values from one dictionary to another.  This does <emphasis>not</emphasis> clear the target dictionary first; if the target dictionary already has some keys, the ones from the source dictionary will be overwritten, but others will be left untouched.  Think of &update; as a merge function, not a copy function.</para>  
      417 <para>&update; 方法是一个字典复制器:它把一个字典中的键和值全部拷贝到另一个字典。 这个操作 <emphasis>并不</emphasis> 事先清空目标字典,如果一些键在目标字典中已经存在,则它们将被覆盖,那些键名在目标字典中不存在的则不改变。应该把 &update; 看作是合并函数,而不是复制函数。</para>  
    417 417 </callout>  
    418 418 <callout arearefs="fileinfo.userdict.1.5">  
     
    429 429 <!--<title>Guido on Derived Classes</title>-->  
    430 430 <title/>  
    431   <para>[todo]Guido, the original author of &python;, explains method overriding this way: "Derived classes may override methods of their base classes. Because methods have no special privileges when calling other methods of the same object, a method of a base class that calls another method defined in the same base class, may in fact end up calling a method of a derived class that overrides it. (For &cpp; programmers: all methods in &python; are effectively virtual.)"  If that doesn't make sense to you (it confuses the hell out of me), feel free to ignore it.  I just thought I'd pass it along.</para>  
      431 <para>&python; 的原作者Guido是这样解释方法覆盖的 “子类可以覆盖父类中的方法。因为方法没有特殊的优先级设置,在调用同一对象的另外方法时,父类中一个方法对另一个同类中的方法的调用,可能其实调用到的却是一个子类中覆盖父类同名方法的方法。(对于 &cpp; 程序员,所有的 &python; 方法都非常有效)”  如果你不明白(它另我颇感困惑),不必在意。我想我要跳过它。</para>  
    431 431 </note>  
    432 432 <caution id="note.dataattributes">  
    433 433 <!--<title>Always Initialize Data Attributes</title>-->  
    434 434 <title/>  
    435   <para>应该总是在 &init; 方法中给所有实例的数据属性赋予一个初始值。这样做将会节省你在后面调试的时间,  
    436   [todo]Always assign an initial value to all of an instance's data attributes in the &init; method.  It will save you hours of debugging later,  
    437   tracking down <classname>AttributeError</classname> exceptions because you're referencing uninitialized (and therefore non-existent) attributes.</para>  
      435 <para>应该总是在 &init; 方法中给一个实例的所有数据属性赋予一个初始值。这样做将会节省你在后面调试的时间,不必为捕捉因使用未初始化(也就是不存在)的属性而导致的 <classname>AttributeError</classname> 异常费时费力。</para>  
    438 436 </caution>  
    439 437 <example id="fileinfo.userdict.normalmethods">  
     
    459 457 </callout>  
    460 458 <callout arearefs="fileinfo.userdict.2.3">  
    461   <para>我们使用 &classattr; 属性来查看是否 &self; 是一个 &userdict_classname;,如果是,太好了,因为我们知道如何拷贝一个 &userdict_classname;:只要创建一个新的 &userdict_classname; ,并传给它真正的字典,这个字典已经存放在 <varname>self.data</varname> 中了。[todo] Then you immediately return the new &userdict_classname; you don't even get to the <literal>import copy</literal> on the next line.</para>  
      459 <para>我们使用 &classattr; 属性来查看是否 &self; 是一个 &userdict_classname;,如果是,太好了,因为我们知道如何拷贝一个 &userdict_classname;:只要创建一个新的 &userdict_classname; ,并传给它真正的字典,这个字典已经存放在 <varname>self.data</varname> 中了。 然后你立即返回这个新的 &userdict_classname;,你甚至于不需要再下面一行中使用 <literal>import copy</literal>。</para>  
    461 459 </callout>  
    462 460 <callout arearefs="fileinfo.userdict.2.4">  
     
    469 467 </calloutlist>  
    470 468 </example>  
    471   <note>[todo]  
    472   <title>Historical Note</title>  
    473   <para>In versions of &python; prior to 2.2, you could not directly subclass built-in datatypes like strings, lists, and dictionaries.  To compensate for this, &python; comes with wrapper classes that mimic the behavior of these built-in datatypes: <classname>UserString</classname>, <classname>UserList</classname>, and &userdict_classname;.  Using a combination of normal and special methods, the &userdict_classname; class does an excellent imitation of a dictionary.  In &python; 2.2 and later, you can inherit classes directly from built-in datatypes like &dict;.  An example of this is given in the examples that come with this book, in <filename>fileinfo_fromdict.py</filename>.</para>  
      469 <note>  
      470 <title>史料记载</title>  
      471 <para>在 &python; 2.2之前的版本中,你不可以直接子类化字符串、列表以及字典之类的内建数据类型。 作为补偿, &python; 提供封装类来模拟内建数据类型的行为,比如:<classname>UserString</classname>, <classname>UserList</classname>, 和 &userdict_classname;。 通过混合使用普通和特殊方法, &userdict_classname; 类出色于模仿字典。 在 &python; 2.2 和其后的版本中,你可以直接从 &dict; 内建数据类型继承。本书 <filename>fileinfo_fromdict.py</filename> 中有这方面的一个例子。</para>  
    474 472 </note>  
    475   <para>In &python;, you can inherit directly from the &dict; built-in datatype, as shown in this example.  There are three differences here compared to the &userdict; version.</para>  
      473 <para>如例子中所示,在 &python; 中,你可以直接继承自内建数据类型 &dict;,这样做有三点与 &userdict; 不同。</para>  
    475 473 <example id="fileinfo.userdict.fromdict">  
    476   <title>Inheriting Directly from Built-In Datatype &dict;</title>  
      474 <title>直接继承自内建数据类型 &dict;</title>  
    476 474 <programlisting>  
    477 475 class FileInfo(dict):                  <co id="fileinfo.userdict.3.1"/>  
     
    484 482 <calloutlist>  
    485 483 <callout arearefs="fileinfo.userdict.3.1">  
    486   <para>The first difference is that you don't need to import the &userdict; module, since &dict; is a built-in datatype and is always available.  The second is that you are inheriting from &dict; directly, instead of from <function>UserDict.UserDict</function>.</para>  
      484 <para>第一个区别是你不需要导入 &userdict; 模块,因为 &dict; 是已经可以使用的内建数据类型。第二个区别是你不是继承自 <function>UserDict.UserDict</function> ,而是直接继承自 &dict;。</para>  
    486 484 </callout>  
    487 485 <callout arearefs="fileinfo.userdict.3.2">  
    488   <para>The third difference is subtle but important.  Because of the way &userdict; works internally, it requires you to manually call its &init; method to properly initialize its internal data structures.  &dict; does not work like this; it is not a wrapper, and it requires no explicit initialization.</para>  
      486 <para>第三个区别有些晦涩,但却很重要。  &userdict; 内部的工作方式要求你手工地调用它的 &init; 方法去正确初始化它的内部数据结构。 &dict; 并不这样工作,它不是一个封装所以不需要明确的初始化。</para>  
    488 486 </callout>  
    489 487 </calloutlist>  
     
    644 642 <note id="compare.strequals.java" role="compare" vendor="java">  
    645 643 <title>&python; &vs; &java; equality and identity</title>  
    646   <para>在 &java; 中,通过使用 <literal>str1 == str2</literal> 可以决定两个字符串变量是否指向同一块物理内存位置。这就做 <emphasis>对象同一性</emphasis>,在 &python; 中写为 <literal>str1 is str2</literal>。在 &java; 中为了比较两个字符串值,你要使用 <literal>str1.equals(str2)</literal>;在 &python; 中,你要使用 <literal>str1 == str2</literal>。 某些 &java; 程序员,他们已经被教授得认为,因为在 &java; 中 &comparisonequals; 是通过一致性而不是值来进行比较的,所以 &java; 是更好的地方。这些人要转到 &python; 上来可能要花些时间。  
    647   [todo]  
    648   In &java;, you determine whether two string variables reference the same physical memory location by using <literal>str1 == str2</literal>.  This is called <emphasis>object identity</emphasis>, and it is written in &python; as <literal>str1 is str2</literal>.  To compare string values in &java;, you would use <literal>str1.equals(str2)</literal>; in &python;, you would use <literal>str1 == str2</literal>.  &java; programmers who have been taught to believe that the world is a better place because &comparisonequals; in &java; compares by identity instead of by value may have a difficult time adjusting to &python;'s lack of such <quote>gotchas</quote>.</para>  
      644 <para>在 &java; 中,通过使用 <literal>str1 == str2</literal> 可以决定两个字符串变量是否指向同一块物理内存位置。这就做 <emphasis>对象同一性</emphasis>,在 &python; 中写为 <literal>str1 is str2</literal>。在 &java; 中为了比较两个字符串值,你要使用 <literal>str1.equals(str2)</literal>;在 &python; 中,你要使用 <literal>str1 == str2</literal>。 某些 &java; 程序员,他们已经被教授得认为,因为在 &java; 中 &comparisonequals; 是通过一致性而不是值来进行比较的,所以 &java; 是更好的地方。这些人要转到 &python; 上来可能要花些时间。在 &java; 中,你通过 <literal>str1 == str2</literal> 来半段两个字符串是否指向同一物理内存位置。这被称作 <emphasis>对象确定(object identity)</emphasis>, 在 &python; 中被写作 <literal>str1 is str2</literal>。 在 &java; 中比较字符串使用 <literal>str1.equals(str2)</literal> ,而在 &python; 中,你使用 <literal>str1 == str2</literal>。那些素来认为 &java; 中比较对象身份而不是值的 &comparisonequals; 令世界变得更好的 &java; 程序员可能对 &python; 中的<quote>这个缺少</quote>接收起来有困难。</para>  
    649 645 </note>  
    650 646 <para>在这个地方,你可能会想,<quote>所有这些工作只是为了在类中做一些我可以对一个内置数据类型所做的操作</quote>。不错,如果你能够从象字典一样的内置数据类型进行继承的话,事情就容易多了(那样整个 &userdict_classname; 类将完全不需要了)。但是也许你可以,专用方法仍然是有用的,因为它们可以用于任何的类,而不只是象 &userdict_classname; 的封装类。</para>  
     
    800 796 <abstract>  
    801 797 <title/>  
    802   <para>[todo]That's it for the hard-core object trickery.  你将在 <xref linkend="soap" endterm="soap.numberonly"/> 中看到一个真实世界应用程序的专有类方法, 它使用 &getattr; 创建一个到远程 Web 服务的代理。</para>  
      798 <para>实打实的对象把戏到此为止。 你将在 <xref linkend="soap" endterm="soap.numberonly"/> 中看到一个真实世界应用程序的专有类方法, 它使用 &getattr; 创建一个到远程 Web 服务的代理。</para>  
    802 798 </abstract>  
    803 799 <para>下一章将继续使用本章的例程探索其他 &python; 的概念, 例如:异常, 文件对象 和 &for; 循环。</para>  
    818 814 <?dbhtml filename="file_handling/index.html"?>  
    819 815 <title>异常和文件处理</title>  
    820   <titleabbrev id="filehandling.numberonly">Chapter 6</titleabbrev>  
      816 <titleabbrev id="filehandling.numberonly">第 6 章</titleabbrev>  
    820 816 <abstract>  
    821 817 <title/>  
  • zh-translations/branches/diveintopython-zh-5.4/zh-cn/xml/soap.xml

    r832 r858  
    5 5 <titleabbrev id="soap.numberonly">第 12 章</titleabbrev>  
    6 6 <abstract>  
    7   <para><xref linkend="oa" endterm="oa.numberonly"/> 关注 HTTP 上面向文档的web 服务。 <quote>输入参数</quote> 是 &url;, <quote>返回值</quote> 是需要你来解析的一个实际的 XML 文档</para>  
    8   <para>本章将更加结构化地关注 &soap; web 服务。 &soap;允许你模拟返回自有数据类型的函数调用,而不仅仅是直接地矗立 HTTP 请求和XML文档. 正像你将要看到的,这个描述恰如其分;你可以使用标准&python;调用语法通过&soap;库去调用一个函数,这个函数也自然会返回 &python; 对象和值. 但解开这层面纱, &soap; 库实际上扮演了一个多XML文档和远程服务器参与的复杂处理过程。</para>  
    9   <para>&soap; 的贴切定义很复杂,不要误认为 &soap; 就是用于调用远程函数。有些人觉得应该补充上: &soap; 还允许单向异步的信息通过,并且是面向文档的Web服务。 这样想法的人是正确的,&soap; 的确是这样,但却不止于此。但这一章的重点在于所谓的 <quote>RPC-style</quote> &soap; -- 调用远程函数获得返回结果。</para>  
      7 <para><xref linkend="oa" endterm="oa.numberonly"/> 关注 HTTP 上面向文档的web 服务。 <quote>输入参数</quote> 是 &url;, <quote>返回值</quote> 是需要你来解析的一个实际的 XML 文档。</para>  
      8 <para>本章将关注更加结构化地 &soap; web 服务。 &soap; 允许你模拟返回自有数据类型的函数调用,而不仅仅是直接地[todo]矗立 HTTP 请求和 XML 文档。 正像你将要看到的,这个描述恰如其份;你可以使用标准 &python; 调用语法通过 &soap; 库去调用一个函数,这个函数也自然会返回 &python; 对象和值。 但解开这层面纱, &soap; 库实际上扮演了一个多 XML 文档和远程服务器参与的复杂处理过程。</para>  
      9 <para>&soap; 的贴切定义很复杂,不要误认为 &soap; 就是用于调用远程函数。有些人觉得应该补充上: &soap; 还允许单向异步的信息通过,并且是面向文档的 Web 服务。 有这样想法的人是正确的,&soap; 的确是这样,但却不止于此。但这一章的重点在于所谓的 <quote>RPC-style</quote> &soap; —— 调用远程函数获得返回结果。</para>  
    10 10 </abstract>  
    11 11 <section id="soap.divein">  
     
    19 19 <programlisting>from SOAPpy import WSDL  
    20 20  
    21   # 你应该理解下面这两个值;  
    22   # 察看 http://www.google.com/apis/  
      21 # you'll need to configure these two values;  
      22 # see http://www.google.com/apis/  
    23 23 WSDLFILE = '/path/to/copy/of/GoogleSearch.wsdl'  
    24 24 APIKEY = 'YOUR_GOOGLE_API_KEY'  
     
    103 103 <step><para>安装完成后,关闭安装程序,没有任何安装成功的昭示(并没有在开始菜单、快捷栏或桌面出现图标)。因为  &pyxml; 仅仅是被其他程序调用的 &xml; 的库集合。</para></step>  
    104 104 </procedure>  
    105   <para>希望检验 &pyxml; 安装的是否正确,可以运行 &python; &ide; ,如下面看到的可以看到 &xml; 库的安装版本。</para>  
      105 <para>希望检验 &pyxml; 安装的是否正确,可以运行 &python; &ide; ,下面的指令可以看到 &xml; 库的安装版本。</para>  
    105 105 <example>  
    106 106 <title>检验 &pyxml; 安装</title>  
     
    116 116 <section>  
    117 117 <title>安装 &fpconst;</title>  
    118   <para>你所需要安装的第二个库是 &fpconst;,使一系列支持 IEEE754 double-precision 特殊值得常量和函数。提供了对 Not-a-Number (NaN), Positive Infinity (Inf), and Negative Infinity (-Inf)等特殊值得支持,这是 &soap; 数据类型规范的组成部分。</para>  
      118 <para>你所需要安装的第二个库是 &fpconst;,使一系列支持 IEEE754 double-precision 特殊值得常量和函数。提供了对 Not-a-Number (NaN), Positive Infinity (Inf) 和 Negative Infinity (-Inf) 等特殊值得支持,这是 &soap; 数据类型规范的组成部分。</para>  
    118 118 <procedure>  
    119 119 <title/>  
     
    122 122 <step><para>从 <ulink url="&url_fpconst;"/> 下载 &fpconst; 的最新版本。</para></step>  
    123 123 <step><para>提供了两种格式的下载: <filename>.tar.gz</filename> 和 <filename>.zip</filename>。如果你使用的是 &windows; 操作系统,下载 <filename>.zip</filename> 文件;其他情况下应该下载 <filename>.tar.gz</filename> 文件。</para></step>  
    124   <step><para>对这个文件进行解压缩。在D &windows; XP 上你可以鼠标右键单击这个文件并选择“解压文件”;在较早的 &windows; 版本上则需要WinZip之类的第三方解压程序。在 &macosx;上,可以右键单击压缩文件进行解压。</para></step>  
      124 <step><para>对这个文件进行解压缩。在 &windows; XP 上你可以鼠标右键单击这个文件并选择“解压文件”;在较早的 &windows; 版本上则需要 WinZip 之类的第三方解压程序。在 &macosx; 上,可以右键单击压缩文件进行解压。</para></step>  
    124 124 <step><para>打开命令提示符窗口并定位到解压目录。</para></step>  
    125 125 <step><para>键入 <userinput>python setup.py install</userinput> 运行安装程序。</para></step>  
    126 126 </procedure>  
    127   <para>希望检验 &fpconst; 安装的是否正确, 运行 &python; &ide; 并查看版本号</para>  
      127 <para>希望检验 &fpconst; 安装的是否正确, 运行 &python; &ide; 并查看版本号</para>  
    127 127 <example>  
    128 128 <title>检验 &fpconst; 安装</title>  
     
    139 139 <section>  
    140 140 <title>安装 &soappy;</title>  
    141   <para>第三个,也是最后一个需要安装的库是 &soap; 库本身: &soappy;.</para>  
      141 <para>第三个,也是最后一个需要安装的库是 &soap; 库本身: &soappy;</para>  
    141 141 <procedure>  
    142 142 <title/>  
    143 143 <para>下面是安装 &soappy;的过程</para>  
    144   <step><para>访问 <ulink url="&url_soappy;"/> 并选择 &soappy; 部分中最新的官方发布</para></step>  
      144 <step><para>访问 <ulink url="&url_soappy;"/> 并选择 &soappy; 部分中最新的官方发布</para></step>  
    144 144 <step><para>提供了两种格式的下载。如果你使用的是 &windows;,那么下载 <filename>.zip</filename> 文件;其他情况则下载 <filename>.tar.gz</filename> 文件。</para></step>  
    145 145 <step><para>和安装 &fpconst; 时一样先解压下载的文件.</para></step>  
    146   <step><para>打开名利提示符窗口并定位到解压 &soappy; 文件的目录.</para></step>  
    147   <step><para>键入 <userinput>python setup.py install</userinput> 运行安装程序</para></step>  
      146 <step><para>打开名利提示符窗口并定位到解压 &soappy; 文件的目录。</para></step>  
      147 <step><para>键入 <userinput>python setup.py install</userinput> 运行安装程序。</para></step>  
    148 148 </procedure>  
    149   <para>希望检验 &soappy; 安装的是否正确, 运行 &python; &ide; 并查看版本号</para>  
      149 <para>希望检验 &soappy; 安装的是否正确, 运行 &python; &ide; 并查看版本号</para>  
    149 149 <example>  
    150 150 <title>检验 &soappy; 安装</title>  
     
    181 181 <calloutlist>  
    182 182 <callout arearefs="soap.firststeps.1.1">  
    183   <para>你通过<classname>SOAPProxy</classname>这个 proxy 类访问远程 &soap; 服务器。这个 proxy 处理了所有的 &soap; 内部事务,其中包括:根据函数名和参数列表创建XML请求文档并将这个请求文档通过HTTP发送到远程 &soap; 服务器,解析XML返回文档并创建 &python; 原始返回值。在下一节中你将看到这个XML文档。</para>  
      183 <para>你通过 <classname>SOAPProxy</classname> 这个 proxy 类访问远程 &soap; 服务器。这个 proxy 处理了所有的 &soap; 内部事务,其中包括:根据函数名和参数列表创建 XML 请求文档并将这个请求文档通过 HTTP 发送到远程 &soap; 服务器,解析 XML 返回文档并创建 &python; 原始返回值。在下一节中你将看到这个 XML 文档。</para>  
    183 183 </callout>  
    184 184 <callout arearefs="soap.firststeps.1.2">  
    185 185 <para>每个 &soap; 服务都有一个 &url; 用以处理所有请求。 相同的 &url; 可以用于所有的函数请求。每个特定服务则只有一个函数。但稍后你将看到的 Google &api; 却有多个函数。这个服务的 &url; 提供给所有函数分享。</para>  
    186   <para>每个 &soap; 服务都有一个命名空间(namespace),这个命名空间是由服务器任意命名的。这不过是为了调用 &soap; 方法而设置的。它使得服务器为多个不相关的服务提供服务 &url; 共享成为可能。这与 &python; 中模块到<link linkend="kgp.packages">packages</link>的关系类似。</para>  
      186 <para>每个 &soap; 服务都有一个命名空间(namespace),这个命名空间是由服务器任意命名的。这不过是为了调用 &soap; 方法而设置的。[todo]它使得服务器为多个不相关的服务提供服务 &url; 共享成为可能。这与 &python; 中模块到 <link linkend="kgp.packages">packages</link> 的关系类似。</para>  
    186 186 </callout>  
    187 187 <callout arearefs="soap.firststeps.1.3">  
     
    195 195 </calloutlist>  
    196 196 </example>  
    197   <para>让我们看一看这些背后的工作</para>  
      197 <para>让我们看一看这些背后的工作</para>  
    197 197 </section>  
    198 198  
     
    205 205 <para> &soap; 提供了一个很方便的方法用以查看背后的情形。</para>  
    206 206 </abstract>  
    207   <para><classname>SOAPProxy</classname>的两个小设置就可以打开查错模式。</para>  
      207 <para><classname>SOAPProxy</classname> 的两个小设置就可以打开查错模式。</para>  
    207 207 <example>  
    208 208 <title> &soap; 网络服务查错</title>  
     
    253 253 </callout>  
    254 254 <callout arearefs="soap.debug.1.2">  
    255   <para>然后,通过设置 <varname>server.config.dumpSOAPIn</varname>和 <varname>server.config.dumpSOAPOut</varname>打开查错模式。</para>  
      255 <para>然后,通过设置 <varname>server.config.dumpSOAPIn</varname> 和 <varname>server.config.dumpSOAPOut</varname> 打开查错模式。</para>  
    255 255 </callout>  
    256 256 <callout arearefs="soap.debug.1.3">  
     
    260 260 </calloutlist>  
    261 261 </example>  
    262   <para>大部分 XML 请求文档都基于���板文件。 忽略所有命名空间声明这些对于所有 &soap; 调用都一成不变的东西。这个<quote>函数调用</quote>的核心是<sgmltag>&lt;Body></sgmltag>当中的部分:</para>  
      262 <para>大部分 XML 请求文档都基于���板文件。 忽略所有命名空间声明这些对于所有 &soap; 调用都一成不变的东西。这个 <quote>函数调用</quote> 的核心是<sgmltag>&lt;Body></sgmltag> 当中的部分:</para>  
    262 262 <informalexample>  
    263 263 <programlisting>  
     
    271 271 <calloutlist>  
    272 272 <callout arearefs="soap.debug.2.1">  
    273   <para>这个元素名<function>getTemp</function>就是函数名。<classname>SOAPProxy</classname> 把 <link linkend="kgp.handler">&getattr; 当作一个发报机</link>。有别使用方法名分别调用本地方法,这里使用方法名构造了一个 XML 请求文档。</para>  
      273 <para>这个元素名 <function>getTemp</function> 就是函数名。<classname>SOAPProxy</classname> 把 <link linkend="kgp.handler">&getattr; 当作一个发报机</link>。有别使用方法名分别调用本地方法,这里使用方法名构造了一个 XML 请求文档。</para>  
    273 273 </callout>  
    274 274 <callout arearefs="soap.debug.2.2">  
    275   <para>函数的 XML 元素被存储于一个特别的命名空间,这个命名空间就是你在建立 <classname>SOAPProxy</classname> 对象时所指定的那个命名空间。也不必为 <literal>SOAP-ENC:root</literal> 而苦恼,因为它也是基于���板文件的。</para>  
      275 <para>函数的 XML 元素被存储于一个特别的命名空间,这个命名空间就是你在建立 <classname>SOAPProxy</classname> 对象时所指定的那个命名空间。也不必为 <literal>SOAP-ENC:root</literal> 而苦恼,因为它也是基于���板文件的。</para>  
    275 275 </callout>  
    276 276 <callout arearefs="soap.debug.2.3">  
    277   <para>函数的参数也被记入 XML 文档。  <classname>SOAPProxy</classname> 查看并确定每个参数的数据类型(这里是string字符串类型)。参数的数据类型记入 <literal>xsi:type</literal> 属性,并在其后记入实际的字符串值。</para>  
      277 <para>函数的参数也被记入 XML 文档。  <classname>SOAPProxy</classname> 查看并确定每个参数的数据类型(这里是 string 字符串类型)。参数的数据类型记入 <literal>xsi:type</literal> 属性,并在其后记入实际的字符串值。</para>  
    277 277 </callout>  
    278 278 </calloutlist>  
    279 279 </informalexample>  
    280   <para>返回的 XML 文档同样容易理解,重点在于知道应该忽略掉哪些内容。把注意力集中在 <sgmltag>&lt;Body></sgmltag>部分:</para>  
      280 <para>返回的 XML 文档同样容易理解,重点在于知道应该忽略掉哪些内容。把注意力集中在 <sgmltag>&lt;Body></sgmltag> 部分:</para>  
    280 280 <informalexample>  
    281 281 <programlisting>  
     
    292 292 <calloutlist>  
    293 293 <callout arearefs="soap.debug.3.1">  
    294   <para>服务器传回的值记录在<sgmltag>&lt;getTempResponse></sgmltag> 部分的几行中。 通常包括函数名和 <literal>回应(Response)</literal>。当然其他的内容也可能出现在这里,但 <classname>SOAPProxy</classname> 所重视的不是这里的元素名,而是命名空间。</para>  
      294 <para>服务器传回的值记录在 <sgmltag>&lt;getTempResponse></sgmltag> 部分的几行中。 通常包括函数名和回应 <literal>(Response)</literal>。当然其他的内容也可能出现在这里,但 <classname>SOAPProxy</classname> 所重视的不是这里的元素名,而是命名空间。</para>  
    294 294 </callout>  
    295 295 <callout arearefs="soap.debug.3.2">  
    296   <para>服务器返回时所使用的命名空间就是在请求时所用的命名空间,也就是在创建 <classname>SOAPProxy</classname>对象时所指定的命名空间。本章稍后的部分中,我们将看到在创建 <classname>SOAPProxy</classname> 对象时忘记指定功能名空间会怎样。</para>  
      296 <para>服务器返回时所使用的命名空间就是在请求时所用的命名空间,也就是在创建 <classname>SOAPProxy</classname> 对象时所指定的命名空间。本章稍后的部分中,我们将看到在创建 <classname>SOAPProxy</classname> 对象时忘记指定功能名空间会怎样。</para>  
    296 296 </callout>  
    297 297 <callout arearefs="soap.debug.3.3">  
    298   <para>这是返回值和它的数据类型(浮点float类型)。 <classname>SOAPProxy</classname> 使用显性数据类型创建一个原始数据类型的 &python; 对象并返回之。</para>  
      298 <para>这是返回值和它的数据类型(浮点类型 float)。 <classname>SOAPProxy</classname> 使用显性数据类型创建一个原始数据类型的 &python; 对象并返回之。</para>  
    298 298 </callout>  
    299 299 </calloutlist>  
     
    311 311 <para><classname>SOAPProxy</classname> 类本地方法调用并透明地转向到远程 &soap; 方法。 正如你所看到的,这是很多的工作,<classname>SOAPProxy</classname> 快速和透明地完成他们。它没有做到的是提供任何方法自省的手段。</para>  
    312 312 </abstract>  
    313   <para>试想一下:前面两部分所展现的调用只有一个参数和返回的简单远程 &soap; 方法。服务 &url;和一系列参数及它们的数据类型需要被知道并跟踪。任何的缺失或错误都会导致整体的失败。</para>  
    314   <para>这并没有什么可惊讶的。如果我要调用一个本地函数,我需要知道函数所在的包和模块名(与之对应的则是服务 &url;he 命名空间)。我还需要知道正确的函数名以及其函数个数。 &python; 精妙地在不需要明示类型,但我还是需要知道有多少个参数需要传递,多少个值将被返回。</para>  
    315   <para>最大的区别就在于内省。 就像你在 <xref linkend="apihelper" endterm="apihelper.numberonly"/>看到的那样, &python; 擅长于让你实时地去探索模块和函数的情况。你可以对一个模块中的所有函数进行列表,并不费吹灰之力地明了函数的声明和参数情况。</para>  
      313 <para>试想一下:前面两部分所展现的调用只有一个参数和返回的简单远程 &soap; 方法。服务 &url; 和一系列参数及它们的数据类型需要被知道并跟踪。任何的缺失或错误都会导致整体的失败。</para>  
      314 <para>这并没有什么可惊讶的。如果我要调用一个本地函数,我需要知道函数所在的包和模块名(与之对应的则是服务 &url; 和命名空间)。我还需要知道正确的函数名以及其函数个数。 &python; 精妙地在不需要明示类型,但我还是需要知道有多少个参数需要传递,多少个值将被返回。</para>  
      315 <para>最大的区别就在于内省。 就像你在 <xref linkend="apihelper" endterm="apihelper.numberonly"/> 看到的那样, &python; 擅长于让你实时地去探索模块和函数的情况。你可以对一个模块中的所有函数进行列表,并不费吹灰之力地明了函数的声明和参数情况。</para>  
    316 316 <para>&wsdl; 允许你对 &soap; 网络服务做相同的事情。 &wsdl; 是 <quote>网络服务描述语言(Web Services Description Language)</quote>的缩写。 尽管是为自如地表述多种类型的网络服务而设定,却也经常用于描述 &soap; 网络服务。</para>  
    317   <para>一个 &wsdl; 文件不过就是一个文件。 更具体地讲,是一个 XML 文件。通常存储于你所访问的 &soap; 网络服务这个被描述对象所在的服务器上,并没有什么特殊之处。 在本章稍后的位置,我们将下载Google API的 &wsdl; 文件并在本地使用它。 这并不意味着本地调用Google, 这个 &wsdl; 文件所描述的仍旧是Google服务器上的远程函数。</para>  
      317 <para>一个 &wsdl; 文件不过就是一个文件。 更具体地讲,是一个 XML 文件。通常存储于你所访问的 &soap; 网络服务这个被描述对象所在的服务器上,并没有什么特殊之处。 在本章稍后的位置,我们将下载 Google API 的 &wsdl; 文件并在本地使用它。 这并不意味着本地调用 Google, 这个 &wsdl; 文件所描述的仍旧是 Google 服务器上的远程函数。</para>  
    317 317 <para>在 &wsdl; 文件中描述了调用相应的 &soap; 网络服务的一切:</para>  
    318 318 <itemizedlist>  
     
    330 330 <section id="soap.introspection">  
    331 331 <?dbhtml filename="soap_web_services/introspection.html"?>  
    332   <title>Introspecting &soap; Web Services with &wsdl;</title>  
      332 <title>以 &wsdl; 进行 &soap; 内省</title>  
    332 332 <abstract>  
    333 333 <title/>  
     
    350 350 </callout>  
    351 351 <callout arearefs="soap.introspection.1.2">  
    352   <para>使用一个 &wsdl; 文件,你还是要用到一个 proxy 类:<classname>WSDL.Proxy</classname>,它只一个参数: &wsdl; 文件。 我所说的是把存储在远程服务器上的 &wsdl; 的 &url;, 但是这个 proxy 类对于本地的 &wsdl; 副本工作同样出色。创建 &wsdl; proxy 将会下载 &wsdl; 文件并解析它, 所以如果 &wsdl; 文件有任何问题(或者由于网络问题不能获得)你会立刻知道。</para>  
      352 <para>使用一个 &wsdl; 文件,你还是要用到一个 proxy 类:<classname>WSDL.Proxy</classname>,它只一个参数: &wsdl; 文件。 我所说的是把存储在远程服务器上的 &wsdl; 的 &url;, 但是这个 proxy 类对于本地的 &wsdl; 副本工作同样出色。创建 &wsdl; proxy 将会下载 &wsdl; 文件并解析它, 所以如果 &wsdl; 文件有任何问题(或者由于网络问题不能获得)你会立刻知道。</para>  
    352 352 </callout>  
    353 353 <callout arearefs="soap.introspection.1.3">  
    354   <para> &wsdl; proxy 类以 &python; 字典<varname>server.methods</varname>的方式揭示有效函数。所以列表有效方法就像调用字典方法<methodname>keys()</methodname>一样简单。</para>  
      354 <para> &wsdl; proxy 类以 &python; 字典 <varname>server.methods</varname> 的方式揭示有效函数。所以列表有效方法就像调用字典方法 <methodname>keys()</methodname> 一样简单。</para>  
    354 354 </callout>  
    355 355 </calloutlist>  
     
    371 371 <calloutlist>  
    372 372 <callout arearefs="soap.introspection.2.1">  
    373   <para> <varname>server.methods</varname> 字典中记录一个 &soappy;的特别结构,被称为<classname>CallInfo</classname>.  <classname>CallInfo</classname> 对象中包含着特定函数和函数参数的信息</para>  
      373 <para> <varname>server.methods</varname> 字典中记录一个 &soappy; 的特别结构,被称为 <classname>CallInfo</classname>。  <classname>CallInfo</classname> 对象中包含着特定函数和函数参数的信息。</para>  
    373 373 </callout>  
    374 374 <callout arearefs="soap.introspection.2.2">  
    375   <para>函数参数信息存储在<varname>callInfo.inparams</varname>中,这是一个记录每一个参数信息的<classname>ParameterInfo</classname>对象的&python;列表。</para>  
      375 <para>函数参数信息存储在 <varname>callInfo.inparams</varname> 中,这是一个记录每一个参数信息的 <classname>ParameterInfo</classname> 对象的 &python; 列表。</para>  
    375 375 </callout>  
    376 376 <callout arearefs="soap.introspection.2.3">  
    377   <para>每个 <classname>ParameterInfo</classname> 对象包含一个 <varname>name</varname> 属性,这便是参数名。再通过 你不需要 &soap; 调用函数时不需要知道参数名,但 &soap; 却支持在调用函数时使用参数名的情形(类似于 &python; )。如果使用参数名, <classname>WSDL.Proxy</classname> 将会正确地把这些参数关联到远程函数。</para>  
      377 <para>每个 <classname>ParameterInfo</classname> 对象包含一个 <varname>name</varname> 属性,这便是参数名。[todo]再通过 你不需要 &soap; 调用函数时不需要知道参数名,但 &soap; 却支持在调用函数时使用参数名的情形(类似于 &python; )。如果使用参数名, <classname>WSDL.Proxy</classname> 将会正确地把这些参数关联到远程函数。</para>  
    377 377 </callout>  
    378 378 <callout arearefs="soap.introspection.2.4">  
    379   <para>每个参数都是都是显性类型的,在XML Schema 中以数据类型定义。你可以在上一节中发现这一点:XML Schema 命名空间是我让你忽略的模版的一部分。就目前的而言,你还是可以继续忽略它。 <varname>zipcode</varname> 参数是一个字符串,如果你向<classname>WSDL.Proxy</classname>对象传递一个 &python; 字符串,它会被正确地关联和传递到服务器。</para>  
      379 <para>每个参数都是都是显性类型的,在 XML Schema 中以数据类型定义。你可以在上一节中发现这一点:XML Schema 命名空间是我让你忽略的模版的一部分。就目前而言,你还是可以继续忽略它。 <varname>zipcode</varname> 参数是一个字符串,如果你向 <classname>WSDL.Proxy</classname> 对象传递一个 &python; 字符串,它会被正确地关联和传递到服务器。</para>  
    379 379 </callout>  
    380 380 </calloutlist>  
    381 381 </example>  
    382   <para>&wsdl; 还允许你自省函数的返回值</para>  
      382 <para>&wsdl; 还允许你自省函数的返回值</para>  
    382 382 <example>  
    383 383 <title>揭示方法返回值</title>  
     
    400 400 </callout>  
    401 401 <callout arearefs="soap.introspection.3.2">  
    402   <para> <classname>ParameterInfo</classname> 对象包含 <varname>name</varname> 和 <varname>type</varname>。这个函数返回一个浮点值,它的名字是<varname>return</varname>。</para>  
      402 <para> <classname>ParameterInfo</classname> 对象包含 <varname>name</varname> 和 <varname>type</varname>。这个函数返回一个浮点值,它的名字是 <varname>return</varname>。</para>  
    402 402 </callout>  
    403 403 </calloutlist>  
    404 404 </example>  
    405   <para>让我们整合一下,通过 &wsdl; proxy 调用一个 &soap; 网络服务</para>  
      405 <para>让我们整合一下,通过 &wsdl; proxy 调用一个 &soap; 网络服务</para>  
    405 405 <example>  
    406 406 <title>通过 &wsdl; proxy 调用一个 &soap; 网络服务</title>  
     
    450 450 <calloutlist>  
    451 451 <callout arearefs="soap.introspection.4.1">  
    452   <para>比直接调用 &soap; 服务时的设置简单,因为在 &wsdl; 文件中包含着调用服务所需要的服务 &url; 和命名空间。创建 <classname>WSDL.Proxy</classname> 对象来下载 &wsdl; 文件,解析之,并设置一个用以调用实际的 &soap;网络服务的  <classname>SOAPProxy</classname> 对象。</para>  
      452 <para>比直接调用 &soap; 服务时的设置简单,因为在 &wsdl; 文件中包含着调用服务所需要的服务 &url; 和命名空间。创建 <classname>WSDL.Proxy</classname> 对象来下载 &wsdl; 文件,解析之,并设置一个用以调用实际的 &soap; 网络服务的  <classname>SOAPProxy</classname> 对象。</para>  
    452 452 </callout>  
    453 453 <callout arearefs="soap.introspection.4.2">  
    454   <para> <classname>WSDL.Proxy</classname> 对象一旦被创建,你可以像调用 <classname>SOAPProxy</classname> 对象一样简单地调用一个函数。这并不奇怪,<classname>WSDL.Proxy</classname> 就是一个具有自省方法的<classname>SOAPProxy</classname>封装套,所以调用函数的语法也是一样的。</para>  
      454 <para> <classname>WSDL.Proxy</classname> 对象一旦被创建,你可以像调用 <classname>SOAPProxy</classname> 对象一样简单地调用一个函数。这并不奇怪,<classname>WSDL.Proxy</classname> 就是一个具有自省方法的 <classname>SOAPProxy</classname> 封装套件,所以调用函数的语法也是一样的。</para>  
    454 454 </callout>  
    455 455 <callout arearefs="soap.introspection.4.3">  
    456   <para>你可以通过<varname>server.soapproxy</varname>访问 <classname>WSDL.Proxy</classname>的 <classname>SOAPProxy</classname>。这对于打开查错模式很重要,这样一来当你通过&wsdl; proxy调用函数时,它的 <classname>SOAPProxy</classname> 将会把线路上来往的XML文档丢下来。</para>  
      456 <para>你可以通过 <varname>server.soapproxy</varname> 访问 <classname>WSDL.Proxy</classname> 的 <classname>SOAPProxy</classname>。这对于打开查错模式很重要,这样一来当你通过 &wsdl; proxy 调用函数时,它的 <classname>SOAPProxy</classname> 将会把线路上来往的 XML 文档[todo]丢下来。</para>  
    456 456 </callout>  
    457 457 </calloutlist>  
     
    469 469 <para>让我们回到这章开始时你看到的那段代码,获得比当前气温更有价值和令人振奋的信息。</para>  
    470 470 </abstract>  
    471   <para>Google 提供了一个 &soap; &api; ,以便通过程序进行Google搜索。使用它的前提是,你注册了Google网络服务。</para>  
      471 <para>Google 提供了一个 &soap; &api; ,以便通过程序进行 Google 搜索。使用它的前提是,你注册了 Google 网络服务。</para>  
    471 471 <procedure>  
    472 472 <title>注册 Google 网络服务</title>  
    473   <step><para>访问 <ulink url="http://www.google.com/apis/"/> 并创建一个账号。 唯一的需要是提供一个E-mail地址。 注册之后,你将通过E-mail收到你的 Google API 许可证(license key)。 你需要在调用 Google 搜索函数时使用这个许可证。</para></step>  
    474   <step><para>还是在 <ulink url="http://www.google.com/apis/"/>上,下载 Google 网络 APIs 开发工具包(kit)。它包含着包括&python;在内的多种语言的样例代码,更重要的是它包含着 &wsdl; 文件。</para></step>  
      473 <step><para>访问 <ulink url="http://www.google.com/apis/"/> 并创建一个账号。 唯一的需要是提供一个 E-mail 地址。 注册之后,你将通过 E-mail 收到你的 Google API 许可证(license key)。 你需要在调用 Google 搜索函数时使用这个许可证。</para></step>  
      474 <step><para>还是在 <ulink url="http://www.google.com/apis/"/> 上,下载 Google 网络 APIs 开发工具包(kit)。它包含着包括 &python; 在内的多种语言的样例代码,更重要的是它包含着 &wsdl; 文件。</para></step>  
    475 475 <step><para>解压这个开发工具包并找到 <filename>GoogleSearch.wsdl</filename>。将这个文件拷贝到你本地驱动器的一个永久地址。在本章后面位置你会用到它。</para></step>  
    476 476 </procedure>  
    477   <para>你有了开发许可证和 Google &wsdl; 文件之后就可以和Google网络服务打交道了。</para>  
      477 <para>你有了开发许可证和 Google &wsdl; 文件之后就可以和 Google 网络服务打交道了。</para>  
    477 477 <example>  
    478 478 <title>内省 Google 网络服务</title>  
     
    500 500 <calloutlist>  
    501 501 <callout arearefs="soap.google.1.1">  
    502   <para>步入Google 网络服务很简单:建立一个 <classname>WSDL.Proxy</classname> 对象并指向到你复制到本地的 Google &wsdl;文件。</para>  
      502 <para>步入 Google 网络服务很简单:建立一个 <classname>WSDL.Proxy</classname> 对象并指向到你复制到本地的 Google &wsdl; 文件。</para>  
    502 502 </callout>  
    503 503 <callout arearefs="soap.google.1.2">  
    504   <para>由 &wsdl; 文件可知,Google提供三个函数:<function>doGoogleSearch</function>, <function>doGetCachedPage</function>和<function>doSpellingSuggestion</function>。顾名思义,执行Google搜索并返回结果,获得Google最后一次扫描该页时获得的缓存,为基于常见拼写错误提出单词拼写建议。</para>  
      504 <para>由 &wsdl; 文件可知,Google 提供三个函数:<function>doGoogleSearch</function>, <function>doGetCachedPage</function> 和 <function>doSpellingSuggestion</function>。顾名思义,执行 Google 搜索并返回结果,获得 Google 最后一次扫描该页时获得的缓存,为基于常见拼写错误提出单词拼写建议。</para>  
    504 504 </callout>  
    505 505 <callout arearefs="soap.google.1.3">  
    506   <para><function>doGoogleSearch</function> 函数需要一系列不同类型的参数。注意: &wsdl; 文件可以告诉你有哪些参数和他们的参数类型,但不能告诉你它们的含义和使用方法。在参数值有限定的情况下,理论上它能够告诉你参数的取值范围,但Google的 &wsdl; 没有那么细化。 <classname>WSDL.Proxy</classname> 不会变魔术,它只能给你 &wsdl; 文件中提供的信息。</para>  
      506 <para><function>doGoogleSearch</function> 函数需要一系列不同类型的参数。注意: &wsdl; 文件可以告诉你有哪些参数和他们的参数类型,但不能告诉你它们的含义和使用方法。在参数值有限定的情况下,理论上它能够告诉你参数的取值范围,但 Google 的 &wsdl; 没有那么细化。 <classname>WSDL.Proxy</classname> 不会变魔术,它只能给你 &wsdl; 文件中提供的信息。</para>  
    506 506 </callout>  
    507 507 </calloutlist>  
     
    512 512 <para>这里简要地列出了 <function>doGoogleSearch</function> 函数的所有参数:</para>  
    513 513 <itemizedlist>  
    514   <listitem><para><varname>key</varname> —— 你注册Google网络服务时获得的Google API 许可证。</para></listitem>  
    515   <listitem><para><varname>q</varname> - 你要搜索的词或词组。其语法与Google的网站表单处完全相同,你所知道的高级搜索语法和技巧这里完全适用。</para></listitem>  
    516   <listitem><para><varname>start</varname> —— 起始的结果编号。与使用Google网页交互搜索时相同,这个函数每次返回10个结果。如果你需要查看第二<quote></quote>页结果则需要将 <varname>start</varname> 设置为10。</para></listitem>  
      514 <listitem><para><varname>key</varname> —— 你注册 Google 网络服务时获得的 Google API 许可证。</para></listitem>  
      515 <listitem><para><varname>q</varname> —— 你要搜索的词或词组。其语法与 Google 的网站表单处完全相同,你所知道的高级搜索语法和技巧这里完全适用。</para></listitem>  
      516 <listitem><para><varname>start</varname> —— 起始的结果编号。与使用 Google 网页交互搜索时相同,这个函数每次返回10个结果。如果你需要查看 <quote>第二</quote> 页结果则需要将 <varname>start</varname> 设置为10。</para></listitem>  
    517 517 <listitem><para><varname>maxResults</varname> —— 返回的结果个数。目前的值是10,当然如果你只对少数返回结果感兴趣或者希望节省网络带宽,也可以定义为返回更少的结果。</para></listitem>  
    518   <listitem><para><varname>filter</varname> —— 如果设置为 &true;,Google将会过滤结果中重复的页面。</para></listitem>  
    519   <listitem><para><varname>restrict</varname> —— 这里设置 <literal>country</literal> 并跟上一个国家代码可以限定只返回特定国家的结果。例如: <literal>countryUK</literal> 用于在英国搜索页面。你也可以设定 <literal>linux</literal>, <literal>mac</literal> 或者 <literal>bsd</literal> 以便搜索Google定义的技术站点组,或者设为 <literal>unclesam</literal> 来搜索美国政府站点。</para></listitem>  
      518 <listitem><para><varname>filter</varname> —— 如果设置为 &true;,Google 将会过滤结果中重复的页面。</para></listitem>  
      519 <listitem><para><varname>restrict</varname> —— 这里设置 <literal>country</literal> 并跟上一个国家代码可以限定只返回特定国家的结果。例如: <literal>countryUK</literal> 用于在英国搜索页面。你也可以设定 <literal>linux</literal>, <literal>mac</literal> 或者 <literal>bsd</literal> 以便搜索 Google 定义的技术站点组,或者设为 <literal>unclesam</literal> 来搜索美国政府站点。</para></listitem>  
    520 520 <listitem><para><varname>safeSearch</varname> —— 如果设置为 &true;, Google 将会过滤掉色情站点。</para></listitem>  
    521 521 <listitem><para><varname>lr</varname> (<quote>语言限制</quote>) —— 这里设置语言限定值返回特定语言的站点。</para></listitem>  
    522   <listitem><para><varname>ie</varname> 和 <varname>oe</varname> (<quote>输入编码input encoding</quote> and <quote>输出编码output encoding</quote>) —— Deprecated,都应该是 <literal>utf-8</literal>.</para></listitem>  
      522 <listitem><para><varname>ie</varname> and <varname>oe</varname> (<quote>输入编码</quote> 和 <quote>输出编码</quote>) —— 不赞成使用,都应该是 <literal>utf-8</literal>。</para></listitem>  
    522 522 </itemizedlist>  
    523 523 <example>  
     
    539 539 <calloutlist>  
    540 540 <callout arearefs="soap.google.2.1">  
    541   <para>在设置好 <classname>WSDL.Proxy</classname> 对象之后,你可以使用十个参数来调用 <function>server.doGoogleSearch</function>。 记住在要使用你注册Google网络服务时授权给你自己的Google API 许可证。</para>  
      541 <para>在设置好 <classname>WSDL.Proxy</classname> 对象之后,你可以使用十个参数来调用 <function>server.doGoogleSearch</function>。 记住在要使用你注册 Google 网络服务时授权给你自己的 Google API 许可证。</para>  
    541 541 </callout>  
    542 542 <callout arearefs="soap.google.2.2">  
    543   <para>有很多的返回信息,但我们还是先来看一下实际的返回结果。它们被存储于 <varname>results.resultElements</varname>之中,你可以像使用普通的 &python; 列表那样来调用它。</para>  
      543 <para>有很多的返回信息,但我们还是先来看一下实际的返回结果。它们被存储于 <varname>results.resultElements</varname> 之中,你可以像使用普通的 &python; 列表那样来调用它。</para>  
    543 543 </callout>  
    544 544 <callout arearefs="soap.google.2.3">  
    545   <para><varname>resultElements</varname>中的每个元素都是一个包含<varname>URL</varname>, <varname>title</varname>, <varname>snippet</varname>以及其他属性的对象。基于这一点,你可以使用诸如<userinput>dir(results.resultElements[0])</userinput>的普通 &python; 自省技术来查看有效属性,或者通过 &wsdl; proxy 对象查看函数的<varname>outparams</varname>。 不同的方法能带给你相同的结果。</para>  
      545 <para><varname>resultElements</varname> 中的每个元素都是一个包含 <varname>URL</varname>, <varname>title</varname>, <varname>snippet</varname> 以及其他属性的对象。基于这一点,你可以使用诸如 <userinput>dir(results.resultElements[0])</userinput> 的普通 &python; 自省技术来查看有效属性,或者通过 &wsdl; proxy 对象查看函数的 <varname>outparams</varname>。 不同的方法能带给你相同的结果。</para>  
    545 545 </callout>  
    546 546 </calloutlist>  
     
    567 567 <calloutlist>  
    568 568 <callout arearefs="soap.google.3.1">  
    569   <para>这个搜索耗时 0.224919 秒。这部包括用于发送和接收 &soap; XML 文档的时间,仅仅是Google在接到搜索请求后执行搜索所花费的时间。</para>  
      569 <para>这个搜索耗时 0.224919 秒。这不包括用于发送和接收 &soap; XML 文档的时间,仅仅是 Google 在接到搜索请求后执行搜索所花费的时间。</para>  
    569 569 </callout>  
    570 570 <callout arearefs="soap.google.3.2">  
    571   <para>总共有接近30,000,000个结果信息,你可以通过以10递增地改变 <varname>start</varname> 参数来重复调用 <function>server.doGoogleSearch</function> 。</para>  
      571 <para>总共有接近30,000,000个结果信息,你可以通过以 10 递增地改变 <varname>start</varname> 参数来重复调用 <function>server.doGoogleSearch</function> 。</para>  
    571 571 </callout>  
    572 572 <callout arearefs="soap.google.3.3">  
    573   <para>对于有些请求,Google还返回一个<ulink url="http://directory.google.com/">Google Directory</ulink>中的类别列表。你可以用这些 URLs 到 <ulink url="http://directory.google.com/"/> 建立到 directory category 页面的链接。</para>  
      573 <para>对于有些请求,Google 还返回一个 <ulink url="http://directory.google.com/">Google Directory</ulink> 中的类别列表。你可以用这些 URLs 到 <ulink url="http://directory.google.com/"/> 建立到 directory category 页面的链接。</para>  
    573 573 </callout>  
    574 574 </calloutlist>  
     
    598 598 <para>是的,&soap; 网络服务的世界中也不总是欢乐和阳光。 有时候也会有故障。</para>  
    599 599 </abstract>  
    600   <para>正如你在���章中看到的,&soap; 牵扯了很多层面。 需要HTTP层,&soap; 需要向HTTP服务器发送XML文档并接收返回的XML文档。这样一来,你在 <xref linkend="oa"/> 学到的差错技术在这里都有了用武之地。你可以 <userinput>import httplib</userinput> 并设置<userinput>httplib.HTTPConnection.debuglevel = 1</userinput> 来查看潜在的 HTTP 传输。</para>  
      600 <para>正如你在���章中看到的,&soap; 牵扯了很多层面。 需要 HTTP 层,&soap; 需要向 HTTP 服务器发送 XML 文档并接收返回的 XML 文档。这样一来,你在 <xref linkend="oa"/> 学到的差错技术在这里都有了用武之地。你可以 <userinput>import httplib</userinput> 并设置 <userinput>httplib.HTTPConnection.debuglevel = 1</userinput> 来查看潜在的 HTTP 传输。</para>  
    600 600 <para>在 HTTP 层之上,还有几个可能发生问题的地方。 &soappy; 隐藏 &soap; 语法的本领令你惊叹不已,但也意味着在发生问题时更难确定问题所在。</para>  
    601 601 <para>下面的这些例子是我在使用 &soap; 网络服务时犯过的一些常见错误以及所产生的错误信息。</para>  
    602 602 <example>  
    603   <title>以错误设置的 Proxy 调用方法</title>  
      603 <title>[todo]以错误的设置调用 Proxy 方法</title>  
    603 603 <screen>  
    604 604 &prompt;<userinput>from SOAPpy import SOAPProxy</userinput>  
     
    626 626 </callout>  
    627 627 <callout arearefs="soap.troubleshooting.1.2">  
    628   <para>服务器返回的是一个 &soap; 错误,&soappy; 溢出了<classname>SOAPpy.Types.faultType</classname> 这个 &python; 异常。 从任何 &soap; 服务器返回的错误都是 &soap; 错误,因此你可以轻易地捕获这个异常。就此出而言,我们能从&soap; 错误信息中看出端倪:由于源 <classname>SOAPProxy</classname> 对象没有设置服务命名空间,因此方法元素也就没有了命名空间。</para>  
      628 <para>服务器返回的是一个 &soap; 错误,&soappy; 溢出了<classname>SOAPpy.Types.faultType</classname> 这个 &python; 异常。 从任何 &soap; 服务器返回的错误都是 &soap; 错误,因此你可以轻易地捕获这个异常。就此出而言,我们能从 &soap; 错误信息中看出端倪:由于源 <classname>SOAPProxy</classname> 对象没有设置服务命名空间,因此方法元素也就没有了命名空间。</para>  
    628 628 </callout>  
    629 629 </calloutlist>  
     
    652 652 <calloutlist>  
    653 653 <callout arearefs="soap.troubleshooting.2.1">  
    654   <para>你看出错误了吗? 这是一个不易察觉的错误:你在使用整数而不是字符串来调用<function>server.getTemp</function> 。自省 &wsdl; 文件不难发现,<function>getTemp()</function> 这个 &soap; 函数接受一个参数 <varname>zipcode</varname>,这是一个字符串参数。 <classname>WSDL.Proxy</classname> <emphasis>不</emphasis> 会为你强制数据类型;你需要根据服务器需要的数据类型传递数据。</para>  
      654 <para>你看出错误了吗? 这是一个不易察觉的错误:你在使用整数而不是字符串来调用 <function>server.getTemp</function> 。自省 &wsdl; 文件不难发现,<function>getTemp()</function> 这个 &soap; 函数接受一个参数 <varname>zipcode</varname>,这是一个字符串参数。 <classname>WSDL.Proxy</classname> <emphasis>不</emphasis> 会为你强制数据类型;你需要根据服务器需要的数据类型传递数据。</para>  
    654 654 </callout>  
    655 655 <callout arearefs="soap.troubleshooting.2.2">  
    656   <para>又是这样,服务器传回一个 &soap; 错误,你能从&soap; 错误信息中看出端倪:你在使用整数类型的参数调用 <function>getTemp</function> 函数,但却没有一个以此命名的函数接收整数参数。理论上讲, &soap; 允许你重载 <emphasis>overload</emphasis> 函数,也就是可以在同一个 &soap; 服务中存在同名函数,并且参数个数也相同,但是参数的数据类型不同。这就是数据类型必须匹配的原因,也说明了为什么 <classname>WSDL.Proxy</classname> 不强制地为你改变数据类型。如果真的强制改变了数据类型,发生这样的错误时,调用的可能是另外一个不相干的函数。看来产生这样的错误是件幸运的事。对于数据类型多加注意会让事情简单很多,一旦搞错了数据类型便立刻会发生错误。</para>  
      656 <para>又是这样,服务器传回一个 &soap; 错误,你能从 &soap; 错误信息中看出端倪:你在使用整数类型的参数调用 <function>getTemp</function> 函数,但却没有一个以此命名的函数接收整数参数。理论上讲, &soap; 允许你重载 <emphasis>overload</emphasis> 函数,也就是可以在同一个 &soap; 服务中存在同名函数,并且参数个数也相同,但是参数的数据类型不同。这就是数据类型必须匹配的原因,也说明了为什么 <classname>WSDL.Proxy</classname> 不强制地为你改变数据类型。如果真的强制改变了数据类型,发生这样的错误时,调用的可能是另外一个不相干的函数。看来产生这样的错误是件幸运的事。对于数据类型多加注意会让事情简单很多,一旦搞错了数据类型便立刻会发生错误。</para>  
    656 656 </callout>  
    657 657 </calloutlist>  
     
    676 676 </calloutlist>  
    677 677 </example>  
    678   <para>那 Google 网络服务方面又如何呢?最常见的错误是我曾经犯过的忘记正确设置应用许可证错误。</para>  
      678 <para>那 Google 网络服务方面又如何呢?最常见的错误是我曾经犯过的忘记正确设置应用许可证错误。</para>  
    678 678 <example>  
    679 679 <title>调用方法返回一个应用指定错误</title>  
     
    760 760 </callout>  
    761 761 <callout arearefs="soap.troubleshooting.4.2">  
    762   <para>Google服务器返回的是一个 &soap; 错误和一大串特别长的错误信息,其中包含了完整的Java堆栈跟踪。 记住 <emphasis>所有</emphasis>的 &soap; 错误都被标示为 &soap; 错误:设置错误,函数参数错误以及应用指定错误等等。在其中埋藏的至关重要信息确是:<literal>非有效授权许可证:foo  
      762 <para>Google 服务器返回的是一个 &soap; 错误和一大串特别长的错误信息,其中包含了完整的 Java 堆栈跟踪。 记住 <emphasis>所有</emphasis> 的 &soap; 错误都被标示为 &soap; 错误:设置错误,函数参数错误以及应用指定错误等等。在其中埋藏的至关重要信息确是:<literal>非有效授权许可证:foo  
    762 762 (Invalid authorization key: foo)</literal>。</para>  
    763 763 </callout>  
     
    766 766 </example>  
    767 767 <itemizedlist role="furtherreading">  
    768   <title> &soap; 故障排除的进一步阅读</title>  
      768 <title>进一步阅读</title>  
    768 768 <listitem><para><ulink url="http://www-106.ibm.com/developerworks/webservices/library/ws-pyth17.html">New developments for &soappy;</ulink> 一步步连接到另一个不名副其实的 &soap; 服务。</para></listitem>  
    769 769 </itemizedlist>  
     
    773 773 <section id="soap.summary">  
    774 774 <?dbhtml filename="soap_web_services/summary.html"?>  
    775   <title>结</title>  
      775 <title>结</title>  
    775 775 <abstract>  
    776 776 <title/>  
    781 781 <para>在开始下一章的学习之前,确保你能自如地做如下工作:</para>  
    782 782 <itemizedlist>  
    783   <listitem><para>连接到 a &soap; 服务器并调用远程方法</para></listitem>  
      783 <listitem><para>连接到 &soap; 服务器并调用远程方法</para></listitem>  
    783 783 <listitem><para>通过 &wsdl; 文件自省远程方法</para></listitem>  
    784 784 <listitem><para>有效排除 &soap; 调用中的错误</para></listitem>  
  • zh-translations/branches/diveintopython-zh-5.4/zh-cn/xml/install.xml

    r218 r858  
    3 3 <?dbhtml filename="installing_python/index.html"?>  
    4 4 <title>安装 &python;</title>  
    5   <titleabbrev id="install.numberonly">Chapter 1</titleabbrev>  
      5 <titleabbrev id="install.numberonly">第 1 章</titleabbrev>  
    5 5 <abstract>  
    6 6 <title/>  
  • zh-translations/branches/diveintopython-zh-5.4/zh-cn/xml/regression.xml

    r201 r858  
    2 2 <chapter id="regression">  
    3 3 <?dbhtml filename="functional_programming/index.html"?>  
    4   <title>Functional Programming</title>  
    5   <titleabbrev id="regression.numberonly">Chapter 16</titleabbrev>  
      4 <title>有效编程(Functional Programming)</title>  
      5 <titleabbrev id="regression.numberonly">第 16 章</titleabbrev>  
    6 6 <section id="regression.divein">  
    7   <title>Diving in</title>  
      7 <title>概览</title>  
    7 7 <abstract>  
    8 8 <title/>  
    9   <para>In <xref linkend="roman"/>, you learned about the philosophy of unit testing.  In <xref linkend="roman1.5"/>, you stepped through the implementation of basic unit tests in &python;.  In <xref linkend="roman2"/>, you saw how unit testing makes large-scale refactoring easier.  This chapter will build on those sample programs, but here we will focus more on advanced &python;-specific techniques, rather than on unit testing itself.</para>  
      9 <para>在 <xref linkend="roman"/> 中,你学会了单元测试的哲学。在 <xref linkend="roman1.5"/>  中你步入了 &python; 基本的单元测试操作,在 <xref linkend="roman2"/> 部分,你看到单元测试如何另大规模重组变得容易。 本章将在这些程序范例的基础上,集中关注于超越单元测试本身的更高级的 &python; 特有技术。</para>  
    9 9 </abstract>  
    10   <para>The following is a complete &python; program that acts as a cheap and simple regression testing framework.  It takes unit tests that you've written for individual modules, collects them all into one big test suite, and runs them all at once.  I actually use this script as part of the build process for this book; I have unit tests for several of the example programs (not just the &roman_filename; module featured in <xref linkend="roman"/>), and the first thing my automated build script does is run this program to make sure all my examples still work.  If this regression test fails, the build immediately stops.  I don't want to release non-working examples any more than you want to download them and sit around scratching your head and yelling at your monitor and wondering why they don't work.</para>  
      10 <para>下面是一个作为简单退化(regression)测试框架运行的完整 &python; 程序。 它将你前面编写的单独单元测试模块组织在一起成为一个测试套件并一次性运行。实际上这是我构建本书自身代码的一部分。我为几个样例程序都编写了单元测试。(不是只有 <xref linkend="roman"/> 中的 &roman_filename; 模块),我的自动构建代码的第一个工作便是确保我所有的例子可以正常工作。 如果退化测试程序失败,构建过程当即终止。 我可不想因为发布了不能工作的样例程序而让你在下载他们后坐在显示器前抓耳挠腮地为程序不能运转而烦恼。</para>  
    10 10 <example>  
    11 11 <title>&regression_filename;</title>  
     
    34 34 </programlisting>  
    35 35 </example>  
    36   <para>Running this script in the same directory as the rest of the example scripts that come with this book will find all the unit tests, named <filename><replaceable>module</replaceable>test.py</filename>, run them as a single test, and pass or fail them all at once.</para>  
      36 <para>把这段代码放在本书其他样例代码相同的目录下运行之,<filename><replaceable>模块 </replaceable>test.py</filename> 中的所有单元测试将被找到并一起被运行。</para>  
    36 36 <example>  
    37   <title>Sample output of &regression_filename;</title>  
      37 <title>&regression_filename; 的范例输出</title>  
    37 37 <screen>  
    38 38 <prompt>[you@localhost py]$ </prompt><userinput>python regression.py -v</userinput>  
     
    75 75 <calloutlist>  
    76 76 <callout arearefs="regression.divein.1.1">  
    77   <para>The first 5 tests are from <filename>apihelpertest.py</filename>, which tests the example script from <xref linkend="apihelper"/>.</para>  
      77 <para>前五个测试来自于 <filename>apihelpertest.py</filename>,用以测试 <xref linkend="apihelper"/> 中的范例代码。</para>  
    77 77 </callout>  
    78 78 <callout arearefs="regression.divein.1.2">  
    79   <para>The next 5 tests are from <filename>odbchelpertest.py</filename>, which tests the example script from <xref linkend="odbchelper"/>.</para>  
      79 <para>接下来的五个代码来自于 <filename>odbchelpertest.py</filename>, 用以测试 <xref linkend="odbchelper"/> 中的范例代码。</para>  
    79 79 </callout>  
    80 80 <callout arearefs="regression.divein.1.3">  
    81   <para>The rest are from <filename>romantest.py</filename>, which you studied in depth in <xref linkend="roman"/>.</para>  
      81 <para>其他的代码来自于 <filename>romantest.py</filename>,你在 <xref linkend="roman"/> 中深入学习过。</para>  
    81 81 </callout>  
    82 82 </calloutlist>  
     
    88 88 <section id="regression.path">  
    89 89 <?dbhtml filename="functional_programming/finding_the_path.html"?>  
    90   <title>Finding the path</title>  
      90 <title>找到路径</title>  
    90 90 <abstract>  
    91 91 <title/>  
    92   <para>When running &python; scripts from the command line, it is sometimes useful to know where the currently running script is located on disk.</para>  
      92 <para>从命令行运行 &python; 代码时,知道所运行代码所在磁盘上的存储位置有时候是有必要的。</para>  
    92 92 </abstract>  
    93   <para>This is one of those obscure little tricks that is virtually impossible to figure out on your own, but simple to remember once you see it.  The key to it is <literal>sys.argv</literal>.  As you saw in <xref linkend="kgp"/>, this is a list that holds the list of command-line arguments.  However, it also holds the name of the running script, exactly as it was called from the command line, and this is enough information to determine its location.</para>  
      93 <para>这是一个你很难自己弄明白,却一看到就会想起的小麻烦。核心功能来源于 <literal>sys.argv</literal>。正如你在 <xref linkend="kgp"/> 中看到的,它包含了很多命令行参数。 当然就像从命令行中运行他们一样,它也同样记录了运行脚本的名字,这些信息足以令我们确定文件的位置。</para>  
    93 93 <example>  
    94 94 <title><filename>fullpath.py</filename></title>  
     
    106 106 <calloutlist>  
    107 107 <callout arearefs="regression.path.1.1">  
    108   <para>Regardless of how you run a script, &sysargv0; will always contain the name of the script, exactly as it appears on the command line.  This may or may not include any path information, as you'll see shortly.</para>  
      108 <para>无论如何运行一段脚本, &sysargv0; 总是像通过命令行调用一样,需要包含脚本的名字。你很快会发现,它不一定包含任何路径信息。</para>  
    108 108 </callout>  
    109 109 <callout arearefs="regression.path.1.2">  
    110   <para>&ospathdirname; takes a filename as a string and returns the directory path portion.  If the given filename does not include any path information, &ospathdirname; returns an empty string.</para>  
      110 <para>&ospathdirname; 接受作为字符串传来的文件名并返回路径部分。如果给定的文件名不包含任何路径信息, &ospathdirname;  返回空字符串。</para>  
    110 110 </callout>  
    111 111 <callout arearefs="regression.path.1.3">  
    112   <para>&ospathabspath; is the key here.  It takes a pathname, which can be partial or even blank, and returns a fully qualified pathname.</para>  
      112 <para>&ospathabspath; 是这里的关键。 它接受的路径名可以是部分的甚至是完全空白,却返回完整有效的路径名。</para>  
    112 112 </callout>  
    113 113 </calloutlist>  
    114 114 </example>  
    115   <para>&ospathabspath; deserves further explanation.  It is very flexible; it can take any kind of pathname.</para>  
      115 <para>进一步的解释 &ospathabspath; 是有必要的。 它非常灵活,可以接受任何类型的路径名。</para>  
    115 115 <example>  
    116   <title>Further explanation of &ospathabspath;</title>  
      116 <title>&ospathabspath; 的进一步解释</title>  
    116 116 <screen>  
    117 117 &prompt;<userinput>import os</userinput>  
     
    133 133 <calloutlist>  
    134 134 <callout arearefs="regression.path.2.1">  
    135   <para><function>os.getcwd()</function> returns the current working directory.</para>  
      135 <para><function>os.getcwd()</function> 返回当前的工作路径。</para>  
    135 135 </callout>  
    136 136 <callout arearefs="regression.path.2.2">  
    137   <para>Calling &ospathabspath; with an empty string returns the current working directory, same as <function>os.getcwd()</function>.</para>  
      137 <para>一个空字符串调用 &ospathabspath; 当前的工作路径,与 <function>os.getcwd()</function>的效果相同。</para>  
    137 137 </callout>  
    138 138 <callout arearefs="regression.path.2.3">  
    139   <para>Calling &ospathabspath; with a partial pathname constructs a fully qualified pathname out of it, based on the current working directory.</para>  
      139 <para>以不完整的路径名调用 &ospathabspath; 可以构建一个基于当前工作路径且完整有效的路径名。</para>  
    139 139 </callout>  
    140 140 <callout arearefs="regression.path.2.4">  
    141   <para>Calling &ospathabspath; with a full pathname simply returns it.</para>  
      141 <para>以完整的路径名调用 &ospathabspath; 则简单地将其直接返回。</para>  
    141 141 </callout>  
    142 142 <callout arearefs="regression.path.2.5">  
    143   <para>&ospathabspath; also <emphasis>normalizes</emphasis> the pathname it returns.  Note that this example worked even though I don't actually have a 'foo' directory.  &ospathabspath; never checks your actual disk; this is all just string manipulation.</para>  
      143 <para>&ospathabspath; 还 <emphasis>格式化</emphasis> 返回的路径名。 注意这个例子在我根本没有‘foo’目录时同样奏效。 &ospathabspath; 从不检查你的磁盘,而仅仅是字符串操作。</para>  
    143 143 </callout>  
    144 144 </calloutlist>  
    145 145 </example>  
    146 146 <note id="os.path.abspath.exist.note">  
    147   <title>&ospathabspath; does not validate pathnames</title>  
    148   <para>The pathnames and filenames you pass to &ospathabspath; do not need to exist.</para>  
      147 <title>&ospathabspath; 并不验证路径名</title>  
      148 <para>传递给 &ospathabspath; 的路径名和文件名可以不存在。</para>  
    149 149 </note>  
    150 150 <note id="os.path.normpath.note">  
    151   <title>Normalizing pathnames</title>  
    152   <para>&ospathabspath; not only constructs full path names, it also normalizes them.  That means that if you are in the <filename>/usr/</filename> directory, <literal>os.path.abspath('bin/../local/bin')</literal> will return <filename>/usr/local/bin</filename>.  It normalizes the path by making it as simple as possible.  If you just want to normalize a pathname like this without turning it into a full pathname, use <function>os.path.normpath</function> instead.</para>  
      151 <title>格式化路径名</title>  
      152 <para>&ospathabspath; 不仅构建完整路径名,还能格式化路径名。 这意味着如果你正工作于 <filename>/usr/</filename> 目录, <literal>os.path.abspath('bin/../local/bin')</literal> 将会返回 <filename>/usr/local/bin</filename> 。 它以尽可能简单的方式格式化路径名。 如果你只是希望简单地返回这样的格式化路径名而不需要完整路径名可以使用 <function>os.path.normpath</function>。</para>  
    153 153 </note>  
    154 154 <example>  
    155   <title>Sample output from <filename>fullpath.py</filename></title>  
      155 <title><filename>fullpath.py</filename> 的范例输出</title>  
    155 155 <screen>  
    156 156 <prompt>[you@localhost py]$ </prompt><userinput>python /home/you/diveintopython/common/py/fullpath.py</userinput> <co id="regression.path.3.1"/>  
     
    175 175 <calloutlist>  
    176 176 <callout arearefs="regression.path.3.1">  
    177   <para>In the first case, &sysargv0; includes the full path of the script.  You can then use the &ospathdirname; function to strip off the script name and return the full directory name, and &ospathabspath; simply returns what you give it.</para>  
      177 <para>在第一种情况下, &sysargv0; 包含代码的完整路径。 你可以通过 &ospathdirname; 函数将文件名从其中剥离出来并返回完整的路径, &ospathabspath; 则是简单地把你传递给它的值返回。</para>  
    177 177 </callout>  
    178 178 <callout arearefs="regression.path.3.2">  
    179   <para>If the script is run by using a partial pathname, &sysargv0; will still contain exactly what appears on the command line.  &ospathdirname; will then give you a partial pathname (relative to the current directory), and &ospathabspath; will construct a full pathname from the partial pathname.</para>  
      179 <para>如果脚本是以不完整路名被运行的, &sysargv0; 还是会包含命令行中应出现的一切。 &ospathdirname; 将会给你一个(相对于当前工作路径的)不完整的路径名,&ospathabspath; 将会以不完整路径名为基础构建一个完整的路径名。</para>  
    179 179 </callout>  
    180 180 <callout arearefs="regression.path.3.3">  
    181   <para>If the script is run from the current directory without giving any path, &ospathdirname; will simply return an empty string.  Given an empty string, &ospathabspath; returns the current directory, which is what you want, since the script was run from the current directory.</para>  
      181 <para>如果没有给定任何路径,而是从当前目录运行脚本, &ospathdirname; 将简单地返回一个空字符串。 由于是从当前目录运行脚本, &ospathabspath; 将针对给定的空字符串给出你所希望获知的当前目录。</para>  
    181 181 </callout>  
    182 182 </calloutlist>  
    183 183 </example>  
    184 184 <note id="os.path.abspath.crossplatform.note">  
    185   <title>&ospathabspath; is cross-platform</title>  
    186   <para>Like the other functions in the &os; and &ospath; modules, &ospathabspath; is cross-platform.  Your results will look slightly different than my examples if you're running on &windows; (which uses backslash as a path separator) or &macos; (which uses colons), but they'll still work.  That's the whole point of the &os; module.</para>  
      185 <title>&ospathabspath; 是跨平台的</title>  
      186 <para>就像 &os; 和 &ospath; 模块的其他函数, &ospathabspath; 是跨平台的。 如果你是在 &windows; (使用反斜杠作为路径符号)或 &macos; (使用冒号)上运行,它们同样工作,只是将获得与我稍有不同的结果。 &os; 的所有函数都是这样的。</para>  
    187 187 </note>  
    188   <formalpara><title>Addendum</title><para>One reader was dissatisfied with this solution, and wanted to be able to run all the unit tests in the current directory, not the directory where &regression_filename; is located.  He suggests this approach instead:</para></formalpara>  
      188 <formalpara><title>补充</title><para>一位读者对这个结果并不满意,他希望能够从当前路径运行所有单元测试,而不是从 &regression_filename; 所在目录运行。 他建议以下面的代码加以取代: </para></formalpara>  
    188 188 <example id="regression.path.cwd.example">  
    189   <title>Running scripts in the current directory</title>  
      189 <title>在当前目录运行脚本</title>  
    189 189 <programlisting>import sys, os, re, unittest  
    190 190  
     
    201 201 <calloutlist>  
    202 202 <callout arearefs="regression.path.4.1">  
    203   <para>Instead of setting <varname>path</varname> to the directory where the currently running script is located, you set it to the current working directory instead.  This will be whatever directory you were in before you ran the script, which is not necessarily the same as the directory the script is in. (Read that sentence a few times until you get it.)</para>  
      203 <para>不是将 <varname>path</varname> 设置为运行代码所在的路径,而是将它设置为当前目录。可以是你在运行脚本之前所在的任何路径,而不需要是运行脚本所在的路径。(多次体味这句话,直到你真正理解了它)</para>  
    203 203 </callout>  
    204 204 <callout arearefs="regression.path.4.2">  
    205   <para>Append this directory to the &python; library search path, so that when you dynamically import the unit test modules later, &python; can find them.  You didn't need to do this when <varname>path</varname> was the directory of the currently running script, because &python; always looks in that directory.</para>  
      205 <para>将这个目录添加到  &python; 库搜索路径中,你稍后动态导入单元测试模块时, &python; 就能找到它们了。 如果 <varname>path</varname> 就是正在运行代码的存储目录,你就不需要这样做了,因为 &python; 总会查找这个目录。</para>  
    205 205 </callout>  
    206 206 <callout arearefs="regression.path.4.3">  
    207   <para>The rest of the function is the same.</para>  
      207 <para>函数的其他部分不变。</para>  
    207 207 </callout>  
    208 208 </calloutlist>  
    209   <para>This technique will allow you to re-use this &regression_filename; script on multiple projects.  Just put the script in a common directory, then change to the project's directory before running it.  All of that project's unit tests will be found and tested, instead of the unit tests in the common directory where &regression_filename; is located.</para>  
      209 <para>这个技术允许你在多个项目中重用 &regression_filename; 代码。 只需要将这个代码放在一个普通目录中,在运行项目前将路径更改为项目的目录。 所有项目的路径将被找到并进行测试工作,而不仅仅局限于 &regression_filename; 所在目录的单元测试。</para>  
    209 209 </example>  
    210 210 </section>  
    211 211 <section id="regression.filter">  
    212 212 <?dbhtml filename="functional_programming/filtering_lists.html"?>  
    213   <title>Filtering lists revisited</title>  
      213 <title>过滤已访问列表</title>  
    213 213 <abstract>  
    214 214 <title/>  
    215   <para>You're already familiar with <link linkend="apihelper.filter">using list comprehensions to filter lists</link>.  There is another way to accomplish this same thing, which some people feel is more expressive.</para>  
      215 <para>你已经熟识了 <link linkend="apihelper.filter"> 应用列表遍历来过滤列表</link>。 这里介绍的是达到相同效果的另一种令很多人感觉清晰的实现方法。</para>  
    215 215 </abstract>  
    216   <para>&python; has a built-in &filter_function; function which takes two arguments, a function and a list, and returns a list.<footnote><para>Technically, the second argument to &filter_function; can be any sequence, including lists, tuples, and custom classes that act like lists by defining the <function>__getitem__</function> special method.  If possible, &filter_function; will return the same datatype as you give it, so filtering a list returns a list, but filtering a tuple returns a tuple.</para></footnote>  The function passed as the first argument to &filter_function; must itself take one argument, and the list that &filter_function; returns will contain all the elements from the list passed to &filter_function; for which the function passed to &filter_function; returns true.</para>  
    217   <para>Got all that?  It's not as difficult as it sounds.</para>  
      216 <para>&python; 有一个内建 &filter_function; 函数,它接受两个参数:一个函数和一个列表,返回一个列表。<footnote><para>从技术层面上讲, &filter_function; 的第二个参数可以是任意的序列,包括列表、元组以及定义了 <function>__getitem__</function> 特殊方法而能像列表一样工作的自定义类。 在可能情况下, &filter_function; 会返回与输入相同的数据类型,也就是过滤一个列表返回一个列表,过滤一个元组返回一个元组。</para></footnote>  作为第一个参数传递给 &filter_function; 的函数本身应接受一个参数,&filter_function; 返回的列表将会包含被传入列表参数传递给 &filter_function; 所有可以另函数返回真(true)的元素。</para>  
      217 <para>都明白了吗?并没有听起来那么难。</para>  
    218 218 <example>  
    219   <title>Introducing &filter_function;</title>  
      219 <title>介绍 &filter_function;</title>  
    219 219 <screen>  
    220 220 &prompt;<userinput>def odd(n):</userinput>                 <co id="regression.filter.1.1"/>  
     
    241 241 <calloutlist>  
    242 242 <callout arearefs="regression.filter.1.1">  
    243   <para><function>odd</function> uses the built-in mod function <quote><literal>%</literal></quote> to return &true; if <varname>n</varname> is odd and &false; if <varname>n</varname> is even.</para>  
      243 <para><function>odd</function> 使用内建的取模(mod)函数 <quote><literal>%</literal></quote> 对于为奇数的 <varname>n</varname> 返回 &true; ;为偶数的返回 &false; 。</para>  
    243 243 </callout>  
    244 244 <callout arearefs="regression.filter.1.2">  
    245   <para>&filter_function; takes two arguments, a function (<function>odd</function>) and a list (<varname>li</varname>).  It loops through the list and calls <function>odd</function> with each element.  If <function>odd</function> returns a true value (remember, any non-zero value is true in &python;), then the element is included in the returned list, otherwise it is filtered out.  The result is a list of only the odd numbers from the original list, in the same order as they appeared in the original.</para>  
      245 <para>&filter_function; 接受两个参数:一个函数(<function>odd</function>)和一个列表(<varname>li</varname>)。 它依列表循环为每个元素调用 <function>odd</function> 函数。 如果 <function>odd</function> 返回的是真(记住, &python; 认为所有非零值为真),则该元素被放在返回列表中,如若不然则被过滤掉。 结果是一个只包含原列表中奇数的列表,出现顺序则和原列表相同。</para>  
    245 245 </callout>  
    246 246 <callout arearefs="regression.filter.1.3">  
    247   <para>You could accomplish the same thing using list comprehensions, as you saw in <xref linkend="apihelper.filter"/>.</para>  
      247 <para>你可以通过遍历的方式完成相同的工作,正如在 <xref linkend="apihelper.filter"/> 中看到的。</para>  
    247 247 </callout>  
    248 248 <callout arearefs="regression.filter.1.4">  
    249   <para>You could also accomplish the same thing with a &for; loop.  Depending on your programming background, this may seem more <quote>straightforward</quote>, but functions like &filter_function; are much more expressive.  Not only is it easier to write, it's easier to read, too.  Reading the &for; loop is like standing too close to a painting; you see all the details, but it may take a few seconds to be able to step back and see the bigger picture: <quote>Oh, you're just filtering the list!</quote></para>  
      249 <para>你可以通过 &for; 循环的方式完成相同的工作。 根据你编程的背景,这样也许更<quote>直接</quote>,但是像  &filter_function; 函数这样的实现方法更清晰。 不但编写简单,而且易于读懂。 &for; 循环就好比近距离的绘画:你可以看到所有的细节,但是或许你应该花几秒时间退后几步看一看图画的全景。 <quote>啊,你仅仅是要过滤列表!</quote></para>  
    249 249 </callout>  
    250 250 </calloutlist>  
    251 251 </example>  
    252 252 <example>  
    253   <title>&filter_function; in &regression_filename;</title>  
      253 <title>&regression_filename; 中的 &filter_function;</title>  
    253 253 <programlisting>  
    254 254 &regression_listdir; <co id="regression.filter.2.1"/>  
     
    262 262 <calloutlist>  
    263 263 <callout arearefs="regression.filter.2.1">  
    264   <para>As you saw in <xref linkend="regression.path"/>, <varname>path</varname> may contain the full or partial pathname of the directory of the currently running script, or it may contain an empty string if the script is being run from the current directory.  Either way, <varname>files</varname> will end up with the names of the files in the same directory as this script you're running.</para>  
      264 <para>正如你在 <xref linkend="regression.path"/> 中看到的, <varname>path</varname> 可能包括正在运行脚本的完全或者部分路径名,或者当脚本运行自当前目录时包含一个空的字符串。 任何一种情况下, <varname>files</varname> 都会获得正运行脚本所在目录的文件名。</para>  
    264 264 </callout>  
    265 265 <callout arearefs="regression.filter.2.2">  
    266   <para>This is a compiled regular expression.  As you saw in <xref linkend="roman.refactoring"/>, if you're going to use the same regular expression over and over, you should compile it for faster performance.  The compiled object has a <function>search</function> method which takes a single argument, the string to search.  If the regular expression matches the string, the <function>search</function> method returns a <classname>Match</classname> object containing information about the regular expression match; otherwise it returns &none;, the &python; null value.</para>  
      266 <para>这时一个复杂的正则表达式。正如你在 <xref linkend="roman.refactoring"/>中看到的,如果你需要反复使用同一个正则表达式,你应该编译它已获得更快的性能。编译后的对象将含有接受一个被寻找字符串作为参数的 <function>search</function> 方法。 如果这个正则表达式匹配字符串, <function>search</function> 方法返回一个包含正则表达式匹配信息的 <classname>Match</classname> 对象;否则返回 &none;, 这是 &python; 空(null) 值。</para>  
    266 266 </callout>  
    267 267 <callout arearefs="regression.filter.2.3">  
    268   <para>For each element in the <varname>files</varname> list, you're going to call the <function>search</function> method of the compiled regular expression object, <varname>test</varname>.  If the regular expression matches, the method will return a <classname>Match</classname> object, which &python; considers to be true, so the element will be included in the list returned by &filter_function;.  If the regular expression does not match, the <function>search</function> method will return &none;, which &python; considers to be false, so the element will not be included.</para>  
      268 <para>对于 <varname>files</varname> 列表中的每个元素,你将会调用正则表达式编译对象 <varname>test</varname> 的 <function>search</function> 方法。 如果正则表达匹配,方法将会返回一个被 &python; 认定为真(true)的  <classname>Match</classname> 对象;如果正则表达不匹配, <function>search</function> 方法将会返回被认定为假(false)的 &none;,元素将被排除。</para>  
    268 268 </callout>  
    269 269 </calloutlist>  
    270 270 </example>  
    271 271 <formalpara>  
    272   <title>Historical note</title>  
    273   <para>Versions of &python; prior to 2.0 did not have <link linkend="odbchelper.map">list comprehensions</link>, so you couldn't <link linkend="apihelper.filter">filter using list comprehensions</link>; the &filter_function; function was the only game in town.  Even with the introduction of list comprehensions in 2.0, some people still prefer the old-style &filter_function; (and its companion function, &map_function;, which you'll see later in this chapter).  Both techniques work at the moment, so which one you use is a matter of style.  There is discussion that &map_function; and &filter_function; might be deprecated in a future version of &python;, but no decision has been made.</para>  
      272 <title>历史注释</title>  
      273 <para>&python; 2.0 早期的版本不包含 <link linkend="odbchelper.map">列表遍历</link>,因此不能 <link linkend="apihelper.filter">以列表遍历方式过滤</link>,&filter_function; 函数是当时唯一的方法。 即便是在引入列表遍历的 2.0 版,有些人仍然钟情于老派的 &filter_function; (和这章稍后将见到的它的伴侣函数 &map_function; ) 两种方法并存于世,使用哪种方法只是风格问题, &map_function; 和 &filter_function; 将在未来的  &python; 版本中被废止的讨论尚无定论。</para>  
    274 274 </formalpara>  
    275 275 <example>  
    276   <title>Filtering using list comprehensions instead</title>  
      276 <title>以列表遍历法过滤</title>  
    276 276 <programlisting>  
    277 277 &regression_listdir;  
     
    284 284 <calloutlist>  
    285 285 <callout arearefs="regression.filter.3.1">  
    286   <para>This will accomplish exactly the same result as using the &filter_function; function.  Which way is more expressive?  That's up to you.</para>  
      286 <para>这种方法将完成和 &filter_function; 函数完全相同的工作。 哪种方法更清晰完全取决于你自己。</para>  
    286 286 </callout>  
    287 287 </calloutlist>  
     
    291 291 <section id="regression.map">  
    292 292 <?dbhtml filename="functional_programming/mapping_lists.html"?>  
    293   <title>Mapping lists revisited</title>  
      293 <title>关联已访问列表</title>  
    293 293 <abstract>  
    294 294 <title/>  
    295   <para>You're already familiar with using <link linkend="odbchelper.map">list comprehensions</link> to map one list into another.  There is another way to accomplish the same thing, using the built-in &map_function; function.  It works much the same way as the <link linkend="regression.filter">&filter_function;</link> function.</para>  
      295 <para>你对使用 <link linkend="odbchelper.map">列表遍历</link> 将列表关联起来的做法已经熟知。 另一种方法可以完成同样的工作:使用内建 &map_function; 函数。 它的工作机理和 <link linkend="regression.filter">&filter_function;</link> 函数类似。</para>  
    295 295 </abstract>  
    296 296 <example>  
    297   <title>Introducing &map_function;</title>  
      297 <title>介绍 &map_function;</title>  
    297 297 <screen>  
    298 298 &prompt;<userinput>def double(n):</userinput>  
     
    315 315 <calloutlist>  
    316 316 <callout arearefs="regression.map.1.1">  
    317   <para>&map_function; takes a function and a list<footnote><para>Again, I should point out that &map_function; can take a list, a tuple, or any object that acts like a sequence.  See previous footnote about &filter_function;.</para></footnote> and returns a new list by calling the function with each element of the list in order.  In this case, the function simply multiplies each element by 2.</para>  
      317 <para>&map_function; 接受一个函数和一个列表作为参数,<footnote><para>同前,我需要指出 &map_function; 可以接受一个列标、元组,或者一个像序列一样的对象。参见前面的关于 &filter_function; 的脚注。</para></footnote> 并以列表中每个元素顺序地调用函数返回一个新的列表。 在这个例子中,函数仅仅是将每个元素乘以2。</para>  
    317 317 </callout>  
    318 318 <callout arearefs="regression.map.1.2">  
    319   <para>You could accomplish the same thing with a list comprehension.  List comprehensions were first introduced in &python; 2.0; &map_function; has been around forever.</para>  
      319 <para>使用列表遍历的方法你可以做到相同的事情。 列表遍历是在 &python; 2.0版时被引入的,&map_function; 便从此永远盘桓。</para>  
    319 319 </callout>  
    320 320 <callout arearefs="regression.map.1.3">  
    321   <para>You could, if you insist on thinking like a &vb; programmer, use a &for; loop to accomplish the same thing.</para>  
      321 <para>你如果坚持以 &vb; 程序员而自居,通过 &for; 循环的方法完成相同的任务也完全可以。</para>  
    321 321 </callout>  
    322 322 </calloutlist>  
    323 323 </example>  
    324 324 <example>  
    325   <title>&map_function; with lists of mixed datatypes</title>  
      325 <title>&map_function; 与混合数据类型的列表</title>  
    325 325 <screen>  
    326 326 &prompt;<userinput>li = [5, 'a', (2, 'b')]</userinput>  
     
    333 333 <calloutlist>  
    334 334 <callout arearefs="regression.map.2.1">  
    335   <para>As a side note, I'd like to point out that &map_function; works just as well with lists of mixed datatypes, as long as the function you're using correctly handles each type.  In this case, the <function>double</function> function simply multiplies the given argument by 2, and &python; Does The Right Thing depending on the datatype of the argument.  For integers, this means actually multiplying it by 2; for strings, it means concatenating the string with itself; for tuples, it means making a new tuple that has all of the elements of the original, then all of the elements of the original again.</para>  
      335 <para>作为一个旁注,我想指出只要提供的那个函数能够正确处理各种数据类型, &map_function; 对于混合数据类型列表的处理同样出色。 在这里,这个 <function>double</function> 函数仅仅是将给定参数乘以 2 , &python; 根据参数的数据类型决定 <emphasis>正确操作</emphasis> 的方法。 对整数而言,这意味着乘 2 ;对字符串而言,意味着把自身和自身连接;对于元组,意味着构建一个包括原始元组全部元素和原始元组组合在一起的新元组。</para>  
    335 335 </callout>  
    336 336 </calloutlist>  
    337 337 </example>  
    338   <para>All right, enough play time.  Let's look at some real code.</para>  
      338 <para>好了,玩够了。让我们来看一些真实代码。</para>  
    338 338 <example>  
    339   <title>&map_function; in &regression_filename;</title>  
      339 <title>&regression_filename; 中的 &map_function;</title>  
    339 339 <programlisting>  
    340 340 &regression_lambda; <co id="regression.map.3.1"/>  
     
    345 345 <calloutlist>  
    346 346 <callout arearefs="regression.map.3.1">  
    347   <para>As you saw in <xref linkend="apihelper.lambda"/>, &lambdafunction; defines an inline function.  And as you saw in <xref linkend="splittingpathnames.example"/>, &ospathsplitext; takes a filename and returns a tuple <literal>(<replaceable>name</replaceable>, <replaceable>extension</replaceable>)</literal>.  So <function>filenameToModuleName</function> is a function which will take a filename and strip off the file extension, and return just the name.</para>  
      347 <para>正如你在 <xref linkend="apihelper.lambda"/> 中所见, &lambdafunction; 定义一个内嵌函数。 也正如你在 <xref linkend="splittingpathnames.example"/> 中所见, &ospathsplitext; 接受一个文件名并返回一个元组 <literal>(<replaceable>name</replaceable>, <replaceable>extension</replaceable>)</literal>。因此 <function>filenameToModuleName</function> 是一个接受文件名并提出文件扩展名而只返回文件名称的函数。</para>  
    347 347 </callout>  
    348 348 <callout arearefs="regression.map.3.2">  
    349   <para>Calling &map_function; takes each filename listed in <varname>files</varname>, passes it to the function <function>filenameToModuleName</function>, and returns a list of the return values of each of those function calls.  In other words, you strip the file extension off of each filename, and store the list of all those stripped filenames in <varname>moduleNames</varname>.</para>  
      349 <para>调用它 &map_function; 接受<varname>files</varname>列出的所有文件名,把它传递给 <function>filenameToModuleName</function> 函数,并且返回每个函数调用结果所组成的列表。 换句话说,你剔除掉文件名的扩展名,并将剔除后的文件名存于 <varname>moduleNames</varname> 之中。</para>  
    349 349 </callout>  
    350 350 </calloutlist>  
    351 351 </example>  
    352   <para>As you'll see in the rest of the chapter, you can extend this type of data-centric thinking all the way to the final goal, which is to define and execute a single test suite that contains the tests from all of those individual test suites.</para>  
      352 <para>如你在本章剩余部分将看到的,你可以将这种数据中心思想扩展应用到定义和执行一个容纳来自很多单个测试套件的测试的一个测试套件的最终目标。</para>  
    352 352 </section>  
    353 353 <section id="regression.datacentric">  
    354 354 <?dbhtml filename="functional_programming/data_centric.html"?>  
    355   <title>Data-centric programming</title>  
      355 <title>数据中心思想编程</title>  
    355 355 <abstract>  
    356 356 <title/>  
    357   <para>By now you're probably scratching your head wondering why this is better than using &for; loops and straight function calls.  And that's a perfectly valid question.  Mostly, it's a matter of perspective.  Using &map_function; and &filter_function; forces you to center your thinking around your data.</para>  
      357 <para>现在的你,可能正抓耳挠腮地狠想,为什么这样比使用 &for; 循环和直接调用函数好。这是一个非常好的问题。通常这是一个程序观问题。 使用 &map_function; 和 &filter_function; 强迫你围绕数据进行思考。</para>  
    357 357 </abstract>  
    358   <para>In this case, you started with no data at all; the first thing you did was <link linkend="regression.path">get the directory path</link> of the current script, and got a list of files in that directory.  That was the bootstrap, and it gave you real data to work with: a list of filenames.</para>  
    359   <para>However, you knew you didn't care about all of those files, only the ones that were actually test suites.  You had <emphasis>too much data</emphasis>, so you needed to &filter_function; it.  How did you know which data to keep?  You needed a test to decide, so you defined one and passed it to the &filter_function; function.  In this case you used a regular expression to decide, but the concept would be the same regardless of how you constructed the test.</para>  
    360   <para>Now you had the filenames of each of the test suites (and only the test suites, since everything else had been filtered out), but you really wanted module names instead.  You had the right amount of data, but it was <emphasis>in the wrong format</emphasis>.  So you defined a function that would transform a single filename into a module name, and you mapped that function onto the entire list.  From one filename, you can get a module name; from a list of filenames, you can get a list of module names.</para>  
    361   <para>Instead of &filter_function;, you could have used a &for; loop with an &if; statement.  Instead of &map_function;, you could have used a &for; loop with a function call.  But using &for; loops like that is busywork.  At best, it simply wastes time; at worst, it introduces obscure bugs.  For instance, you need to figure out how to test for the condition <quote>is this file a test suite?</quote> anyway; that's the application-specific logic, and no language can write that for us.  But once you've figured that out, do you really want go to all the trouble of defining a new empty list and writing a &for; loop and an &if; statement and manually calling <function>append</function> to add each element to the new list if it passes the condition and then keeping track of which variable holds the new filtered data and which one holds the old unfiltered data?  Why not just define the test condition, then let &python; do the rest of that work for us?</para>  
    362   <para>Oh sure, you could try to be fancy and delete elements in place without creating a new list.  But you've been burned by that before.  Trying to modify a data structure that you're looping through can be tricky.  You delete an element, then loop to the next element, and suddenly you've skipped one.  Is &python; one of the languages that works that way?  How long would it take you to figure it out?  Would you remember for certain whether it was safe the next time you tried?  Programmers spend so much time and make so many mistakes dealing with purely technical issues like this, and it's all pointless.  It doesn't advance your program at all; it's just busywork.</para>  
    363   <para>I resisted list comprehensions when I first learned &python;, and I resisted &filter_function; and &map_function; even longer.  I insisted on making my life more difficult, sticking to the familiar way of &for; loops and &if; statements and step-by-step code-centric programming.  And my &python; programs looked a lot like &vb; programs, detailing every step of every operation in every function.  And they had all the same types of little problems and obscure bugs.  And it was all pointless.</para>  
    364   <para>Let it all go.  Busywork code is not important.  Data is important.  And data is not difficult.  It's only data.  If you have too much, filter it.  If it's not what you want, map it.  Focus on the data; leave the busywork behind.</para>  
      358 <para>就此而言,你从没有数据开始,你所做的第一件事是 <link linkend="regression.path">获得当前脚本的目录路径</link>,并获得该目录中的文件列表。 这就是关键的一步,使你有了被处理的真实数据:文件名列表。</para>  
      359 <para>当然,你知道你并不关心所有的文件,而只关心测试套件。你有 <emphasis>太多数据</emphasis>, 因此你需要 &filter_function; 数据。   你如何知道哪些数据应该保留? 你需要一个测试来确定,因此你定义一个测试并把它传给 &filter_function; 函数。 这里你应用了一个正则表达式来确定,但无论如何构建测试,原则是一样的。</para>  
      360 <para>现在你有了每个测试套件的文件名(且局限于测试套件,因为所有其他内容都被过滤掉了),但是你确实还需要以模块名来替代之。 你有正确数量的数据,只是 <emphasis>格式不正确</emphasis>。  因此,你定义了一个函数来将文件名转换为模块名,并把这个函数关联到整个列表。 从一个文件名,你可以获得一个模块名,从一个文件名列表,你可以获得一个模块名列表。</para>  
      361 <para>如果不应用 &filter_function;, 你也可以使用 &for; 循环结合一个 &if; 语句的方法。 &map_function; 的使用则可以由一个 &for; 循环和一个函数调用来取代。 但是 &for; 循环看起来像是个繁重的工作。至少,简单讲是在浪费时间,糟糕的化还会隐埋错误(Bug)。 比方说,你需要弄清楚如何测试这样一个条件<quote>这个文件是测试套件吗?</quote> 不管怎么说,这是应用细化逻辑,没有哪个语言可以让我们这样做。 但是一旦你搞清楚了,你还需要费尽周折的定义一个新的空列表、写一个  &for; 循环以及一个 &if; 语句并手工地调用 <function>append</function> 将符合条件的元素一个个添加到新列表中,然后一路注意区分那个变量里放着过滤后的数据,那个变量里放着未过滤得老数据吗? 为什么不直接定义测试条件,然后由 &python; 为你完成接下来的工作呢?</para>  
      362 <para>当然啦,你可以尝试眩一点的做法,去删除列表中的元素而不新建一个列表。 但是你以前吃过这样的亏。 试图在循环中改变数据结构是很容易出问题的。 &python; 是一个这样工作的语言吗? 用多长时间你才能搞清这一点? 你能确定记得你第二次这样尝试的安全性? 程序员在和这类纯技术课题较劲的过程中,花费了太多的时间、犯了太多的错误,却并没有什么意义。这样并不可能令你的程序有所进步,只不过是费力不讨好。</para>  
      363 <para>我在第一次学习 &python; 时是抵触列表遍历的, 而且我抗拒 &filter_function; 和 &map_function; 的时间更长。 我坚持着我更艰难的生活,固守着类似于 &for; 循环和 &if; 语句以及一步步地代码编程方式。 而且我的 &python; 程序看起来很像是 &vb; 程序,细化每一个函数中的每一个操作步骤。 它们却有着同样的小错误和隐蔽的 Bug。 这一切其实都没有意义。</para>  
      364 <para>让这一切都远去吧。 费力不讨好的编程不重要,数据重要。 并且数据并不难,他们不过就是数据。 如果多了,就过滤。 如果不是我们要的,就关联。 聚焦在数据上,摒弃费力的劳作。</para>  
    365 365 </section>  
    366 366 <section id="regression.import">  
    367 367 <?dbhtml filename="functional_programming/dynamic_import.html"?>  
    368   <title>Dynamically importing modules</title>  
      368 <title>动态导入模块</title>  
    368 368 <abstract>  
    369 369 <title/>  
    370   <para>OK, enough philosophizing.  Let's talk about dynamically importing modules.</para>  
      370 <para>好了,大道理谈够了。让我们谈谈动态倒入数据吧。</para>  
    370 370 </abstract>  
    371   <para>First, let's look at how you normally import modules.  The &importmodule; syntax looks in the search path for the named module and imports it by name.  You can even import multiple modules at once this way, with a comma-separated list.  You did this on the very first line of this chapter's script.</para>  
      371 <para>首先,让我们看一看正常的导入模块。  &importmodule; 语法查看搜索路径寻找已命名模块并以名字导入它们。你甚至于可以以这种方法,以逗号分割同时导入多个模块,本章代码前几行就是这样做的。</para>  
    371 371 <example>  
    372   <title>Importing multiple modules at once</title>  
      372 <title>同时导入多个模块</title>  
    372 372 <programlisting>  
    373 373 &regression_import; <co id="regression.import.1.1"/>  
     
    384 384 <calloutlist>  
    385 385 <callout arearefs="regression.import.1.1">  
    386   <para>This imports four modules at once: <filename class="headerfile">sys</filename> (for system functions and access to the command line parameters), <filename class="headerfile">os</filename> (for operating system functions like directory listings), <filename class="headerfile">re</filename> (for regular expressions), and <filename class="headerfile">unittest</filename> (for unit testing).</para>  
      386 <para>这里同时导入四个模块:<filename class="headerfile">sys</filename> (为系统函数和得到命令行参数), <filename class="headerfile">os</filename> (为目录列表之类的操作系统函数), <filename class="headerfile">re</filename> (为正则表达式),以及 <filename class="headerfile">unittest</filename> (为单元测试)。</para>  
    386 386 </callout>  
    387 387 </calloutlist>  
    388 388 </example>  
    389   <para>Now let's do the same thing, but with dynamic imports.</para>  
      389 <para>现在让我们用动态导入做同样的事。</para>  
    389 389 <example>  
    390   <title>Importing modules dynamically</title>  
      390 <title>动态倒入模块</title>  
    390 390 <screen>  
    391 391 &prompt;<userinput>sys = __import__('sys')</userinput>           <co id="regression.import.2.1"/>  
     
    403 403 <calloutlist>  
    404 404 <callout arearefs="regression.import.2.1">  
    405   <para>The built-in &importfunction; function accomplishes the same goal as using the &import; statement, but it's an actual function, and it takes a string as an argument.</para>  
      405 <para>内建 &importfunction; 函数与 &import; 语句的既定目标相同,但它是一个真正的函数,并接受一个字符串参数。</para>  
    405 405 </callout>  
    406 406 <callout arearefs="regression.import.2.2">  
    407   <para>The variable <varname>sys</varname> is now the <filename class="headerfile">sys</filename> module, just as if you had said <literal>import sys</literal>.  The variable <varname>os</varname> is now the <filename class="headerfile">os</filename> module, and so forth.</para>  
      407 <para>变量 <varname>sys</varname> 现在是 <filename class="headerfile">sys</filename> 模块, 就像你所说的 <literal>import sys</literal>。 变量 <varname>os</varname> 现在是一个 <filename class="headerfile">os</filename> 模块,等等。</para>  
    407 407 </callout>  
    408 408 </calloutlist>  
    409 409 </example>  
    410   <para>So &importfunction; imports a module, but takes a string argument to do it.  In this case the module you imported was just a hard-coded string, but it could just as easily be a variable, or the result of a function call.  And the variable that you assign the module to doesn't need to match the module name, either.  You could import a series of modules and assign them to a list.</para>  
      410 <para>因此 &importfunction; 导入一个模块,但是是通过一个字符串参数来做到的。 依此处讲,你导入的仅仅是一个硬性的字符串代码,但它可以是一个简单的变量,或者一个函数调用的结果。 并且你指向模块的变量也不必与模块名匹配。 你可以导入一系列模块并把它们指派给一个列表。</para>  
    410 410 <example>  
    411   <title>Importing a list of modules dynamically</title>  
      411 <title>动态导入一个列表锁定的模块</title>  
    411 411 <screen>  
    412 412 &prompt;<userinput>moduleNames = ['sys', 'os', 're', 'unittest']</userinput> <co id="regression.import.3.1"/>  
     
    431 431 <calloutlist>  
    432 432 <callout arearefs="regression.import.3.1">  
    433   <para><varname>moduleNames</varname> is just a list of strings.  Nothing fancy, except that the strings happen to be names of modules that you could import, if you wanted to.</para>  
      433 <para><varname>moduleNames</varname> 只是一个字符串列表。 没什么特别的,只是这些名字刚好是你可应需而用的可导入模块名。</para>  
    433 433 </callout>  
    434 434 <callout arearefs="regression.import.3.2">  
    435   <para>Surprise, you wanted to import them, and you did, by mapping the &importfunction; function onto the list.  Remember, this takes each element of the list (<varname>moduleNames</varname>) and calls the function (&importfunction;) over and over, once with each element of the list, builds a list of the return values, and returns the result.</para>  
      435 <para>令人惊奇,你需要导入他们,且通过关联 &importfunction; 到列表实现了。记住,列表(<varname>moduleNames</varname>)的每个元素将被用来一次次调用函数 (&importfunction;)并以一个返回值构成的列表作为返回结果。</para>  
    435 435 </callout>  
    436 436 <callout arearefs="regression.import.3.3">  
    437   <para>So now from a list of strings, you've created a list of actual modules.  (Your paths may be different, depending on your operating system, where you installed Python, the phase of the moon, etc.)</para>  
      437 <para>所以现在你已经由一个字符串列表构建起了一个实际模块的列表。(你的路径可能不同,这取决于你的操作系统,你安装 Python 的位置,月亮残缺的程度等等)</para>  
    437 437 </callout>  
    438 438 <callout arearefs="regression.import.3.4">  
    439   <para>To drive home the point that these are real modules, let's look at some module attributes.  Remember, <varname>modules[0]</varname> <emphasis>is</emphasis> the &sys; module, so <varname>modules[0].version</varname> <emphasis>is</emphasis> <varname>sys.version</varname>.  All the other attributes and methods of these modules are also available.  There's nothing magic about the &import; statement, and there's nothing magic about modules.  Modules are objects.  Everything is an object.</para>  
      439 <para>从这些是真实模块这一点出发,让我们来看一些模块属性。 记住, <varname>modules[0]</varname> <emphasis>是</emphasis>  &sys; 模块, 因此, <varname>modules[0].version</varname> <emphasis>是</emphasis> <varname>sys.version</varname>。  所有模块的其他属性和方法也都可用。 &import; 语句没什么神奇的, 模块也没什么神奇的。 模块就是对象,一切都是对象。</para>  
    439 439 </callout>  
    440 440 </calloutlist>  
    441 441 </example>  
    442   <para>Now you should be able to put this all together and figure out what most of this chapter's code sample is doing.</para>  
      442 <para>现在,你应该能够把这一切放在一起并能搞清楚本章大部分范例代码的是做什么的。</para>  
    442 442 </section>  
    443 443 <section id="regression.alltogether">  
    444 444 <?dbhtml filename="functional_programming/all_together.html"?>  
    445   <title>Putting it all together</title>  
      445 <title>把一切放在一起</title>  
    445 445 <abstract>  
    446 446 <title/>  
    447   <para>You've learned enough now to deconstruct the first seven lines of this chapter's code sample: reading a directory and importing selected modules within it.</para>  
      447 <para>你已经学习了足够的知识,现在来分析本章范例代码的前七行:读取一个目录并从中导入选定的模块。</para>  
    447 447 </abstract>  
    448 448 <example>  
    449   <title>The &regressiontestfunction; function</title>  
      449 <title>&regressiontestfunction; 函数</title>  
    449 449 <programlisting>  
    450 450 &regression_testdef;  
     
    468 468 </programlisting>  
    469 469 </example>  
    470   <para>Let's look at it line by line, interactively.  Assume that the current directory is <filename>c:\diveintopython\py</filename>, which contains the examples that come with this book, including this chapter's script.  As you saw in <xref linkend="regression.path"/>, the script directory will end up in the <varname>path</varname> variable, so let's start hard-code that and go from there.</para>  
      470 <para>让我们一行行交互地看。 假定当前目录是 <filename>c:\diveintopython\py</filename>,其中有包含本章脚本在内的本书众多范例。 正如在 <xref linkend="regression.path"/> 中所见, 脚本目录将最终存于 <varname>path</varname> 变量, 因此让我们从这里开始以实打实的代码起步。</para>  
    470 470 <example>  
    471   <title>Step 1: Get all the files</title>  
      471 <title>步骤 1: 获得所有文件</title>  
    471 471 <screen>  
    472 472 &prompt;<userinput>&regression_import;</userinput>  
     
    486 486 <calloutlist>  
    487 487 <callout arearefs="regression.alltogether.1.1">  
    488   <para><varname>files</varname> is a list of all the files and directories in the script's directory.  (If you've been running some of the examples already, you may also see some <filename>.pyc</filename> files in there as well.)</para>  
      488 <para><varname>files</varname> 是由脚本所在目录的所有文件和目录构成的列表。 (如果你已经运行了其中的一些范例,可能还会看到一些 <filename>.pyc</filename> 文件。)</para>  
    488 488 </callout>  
    489 489 </calloutlist>  
    490 490 </example>  
    491 491 <example>  
    492   <title>Step 2: Filter to find the files you care about</title>  
      492 <title>步骤 2: 找到你关注的多个文件</title>  
    492 492 <screen>  
    493 493 &prompt;<userinput>&regression_compile1;</userinput> <co id="regression.alltogether.2.1"/>  
     
    500 500 <calloutlist>  
    501 501 <callout arearefs="regression.alltogether.2.1">  
    502   <para>This regular expression will match any string that ends with <literal>test.py</literal>.  Note that you need to escape the period, since a period in a regular expression usually means <quote>match any single character</quote>, but you actually want to match a literal period instead.</para>  
      502 <para>这个正则表达式将匹配以 <literal>test.py</literal> 结尾的任意字符串。 注意,你必须转义这个点号,因为正则表达式中的点号通常意味着 <quote>匹配任意单字符</quote>, 但是你实际上想匹配的事一个真正的点号。</para>  
    502 502 </callout>  
    503 503 <callout arearefs="regression.alltogether.2.2">  
    504   <para>The compiled regular expression acts like a function, so you can use it to filter the large list of files and directories, to find the ones that match the regular expression.</para>  
      504 <para>被编译的正则表达式就像一个函数,因此你可以用它来过滤文件和目录构成的大列表,找寻符合正则表达式的所有元素。</para>  
    504 504 </callout>  
    505 505 <callout arearefs="regression.alltogether.2.3">  
    506   <para>And you're left with the list of unit testing scripts, because they were the only ones named <filename>SOMETHINGtest.py</filename>.</para>  
      506 <para>剩下的是一个单元测试脚本列表,因为只有它们是 <filename>SOMETHINGtest.py</filename>  的文件。</para>  
    506 506 </callout>  
    507 507 </calloutlist>  
    508 508 </example>  
    509 509 <example>  
    510   <title>Step 3: Map filenames to module names</title>  
      510 <title>步骤 3: 文件名和模块名关联</title>  
    510 510 <screen>  
    511 511 &prompt;<userinput>&regression_lambda1;</userinput> <co id="regression.alltogether.3.1"/>  
     
    524 524 <calloutlist>  
    525 525 <callout arearefs="regression.alltogether.3.1">  
    526   <para>As you saw in <xref linkend="apihelper.lambda"/>, &lambdafunction; is a quick-and-dirty way of creating an inline, one-line function.  This one takes a filename with an extension and returns just the filename part, using the standard library function <function>os.path.splitext</function> that you saw in <xref linkend="splittingpathnames.example"/>.</para>  
      526 <para>正如你在 <xref linkend="apihelper.lambda"/> 中所见, &lambdafunction; 快餐式创建内嵌单行函数。 这里应用你在  <xref linkend="splittingpathnames.example"/> 中已经见过的,标准库的 <function>os.path.splitext</function> 将一个带有扩展名的文件名返回成只包含文件名称部分。</para>  
    526 526 </callout>  
    527 527 <callout arearefs="regression.alltogether.3.2">  
    528   <para><varname>filenameToModuleName</varname> is a function.  There's nothing magic about &lambdafunction; functions as opposed to regular functions that you define with a <literal>def</literal> statement.  You can call the <varname>filenameToModuleName</varname> function like any other, and it does just what you wanted it to do: strips the file extension off of its argument.</para>  
      528 <para><varname>filenameToModuleName</varname> 是一个函数。 &lambdafunction; 与你以 <literal>def</literal> 语句定义的针对正则表达式的函数相比并不神奇。 你可以如其他函数一样的调用 <varname>filenameToModuleName</varname> ,它也将如你所愿:从参数中剔除扩展名。</para>  
    528 528 </callout>  
    529 529 <callout arearefs="regression.alltogether.3.3">  
    530   <para>Now you can apply this function to each file in the list of unit test files, using &map_function;.</para>  
      530 <para>现在你可以通过 &map_function; 把这个函数应用于单元测试文件列表中的每一个文件。</para>  
    530 530 </callout>  
    531 531 <callout arearefs="regression.alltogether.3.4">  
    532   <para>And the result is just what you wanted: a list of modules, as strings.</para>  
      532 <para>结果当然如你所愿:以指代模块的字符串构成的一个列表。</para>  
    532 532 </callout>  
    533 533 </calloutlist>  
    534 534 </example>  
    535 535 <example>  
    536   <title>Step 4: Mapping module names to modules</title>  
      536 <title>步骤 4: 模块名和模块关联</title>  
    536 536 <screen>  
    537 537 &prompt;<userinput>&regression_mapimport1;</userinput> <co id="regression.alltogether.4.1"/>  
     
    552 552 <calloutlist>  
    553 553 <callout arearefs="regression.alltogether.4.1">  
    554   <para>As you saw in <xref linkend="regression.import"/>, you can use a combination of &map_function; and &importfunction; to map a list of module names (as strings) into actual modules (which you can call or access like any other module).  
      554 <para>正如你在 <xref linkend="regression.import"/> 中所见,你可以通过 &map_function; 和 &importfunction; 的协同工作,将模块名(字符串)关联到实际的模块(向其他模块一样可以被调用和使用)。  
    554 554 </para>  
    555 555 </callout>  
    556 556 <callout arearefs="regression.alltogether.4.2">  
    557   <para><varname>modules</varname> is now a list of modules, fully accessible like any other module.</para>  
      557 <para><varname>modules</varname> 现在是一个模块列表,向其他模块一样可用。</para>  
    557 557 </callout>  
    558 558 <callout arearefs="regression.alltogether.4.3">  
    559   <para>The last module in the list <emphasis>is</emphasis> the &romantest_module; module, just as if you had said <literal>import romantest</literal>.</para>  
      559 <para>该列表的最后一个模块 <emphasis>是</emphasis> &romantest_module; 模块,恰是你曾说的 <literal>import romantest</literal> 所指的模块。</para>  
    559 559 </callout>  
    560 560 </calloutlist>  
    561 561 </example>  
    562 562 <example>  
    563   <title>Step 5: Loading the modules into a test suite</title>  
      563 <title>步骤 5: 将模块调入测试套件</title>  
    563 563 <screen>  
    564 564 &prompt;<userinput>&regression_load1;</userinput>  
     
    581 581 <calloutlist>  
    582 582 <callout arearefs="regression.alltogether.5.1">  
    583   <para>These are real module objects.  Not only can you access them like any other module, instantiate classes and call functions, you can also introspect into the module to figure out which classes and functions it has in the first place.  That's what the <function>loadTestsFromModule</function> method does: it introspects into each module and returns a <literal>unittest.TestSuite</literal> object for each module.  Each <literal>TestSuite</literal> object actually contains a list of <literal>TestSuite</literal> objects, one for each <literal>TestCase</literal> class in your module, and each of those <literal>TestSuite</literal> objects contains a list of tests, one for each test method in your module.</para>  
      583 <para>模块对象的存在,使你不但可以像其他模块一样地使用它们;通过类的实例化和函数的调用,你还可以内省模块,从而弄清楚已经有了那些类和函数。 这正是 <function>loadTestsFromModule</function> 方法的工作:内省每一个模块并为每个模块返回一个 <literal>unittest.TestSuite</literal> 对象。 每个 <literal>TestSuite</literal> 对象实际上都包含了一个 <literal>TestSuite</literal> 对象列表,每个对象对应着你的模块中的一个测试方法。</para>  
    583 583 </callout>  
    584 584 <callout arearefs="regression.alltogether.5.2">  
    585   <para>Finally, you wrap the list of <literal>TestSuite</literal> objects into one big test suite.  The &unittest_modulename; module has no problem traversing this tree of nested test suites within test suites; eventually it gets down to an individual test method and executes it, verifies that it passes or fails, and moves on to the next one.</para>  
      585 <para>最后,你将 <literal>测试套件(TestSuite)</literal> 列表封装成一个大的测试套件。&unittest_modulename; 模块会很自如地遍历嵌套于测试套件中的树状结构,最后深入到独立测试方法,一个个加以运行并判断通过或是失败。</para>  
    585 585 </callout>  
    586 586 </calloutlist>  
    587 587 </example>  
    588   <para>This introspection process is what the &unittest_modulename; module usually does for us.  Remember that magic-looking <literal>unittest.main()</literal> function that our individual test modules called to kick the whole thing off?  <function>unittest.main()</function> actually creates an instance of <literal>unittest.TestProgram</literal>, which in turn creates an instance of a <literal>unittest.defaultTestLoader</literal> and loads it up with the module that called it.  (How does it get a reference to the module that called it if you don't give it one?  By using the equally-magic <literal>__import__('__main__')</literal> command, which dynamically imports the currently-running module.  I could write a book on all the tricks and techniques used in the &unittest_modulename; module, but then I'd never finish this one.)</para>  
      588 <para>自省过程是 &unittest_modulename; 模块经常为我们做的一项工作。 还记得我们的独立测试模块调用并大刀阔斧地完成一起工作的那个看似神奇的 <literal>unittest.main()</literal>函数吗?  <function>unittest.main()</function> 实际上创建了一个 <literal>unittest.TestProgram</literal> 的实例,而这个实例实际上创建了一个 <literal>unittest.defaultTestLoader</literal> 的实例并以调用它的模块启动它。 (如果你不给出,如何知道调用它的模块是哪一个?通过使用同样神奇的 <literal>__import__('__main__')</literal> 命令,动态导入正在运行的模块。 我可以就 &unittest_modulename; 模块中使用的所有技巧和技术写一本书,但那样我就没法写完这本了。)</para>  
    588 588 <example>  
    589   <title>Step 6: Telling &unittest_modulename; to use your test suite</title>  
      589 <title>步骤 6: 告知 &unittest_modulename; 使用你的测试套件</title>  
    589 589 <programlisting>  
    590 590 &regression_ifmain;  
     
    598 598 <calloutlist>  
    599 599 <callout arearefs="regression.alltogether.6.1">  
    600   <para>Instead of letting the &unittest_modulename; module do all its magic for us, you've done most of it yourself.  You've created a function (<function>regressionTest</function>) that imports the modules yourself, calls <literal>unittest.defaultTestLoader</literal> yourself, and wraps it all up in a test suite.  Now all you need to do is tell &unittest_modulename; that, instead of looking for tests and building a test suite in the usual way, it should just call the <function>regressionTest</function> function, which returns a ready-to-use <literal>TestSuite</literal>.</para>  
      600 <para>在不使用 &unittest_modulename; 模块来为我们做这一切的神奇工作的情况下,你实际上已自己做到了。 你已经创建了一个自己就能导入模块、调用 <literal>unittest.defaultTestLoader</literal> 并封装于一个测试套件的(<function>regressionTest</function>) 函数。现在你所要做的不是去寻找测试并以通用的方法构建一个测试套件,而是告诉 &unittest_modulename; 前面那些,它将调用返回可以直接使用的 <literal>TestSuite</literal> 的  <function>regressionTest</function> 函数。</para>  
    600 600 </callout>  
    601 601 </calloutlist>  
    604 604 <section id="regression.summary">  
    605 605 <?dbhtml filename="functional_programming/summary.html"?>  
    606   <title>Summary</title>  
      606 <title>小结</title>  
    606 606 <abstract>  
    607 607 <title/>  
    608   <para>The &regression_filename; program and its output should now make perfect sense.</para>  
      608 <para>&regression_filename; 程序及其输出到现在应该很清楚了。</para>  
    608 608 </abstract>  
    609   <para>You should now feel comfortable doing all of these things:</para>  
      609 <para>你现在应该能够很自如地做到如下事情:</para>  
    609 609 <itemizedlist>  
    610   <listitem><para>Manipulating <link linkend="regression.path">path information</link> from the command line.</para></listitem>  
    611   <listitem><para>Filtering lists <link linkend="regression.filter">using &filter_function;</link> instead of list comprehensions.</para></listitem>  
    612   <listitem><para>Mapping lists <link linkend="regression.map">using &map_function;</link> instead of list comprehensions.</para></listitem>  
    613   <listitem><para>Dynamically <link linkend="regression.import">importing modules</link>.</para></listitem>  
      610 <listitem><para>从命令行操作 <link linkend="regression.path">路径信息</link>。</para></listitem>  
      611 <listitem><para>在不使用列表遍历的情况下, <link linkend="regression.filter">使用  &filter_function;</link> 过滤列表。</para></listitem>  
      612 <listitem><para>在不使用列表遍历的情况下, <link linkend="regression.map">使用 &map_function;</link>关联列表。</para></listitem>  
      613 <listitem><para>动态<link linkend="regression.import">导入模块</link>。</para></listitem>  
    614 614 </itemizedlist>  
    615 615 </section>  
  • zh-translations/branches/diveintopython-zh-5.4/zh-cn/xml/soundex.xml

    r211 r858  
    2 2 <chapter id="soundex">  
    3 3 <?dbhtml filename="performance_tuning/index.html"?>  
    4   <title>Performance Tuning</title>  
    5   <titleabbrev id="soundex.numberonly">第十八章</titleabbrev>  
      4 <title>性能优化</title>  
      5 <titleabbrev id="soundex.numberonly">第 18 章</titleabbrev>  
    6 6 <abstract>  
    7 7 <title/>  
    8   <para>性能优化(Performance tuning)是一件多姿多彩的事情。不过因为 &python; 是一种解释性语言并不表示你不应该担心代码优化。但也不要对它 <emphasis>太</emphasis> 担心。</para>  
      8 <para>性能优化 (Performance tuning) 是一件多姿多彩的事情。&python; 是一种解释性语言并不表示你不应该担心代码优化。但也不必 <emphasis>太</emphasis> 担心。</para>  
    8 8 </abstract>  
    9 9 <section id="soundex.divein">  
    10   <title>Diving in</title>  
      10 <title>概览</title>  
    10 10 <abstract>  
    11 11 <title/>  
    12   <para>由于在优化代码方面存在太多的缺陷,因此很难知道从哪里开��。</para>  
      12 <para>由于代码优化过程中存在太多的不明确因素,以至于你很难清除该从何入��。</para>  
    12 12 </abstract>  
    13   <para>让我们从这里开始:<emphasis>你确信完全需要去做吗?</emphasis>  你的代码很差吗?花费时间进行优化值得吗?在整个应用程序的生命周期里,与花费在等待一个远程的数据库,或等待用户输入相比,运行代码会花费多少时间?</para>  
    14   <para>第二,<emphasis>确定你写完程序了吗?</emphasis> 过早的优化就象在一块半生不熟的蛋糕上撒上糖霜。你花费了几小时或几天(或更长)来优化你的代码来提高性能,仅仅是为了发现它不能完成你希望它做的。那是浪费时间(译注:原文为“That's time down the drain”)。 </para>  
    15   <para>这并不是说代码优化是无用的,但是你需要检查一下整个系统,并且决定在时间使用上它是否是最好的。在优化代码上每小每花费一分钟,这一分钟你就不能用来增加新特性,或编写文档,或同你的孩子们玩,或编写单元测试。</para>  
    16   <para>哦,是的,单元测试。不言而喻,在开始性能优化之前你需要一个完全的单元测试集。你需要的最后一件事情就是在乱动你的算法时引入新的问题。</para>  
    17   <para>With these caveats in place, let's look at some techniques for optimizing &python; code.  The code in question is an implementation of the Soundex algorithm.  Soundex was a method used in the early 20th century for categorizing surnames in the United States census.  It grouped similar-sounding names together, so even if a name was misspelled, researchers had a chance of finding it.  Soundex is still used today for much the same reason, although of course we use computerized database servers now.  Most database servers include a Soundex function.</para>  
    18   <para>There are several subtle variations of the Soundex algorithm.  This is the one used in this chapter:</para>  
      13 <para>让我们从这里开始:<emphasis>你真的确信你要这样做吗?</emphasis>  你的代码真的那么差吗?值得花时间去优化它吗?在你的应用程序的生命周期中,与花费在等待一个远程数据库服务器,或是等待用户输入相比,运行这段代码将花费多少时间?</para>  
      14 <para>第二,<emphasis>你确信已经完成代码编写了吗?</emphasis> 过早的优化就像是在一块半生不熟的蛋糕上撒糖霜。你花费了几小时、几天(或更长) 时间来优化你的代码以提高性能,却发现它不能完成你希望它做的工作。那是浪费时间。</para>  
      15 <para>这并不是说代码优化毫无用处,但是你需要检查一下整个系统,并且确定把时间花在这上面是值得的。在优化代码上每花费一分钟,就意味着你少了增加新功能、编写文档或者陪你的孩子玩或者编写单元测试的一分钟。</para>  
      16 <para>哦,是的,单元测试。不必我说,在开始性能优化之前你需要一个完全的单元测试集。你需要的最后一件事情就是在乱动你的算法时引入新的问题。</para>  
      17 <para>谨记着这些忠告,让我们来看一些优化 &python; 代码的技术。 我们要研究的代码是实施 Soundex 算法。 Soundex 是一种 20 世纪在美国人口普查中归档姓氏的方法。 它把听起来相似的姓氏归在一起,使得在即便错误拼写的情况下调查者仍能查找到。 Soundex 今天仍然因差不多的原因被应用着,当然现在用计算机数据库服务器了。 大部分的数据库服务器都有 Soundex 函数。</para>  
      18 <para>Soundex 算法有几个差别不大的变化版本。这是本章使用的:</para>  
    19 19 <orderedlist>  
    20   <listitem><para>Keep the first letter of the name as-is.</para></listitem>  
    21   <listitem><para>Convert the remaining letters to digits, according to a specific table:</para>  
      20 <listitem><para>名字的第一个字母不变</para></listitem>  
      21 <listitem><para>根据特定的对照表,将剩下的字母转换为数字:</para>  
    22 22 <itemizedlist>  
    23   <listitem><para>B, F, P, and V become 1.</para></listitem>  
    24   <listitem><para>C, G, J, K, Q, S, X, and Z become 2.</para></listitem>  
    25   <listitem><para>D and T become 3.</para></listitem>  
    26   <listitem><para>L becomes 4.</para></listitem>  
    27   <listitem><para>M and N become 5.</para></listitem>  
    28   <listitem><para>R becomes 6.</para></listitem>  
    29   <listitem><para>All other letters become 9.</para></listitem>  
      23 <listitem><para>B、 F、 P 和 V 转换为 1。</para></listitem>  
      24 <listitem><para>C、 G、 J、 K、 Q、 S、 X 和 Z 转换为 2.</para></listitem>  
      25 <listitem><para>D 和 T 转换为 3.</para></listitem>  
      26 <listitem><para>L 转换为 4.</para></listitem>  
      27 <listitem><para>M 和 N 转换为 5.</para></listitem>  
      28 <listitem><para>R 转换为 6.</para></listitem>  
      29 <listitem><para>所有其他字母转换为 9.</para></listitem>  
    30 30 </itemizedlist>  
    31 31 </listitem>  
    32   <listitem><para>Remove consecutive duplicates.</para></listitem>  
    33   <listitem><para>Remove all 9s altogether.</para></listitem>  
    34   <listitem><para>If the result is shorter than four characters (the first letter plus three digits), pad the result with trailing zeros.</para></listitem>  
    35   <listitem><para>if the result is longer than four characters, discard everything after the fourth character.</para></listitem>  
      32 <listitem><para>去除连续重复。</para></listitem>  
      33 <listitem><para>去除所有 9 。</para></listitem>  
      34 <listitem><para>如果结果都少于四个字符(第一个字母加上后面的三位字符),就以零补齐。</para></listitem>  
      35 <listitem><para>如果结果超过四个字符,丢弃掉四位之后的字符。</para></listitem>  
    36 36 </orderedlist>  
    37   <para>For example, my name, <literal>Pilgrim</literal>, becomes P942695.  That has no consecutive duplicates, so nothing to do there.  Then you remove the 9s, leaving P4265.  That's too long, so you discard the excess character, leaving P426.</para>  
    38   <para>Another example: <literal>Woo</literal> becomes W99, which becomes W9, which becomes W, which gets padded with zeros to become W000.</para>  
    39   <para>Here's a first attempt at a Soundex function:</para>  
      37 <para>比如,我的名字 <literal>Pilgrim</literal> 被转换为 P942695。 没有连续重复,所以这一步不需要做。然后是去除 9 ,剩下 P4265。 太长了,所以你把超出的字符丢弃,剩下 P426。</para>  
      38 <para>另一个例子: <literal>Woo</literal> 被转换为 W99,变成 W9,变成 W,然后以补零成为 W000 。</para>  
      39 <para>这是 Soundex 函数的第一次尝试:</para>  
    40 40 <example>  
    41 41 <title><filename>soundex/stage1/soundex1a.py</filename></title>  
     
    120 120 </example>  
    121 121 <itemizedlist role="furtherreading">  
    122   <title>Further Reading on Soundex</title>  
    123   <listitem><para><ulink url="http://www.avotaynu.com/soundex.html">Soundexing and Genealogy</ulink> gives a chronology of the evolution of the Soundex and its regional variations.</para></listitem>  
      122 <title>进一步阅读</title>  
      123 <listitem><para><ulink url="http://www.avotaynu.com/soundex.html">Soundexing and Genealogy</ulink> 给出了 Soundex 发展的年代表以及地域变化。</para></listitem>  
    124 124 </itemizedlist>  
    125 125 </section>  
     
    127 127 <section id="soundex.timeit">  
    128 128 <?dbhtml filename="performance_tuning/timeit.html"?>  
    129   <title>Using the &timeit; Module</title>  
      129 <title>使用 &timeit; 模块</title>  
    129 129 <abstract>  
    130 130 <title/>  
    131   <para>The most important thing you need to know about optimizing &python; code is that you shouldn't write your own timing function.</para>  
      131 <para>关于 &python; 代码优化你需要知道的最重要问题是,决不要自己编写计时函数。</para>  
    131 131 </abstract>  
    132   <para>Timing short pieces of code is incredibly complex.  How much processor time is your computer devoting to running this code?  Are there things running in the background?  Are you sure?  Every modern computer has background processes running, some all the time, some intermittently.  Cron jobs fire off at consistent intervals; background services occasionally <quote>wake up</quote> to do useful things like check for new mail, connect to instant messaging servers, check for application updates, scan for viruses, check whether a disk has been inserted into your CD drive in the last 100 nanoseconds, and so on.  Before you start your timing tests, turn everything off and disconnect from the network.  Then turn off all the things you forgot to turn off the first time, then turn off the service that's incessantly checking whether the network has come back yet, then ...</para>  
    133   <para>And then there's the matter of the variations introduced by the timing framework itself.  Does the &python; interpreter cache method name lookups?  Does it cache code block compilations?  Regular expressions?  Will your code have side effects if run more than once?  Don't forget that you're dealing with small fractions of a second, so small mistakes in your timing framework will irreparably skew your results.</para>  
    134   <para>The &python; community has a saying: <quote>&python; comes with batteries included.</quote>  Don't write your own timing framework.  &python; 2.3 comes with a perfectly good one called &timeit;.</para>  
      132 <para>为一个很短的代码计时都很复杂。 处理器有多少时间用于运行这个代码? 有什么在后台运行吗? 每个现代计算机都在后台运行持续或者间歇的程序。 小小的疏忽可能破坏你的百年大计,后台服务偶尔被 <quote>唤醒</quote> 在最后千分之一秒做一些像查收信件,连接计时通信服务器,检查应用程序更新,扫描病毒,查看是否有磁盘被插入光驱之类很有意义的事。 在开始计时测试之前,把一切都关掉,断开网络的连接。再次确定一切都关上后关掉那些不断查看网络是否恢复的服务等等。</para>  
      133 <para>接下来是计时框架本身引入的变化因素。 &python; 解释器是否缓存方法名查找? 是否缓存代码块编辑? 正则表达式? 你的代码重复运行时有副作用吗? 不要忘记,你的工作结果将以比秒更小的单位呈现,你的计时框架中的小错误将会带来不可挽回的结果扭曲。</para>  
      134 <para>&python; 社区有句俗语: <quote>&python; 自己带着电池。</quote> 别自己写计时框架。 &python; 2.3 具备一个叫做 &timeit; 的完美计时工具。</para>  
    135 135 <example>  
    136   <title>Introducing &timeit;</title>  
      136 <title>介绍 &timeit;</title>  
    136 136 &para_download;  
    137 137 <screen>  
     
    149 149 <calloutlist>  
    150 150 <callout arearefs="soundex.timeit.1.1">  
    151   <para>The &timeit; module defines one class, &timer;, which takes two arguments.  Both arguments are strings.  The first argument is the statement you wish to time; in this case, you are timing a call to the &soundex; function within the &soundex_modulename; with an argument of <literal>'Pilgrim'</literal>.  The second argument to the <classname>Timer</classname> class is the import statement that sets up the environment for the statement.  Internally, &timeit; sets up an isolated virtual environment, manually executes the setup statement (importing the &soundex_modulename; module), then manually compiles and executes the timed statement (calling the &soundex; function).</para>  
      151 <para>&timeit; 模块定义了接受两个参数的 &timer; 类。两个参数都是字符串。 第一个参数是你要计时的语句,这里你计时的是以<literal>'Pilgrim'</literal>参数调用 &soundex; 函数。 传递给 <classname>Timer</classname> 的第二个参数是为第一个参数语句构建环境的导入语句。 从内部讲, &timeit; 构建起一个独立的虚拟环境, 手工地执行建立语句(导入 &soundex_modulename; 模块),然后手工地编译和执行被计时语句(调用 &soundex; 函数)。</para>  
    151 151 </callout>  
    152 152 <callout arearefs="soundex.timeit.1.2">  
    153   <para>Once you have the &timer; object, the easiest thing to do is call <methodname>timeit()</methodname>, which calls your function 1 million times and returns the number of seconds it took to do it.</para>  
      153 <para>一旦有了 &timer; 对象,最简单的事就是调用 <methodname>timeit()</methodname>,它调用你的函数一百万次并返回所耗费的秒数。</para>  
    153 153 </callout>  
    154 154 <callout arearefs="soundex.timeit.1.3">  
    155   <para>The other major method of the &timer; object is <methodname>repeat()</methodname>, which takes two optional arguments.  The first argument is the number of times to repeat the entire test, and the second argument is the number of times to call the timed statement within each test.  Both arguments are optional, and they default to <literal>3</literal> and <literal>1000000</literal> respectively.  The <methodname>repeat()</methodname> method returns a list of the times each test cycle took, in seconds.</para>  
      155 <para>&timer; 对象的另一个主要方法是 <methodname>repeat()</methodname>, 它接受两个可选参数。 第一个参数是重复整个测试的次数,第二个参数是每个测试中调用被计时语句的次数。 两个参数都是可选的,它们的默认值分别是 <literal>3</literal> 和 <literal>1000000</literal>。 <methodname>repeat()</methodname> 方法返回以秒记录的每个测试循环的耗时列表。</para>  
    155 155 </callout>  
    156 156 </calloutlist>  
     
    161 161 <tip>  
    162 162 <title/>  
    163   <para>You can use the &timeit; module on the command line to test an existing &python; program, without modifying the code.  See <ulink url="http://docs.python.org/lib/node396.html"/> for documentation on the command-line flags.</para>  
      163 <para>你可以在命令行使用 &timeit; 模块来测试一个已存在的 &python; 程序,而不需要修改代码。在 <ulink url="http://docs.python.org/lib/node396.html"/> 查看文档中关于命令行选项的内容。</para>  
    163 163 </tip>  
    164   <para>Note that <methodname>repeat()</methodname> returns a list of times.  The times will almost never be identical, due to slight variations in how much processor time the &python; interpreter is getting (and those pesky background processes that you can't get rid of).  Your first thought might be to say <quote>Let's take the average and call that The True Number.</quote></para>  
    165   <para>In fact, that's almost certainly wrong.  The tests that took longer didn't take longer because of variations in your code or in the &python; interpreter; they took longer because of those pesky background processes, or other factors outside of the &python; interpreter that you can't fully eliminate.  If the different timing results differ by more than a few percent, you still have too much variability to trust the results.  Otherwise, take the minimum time and discard the rest.</para>  
    166   <para>&python; has a handy <function>min</function> function that takes a list and returns the smallest value:</para>  
      164 <para>注意 <methodname>repeat()</methodname> 返回一个时间列表。 由于 &python; 计时器使用的处理器时间的微小变化(或者那些你没办法根除的可恶的后台进程),这些时间中几乎不可能出现重复。你的第一想法也许是说:<quote>让我们求平均值获得真实的数据。</quote></para>  
      165 <para>事实上,那几乎是确定错误的。 你的代码或者 &python; 解释器的变化可能缩短耗时,那些没办法去处的可恶后台进程或者其他 &python; 解释器以外的因素也许另耗时延长。 如果计时结果之间的差异超过百分之几,太多的可变因素使你没法相信结果,如果不是这样则可以取最小值而丢弃其他结果。</para>  
      166 <para>&python; 有一个方便的 <function>min</function> 函数可以把输入的列表返回成最小值:</para>  
    167 167 <informalexample>  
    168 168 <screen>  
     
    174 174 <tip>  
    175 175 <title/>  
    176   <para>The &timeit; module only works if you already know what piece of code you need to optimize.  If you have a larger &python; program and don't know where your performance problems are, check out <ulink url="http://docs.python.org/lib/module-hotshot.html">the <filename>hotshot</filename> module.</ulink></para>  
      176 <para>&timeit; 模块只有在你知道那段代码需要优化时使用。 如果你有一个很大的 &python; 程序并且不知道你的性能问题所在,到 <ulink url="http://docs.python.org/lib/module-hotshot.html"> 查看 <filename>hotshot</filename> 模块</ulink>。</para>  
    176 176 </tip>  
    177 177 </section>  
     
    180 180 <section id="soundex.stage1">  
    181 181 <?dbhtml filename="performance_tuning/regular_expressions.html"?>  
    182   <title>Optimizing Regular Expressions</title>  
      182 <title>优化正则表达式</title>  
    182 182 <abstract>  
    183 183 <title/>  
    184   <para>The first thing the &soundex; function checks is whether the input is a non-empty string of letters.  What's the best way to do this?</para>  
      184 <para> &soundex; 函数的第一件事是检查输入是否是一个空字符串。 怎样做是最好的方法?</para>  
    184 184 </abstract>  
    185   <para>If you answered <quote>regular expressions</quote>, go sit in the corner and contemplate your bad instincts.  Regular expressions are almost never the right answer; they should be avoided whenever possible.  Not only for performance reasons, but simply because they're difficult to debug and maintain.  Also for performance reasons.</para>  
    186   <para>This code fragment from <filename>soundex/stage1/soundex1a.py</filename> checks whether the function argument <varname>source</varname> is a word made entirely of letters, with at least one letter (not the empty string):</para>  
      185 <para>如果你回答 <quote>正则表达式</quote>,坐在角落里反省你糟糕的直觉。正则表达式几乎永远不是最好的答案,而且应该被尽可能避开。 这不仅仅是基于性能考虑,而是因为差错和维护都很困难,当然性能也是个原因。</para>  
      186 <para>这是 <filename>soundex/stage1/soundex1a.py</filename> 检查 <varname>source</varname> 是否全部由字母构成的一段代码,至少是一个字母(而不是空字符串):</para>  
    187 187 <informalexample>  
    188 188 <programlisting>  
     
    194 194 </programlisting>  
    195 195 </informalexample>  
    196   <para>How does <filename>soundex1a.py</filename> perform?  For convenience, the <literal>__main__</literal> section of the script contains this code that calls the &timeit; module, sets up a timing test with three different names, tests each name three times, and displays the minimum time for each:</para>  
      196 <para><filename>soundex1a.py</filename> 表现如何? 为了方便,<literal>__main__</literal> 部分的代码包含了调用 &timeit; 模块,建立一个分别测试三个不同名字三次并显示最短耗时的一个计时测试代码:</para>  
    196 196 <informalexample>  
    197 197 <programlisting>  
     
    206 206 </programlisting>  
    207 207 </informalexample>  
    208   <para>So how does <filename>soundex1a.py</filename> perform with this regular expression?</para>  
      208 <para>那么,应用正则表达式的 <filename>soundex1a.py</filename> 表现如何呢?</para>  
    208 208 <informalexample>  
    209 209 <screen>  
     
    215 215 </screen>  
    216 216 </informalexample>  
    217   <para>As you might expect, the algorithm takes significantly longer when called with longer names.  There will be a few things we can do to narrow that gap (make the function take less relative time for longer input), but the nature of the algorithm dictates that it will never run in constant time.</para>  
    218   <para>The other thing to keep in mind is that we are testing a representative sample of names.  <literal>Woo</literal> is a kind of trivial case, in that it gets shorted down to a single letter and then padded with zeros.  <literal>Pilgrim</literal> is a normal case, of average length and a mixture of significant and ignored letters.  <literal>Flingjingwaller</literal> is extraordinarily long and contains consecutive duplicates.  Other tests might also be helpful, but this hits a good range of different cases.</para>  
    219   <para>So what about that regular expression?  Well, it's inefficient.  Since the expression is testing for ranges of characters (<literal>A-Z</literal> in uppercase, and <literal>a-z</literal> in lowercase), we can use a shorthand regular expression syntax.  Here is <filename>soundex/stage1/soundex1b.py</filename>:</para>  
      217 <para>正如你预料,名字越长,算法耗时就越长。 有几个工作可以另我们减小这个差距(使函数对于长输入花费较短的相对时间)但是算法的本质决定它不可能每次运行时间都相同。</para>  
      218 <para>另一点应铭记于心的是,我们测试的是有代表性的名字样本。 <literal>Woo</literal> 是个被缩短到单字符并补零的小样本; <literal>Pilgrim</literal> 是个夹带着特别字符和忽略字符的平均长度的正常样本; <literal>Flingjingwaller</literal> 是一个包含连续重复字符并且特别长的样本。 其它的测试可能同样有帮助,但它们已经是很好的不同样本范围了。</para>  
      219 <para>那么那个正则表达式如何呢? 嗯,缺乏效率。因为这个表达式测试不止一个范围的字符 (<literal>A-Z</literal> 的大写范围和 <literal>a-z</literal> 的小写字母范围),我们可以使用一个正则表达式的缩写语法。这便是 <filename>soundex/stage1/soundex1b.py</filename>:</para>  
    220 220 <informalexample>  
    221 221 <programlisting>  
     
    224 224 </programlisting>  
    225 225 </informalexample>  
    226   <para>&timeit; says <filename>soundex1b.py</filename> is slightly faster than <filename>soundex1a.py</filename>, but nothing to get terribly excited about:</para>  
      226 <para>&timeit; 显示 <filename>soundex1b.py</filename> 比 <filename>soundex1a.py</filename> 稍微快一些,但是没什么令人激动的变化:</para>  
    226 226 <informalexample>  
    227 227 <screen>  
     
    233 233 </screen>  
    234 234 </informalexample>  
    235   <para>We saw in <xref linkend="roman.refactoring"/> that regular expressions can be compiled and reused for faster results.  Since this regular expression never changes across function calls, we can compile it once and use the compiled version.  Here is <filename>soundex/stage1/soundex1c.py</filename>:</para>  
      235 <para>在 <xref linkend="roman.refactoring"/> 中我们看到正则表达式可以被编译并在重用时以更快速度获得结果。因为这个正则表达式在函数中每次被调用时都不变化,我们可以编译它一次并使用被编译的版本。这便是 <filename>soundex/stage1/soundex1c.py</filename>:</para>  
    235 235 <informalexample>  
    236 236 <programlisting>  
     
    242 242 </programlisting>  
    243 243 </informalexample>  
    244   <para>Using a compiled regular expression in <filename>soundex1c.py</filename> is significantly faster:</para>  
      244 <para><filename>soundex1c.py</filename> 中使用被编译的正则表达式产生了显著的提速:</para>  
    244 244 <informalexample>  
    245 245 <screen>  
     
    251 251 </screen>  
    252 252 </informalexample>  
    253   <para>But is this the wrong path?  The logic here is simple: the input <varname>source</varname> needs to be non-empty, and it needs to be composed entirely of letters.  Wouldn't it be faster to write a loop checking each character, and do away with regular expressions altogether?</para>  
    254   <para>Here is <filename>soundex/stage1/soundex1d.py</filename>:</para>  
      253 <para>但是这样的优化是正路吗? 这里的逻辑很简单:输入 <varname>source</varname> 应该是非空,并且需要完全由字母构成。 如果编写一个循环查看每个字符并且与正则表达式一同工作是否会更快些?</para>  
      254 <para>这便是 <filename>soundex/stage1/soundex1d.py</filename>:</para>  
    255 255 <informalexample>  
    256 256 <programlisting>  
     
    262 262 </programlisting>  
    263 263 </informalexample>  
    264   <para>It turns out that this technique in <filename>soundex1d.py</filename> is <emphasis>not</emphasis> faster than using a compiled regular expression (although it is faster than using a non-compiled regular expression):</para>  
      264 <para>这个技术在 <filename>soundex1d.py</filename> 中恰好 <emphasis>不及</emphasis> 编译后的正则表达式快(尽管比使用未编译的正则表达式快):</para>  
    264 264 <informalexample>  
    265 265 <screen>  
     
    271 271 </screen>  
    272 272 </informalexample>  
    273   <para>Why isn't <filename>soundex1d.py</filename> faster?  The answer lies in the interpreted nature of &python;.  The regular expression engine is written in C, and compiled to run natively on your computer.  On the other hand, this loop is written in &python;, and runs through the &python; interpreter.  Even though the loop is relatively simple, it's not simple enough to make up for the overhead of being interpreted.  Regular expressions are never the right answer... except when they are.</para>  
    274   <para>It turns out that &python; offers an obscure string method.  You can be excused for not knowing about it, since it's never been mentioned in this book.  The method is called <methodname>isalpha()</methodname>, and it checks whether a string contains only letters.</para>  
    275   <para>This is <filename>soundex/stage1/soundex1e.py</filename>:</para>  
      273 <para>为什么 <filename>soundex1d.py</filename> 没能更快? 答案来自 &python; 的编译本质。 正则表达式引擎以 C 语言编写, 被编译后则能本能地在你的计算机上运行。另一方面,循环是以 &python; 编写,要通过 &python; 解释器。尽管循环相对简单,但没能简单到补偿花在代码解释上的时间。正则表达式永远不是正确答案...... 但例外还是存在的。</para>  
      274 <para>恰巧 &python; 提供了一个晦涩的字符串方法。 你有理由不了解它,因为本书未曾提到它。 这个方法便是 <methodname>isalpha()</methodname>, 它检查一个字符串是否只包含字母。</para>  
      275 <para>这便是 <filename>soundex/stage1/soundex1e.py</filename>:</para>  
    276 276 <informalexample>  
    277 277 <programlisting>  
     
    280 280 </programlisting>  
    281 281 </informalexample>  
    282   <para>How much did we gain by using this specific method in <filename>soundex1e.py</filename>?  Quite a bit.</para>  
      282 <para>在 <filename>soundex1e.py</filename> 中应用这个特殊方法我们能得到多少好处?  很多。</para>  
    282 282 <informalexample>  
    283 283 <screen>  
     
    290 290 </informalexample>  
    291 291 <example>  
    292   <title>Best Result So Far: <filename>soundex/stage1/soundex1e.py</filename></title>  
      292 <title>目前为止最好的结果: <filename>soundex/stage1/soundex1e.py</filename></title>  
    292 292 <programlisting>  
    293 293 import string, re  
     
    351 351 <section id="soundex.stage2">  
    352 352 <?dbhtml filename="performance_tuning/dictionary_lookups.html"?>  
    353   <title>Optimizing Dictionary Lookups</title>  
      353 <title>优化字典查找</title>  
    353 353 <abstract>  
    354 354 <title/>  
    355   <para>The second step of the Soundex algorithm is to convert characters to digits in a specific pattern.  What's the best way to do this?</para>  
      355 <para> Soundex 算法的第二步是依照特定规则将字符转换为数字。 做到这点最好的方法是什么?</para>  
    355 355 </abstract>  
    356   <para>The most obvious solution is to define a dictionary with individual characters as keys and their corresponding digits as values, and do dictionary lookups on each character.  This is what we have in <filename>soundex/stage1/soundex1c.py</filename> (the current best result so far):</para>  
      356 <para>最明显的解决方案是定义一个以单字符为键并以所对应数字为值的字典,以字典查找每个字符。这便是 <filename>soundex/stage1/soundex1c.py</filename> 中使用的方法(目前最好的结果):</para>  
    356 356 <informalexample>  
    357 357 <programlisting>  
     
    395 395 </programlisting>  
    396 396 </informalexample>  
    397   <para>You timed <filename>soundex1c.py</filename> already; this is how it performs:</para>  
      397 <para>你已经为 <filename>soundex1c.py</filename> 计时,这便是其表现:</para>  
    397 397 <informalexample>  
    398 398 <screen>  
     
    404 404 </screen>  
    405 405 </informalexample>  
    406   <para>This code is straightforward, but is it the best solution?  Calling <methodname>upper()</methodname> on each individual character seems inefficient; it would probably be better to call <methodname>upper()</methodname> once on the entire string.</para>  
    407   <para>Then there's the matter of incrementally building the <varname>digits</varname> string.  Incrementally building strings like this is horribly inefficient; internally, the &python; interpreter needs to create a new string each time through the loop, then discard the old one.</para>  
    408   <para>&python; is good at lists, though.  It can treat a string as a list of characters automatically.  And lists are easy to combine into strings again, using the string method <methodname>join()</methodname>.</para>  
    409   <para>Here is <filename>soundex/stage2/soundex2a.py</filename>, which converts letters to digits by using &map; and &lambdafunction;:</para>  
      406 <para>这段代码很直接,但它是最佳解决方案吗?为每个字符分别调用 <methodname>upper()</methodname> 看起来不是很有效率,为整个字符串调用 <methodname>upper()</methodname> 一次可能会好些。</para>  
      407 <para>然后是一砖一瓦的建立 <varname>digits</varname> 字符串。 一砖一瓦的建造好像非常欠缺效率。在 &python; 内部,解释器需要在循环的每一轮创建一个新的字符串,然后丢弃旧的。</para>  
      408 <para>但是,&python; 擅长于列表。 可以自动地将字符串作为列表来对待。而且使用 <methodname>join()</methodname> 方法可以很容易地将列表合并成字符串。</para>  
      409 <para>这便是 <filename>soundex/stage2/soundex2a.py</filename>,通过 &map; 和 &lambdafunction; 把所有字母转换为数字:</para>  
    410 410 <informalexample>  
    411 411 <programlisting>  
     
    416 416 </programlisting>  
    417 417 </informalexample>  
    418   <para>Surprisingly, <filename>soundex2a.py</filename> is not faster:</para>  
      418 <para>太震惊了, <filename>soundex2a.py</filename> 并不快:</para>  
    418 418 <informalexample>  
    419 419 <screen>  
     
    428 428 <para>Even better, double the size of the <varname>charToSoundex</varname> dictionary to include lowercase letters.</para>  
    429 429 -->  
    430   <para>The overhead of the anonymous &lambdafunction; function kills any performance you gain by dealing with the string as a list of characters.</para>  
    431   <para><filename>soundex/stage2/soundex2b.py</filename> uses a list comprehension instead of &map; and &lambdafunction;:</para>  
      430 <para>匿名 &lambdafunction; 函数的使用耗费掉了从以字符列表替代字符串争取来的时间。</para>  
      431 <para><filename>soundex/stage2/soundex2b.py</filename> 使用了一个列表遍历来替代 &map; 和 &lambdafunction;:</para>  
    432 432 <informalexample>  
    433 433 <programlisting>  
     
    436 436 </programlisting>  
    437 437 </informalexample>  
    438   <para>Using a list comprehension in <filename>soundex2b.py</filename> is faster than using &map; and &lambdafunction; in <filename>soundex2a.py</filename>, but still not faster than the original code (incrementally building a string in <filename>soundex1c.py</filename>):</para>  
      438 <para>在 <filename>soundex2b.py</filename> 中使用列表遍历比 <filename>soundex2a.py</filename> 中使用 &map; 和 &lambdafunction; 快,但还没有最初的代码快(<filename>soundex1c.py</filename> 中一砖一瓦的构建字符串):</para>  
    438 438 <informalexample>  
    439 439 <screen>  
     
    445 445 </screen>  
    446 446 </informalexample>  
    447   <para>It's time for a radically different approach.  Dictionary lookups are a general purpose tool.  Dictionary keys can be any length string (or many other data types), but in this case we are only dealing with single-character keys <emphasis>and</emphasis> single-character values.  It turns out that &python; has a specialized function for handling exactly this situation: the <function>string.maketrans</function> function.</para>  
    448   <para>This is <filename>soundex/stage2/soundex2c.py</filename>:</para>  
      447 <para>是时候从本质不同的方法来思考了。 字典查找是一个普通目的实现工具。 字典的键可以是任意长度的字符串(或者很多其他数据类型)但这里我们只和单字符键 <emphasis>和</emphasis> 单字符值打交道。 恰巧 &python; 有处理这种情况的特别函数:<function>string.maketrans</function> 函数。</para>  
      448 <para>这便是 <filename>soundex/stage2/soundex2c.py</filename>:</para>  
    449 449 <informalexample>  
    450 450 <programlisting>  
     
    456 456 </programlisting>  
    457 457 </informalexample>  
    458   <para>What the heck is going on here?  <function>string.maketrans</function> creates a translation matrix between two strings: the first argument and the second argument.  In this case, the first argument is the string <literal>ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz</literal>, and the second argument is the string <literal>9123912992245591262391929291239129922455912623919292</literal>.  See the pattern?  It's the same conversion pattern we were setting up longhand with a dictionary.  A maps to 9, B maps to 1, C maps to 2, and so forth.  But it's not a dictionary; it's a specialized data structure that you can access using the string method <methodname>translate</methodname>, which translates each character into the corresponding digit, according to the matrix defined by <function>string.maketrans</function>.</para>  
    459   <para>&timeit; shows that <filename>soundex2c.py</filename> is significantly faster than defining a dictionary and looping through the input and building the output incrementally:</para>  
      458 <para>这儿在干什么? <function>string.maketrans</function> 创建一个两个字符串间的翻译矩阵:第一参数和第二参数。 就此而言,第一个参数是字符串 <literal>ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz</literal>, 第二个参数是字符串 <literal>9123912992245591262391929291239129922455912623919292</literal>。 看到其模式了? 恰好与我们用冗长的字典构建的模式相同。 A 映射到 9, B 映射到 1, C 映射到 2 等等。 但它不是一个字典。而是一个你可以通过字符串方法 <methodname>translate</methodname> 使用的特别数据结构。它根据 <function>string.maketrans</function> 定义的矩阵将每个字符翻译为对应的数字。</para>  
      459 <para>&timeit; 显示 <filename>soundex2c.py</filename> 比定义字典并对输入进行循环一砖一瓦地构建输出快很多:</para>  
    460 460 <informalexample>  
    461 461 <screen>  
     
    466 466 </screen>  
    467 467 </informalexample>  
    468   <para>You're not going to get much better than that.  &python; has a specialized function that does exactly what you want to do; use it and move on.</para>  
      468 <para>你不可能做得更多了。 &python; 有一个特殊函数,通过使用它做到了一个和你的工作差不多的事情。就用它并继续吧!</para>  
    468 468 <example>  
    469   <title>Best Result So Far: <filename>soundex/stage2/soundex2c.py</filename></title>  
      469 <title>目前的最佳结果:<filename>soundex/stage2/soundex2c.py</filename></title>  
    469 469 <programlisting>  
    470 470 import string, re  
     
    502 502 <section id="soundex.stage3">  
    503 503 <?dbhtml filename="performance_tuning/list_operations.html"?>  
    504   <title>Optimizing List Operations</title>  
      504 <title>优化列表操作</title>  
    504 504 <abstract>  
    505 505 <title/>  
    506   <para>The third step in the Soundex algorithm is eliminating consecutive duplicate digits.  What's the best way to do this?</para>  
      506 <para>Soundex 算法的第三步是去除连续重复字符。 怎样做是最佳方法?</para>  
    506 506 </abstract>  
    507   <para>Here's the code we have so far, in <filename>soundex/stage2/soundex2c.py</filename>:</para>  
      507 <para>这里是我们目前在 <filename>soundex/stage2/soundex2c.py</filename> 中的代码:</para>  
    507 507 <informalexample>  
    508 508 <programlisting>  
     
    516 516 </programlisting>  
    517 517 </informalexample>  
    518   <para>Here are the performance results for <filename>soundex2c.py</filename>:</para>  
      518 <para>这里是 <filename>soundex2c.py</filename> 的性能表现:</para>  
    518 518 <informalexample>  
    519 519 <screen>  
     
    525 525 </screen>  
    526 526 </informalexample>  
    527   <para>The first thing to consider is whether it's efficient to check <varname>digits[-1]</varname> each time through the loop.  Are list indexes expensive?  Would we be better off maintaining the last digit in a separate variable, and checking that instead?</para>  
    528   <para>To answer this question, here is <filename>soundex/stage3/soundex3a.py</filename>:</para>  
      527 <para>第一件事是考虑,考察在循环的每一轮都检查 <varname>digits[-1]</varname> 是否有效率。列表索引代价大吗? 如果把上一个数字存在另外的变量中以便检查是否会获益?</para>  
      528 <para>这里的 <filename>soundex/stage3/soundex3a.py</filename> 将回答这个问题:</para>  
    529 529 <informalexample>  
    530 530 <programlisting>  
     
    537 537 </programlisting>  
    538 538 </informalexample>  
    539   <para><filename>soundex3a.py</filename> does not run any faster than <filename>soundex2c.py</filename>, and may even be slightly slower (although it's not enough of a difference to say for sure):</para>  
      539 <para><filename>soundex3a.py</filename> 并不比 <filename>soundex2c.py</filename> 运行的快,而且甚至更慢些(差异还没有大到可以确信这一点):</para>  
    539 539 <informalexample>  
    540 540 <screen>  
     
    546 546 </screen>  
    547 547 </informalexample>  
    548   <para>Why isn't <filename>soundex3a.py</filename> faster?  It turns out that list indexes in &python; are extremely efficient.  Repeatedly accessing <varname>digits2[-1]</varname> is no problem at all.  On the other hand, manually maintaining the last seen digit in a separate variable means we have <emphasis>two</emphasis> variable assignments for each digit we're storing, which wipes out any small gains we might have gotten from eliminating the list lookup.</para>  
    549   <para>Let's try something radically different.  If it's possible to treat a string as a list of characters, it should be possible to use a list comprehension to iterate through the list.  The problem is, the code needs access to the previous character in the list, and that's not easy to do with a straightforward list comprehension.</para>  
    550   <para>However, it is possible to create a list of index numbers using the built-in <function>range()</function> function, and use those index numbers to progressively search through the list and pull out each character that is different from the previous character.  That will give you a list of characters, and you can use the string method <methodname>join()</methodname> to reconstruct a string from that.</para>  
    551   <para>Here is <filename>soundex/stage3/soundex3b.py</filename>:</para>  
      548 <para>为什么 <filename>soundex3a.py</filename> 不更快呢? 其实 &python; 的索引功能恰恰很有效。 重复使用 <varname>digits2[-1]</varname> 根本没什么问题。 另一方面,手工另外保留上一个数字为另外的变量意味着我们存储的每个数字的 <emphasis>两个</emphasis> 变量赋值,这便抹杀了我们避开索引查找所带来的微小好处。</para>  
      549 <para>让我们从本质上不同的方法来思考。如果可以把字符串当作字符列表来对待,那么使用列表遍历遍寻列表便成为可能。问题是代码需要使用列表中的上一个字符,而且使用列表遍历做到这一点并不容易。</para>  
      550 <para>但是,使用内建的 <function>range()</function> 函数创建一个索引数字构成的列表是可以的。 使用这些索引数字一步步搜索列表并拿出与前面不同的字符。 这样将使你得到一个字符串列表,使用字符串方法 <methodname>join()</methodname> 便可重建字符串。</para>  
      551 <para>这便是 <filename>soundex/stage3/soundex3b.py</filename>:</para>  
    552 552 <informalexample>  
    553 553 <programlisting>  
     
    556 556 </programlisting>  
    557 557 </informalexample>  
    558   <para>Is this faster?  In a word, no.</para>  
      558 <para>这样快了吗? 一个字,否。</para>  
    558 558 <informalexample>  
    559 559 <screen>  
     
    565 565 </screen>  
    566 566 </informalexample>  
    567   <para>It's possible that the techniques so far as have been <quote>string-centric</quote>.  &python; can convert a string into a list of characters with a single command: <function>list('abc')</function> returns <literal>['a', 'b', 'c']</literal>.  Furthermore, lists can be <emphasis>modified in place</emphasis> very quickly.  Instead of incrementally building a new list (or string) out of the source string, why not move elements around within a single list?</para>  
    568   <para>Here is <filename>soundex/stage3/soundex3c.py</filename>, which modifies a list in place to remove consecutive duplicate elements:</para>  
      567 <para>有可能因为目前的这些方法都是 <quote>字符串中心化</quote> 的。 &python; 可以通过一个命令把一个字符串转化为一个字符列表: <function>list('abc')</function> 返回 <literal>['a', 'b', 'c']</literal>。 更进一步,列表可以被很快地 <emphasis>就地</emphasis> 改变。 与其一砖一瓦地建造一个新的列表(或者字符串),为什么不在一个列表中移动元素?</para>  
      568 <para>这便是 <filename>soundex/stage3/soundex3c.py</filename>, 就地修改列表去除连续重复元素:</para>  
    569 569 <informalexample>  
    570 570 <programlisting>  
     
    579 579 </programlisting>  
    580 580 </informalexample>  
    581   <para>Is this faster than <filename>soundex3a.py</filename> or <filename>soundex3b.py</filename>?  No, in fact it's the slowest method yet:</para>  
      581 <para>这比 <filename>soundex3a.py</filename> 或 <filename>soundex3b.py</filename> 快吗? 不,实际上这是目前最慢的一种方法:</para>  
    581 581 <informalexample>  
    582 582 <screen>  
     
    588 588 </screen>  
    589 589 </informalexample>  
    590   <para>We haven't made any progress here at all, except to try and rule out several <quote>clever</quote> techniques.  The fastest code we've seen so far was the original, most straightforward method (<filename>soundex2c.py</filename>).  Sometimes it doesn't pay to be clever.</para>  
      590 <para>我们在这儿除了试用了几种 <quote>clever</quote> 的技术,根本没有什么进步。 到目前为止最快的方法就是最直接的原始方法(<filename>soundex2c.py</filename>)。 有时候聪明未必有回报。</para>  
    590 590 <example>  
    591   <title>Best Result So Far: <filename>soundex/stage2/soundex2c.py</filename></title>  
      591 <title>目前的最佳结果: <filename>soundex/stage2/soundex2c.py</filename></title>  
    591 591 <programlisting>  
    592 592 import string, re  
     
    624 624 <section id="soundex.stage4">  
    625 625 <?dbhtml filename="performance_tuning/string_manipulation.html"?>  
    626   <title>Optimizing String Manipulation</title>  
      626 <title>优化字符串操作</title>  
    626 626 <abstract>  
    627 627 <title/>  
    628   <para>The final step of the Soundex algorithm is padding short results with zeros, and truncating long results.  What is the best way to do this?</para>  
      628 <para>Soundex 算法的最后一步是对短结果补零和截短长结果。最佳的做法是什么?</para>  
    628 628 </abstract>  
    629   <para>This is what we have so far, taken from <filename>soundex/stage2/soundex2c.py</filename>:</para>  
      629 <para>这是目前在 <filename>soundex/stage2/soundex2c.py</filename> 中的做法:</para>  
    629 629 <informalexample>  
    630 630 <programlisting>  
     
    638 638 </programlisting>  
    639 639 </informalexample>  
    640   <para>These are the results for <filename>soundex2c.py</filename>:</para>  
      640 <para>这里是 <filename>soundex2c.py</filename> 的表现:</para>  
    640 640 <informalexample>  
    641 641 <screen>  
     
    647 647 </screen>  
    648 648 </informalexample>  
    649   <para>The first thing to consider is replacing that regular expression with a loop.  This code is from <filename>soundex/stage4/soundex4a.py</filename>:</para>  
      649 <para>思考的第一件事是以循环取代正则表达式。这里的代码来自 <filename>soundex/stage4/soundex4a.py</filename>:</para>  
    649 649 <informalexample>  
    650 650 <programlisting>  
     
    656 656 </programlisting>  
    657 657 </informalexample>  
    658   <para>Is <filename>soundex4a.py</filename> faster?  Yes it is:</para>  
      658 <para><filename>soundex4a.py</filename> 快了吗?是的:</para>  
    658 658 <informalexample>  
    659 659 <screen>  
     
    665 665 </screen>  
    666 666 </informalexample>  
    667   <para>But wait a minute.  A loop to remove characters from a string?  We can use a simple string method for that.  Here's <filename>soundex/stage4/soundex4b.py</filename>:</para>  
      667 <para>但是,等一下。 一个从字符串去除字符的循环? 我们可以用一个简单的字符串方法做到。这便是  <filename>soundex/stage4/soundex4b.py</filename>:</para>  
    667 667 <informalexample>  
    668 668 <programlisting>  
     
    671 671 </programlisting>  
    672 672 </informalexample>  
    673   <para>Is <filename>soundex4b.py</filename> faster?  That's an interesting question.  It depends on the input:</para>  
      673 <para><filename>soundex4b.py</filename> 快了吗?这是个有趣的问题,它取决输入值:</para>  
    673 673 <informalexample>  
    674 674 <screen>  
     
    680 680 </screen>  
    681 681 </informalexample>  
    682   <para>The string method in <filename>soundex4b.py</filename> is faster than the loop for most names, but it's actually slightly slower than <filename>soundex4a.py</filename> in the trivial case (of a very short name).  Performance optimizations aren't always uniform; tuning that makes one case faster can sometimes make other cases slower.  In this case, the majority of cases will benefit from the change, so let's leave it at that, but the principle is an important one to remember.</para>  
    683   <para>Last but not least, let's examine the final two steps of the algorithm: padding short results with zeros, and truncating long results to four characters.  The code you see in <filename>soundex4b.py</filename> does just that, but it's horribly inefficient.  Take a look at <filename>soundex/stage4/soundex4c.py</filename> to see why:</para>  
      682 <para><filename>soundex4b.py</filename> 中的字符串方法对于大多数名字比循环快,但是对于短小的情况(很短的名字)却比 <filename>soundex4a.py</filename> 略微慢些。 性能优化并不总是一致的,对于一个情况快些,却可能对另外一些情况慢些。 就此而言,大多数情况将会从改变中获益,所以就改吧,但是别忘了原则。</para>  
      683 <para>最后仍很重要的是,让我们检测算法的最后两步:以零补齐短结果和截短超过四字符的长结果。你在  <filename>soundex4b.py</filename> 中看到的代码就是做这个工作的,但是太没效率了。看一下 <filename>soundex/stage4/soundex4c.py</filename> 找出原因:</para>  
    684 684 <informalexample>  
    685 685 <programlisting>  
     
    688 688 </programlisting>  
    689 689 </informalexample>  
    690   <para>Why do we need a <literal>while</literal> loop to pad out the result?  We know in advance that we're going to truncate the result to four characters, and we know that we already have at least one character (the initial letter, which is passed unchanged from the original <varname>source</varname> variable).  That means we can simply add three zeros to the output, then truncate it.  Don't get stuck in a rut over the exact wording of the problem; looking at the problem slightly differently can lead to a simpler solution.</para>  
    691   <para>How much speed do we gain in <filename>soundex4c.py</filename> by dropping the <literal>while</literal> loop?  It's significant:</para>  
      690 <para>我们为什么需要一个 <literal>while</literal> 循环来补齐结果? 我们早就知道我们需要把结果截成四字符,并且我们知道我们已经有了一个没有改变的字符(起始字符从 <varname>source</varname> 中不作改变的拿过来)。 这意味着我们可以仅仅在输出的结尾添加三个零,然后截短它。 不要害怕重新理解问题,从不太一样的角度看问题可以获得简单的解决方案。</para>  
      691 <para>我们丢弃 <literal>while</literal> 循环后从 <filename>soundex4c.py</filename> 中获得怎样的速度? 太明显了:</para>  
    692 692 <informalexample>  
    693 693 <screen>  
     
    698 698 </screen>  
    699 699 </informalexample>  
    700   <para>Finally, there is still one more thing you can do to these three lines of code to make them faster: you can combine them into one line.  Take a look at <filename>soundex/stage4/soundex4d.py</filename>:</para>  
      700 <para>最后,还有一件事可以另这三行运行的更快:你可以把它们合并为一行。 看一眼 <filename>soundex/stage4/soundex4d.py</filename>:</para>  
    700 700 <informalexample>  
    701 701 <programlisting>  
     
    704 704 </programlisting>  
    705 705 </informalexample>  
    706   <para>Putting all this code on one line in <filename>soundex4d.py</filename> is barely faster than <filename>soundex4c.py</filename>:</para>  
      706 <para>在 <filename>soundex4d.py</filename> 中把所有代码放在一行可以比 <filename>soundex4c.py</filename> 稍微快那么一点:</para>  
    706 706 <informalexample>  
    707 707 <screen>  
    713 713 </screen>  
    714 714 </informalexample>  
    715   <para>It is also significantly less readable, and for not much performance gain.  Is that worth it?  I hope you have good comments.  Performance isn't everything.  Your optimization efforts must always be balanced against threats to your program's readability and maintainability.</para>  
      715 <para>它非常难懂,而且优化也不明显。 这值得吗? 我希望你有很好的见解。 性能并不是一切。 你在优化方面的努力应该与程序的可读性和可维护性相平衡。</para>  
    715 715 </section>  
    716 716  
    717 717 <section id="soundex.summary">  
    718 718 <?dbhtml filename="performance_tuning/summary.html"?>  
    719   <title>Summary</title>  
      719 <title>小结</title>  
    719 719 <abstract>  
    720 720 <title/>  
    721   <para>This chapter has illustrated several important aspects of performance tuning in &python;, and performance tuning in general.</para>  
      721 <para>这一章展示了性能优化的几个重要方面,这里是就 &python; 而言,但它们却普遍使用。</para>  
    721 721 </abstract>  
    722 722 <itemizedlist>  
    723   <listitem><para>If you need to choose between regular expressions and writing a loop, choose regular expressions.  The regular expression engine is compiled in C and runs natively on your computer; your loop is written in &python; and runs through the &python; interpreter.</para></listitem>  
    724   <listitem><para>If you need to choose between regular expressions and string methods, choose string methods.  Both are compiled in C, so choose the simpler one.</para></listitem>  
    725   <listitem><para>General-purpose dictionary lookups are fast, but specialtiy functions such as <function>string.maketrans</function> and string methods such as <methodname>isalpha()</methodname> are faster.  If &python; has a custom-tailored function for you, use it.</para></listitem>  
    726   <listitem><para>Don't be too clever.  Sometimes the most obvious algorithm is also the fastest.</para></listitem>  
    727   <listitem><para>Don't sweat it too much.  Performance isn't everything.</para></listitem>  
      723 <listitem><para>如果你要在正则表达式和编写循环间抉择,选择正则表达式。 正则表达式因其是以 C 语言编译的可以本能地在你的计算机上运行,你的循环却以 &python; 编写需要通过 &python; 解释器运行。</para></listitem>  
      724 <listitem><para>如果你需要在正则表达式和字符串方法间抉择,选择字符串方法。 它们都是以 C 编译的,所以选取简单的。</para></listitem>  
      725 <listitem><para>字典查找的通常应用很快,但是 <function>string.maketrans</function> 之类的特殊函数和 <methodname>isalpha()</methodname> 之类的字符串方法更快。如果 &python; 有定制方法给你用,就使它吧!</para></listitem>  
      726 <listitem><para>别太聪明了。 有时一些明显的算法是最快的。</para></listitem>  
      727 <listitem><para>不要太迷恋性能优化,性能并不是一切。</para></listitem>  
    728 728 </itemizedlist>  
    729   <para>I can't emphasize that last point strongly enough.  Over the course of this chapter, you made this function three times faster and saved 20 seconds over 1 million function calls.  Great.  Now think: over the course of those million function calls, how many seconds will your surrounding application wait for a database connection?  Or wait for disk I/O?  Or wait for user input?  Don't spend too much time over-optimizing one algorithm, or you'll ignore obvious improvements somewhere else.  Develop an instinct for the sort of code that &python; runs well, correct obvious blunders if you find them, and leave the rest alone.</para>  
      729 <para>最后一点太重要了,这章中你令这个程序三倍的提速并且另百万次的调用节省 20 秒。 太棒了! 现在思考一下:在那百万次的函数调用中,有多少秒花在周边应用程序等待数据连结上?花在磁盘输入/输出上?花在等待用户输入上? 不要在过度优化算法上花时间,从而忽略了其它地方可以做的明显改进。 开发你编写运行良好的 &python; 代码的直觉,如果发现明显的失误则修正它们,并不对其它部分过分操作。</para>  
    729 729 </section>  
    730 730  
  • zh-translations/branches/diveintopython-zh-5.4/zh-cn/xml/plural.xml

    r201 r858  
    2 2 <chapter id="plural">  
    3 3 <?dbhtml filename="dynamic_functions/index.html"?>  
    4   <title>Dynamic functions</title>  
    5   <titleabbrev id="plural.numberonly">Chapter 17</titleabbrev>  
      4 <title>动态函数</title>  
      5 <titleabbrev id="plural.numberonly">第 17 章</titleabbrev>  
    6 6 <section id="plural.divein">  
    7   <title>Diving in</title>  
      7 <title>概览</title>  
    7 7 <abstract>  
    8 8 <title/>  
    9   <para>I want to talk about plural nouns.  Also, functions that return other functions, advanced regular expressions, and generators.  Generators are new in &python; 2.3.  But first, let's talk about how to make plural nouns.</para>  
      9 <para>我想谈谈复数名词。 还有,返回一些函数的一些函数,一些高级的正则表达式和一些生成器(Generator)。生成器是 &python; 2.3 新引入的。 但首先还是让我们先来谈谈书和创造复数名词。</para>  
    9 9 </abstract>  
    10   <para>If you haven't read <xref linkend="re"/>, now would be a good time.  This chapter assumes you understand the basics of regular expressions, and quickly descends into more advanced uses.</para>  
    11   <para>English is a schizophrenic language that borrows from a lot of other languages, and the rules for making singular nouns into plural nouns are varied and complex.  There are rules, and then there are exceptions to those rules, and then there are exceptions to the exceptions.</para>  
    12   <para>If you grew up in an English-speaking country or learned English in a formal school setting, you're probably familiar with the basic rules:</para>  
      10 <para>如果你还没有看 <xref linkend="re"/>,现在是个绝佳的机会。 这章中假定你已理解了正则表达式的基础内容并迅速深入更高级的应用。</para>  
      11 <para>英语是一个吸收很多外来语而另人疯掉的语言,把单数名词变成复数的规则则是复杂而又多变的。 有规则,有例外,[todo]更有例的例外。</para>  
      12 <para>如果你在英语国家长大或是在正规学校学习了英语,你可能对下面的基本规则很熟悉:</para>  
    13 13 <orderedlist>  
    14   <listitem><para>If a word ends in S, X, or Z, add ES.  <quote>Bass</quote> becomes <quote>basses</quote>, <quote>fax</quote> becomes <quote>faxes</quote>, and <quote>waltz</quote> becomes <quote>waltzes</quote>.</para></listitem>  
    15   <listitem><para>If a word ends in a noisy H, add ES; if it ends in a silent H, just add S.  What's a noisy H?  One that gets combined with other letters to make a sound that you can hear.  So <quote>coach</quote> becomes <quote>coaches</quote> and <quote>rash</quote> becomes <quote>rashes</quote>, because you can hear the CH and SH sounds when you say them.  But <quote>cheetah</quote> becomes <quote>cheetahs</quote>, because the H is silent.</para></listitem>  
    16   <listitem><para>If a word ends in Y that sounds like I, change the Y to IES; if the Y is combined with a vowel to sound like something else, just add S.  So <quote>vacancy</quote> becomes <quote>vacancies</quote>, but <quote>day</quote> becomes <quote>days</quote>.</para></listitem>  
    17   <listitem><para>If all else fails, just add S and hope for the best.</para></listitem>  
      14 <listitem><para>如果一个词以 S, X, Z,或者 ES结尾,  <quote>Bass</quote> 变成 <quote>basses</quote>, <quote>fax</quote> 变成 <quote>faxes</quote>,还有 <quote>waltz</quote> 变成 <quote>waltzes</quote>。</para></listitem>  
      15 <listitem><para>如果一个词以发音的 H 加 ES 结尾,或以不发音的 H 加 S 结尾。 什么是发音的 H? 和其他字母混合在一起发出一个你可以听到的声音。 那么, <quote>coach</quote> 变成 <quote>coaches</quote> , <quote>rash</quote> 变成 <quote>rashes</quote>, 因为在读出来时,你可以听到 CH 和 SH 的声音。 但是, <quote>cheetah</quote> 变成 <quote>cheetahs</quote>,因为 H 不发音。</para></listitem>  
      16 <listitem><para>如果一个词以发 I 音的 Y 结尾,把 Y 变成 IES;如果 Y 与元音搭配在一起发出其他声音则只添加 S。因此, <quote>vacancy</quote> 变成 <quote>vacancies</quote>,但 <quote>day</quote> 变成 <quote>days</quote>.</para></listitem>  
      17 <listitem><para>如果一切规则都不适用,就只添加 S 并祈祷不会错。</para></listitem>  
    18 18 </orderedlist>  
    19   <para>(I know, there are a lot of exceptions.  <quote>Man</quote> becomes <quote>men</quote> and <quote>woman</quote> becomes <quote>women</quote>, but <quote>human</quote> becomes <quote>humans</quote>.  <quote>Mouse</quote> becomes <quote>mice</quote> and <quote>louse</quote> becomes <quote>lice</quote>, but <quote>house</quote> becomes <quote>houses</quote>.  <quote>Knife</quote> becomes <quote>knives</quote> and <quote>wife</quote> becomes <quote>wives</quote>, but <quote>lowlife</quote> becomes <quote>lowlifes</quote>.  And don't even get me started on words that are their own plural, like <quote>sheep</quote>, <quote>deer</quote>, and <quote>haiku</quote>.)</para>  
    20   <para>Other languages are, of course, completely different.</para>  
    21   <para>Let's design a module that pluralizes nouns.  Start with just English nouns, and just these four rules, but keep in mind that you'll inevitably need to add more rules, and you may eventually need to add more languages.</para>  
      19 <para>(我知道有很多例外情况, 比如: <quote>Man</quote> 变成 <quote>men</quote>, <quote>woman</quote> 变成 <quote>women</quote>,但是, <quote>human</quote> 却变成 <quote>humans</quote>。 <quote>Mouse</quote> 变成 <quote>mice</quote>, <quote>louse</quote> 变成 <quote>lice</quote>, 但是, <quote>house</quote> 却变成 <quote>houses</quote>。 <quote>Knife</quote> 变成 <quote>knives</quote>, <quote>wife</quote> 变成 <quote>wives</quote>,但是 <quote>lowlife</quote> 却变成 <quote>lowlifes</quote>。 [todo]更不要说那些复数就是本身的词,比如 <quote>sheep</quote>, <quote>deer</quote>, and <quote>haiku</quote>。)</para>  
      20 <para>其他的语言当然完全不同。</para>  
      21 <para>让我们来设计一个复数化名词的模块吧! 从英语名词开始,仅考虑上面的四种规则,但是记得你将来需要不断添加规则,更可能最后添加进更多的语言。</para>  
    22 22 </section>  
    23 23 <section id="plural.stage1">  
    24 24 <?dbhtml filename="dynamic_functions/stage1.html"?>  
    25   <title>&plural_filename;, stage 1</title>  
      25 <title>&plural_filename;, 第 1 阶段</title>  
    25 25 <abstract>  
    26 26 <title/>  
    27   <para>So you're looking at words, which at least in English are strings of characters.  And you have rules that say you need to find different combinations of characters, and then do different things to them.  This sounds like a job for regular expressions.</para>  
      27 <para>你所针对的单词,至少在英语中是字符串和字符。 另外你需要规则来找出不同的字符(字母)组合,并对他们进行不同的操作。这听起来像是正则表达式的工作。</para>  
    27 27 </abstract>  
    28 28 <example>  
     
    47 47 <calloutlist>  
    48 48 <callout arearefs="plural.stage1.1.1">  
    49   <para>OK, this is a regular expression, but it uses a syntax you didn't see in <xref linkend="re"/>.  The square brackets mean <quote>match exactly one of these characters</quote>.  So <literal>[sxz]</literal> means <quote><literal>s</literal>, or <literal>x</literal>, or <literal>z</literal></quote>, but only one of them.  The <literal>$</literal> should be familiar; it matches the end of string.  So you're checking to see if <varname>noun</varname> ends with <literal>s</literal>, <literal>x</literal>, or <literal>z</literal>.</para>  
      49 <para>好啦,这是一个正则表达式,但是它使用了你在 <xref linkend="re"/> 中未曾见过的语法。 方括号的意思是 <quote>[todo]匹配完全匹配这些字符中的一个</quote>。 也就是说,<literal>[sxz]</literal> 意味着 <quote><literal>s</literal>,或者 <literal>x</literal>,再或者 <literal>z</literal></quote>, 但只是其中的一个。 <literal>$</literal> 应该不陌生,它意味着匹配字符串的结尾。 也就是说,检查 <varname>noun</varname> 是否以 <literal>s</literal>,<literal>x</literal>,或者 <literal>z</literal> 结尾。</para>  
    49 49 </callout>  
    50 50 <callout arearefs="plural.stage1.1.2">  
    51   <para>This <function>re.sub</function> function performs regular expression-based string substitutions.  Let's look at it in more detail.</para>  
      51 <para><function>re.sub</function> 函数起着以正则表达式为基础的替换工作。 让我们更具体地看看它。</para>  
    51 51 </callout>  
    52 52 </calloutlist>  
    53 53 </example>  
    54 54 <example>  
    55   <title>Introducing <function>re.sub</function></title>  
      55 <title>介绍 <function>re.sub</function></title>  
    55 55 <screen>  
    56 56 &prompt;<userinput>import re</userinput>  
     
    69 69 <calloutlist>  
    70 70 <callout arearefs="plural.stage1.2.1">  
    71   <para>Does the string <literal>Mark</literal> contain <literal>a</literal>, <literal>b</literal>, or <literal>c</literal>?  Yes, it contains <literal>a</literal>.</para>  
      71 <para><literal>Mark</literal> 包含 <literal>a</literal>, <literal>b</literal>, 或者  <literal>c</literal>吗? 是的,含有 <literal>a</literal>。</para>  
    71 71 </callout>  
    72 72 <callout arearefs="plural.stage1.2.2">  
    73   <para>OK, now find <literal>a</literal>, <literal>b</literal>, or <literal>c</literal>, and replace it with <literal>o</literal>.  <literal>Mark</literal> becomes <literal>Mork</literal>.</para>  
      73 <para>好的,现在找出 <literal>a</literal>, <literal>b</literal>,或者  <literal>c</literal>并以 <literal>o</literal> 取代之。 <literal>Mark</literal> 就变成 <literal>Mork</literal> 了。</para>  
    73 73 </callout>  
    74 74 <callout arearefs="plural.stage1.2.3">  
    75   <para>The same function turns <literal>rock</literal> into <literal>rook</literal>.</para>  
      75 <para>同一方法可以将 <literal>rock</literal> 变成 <literal>rook</literal>。</para>  
    75 75 </callout>  
    76 76 <callout arearefs="plural.stage1.2.4">  
    77   <para>You might think this would turn <literal>caps</literal> into <literal>oaps</literal>, but it doesn't.  <literal>re.sub</literal> replaces <emphasis>all</emphasis> of the matches, not just the first one.  So this regular expression turns <literal>caps</literal> into <literal>oops</literal>, because both the <literal>c</literal> and the <literal>a</literal> get turned into <literal>o</literal>.</para>  
      77 <para>你可能认为它可以将 <literal>caps</literal> 变成 <literal>oaps</literal>,但不是这样。 <literal>re.sub</literal> 替换 <emphasis>所有</emphasis> 的匹配项,并不只是第一个匹配项。 因此正则表达式将会把 <literal>caps</literal> 变成 <literal>oops</literal>。因为, <literal>c</literal> 和 <literal>a</literal> 都被转换为 <literal>o</literal>了。</para>  
    77 77 </callout>  
    78 78 </calloutlist>  
    79 79 </example>  
    80 80 <example>  
    81   <title>Back to <filename>plural1.py</filename></title>  
      81 <title>回到 <filename>plural1.py</filename></title>  
    81 81 <programlisting>  
    82 82 &importre;  
     
    99 99 <calloutlist>  
    100 100 <callout arearefs="plural.stage1.3.1">  
    101   <para>Back to the <function>plural</function> function.  What are you doing?  You're replacing the end of string with <literal>es</literal>.  In other words, adding <literal>es</literal> to the string.  You could accomplish the same thing with string concatenation, for example <literal>noun + 'es'</literal>, but I'm using regular expressions for everything, for consistency, for reasons that will become clear later in the chapter.</para>  
      101 <para>回到 <function>plural</function> 函数。 你在做什么? 你在以 <literal>es</literal> 取代字符串的结尾。 换句话说,追加 <literal>es</literal> 到字符串。 你可以通过字符串拼合做到相同的事。例如: <literal>noun + 'es'</literal>, 但是我使用正则表达式做这一切, 既是为了保持一致,也是为了本章稍后你会明白的其它原因。</para>  
    101 101 </callout>  
    102 102 <callout arearefs="plural.stage1.3.2">  
    103   <para>Look closely, this is another new variation.  The <literal>^</literal> as the first character inside the square brackets means something special: negation.  <literal>[^abc]</literal> means <quote>any single character <emphasis>except</emphasis> <literal>a</literal>, <literal>b</literal>, or <literal>c</literal></quote>.  So <literal>[^aeioudgkprt]</literal> means any character except <literal>a</literal>, <literal>e</literal>, <literal>i</literal>, <literal>o</literal>, <literal>u</literal>, <literal>d</literal>, <literal>g</literal>, <literal>k</literal>, <literal>p</literal>, <literal>r</literal>, or <literal>t</literal>.  Then that character needs to be followed by <literal>h</literal>, followed by end of string.  You're looking for words that end in H where the H can be heard.</para>  
      103 <para>仔细看看,这是另一个新的内容。 <literal>^</literal> 是方括号里面的第一个字符,这有特别的含义:否定。  <literal>[^abc]</literal> 意味着 <quote> 除 <literal>a</literal>、 <literal>b</literal>、 和 <literal>c</literal> <emphasis>以外的</emphasis> 任意单字符</quote>。 所以, <literal>[^aeioudgkprt]</literal> 意味着除 <literal>a</literal>、 <literal>e</literal>、 <literal>i</literal>、 <literal>o</literal>、 <literal>u</literal>、 <literal>d</literal>、 <literal>g</literal>、 <literal>k</literal>、 <literal>p</literal>、 <literal>r</literal> 和 <literal>t</literal> 以外的任意字符。 这个字符之后应该跟着一个 <literal>h</literal>,然后是字符串的结尾。你在寻找的事以发音的 H 结尾的单词。</para>  
    103 103 </callout>  
    104 104 <callout arearefs="plural.stage1.3.3">  
    105   <para>Same pattern here: match words that end in Y, where the character before the Y is <emphasis>not</emphasis> <literal>a</literal>, <literal>e</literal>, <literal>i</literal>, <literal>o</literal>, or <literal>u</literal>.  You're looking for words that end in Y that sounds like I.</para>  
      105 <para>这是一个相似的表达:匹配 Y 前面<emphasis>不是</emphasis> <literal>a</literal>、 <literal>e</literal>、 <literal>i</literal>、 <literal>o</literal> 和 <literal>u</literal>,并以这个 Y 结尾的单词。你在查找的是以发 I 音的 Y 结尾的单词。</para>  
    105 105 </callout>  
    106 106 </calloutlist>  
    107 107 </example>  
    108 108 <example>  
    109   <title>More on negation regular expressions</title>  
      109 <title>正则表达式中否定的更多应用</title>  
    109 109 <screen>  
    110 110 &prompt;<userinput>import re</userinput>  
     
    124 124 <calloutlist>  
    125 125 <callout arearefs="plural.stage1.4.1">  
    126   <para><literal>vacancy</literal> matches this regular expression, because it ends in <literal>cy</literal>, and <literal>c</literal> is not <literal>a</literal>, <literal>e</literal>, <literal>i</literal>, <literal>o</literal>, or <literal>u</literal>.</para>  
      126 <para><literal>vacancy</literal> 匹配这个正则表达式,因为它以 <literal>cy</literal> 结尾,并且 <literal>c</literal> 不在 <literal>a</literal>、 <literal>e</literal>、 <literal>i</literal>、 <literal>o</literal> 和 <literal>u</literal> 之列。</para>  
    126 126 </callout>  
    127 127 <callout arearefs="plural.stage1.4.2">  
    128   <para><literal>boy</literal> does not match, because it ends in <literal>oy</literal>, and you specifically said that the character before the <literal>y</literal> could not be <literal>o</literal>.  <literal>day</literal> does not match, because it ends in <literal>ay</literal>.</para>  
      128 <para><literal>boy</literal> 不能匹配,因为它以 <literal>oy</literal> 结尾,并且你特别指出 <literal>y</literal> 之前的字符不可以是 <literal>o</literal>。 <literal>day</literal> 不能匹配是因为以 <literal>ay</literal> 结尾。</para>  
    128 128 </callout>  
    129 129 <callout arearefs="plural.stage1.4.3">  
    130   <para><literal>pita</literal> does not match, because it does not end in <literal>y</literal>.</para>  
      130 <para><literal>pita</literal> 不匹配是因为不以 <literal>y</literal> 结尾。</para>  
    130 130 </callout>  
    131 131 </calloutlist>  
    132 132 </example>  
    133 133 <example>  
    134   <title>More on <function>re.sub</function></title>  
      134 <title>更多的 <function>re.sub</function></title>  
    134 134 <screen>  
    135 135 &prompt;<userinput>re.sub('y$', 'ies', 'vacancy')</userinput>              <co id="plural.stage1.5.1"/>  
     
    146 146 <calloutlist>  
    147 147 <callout arearefs="plural.stage1.5.1">  
    148   <para>This regular expression turns <literal>vacancy</literal> into <literal>vacancies</literal> and <literal>agency</literal> into <literal>agencies</literal>, which is what you wanted.  Note that it would also turn <literal>boy</literal> into <literal>boies</literal>, but that will never happen in the function because you did that <function>re.search</function> first to find out whether you should do this <function>re.sub</function>.</para>  
      148 <para>正则表达式把 <literal>vacancy</literal> 变为 <literal>vacancies</literal>, 把 <literal>agency</literal> 变为 <literal>agencies</literal> 正是你想要的。 注意,将 <literal>boy</literal> 变成 <literal>boies</literal> 是可行的,但是永远不会发生,因为 <function>re.search</function> 首先确定是否应该应用 <function>re.sub</function>。</para>  
    148 148 </callout>  
    149 149 <callout arearefs="plural.stage1.5.2">  
    150   <para>Just in passing, I want to point out that it is possible to combine these two regular expressions (one to find out if the rule applies, and another to actually apply it) into a single regular expression.  Here's what that would look like.  Most of it should look familiar: you're using a remembered group, which you learned in <xref linkend="re.phone"/>, to remember the character before the <literal>y</literal>.  Then in the substitution string, you use a new syntax, <literal>\1</literal>, which means <quote>hey, that first group you remembered?  put it here</quote>.  In this case, you remember the <literal>c</literal> before the <literal>y</literal>, and then when you do the substitution, you substitute <literal>c</literal> in place of <literal>c</literal>, and <literal>ies</literal> in place of <literal>y</literal>.  (If you have more than one remembered group, you can use <literal>\2</literal> and <literal>\3</literal> and so on.)</para>  
      150 <para>顺便提一下,可以将两个正则表达式(一个确定规则适用与否,一个应用规则)合并在一起成为一个正则表达式。 [todo]这便是合并的样子。 它的大部分已经很熟悉:你应用的是在 <xref linkend="re.phone"/> 学过的记忆组(remembered group)记住 <literal>y</literal> 之前的字符。然后在替换字符串,你使用一个新的语法 <literal>\1</literal>,这意味着: <quote>嘿!记得前面的第一个组吗? 把它放这儿</quote>。 就此而言,记住了 <literal>y</literal> 之前的 <literal>c</literal> ,然后你做替换工作,你将 <literal>c</literal> 替换到 <literal>c</literal> 的位置,并将 <literal>ies</literal> 替换到 <literal>y</literal> 的位置。 (如果你有不只一个组则可以使用 <literal>\2</literal> 或者 <literal>\3</literal> 等等。)</para>  
    150 150 </callout>  
    151 151 </calloutlist>  
    152 152 </example>  
    153   <para>Regular expression substitutions are extremely powerful, and the <literal>\1</literal> syntax makes them even more powerful.  But combining the entire operation into one regular expression is also much harder to read, and it doesn't directly map to the way you first described the pluralizing rules.  You originally laid out rules like <quote>if the word ends in S, X, or Z, then add ES</quote>.  And if you look at this function, you have two lines of code that say <quote>if the word ends in S, X, or Z, then add ES</quote>.  It doesn't get much more direct than that.</para>  
      153 <para>正则表达式替换非常强大,并且 <literal>\1</literal> 语法使之更加强大。 但是将整个操作放在一个正则表达式中仍然晦涩难懂,也不能与前面描述的复数规则直接呼应。 你原来列出的规则,比如 <quote>如果单词以 S, X 或者 Z 结尾,结尾追加 ES</quote>。 如果你在函数中看到两行代码描述 <quote>如果单词以 S, X 或者 Z 结尾,结尾追加 ES</quote>,更加直观些。</para>  
    153 153 </section>  
    154 154  
    155 155 <section id="plural.stage2">  
    156 156 <?dbhtml filename="dynamic_functions/stage2.html"?>  
    157   <title>&plural_filename;, stage 2</title>  
      157 <title>&plural_filename; 第 2 阶段</title>  
    157 157 <abstract>  
    158 158 <title/>  
    159   <para>Now you're going to add a level of abstraction.  You started by defining a list of rules: if this, then do that, otherwise go to the next rule.  Let's temporarily complicate part of the program so you can simplify another part.</para>  
      159 <para>现在你将增加一个抽象过程。 你从定义一个规则列表开始:如果这样,[todo]就做那个,否则判断下一规则。 让我们暂时将程序一部分复杂化以便使另一部分简单化。</para>  
    159 159 </abstract>  
    160 160 <example>  
     
    201 201 <calloutlist>  
    202 202 <callout arearefs="plural.stage2.1.1">  
    203   <para>This version looks more complicated (it's certainly longer), but it does exactly the same thing: try to match four different rules, in order, and apply the appropriate regular expression when a match is found.  The difference is that each individual match and apply rule is defined in its own function, and the functions are then listed in this <varname>rules</varname> variable, which is a tuple of tuples.</para>  
      203 <para>这个版本看起来更加复杂 (至少是长了),但做的工作没有变化:试图顺序匹配四种不同规则,并在匹配时应用恰当的正则表达式。 不同之处在于,每个独立的匹配和应用规则都在自己的[todo]方程中定义,并且这些方程列于 <varname>rules</varname> 变量这个元组的元组之中。</para>  
    203 203 </callout>  
    204 204 <callout arearefs="plural.stage2.1.2">  
    205   <para>Using a &for; loop, you can pull out the match and apply rules two at a time (one match, one apply) from the <varname>rules</varname> tuple.  On the first iteration of the &for; loop, <varname>matchesRule</varname> will get <function>match_sxz</function>, and <varname>applyRule</varname> will get <function>apply_sxz</function>.  On the second iteration (assuming you get that far), <varname>matchesRule</varname> will be assigned <function>match_h</function>, and <varname>applyRule</varname> will be assigned <function>apply_h</function>.</para>  
      205 <para>使用一个 &for; 循环,你可以根据 <varname>rules</varname> 元组一次性进行匹配和应用规则两项工作(一个匹配和一个应用)。 &for; 循环第一轮中,<varname>matchesRule</varname> 将使用 <function>match_sxz</function>, <varname>applyRule</varname> 将使用 <function>apply_sxz</function>; 在第二轮中(假设真走到了这么远), <varname>matchesRule</varname> 将被赋予 <function>match_h</function>, <varname>applyRule</varname> 将被赋予 <function>apply_h</function>。</para>  
    205 205 </callout>  
    206 206 <callout arearefs="plural.stage2.1.3">  
    207   <para>Remember that <link linkend="odbchelper.objects">everything in &python; is an object</link>, including functions.  <varname>rules</varname> contains actual functions; not names of functions, but actual functions.  When they get assigned in the &for; loop, then <varname>matchesRule</varname> and <varname>applyRule</varname> are actual functions that you can call.  So on the first iteration of the &for; loop, this is equivalent to calling <function>matches_sxz(noun)</function>.</para>  
      207 <para>记住 <link linkend="odbchelper.objects">&python; 中的一切都是对象</link>,包括函数。<varname>rules</varname> 包含函数:不是指函数名,而是指函数本身。 当他们被赋予给 &for; 循环中, <varname>matchesRule</varname> 和 <varname>applyRule</varname> 就成了你可以调用的真正函数。 因此,在 &for; 循环第一轮中,就相当于调用 <function>matches_sxz(noun)</function>。</para>  
    207 207 </callout>  
    208 208 <callout arearefs="plural.stage2.1.4">  
    209   <para>On the first iteration of the &for; loop, this is equivalent to calling <function>apply_sxz(noun)</function>, and so forth.</para>  
      209 <para>在 &for; 循环第一轮中,就相当于调用 <function>apply_sxz(noun)</function>,等等。</para>  
    209 209 </callout>  
    210 210 </calloutlist>  
    211 211 </example>  
    212   <para>If this additional level of abstraction is confusing, try unrolling the function to see the equivalence.  This &for; loop is equivalent to the following:</para>  
      212 <para>这个抽象过程有些令人迷惑, 试着剖析函数看看实际的等价内容。 这个 &for; 循环相当于:</para>  
    212 212 <example>  
    213   <title>Unrolling the <function>plural</function> function</title>  
      213 <title>剖析 <function>plural</function> 函数</title>  
    213 213 <programlisting>  
    214 214 def plural(noun):  
    215 215     if match_sxz(noun):  
    216 216         return apply_sxz(noun)  
    217       if match_h(noun):  
      217     if match_h(noun):/  
    217 217         return apply_h(noun)  
    218 218     if match_y(noun):  
     
    229 229 </programlisting>  
    230 230 </example>  
    231   <para>The benefit here is that that <function>plural</function> function is now simplified.  It takes a list of rules, defined elsewhere, and iterates through them in a generic fashion.  Get a match rule; does it match?  Then call the apply rule.  The rules could be defined anywhere, in any way.  The <function>plural</function> function doesn't care.</para>  
    232   <para>Now, was adding this level of abstraction worth it?  Well, not yet.  Let's consider what it would take to add a new rule to the function.  Well, in the previous example, it would require adding an &if; statement to the <function>plural</function> function.  In this example, it would require adding two functions, <function>match_foo</function> and <function>apply_foo</function>, and then updating the <varname>rules</varname> list to specify where in the order the new match and apply functions should be called relative to the other rules.</para>  
    233   <para>This is really just a stepping stone to the next section.  Let's move on.</para>  
      231 <para>这里的好处在于 <function>plural</function> 函数现在被简化了。 它以普通的方法反复使用其它地方定义的规则。 获得一个匹配规则,匹配吗? 调用并应用规则。 规则可以在任意地方以任意方法定义, <function>plural</function> 函数对此并不[todo](关心?)在乎。</para>  
      232 <para>现在,添加这个抽象过程值得吗? 嗯... 还不值。 让我们看看如何[todo]向方程添加一个新的规则。 啊哈,在先前的范例中,需要向 <function>plural</function> 函数添加一个 &if; 语句;在这个例子中,需要增加两个函数: <function>match_foo</function> 和 <function>apply_foo</function>,然后更新 <varname>rules</varname> 列表指定在什么相对位置调用这个新匹配和新规则应用。</para>  
      233 <para>这其实不过是步入下一节的一个基石。让我们继续。</para>  
    234 234 </section>  
    235 235  
    236 236 <section id="plural.stage3">  
    237 237 <?dbhtml filename="dynamic_functions/stage3.html"?>  
    238   <title>&plural_filename;, stage 3</title>  
      238 <title>&plural_filename; 第 3 阶段</title>  
    238 238 <abstract>  
    239 239 <title/>  
    240   <para>Defining separate named functions for each match and apply rule isn't really necessary.  You never call them directly; you define them in the <varname>rules</varname> list and call them through there.  Let's streamline the rules definition by anonymizing those functions.</para>  
      240 <para>将每个匹配和规则应用分别制作成函数没有必要。 你从来不会直接调用它们:你把它们定义于 <varname>rules</varname> 列表之中并从那里调用它们。 让我们隐去他们的函数名而抓住规则定义主线。</para>  
    240 240 </abstract>  
    241 241 <example>  
     
    255 255 <calloutlist>  
    256 256 <callout arearefs="plural.stage3.1.1">  
    257   <para>This is the same set of rules as you defined in stage 2.  The only difference is that instead of defining named functions like <function>match_sxz</function> and <function>apply_sxz</function>, you have <quote>inlined</quote> those function definitions directly into the <varname>rules</varname> list itself, using <link linkend="apihelper.lambda">lambda functions</link>.</para>  
      257 <para>这与第 2 阶段定义的规则是一样的。惟一的区别是不再定义 <function>match_sxz</function> 和 <function>apply_sxz</function> 之类的函数,而是以 <link linkend="apihelper.lambda">lambda 函数</link> 法将这些函数的内容直接 <quote>嵌入</quote> <varname>rules</varname> 列表本身。 .</para>  
    257 257 </callout>  
    258 258 <callout arearefs="plural.stage3.1.2">  
    259   <para>Note that the <function>plural</function> function hasn't changed at all.  It iterates through a set of rule functions, checks the first rule, and if it returns a true value, calls the second rule and returns the value.  Same as above, word for word.  The only difference is that the rule functions were defined inline, anonymously, using lambda functions.  But the <function>plural</function> function doesn't care how they were defined; it just gets a list of rules and blindly works through them.</para>  
      259 <para>注意 <function>plural</function> 函数完全没有变化,还是反复于一系列的规则函数,检查第一个匹配规则,如果返回真调用第二个应用规则并返回值。 和前面一样,给定单词返回单词。 唯一的区别是规则函数被内嵌定义,化名作 lambda 函数。 但是 <function>plural</function> 函数并不在乎它们是如何定义的,只是拿到规则列表,闭着眼睛干活。</para>  
    259 259 </callout>  
    260 260 </calloutlist>  
    261 261 </example>  
    262   <para>Now to add a new rule, all you need to do is define the functions directly in the <varname>rules</varname> list itself: one match rule, and one apply rule.  But defining the rule functions inline like this makes it very clear that you have some unnecessary duplication here.  You have four pairs of functions, and they all follow the same pattern.  The match function is a single call to <function>re.search</function>, and the apply function is a single call to <function>re.sub</function>.  Let's factor out these similarities.</para>  
      262 <para>现在添加一条新的规则,所有你要做的就是直接在 <varname>rules</varname> 列表之中定义函数:一个匹配规则,一个应用规则。 这样内嵌的规则方程定义方法使得没必要的重复很容易发现。 你有四对函数,它们采用相同的模式。 匹配函数就是调用 <function>re.search</function>,应用函数就是调用 <function>re.sub</function>。 让我们提炼出这些共同点。</para>  
    262 262 </section>  
    263 263  
    264 264 <section id="plural.stage4">  
    265 265 <?dbhtml filename="dynamic_functions/stage4.html"?>  
    266   <title>&plural_filename;, stage 4</title>  
      266 <title>&plural_filename; 第 4 阶段</title>  
    266 266 <abstract>  
    267 267 <title/>  
    268   <para>Let's factor out the duplication in the code so that defining new rules can be easier.</para>  
      268 <para>让我们精炼出代码中的重复之处,以便更容易地定义新规则。</para>  
    268 268 </abstract>  
    269 269 <example id="plural.stage4.example.1">  
     
    284 284 <calloutlist>  
    285 285 <callout arearefs="plural.stage4.1.1">  
    286   <para><function>buildMatchAndApplyFunctions</function> is a function that builds other functions dynamically.  It takes <varname>pattern</varname>, <varname>search</varname> and <varname>replace</varname> (actually it takes a tuple, but more on that in a minute), and you can build the match function using the &lambdafunction; syntax to be a function that takes one parameter (<varname>word</varname>) and calls <function>re.search</function> with the <varname>pattern</varname> that was passed to the <function>buildMatchAndApplyFunctions</function> function, and the <varname>word</varname> that was passed to the match function you're building.  Whoa.</para>  
      286 <para><function>buildMatchAndApplyFunctions</function> 是一个动态生成其它函数的函数。 它将 <varname>pattern</varname>, <varname>search</varname> 和 <varname>replace</varname> ([todo]实际上是把一个元组,但很快就会变得不止于此),通过使用 &lambdafunction; 语法构建一个接受单参数(<varname>word</varname>)并以传递给 <function>buildMatchAndApplyFunctions</function> 的 <varname>pattern</varname> 和 传递给新函数的 <varname>word</varname> 调用 <function>re.search</function> 的匹配函数! 哇塞!</para>  
    286 286 </callout>  
    287 287 <callout arearefs="plural.stage4.1.2">  
    288   <para>Building the apply function works the same way.  The apply function is a function that takes one parameter, and calls <function>re.sub</function> with the <varname>search</varname> and <varname>replace</varname> parameters that were passed to the <function>buildMatchAndApplyFunctions</function> function, and the <varname>word</varname> that was passed to the apply function you're building.  This technique of using the values of outside parameters within a dynamic function is called <emphasis>closures</emphasis>.  You're essentially defining constants within the apply function you're building: it takes one parameter (<varname>word</varname>), but it then acts on that plus two other values (<varname>search</varname> and <varname>replace</varname>) which were set when you defined the apply function.</para>  
      288 <para>构建应用规则函数的方法相同。 应用规则函数是一个接受单参数并以传递给 <function>buildMatchAndApplyFunctions</function> 的 <varname>search</varname> 和 <varname>replace</varname> 以及传递给这个应用规则函数的 <varname>word</varname> 调用 <function>re.sub</function> 的函数。在一个动态函数中应用外部参数值的技术被称作 <emphasis>[todo]闭宝(closures)</emphasis>。你实际上是在应用规则函数中定义常数:接受一个参数(<varname>word</varname>),但随后它与定义应用规则函数时设置的另外两个值 (<varname>search</varname> 和 <varname>replace</varname>)一起工作。</para>  
    288 288 </callout>  
    289 289 <callout arearefs="plural.stage4.1.3">  
    290   <para>Finally, the <function>buildMatchAndApplyFunctions</function> function returns a tuple of two values: the two functions you just created.  The constants you defined within those functions (<varname>pattern</varname> within <varname>matchFunction</varname>, and <varname>search</varname> and <varname>replace</varname> within <varname>applyFunction</varname>) stay with those functions, even after you return from <function>buildMatchAndApplyFunctions</function>.  That's insanely cool.</para>  
      290 <para>最终, <function>buildMatchAndApplyFunctions</function> 函数返回一个包含两个值的元组:你刚刚创建的两个函数。你在这些函数中定义的(<varname>matchFunction</varname> 中的 <varname>pattern</varname> 以及 <varname>applyFunction</varname> 中的 <varname>search</varname> 和 <varname>replace</varname>) 保留在这些函数中,由 <function>buildMatchAndApplyFunctions</function> 一同返回。 这简直太酷了。</para>  
    290 290 </callout>  
    291 291 </calloutlist>  
    292 292 </example>  
    293   <para>If this is incredibly confusing (and it should be, this is weird stuff), it may become clearer when you see how to use it.</para>  
      293 <para>如果这太费解(它应该是这样,这是个怪异的东西),可能需要通过了解它的使用来搞明白。</para>  
    293 293 <example>  
    294   <title><filename>plural4.py</filename> continued</title>  
      294 <title><filename>plural4.py</filename> 继续</title>  
    294 294 <programlisting>  
    295 295 &p4_patterns; <co id="plural.stage4.2.1"/>  
     
    303 303 <calloutlist>  
    304 304 <callout arearefs="plural.stage4.2.1">  
    305   <para>Our pluralization rules are now defined as a series of strings (not functions).  The first string is the regular expression that you would use in <function>re.search</function> to see if this rule matches; the second and third are the search and replace expressions you would use in <function>re.sub</function> to actually apply the rule to turn a noun into its plural.</para>  
      305 <para>我们的复数化规则现在被定义成一组字符串(不是函数)。 第一个字符串是你在调用 <function>re.search</function> 时使用的正则表达式;第二个和第三个字符串是是你在通过调用 <function>re.sub</function> 来应用规则将名词变为复数时使用的搜索和替换表达式。 </para>  
    305 305 </callout>  
    306 306 <callout arearefs="plural.stage4.2.2">  
    307   <para>This line is magic.  It takes the list of strings in <varname>patterns</varname> and turns them into a list of functions.  How?  By mapping the strings to the <function>buildMatchAndApplyFunctions</function> function, which just happens to take three strings as parameters and return a tuple of two functions.  This means that <varname>rules</varname> ends up being exactly the same as the previous example: a list of tuples, where each tuple is a pair of functions, where the first function is the match function that calls <function>re.search</function>, and the second function is the apply function that calls <function>re.sub</function>.</para>  
      307 <para>这很神奇。 把传进去的 <varname>patterns</varname> 字符串转换为传回来的函数。 如何做到的呢? 将这些字符串映射给 <function>buildMatchAndApplyFunctions</function> 函数之后,三个字符串参数转换成了两个函数组成的元组。 这意味着 <varname>rules</varname> 被转换成了前面范例中相同的内容:由许多调用 <function>re.search</function> 函数的匹配函数和调用 <function>re.sub</function> 的规则应用函数构成的函数组组成的一个元组。</para>  
    307 307 </callout>  
    308 308 </calloutlist>  
    309 309 </example>  
    310   <para>I swear I am not making this up: <varname>rules</varname> ends up with exactly the same list of functions as the previous example.  Unroll the <varname>rules</varname> definition, and you'll get this:</para>  
      310 <para>我发誓这不是我信口雌黄:<varname>rules</varname> 被转换成了前面范例中相同的内容。 剖析 <varname>rules</varname> 的定义,你看到的是:</para>  
    310 310 <example>  
    311   <title>Unrolling the rules definition</title>  
      311 <title>剖析规则定义</title>  
    311 311 <programlisting>  
    312 312 &p3_rules;  
     
    318 318 </example>  
    319 319 <example id="plural.finishing.up">  
    320   <title><filename>plural4.py</filename>, finishing up</title>  
      320 <title><filename>plural4.py</filename> 的完成</title>  
    320 320 <programlisting>  
    321 321 &p4_defplural;  
     
    327 327 <calloutlist>  
    328 328 <callout arearefs="plural.stage4.3.1">  
    329   <para>Since the <varname>rules</varname> list is the same as the previous example, it should come as no surprise that the <function>plural</function> function hasn't changed.  Remember, it's completely generic; it takes a list of rule functions and calls them in order.  It doesn't care how the rules are defined.  In <link linkend="plural.stage2">stage 2</link>, they were defined as seperate named functions.  In <link linkend="plural.stage3">stage 3</link>, they were defined as anonymous &lambdafunction; functions.  Now in stage 4, they are built dynamically by mapping the <function>buildMatchAndApplyFunctions</function> function onto a list of raw strings.  Doesn't matter; the <function>plural</function> function still works the same way.</para>  
      329 <para>由于 <varname>rules</varname> 列表和前面的范例是相同的, <function>plural</function> 函数没有变化也就不另人诧异了。 记住,这没什么特别的,按照顺序调用一系列函数。 不必在意规则是如何定义的。 在 <link linkend="plural.stage2">第 2 阶段</link>,它们被定义为各具名称的函数。 在 <link linkend="plural.stage3">第 3 阶段</link>, 他们被定义为匿名的 &lambdafunction; 函数。 现在第 4 阶段,它们通过 <function>buildMatchAndApplyFunctions</function> 映射原始的字符串列表被动态创建。 无所谓, <function>plural</function> 函数的工作方法没有变。</para>  
    329 329 </callout>  
    330 330 </calloutlist>  
    331 331 </example>  
    332   <para>Just in case that wasn't mind-blowing enough, I must confess that there was a subtlety in the definition of <function>buildMatchAndApplyFunctions</function> that I skipped over.  Let's go back and take another look.</para>  
      332 <para>还不够兴奋吧!我必须承认,在定义 <function>buildMatchAndApplyFunctions</function> 时我跳过了一个微妙之处。 让我们回过头再看一下。</para>  
    332 332 <example>  
    333   <title>Another look at <function>buildMatchAndApplyFunctions</function></title>  
      333 <title>回头看 <function>buildMatchAndApplyFunctions</function></title>  
    333 333 <programlisting>  
    334 334 &p4_defbuild; <co id="plural.stage4.4.1"/>  
     
    339 339 <calloutlist>  
    340 340 <callout arearefs="plural.stage4.4.1">  
    341   <para>Notice the double parentheses?  This function doesn't actually take three parameters; it actually takes one parameter, a tuple of three elements.  But the tuple is expanded when the function is called, and the three elements of the tuple are each assigned to different variables: <varname>pattern</varname>, <varname>search</varname>, and <varname>replace</varname>.  Confused yet?  Let's see it in action.</para>  
      341 <para>注意到双括号了吗? 这个函数并不是真的接受三个参数,实际上只接受一个参数:一个三元素元组。但是在函数被调用时元组概念被展开了,元组的三个元素也被赋予了不同的变量: <varname>pattern</varname>, <varname>search</varname> 和 <varname>replace</varname>。 乱吗?让我们在使用中理解。</para>  
    341 341 </callout>  
    342 342 </calloutlist>  
    343 343 </example>  
    344 344 <example>  
    345   <title>Expanding tuples when calling functions</title>  
      345 <title>调用函数时展开元组</title>  
    345 345 <screen>  
    346 346 &prompt;<userinput>def foo((a, b, c)):</userinput>  
     
    358 358 <calloutlist>  
    359 359 <callout arearefs="plural.stage4.5.1">  
    360   <para>The proper way to call the function <function>foo</function> is with a tuple of three elements.  When the function is called, the elements are assigned to different local variables within <function>foo</function>.</para>  
      360 <para>调用 <function>foo</function> 的正确方法是使用一个三元素元组。 函数被调用时,元素被分别赋予 <function>foo</function> 中的多个局部变量。</para>  
    360 360 </callout>  
    361 361 </calloutlist>  
    362   <para>Now let's go back and see why this auto-tuple-expansion trick was necessary.  <varname>patterns</varname> was a list of tuples, and each tuple had three elements.  When you called <literal>map(buildMatchAndApplyFunctions, patterns)</literal>, that means that <function>buildMatchAndApplyFunctions</function> is <emphasis>not</emphasis> getting called with three parameters.  Using <function>map</function> to map a single list onto a function always calls the function with a single parameter: each element of the list.  In the case of <varname>patterns</varname>, each element of the list is a tuple, so <function>buildMatchAndApplyFunctions</function> always gets called with the tuple, and you use the auto-tuple-expansion trick in the definition of <function>buildMatchAndApplyFunctions</function> to assign the elements of that tuple to named variables that you can work with.</para>  
      362 <para>现在,让我们回过头看一看这个元组自动展开技巧的必要性。 <varname>patterns</varname> 是一个元组列表,并且每个元组都有三个元组。调用 <literal>map(buildMatchAndApplyFunctions, patterns)</literal>,这并<emphasis>不</emphasis> 意味着是以三个参数调用 <function>buildMatchAndApplyFunctions</function>。 使用 <function>map</function> 映射一个列表到函数时,通常使用单参数:列表中的每个元素。 就 <varname>patterns</varname> 而言, 列表的每个元素都是一个元组,所以 <function>buildMatchAndApplyFunctions</function> 经常是以元组来调用,在 <function>buildMatchAndApplyFunctions</function> 中使用元组自动展开技巧将元素赋值给可以被使用的变量。</para>  
    362 362 </example>  
    363 363 </section>  
     
    367 367 <section id="plural.stage5">  
    368 368 <?dbhtml filename="dynamic_functions/stage5.html"?>  
    369   <title>&plural_filename;, stage 5</title>  
      369 <title>&plural_filename; 第 5 阶段</title>  
    369 369 <abstract>  
    370 370 <title/>  
    371   <para>You've factored out all the duplicate code and added enough abstractions so that the pluralization rules are defined in a list of strings.  The next logical step is to take these strings and put them in a separate file, where they can be maintained separately from the code that uses them.</para>  
      371 <para>你已经精炼了所有重复代码,也尽可能地把复数规则提炼到定义一个字符串列表。 接下来的步骤是把这些字符串提出来放在另外的文件中,从而可以和使用它们的代码分开来维护。</para>  
    371 371 </abstract>  
    372   <para>First, let's create a text file that contains the rules you want.  No fancy data structures, just space- (or tab-)delimited strings in three columns.  You'll call it <filename>rules.en</filename>; <quote>en</quote> stands for English.  These are the rules for pluralizing English nouns.  You could add other rule files for other languages later.</para>  
      372 <para>首先,让我们建立一个包含所有你需要的规则的文本文件。 没有什么特别的结构,不过是以空白(或者制表符)把字符串列成三列。 你把它命名为 <filename>rules.en</filename>, <quote>en</quote> 是英语的意思。 这些是英语名词复数的规则,你以后可以为其它语言添加规则文件。</para>  
    372 372 <example>  
    373 373 <title><filename>rules.en</filename></title>  
     
    382 382 </programlisting>  
    383 383 </example>  
    384   <para>Now let's see how you can use this rules file.</para>  
      384 <para>现在来看看如何使用规则文件。</para>  
    384 384 <example>  
    385 385 <title><filename>plural5.py</filename></title>  
     
    402 402 <calloutlist>  
    403 403 <callout arearefs="plural.stage5.1.1">  
    404   <para>You're still using the closures technique here (building a function dynamically that uses variables defined outside the function), but now you've combined the separate match and apply functions into one.  (The reason for this change will become clear in the next section.)  This will let you accomplish the same thing as having two functions, but you'll need to call it differently, as you'll see in a minute.</para>  
      404 <para>在这里你还将使用闭合技术(动态构建函数时使用函数外部定义的变量),但是现在你把原来分开的匹配函数和规则应用函数合二为一。 (你将在下一节中明了其原因) 你很快会看到,这与分别调用两个函数效果相同。</para>  
    404 404 </callout>  
    405 405 <callout arearefs="plural.stage5.1.2">  
    406   <para>Our <function>plural</function> function now takes an optional second parameter, <varname>language</varname>, which defaults to <literal>en</literal>.</para>  
      406 <para>咱们的 <function>plural</function> 函数现在接受的第二个参数是默认值为 <literal>en</literal> 的可选参数 <varname>language</varname>。</para>  
    406 406 </callout>  
    407 407 <callout arearefs="plural.stage5.1.3">  
    408   <para>You use the <varname>language</varname> parameter to construct a filename, then open the file and read the contents into a list.  If <varname>language</varname> is <literal>en</literal>, then you'll open the <filename>rules.en</filename> file, read the entire thing, break it up by carriage returns, and return a list.  Each line of the file will be one element in the list.</para>  
      408 <para>你使用 <varname>language</varname> 参数命名一个文件,打开这个文件并读取其中的内容到一个列表。如果 <varname>language</varname> 是 <literal>en</literal>,那么你将打开 <filename>rules.en</filename> 文件,读取全部内容,以其中的回车符作为分割构建一个列表。 文件的每一行将成为列表的一个元素。</para>  
    408 408 </callout>  
    409 409 <callout arearefs="plural.stage5.1.4">  
    410   <para>As you saw, each line in the file really has three values, but they're separated by whitespace (tabs or spaces, it makes no difference).  Mapping the <function>string.split</function> function onto this list will create a new list where each element is a tuple of three strings.  So a line like <literal>[sxz]$ $ es</literal> will be broken up into the tuple <literal>('[sxz]$', '$', 'es')</literal>.  This means that <varname>patterns</varname> will end up as a list of tuples, just like you hard-coded it in <link linkend="plural.stage4">stage 4</link>.</para>  
      410 <para>如你所见,文件的每一行都有三个值,但是他们是以空白字符(制表符或者空格符,这没什么区别)分割。 用 <function>string.split</function> 函数映射列表来创建一个每个元素都是三元素元组的新列表。 因此,像 <literal>[sxz]$ $ es</literal> 这样的一行将被打碎并放入 <literal>('[sxz]$', '$', 'es')</literal> 这样的元组。 这意味着 <varname>patterns</varname> 将最终变成元组列表的形式,就像 <link linkend="plural.stage4">第 4 阶段</link> 实打实编写的那样。</para>  
    410 410 </callout>  
    411 411 <callout arearefs="plural.stage5.1.5">  
    412   <para>If <varname>patterns</varname> is a list of tuples, then <varname>rules</varname> will be a list of the functions created dynamically by each call to <function>buildRule</function>.  Calling <function>buildRule(('[sxz]$', '$', 'es'))</function> returns a function that takes a single parameter, <varname>word</varname>.  When this returned function is called, it will execute <literal>re.search('[sxz]$', word) and re.sub('$', 'es', word)</literal>.</para>  
      412 <para>如果 <varname>patterns</varname> 是一个元组列表,那么 <varname>rules</varname> 就可以通过一个个调用 <function>buildRule</function> 动态地声称函数列表。 调用 <function>buildRule(('[sxz]$', '$', 'es'))</function> 返回一个接受单参数 <varname>word</varname> 的函数。 当返回的函数被调用,则将执行 <literal>re.search('[sxz]$', word) 和 re.sub('$', 'es', word)</literal>。</para>  
    412 412 </callout>  
    413 413 <callout arearefs="plural.stage5.1.6">  
    414   <para>Because you're now building a combined match-and-apply function, you need to call it differently.  Just call the function, and if it returns something, then that's the plural; if it returns nothing (&none;), then the rule didn't match and you need to try another rule.</para>  
      414 <para>因为你现在构建的是一个匹配和规则应用和一的函数,你需要分别调用它们。 仅仅是调用函数,如果返回了内容,那么返回的便是复数;如果没有返回(也就是返回了&none;),那么该规则未能匹配,那么应该尝试其他规则。</para>  
    414 414 </callout>  
    415 415 </calloutlist>  
    416 416 </example>  
    417   <para>So the improvement here is that you've completely separated the pluralization rules into an external file.  Not only can the file be maintained separately from the code, but you've set up a naming scheme where the same <function>plural</function> function can use different rule files, based on the <varname>language</varname> parameter.</para>  
      417 <para>这里的进步是你把复数规则完全分离到另外的文件中。不但这个文件可以独立于代码单独维护,而且你建立了一个命名规划使 <function>plural</function> 函数可以根据 <varname>language</varname> 参数使用不同的规则文件。</para>  
    417 417  
    418   <para>The downside here is that you're reading that file every time you call the <function>plural</function> function.  I thought I could get through this entire book without using the phrase <quote>left as an exercise for the reader</quote>, but here you go: building a caching mechanism for the language-specific rule files that auto-refreshes itself if the rule files change between calls <emphasis>is left as an exercise for the reader</emphasis>.  Have fun.</para>  
      418 <para>这里的缺陷是每次调用 <function>plural</function> 函数都需要去读取一次文件。 我想我可以在整本书中都不使用 <quote>留给读者去练习</quote>, 但是这里:为特定的语言规则文件建立一个缓存机制,并在调用期间规则文件改变时自动刷新 <emphasis>留给读者作为练习</emphasis>。 祝你顺利。</para>  
    418 418 </section>  
    419 419  
    420 420 <section id="plural.stage6">  
    421 421 <?dbhtml filename="dynamic_functions/stage6.html"?>  
    422   <title>&plural_filename;, stage 6</title>  
      422 <title>&plural_filename; 第 6 阶段</title>  
    422 422 <abstract>  
    423 423 <title/>  
    424   <para>Now you're ready to talk about generators.</para>  
      424 <para>现在你已准备好探讨生成器(Generator )了.</para>  
    424 424 </abstract>  
    425 425 <example>  
     
    449 449 </programlisting>  
    450 450 </example>  
    451   <para>This uses a technique called generators, which I'm not even going to try to explain until you look at a simpler example first.</para>  
      451 <para>这里使用了被称作生成器的技术,我不打算在你看过一个简单例子之前试图解释它。</para>  
    451 451 <example id="plural.introducing.generators">  
    452   <title>Introducing generators</title>  
      452 <title>介绍生成器</title>  
    452 452 <screen>  
    453 453 &prompt;<userinput>def make_counter(x):</userinput>  
     
    475 475 <calloutlist>  
    476 476 <callout arearefs="plural.stage6.2.1">  
    477   <para>The presence of the &yield; keyword in <function>make_counter</function> means that this is not a normal function.  It is a special kind of function which generates values one at a time.  You can think of it as a resumable function.  Calling it will return a generator that can be used to generate successive values of <varname>x</varname>.</para>  
      477 <para><function>make_counter</function> 中出现关键字 &yield; 意味着这不是一个普通的函数。它是一种每次生成一个值的特殊函数。 你可以把它看成是一个恢复函数。每次调用都返回一个可以成功生成 <varname>x</varname> 值的生成器。 </para>  
    477 477 </callout>  
    478 478 <callout arearefs="plural.stage6.2.2">  
    479   <para>To create an instance of the <function>make_counter</function> generator, just call it like any other function.  Note that this does not actually execute the function code.  You can tell this because the first line of <function>make_counter</function> is a &print; statement, but nothing has been printed yet.</para>  
      479 <para>建立一个 <function>make_counter</function> 生成器的实例,[todo]只需像调用任何其他函数。 注意这并没有真正的执行函数代码。 <function>make_counter</function> 第一行的 &print; 语句是一个语句,但是没有任何内容被打印就可以说明这一点。</para>  
    479 479 </callout>  
    480 480 <callout arearefs="plural.stage6.2.3">  
    481   <para>The <function>make_counter</function> function returns a generator object.</para>  
      481 <para><function>make_counter</function> 函数返回一个生成器对象。</para>  
    481 481 </callout>  
    482 482 <callout arearefs="plural.stage6.2.4">  
    483   <para>The first time you call the <function>next()</function> method on the generator object, it executes the code in <function>make_counter</function> up to the first &yield; statement, and then returns the value that was yielded.  In this case, that will be <literal>2</literal>, because you originally created the generator by calling <function>make_counter(2)</function>.</para>  
      483 <para>你第一次调用生成器对象的 <function>next()</function> 方法,<function>make_counter</function> 中的代码执行到第一个 &yield; 语句,然后返回制造出来的值。 依此而言,这个值是 <literal>2</literal>,因为你是通过  <function>make_counter(2)</function> 创建生成器的。</para>  
    483 483 </callout>  
    484 484 <callout arearefs="plural.stage6.2.5">  
    485   <para>Repeatedly calling <function>next()</function> on the generator object <emphasis>resumes where you left off</emphasis> and continues until you hit the next &yield; statement.  The next line of code waiting to be executed is the &print; statement that prints <literal>incrementing x</literal>, and then after that the <literal>x = x + 1</literal> statement that actually increments it.  Then you loop through the &while; loop again, and the first thing you do is <literal>yield x</literal>, which returns the current value of <varname>x</varname> (now 3).</para>  
      485 <para>不断调用生成器对象的 <function>next()</function> <emphasis>将从你上次离开的位置重新开始</emphasis> 并继续下去直到你又一次遇到 &yield; 语句。 接下来将被执行的是 &print; 语句以打印出 <literal>incrementing x</literal>,然后 <literal>x = x + 1</literal> 语句实际的增加了它。 然后你进入 &while; 循环的又一轮循环,你所做的第一件事是 <literal>yield x</literal>。返回目前的 <varname>x</varname> 值(现在是3)。</para>  
    485 485 </callout>  
    486 486 <callout arearefs="plural.stage6.2.6">  
    487   <para>The second time you call <function>counter.next()</function>, you do all the same things again, but this time <varname>x</varname> is now <literal>4</literal>.  And so forth.  Since <function>make_counter</function> sets up an infinite loop, you could theoretically do this forever, and it would just keep incrementing <varname>x</varname> and spitting out values.  But let's look at more productive uses of generators instead.</para>  
      487 <para>第二次你调用 <function>counter.next()</function> 时,你又做一遍相同的事情,但是这次 <varname>x</varname> 是  
      488  <literal>4</literal>。 如此继续。 因为 <function>make_counter</function> 设置的是一个无限循环,理论上你可以永远这样继续下去,不断地递增并弹出 <varname>x</varname> 值。 现在让我们看看生成器更具意义的应用。</para>  
    488 489 </callout>  
    489 490 </calloutlist>  
    490 491 </example>  
    491 492 <example id="plural.fib.example">  
    492   <title>Using generators instead of recursion</title>  
      493 <title>使用生成器替代递归</title>  
    492 493 <programlisting>  
    493 494 def fibonacci(max):  
     
    505 506 <calloutlist>  
    506 507 <callout arearefs="plural.stage6.3.1">  
    507   <para>The Fibonacci sequence is a sequence of numbers where each number is the sum of the two numbers before it.  It starts with &zero; and &one;, goes up slowly at first, then more and more rapidly.  To start the sequence, you need two variables: <varname>a</varname> starts at &zero;, and <varname>b</varname> starts at &one;.</para>  
      508 <para>斐波纳契数列(Fibonacci sequence)是每个数都是前面两个数值和的一个数列。它从 &zero; 和 &one; 开始,开始增长的很慢,但越来越快。 开始这个数列你需要两个变量: <varname>a</varname> 从 &zero;开始, <varname>b</varname> 从 &one; 开始。</para>  
    507 508 </callout>  
    508 509 <callout arearefs="plural.stage6.3.2">  
    509   <para><varname>a</varname> is the current number in the sequence, so yield it.</para>  
      510 <para><varname>a</varname> 是数列的当前值,弹出它。</para>  
    509 510 </callout>  
    510 511 <callout arearefs="plural.stage6.3.3">  
    511   <para><varname>b</varname> is the next number in the sequence, so assign that to <varname>a</varname>, but also calculate the next value (<literal>a+b</literal>) and assign that to <varname>b</varname> for later use.  Note that this happens in parallel; if <varname>a</varname> is <literal>3</literal> and <varname>b</varname> is <literal>5</literal>, then <literal>a, b = b, a+b</literal> will set <varname>a</varname> to <literal>5</literal> (the previous value of <varname>b</varname>) and <varname>b</varname> to <literal>8</literal> (the sum of the previous values of <varname>a</varname> and <varname>b</varname>).</para>  
      512 <para><varname>b</varname> 是数列的下一个数,把它赋值给 <varname>a</varname>,同时计算出(<literal>a+b</literal>)并赋值给 <varname>b</varname> 放在一边稍后使用。 注意这是并行发生的,如果 <varname>a</varname> 是 <literal>3</literal>, <varname>b</varname> 是 <literal>5</literal>,那么 <literal>a, b = b, a+b</literal> 将会设置 <varname>a</varname> 为 <literal>5</literal> ( <varname>b</varname> 的原值), <varname>b</varname> 为 <literal>8</literal> ( <varname>a</varname> 和 <varname>b</varname> 之和)。</para>  
    511 512 </callout>  
    512 513 </calloutlist>  
    513 514 </example>  
    514   <para>So you have a function that spits out successive Fibonacci numbers.  Sure, you could do that with recursion, but this way is easier to read.  Also, it works well with &for; loops.</para>  
      515 <para>这样你就有了成功生成 Fibonacci 数的函数了。 当然你也可以通过递归做到,但是这里的方法更加容易和理解。并且也与 &for; 工作的很好。</para>  
    514 515 <example>  
    515   <title>Generators in &for; loops</title>  
      516 <title>&for; 循环中的生成器</title>  
    515 516 <screen>  
    516 517 &prompt;<userinput>for n in fibonacci(1000):</userinput> <co id="plural.stage6.4.1"/>  
     
    525 526 <calloutlist>  
    526 527 <callout arearefs="plural.stage6.4.1">  
    527   <para>You can use a generator like <function>fibonacci</function> in a &for; loop directly.  The &for; loop will create the generator object and successively call the <function>next()</function> method to get values to assign to the &for; loop index variable (<varname>n</varname>).</para>  
      528 <para>你可以在 &for; 循环中直接使用 <function>fibonacci</function> 这样的生成器。 &for; 循环将会创建一个生成器对象并成功调用其 <function>next()</function> 方法获得值并赋予 &for; 循环变量(<varname>n</varname>)。</para>  
    527 528 </callout>  
    528 529 <callout arearefs="plural.stage6.4.2">  
    529   <para>Each time through the &for; loop, <varname>n</varname> gets a new value from the &yield; statement in <function>fibonacci</function>, and all you do is print it out.  Once <function>fibonacci</function> runs out of numbers (<varname>a</varname> gets bigger than <varname>max</varname>, which in this case is <literal>1000</literal>), then the &for; loop exits gracefully.</para>  
      530 <para>每轮 &for; 循环 <varname>n</varname> 都从 <function>fibonacci</function> 的 &yield; 语句获得一个新的值。 当 <function>fibonacci</function> 超出数字限定(<varname>a</varname>达到超过<varname>max</varname> 你在这里限定的 <literal>1000</literal>)很自然地退出 &for; 循环。</para>  
    529 530 </callout>  
    530 531 </calloutlist>  
    531 532 </example>  
    532   <para>OK, let's go back to the <function>plural</function> function and see how you're using this.</para>  
      533 <para>好了,让我们回到 <function>plural</function> 函数看看如何可以把它用起来。</para>  
    532 533 <example>  
    533   <title>Generators that generate dynamic functions</title>  
      534 <title>生成器生成动态函数</title>  
    533 534 <programlisting>  
    534 535 &p6_defrules;  
     
    548 549 <calloutlist>  
    549 550 <callout arearefs="plural.stage6.5.1">  
    550   <para><literal>for line in file(...)</literal> is a common idiom for reading lines from a file, one line at a time.  It works because <emphasis><function>file</function> actually returns a generator</emphasis> whose <function>next()</function> method returns the next line of the file.  That is so insanely cool, I wet myself just thinking about it.</para>  
      551 <para><literal>for line in file(...)</literal> 是从文件中一行行读取的通用方法,每次一行。 它的正常工作是因为 <emphasis><function>file</function> 实际上返回一个生成器</emphasis>, 它的 <function>next()</function> 方法返回文件中的下一行。 简直太酷了,光是想想就让我满头大汗。</para>  
    550 551 </callout>  
    551 552 <callout arearefs="plural.stage6.5.2">  
    552   <para>No magic here.  Remember that the lines of the rules file have three values separated by whitespace, so <literal>line.split()</literal> returns a tuple of 3 values, and you assign those values to 3 local variables.</para>  
      553 <para>这没有什么神奇之处。 还记得规则文件的每一行都用空白分开三个值吗?所以 <literal>line.split()</literal> 返回一个三元素元组,你把这些值赋给了 3 个局部变量。</para>  
    552 553 </callout>  
    553 554 <callout arearefs="plural.stage6.5.3">  
    554   <para><emphasis>And then you yield.</emphasis>  What do you yield?  A function, built dynamically with &lambdafunction;, that is actually a closure (it uses the local variables <varname>pattern</varname>, <varname>search</varname>, and <varname>replace</varname> as constants).  In other words, <function>rules</function> is a generator that spits out rule functions.</para>  
      555 <para><emphasis>然后你不断地弹出。</emphasis>  你弹出什么呢? 一个使用 &lambdafunction; 动态生成的函数,而这个函数实际上是一个闭合(把本地变量 <varname>pattern</varname>, <varname>search</varname> 和 <varname>replace</varname> 作为常量)。 换句话说, <function>rules</function> 是一个弹出规则函数的生成器。</para>  
    554 555 </callout>  
    555 556 <callout arearefs="plural.stage6.5.4">  
    556   <para>Since <function>rules</function> is a generator, you can use it directly in a &for; loop.  The first time through the &for; loop, you will call the <function>rules</function> function, which will open the rules file, read the first line out of it, dynamically build a function that matches and applies the first rule defined in the rules file, and yields the dynamically built function.  The second time through the &for; loop, you will pick up where you left off in <function>rules</function> (which was in the middle of the <literal>for line in file(...)</literal> loop), read the second line of the rules file, dynamically build another function that matches and applies the second rule defined in the rules file, and yields it.  And so forth.</para>  
      557 <para>既然 <function>rules</function> 是一个生成器,你就可以在 &for; 循环中直接使用它。 &for; 循环的第一轮你调用 <function>rules</function> 函数,打开规则文件,读取第一行,动态构建一个根据规则文件第一行匹配并应用规则的函数。 &for; 循环的第二轮将会从上一轮 <function>rules</function> 中停下的位置( <literal>for line in file(...)</literal> 循环内部)读取规则文件的第二行,动态构建根据规则文件第二行匹配并应用规则的另一个函数。如此继续下去。</para>  
    556 557 </callout>  
    557 558 </calloutlist>  
    558 559 </example>  
    559   <para>What have you gained over <link linkend="plural.stage5">stage 5</link>?  In stage 5, you read the entire rules file and built a list of all the possible rules before you even tried the first one.  Now with generators, you can do everything lazily: you open the first and read the first rule and create a function to try it, but if that works you don't ever read the rest of the file or create any other functions.</para>  
      560 <para>你在 <link linkend="plural.stage5">第 5 阶段</link> 得到的是什么? 第 5 阶段中,你读取整个规则文件并在使用第一条规则之前构建一个所有规则组成的列表。 现在有了生成器,你可以更舒适地做到这一切:你打开并读取第一条规则,根据它创建函数并使用之,如果它适用则根本不去读取规则文件剩下的内容,也不去建立另外的函数。</para>  
    559 560 <itemizedlist role="furtherreading">  
    560   <title>Further reading</title>  
    561   <listitem><para><ulink url="http://www.python.org/peps/pep-0255.html">PEP 255</ulink> defines generators.</para></listitem>  
    562   <listitem><para>&pythoncookbook; has <ulink url="http://www.google.com/search?q=generators+cookbook+site:aspn.activestate.com">many more examples of generators</ulink>.</para></listitem>  
      561 <title>进一步阅读</title>  
      562 <listitem><para><ulink url="http://www.python.org/peps/pep-0255.html">PEP 255</ulink> 定义生成器。</para></listitem>  
      563 <listitem><para>&pythoncookbook; 有 <ulink url="http://www.google.com/search?q=generators+cookbook+site:aspn.activestate.com">生成器的例子</ulink>.</para></listitem>  
    563 564 </itemizedlist>  
    564 565 </section>  
    571 572 <section id="plural.summary">  
    572 573 <?dbhtml filename="dynamic_functions/summary.html"?>  
    573   <title>Summary</title>  
      574 <title>小结</title>  
    573 574 <abstract>  
    574 575 <title/>  
    575   <para>You talked about several different advanced techniques in this chapter.  Not all of them are appropriate for every situation.</para>  
      576 <para>这一章中我们探讨了几个不同的高级技术。他们并不都是用于任何情况。</para>  
    575 576 </abstract>  
    576   <para>You should now be comfortable with all of these techniques:</para>  
      577 <para>你现在应该能自如应用如下技术:</para>  
    576 577 <itemizedlist>  
    577   <listitem><para>Performing <link linkend="plural.stage1">string substitution with regular expressions</link>.</para></listitem>  
    578   <listitem><para>Treating <link linkend="plural.stage2">functions as objects</link>, storing them in lists, assigning them to variables, and calling them through those variables.</para></listitem>  
    579   <listitem><para>Building <link linkend="plural.stage3">dynamic functions with &lambdafunction;</link>.</para></listitem>  
    580   <listitem><para>Building <link linkend="plural.stage4">closures</link>, dynamic functions that contain surrounding variables as constants.</para></listitem>  
    581   <listitem><para>Building <link linkend="plural.stage6">generators</link>, resumable functions that perform incremental logic and return different values each time you call them.</para></listitem>  
      578 <listitem><para>应用 <link linkend="plural.stage1">正则表达式进行字符串替换</link>。</para></listitem>  
      579 <listitem><para>将 <link linkend="plural.stage2">函数当作对象</link>,把它们存于列表中,把它们赋值给变量,并通过变量来调用它们。</para></listitem>  
      580 <listitem><para>构建 <link linkend="plural.stage3">应用 &lambdafunction; 的动态函数</link>。</para></listitem>  
      581 <listitem><para>构建 <link linkend="plural.stage4">闭合</link>,将外部变量作为常量构建动态函数。</para></listitem>  
      582 <listitem><para>构建 <link linkend="plural.stage6">生成器</link>,进行逻辑递增操作并在每次调用时返回不同值的恢复执行函数。</para></listitem>  
    582 583 </itemizedlist>  
    583   <para>Adding abstractions, building functions dynamically, building closures, and using generators can all make your code simpler, more readable, and more flexible.  But they can also end up making it more difficult to debug later.  It's up to you to find the right balance between simplicity and power.</para>  
      584 <para>抽象化,动态构建函数,构建闭合以及应用生成器能够使你的代码更加简单华、可读化、灵活化。 在简洁和功能实现是需要你从中平衡的。</para>  
    583 584 </section>  
    584 585 </chapter>  
  • zh-translations/branches/diveintopython-zh-5.4/zh-cn/xml/openanything.xml

    r314 r858  
    3 3 <?dbhtml filename="http_web_services/index.html"?>  
    4 4 <title>HTTP Web 服务</title>  
    5   <titleabbrev id="oa.numberonly">Chapter 11</titleabbrev>  
      5 <titleabbrev id="oa.numberonly">第 11 章</titleabbrev>  
    5 5 <section id="oa.divein">  
    6 6 <title>概览</title>  
  • zh-translations/branches/diveintopython-zh-5.4/zh-cn/xml/dialect.xml

    r248 r858  
    3 3 <?dbhtml filename="html_processing/index.html"?>  
    4 4 <title>&html; 处理</title>  
    5   <titleabbrev id="dialect.numberonly">Chapter 8</titleabbrev>  
      5 <titleabbrev id="dialect.numberonly">第 8 章</titleabbrev>  
    5 5 <section id="dialect.divein">  
    6 6 <title>概览</title>  
  • zh-translations/branches/diveintopython-zh-5.4/zh-cn/xml/re.xml

    r250 r858  
    3 3 <?dbhtml filename="regular_expressions/index.html"?>  
    4 4 <title>正则表达式</title>  
    5   <titleabbrev id="re.numberonly">Chapter 7</titleabbrev>  
      5 <titleabbrev id="re.numberonly">第 7 章</titleabbrev>  
    5 5 <abstract>  
    6 6 <title/>  
    13 13 <abstract>  
    14 14 <title/>  
    15   <para>如果你要解决的问题利用字符串函数能够完成,你应该使用他们。他们快速、简单且容易阅读,而对于快速、简单、可读性强的代码等方面有很多内容。但是,如果你发现你用了许多不同的字符串函数和if语句来处理一个特殊情况,或者你组合使用了split、join等函数而导致用一种奇怪的甚至读不下去的方式理解列表,此时,你也许需要转到正则表达式了。</para>  
      15 <para>如果你要解决的问题利用字符串函数能够完成,你应该使用他们。他们快速、简单且容易阅读,而对于快速、简单、可读性强的代码等方面有很多内容。但是,如果你发现你用了许多不同的字符串函数和 if语句来处理一个特殊情况,或者你组合使用了 &split; 、&join; 等函数而导致用一种奇怪的甚至读不下去的方式理解列表,此时,你也许需要转到正则表达式了。</para>  
    15 15 </abstract>  
    16   <para>尽管正则表达式语法较之普通代码相对麻烦一些,但是却可以得到更可读的结果,与用一长串字符串函数的解决方案相比要好很多。在正则表达式内部有多种方法嵌入注释,从而使之具有自文档化(self-documenting)的能力。</para>  
      16 <para>尽管正则表达式语法较之普通代码相对麻烦一些,但是却可以得到更可读的结果,与用一长串字符串函数的解决方案相比要好很多。在正则表达式内部有多种方法嵌入注释,从而使之具有自文档化 (self-documenting) 的能力。</para>  
    16 16 </section>  
    17 17 <section id="re.matching">  
  • zh-translations/branches/diveintopython-zh-5.4/zh-cn/xml/odbchelper.xml

    r224 r858  
    3 3 <?dbhtml filename="getting_to_know_python/index.html"?>  
    4 4 <title>第一个 &python; 程序</title>  
    5   <titleabbrev id="odbchelper.numberonly">Chapter 2</titleabbrev>  
      5 <titleabbrev id="odbchelper.numberonly">第 2 章</titleabbrev>  
    5 5 <abstract>  
    6 6 <title/>  
    310 310 <?dbhtml filename="native_data_types/index.html"?>  
    311 311 <title>内置数据类型</title>  
    312   <titleabbrev id="datatypes.numberonly">Chapter 3</titleabbrev>  
      312 <titleabbrev id="datatypes.numberonly">第 3 章</titleabbrev>  
    312 312 <para>让我们用点儿时间来回顾一下您的第一个 &python; 程序。 但首先, 先说些其他的内容, 因为您需要了解一下 dictionary (字典)、tuple (元组) 和list (列表) (哦, 我的老天!) 。如果您是一个 &perl; hacker, 当然可以撇开 dictionary 和 list, 但是仍然需要注意 tuple。</para>  
    313 313 <section id="odbchelper.dict">  
  • zh-translations/branches/diveintopython-zh-5.4/zh-cn/xml/roman.xml

    r842 r858  
    284 284 <titleabbrev id="roman1.5.numberonly">第 14 章</titleabbrev>  
    285 285 <section id="roman.stage1">  
    286   <title>&roman_filename;, 第 1 </title>  
      286 <title>&roman_filename;, 第 1 阶段</title>  
    286 286 <abstract>  
    287 287 <title/>  
     
    468 468 <section id="roman.stage2">  
    469 469 <?dbhtml filename="unit_testing/stage_2.html"?>  
    470   <title>&roman_filename;, 第 2 </title>  
      470 <title>&roman_filename;, 第 2 阶段</title>  
    470 470 <abstract>  
    471 471 <title/>  
     
    674 674 <section id="roman.stage3">  
    675 675 <?dbhtml filename="unit_testing/stage_3.html"?>  
    676   <title>&roman_filename;, 第 3 </title>  
      676 <title>&roman_filename;, 第 3 阶段</title>  
    676 676 <abstract>  
    677 677 <title/>  
     
    855 855 <section id="roman.stage4">  
    856 856 <?dbhtml filename="unit_testing/stage_4.html"?>  
    857   <title>&roman_filename;, 第 4 </title>  
      857 <title>&roman_filename;, 第 4 阶段</title>  
    857 857 <abstract>  
    858 858 <title/>  
    993 993 <section id="roman.stage5">  
    994 994 <?dbhtml filename="unit_testing/stage_5.html"?>  
    995   <title>&roman_filename;, 第 5 </title>  
      995 <title>&roman_filename;, 第 5 阶段</title>  
    995 995 <abstract>  
    996 996 <title/>