Changeset 948

Show
Ignore:
Timestamp:
Tue Mar 21 02:20:27 2006
Author:
osmond
Message:

update

Files:

Legend:

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

    r929 r948  
    72 72 <calloutlist>  
    73 73 <callout arearefs="fileinfo_divein.1.1">  
    74   <para>这个程序的输入要取决于你硬盘上的文件。为了得到有意义的输出,你应该修改目录路径指向你自已机器上的一个MP3文件目录。</para>  
      74 <para>这个程序的输入要取决于你硬盘上的文件。为了得到有意义的输出,你应该修改目录路径指向你自已机器上的一个 MP3 文件目录。</para>  
    74 74 </callout>  
    75 75 </calloutlist>  
     
    133 133 <abstract>  
    134 134 <title/>  
    135   <para>&python;有两种导入模块的方法。两种都有用,你应该知道什么时候使用哪一种方法。一种方法, &importmodule;,你已经在<xref linkend="odbchelper.objects"/>看过了。另一种方法完成同样的事情,但是它与第一种有着细微但重要的区别。</para>  
      135 <para>&python; 有两种导入模块的方法。两种都有用,你应该知道什么时候使用哪一种方法。一种方法, &importmodule;,你已经在<xref linkend="odbchelper.objects"/>看过了。另一种方法完成同样的事情,但是它与第一种有着细微但重要的区别。</para>  
    135 135 </abstract>  
    136 136 <informalexample>  
     
    142 142 </programlisting>  
    143 143 </informalexample>  
    144   <para>它与你所熟知的<link linkend="odbchelper.import">&importmodule;</link>语法很相似,但是有一个重要的区别:被导入模块 &types; 的属性和方法被直接导入到局部名字空间去了,所以它们可以直接使用,而不需要加上模块名的限定。你可以导入独立的项或使用 &frommoduleimportstar; 来导入所有东西。</para>  
      144 <para>它与你所熟知的 <link linkend="odbchelper.import">&importmodule;</link> 语法很相似,但是有一个重要的区别:被导入模块 &types; 的属性和方法被直接导入到局部名字空间去了,所以它们可以直接使用,而不需要加上模块名的限定。你可以导入独立的项或使用 &frommoduleimportstar; 来导入所有东西。</para>  
    144 144 <note id="compare.fromimport.perl" role="compare" vendor="perl">  
    145 145 <title>&python; &vs; &perl;: &frommoduleimport;</title>  
     
    164 164 <calloutlist>  
    165 165 <callout arearefs="fileinfo.import.1.1">  
    166   <para><filename class="headerfile">types</filename> 模块不包含方法,只���表示每种 &python; 对象类型的属性。注意这个属性必需用模块名<filename class="headerfile">types</filename>进行限定。</para>  
      166 <para><filename class="headerfile">types</filename> 模块不包含方法,只���表示每种 &python; 对象类型的属性。注意这个属性必需用模块名 <filename class="headerfile">types</filename> 进行限定。</para>  
    166 166 </callout>  
    167 167 <callout arearefs="fileinfo.import.1.2">  
    168   <para>&functiontype; 本身没有被定义在当前名字空间中;它只存在于<filename class="headerfile">types</filename> 的上下文环境中。</para>  
      168 <para>&functiontype; 本身没有被定义在当前名字空间中;它只存在于 <filename class="headerfile">types</filename> 的上下文环境中。</para>  
    168 168 </callout>  
    169 169 <callout arearefs="fileinfo.import.1.3">  
    170   <para>这个语法从<filename class="headerfile">types</filename>模块中直接将 &functiontype; 属性导入到局部名字空间中。</para>  
      170 <para>这个语法从 <filename class="headerfile">types</filename> 模块中直接将 &functiontype; 属性导入到局部名字空间中。</para>  
    170 170 </callout>  
    171 171 <callout arearefs="fileinfo.import.1.4">  
    172   <para>现在 &functiontype; 可以直接使用, 与 <filename class="headerfile">types</filename> 无关了。</para>  
      172 <para>现在 &functiontype; 可以直接使用与 <filename class="headerfile">types</filename> 无关了。</para>  
    172 172 </callout>  
    173 173 </calloutlist>  
     
    202 202 <para>&python; 是完全面向对象的:你可以定义自已的类,从自已的或内置的类继承,然后从你定义的类创建实例。</para>  
    203 203 </abstract>  
    204   <para>在 &python; 中定义类很简单。就象定义函数,没有单独的接口定义。只要定义类,然后就可以开始编码。&python; 类开始为保留字 &class;,后面跟着类名。从技术上讲,有这些就够了,因为一个类并不必须从其它类继承。</para>  
      204 <para>在 &python; 中定义类很简单。就象定义函数,没有单独的接口定义。只要定义类,然后就可以开始编码。&python; 类以保留字 &class; 开始,后面跟着类名。从技术上讲,有这些就够了,因为一个类并非必须从其它类继承。</para>  
    204 204 <example id="fileinfo.class.simplest">  
    205 205 <title>最简单的 &python; 类</title>  
     
    213 213 </callout>  
    214 214 <callout arearefs="fileinfo.class.1.2">  
    215   <para>这个类没有定义任何方法或属性,但是从语法上,需要在定义中有些东西,所以你使用 &pass;。这是一个&python;保留字,仅仅表示<quote>向前走,不要往这看</quote>。它是一条什么都不做的语句,当你删空函数或类时,它是一个很好的占位符。</para>  
      215 <para>这个类没有定义任何方法或属性,但是从语法上,需要在定义中有些东西,所以你使用 &pass;。这是一个 &python; 保留字,仅仅表示 <quote>向前走,不要往这看</quote>。它是一条什么都不做的语句,当你删空函数或类时,它是一个很好的占位符。</para>  
    215 215 </callout>  
    216 216 <callout arearefs="fileinfo.class.1.3">  
    217   <para>你可能猜到了,在类中的所有东西都要缩近,就象位于函数、&if; 语句,&for; 循环,诸如类的代码。第一条不缩近的东西不属于这个类。</para>  
      217 <para>你可能猜到了,在类中的所有东西都要缩近,就象位于函数、&if; 语句,&for; 循环,诸如类的代码。第一条不缩近的东西不属于这个类。</para>  
    217 217 </callout>  
    218 218 </calloutlist>  
     
    222 222 <note id="compare.pass.java" role="compare" vendor="java">  
    223 223 <title>&python; &vs; &java;: &pass;</title>  
    224   <para>在 &python; 中的 &pass; 语句就象 &java; 或 &c; 中的大括号空集(<literal>{}</literal>)。</para>  
      224 <para>在 &python; 中的 &pass; 语句就象 &java; 或 &c; 中的大括号空集 (<literal>{}</literal>)。</para>  
    224 224 </note>  
    225 225 <para>当然,实际上大多数的类都是从其它的类继承来的,并且它们会定义自已的类方法和属性。但是就象你刚才看到的,除了名字以外,类没有什么必须要具有的。特别是,&cpp; 程序员可能会感到奇怪,&python; 的类没有显示的构造函数和析构函数。&python; 类的确存在与构造函数相似的东西: &init; 方法。</para>  
     
    233 233 <calloutlist>  
    234 234 <callout arearefs="fileinfo.class.2.1">  
    235   <para>在 &python; 中,类的基类只是简单地列在类名后面的小括号里。所以 <classname>FileInfo</classname> 类是从 <classname>UserDict</classname> 类 (它是从 <link linkend="fileinfo.fromimport"><filename class="headerfile">UserDict</filename> 模块导进来的</link>)继承来的。<classname>UserDict</classname> 是一个象字典一样工作的类,它允许你完全子类化字典数据类型,同时增加你自已的行为。(也存在相似的类 <classname>UserList</classname> 和 <classname>UserString</classname> ,它们允许你子类化列表和字符串。)(译注:在 2.2 之后已经可以从 dict, list 来派生子类了) 在这个类的背后有一些“巫术”,我们将在本章的后面,随着更进一步地研究 <classname>UserDict</classname> 类,揭开这些秘密。</para>  
      235 <para>在 &python; 中,类的基类只是简单地列在类名后面的小括号里。所以 <classname>FileInfo</classname> 类是从 <classname>UserDict</classname> 类 (它是从 <link linkend="fileinfo.fromimport"><filename class="headerfile">UserDict</filename> 模块导进来的</link>) 继承来的。<classname>UserDict</classname> 是一个象字典一样工作的类,它允许你完全子类化字典数据类型,同时增加你自已的行为。(也存在相似的类 <classname>UserList</classname> 和 <classname>UserString</classname> ,它们允许你子类化列表和字符串。)(译注:在 2.2 之后已经可以从 dict, list 来派生子类了) 在这个类的背后有一些“巫术”,我们将在本章的后面,随着更进一步地研究 <classname>UserDict</classname> 类,揭开这些秘密。</para>  
    235 235 </callout>  
    236 236 </calloutlist>  
     
    253 253 <calloutlist>  
    254 254 <callout arearefs="fileinfo.class.2.2">  
    255   <para>类也可以 (并且 <link linkend="tip.docstring">应该</link>) 有 &docstring;s ,就象方法和函数一样。</para>  
      255 <para>类也可以 (并且 <link linkend="tip.docstring">应该</link>) 有 &docstring;s ,就象方法和函数一样。</para>  
    255 255 </callout>  
    256 256 <callout arearefs="fileinfo.class.2.3">  
    257   <para>&init; 在类的实例创建后被立即调用。它可能会引诱你称之为类的构造函数,但这种说法并不正确。说它引诱,是因为它看上去象(按照习惯, &init; 是类中第一个定义的方法),行为也象(在一个新创建的类实例中,它是首先被执行的代码),并且叫起来也象(<quote>init</quote>当然意味着构造的本性)。说它不正确,是因为对象在调用 &init; 时已经被构造出来了,你已经有了一个对类的新实例的有效引用。但 &init; 是在 &python; 中你可以得到的最接近构造函数的东西,并且它也扮演着非常相同的角色。</para>  
      257 <para>&init; 在类的实例创建后被立即调用。它可能会引诱你称之为类的构造函数,但这种说法并不正确。说它引诱,是因为它看上去象(按照习惯, &init; 是类中第一个定义的方法),行为也象(在一个新创建的类实例中,它是首先被执行的代码),并且叫起来也象(<quote>init</quote>当然意味着构造的本性)。说它不正确,是因为对象在调用 &init; 时已经被构造出来了,你已经有了一个对类的新实例的有效引用。但 &init; 是在 &python; 中你可以得到的最接近构造函数的东西,并且它也扮演着非常相同的角色。</para>  
    257 257 </callout>  
    258 258 <callout arearefs="fileinfo.class.2.4">  
    259   <para>每个类方法的第一个参数,包括 &init;,都是指向类的当前实例的引用。按照习惯这个参数总是被叫为 &self;。在 &init; 方法中,&self; 指向新创建的对象;在其它的类方法中,它指向方法被调用的类实例。尽管当定义方法时你需要明确指定 &self;,但在调用方法时,你<emphasis>不</emphasis>用指定它,&python;会替你自动加上的。</para>  
      259 <para>每个类方法的第一个参数,包括 &init;,都是指向类的当前实例的引用。按照习惯这个参数总是被称为 &self;。在 &init; 方法中,&self; 指向新创建的对象;在其它的类方法中,它指向方法被调用的类实例。尽管当定义方法时你需要明确指定 &self;,但在调用方法时,你<emphasis>不</emphasis>用指定它,&python; 会替你自动加上的。</para>  
    259 259 </callout>  
    260 260 <callout arearefs="fileinfo.class.2.5">  
     
    268 268 <note id="compare.self.java" role="compare" vendor="java">  
    269 269 <title>&python; &vs; &java;: &self;</title>  
    270   <para>习惯上,任何 &python; 类方法的第一个参数(对当前实例的引用)都叫做 &self;。这个参数扮演着 &cpp; 或 &java; 中的保留字 &this; 的角色,但 &self; 在 &python; 中并不是一个保留字,它只是一个命名习惯。虽然如此,也请除了 &self; 之外不要使用其它的名字,这是一个非常坚固的习惯。</para>  
      270 <para>习惯上,任何 &python; 类方法的第一个参数(对当前实例的引用)都叫做 &self;。这个参数扮演着 &cpp; 或 &java; 中的保留字 &this; 的角色,但 &self; 在 &python; 中并不是一个保留字,它只是一个命名习惯。虽然如此,也请除了 &self; 之外不要使用其它的名字,这是一个非常坚固的习惯。</para>  
    270 270 </note>  
    271 271 <example id="fileinfo.init.code.example">  
     
    281 281 <calloutlist>  
    282 282 <callout arearefs="fileinfo.class.2.6">  
    283   <para>一些伪面向对象语言,象 &powerbuilder; 有一种<quote>扩展</quote>构造函数和其它事件的概念,即父类的方法在子类的方法执行前被自动调用。&python;不是这样,你必须显示地调用在父类中的适合方法。</para>  
      283 <para>一些伪面向对象语言,象 &powerbuilder; 有一种<quote>扩展</quote>构造函数和其它事件的概念,即父类的方法在子类的方法执行前被自动调用。&python; 不是这样,你必须显示地调用在父类中的适合方法。</para>  
    283 283 </callout>  
    284 284 <callout arearefs="fileinfo.class.2.7">  
    285   <para>我告诉过你,这个类象字典一样工作,那么这里就是第一个印象。我们将参数 <varname>filename</varname> 赋值给对象 <literal>name</literal> 关键字,作为它的值。</para>  
      285 <para>我告诉过你,这个类象字典一样工作,那么这里就是第一个印象。我们将参数 <varname>filename</varname> 赋值给对象 <literal>name</literal> 关键字,作为它的值。</para>  
    285 285 </callout>  
    286 286 <callout arearefs="fileinfo.class.2.8">  
     
    294 294 <section>  
    295 295 <title>了解何时去使用 &self; 和 &init;</title>  
    296   <para>当定义你自已的类方法时,你<emphasis>必须</emphasis>明确将 &self; 作为每个方法的第一个参数列出,包括 &init;。当从你的类中调用一个父类的一个方法时,你必须包括 &self; 参数。但当你从类的外部调用你的类方法时,你不必对 &self; 参数指定任何值;你完全将其忽略,而 &python; 会自动地替你增加实例的引用。我知道刚开始这有些混乱,它并不是自相矛盾的,因为它依靠于一个你还不了解的区别(在绑定与非绑定方法之间),故看上去是矛盾的。</para>  
      296 <para>当定义你自已的类方法时,你 <emphasis>必须</emphasis> 明确将 &self; 作为每个方法的第一个参数列出,包括 &init;。当从你的类中调用一个父类的一个方法时,你必须包括 &self; 参数。但当你从类的外部调用你的类方法时,你不必对 &self; 参数指定任何值;你完全将其忽略,而 &python; 会自动地替你增加实例的引用。我知道刚开始这有些混乱,它并不是自相矛盾的,因为它依靠于一个你还不了解的区别(在绑定与非绑定方法之间),故看上去是矛盾的。</para>  
    296 296 <para>噢。我知道有很多知识需要吸收,但是你要掌握它。所有的 &python; 类以相同的方式工作,所以一旦你学会了一个,就是学会了全部。如果你忘了别的任何事,也要记住这件事,因为我认定它会让你出错:</para>  
    297 297 <note id="tip.initoptional">  
    298 298 <title>&init; 方法</title>  
    299   <para>&init; 方法是可选的,但是一旦你定义了,就必须记得显示调用父类的 &init; 方法(如果它定义了的话)。这样更是正确的:无论何时子类想扩展父类的行为,后代方法必须在适当的时机,使用适当的参数,显式调用父类方法。</para>  
      299 <para>&init; 方法是可选的,但是一旦你定义了,就必须记得显示调用父类的 &init; 方法(如果它定义了的话)。这样更是正确的:无论何时子类想扩展父类的行为,后代方法必须在适当的时机,使用适当的参数,显式调用父类方法。</para>  
    299 299 </note>  
    300 300 <itemizedlist role="furtherreading">  
     
    328 328 <calloutlist>  
    329 329 <callout arearefs="fileinfo.create.1.1">  
    330   <para>你正在创建 <classname>FileInfo</classname> 类(定义在 &fileinfo_modulename; 模块中)的实例,并且将新创建的实例赋值给变量 <varname>f</varname>。你传入了一个参数,<literal>/music/_singles/kairo.mp3</literal>,它将最后作为在 <classname>FileInfo</classname> 中 &init; 方法中的 <varname>filename</varname> 参数。</para>  
      330 <para>你正在创建 <classname>FileInfo</classname> 类(定义在 &fileinfo_modulename; 模块中)的实例,并且将新创建的实例赋值给变量 <varname>f</varname>。你传入了一个参数,<literal>/music/_singles/kairo.mp3</literal>,它将最后作为在 <classname>FileInfo</classname> 中 &init; 方法中的 <varname>filename</varname> 参数。</para>  
    330 330 </callout>  
    331 331 <callout arearefs="fileinfo.create.1.2">  
    332   <para>每一个类的实例有一个内置属性, &classattr;,它是对象的类。(注意这个表示包括了在我机器上的实例的物理地址,你的表示不会一样。)&java; 程序员可能对 <classname>Class</classname> 类熟悉,这个类包含了象 <function>getName</function> 和 <function>getSuperclass</function>  之类用来得到一个对象元数据信息的方法。在 &python; 中,这类元数据可以直接通过对象本身的属性,象 &classattr;, <literal>__name__</literal>, 和 <literal>__bases__</literal> 来得到。</para>  
      332 <para>每一个类的实例有一个内置属性, &classattr;,它是对象的类。(注意这个表示包括了在我机器上的实例的物理地址,你的表示不会一样。) &java; 程序员可能对 <classname>Class</classname> 类熟悉,这个类包含了象 <function>getName</function> 和 <function>getSuperclass</function>  之类用来得到一个对象元数据信息的方法。在 &python; 中,这类元数据可以直接通过对象本身的属性,象 &classattr;, <literal>__name__</literal> 和 <literal>__bases__</literal> 来得到。</para>  
    332 332 </callout>  
    333 333 <callout arearefs="fileinfo.create.1.3">  
     
    337 337 </callout>  
    338 338 <callout arearefs="fileinfo.create.1.4">  
    339   <para>还记得什么时候 &init; 方法<link linkend="fileinfo.class.example">将它的 <varname>filename</varname> 参数赋给 <literal>self["name"]</literal></link> 吗?哦,答案在这。在创建类实例时你传入的参数被正确发送到 &init; 方法中(当我们创建类实例时,我们所传递的参数被正确地发送给 __init__ 方法(随同一起传递的还有对象的引用,&self;,它是由 &python; 自动添加的)。</para>  
      339 <para>还记得什么时候 &init; 方法<link linkend="fileinfo.class.example">将它的 <varname>filename</varname> 参数赋给 <literal>self["name"]</literal></link> 吗?哦,答案在这。在创建类实例时你传入的参数被正确发送到 &init; 方法中(当我们创建类实例时,我们所传递的参数被正确地发送给 __init__ 方法(随同一起传递的还有对象的引用,&self;,它是由 &python; 自动添加的)。</para>  
    339 339 </callout>  
    340 340 </calloutlist>  
     
    357 357 <calloutlist>  
    358 358 <callout arearefs="fileinfo.create.2.1">  
    359   <para>每次 <function>leakmem</function> 函数被调用,你创建了 &fileinfo_classname; 的一个实例,将其赋给变量 <varname>f</varname>,这个变量是函数内的一个局部变量。然后函数结束没有释放 <varname>f</varname>,所以你可能认为有内存泄漏,但是你错了。当函数结束时,局部变量 <varname>f</varname> 超出了作用域。在这个地方,不再有任何对 &fileinfo_classname; 新创建实例的引用(因为除了 <varname>f</varname> 我们从未将其赋值给其它变量),所以 &python; 替我们销毁掉实例。</para>  
      359 <para>每次 <function>leakmem</function> 函数被调用,你创建了 &fileinfo_classname; 的一个实例,将其赋给变量 <varname>f</varname>,这个变量是函数内的一个局部变量。然后函数结束没有释放 <varname>f</varname>,所以你可能认为有内存泄漏,但是你错了。当函数结束时,局部变量 <varname>f</varname> 超出了作用域。在这个地方,不再有任何对 &fileinfo_classname; 新创建实例的引用(因为除了 <varname>f</varname> 我们从未将其赋值给其它变量),所以 &python; 替我们销毁掉实例。</para>  
    359 359 </callout>  
    360 360 <callout arearefs="fileinfo.create.2.3">  
     
    365 365 </example>  
    366 366 <para>对于这种垃圾收集的方式,技术上的术语叫做<quote>引用计数</quote>。&python; 维护着对每个实例的引用列表。在上面的例子中,只有一个 &fileinfo_classname; 的实例引用:局部变量 <varname>f</varname>。当函数结束时,变量 <varname>f</varname> 超出作用域,所以引用计数降为 &zero;,则 &python; 自动销毁掉实例。</para>  
    367   <para>在 &python; 的以前版本中,存在引用计数失败的情况,这样 &python; 不能在后面进行清除。如果你创建两个实例,它们相互引用(例如,双重链表,每一个结点有都一个指向列表中前一个和后一个结点的指针),任一个实例都不会被自动销毁,因为 &python; (正确)认为对于每个实例都存在一个引用。 &python; 2.0有一种额外的垃圾回收方式,叫做<quote>标记后清除</quote>,它足够聪明,可以正确地清除循环引用。</para>  
      367 <para>在 &python; 的以前版本中,存在引用计数失败的情况,这样 &python; 不能在后面进行清除。如果你创建两个实例,它们相互引用(例如,双重链表,每一个结点有都一个指向列表中前一个和后一个结点的指针),任一个实例都不会被自动销毁,因为 &python; (正确)认为对于每个实例都存在一个引用。 &python; 2.0 有一种额外的垃圾回收方式,叫做<quote>标记后清除</quote>,它足够聪明,可以正确地清除循环引用。</para>  
    367 367 <para>作为曾经读过哲学专业的一员,让我感到困惑的是,当没有人对事物进行观察时,它们就消失了,但是这确实是在 &python; 中所发生的。通常,你可以完全忘记内存管理,让 &python; 在后面进行清理。</para>  
    368 368 <itemizedlist role="furtherreading">  
    369   <title>进一步阅读关于垃圾回收</title>  
      369 <title>进一步阅读</title>  
    369 369 <listitem><para>&pythonlibraryreference; 总结了 <ulink url="&url_pythonlibraryreference;specialattrs.html">象 &classattr; 之类的内置属性</ulink>。</para></listitem>  
    370   <listitem><para>&pythonlibraryreference; 提供了 <ulink url="&url_pythonlibraryreference;module-gc.html"><filename class="headerfile">gc</filename> 模块的文档</ulink>,此模块给予你对 &python; 的垃圾回收的底层控制权。</para></listitem>  
      370 <listitem><para>&pythonlibraryreference; 提供了 <ulink url="&url_pythonlibraryreference;module-gc.html"><filename class="headerfile">gc</filename> 模块的文档</ulink>,此模块给予你对 &python; 的垃圾回收的底层控制权。</para></listitem>  
    370 370 </itemizedlist>  
    371 371 </section>  
     
    412 412 </callout>  
    413 413 <callout arearefs="fileinfo.userdict.1.3">  
    414   <para>&python; 支持数据属性(在 &java; 和 &powerbuilder; 中叫做 <quote>实例变量</quote>,在 &cpp; 中叫 <quote>数据成员</quote>),它是由某个特定的类实例所拥有的数据。在本例中,每个 &userdict_classname; 实例将拥有一个 <varname>data</varname> 数据属性。要从类外的代码引用这个属性,需要用实例的名字限定它,<literal><replaceable>instance</replaceable>.data</literal>,限定的方法与你用模块的名字来限定函数一样。要在类的内部引用一个数据属性,我们使用 &self; 作为限定符。习惯上,所有的数据属性都在 &init;  方法中初始化为有意义的值。然而,这并不是必须的,因为数据属性,象局部变量一样,当你首次赋给它值的时候<link linkend="odbchelper.vardef">突然产生</link>。</para>  
      414 <para>&python; 支持数据属性(在 &java; 和 &powerbuilder; 中叫做 <quote>实例变量</quote>,在 &cpp; 中叫 <quote>数据成员</quote>),它是由某个特定的类实例所拥有的数据。在本例中,每个 &userdict_classname; 实例将拥有一个 <varname>data</varname> 数据属性。要从类外的代码引用这个属性,需要用实例的名字限定它,<literal><replaceable>instance</replaceable>.data</literal>,限定的方法与你用模块的名字来限定函数一样。要在类的内部引用一个数据属性,我们使用 &self; 作为限定符。习惯上,所有的数据属性都在 &init;  方法中初始化为有意义的值。然而,这并不是必须的,因为数据属性,象局部变量一样,当你首次赋给它值的时候<link linkend="odbchelper.vardef">突然产生</link>。</para>  
    414 414 </callout>  
    415 415 <callout arearefs="fileinfo.userdict.1.4">  
     
    418 418 </callout>  
    419 419 <callout arearefs="fileinfo.userdict.1.5">  
    420   <para>这个语法你可能以前没看过(我还没有在这本书中的例子中用过它)。这是一条 &if; 语句,但是没有在下一行有一个缩近块,而只是在冒号后面,在同一行上有单条语句。这完全是合法的,它只是当你在一个块中仅有一条语句时的一个简写。(它就象在 &cpp; 中没有用大括号包括的单行语句。) 你可以用这种语法,或者可以在后面的行拥有缩近代码,但是不能对同一个块同时用两种方式。</para>  
      420 <para>这个语法你可能以前没看过(我还没有在这本书中的例子中用过它)。这是一条 &if; 语句,但是没有在下一行有一个缩近块,而只是在冒号后面,在同一行上有单条语句。这完全是合法的,它只是当你在一个块中仅有一条语句时的一个简写。(它就象在 &cpp; 中没有用大括号包括的单行语句。) 你可以用这种语法,或者可以在后面的行拥有缩近代码,但是不能对同一个块同时用两种方式。</para>  
    420 420 </callout>  
    421 421 </calloutlist>  
     
    424 424 <note id="compare.overloading" role="compare" vendor="java">  
    425 425 <title>&python; &vs; &java;: Function Overloading</title>  
    426   <para>&java;  和 &powerbuilder; 支持通过参数列表的重载,&ie; 一个类可以有同名的多个方法,但这些方法或者是参数个数不同,或参数的类型不同。其它语言(最明显如 &plsql;)甚至支持通过参数名的重载,&ie; 一个类可以有同名的多个方法,这些方法有相同类型,相同个数的参数,但参数名不同。&python; 两种都不支持,总之是没有任何形式的函数重载。一个 &init; 方法就是一个 &init; 方法,不管它有什么样的参数。每个类只能有一个 &init; 方法,并且如果一个子类拥有一个 &init; 方法,它 <emphasis>总是</emphasis> 覆盖父类的 &init; 方法,甚至子类可以用不同的参数列表来定义它。</para>  
      426 <para>&java;  和 &powerbuilder; 支持通过参数列表的重载,&ie; 一个类可以有同名的多个方法,但这些方法或者是参数个数不同,或参数的类型不同。其它语言(最明显如 &plsql;甚至支持通过参数名的重载,&ie; 一个类可以有同名的多个方法,这些方法有相同类型,相同个数的参数,但参数名不同。&python; 两种都不支持,总之是没有任何形式的函数重载。一个 &init; 方法就是一个 &init; 方法,不管它有什么样的参数。每个类只能有一个 &init; 方法,并且如果一个子类拥有一个 &init; 方法,它 <emphasis>总是</emphasis> 覆盖父类的 &init; 方法,甚至子类可以用不同的参数列表来定义它。</para>  
    426 426 </note>  
    427 427 <note id="fileinfo.derivedclasses">  
    428 428 <!--<title>Guido on Derived Classes</title>-->  
    429 429 <title/>  
    430   <para>&python; 的原作者Guido是这样解释方法覆盖的 “子类可以覆盖父类中的方法。因为方法没有特殊的优先级设置,在调用同一对象的另外方法时,父类中一个方法对另一个同类中的方法的调用,可能其实调用到的却是一个子类中覆盖父类同名方法的方法。(对于 &cpp; 程序员,所有的 &python; 方法都非常有效)”  如果你不明白(它另我颇感困惑),不必在意。我想我要跳过它。</para>  
      430 <para>&python; 的原作者 Guido 是这样解释方法覆盖的 “子类可以覆盖父类中的方法。因为方法没有特殊的优先级设置,在调用同一对象的另外方法时,父类中一个方法对另一个同类中的方法的调用,可能其实调用到的却是一个子类中覆盖父类同名方法的方法。(对于 &cpp; 程序员,所有的 &python; 方法都非常有效)”  如果你不明白(它另我颇感困惑),不必在意。我想我要跳过它。</para>  
    430 430 </note>  
    431 431 <caution id="note.dataattributes">  
     
    451 451 <calloutlist>  
    452 452 <callout arearefs="fileinfo.userdict.2.1">  
    453   <para>&clear; 是一个普通的类方法,可以在任何时候被任何人公开调用。注意,&clear; 象所有的类方法一样(常规的或专用的),使用  &self;  作为它的第一个参数。(记住,当你调用方法时,不用包括 &self;;这件事是 &python; 替你做的。) 还应注意这个封装类的基本技术:将一个真正的字典 (<varname>data</varname>)  作为数据属性保存起来,定义所有真正字典所拥有的方法,并且将每个类方法重定向到真正字典上的相应方法。(在你忘记的情况下,字典的 &clear; 方法 <link linkend="odbchelper.dict.del">删除它的所有关键字</link> 和关键字相应的值。)</para>  
      453 <para>&clear; 是一个普通的类方法,可以在任何时候被任何人公开调用。注意,&clear; 象所有的类方法一样(常规的或专用的),使用  &self;  作为它的第一个参数。(记住,当你调用方法时,不用包括 &self;;这件事是 &python; 替你做的。) 还应注意这个封装类的基本技术:将一个真正的字典 (<varname>data</varname>)  作为数据属性保存起来,定义所有真正字典所拥有的方法,并且将每个类方法重定向到真正字典上的相应方法。(在你忘记的情况下,字典的 &clear; 方法 <link linkend="odbchelper.dict.del">删除它的所有关键字</link> 和关键字相应的值。)</para>  
    453 453 </callout>  
    454 454 <callout arearefs="fileinfo.userdict.2.2">  
    455   <para>真正字典的 &copy;  方法会返回一个新的字典,它是原始字典的原样的复制(所有的键-值对都相同)。但是 &userdict_classname; 不能简单地重定向到 <function>self.data.copy</function>,因为那个方法返回一个真正的字典,而我们想要的是返回同一个类的一个新的实例,就象是 &self;。</para>  
      455 <para>真正字典的 &copy;  方法会返回一个新的字典,它是原始字典的原样的复制(所有的键-值对都相同)。但是 &userdict_classname; 不能简单地重定向到 <function>self.data.copy</function>,因为那个方法返回一个真正的字典,而我们想要的是返回同一个类的一个新的实例,就象是 &self;。</para>  
    455 455 </callout>  
    456 456 <callout arearefs="fileinfo.userdict.2.3">  
     
    460 460 </callout>  
    461 461 <callout arearefs="fileinfo.userdict.2.4">  
    462   <para>如果 <literal>&self;.&classattr;</literal> 不是 &userdict_classname;,那么 &self; 一定是 &userdict_classname; 的某个子类(如可能为 &fileinfo_classname;),生活总是存在意外。 &userdict_classname;  不知道如何生成它的子类的一个原样的拷贝,例如,有可能在子类中定义了其它的数据属性,所以我们只能完全复制它们,确定拷贝了它们的全部内容。幸运的是,&python; 带了一个模块可以正确地完成这件事,它叫做 &copy;。在这里我不想深入细节(然而它是一个绝对酷的模块,是否你想到要自已研究它了呢?)。说 &copy; 能够拷贝任何 &python; 对象就够了,这就是为什么我们在这里用它的原因。</para>  
      462 <para>如果 <literal>&self;.&classattr;</literal> 不是 &userdict_classname;,那么 &self; 一定是 &userdict_classname; 的某个子类(如可能为 &fileinfo_classname;),生活总是存在意外。 &userdict_classname;  不知道如何生成它的子类的一个原样的拷贝,例如,有可能在子类中定义了其它的数据属性,所以我们只能完全复制它们,确定拷贝了它们的全部内容。幸运的是,&python; 带了一个模块可以正确地完成这件事,它叫做 &copy;。在这里我不想深入细节(然而它是一个绝对酷的模块,是否你想到要自已研究它了呢?)。说 &copy; 能够拷贝任何 &python; 对象就够了,这就是为什么我们在这里用它的原因。</para>  
    462 462 </callout>  
    463 463 <callout arearefs="fileinfo.userdict.2.5">  
     
    469 469 <note>  
    470 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>  
      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>  
    471 471 </note>  
    472 472 <para>如例子中所示,在 &python; 中,你可以直接继承自内建数据类型 &dict;,这样做有三点与 &userdict; 不同。</para>  
     
    491 491 <itemizedlist role="furtherreading">  
    492 492 <title>进一步阅读</title>  
    493   <listitem><para>&pythonlibraryreference; 提供了 <ulink url="&url_pythonlibraryreference;module-UserDict.html">&userdict; 模块</ulink> 和 <ulink url="&url_pythonlibraryreference;module-copy.html">&copy; 模块</ulink>的文档。</para></listitem>  
      493 <listitem><para>&pythonlibraryreference; 提供了 <ulink url="&url_pythonlibraryreference;module-UserDict.html">&userdict; 模块</ulink> 和 <ulink url="&url_pythonlibraryreference;module-copy.html">&copy; 模块</ulink> 的文档。</para></listitem>  
    493 493 </itemizedlist>  
    494 494 </section>  
     
    499 499 <abstract>  
    500 500 <title/>  
    501   <para>除了普通的类方法,还有一些对于 &python; 类可以定义的专用方法。专用方法是在特殊情况下或当使用特别语法时由 &python; 替你调用的,而不是在代码中直接调用(象普通的方法那样)。</para>  
      501 <para>除了普通的类方法,还有一些对于 &python; 类可以定义的专用方法。专用方法是在特殊情况下或当使用特别语法时由 &python; 替你调用的,而不是在代码中直接调用(象普通的方法那样)。</para>  
    501 501 </abstract>  
    502 502 <para>就象你在 <link linkend="fileinfo.userdict">上一节</link> 所看到的,普通的方法对在类中封装字典很有帮助。但是只有普通方法是不够的,因为除了对字典调用方法之外,还有很多事情可以做的。例如,你可以通过一种没有包括明确方法调用的语法来 <link linkend="odbchelper.dict.define">获得</link> 和 <link linkend="odbchelper.dict.modify">设置</link> 数据项。这就是专用方法产生的原因:它们提供了一种方法,可以将非方法调用语法映射到方法调用上。</para>  
     
    517 517 <calloutlist>  
    518 518 <callout arearefs="fileinfo.specialmethods.1.1">  
    519   <para>&getitem; 专用方法很简单。象普通的方法 &clear;,&keys;和 &values;一样,它只是重定向到字典,返回字典的值。但是怎么调用它呢?哦,你可以直接调用  &getitem;,但是在实际中你其实不会那样做:我在这里执行它只是要告诉你它是如何工作的。正确地使用  &getitem; 的方法是让 &python; 来替你调用。</para>  
      519 <para>&getitem; 专用方法很简单。象普通的方法 &clear;,&keys; 和 &values; 一样,它只是重定向到字典,返回字典的值。但是怎么调用它呢?哦,你可以直接调用  &getitem;,但是在实际中你其实不会那样做:我在这里执行它只是要告诉你它是如何工作的。正确地使用  &getitem; 的方法是让 &python; 来替你调用。</para>  
    519 519 </callout>  
    520 520 <callout arearefs="fileinfo.specialmethods.1.2">  
     
    547 547 </example>  
    548 548 <para>&setitem;  是一个专用类方法,因为它可以让 &python; 来替你调用,但是它仍然是一个类方法。就象在 &userdict_classname; 中定义 &setitem;  方法一样容易,我们可以在子类中重新定义它,对父类的方法进行覆盖。这就允许我们定义出在某些方面象字典一样动作的类,但是可以定义它自已的行为,超过和超出内置的字典。</para>  
    549   <para>这个概念是本章中我们正在学习的整个框架的基础。每个文件类型可以拥有一个处理器类,这些类知道如何从一个特殊的文类型得到元数据。一但知道了某些属性(象文件名和位置),处理器类就知道如何自动地得到其它的属性。它的实现是通过覆盖 &setitem; 方法,检查特别的关键字,然后当找到后加入额外的处理。</para>  
    550   <para>例如,&mp3fileinfo_classname; 是 &fileinfo_classname; 的子类。在设置了一个 &mp3fileinfo_classname; 类的 <literal>name</literal> 时,并不只是设置 <literal>name</literal> 关键字(象父类 &fileinfo_classname; 所做的),它还要在文件自身内进行搜索 &mp3; 的标记然后填充一整套关键字集合。 下面的例子将展示其如何工作。</para>  
      549 <para>这个概念是本章中我们正在学习的整个框架的基础。每个文件类型可以拥有一个处理器类,这些类知道如何从一个特殊的文类型得到元数据。一但知道了某些属性(象文件名和位置),处理器类就知道如何自动地得到其它的属性。它的实现是通过覆盖 &setitem; 方法,检查特别的关键字,然后当找到后加入额外的处理。</para>  
      550 <para>例如,&mp3fileinfo_classname; 是 &fileinfo_classname; 的子类。在设置了一个 &mp3fileinfo_classname; 类的 <literal>name</literal> 时,并不只是设置 <literal>name</literal> 关键字(象父类 &fileinfo_classname; 所做的),它还要在文件自身内进行搜索 &mp3; 的标记然后填充一整套关键字集合。 下面的例子将展示其如何工作。</para>  
    551 551 <example>  
    552 552 <title>在 &mp3fileinfo_classname; 中覆盖 &setitem;</title>  
     
    558 558 <calloutlist>  
    559 559 <callout arearefs="fileinfo.specialmethods.3.1">  
    560   <para>注意我们的 &setitem; 方法严格按照父类方法相同的形式进行定义。这一点很重要,因为 &python; 将替你执行方法,则它希望这个函数用确定个数的参数进行定义。 (从技术上说,参数的名字没有关系,只是个数。)</para>  
      560 <para>注意我们的 &setitem; 方法严格按照父类方法相同的形式进行定义。这一点很重要,因为 &python; 将替你执行方法,则它希望这个函数用确定个数的参数进行定义。 (从技术上说,参数的名字没有关系,只是个数。)</para>  
    560 560 </callout>  
    561 561 <callout arearefs="fileinfo.specialmethods.3.2">  
     
    594 594 <calloutlist>  
    595 595 <callout arearefs="fileinfo.specialmethods.4.1">  
    596   <para>首先,我们创建了一个 &mp3fileinfo_classname; 的实例,没有传递给它文件名。(我们可以不用它,因为 &init; 方法的 <varname>filename</varname> 参数是 <link linkend="apihelper.optional">可选的</link>。) 因为 &mp3fileinfo_classname; 没有它自已的 &init; 方法,&python; 沿着父类树走,发现了 &fileinfo_classname; 的 &init; 方法。这个 &init; 方法手工调用了 &userdict_classname; 的 &init; 方法,然后设置 <literal>name</literal> 关键字为 <varname>filename</varname>,它为 &none;,因为我们还没有传入一个文件名。所以,<varname>mp3file</varname> 最初看上去象是有一个关键字,值为 &none; 的<literal>name</literal>,的字典。</para>  
      596 <para>首先,我们创建了一个 &mp3fileinfo_classname; 的实例,没有传递给它文件名。(我们可以不用它,因为 &init; 方法的 <varname>filename</varname> 参数是 <link linkend="apihelper.optional">可选的</link>。) 因为 &mp3fileinfo_classname; 没有它自已的 &init; 方法,&python; 沿着父类树走,发现了 &fileinfo_classname; 的 &init; 方法。这个 &init; 方法手工调用了 &userdict_classname; 的 &init; 方法,然后设置 <literal>name</literal> 关键字为 <varname>filename</varname>,它为 &none;,因为我们还没有传入一个文件名。所以,<varname>mp3file</varname> 最初看上去象是有一个关键字的字典,<literal>name</literal> 的值为 &none;。</para>  
    596 596 </callout>  
    597 597 <callout arearefs="fileinfo.specialmethods.4.2">  
    598   <para>现在真正有趣的开始了。设置 <varname>mp3file</varname> 的 <literal>name</literal> 关键字触发了 &mp3fileinfo_classname; 上的 &setitem; 方法(不是 &userdict_classname;),这个方法注意到我们正在用一个真实的值来设置 <literal>name</literal> 关键字,接着调用 <function>self.__parse</function>。尽管我们完全还没有研究过 <function>__parse</function> 方法,从它的输出你可以看出,它设置了其它几个关键字:<literal>album</literal>, <literal>artist</literal>, <literal>genre</literal>, <literal>title</literal>, <literal>year</literal> 和 <literal>comment</literal>。</para>  
      598 <para>现在真正有趣的开始了。设置 <varname>mp3file</varname> 的 <literal>name</literal> 关键字触发了 &mp3fileinfo_classname; 上的 &setitem; 方法(不是 &userdict_classname;),这个方法注意到我们正在用一个真实的值来设置 <literal>name</literal> 关键字,接着调用 <function>self.__parse</function>。尽管我们完全还没有研究过 <function>__parse</function> 方法,从它的输出你可以看出,它设置了其它几个关键字:<literal>album</literal>, <literal>artist</literal>, <literal>genre</literal>, <literal>title</literal>, <literal>year</literal> 和 <literal>comment</literal>。</para>  
    598 598 </callout>  
    599 599 <callout arearefs="fileinfo.specialmethods.4.3">  
     
    636 636 </callout>  
    637 637 <callout arearefs="fileinfo.morespecial.1.4">  
    638   <para>&delitem; 在调用 <literal>del <replaceable>instance</replaceable>[<replaceable>key</replaceable>]</literal> 时调用 ,你可能记得它作为 <link linkend="odbchelper.dict.del">从字典中删除单个元素</link>的方法。当你在类实例中使用 &del; 时,&python; 替你调用 &delitem; 专用方法。</para>  
      638 <para>&delitem; 在调用 <literal>del <replaceable>instance</replaceable>[<replaceable>key</replaceable>]</literal> 时调用 ,你可能记得它作为 <link linkend="odbchelper.dict.del">从字典中删除单个元素</link> 的方法。当你在类实例中使用 &del; 时,&python; 替你调用 &delitem; 专用方法。</para>  
    638 638 </callout>  
    639 639 </calloutlist>  
     
    642 642 <note id="compare.strequals.java" role="compare" vendor="java">  
    643 643 <title>&python; &vs; &java; equality and identity</title>  
    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>  
      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>  
    644 644 </note>  
    645   <para>在这个地方,你可能会想,<quote>所有这些工作只是为了在类中做一些我可以对一个内置数据类型所做的操作</quote>。不错,如果你能够从象字典一样的内置数据类型进行继承的话,事情就容易多了(那样整个 &userdict_classname; 类将完全不需要了)。尽管你可以这样做,专用方法仍然是有用的,因为它们可以用于任何的类,而不只是象 &userdict_classname; 这样的封装类。</para>  
      645 <para>在这个地方,你可能会想,<quote>所有这些工作只是为了在类中做一些我可以对一个内置数据类型所做的操作</quote>。不错,如果你能够从象字典一样的内置数据类型进行继承的话,事情就容易多了(那样整个 &userdict_classname; 类将完全不需要了)。尽管你可以这样做,专用方法仍然是有用的,因为它们可以用于任何的类,而不只是象 &userdict_classname; 这样的封装类。</para>  
    645 645 <para>专用方法意味着 <emphasis>任何类</emphasis> 可以象字典一样保存键-值对,只要定义  &setitem; 方法。任何类可以表现得象一个序列,只要通过定义 &getitem; 方法。任何定义了 &cmpspecial; 方法的类可以用 &comparisonequals; 进行比较。并且如果你的类表现拥有类似长度的东西,不要定义 <function>GetLength</function> 方法,而定义 &lenspecial; 方法,使用 <literal>len(<replaceable>instance</replaceable>)</literal>。</para>  
    646 646 <note id="note.physical.v.logical">  
    647 647 <!--<title>Physical &vs; Logical Models</title>-->  
    648 648 <title/>  
    649   <para>其它的面向对象语言仅让你定义一个对象的物理模型(<quote>这个对象有 <function>GetLength</function> 方法</quote>),而 &python; 的专用类方法象 &lenspecial; 允许你定义一个对象的逻辑模型(<quote>这个对象有一个长度</quote>)。  
      649 <para>其它的面向对象语言仅让你定义一个对象的物理模型(<quote>这个对象有 <function>GetLength</function> 方法</quote>),而 &python; 的专用类方法象 &lenspecial; 允许你定义一个对象的逻辑模型(<quote>这个对象有一个长度</quote>)。  
    649 649 </para>  
    650 650 </note>  
    651   <para>&python; 存在许多其它的专用方法。有一整套的专用方法,可以让类表现得象数值一样,允许你在类实例上进行加,减,和执行其它算数操作。(关于这一点典型的例子就是表示复数的类,数值带有实数和虚数部分。) &callspecial; 方法让一个类表现得象一个函数,允许你直接调用一个类实例。并且存在其它的专用函数,允许类拥有只读或只写数据属性,在后面的章节中我们会更多地谈到这些。</para>  
      651 <para>&python; 存在许多其它的专用方法。有一整套的专用方法,可以让类表现得象数值一样,允许你在类实例上进行加,减,和执行其它算数操作。(关于这一点典型的例子就是表示复数的类,数值带有实数和虚数部分。)  &callspecial; 方法让一个类表现得象一个函数,允许你直接调用一个类实例。并且存在其它的专用函数,允许类拥有只读或只写数据属性,在后面的章节中我们会更多地谈到这些。</para>  
    651 651 <itemizedlist role="furtherreading">  
    652 652 <title>进一步阅读</title>  
    653   <listitem><para>&pythonlanguagereference; 提供了 <ulink url="&url_pythonlanguagereference;specialnames.html">所有专用类方法</ulink>的文档。</para></listitem>  
      653 <listitem><para>&pythonlanguagereference; 提供了 <ulink url="&url_pythonlanguagereference;specialnames.html">所有专用类方法</ulink> 的文档。</para></listitem>  
    653 653 </itemizedlist>  
    654 654 </section>  
     
    663 663 <abstract>  
    664 664 <title/>  
    665   <para>你已经知道了 <link linkend="fileinfo.userdict.init.example">数据属性</link>,它们是被一个特定的类�例所拥有的变量。&python; 也支持类属性,它们是由类本身所拥有的。</para>  
      665 <para>你已经知道了 <link linkend="fileinfo.userdict.init.example">数据属性</link>,它们是被一个特定的类�例所拥有的变量。&python; 也支持类属性,它们是由类本身所拥有的。</para>  
    665 665 </abstract>  
    666 666 <example id="fileinfo.classattributes.intro">  
     
    703 703 <note id="compare.classattr.java" role="compare" vendor="java">  
    704 704 <title>&python; vs. &java; attribute definitions</title>  
    705   <para>在 &java; 中,静态变量(在 &python; 中叫类属性)和实例变量(在 &python; 中叫数据属性)两者是紧跟在类定义之后定义的(一个有 <literal>static</literal> 关键字,一个没有)。在 &python; 中,只有类属性可以定义在这里,数据属性定义在 &init; 方法中定义。</para>  
      705 <para>在 &java; 中,静态变量(在 &python; 中叫类属性)和实例变量(在 &python; 中叫数据属性)两者是紧跟在类定义之后定义的(一个有 <literal>static</literal> 关键字,一个没有)。在 &python; 中,只有类属性可以定义在这里,数据属性定义在 &init; 方法中。</para>  
    705 705 </note>  
    706   <para>类属性可以作为类级别的常量来使用(这就是为什么我们在 &mp3fileinfo_classname; 中使用它们),但是它们不是真正的常量。你也可以修改它们。</para>  
      706 <para>类属性可以作为类级别的常量来使用(这就是为什么我们在 &mp3fileinfo_classname; 中使用它们),但是它们不是真正的常量。你也可以修改它们。</para>  
    706 706 <note>  
    707 707 <title/>  
     
    738 738 </callout>  
    739 739 <callout arearefs="fileinfo.classattributes.2.2">  
    740   <para><literal>__class__</literal> 是每个类实例的一个内置属性(也是每个类的)。它是一个类的引用,而 <varname>self</varname> 是一个类(在本例中, 是 <classname>counter</classname> 类)的实例。</para>  
      740 <para><literal>__class__</literal> 是每个类实例的一个内置属性(也是每个类的)。它是一个类的引用,而 <varname>self</varname> 是一个类(在本例中, 是 <classname>counter</classname> 类)的实例。</para>  
    740 740 </callout>  
    741 741 <callout arearefs="fileinfo.classattributes.2.3">  
     
    765 765 <para>与大多数的语言不同,一个 &python; 函数,方法,或属性是私有还是公有,完全取决于它的名字。</para>  
    766 766 </abstract>  
    767   <para>如果一个 &python; 函数,类方法,或属性的名字以两个下划线开始(但不是结束),它是私有的;其它所有的都是公有的。  
    768   &python; 没有类方法 <emphasis>保护</emphasis> 的概念(只能用于它们自已的类和子类中)。类方法或者是私有(只能在它们自已的类中使用)或者是公有(任何地方都可使用)。</para>  
    769   <para>在 &mp3fileinfo_classname; 中,有两个方法:<function>__parse</function> 和 &setitem;。正如我们已经讨论过的,&setitem; 是一个 <link linkend="fileinfo.specialmethods.setitem.example">专有方法</link>;通常,你不直接调用它,而是通过在一个类上使用字典语法来调用,但它是公有的,并且如果有一个真正好的理由,你可以直接调用它(甚至从 &fileinfo_modulename; 模块的外面)。然而,<function>__parse</function> 是私有的,因为在它的名字前面有两个下划线。</para>  
      767 <para>如果一个 &python; 函数,类方法,或属性的名字以两个下划线开始(但不是结束),它是私有的;其它所有的都是公有的。  
      768 &python; 没有类方法 <emphasis>保护</emphasis> 的概念(只能用于它们自已的类和子类中)。类方法或者是私有(只能在它们自已的类中使用)或者是公有(任何地方都可使用)。</para>  
      769 <para>在 &mp3fileinfo_classname; 中,有两个方法:<function>__parse</function> 和 &setitem;。正如我们已经讨论过的,&setitem; 是一个 <link linkend="fileinfo.specialmethods.setitem.example">专有方法</link>;通常,你不直接调用它,而是通过在一个类上使用字典语法来调用,但它是公有的,并且如果有一个真正好的理由,你可以直接调用它(甚至从 &fileinfo_modulename; 模块的外面)。然而,<function>__parse</function> 是私有的,因为在它的名字前面有两个下划线。</para>  
    770 770 <note id="tip.specialmethodnames">  
    771 771 <title>Method Naming Conventions</title>  
    772   <para>在 &python; 中,所有的专用方法(象 <link linkend="fileinfo.specialmethods.setitem.example">&setitem;</link>)和内置属性(象 <link linkend="odbchelper.import">&doc;</link>)遵守一个标准的命名习惯:开始和结束都有两个下划下。不要对你自已的方法和属性用这种方法命名;到后面,它只会把你(或其它人)搞乱。</para>  
      772 <para>在 &python; 中,所有的专用方法(象 <link linkend="fileinfo.specialmethods.setitem.example">&setitem;</link>)和内置属性(象 <link linkend="odbchelper.import">&doc;</link>)遵守一个标准的命名习惯:开始和结束都有两个下划下。不要对你自已的方法和属性用这种方法命名;到后面,它只会把你(或其它人)搞乱。</para>  
    772 772 </note>  
    773 773 <example>  
     
    782 782 <callout arearefs="fileinfo.private.1.1">  
    783 783 <para>如果你试图调用一个私有方法,&python; 将引发一个有些误导的异常,宣称那个方法不存在。当然它确实存在,但是它是私有的,所以在类外是不可使用的。</para>  
    784   <para>严格地说,私有方法在它们的类外是可以访问的,只是不 <emphasis>容易</emphasis> 处理。在 &python; 中没有什么是真正私有的;在内部,私有方法和属性的名字被忽然改变和恢复,以致于使得它们看上去用它们给定的名字是无法使用的。你可以通过 <function>_MP3FileInfo__parse</function> 名字来使用 &mp3fileinfo_classname; 类的 <function>__parse</function> 方法。 知道了这个方法很有趣,然后要保证决不在真正的代码中使用它。私有方法由于某种原因而私有,但是象其它很多在Python中的东西一样,它们的私有化基本上是习惯问题,而不是强迫的。</para>  
      784 <para>严格地说,私有方法在它们的类外是可以访问的,只是不 <emphasis>容易</emphasis> 处理。在 &python; 中没有什么是真正私有的;在内部,私有方法和属性的名字被忽然改变和恢复,以致于使得它们看上去用它们给定的名字是无法使用的。你可以通过 <function>_MP3FileInfo__parse</function> 名字来使用 &mp3fileinfo_classname; 类的 <function>__parse</function> 方法。 知道了这个方法很有趣,然后要保证决不在真正的代码中使用它。私有方法由于某种原因而私有,但是象其它很多在 &python; 中的东西一样,它们的私有化基本上是习惯问题,而不是强迫的。</para>  
    784 784 </callout>  
    785 785 </calloutlist>  
     
    788 788 <itemizedlist role="furtherreading">  
    789 789 <title>进一步阅读</title>  
    790   <listitem><para>&pythontutorial; 讨论了 <ulink url="&url_pythontutorial;node11.html#SECTION0011600000000000000000">私有变量</ulink>的内部工作方式。</para></listitem>  
      790 <listitem><para>&pythontutorial; 讨论了 <ulink url="&url_pythontutorial;node11.html#SECTION0011600000000000000000">私有变量</ulink> 的内部工作方式。</para></listitem>  
    790 790 </itemizedlist>  
    791 791 </section>  
     
    802 802 <itemizedlist>  
    803 803 <listitem><para>使用 <link linkend="odbchelper.import">&importmodule;</link> 或 <link linkend="fileinfo.fromimport">&frommoduleimport;</link>导入模块</para></listitem>  
    804   <listitem><para><link linkend="fileinfo.class">定义</link> 和 <link linkend="fileinfo.create">实例化</link>类</para></listitem>  
      804 <listitem><para><link linkend="fileinfo.class">定义</link> 和 <link linkend="fileinfo.create">实例化</link> 类</para></listitem>  
    804 804 <listitem><para>定义 <link linkend="fileinfo.class.example">&init; 方法</link> 和其他 <link linkend="fileinfo.specialmethods">专用类方法</link>, 并理解它们何时会调用</para></listitem>  
    805 805 <listitem><para>子类化 <link linkend="fileinfo.userdict">&userdict_classname;</link> 来定义作为象字典的类</para></listitem>  
     
    810 810 </section>  
    811 811 </chapter>  
    812   <!-- =================================================================================================== -->  
      812 <!-- =============================================================================== -->  
    812 812 <chapter id="filehandling">  
    813 813 <?dbhtml filename="file_handling/index.html"?>  
     
    836 836 <listitem><para><link linkend="odbchelper.tuplemethods">调用不存在的方法</link> 将引发 <errorcode>AttributeError</errorcode> 异常。</para></listitem>  
    837 837 <listitem><para><link linkend="odbchelper.unboundvariable">引用不存在的变量</link> 将引发 <errorcode>NameError</errorcode> 异常。</para></listitem>  
    838   <listitem><para><link linkend="odbchelper.stringformatting.coerce">未强制转换就混数据类型</link> 将引发 <errorcode>TypeError</errorcode> 异常。</para></listitem>  
      838 <listitem><para><link linkend="odbchelper.stringformatting.coerce">未强制转换就混数据类型</link> 将引发 <errorcode>TypeError</errorcode> 异常。</para></listitem>  
    838 838 </itemizedlist>  
    839   <para>在这些情况下,我们都在简单使用 &python; &ide;:一个错误发生了,异常被打印出来 (根据你的 &ide;,有意地以一种刺眼的红色形式表示),这便是。这叫做 <emphasis>未处理</emphasis> 异常;当异常被引发时,没有代码来明确地关注和处理它,所以异常被传给置在 &python; 中的缺省的处理,它会输出一些调试信息并且终止运行。在 &ide; 中,这不是什么大事,但是如果发生在你真正的 &python; 程序运行的时候,整个程序将会终止。</para>  
    840   <para>然而,一个异常不一定会引起程序的完全崩溃。当异常引发时,可以被  <emphasis>处理</emphasis> 掉。有时候一个异常实际是因为代码中的 bug (比如使用一个不存在的变量),但是许多时候,一个异常是可以预计的。如果你打开一个文件,它可能不存在。如果你连接一个数据库,它可能不可连接或没有访问所需的正确的安全证书。如果知道一行代码可能会引发异常,你应该使用一个 &tryexcept; 块来处理异常。</para>  
      839 <para>在这些情况下,我们都在简单使用 &python; &ide;:一个错误发生了,异常被打印出来 (根据你的 &ide;,有意地以一种刺眼的红色形式表示),这便是。这叫做 <emphasis>未处理</emphasis> 异常;当异常被引发时,没有代码来明确地关注和处理它,所以异常被传给置在 &python; 中的缺省的处理,它会输出一些调试信息并且终止运行。在 &ide; 中,这不是什么大事,但是如果发生在你真正的 &python; 程序运行的时候,整个程序将会终止。</para>  
      840 <para>然而,一个异常不一定会引起程序的完全崩溃。当异常引发时,可以被  <emphasis>处理</emphasis> 掉。有时候一个异常实际是因为代码中的 bug (比如使用一个不存在的变量),但是许多时候,一个异常是可以预计的。如果你打开一个文件,它可能不存在。如果你连接一个数据库,它可能不可连接或没有访问所需的正确的安全证书。如果知道一行代码可能会引发异常,你应该使用一个 &tryexcept; 块来处理异常。</para>  
    841 841 <example>  
    842 842 <title>打开一个不存在的文件</title>  
     
    854 854 <calloutlist>  
    855 855 <callout arearefs="fileinfo.exceptions.1.1">  
    856   <para>使用内置 &open; 函数,我们可以试着打开一个文件来读取 (在下一节有关于 &open; 的更多内容)。但是那个文件不存在,所以这样就引发 &ioerror; 异常。因为我们没有提供任何显式的对 &ioerror; 异常的检查,&python; 仅仅打印出某个关于发生了什么的调试信息,然后终止。</para>  
      856 <para>使用内置 &open; 函数,我们可以试着打开一个文件来读取 (在下一节有关于 &open; 的更多内容)。但是那个文件不存在,所以这样就引发 &ioerror; 异常。因为我们没有提供任何显式的对 &ioerror; 异常的检查,&python; 仅仅打印出某个关于发生了什么的调试信息,然后终止。</para>  
    856 856 </callout>  
    857 857 <callout arearefs="fileinfo.exceptions.1.2">  
     
    867 867 </calloutlist>  
    868 868 </example>  
    869   <para>异常可能看上去不友好 (毕竟,如果你不捕捉异常,整个程序将崩溃),但是考虑一下别的方法。你希望获得一个没有用的文件对象而不是一个不存在的文件吧?不管怎么样你都得检查它的有效性,而且如果你忘记了,你的程序将会在下面某个地方给出奇怪的错误,这样你将不得不追溯到源程序。我确信你做过这种事;这可并不有趣。使用异常,一发生错误,你就可以在问题的源头通过标准的方法来处理它们。</para>  
      869 <para>异常可能看上去不友好 (毕竟,如果你不捕捉异常,整个程序将崩溃),但是考虑一下别的方法。你希望获得一个没有用的文件对象而不是一个不存在的文件吧?不管怎么样你都得检查它的有效性,而且如果你忘记了,你的程序将会在下面某个地方给出奇怪的错误,这样你将不得不追溯到源程序。我确信你做过这种事;这可并不有趣。使用异常,一发生错误,你就可以在问题的源头通过标准的方法来处理它们。</para>  
    869 869 <section>  
    870 870 <title>为其他用途使用异常</title>  
    871   <para>除了处理实际的错误条件之外,对于异常还有许多其它的用处。在标准 &python; 库中一个普通的用法就是试着导入一个模块,然后检查是否它能使用。导入一个并不存在的模块将引发一个 &importerror;  异常。你可以使用这种方法来定义多级别的功能,依靠在运行时哪个模块是有效的,或支持多种平台 (即平台特定代码被分离到不同的模块中)。</para>  
      871 <para>除了处理实际的错误条件之外,对于异常还有许多其它的用处。在标准 &python; 库中一个普通的用法就是试着导入一个模块,然后检查是否它能使用。导入一个并不存在的模块将引发一个 &importerror;  异常。你可以使用这种方法来定义多级别的功能,依靠在运行时哪个模块是有效的,或支持多种平台 (即平台特定代码被分离到不同的模块中)。</para>  
    871 871 <para>你也能通过创建一个从内置的  <classname>Exception</classname> 类继承的类定义你自己的异常, 然后使用 <function>raise</function> 命令引发你的异常。如果你对此感兴趣,请看进一步阅读的部分。</para>  
    872 872  
     
    897 897 <calloutlist>  
    898 898 <callout arearefs="fileinfo.exceptions.2.1">  
    899   <para>&termios; 是一个 &unix; 特定模块,它提供了对于输入终端的底层控制。如果这个模块无效 (因为它不在你的系统上,或你的系统不支持它),则导入失败,&python; 引发我们捕捉的 &importerror; 异常。</para>  
      899 <para>&termios; 是一个 &unix; 特定模块,它提供了对于输入终端的底层控制。如果这个模块无效 (因为它不在你的系统上,或你的系统不支持它),则导入失败,&python; 引发我们捕捉的 &importerror; 异常。</para>  
    899 899 </callout>  
    900 900 <callout arearefs="fileinfo.exceptions.2.2">  
     
    906 906 </callout>  
    907 907 <callout arearefs="fileinfo.exceptions.2.4">  
    908   <para>这些平台特定的模块没有一个有效 (有可能,因为 &python; 已经移植到了许多不同的平台上了),所以我们需要回头使用一个缺省口令输入函数 (这个函数定义在 &getpass; 模块中的别的地方)。注意,我们在这里做的:我们将函数 <function>default_getpass</function> 赋给变量 <varname>getpass</varname>。如果你读了官方 &getpass; 文档,它会告诉你 &getpass; 模块定义了一个 <function>getpass</function> 函数。它是这样做的:通过绑定 <varname>getpass</varname> 到正确的函数来适应你的平台。然后当你调用 <function>getpass</function> 函数时,你实际上调用了平台特定的函数,是这段代码已经为你设置好的。你不需要知道或关心你的代码正运行在何种平台上;只要调用 <function>getpass</function>,则它总能正确处理。</para>  
      908 <para>这些平台特定的模块没有一个有效 (有可能,因为 &python; 已经移植到了许多不同的平台上了),所以我们需要回头使用一个缺省口令输入函数 (这个函数定义在 &getpass; 模块中的别的地方)。注意,我们在这里做的:我们将函数 <function>default_getpass</function> 赋给变量 <varname>getpass</varname>。如果你读了官方 &getpass; 文档,它会告诉你 &getpass; 模块定义了一个 <function>getpass</function> 函数。它是这样做的:通过绑定 <varname>getpass</varname> 到正确的函数来适应你的平台。然后当你调用 <function>getpass</function> 函数时,你实际上调用了平台特定的函数,是这段代码已经为你设置好的。你不需要知道或关心你的代码正运行在何种平台上;只要调用 <function>getpass</function>,则它总能正确处理。</para>  
    908 908 </callout>  
    909 909 <callout arearefs="fileinfo.exceptions.2.5">  
     
    941 941 <calloutlist>  
    942 942 <callout arearefs="fileinfo.files.1.1">  
    943   <para>&open; 方法可以接收三个参数:文件名,模式,和缓冲区参数。只有第一个参数,文件名,是必须的;其它两个是 <link linkend="apihelper.optional">可选的</link>。如果没有指定,文件以文本方式打开。这里我们以二进制方式打开文件进行读取。(<literal>print open.__doc__</literal> 会给出所有可能模式的很好的解释。)</para>  
      943 <para>&open; 方法可以接收三个参数:文件名,模式,和缓冲区参数。只有第一个参数,文件名,是必须的;其它两个是 <link linkend="apihelper.optional">可选的</link>。如果没有指定,文件以文本方式打开。这里我们以二进制方式打开文件进行读取。(<literal>print open.__doc__</literal> 会给出所有可能模式的很好的解释。)</para>  
    943 943 </callout>  
    944 944 <callout arearefs="fileinfo.files.1.2">  
    945   <para>&open;  函数返回一个对象 (到现在为止,<link linkend="odbchelper.objects">这一点应该不会使你感到吃惊</link>)。一个文件对象有几个有用的属性。</para>  
      945 <para>&open;  函数返回一个对象 (到现在为止,<link linkend="odbchelper.objects">这一点应该不会使你感到吃惊</link>)。一个文件对象有几个有用的属性。</para>  
    945 945 </callout>  
    946 946 <callout arearefs="fileinfo.files.1.3">  
     
    978 978 </callout>  
    979 979 <callout arearefs="fileinfo.files.2.2">  
    980   <para>文件对象的 &seek; 方法在打开文件中移动到另一个位置。第二个参数指出第一个参数是什么意思:&zero; 表示移动到一个绝对位置 (从文件开始算起),&one; 表示移到一个相对位置 (从当前位置算起),还有 <literal>2</literal>  表示对于文件尾的一个相对位置。因为我们搜索的 &mp3; 标记保存在文件的末尾,我们使用 <literal>2</literal>  并且告诉文件对象从文件尾移动到 <literal>128</literal> 字节的位置。</para>  
      980 <para>文件对象的 &seek; 方法在打开文件中移动到另一个位置。第二个参数指出第一个参数是什么意思:&zero; 表示移动到一个绝对位置 (从文件开始算起),&one; 表示移到一个相对位置 (从当前位置算起),还有 <literal>2</literal>  表示对于文件尾的一个相对位置。因为我们搜索的 &mp3; 标记保存在文件的末尾,我们使用 <literal>2</literal>  并且告诉文件对象从文件尾移动到 <literal>128</literal> 字节的位置。</para>  
    980 980 </callout>  
    981 981 <callout arearefs="fileinfo.files.2.3">  
     
    984 984 </callout>  
    985 985 <callout arearefs="fileinfo.files.2.4">  
    986   <para> &read;  方法从打开文件中读取指定个数的字节,并且返回含有读取数据的字符串。可选参数指定了读取的最大字节数。如果没有指定参数,&read; 将读到文件末尾。(我们本可以在这里简单地说一下  <literal>read()</literal> ,因为我们确切地知道在文件的何处,事实上,我们读的是最后 128 个字节。) 读出的数据赋给变量 <varname>tagData</varname>,并且当前的位置根据所读的字节数作了修改。</para>  
      986 <para> &read;  方法从打开文件中读取指定个数的字节,并且返回含有读取数据的字符串。可选参数指定了读取的最大字节数。如果没有指定参数,&read; 将读到文件末尾。(我们本可以在这里简单地说一下  <literal>read()</literal> ,因为我们确切地知道在文件的何处,事实上,我们读的是最后 128 个字节。) 读出的数据赋给变量 <varname>tagData</varname>,并且当前的位置根据所读的字节数作了修改。</para>  
    986 986 </callout>  
    987 987 <callout arearefs="fileinfo.files.2.5">  
     
    1019 1019 <calloutlist>  
    1020 1020 <callout arearefs="fileinfo.files.3.1">  
    1021   <para>文件对象的 <varname>closed</varname> 属性表示对象是否打开或关闭了文件。在本例中,文件仍然打开着 (<varname>closed</varname> 是 &false;)。打开文件要消耗系统资源,并且根据文件模式,其它程序可能不能使用它们。一旦你处理完它们,把文件关闭这一点很重要。</para>  
      1021 <para>文件对象的 <varname>closed</varname> 属性表示对象是否打开或关闭了文件。在本例中,文件仍然打开着 (<varname>closed</varname> 是 &false;)。打开文件要消耗系统资源,并且根据文件模式,其它程序可能不能使用它们。一旦你处理完它们,把文件关闭这一点很重要。</para>  
    1021 1021 </callout>  
    1022 1022 <callout arearefs="fileinfo.files.3.2">  
    1023   <para>为了关闭文件,调用文件对象的 &close; 方法。这样就释放掉你加在文件上的锁 (如果有的话),刷新被缓冲的系统确实还未写入的输出 (如果有的话),并且释放系统资源。</para>  
      1023 <para>为了关闭文件,调用文件对象的 &close; 方法。这样就释放掉你加在文件上的锁 (如果有的话),刷新被缓冲的系统确实还未写入的输出 (如果有的话),并且释放系统资源。</para>  
    1023 1023 </callout>  
    1024 1024 <callout arearefs="fileinfo.files.3.3">  
     
    1056 1056 <calloutlist>  
    1057 1057 <callout arearefs="fileinfo.files.4.1">  
    1058   <para>因为打开和读取文件有风险,并且可能引发异常,所有这些代码都用一个 &tryexcept; 块封装。(嘿,<link linkend="odbchelper.indenting">标准化的缩近</link> 不好吗?这就是你开始欣赏它的地方。)</para>  
      1058 <para>因为打开和读取文件有风险,并且可能引发异常,所有这些代码都用一个 &tryexcept; 块封装。(嘿,<link linkend="odbchelper.indenting">标准化的缩近</link> 不好吗?这就是你开始欣赏它的地方。)</para>  
    1058 1058 </callout>  
    1059 1059 <callout arearefs="fileinfo.files.4.2">  
    1060   <para>&open; 函数可能引发 &ioerror; 异常。(可能是文件不存在。)</para>  
      1060 <para>&open; 函数可能引发 &ioerror; 异常。(可能是文件不存在。)</para>  
    1060 1060 </callout>  
    1061 1061 <callout arearefs="fileinfo.files.4.3">  
    1062   <para>&seek; 方法可能引发 &ioerror; 异常。(可能是文件长度小于 128 字节。)</para>  
      1062 <para>&seek; 方法可能引发 &ioerror; 异常。(可能是文件长度小于 128 字节。)</para>  
    1062 1062 </callout>  
    1063 1063 <callout arearefs="fileinfo.files.4.4">  
    1064   <para>&read; 方法可能引发 &ioerror; 异常。(可能磁盘有坏扇区,或它在一个网络驱劝器上,而网络刚好断了。)</para>  
      1064 <para>&read; 方法可能引发 &ioerror; 异常。(可能磁盘有坏扇区,或它在一个网络驱动器上,而网络刚好断了。)</para>  
    1064 1064 </callout>  
    1065 1065 <callout arearefs="fileinfo.files.4.5">  
     
    1071 1071 </callout>  
    1072 1072 <callout arearefs="fileinfo.files.4.6">  
    1073   <para>最后,处理我们的 &ioerror; 异常。它可能是由调用 &open;, &seek; 或 &read; 引发的 &ioerror; 异常。这里,我们其实不用关心,因为将要做的事就是静静地忽略它然后继续。(记住,&pass; 是一条不做任何事的 &python; 语句。) 这样完全合法,<quote>处理</quote> 一个异常可以明确表示不做任何事。它仍然被认为处理过了,并且处理将正常继续,从 &tryexcept; 块的下一行代码。</para>  
      1073 <para>最后,处理我们的 &ioerror; 异常。它可能是由调用 &open;, &seek; 或 &read; 引发的 &ioerror; 异常。这里,我们其实不用关心,因为将要做的事就是静静地忽略它然后继续。(记住,&pass; 是一条不做任何事的 &python; 语句。) 这样完全合法,<quote>处理</quote> 一个异常可以明确表示不做任何事。它仍然被认为处理过了,并且处理将正常继续,从 &tryexcept; 块的下一行代码。</para>  
    1073 1073 </callout>  
    1074 1074 </calloutlist>  
     
    1100 1100 <calloutlist>  
    1101 1101 <callout arearefs="fileinfo.files.5.1">  
    1102   <para>你可以大胆地开始创建任何的新文件 <filename>test.log</filename> 或覆盖现有文件, 为写入目的而打开它。  (第二个参数 <literal>"w"</literal> 的意思是为文件写入而打开。)  是的,它和想象中的一样危险。 我希望你不必关心文件以前的内容, 因为它现在已经不存在了。</para>  
      1102 <para>你可以大胆地开始创建任何的新文件 <filename>test.log</filename> 或覆盖现有文件, 为写入目的而打开它。  (第二个参数 <literal>"w"</literal> 的意思是为文件写入而打开。)  是的,它和想象中的一样危险。 我希望你不必关心文件以前的内容, 因为它现在已经不存在了。</para>  
    1102 1102 </callout>  
    1103 1103 <callout arearefs="fileinfo.files.5.2">  
     
    1109 1109 </callout>  
    1110 1110 <callout arearefs="fileinfo.files.5.4">  
    1111   <para>碰巧你知道 <filename>test.log</filename> 存在 (因为你刚向它写完了数据), 所以你可以打开它并向其追加数据。  (<literal>"a"</literal> 参数的意思是为追加目的打开文件。)  实际上即使文件不存在你也可以这样做, 因为以追加方式打开一文件时, 如果需要的话会创建文件。  但是追加操作 <emphasis>从不</emphasis> 损坏文件的现有内容。</para>  
      1111 <para>碰巧你知道 <filename>test.log</filename> 存在 (因为你刚向它写完了数据), 所以你可以打开它并向其追加数据。  (<literal>"a"</literal> 参数的意思是为追加目的打开文件。)  实际上即使文件不存在你也可以这样做, 因为以追加方式打开一文件时, 如果需要的话会创建文件。  但是追加操作 <emphasis>从不</emphasis> 损坏文件的现有内容。</para>  
    1111 1111 </callout>  
    1112 1112 <callout arearefs="fileinfo.files.5.5">  
    1113   <para>正如你所看到的, 原来的行和你以追加方式写入的第二行现在都在 <filename>test.log</filename>中了。  同时注意回车符并没包含进来。 因为两次写入文件时都没有明确地写入回车符, 所以文件中没有包含回车符。  你可以用 <literal>"\n"</literal> 写入回车符。  因为你没做这项工作, 所以你写到文件的所有内容都将显示在同一行上。</para>  
      1113 <para>正如你所看到的, 原来的行和你以追加方式写入的第二行现在都在 <filename>test.log</filename> 中了。  同时注意回车符并没包含进来。 因为两次写入文件时都没有明确地写入回车符, 所以文件中没有包含回车符。  你可以用 <literal>"\n"</literal> 写入回车符。  因为你没做这项工作, 所以你写到文件的所有内容都将显示在同一行上。</para>  
    1113 1113 </callout>  
    1114 1114 </calloutlist>  
     
    1132 1132 <para>与其它大多数语言一样,&python; 也拥有 &for; 循环。你到现在还未曾看到它们的唯一原因就是,&python; 在其它太多的方面表现出色,通常你不需要它们。</para>  
    1133 1133 </abstract>  
    1134   <para>其它大多数语言没有象 &python; 一样的强大的 list 数据类型,所以你需要亲自做很多事情,指定开始,结束和步长,来定义一定范围的整数或字符或其它可重复的实体。但是在 &python; 中,&for;  循环简单地在一个列表上循环,与  <link linkend="odbchelper.map">list 理解</link> 的工作方式相同。</para>  
      1134 <para>其它大多数语言没有象 &python; 一样的强大的 list 数据类型,所以你需要亲自做很多事情,指定开始,结束和步长,来定义一定范围的整数或字符或其它可重复的实体。但是在 &python; 中,&for;  循环简单地在一个列表上循环,与  <link linkend="odbchelper.map">list 解析</link> 的工作方式相同。</para>  
    1134 1134 <example>  
    1135 1135 <title>&for; 循环介绍</title>  
     
    1147 1147 <calloutlist>  
    1148 1148 <callout arearefs="fileinfo.for.1.1">  
    1149   <para>&for; 循环的语法同 <link linkend="odbchelper.map">list 理解</link> 相似。<varname>li</varname> 是一个 list,而 <varname>s</varname> 将从第一个元素开始依次接收每个元素的值。</para>  
      1149 <para>&for; 循环的语法同 <link linkend="odbchelper.map">list 解析</link> 相似。<varname>li</varname> 是一个 list,而 <varname>s</varname> 将从第一个元素开始依次接收每个元素的值。</para>  
    1149 1149 </callout>  
    1150 1150 <callout arearefs="fileinfo.for.1.2">  
     
    1153 1153 </callout>  
    1154 1154 <callout arearefs="fileinfo.for.1.3">  
    1155   <para>这就是为什么你以前没看到过 &for; 循环的原因:至今我们都不需要它。太令人吃惊了,当你想要的只是一个 &join; 或是 list 理解时,而用其它语言常常需要使用 &for; 循环。</para>  
      1155 <para>这就是为什么你以前没看到过 &for; 循环的原因:至今我们都不需要它。太令人吃惊了,当你想要的只是一个 &join; 或是 list 解析时,而用其它语言常常需要使用 &for; 循环。</para>  
    1155 1155 </callout>  
    1156 1156 </calloutlist>  
    1157 1157 </example>  
    1158   <para>要做一个 <quote>通常的</quote> (&vb; 标准的) 计数 &for; 循环也非常简单。</para>  
      1158 <para>要做一个 <quote>通常的</quote> (&vb; 标准的) 计数 &for; 循环也非常简单。</para>  
    1158 1158 <example id="fileinfo.for.counter">  
    1159 1159 <title>简单计数</title>  
     
    1179 1179 <calloutlist>  
    1180 1180 <callout arearefs="fileinfo.for.3.1">  
    1181   <para>正如你在 <xref linkend="odbchelper.multiassign.range"/> 所看到的, &range; 生成一个整数的 list, 通过它来控制循环。  我知道它看上去有些奇怪, 但是它对计数循环偶尔 (我只是说 <emphasis>偶尔</emphasis>) 会有用 。</para>  
      1181 <para>正如你在 <xref linkend="odbchelper.multiassign.range"/> 所看到的,&range; 生成一个整数的 list, 通过它来控制循环。  我知道它看上去有些奇怪, 但是它对计数循环偶尔 (我只是说 <emphasis>偶尔</emphasis>) 会有用 。</para>  
    1181 1181 </callout>  
    1182 1182 <callout arearefs="fileinfo.for.3.2">  
    1183   <para>我们从来没这么用过。 这是 &vb; 的思维风格。摆脱它吧。 正确遍历 list 的方法是上一个例子所展示的。</para>  
      1183 <para>我们从来没这么用过。 这是 &vb; 的思维风格。摆脱它吧。 正确遍历 list 的方法是前面的例子所展示的。</para>  
    1183 1183 </callout>  
    1184 1184 </calloutlist>  
     
    1215 1215 </callout>  
    1216 1216 <callout arearefs="fileinfo.for.2.3">  
    1217   <para>使用 <link linkend="odbchelper.multiassign">多变量赋值</link> 和 <link linkend="odbchelper.map">list 理解</link>,你可以使用单行语句来替换整个 &for; 循环。在实际的编码中是否这样做只是个人风格问题;我喜欢它是因为,可以使得将一个 dictionary 映射到一个 list,然后将 list 合并成一个字符串,这一过程显得很清晰。其它的程序员宁愿将其写成一个 &for; 循环。请注意在两种情况下输出是一样的,然而这一版本稍微快一些,因为它只有一条 &print; 语句而不是许多。</para>  
      1217 <para>使用 <link linkend="odbchelper.multiassign">多变量赋值</link> 和 <link linkend="odbchelper.map">list 解析</link>,你可以使用单行语句来替换整个 &for; 循环。在实际的编码中是否这样做只是个人风格问题;我喜欢它是因为,可以使得将一个 dictionary 映射到一个 list,然后将 list 合并成一个字符串,这一过程显得很清晰。其它的程序员宁愿将其写成一个 &for; 循环。请注意在两种情况下输出是一样的,然而这一版本稍微快一些,因为它只有一条 &print; 语句而不是许多。</para>  
    1217 1217 </callout>  
    1218 1218 </calloutlist>  
     
    1248 1248 <abstract>  
    1249 1249 <title/>  
    1250   <para>与其它任何 &python; 的东西一样, 模块也是对象。 一旦导入,总是可以全局 dictionary &sysmodules; 来得到一个模块的引用。</para>  
      1250 <para>与其它任何 &python; 的东西一样, 模块也是对象。 一旦导入,总可以用全局 dictionary &sysmodules; 来得到一个模块的引用。</para>  
    1250 1250 </abstract>  
    1251 1251 <example>  
     
    1269 1269 <calloutlist>  
    1270 1270 <callout arearefs="fileinfo.modules.1.1">  
    1271   <para>这个 &sys; 模块包含了系统级的信息,象正在运行的 &python; 的版本 (<literal>&sys;.version</literal> 或 <literal>&sys;.version_info</literal>),和系统级选项,象最大允许递归的深度 (<literal>&sys;.getrecursionlimit()</literal> 和 <literal>&sys;.setrecursionlimit()</literal>)。</para>  
      1271 <para>这个 &sys; 模块包含了系统级的信息,象正在运行的 &python; 的版本 (<literal>&sys;.version</literal> 或 <literal>&sys;.version_info</literal>),和系统级选项,象最大允许递归的深度 (<literal>&sys;.getrecursionlimit()</literal> 和 <literal>&sys;.setrecursionlimit()</literal>)。</para>  
    1271 1271 </callout>  
    1272 1272 <callout arearefs="fileinfo.modules.1.2">  
    1273   <para>&sysmodules; 是一个字典,它包含了从 &python; 开始运行起被导入的所有模块。键字就是模块名,键值就是模块对象。请注意除了你的程序导入的模块外还有其它模块。&python; 在启动时预先装入了一些模块,如果你在一个 &python; &ide; 环境下,&sysmodules; 包含了你在 &ide; 中运行的所有程序所导入的所有模块。</para>  
      1273 <para>&sysmodules; 是一个字典,它包含了从 &python; 开始运行起被导入的所有模块。键字就是模块名,键值就是模块对象。请注意除了你的程序导入的模块外还有其它模块。&python; 在启动时预先装入了一些模块,如果你在一个 &python; &ide; 环境下,&sysmodules; 包含了你在 &ide; 中运行的所有程序所导入的所有模块。</para>  
    1273 1273 </callout>  
    1274 1274 </calloutlist>  
    1275 1275 </example>  
    1276   <para>这个例子展示了如何使用 &sysmodules;。</para>  
      1276 <para>下面的例子展示了如何使用 &sysmodules;。</para>  
    1276 1276 <example>  
    1277 1277 <title>使用 &sysmodules;</title>  
     
    1318 1318 <calloutlist>  
    1319 1319 <callout arearefs="fileinfo.modules.2.1">  
    1320   <para>每个 &python; 类拥有一个内置的 <link linkend="fileinfo.classattributes">类属性</link>  &moduleattr;,它定义了这个类的模块的名字。</para>  
      1320 <para>每个 &python; 类拥有一个内置的 <link linkend="fileinfo.classattributes">类属性</link>  &moduleattr;,它定义了这个类的模块的名字。</para>  
    1320 1320 </callout>  
    1321 1321 <callout arearefs="fileinfo.modules.2.2">  
     
    1338 1338 </callout>  
    1339 1339 <callout arearefs="fileinfo.modules.3.2">  
    1340   <para>我们会在后面再仔细研究这一行,在我们了解了 &os; 模块之后。那么现在,只要信 <varname>subclass</varname> 最终为一个类的名字就行了,象 <classname>MP3FileInfo</classname>。</para>  
      1340 <para>我们会在后面再仔细研究这一行,在我们了解了 &os; 模块之后。那么现在,只要信 <varname>subclass</varname> 最终为一个类的名字就行了,象 <classname>MP3FileInfo</classname>。</para>  
    1340 1340 </callout>  
    1341 1341 <callout arearefs="fileinfo.modules.3.3">  
    1342   <para>你已经了解了 <link linkend="apihelper.getattr">&getattr;</link>,它可以通过名字得到一个对象的引用。&hasattr; 是一个补充性的函数,用来检查是否一个对象具有一个特别的属性;在本例中,用来检查一个模块是否有一个特别的类 (然而它可以用于任何类和任何属性,就象 &getattr;)。用英语来说,这行代码是说, <quote>If this module has the class named by <varname>subclass</varname> then return it, otherwise return the base class <classname>FileInfo</classname> (如果这个模块有一个名为 <varname>subclass</varname> 的类,那么返回它,否则返回基类 <classname>FileInfo</classname>)</quote>。</para>  
      1342 <para>你已经了解了 <link linkend="apihelper.getattr">&getattr;</link>,它可以通过名字得到一个对象的引用。&hasattr; 是一个补充性的函数,用来检查是否一个对象具有一个特别的属性;在本例中,用来检查一个模块是否有一个特别的类 (然而它可以用于任何类和任何属性,就象 &getattr;)。用英语来说,这行代码是说, <quote>If this module has the class named by <varname>subclass</varname> then return it, otherwise return the base class <classname>FileInfo</classname> (如果这个模块有一个名为 <varname>subclass</varname> 的类,那么返回它,否则返回基类 <classname>FileInfo</classname>)</quote>。</para>  
    1342 1342 </callout>  
    1343 1343 </calloutlist>  
     
    1375 1375 </callout>  
    1376 1376 <callout arearefs="fileinfo.os.1.2">  
    1377   <para>&ospath; 的 &join; 函数用一个或多个部分路径名构造成一个路径名。在这个简单的例子中,它只是将字符串进行连接。 (请注意在 &windows; 下处理路径名是一个麻烦的事,因为反斜线字符必须被转义。)</para>  
      1377 <para>&ospath; 的 &join; 函数用一个或多个部分路径名构造成一个路径名。在这个简单的例子中,它只是将字符串进行连接。 (请注意在 &windows; 下处理路径名是一个麻烦的事,因为反斜线字符必须被转义。)</para>  
    1377 1377 </callout>  
    1378 1378 <callout arearefs="fileinfo.os.1.3">  
     
    1416 1416 </callout>  
    1417 1417 <callout arearefs="fileinfo.os.2.5">  
    1418   <para>&ospath; 也包含了一个 &splitext; 函数,可以用来对文件名进行分割,并且回一个包含了文件名和文件扩展名的 tuple。我们使用相同的技术来将它们赋值给独立的变量。</para>  
      1418 <para>&ospath; 也包含了一个 &splitext; 函数,可以用来对文件名进行分割,并且回一个包含了文件名和文件扩展名的 tuple。我们使用相同的技术来将它们赋值给独立的变量。</para>  
    1418 1418 </callout>  
    1419 1419 </calloutlist>  
     
    1450 1450 </callout>  
    1451 1451 <callout arearefs="fileinfo.os.3.3">  
    1452   <para>你可以使用 <link linkend="apihelper.filter">list 过滤</link> 和 &ospath; 模块的 <function>isfile</function> 函数,从文件夹中将文件分离出来。&isfile;  接收一个路径名,如果路径表示一个文件,则返回 1,否则为 0。在这里,我们使用 <literal>&ospath;.&join;</literal> 来确保一个全路径名,但 &isfile;  对一个相对于当前工作目录的部分路径也是有效的。你可以使用 <literal>os.getcwd()</literal>  来得到当前的工作目录。</para>  
      1452 <para>你可以使用 <link linkend="apihelper.filter">过滤列表</link> 和 &ospath; 模块的 <function>isfile</function> 函数,从文件夹中将文件分离出来。&isfile;  接收一个路径名,如果路径表示一个文件,则返回 1,否则为 0。在这里,我们使用 <literal>&ospath;.&join;</literal> 来确保一个全路径名,但 &isfile;  对一个相对于当前工作目录的部分路径也是有效的。你可以使用 <literal>os.getcwd()</literal>  来得到当前的工作目录。</para>  
    1452 1452 </callout>  
    1453 1453 <callout arearefs="fileinfo.os.3.4">  
     
    1475 1475 </callout>  
    1476 1476 <callout arearefs="fileinfo.os.3a.4">  
    1477   <para>对每个文件,我们查看是否扩展名在我们关心的文件扩展名 list 中 (<varname>fileExtList</varname>,被传递给 <function>listDirectory</function> 函数)。</para>  
      1477 <para>对每个文件,我们查看是否扩展名在我们关心的文件扩展名 list 中 (<varname>fileExtList</varname>,被传递给 <function>listDirectory</function> 函数)。</para>  
    1477 1477 </callout>  
    1478 1478 <callout arearefs="fileinfo.os.3a.5">  
     
    1519 1519 </callout>  
    1520 1520 <callout arearefs="fileinfo.os.4.4">  
    1521   <para>现在考查这种情况: 你有一个 <filename>music</filename> 目录, 它包含几个子目录,  子目录中包含一些 <filename>.mp3</filename> 文件。 你可以用两个通配符仅仅调用 &glob; 一次立刻获得所有这些文件的一个 list。  一个通配符是 <literal>"*.mp3"</literal> (用于匹配 <filename>.mp3</filename> 文件), 另一个通配符是 <emphasis>子目录名本身</emphasis>, 用于匹配 <filename>c:\music</filename> 中的所有子目录。 这看上去很简单, 但他蕴含了强大的功能。</para>  
      1521 <para>现在考查这种情况: 你有一个 <filename>music</filename> 目录, 它包含几个子目录,  子目录中包含一些 <filename>.mp3</filename> 文件。 你可以用两个通配符仅仅调用 &glob; 一次立刻获得所有这些文件的一个 list。  一个通配符是 <literal>"*.mp3"</literal> (用于匹配 <filename>.mp3</filename> 文件), 另一个通配符是 <emphasis>子目录名本身</emphasis>, 用于匹配 <filename>c:\music</filename> 中的所有子目录。 这看上去很简单, 但他蕴含了强大的功能。</para>  
    1521 1521 </callout>  
    1522 1522 </calloutlist>  
     
    1550 1550 <calloutlist>  
    1551 1551 <callout arearefs="fileinfo.alltogether.1.1">  
    1552   <para>&listdirectory; 是整个模块主要的引吸之处。它接收一个 dictionary (在我的例子中如 <filename class="directory">c:\music\_singles\</filename>) 和一个感兴趣的文件扩展名列表 (如 <literal>['.mp3']</literal>),接着它返回一个类实例的 list ,这些类实例的行为象 dictionary,包含了在目录中每个感兴趣文件的元数据。并且实现起来只用了几行直观的代码。</para>  
      1552 <para>&listdirectory; 是整个模块主要的引吸之处。它接收一个 dictionary (在我的例子中如 <filename class="directory">c:\music\_singles\</filename>) 和一个感兴趣的文件扩展名列表 (如 <literal>['.mp3']</literal>),接着它返回一个类实例的 list ,这些类实例的行为象 dictionary,包含了在目录中每个感兴趣文件的元数据。并且实现起来只用了几行直观的代码。</para>  
    1552 1552 </callout>  
    1553 1553 <callout arearefs="fileinfo.alltogether.1.2">  
    1554   <para>正如在 <link linkend="fileinfo.os">前一节</link> 我们所看到的,这行代码得到一个全路径名的列表,它们是在 <varname>directory</varname> 中有着我们感兴趣的文件后缀 (由 <varname>fileExtList</varname> 所指定的) 的所有文件的路径名。</para>  
      1554 <para>正如在 <link linkend="fileinfo.os">前一节</link> 我们所看到的,这行代码得到一个全路径名的列表,它们是在 <varname>directory</varname> 中有着我们感兴趣的文件后缀 (由 <varname>fileExtList</varname> 所指定的) 的所有文件的路径名。</para>  
    1554 1554 </callout>  
    1555 1555 <callout arearefs="fileinfo.alltogether.1.3">  
    1565 1565 </callout>  
    1566 1566 <callout arearefs="fileinfo.alltogether.1.6">  
    1567   <para>对每个属于我们 <quote>感兴趣文件</quote> 列表 (<varname>fileList</varname>)中的文件,我们用文件名 (<varname>f</varname>) 来调用 <function>getFileInfoClass</function>。调用 <literal>getFileInfoClass(f)</literal> 返回一个类;我们并不知道确切是哪一个类,但是我们并不关心。接着我们创建这个类 (不管它是什么) 的一个实例,传入文件名 (又是 <varname>f</varname>) 给的 &init; 方法。正如我们在 <link linkend="fileinfo.specialmethods.setname">本章的前面</link> 所看到的,&fileinfo_classname; 的 &init; 方法设置了 <literal>self["name"]</literal>,它将引发 &setitem; 的调用,&setitem; 在子类 (&mp3fileinfo_classname;) 中被覆盖掉了,用来适当地对文件进行分析,取出文件的元数据。我们对所有感兴趣的文件进行处理,返回结果实例的一个 list。</para>  
      1567 <para>对每个属于我们 <quote>感兴趣文件</quote> 列表 (<varname>fileList</varname>)中的文件,我们用文件名 (<varname>f</varname>) 来调用 <function>getFileInfoClass</function>。调用 <literal>getFileInfoClass(f)</literal> 返回一个类;我们并不知道确切是哪一个类,但是我们并不关心。接着我们创建这个类 (不管它是什么) 的一个实例,传入文件名 (又是 <varname>f</varname>) 给的 &init; 方法。正如我们在 <link linkend="fileinfo.specialmethods.setname">本章的前面</link> 所看到的,&fileinfo_classname; 的 &init; 方法设置了 <literal>self["name"]</literal>,它将引发 &setitem; 的调用,&setitem; 在子类 (&mp3fileinfo_classname;) 中被覆盖掉了,用来适当地对文件进行分析,取出文件的元数据。我们对所有感兴趣的文件进行处理,返回结果实例的一个 list。</para>  
    1567 1567 </callout>  
    1568 1568 </calloutlist>  
    1569 1569 </example>  
    1570   <para>请注意 &listdirectory; 完全是通用的。它事先不知道将得到文件哪种类型,或者哪些定义好的类能够处理这些文件。它检查目录中要进行处理的文件,然后反观本身模块,了解定义了什么特别的处理类 (象 &mp3fileinfo_classname;)。你可以对这个程序进行扩充,对其它类型的文件进行处理,只要用适合的名字定义类:<classname>HTMLFileInfo</classname> 用于 <acronym>HTML</acronym> 文件,<classname>DOCFileInfo</classname> 用于 <application>Word</application> <literal>.doc</literal> 文件,等等。&listdirectory; 将会对它们都进行处理,不作改变,将工作交给适当的类,接着收集结果。</para>  
      1570 <para>请注意 &listdirectory; 完全是通用的。它事先不知道将得到文件哪种类型,或者哪些定义好的类能够处理这些文件。它检查目录中要进行处理的文件,然后反观本身模块,了解定义了什么特别的处理类 (象 &mp3fileinfo_classname;)。你可以对这个程序进行扩充,对其它类型的文件进行处理,只要用适合的名字定义类:<classname>HTMLFileInfo</classname> 用于 <acronym>HTML</acronym> 文件,<classname>DOCFileInfo</classname> 用于 <application>Word</application> <literal>.doc</literal> 文件,等等。&listdirectory; 将会对它们都进行处理,不作改变,将工作交给适当的类,接着收集结果。</para>  
    1570 1570 </section>  
    1571 1571 <section id="fileinfo.summary2">  
  • zh-translations/branches/diveintopython-zh-5.4/zh-cn/xml/odbchelper.xml

    r889 r948  
    999 999 <abstract>  
    1000 1000 <title></title>  
    1001   <para>&python; 的强大特性之一是其对 list 的理解, 它提供一种紧凑的方法, 可以通过对 list 中的每个元素应用一个函数, 从而将一个 list 映射为另一个 list。</para>  
      1001 <para>&python; 的强大特性之一是其对 list 的解析, 它提供一种紧凑的方法, 可以通过对 list 中的每个元素应用一个函数, 从而将一个 list 映射为另一个 list。</para>  
    1001 1001 </abstract>  
    1002 1002 <example>  
    1003   <title>List 理解介绍</title>  
      1003 <title>List 解析介绍</title>  
    1003 1003 <screen>&prompt;<userinput>li = [1, 9, 8, 4]</userinput>  
    1004 1004 &prompt;<userinput>[elem*2 for elem in li]</userinput>      <co id="odbchelper.map.1.1"/>  
     
    1016 1016 </callout>  
    1017 1017 <callout arearefs="odbchelper.map.1.2">  
    1018   <para>需要注意是, 对 list 的理解并不改变原始的 list。</para>  
      1018 <para>需要注意是, 对 list 的解析并不改变原始的 list。</para>  
    1018 1018 </callout>  
    1019 1019 <callout arearefs="odbchelper.map.1.3">  
    1020   <para>将一个 list 的理解结果赋值给对其映射的变量是安全的。不用担心存在竞争情况或任何古怪事情的发生。&python; 会在内存中创建新的 list, 当对list 的理解完成时, &python; 将结果赋给变量。</para>  
      1020 <para>将一个 list 的解析结果赋值给对其映射的变量是安全的。不用担心存在竞争情况或任何古怪事情的发生。&python; 会在内存中创建新的 list, 当对 list 的解析完成时, &python; 将结果赋给变量。</para>  
    1020 1020 </callout>  
    1021 1021 </calloutlist>  
     
    1025 1025 <informalexample>  
    1026 1026 <!--<title>List Comprehensions in &odbchelper_function;</title>-->  
    1027   <para>声明位于 <xref linkend="odbchelper" endterm="odbchelper.numberonly"/> 的函数 &odbchelper_function; 对 list 的理解:</para>  
      1027 <para>声明位于 <xref linkend="odbchelper" endterm="odbchelper.numberonly"/> 的函数 &odbchelper_function; 对 list 的解析:</para>  
    1027 1027 <programlisting>  
    1028 1028 &odbchelper_listmapping;</programlisting>  
     
    1053 1053 <para>现在让我们看一看 &odbchelper_function; 做了些什么。它接收一个 list, <literal><varname>params</varname>.&items;()</literal>, 通过对每个元素应用字符串格式化将其映射为一个新 list。这个新 list 将拥有与 <literal><varname>params</varname>.&items;()</literal> 相同的元素数量, 在新 list 中的每个元素都将包含从 dictionary <varname>params</varname> 来的一个键和与其关联值的字符串。</para>  
    1054 1054 <example>  
    1055   <title>&odbchelper_function; 中的 list 理解</title>  
      1055 <title>&odbchelper_function; 中的 list 解析</title>  
    1055 1055 <screen>&prompt;<userinput>params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}</userinput>  
    1056 1056 &prompt;<userinput>params.items()</userinput>  
     
    1065 1065 <calloutlist>  
    1066 1066 <callout arearefs="odbchelper.map.3.1">  
    1067   <para>请注意我们正在使用两个变量对 list <literal>params.items()</literal> 进行遍历。这是 <link linkend="odbchelper.multiassign">多变量赋值</link> 的另一种用法。<literal>params.items()</literal> 的第一个元素是 <literal>('server', 'mpilgrim')</literal>, 所以在 list 理解的第一次遍历中, <varname>k</varname> 将为 <literal>'server'</literal>,  <varname>v</varname> 将为 <literal>'mpilgrim'</literal>。在本例中, 我们忽略了返回 list 中 <varname>v</varname> 的值, 而只包含了 <varname>k</varname> 的值, 所以这个 list 理解最后等于 <literal><varname>params</varname>.<function>keys</function>()</literal>。</para>  
      1067 <para>请注意我们正在使用两个变量对 list <literal>params.items()</literal> 进行遍历。这是 <link linkend="odbchelper.multiassign">多变量赋值</link> 的另一种用法。<literal>params.items()</literal> 的第一个元素是 <literal>('server', 'mpilgrim')</literal>, 所以在 list 解析的第一次遍历中, <varname>k</varname> 将为 <literal>'server'</literal>,  <varname>v</varname> 将为 <literal>'mpilgrim'</literal>。在本例中, 我们忽略了返回 list 中 <varname>v</varname> 的值, 而只包含了 <varname>k</varname> 的值, 所以这个 list 解析最后等于 <literal><varname>params</varname>.<function>keys</function>()</literal>。</para>  
    1067 1067 </callout>  
    1068 1068 <callout arearefs="odbchelper.map.3.2">  
    1069   <para>这里我们做着相同的事情, 但是忽略了 <varname>k</varname> 的值, 所以这个 list 理解最后等于 <literal><varname>params</varname>.<function>values</function>()</literal>。</para>  
      1069 <para>这里我们做着相同的事情, 但是忽略了 <varname>k</varname> 的值, 所以这个 list 解析最后等于 <literal><varname>params</varname>.<function>values</function>()</literal>。</para>  
    1069 1069 </callout>  
    1070 1070 <callout arearefs="odbchelper.map.3.3">  
    1071   <para>用一些简单的 <link linkend="odbchelper.stringformatting">字符串格式化</link> 将前面两个例子合并起来 , 我们就得到一个包括了 dictionary 中每个元素的 key- value 对的 list。这个看上去有点象程序的 <link linkend="odbchelper.output">输出结果</link> , 剩下的就只是将这个 list 中的元素接起来形成一个字符串了。</para>  
      1071 <para>用一些简单的 <link linkend="odbchelper.stringformatting">字符串格式化</link> 将前面两个例子合并起来 , 我们就得到一个包括了 dictionary 中每个元素的 key-value 对的 list。这个看上去有点象程序的 <link linkend="odbchelper.output">输出结果</link>, 剩下的就只是将这个 list 中的元素接起来形成一个字符串了。</para>  
    1071 1071 </callout>  
    1072 1072 </calloutlist>  
     
    1078 1078 <title>进一步阅读</title>  
    1079 1079 <listitem><para>&pythontutorial; 讨论了另一种方法来映射 list <ulink url="&url_pythontutorial;node7.html#SECTION007130000000000000000">使用内置的 <function>map</function> 函数</ulink>。</para></listitem>  
    1080   <listitem><para>&pythontutorial; 展示了如何 <ulink url="&url_pythontutorial;node7.html#SECTION007140000000000000000">嵌套 list 的 list 理解</ulink>。</para></listitem>  
      1080 <listitem><para>&pythontutorial; 展示了如何 <ulink url="&url_pythontutorial;node7.html#SECTION007140000000000000000">嵌套 list 的 list 解析</ulink>。</para></listitem>  
    1080 1080 </itemizedlist>  
    1081 1081 </section>  
    1174 1174 <listitem><para><link linkend="odbchelper.objects">任意一个对象</link> 的访问方法, 包括: 字符串, list, dictionary, 函数和模块</para></listitem>  
    1175 1175 <listitem><para>通过 <link linkend="odbchelper.stringformatting">字符串格式化</link> 连接值</para></listitem>  
    1176   <listitem><para>使用对 list 理解 <link linkend="odbchelper.map">映射 list</link> 为其他的 list</para></listitem>  
      1176 <listitem><para>使用 list 解析 <link linkend="odbchelper.map">映射 list</link> 为其他的 list</para></listitem>  
    1176 1176 <listitem><para><link linkend="odbchelper.join">分割字符串</link> 成为 list 和 连接 list 成为字符串</para></listitem>  
    1177 1177 </itemizedlist>  
  • zh-translations/branches/diveintopython-zh-5.4/zh-cn/xml/apihelper.xml

    r913 r948  
    6 6 <abstract>  
    7 7 <title/>  
    8   <para>本章论述了 &python; 众多强大功能之一:自省。正如你所知道的,<link linkend="odbchelper.objects"> &python; 中任何东西都是对象</link>,自省是指代码可以查看内存中以对象形式存在的其它模块和函数,获取它们的信息,并对它们进行操作。用这种方法, 你可以定义没有名称的函数,不按函数声明的参数顺序调用函数,甚至引用事先并不知道名称的函数。</para>  
      8 <para>本章论述了 &python; 众多强大功能之一:自省。正如你所知道的,<link linkend="odbchelper.objects">&python; 中任何东西都是对象</link>,自省是指代码可以查看内存中以对象形式存在的其它模块和函数,获取它们的信息,并对它们进行操作。用这种方法, 你可以定义没有名称的函数,不按函数声明的参数顺序调用函数,甚至引用事先并不知道名称的函数。</para>  
    8 8 </abstract>  
    9 9 <section id="apihelper.divein">  
     
    12 12 <abstract>  
    13 13 <title/>  
    14   <para>下面是一个完整可运行的 &python; 程序。大概看一下这段程序,你应该可以理解不少了。用数字标出的行阐述了<xref linkend="odbchelper"/>中涉及的一些概念。如果剩下来的代码看起来有点奇怪,不用担心,通过阅读本章你将会理解所有这些。</para>  
      14 <para>下面是一个完整可运行的 &python; 程序。大概看一下这段程序,你应该可以理解不少了。用数字标出的行阐述了 <xref linkend="odbchelper"/> 中涉及的一些概念。如果剩下来的代码看起来有点奇怪,不用担心,通过阅读本章你将会理解所有这些。</para>  
    14 14 </abstract>  
    15 15 <example>  
     
    30 30 <calloutlist>  
    31 31 <callout arearefs="apihelper.intro.1.1">  
    32   <para>该模块有一个声明为 &info; 的函数。根据它的<link linkend="odbchelper.funcdef">函数声明</link>可知,它有三个参数: <varname>object</varname>、<varname>spacing</varname>和<varname>collapse</varname>。实际上后面两个参数都是可选参数,关于这点你很快就会看到。</para>  
      32 <para>该模块有一个声明为 &info; 的函数。根据它的 <link linkend="odbchelper.funcdef">函数声明</link> 可知,它有三个参数: <varname>object</varname>、<varname>spacing</varname> 和 <varname>collapse</varname>。实际上后面两个参数都是可选参数,关于这点你很快就会看到。</para>  
    32 32 </callout>  
    33 33 <callout arearefs="apihelper.intro.1.2">  
    34   <para>&info;函数有一个多行的<link linkend="odbchelper.docstring">&docstring;</link>,简要地描述了函数的功能。注意这里并没有提到返回值;单独使用这个函数只是为了这个函数产生的效果,并不是为了它的返回值。</para>  
      34 <para>&info; 函数有一个多行的 <link linkend="odbchelper.docstring">&docstring;</link>,简要地描述了函数的功能。注意这里并没有提到返回值;单独使用这个函数只是为了这个函数产生的效果,并不是为了它的返回值。</para>  
    34 34 </callout>  
    35 35 <callout arearefs="apihelper.intro.1.3">  
     
    39 39 </callout>  
    40 40 <callout arearefs="apihelper.intro.1.4">  
    41   <para><literal>&if; &name;</literal> <link linkend="odbchelper.ifnametrick">技巧</link>允许这个程序在自己独立运行时做些有用的事情,同时又不妨碍作为其它程序的模块使用。 在这个例子中,程序只是简单地打印出&info; 函数的 &docstring;。</para>  
      41 <para><literal>&if; &name;</literal> <link linkend="odbchelper.ifnametrick">技巧</link>允许这个程序在自己独立运行时做些有用的事情,同时又不妨碍作为其它程序的模块使用。 在这个例子中,程序只是简单地打印出 &info; 函数的 &docstring;。</para>  
    41 41 </callout>  
    42 42 <callout arearefs="apihelper.intro.1.5">  
    43   <para><link linkend="odbchelper.ifnametrick">&if;语句</link> 使用 &comparisonequals; 进行比较, 而且不需要括号。</para>  
      43 <para><link linkend="odbchelper.ifnametrick">&if; 语句</link> 使用 &comparisonequals; 进行比较, 而且不需要括号。</para>  
    43 43 </callout>  
    44 44 </calloutlist>  
    45 45 </example>  
    46   <para>&info; 函数的设计意图是提供给工作在&python; &ide; 中的开发人员使用,它可以使用任何含有函数或者方法的对象(比如模块,含有函数,又比如list,含有方法)作为参数,并打印出对象的所有函数和它们的 &docstring;。</para>  
      46 <para>&info; 函数的设计意图是提供给工作在 &python; &ide; 中的开发人员使用,它可以使用任何含有函数或者方法的对象(比如模块,含有函数,又比如list,含有方法)作为参数,并打印出对象的所有函数和它们的 &docstring;。</para>  
    46 46 <example>  
    47 47 <title>&apihelper_filename; 的用法示例</title>  
    48 48 <screen>&apihelper_output;</screen>  
    49 49 </example>  
    50   <para>缺省地,程序输出进行了格式化处理易于阅读。多行 &docstring; 被合并到单行中,要改变这个选项需要指定<parameter>collapse</parameter>参数的值为<constant>0</constant>。如果函数名称长于10个字符,你可以将<parameter>spacing</parameter>参数的值指定为更大的值以使输出更容易阅读。</para>  
      50 <para>缺省地,程序输出进行了格式化处理易于阅读。多行 &docstring; 被合并到单行中,要改变这个选项需要指定 <parameter>collapse</parameter> 参数的值为 <constant>0</constant>。如果函数名称长于10个字符,你可以将 <parameter>spacing</parameter> 参数的值指定为更大的值以使输出更容易阅读。</para>  
    50 50 <example>  
    51 51 <title>&apihelper_filename; 的高级用法</title>  
     
    79 79 &apihelper_funcdef;</programlisting>  
    80 80 </informalexample>  
    81   <para><varname>spacing</varname> 和 <varname>collapse</varname> 是可选参数,因为它们已经定义了缺省值。<varname>object</varname> 是必备参数,因为它没有指定缺省值。如果调用&info; 时只指定一个参数,那么<varname>spacing</varname> 缺省为 <constant>10</constant> ,<varname>collapse</varname> 缺省为 &one;。如果调用 &info; 时指定两个参数,<varname>collapse</varname> 依然默认为 &one;。</para>  
    82   <para>假如你要指定<varname>collapse</varname>的值,但是又想要接受 <varname>spacing</varname>的缺省值。在绝大部分语言中,你可能运气就不太好了,因为你需要使用三个参数来调用函数,这势必要重新指定<varname>spacing</varname>的值。但是在 &python; 中,参数可以通过名称以任意顺序指定。</para>  
      81 <para><varname>spacing</varname> 和 <varname>collapse</varname> 是可选参数,因为它们已经定义了缺省值。<varname>object</varname> 是必备参数,因为它没有指定缺省值。如果调用 &info; 时只指定一个参数,那么 <varname>spacing</varname> 缺省为 <constant>10</constant> ,<varname>collapse</varname> 缺省为 &one;。如果调用 &info; 时指定两个参数,<varname>collapse</varname> 依然默认为 &one;。</para>  
      82 <para>假如你要指定 <varname>collapse</varname> 的值,但是又想要接受 <varname>spacing</varname> 的缺省值。在绝大部分语言中,你可能运气就不太好了,因为你需要使用三个参数来调用函数,这势必要重新指定 <varname>spacing</varname> 的值。但是在 &python; 中,参数可以通过名称以任意顺序指定。</para>  
    83 83 <example>  
    84 84 <title>&info; 的有效调用</title>  
     
    93 93 </callout>  
    94 94 <callout arearefs="apihelper_args.1.2">  
    95   <para>使用两个参数, <varname>collapse</varname> 使用缺省值 &one;。</para>  
      95 <para>使用两个参数,<varname>collapse</varname> 使用缺省值 &one;。</para>  
    95 95 </callout>  
    96 96 <callout arearefs="apihelper_args.1.3">  
    97   <para>这里你显式命名了<varname>collapse</varname>并指定了它的值。<varname>spacing</varname> 将依然使用它的缺省值 <literal>10</literal>。</para>  
      97 <para>这里你显式命名了 <varname>collapse</varname> 并指定了它的值。<varname>spacing</varname> 将依然使用它的缺省值 <literal>10</literal>。</para>  
    97 97 </callout>  
    98 98 <callout arearefs="apihelper_args.1.4">  
     
    110 110 <itemizedlist role="furtherreading">  
    111 111 <title>进一步阅读</title>  
    112   <listitem><para>&pythontutorial; 确切地讨论了 <ulink url="&url_pythontutorial;node6.html#SECTION006710000000000000000">何时、如何进行缺省参数赋值</ulink>,这都和缺省值是一个list还是一个具有副作用的表达式有关。</para></listitem>  
      112 <listitem><para>&pythontutorial; 确切地讨论了 <ulink url="&url_pythontutorial;node6.html#SECTION006710000000000000000">何时、如何进行缺省参数赋值</ulink>,这都和缺省值是一个 list 还是一个具有副作用的表达式有关。</para></listitem>  
    112 112 </itemizedlist>  
    113 113 </section>  
    114 114 <section id="apihelper.builtin">  
    115 115 <?dbhtml filename="power_of_introspection/built_in_functions.html"?>  
    116   <title>使用&type;、&str;、&dir; 和其它内置函数</title>  
      116 <title>使用 &type;、&str;、&dir; 和其它内置函数</title>  
    116 116 <abstract>  
    117 117 <title/>  
     
    122 122 <section>  
    123 123 <title>&type; 函数</title>  
    124   <para>&type; 函数返回任意对象的数据类型。在<filename class="headerfile">types</filename>模块中列出了可能的数据类型。这对于处理多种数据类型的帮助者函数非常有用。</para>  
      124 <para>&type; 函数返回任意对象的数据类型。在 <filename class="headerfile">types</filename> 模块中列出了可能的数据类型。这对于处理多种数据类型的帮助者函数非常有用。</para>  
    124 124 <example id="apihelper.type.intro">  
    125 125 <title>&type; 介绍</title>  
     
    138 138 <calloutlist>  
    139 139 <callout arearefs="apihelper.builtin.1.1">  
    140   <para>&type; 可以接收任何东西作为参数 -- 我的意思是任何东西 -- 并返回它的数据类型。整型、字符串、列表、字典、元组、函数、类、模块、甚至类型对象都可以作为参数被type函数接受。</para>  
      140 <para>&type; 可以接收任何东西作为参数 -- 我的意思是任何东西 -- 并返回它的数据类型。整型、字符串、列表、字典、元组、函数、类、模块、甚至类型对象都可以作为参数被 &type; 函数接受。</para>  
    140 140 </callout>  
    141 141 <callout arearefs="apihelper.builtin.1.2">  
     
    147 147 </callout>  
    148 148 <callout arearefs="apihelper.builtin.1.4">  
    149   <para>你可以使用<filename class="headerfile">types</filename>模块中的常量来进行对象类型的比较。这就是&info; 函数所做的,很快你就会看到。</para>  
      149 <para>你可以使用 <filename class="headerfile">types</filename> 模块中的常量来进行对象类型的比较。这就是 &info; 函数所做的,很快你就会看到。</para>  
    149 149 </callout>  
    150 150 </calloutlist>  
     
    181 181 </callout>  
    182 182 <callout arearefs="apihelper.builtin.2.4">  
    183   <para>&str; 的一个细小但重要的行为是它可以作用于&none;,&none; 是&python;的null值。这个调用返回字符串<literal>'None'</literal>。你将会使用这一点来提高你的 &info; 函数,这一点你很快就会看到。</para>  
      183 <para>&str; 的一个细小但重要的行为是它可以作用于 &none;,&none; 是 &python; 的 null 值。这个调用返回字符串 <literal>'None'</literal>。你将会使用这一点来提高你的 &info; 函数,这一点你很快就会看到。</para>  
    183 183 </callout>  
    184 184 </calloutlist>  
    185 185 </example>  
    186   <para>&info; 函数的核心是强大的 &dir; 函数。&dir;函数返回任意对象的属性和方法列表,包括模块对象、函数对象、字符串对象、列表对象、字典对象...相当多的东西。</para>  
      186 <para>&info; 函数的核心是强大的 &dir; 函数。&dir; 函数返回任意对象的属性和方法列表,包括模块对象、函数对象、字符串对象、列表对象、字典对象 …… 相当多的东西。</para>  
    186 186 <example id="apihelper.dir.intro">  
    187 187 <title>&dir; 介绍</title>  
     
    200 200 <calloutlist>  
    201 201 <callout arearefs="apihelper.builtin.3.1">  
    202   <para><varname>li</varname> 是一个列表,所以<literal>&dir;(<varname>li</varname>)</literal> 返回所有列表方法的一个列表。注意返回的列表只包含了字符串形式的方法名称,而不是方法对象本身。</para>  
      202 <para><varname>li</varname> 是一个列表,所以 <literal>&dir;(<varname>li</varname>)</literal> 返回所有列表方法的一个列表。注意返回的列表只包含了字符串形式的方法名称,而不是方法对象本身。</para>  
    202 202 </callout>  
    203 203 <callout arearefs="apihelper.builtin.3.2">  
    204   <para><varname>d</varname> 是一个字典,所以<literal>&dir;(<varname>d</varname>)</literal> 返回字典方法的名称列表。其中至少有一个方法,<link linkend="odbchelper.items">&keys;</link>,看起来还是挺熟悉的。</para>  
      204 <para><varname>d</varname> 是一个字典,所以 <literal>&dir;(<varname>d</varname>)</literal> 返回字典方法的名称列表。其中至少有一个方法,<link linkend="odbchelper.items">&keys;</link>,看起来还是挺熟悉的。</para>  
    204 204 </callout>  
    205 205 <callout arearefs="apihelper.builtin.3.3">  
    206   <para>这里就是真正变得有趣的地方。&odbchelper_modulename; 是一个模块,所以<literal>&dir;(&odbchelper_modulename;)</literal>返回模块中定义的所有部件的列表,包括内置的属性,例如<link linkend="odbchelper.ifnametrick">&name;</link>,<link linkend="odbchelper.import">&doc;</link>, 以及其它你所定义的属性和方法。在这个例子中,&odbchelper_modulename; 只有一个用户定义的方法,就是在<xref linkend="odbchelper" endterm="odbchelper.numberonly"/>中论述的 &odbchelper_function; 函数。</para>  
      206 <para>这里就是真正变得有趣的地方。&odbchelper_modulename; 是一个模块,所以 <literal>&dir;(&odbchelper_modulename;)</literal> 返回模块中定义的所有部件的列表,包括内置的属性,例如 <link linkend="odbchelper.ifnametrick">&name;</link>,<link linkend="odbchelper.import">&doc;</link>, 以及其它你所定义的属性和方法。在这个例子中,&odbchelper_modulename; 只有一个用户定义的方法,就是在<xref linkend="odbchelper" endterm="odbchelper.numberonly"/>中论述的 &odbchelper_function; 函数。</para>  
    206 206 </callout>  
    207 207 </calloutlist>  
    208 208 </example>  
    209   <para>最后是 &callable; 函数,它接收任何对象作为参数,如果参数对象是可调用的那么返回&true;,否则返回&false;。可调用对象包括函数、类方法,甚至类自身。(下一章将更多的关注类)。</para>  
      209 <para>最后是 &callable; 函数,它接收任何对象作为参数,如果参数对象是可调用的那么返回 &true;,否则返回 &false;。可调用对象包括函数、类方法,甚至类自身。(下一章将更多的关注类)。</para>  
    209 209 <example id="apihelper.builtin.callable">  
    210 210 <title>&callable; 介绍</title>  
     
    233 233 <calloutlist>  
    234 234 <callout arearefs="apihelper.builtin.4.1">  
    235   <para>&string; 模块中的函数现在已经不赞成使用了(尽管很多人现在仍然还在使用&join;函数),但是在这个模块中包含了许多有用的变量比如<varname>string.punctuation</varname>, 这个模块包含了所有标准的标点符号字符</para>  
      235 <para>&string; 模块中的函数现在已经不赞成使用了(尽管很多人现在仍然还在使用 &join; 函数),但是在这个模块中包含了许多有用的变量比如 <varname>string.punctuation</varname>,这个模块包含了所有标准的标点符号字符。</para>  
    235 235 </callout>  
    236 236 <callout arearefs="apihelper.builtin.4.2">  
    237   <para><link linkend="odbchelper.join"><function>string.join</function></link>是一个用于连接字符串列表的函数。</para>  
      237 <para><link linkend="odbchelper.join"><function>string.join</function></link> 是一个用于连接字符串列表的函数。</para>  
    237 237 </callout>  
    238 238 <callout arearefs="apihelper.builtin.4.3">  
    239   <para><varname>string.punctuation</varname>是不可调用的对象;它是一个字符串。(字符串确有可调用的方法,但是字符串本身不是可调用的。)</para>  
      239 <para><varname>string.punctuation</varname> 是不可调用的对象;它是一个字符串。(字符串确有可调用的方法,但是字符串本身不是可调用的。)</para>  
    239 239 </callout>  
    240 240 <callout arearefs="apihelper.builtin.4.4">  
    241   <para><function>string.join</function>是可调用的;这个函数可以接受两个参数。</para>  
      241 <para><function>string.join</function> 是可调用的;这个函数可以接受两个参数。</para>  
    241 241 </callout>  
    242 242 <callout arearefs="apihelper.builtin.4.5">  
     
    252 252 <section>  
    253 253 <title>内置函数</title>  
    254   <para>&type;、&str;、&dir; 和其它的 &python; 内置函数都归组到了 &builtins; (前后分别是双下划线)这个特殊的模块中。如果有帮助的话,你可以认为 &python; 在启动时自动执行了<literal>from __builtin__ import *</literal>,此语句将所有的<quote>内置</quote> 函数导入该命名空间,所以在这个命名空间中可以直接使用这些内置函数。</para>  
    255   <para>像这样考虑的好处是,是你可以获取<filename class="headerfile">__builtin__</filename>模块信息的,并以组的形式访问所有的内置函数和属性。 猜到什么了吗,&python; 有一个称为&info;的函数。自己尝试一下,略看一下结果列表。后面我们将深入到一些更重要的函数。(一些内置的错误类,比如<link linkend="odbchelper.tuplemethods"><errorcode>AttributeError</errorcode></link>,应该看上去已经熟悉了。)</para>  
      254 <para>&type;、&str;、&dir; 和其它的 &python; 内置函数都归组到了 &builtins; (前后分别是双下划线)这个特殊的模块中。如果有帮助的话,你可以认为 &python; 在启动时自动执行了 <literal>from __builtin__ import *</literal>,此语句将所有的 <quote>内置</quote>  函数导入该命名空间,所以在这个命名空间中可以直接使用这些内置函数。</para>  
      255 <para>像这样考虑的好处是,是你可以获取 <filename class="headerfile">__builtin__</filename> 模块信息的,并以组的形式访问所有的内置函数和属性。 猜到什么了吗,&python; 有一个称为 &info; 的函数。自己尝试一下,略看一下结果列表。后面我们将深入到一些更重要的函数。(一些内置的错误类,比如 <link linkend="odbchelper.tuplemethods"><errorcode>AttributeError</errorcode></link>,应该看上去已经熟悉了。)</para>  
    256 256 <example id="apihelper.builtin.list">  
    257 257 <title>内置属性和内置函数</title>  
     
    272 272 <note id="tip.manuals">  
    273 273 <title>&python; 是自文档化的</title>  
    274   <para>&python; 提供了很多出色的参考手册,你应该好好的精读一下所有 &python; 提供的必备模块。对于其它大部分语言,你会发现自己要常常回头参考手册或者man页来提醒自己如何使用这些模块,但是&python;不同于此,它很大程度上是自文档化的。</para>  
      274 <para>&python; 提供了很多出色的参考手册,你应该好好的精读一下所有 &python; 提供的必备模块。对于其它大部分语言,你会发现自己要常常回头参考手册或者 man 页来提醒自己如何使用这些模块,但是 &python; 不同于此,它很大程度上是自文档化的。</para>  
    274 274 </note>  
    275 275 <itemizedlist role="furtherreading">  
    276 276 <title>进一步阅读</title>  
    277   <listitem><para>&pythonlibraryreference; 对<ulink url="&url_pythonlibraryreference;built-in-funcs.html">所有的内置函数</ulink> 和 <ulink url="&url_pythonlibraryreference;module-exceptions.html">所有的内置异常</ulink>都进行了文档化。</para></listitem>  
      277 <listitem><para>&pythonlibraryreference; 对 <ulink url="&url_pythonlibraryreference;built-in-funcs.html"> 所有的内置函数</ulink> 和 <ulink url="&url_pythonlibraryreference;module-exceptions.html"> 所有的内置异常</ulink> 都进行了文档化。</para></listitem>  
    277 277 </itemizedlist>  
    278 278 </section>  
     
    282 282 <section id="apihelper.getattr">  
    283 283 <?dbhtml filename="power_of_introspection/getattr.html"?>  
    284   <title>通过&getattr;获取对象引用</title>  
      284 <title>通过 &getattr; 获取对象引用</title>  
    284 284 <abstract>  
    285 285 <title/>  
    286   <para>你已经知道<link linkend="odbchelper.objects">&python; 函数是对象</link>。  你不知道的是,使用&getattr;函数,可以得到一个直到运行时才知道名称的函数的引用.</para>  
      286 <para>你已经知道 <link linkend="odbchelper.objects">&python; 函数是对象</link>。  你不知道的是,使用 &getattr; 函数,可以得到一个直到运行时才知道名称的函数的引用。</para>  
    286 286 </abstract>  
    287 287 <example id="apihelper.getattr.intro">  
    288   <title>&getattr;介绍</title>  
      288 <title>&getattr; 介绍</title>  
    288 288 <screen>&prompt;<userinput>li = ["Larry", "Curly"]</userinput>  
    289 289 &prompt;<userinput>li.pop</userinput>                       <co id="apihelper.getattr.1.1"/>  
     
    304 304 <calloutlist>  
    305 305 <callout arearefs="apihelper.getattr.1.1">  
    306   <para>该语句获取列表的 &pop; 方法的引用。注意该语句并不是调用 &pop; 方法;调用pop方法应该是 <literal>li.pop()</literal>。这里指的是方法对象本身。</para>  
      306 <para>该语句获取列表的 &pop; 方法的引用。注意该语句并不是调用 &pop; 方法;调用 &pop; 方法的应该是 <literal>li.pop()</literal>。这里指的是方法对象本身。</para>  
    306 306 </callout>  
    307 307 <callout arearefs="apihelper.getattr.1.2">  
    308   <para>该语句也是返回 &pop; 方法的引用,但是此时,方法名称是作为一个字符串参数传递给&getattr;函数的。&getattr;是一个有用到令人无法致信的内置函数,可以返回任何对象的任何属性。在这个例子中,对象是一个list,属性是 &pop; 方法。</para>  
      308 <para>该语句也是返回 &pop; 方法的引用,但是此时,方法名称是作为一个字符串参数传递给 &getattr; 函数的。&getattr; 是一个有用到令人无法致信的内置函数,可以返回任何对象的任何属性。在这个例子中,对象是一个 list,属性是 &pop; 方法。</para>  
    308 308 </callout>  
    309 309 <callout arearefs="apihelper.getattr.1.3">  
    310   <para>如果不深信它是多么的有用,试试这个:&getattr; 的返回值<emphasis>是</emphasis> 方法,然后你就可以调用它就像直接使用 <literal>li.append("Moe")</literal>一样。但是实际上你没有直接调用函数;只是以字符串形式指定了函数名称。</para>  
      310 <para>如果不深信它是多么的有用,试试这个:&getattr; 的返回值 <emphasis>是</emphasis> 方法,然后你就可以调用它就像直接使用 <literal>li.append("Moe")</literal> 一样。但是实际上你没有直接调用函数;只是以字符串形式指定了函数名称。</para>  
    310 310 </callout>  
    311 311 <callout arearefs="apihelper.getattr.1.4">  
     
    316 316 </callout>  
    317 317 <callout arearefs="apihelper.getattr.1.5">  
    318   <para>理论上, &getattr; 可以作用于元组,但是由于<link linkend="odbchelper.tuplemethods">元组没有方法</link>, 所以不管你指定什么属性名称&getattr; 都会引发一个异常。</para>  
      318 <para>理论上,&getattr; 可以作用于元组,但是由于 <link linkend="odbchelper.tuplemethods">元组没有方法</link>,所以不管你指定什么属性名称 &getattr; 都会引发一个异常。</para>  
    318 318 </callout>  
    319 319 </calloutlist>  
     
    324 324 <para>&getattr; 不仅仅适用于内置数据类型,也可作用于模块。</para>  
    325 325 <example id="apihelper.getattr.example">  
    326   <title>&apihelper_filename; 中的&getattr; 函数</title>  
      326 <title>&apihelper_filename; 中的 &getattr; 函数</title>  
    326 326 <screen>&prompt;<userinput>import &odbchelper_name;</userinput>  
    327 327 &prompt;<userinput>&odbchelper_name;.&odbchelper_funcname;</userinput>             <co id="apihelper.getattr.2.1"/>  
     
    343 343 <calloutlist>  
    344 344 <callout arearefs="apihelper.getattr.2.1">  
    345   <para>该语句返回 &odbchelper_modulename; 模块中 &odbchelper_function; 函数的引用,<xref linkend="odbchelper"/>你已经研习过这个方法了。(你看到的这个十六进制地址是我机器上的;你的输出结果会有所不同。)</para>  
      345 <para>该语句返回 &odbchelper_modulename; 模块中 &odbchelper_function; 函数的引用,<xref linkend="odbchelper"/> 你已经研习过这个方法了。(你看到的这个十六进制地址是我机器上的;你的输出结果会有所不同。)</para>  
    345 345 </callout>  
    346 346 <callout arearefs="apihelper.getattr.2.2">  
    347   <para>使用&getattr;,你能够获得同一函数的同一引用。通常,<literal>&getattr;(<replaceable>object</replaceable>, "<replaceable>attribute</replaceable>")</literal> 等价于 <literal><replaceable>object</replaceable>.<replaceable>attribute</replaceable></literal>。如果 <replaceable>object</replaceable> 是一个模块的话,那么<replaceable>attribute</replaceable> 可能是定义在模块中的任何东西:函数、类或者全局变量。</para>  
      347 <para>使用 &getattr;,你能够获得同一函数的同一引用。通常,<literal>&getattr;(<replaceable>object</replaceable>, "<replaceable>attribute</replaceable>")</literal> 等价于 <literal><replaceable>object</replaceable>.<replaceable>attribute</replaceable></literal>。如果 <replaceable>object</replaceable> 是一个模块的话,那么 <replaceable>attribute</replaceable> 可能是定义在模块中的任何东西:函数、类或者全局变量。</para>  
    347 347 </callout>  
    348 348 <callout arearefs="apihelper.getattr.2.3">  
     
    352 352 </callout>  
    353 353 <callout arearefs="apihelper.getattr.2.4">  
    354   <para>在这个例子中, <varname>method</varname> 是函数的名称,通过获取 <link linkend="apihelper.type.intro">&type;</link> 可以进行验证。</para>  
      354 <para>在这个例子中<varname>method</varname> 是函数的名称,通过获取 <link linkend="apihelper.type.intro">&type;</link> 可以进行验证。</para>  
    354 354 </callout>  
    355 355 <callout arearefs="apihelper.getattr.2.5">  
     
    362 362 <section>  
    363 363 <title>&getattr; 作为一个分发者</title>  
    364   <para>&getattr;常见的使用模式是作为一个分发者。举个例子,如果你有一个程序可以以不同的格式输出数据,你可以为个每种输出格式定义各自的格式输出函数,然后使用唯一的分发函数调用所需的格式输出函数。</para>  
    365   <para>例如,让我们假设有一个以&html;、&xml;和普通文本格式打印站点统计的程序。输出格式在命令行中指定,或者保存在配置文件中。<filename>statsout</filename> 模块定义了三个函数:<function>output_html</function>、<function>output_xml</function>和<function>output_text</function>。然后主程序定义了唯一的输出函数,如下:</para>  
      364 <para>&getattr; 常见的使用模式是作为一个分发者。举个例子,如果你有一个程序可以以不同的格式输出数据,你可以为每种输出格式定义各自的格式输出函数,然后使用唯一的分发函数调用所需的格式输出函数。</para>  
      365 <para>例如,让我们假设有一个以 &html;、&xml; 和普通文本格式打印站点统计的程序。输出格式在命令行中指定,或者保存在配置文件中。<filename>statsout</filename> 模块定义了三个函数:<function>output_html</function>、<function>output_xml</function> 和 <function>output_text</function>。然后主程序定义了唯一的输出函数,如下:</para>  
    366 366 <example id="apihelper.getattr.dispatch">  
    367 367 <title>使用&getattr; 创建分发者</title>  
     
    376 376 <calloutlist>  
    377 377 <callout arearefs="apihelper.getattr.3.1">  
    378   <para><function>output</function> 函数接收一个必备参数<varname>data</varname>,和一个可选参数<varname>format</varname>。如果没有指定<varname>format</varname>参数,其缺省值是<literal>text</literal>并完成普通文本输出函数的调用。</para>  
      378 <para><function>output</function> 函数接收一个必备参数 <varname>data</varname>,和一个可选参数 <varname>format</varname>。如果没有指定 <varname>format</varname> 参数,其缺省值是 <literal>text</literal> 并完成普通文本输出函数的调用。</para>  
    378 378 </callout>  
    379 379 <callout arearefs="apihelper.getattr.3.2">  
    380   <para>你可以连接<varname>format</varname> 参数值和"output_"来创建一个函数名称作为参数值,然后从<filename>statsout</filename>模块中取得该函数。这种方式允许今后很容易的扩展程序以支持其它的输出格式,而且无需修改分发函数。所要做的仅仅是向<filename>statsout</filename>中添加一个函数,比如<function>output_pdf</function>,之后只要将“pdf”作为<varname>format</varname>的参数值传递给<function>output</function> 函数即可。</para>  
      380 <para>你可以连接 <varname>format</varname> 参数值和 "output_" 来创建一个函数名称作为参数值,然后从 <filename>statsout</filename> 模块中取得该函数。这种方式允许今后很容易的扩展程序以支持其它的输出格式,而且无需修改分发函数。所要做的仅仅是向 <filename>statsout</filename> 中添加一个函数,比如 <function>output_pdf</function>,之后只要将 “pdf” 作为 <varname>format</varname> 的参数值传递给 <function>output</function> 函数即可。</para>  
    380 380 </callout>  
    381 381 <callout arearefs="apihelper.getattr.3.3">  
    382   <para>现在你可以简单的调用输出函数就像调用其它函数一样了。<varname>output_function</varname> 变量是指向<filename>statsout</filename> 模块中相应函数的引用。</para>  
      382 <para>现在你可以简单的调用输出函数就像调用其它函数一样了。<varname>output_function</varname> 变量是指向 <filename>statsout</filename> 模块中相应函数的引用。</para>  
    382 382 </callout>  
    383 383 </calloutlist>  
    384   <para>你是否发现前面示例的一个Bug?即字符串和函数之间的松耦合,而且没有错误检查。如果用户传入一个格式参数,但是在<filename>statsout</filename>中没有定义相应的格式输出函数,会发生什么呢?还好,&getattr; 会返回&none;,它会取代一个有效函数并被赋值给<varname>output_function</varname>,然后下一行调用函数的语句将会失败并抛出一个异常。这种方式不好。</para>  
      384 <para>你是否发现前面示例的一个 Bug?即字符串和函数之间的松耦合,而且没有错误检查。如果用户传入一个格式参数,但是在 <filename>statsout</filename> 中没有定义相应的格式输出函数,会发生什么呢?还好,&getattr; 会返回 &none;,它会取代一个有效函数并被赋值给 <varname>output_function</varname>,然后下一行调用函数的语句将会失败并抛出一个异常。这种方式不好。</para>  
    384 384 <para>值得庆幸的是,&getattr; 能够使用可选的第三个参数,一个缺省返回值。</para>  
    385 385 <example id="apihelper.getattr.default">  
     
    398 398 <calloutlist>  
    399 399 <callout arearefs="apihelper.getattr.4.1">  
    400   <para>这个函数调用确保可以工作,因为你在调用&getattr; 时添加了第三个参数。第三个参数是一个缺省返回值,如果第二个参数指定的属性或者方法没能找到,则将返回这个缺省返回值。</para>  
      400 <para>这个函数调用一定可以工作,因为你在调用 &getattr; 时添加了第三个参数。第三个参数是一个缺省返回值,如果第二个参数指定的属性或者方法没能找到,则将返回这个缺省返回值。</para>  
    400 400 </callout>  
    401 401 </calloutlist>  
     
    410 410 <abstract>  
    411 411 <title/>  
    412   <para>如你所知,&python; 具有通过列表遍历(<xref linkend="odbchelper.map"/>)将列表映射到其它列表的强大能力。这种能力同过滤机制结合使用,使列表中的有些元素被映射的同时跳过另外一些元素。</para>  
      412 <para>如你所知,&python; 具有通过列表解析(<xref linkend="odbchelper.map"/>)将列表映射到其它列表的强大能力。这种能力同过滤机制结合使用,使列表中的有些元素被映射的同时跳过另外一些元素。</para>  
    412 412 </abstract>  
    413 413 <informalexample>  
     
    418 418 [<replaceable>mapping-expression</replaceable> for <replaceable>element</replaceable> in <replaceable>source-list</replaceable> if <replaceable>filter-expression</replaceable>]</programlisting>  
    419 419 </informalexample>  
    420   <para>这是你所知所爱的<link linkend="odbchelper.map">列表解析</link>的扩展。前三部分都是相同的;最后一部分,以&if;开头的是过滤器表达式。过滤器表达式可以是返回值为真或者假(在&python;是<link linkend="tip.boolean">几乎任何东西</link>)的任何表达式。任何经过滤器表达式演算值为元素的真都可以包含在映射中。其它的元素都将忽略,它们不会进入映射表达式,更不会包含在输出列表中。</para>  
      420 <para>这是你所知所爱的 <link linkend="odbchelper.map">列表解析</link> 的扩展。前三部分都是相同的;最后一部分,以 &if; 开头的是过滤器表达式。过滤器表达式可以是返回值为真或者假(在 &python; 中是 <link linkend="tip.boolean">几乎任何东西</link>)的任何表达式。任何经过滤器表达式演算值为元素的真都可以包含在映射中。其它的元素都将忽略,它们不会进入映射表达式,更不会包含在输出列表中。</para>  
    420 420 <example>  
    421 421 <title>列表过滤介绍</title>  
     
    430 430 <calloutlist>  
    431 431 <callout arearefs="apihelper.filter.1.1">  
    432   <para>这里的映射表达式很简单(只是返回每个元素的值),所以请把注意力集中到过滤器表达式上。由于&python; 会遍历整个列表,它将对每个元素执行过滤器表达式。如果过滤器表达式演算值为真,该元素就会被映射,同时映射表达式的结果将包含在返回的列表中。这里,你过滤掉了所有单字符的字符串,留下了一个由长字符串构成的列表。</para>  
      432 <para>这里的映射表达式很简单(只是返回每个元素的值),所以请把注意力集中到过滤器表达式上。由于 &python; 会遍历整个列表,它将对每个元素执行过滤器表达式。如果过滤器表达式演算值为真,该元素就会被映射,同时映射表达式的结果将包含在返回的列表中。这里,你过滤掉了所有单字符的字符串,留下了一个由长字符串构成的列表。</para>  
    432 432 </callout>  
    433 433 <callout arearefs="apihelper.filter.1.2">  
    434   <para>这里你过滤掉了一个特定值<literal>b</literal>。 注意这个过滤器会过滤掉所有的 <literal>b</literal>,因为每次取出 <literal>b</literal>,过滤表达式都将为假。</para>  
      434 <para>这里你过滤掉了一个特定值 <literal>b</literal>。 注意这个过滤器会过滤掉所有的 <literal>b</literal>,因为每次取出 <literal>b</literal>,过滤表达式都将为假。</para>  
    434 434 </callout>  
    435 435 <callout arearefs="apihelper.filter.1.3">  
     
    447 447 </informalexample>  
    448 448 <para>这行看上去挺复杂的,确实也很复杂,但是基本结构都还是一样的。整个过滤表达式返回一个列表,并赋值给 <varname>methodList</varname> 变量。表达式的前半部分是列表映射部分。映射表达式是一个和遍历元素相同的表达式,因此它返回每个元素的值。<literal>&dir;(<varname>object</varname>)</literal> 返回 <varname>object</varname> 对象的属性和方法列表——你正在映射的列表。所以唯一新出现的部分就是在 &if; 后面的过滤表达式。</para>  
    449   <para>过滤表达式看上去很恐怖,其实不是。你已经知道了 <link linkend="apihelper.builtin.callable">&callable;</link>、<link linkend="apihelper.getattr.intro">&getattr;</link> 和 <link linkend="odbchelper.tuplemethods">&in;</link>。正如你在 <link linkend="apihelper.getattr">前面的部分</link> 中看到的,如果 <varname>object</varname> 是一个模块,并且<varname>method</varname> 是上述模块中某个函数的名称,那么表达式 <literal>&apihelper_getattr;</literal> 将返回一个函数对象。</para>  
    450   <para>所以这个表达式接收一个名为 <varname>object</varname> 的对象,然后得到它的属性、方法、函数和其他部件的名称列表,接着过滤掉我们不关心的部件。执行过滤行为是通过对每个属性/方法/函数的名称调用 &getattr; 函数取得实际部件的引用,然后检查这些部件对象是否是可调用的,当然这些可调用的部件对象可能是方法或者函数,同时也可能是内置的(比如列表的 &pop; 方法)或者用户自定义的(比如 &odbchelper_modulename; 模块的 &odbchelper_function; 函数)。这里你不用关心其它的属性,如内置在每一个模块中的 &name; 属性。</para>  
      449 <para>过滤表达式看上去很恐怖,其实不是。你已经知道了 <link linkend="apihelper.builtin.callable">&callable;</link>、<link linkend="apihelper.getattr.intro">&getattr;</link> 和 <link linkend="odbchelper.tuplemethods">&in;</link>。正如你在 <link linkend="apihelper.getattr">前面的部分</link> 中看到的,如果 <varname>object</varname> 是一个模块,并且 <varname>method</varname> 是上述模块中某个函数的名称,那么表达式 <literal>&apihelper_getattr;</literal> 将返回一个函数对象。</para>  
      450 <para>所以这个表达式接收一个名为 <varname>object</varname> 的对象,然后得到它的属性、方法、函数和其他部件的名称列表,接着过滤掉我们不关心的部件。执行过滤行为是通过对每个属性/方法/函数的名称调用 &getattr; 函数取得实际部件的引用,然后检查这些部件对象是否是可调用的,当然这些可调用的部件对象可能是方法或者函数,同时也可能是内置的(比如列表的 &pop; 方法)或者用户自定义的(比如 &odbchelper_modulename; 模块的 &odbchelper_function; 函数)。这里你不用关心其它的属性,如内置在每一个模块中的 &name; 属性。</para>  
    451 451 <itemizedlist role="furtherreading">  
    452 452 <title>进一步阅读</title>  
    453   <listitem><para>&pythontutorial; 讨论了<ulink url="&url_pythontutorial;node7.html#SECTION007130000000000000000">使用内置<function>过滤器</function>函数</ulink>过滤列表的另一种方式。</para></listitem>  
      453 <listitem><para>&pythontutorial; 讨论了 <ulink url="&url_pythontutorial;node7.html#SECTION007130000000000000000">使用内置<function>过滤器</function>函数</ulink> 过滤列表的另一种方式。</para></listitem>  
    453 453 </itemizedlist>  
    454 454 </section>  
     
    471 471 <calloutlist>  
    472 472 <callout arearefs="apihelper.andor.1.1">  
    473   <para>使用 &andfunction; 时,在布尔上下文中从左到右演算表达式的值。&zero;、<literal>''</literal>、<literal>[]</literal>、<literal>()</literal>、<literal>{}</literal>、&none; 在布尔上下文中为假;其它任何东西都为真。还好,几乎是所有东西。默认情况下,布尔上下文中的类实例为真,但是你可以在类中定义特定的方法使得类实例的演算值为假。你将会在<xref linkend="fileinfo" endterm="fileinfo.numberonly"/>中了解到类和这些特殊方法。如果布尔上下文中的所有值都为真,那么 &andfunction; 返回最后一个值。在这个例子中,&andfunction; 演算 <literal>'a'</literal> 的值为真,然后是 <literal>'b'</literal> 的演算值为真,最终返回<literal>'b'</literal>。</para>  
      473 <para>使用 &andfunction; 时,在布尔上下文中从左到右演算表达式的值。&zero;、<literal>''</literal>、<literal>[]</literal>、<literal>()</literal>、<literal>{}</literal>、&none; 在布尔上下文中为假;其它任何东西都为真。还好,几乎是所有东西。默认情况下,布尔上下文中的类实例为真,但是你可以在类中定义特定的方法使得类实例的演算值为假。你将会在<xref linkend="fileinfo" endterm="fileinfo.numberonly"/>中了解到类和这些特殊方法。如果布尔上下文中的所有值都为真,那么 &andfunction; 返回最后一个值。在这个例子中,&andfunction; 演算 <literal>'a'</literal> 的值为真,然后是 <literal>'b'</literal> 的演算值为真,最终返回 <literal>'b'</literal>。</para>  
    473 473 </callout>  
    474 474 <callout arearefs="apihelper.andor.1.2">  
     
    505 505 </callout>  
    506 506 <callout arearefs="apihelper.andor.2.4">  
    507   <para>注意 &orfunction; 在布尔上下文中会一直进行表达式演算直到找到第一个真值,然后就会忽略剩余的比较值。如果某些值具有副作用,这种特性就非常重要了。在这里,函数<function>sidefx</function> 永远都不会被调用,因为 &orfunction; 演算 <literal>'a'</literal> 的值为真,所以紧接着就立刻返回 <literal>'a'</literal> 了。</para>  
      507 <para>注意 &orfunction; 在布尔上下文中会一直进行表达式演算直到找到第一个真值,然后就会忽略剩余的比较值。如果某些值具有副作用,这种特性就非常重要了。在这里,函数 <function>sidefx</function> 永远都不会被调用,因为 &orfunction; 演算 <literal>'a'</literal> 的值为真,所以紧接着就立刻返回 <literal>'a'</literal> 了。</para>  
    507 507 </callout>  
    508 508 </calloutlist>  
    509 509 </example>  
    510   <para>如果你是一名 &c; 语言黑客,肯定很熟悉&candor; 表达式,如果 <replaceable>bool</replaceable> 为真,表达式演算值为 <varname>a</varname>,否则为 <varname>b</varname>。基于 &python; 中 &andfunction; 和 &orfunction; 的工作方式,你可以完成相同的事情。</para>  
      510 <para>如果你是一名 &c; 语言黑客,肯定很熟悉 &candor; 表达式,如果 <replaceable>bool</replaceable> 为真,表达式演算值为 <varname>a</varname>,否则为 <varname>b</varname>。基于 &python; 中 &andfunction; 和 &orfunction; 的工作方式,你可以完成相同的事情。</para>  
    510 510 <section>  
    511 511 <title>使用 &andor; 技巧</title>  
     
    523 523 <calloutlist>  
    524 524 <callout arearefs="apihelper.andor.3.1">  
    525   <para>这个语法看起来类似于 &c; 语言中的 &candor; 表达式。整个表达式从左到右进行演算,所以先进行 &andfunction;表达式的演算。 <literal>1 and 'first'</literal> 演算值为 <literal>'first'</literal>,然后 <literal>'first' or 'second'</literal> 的演算值为 <literal>'first'</literal>。</para>  
      525 <para>这个语法看起来类似于 &c; 语言中的 &candor; 表达式。整个表达式从左到右进行演算,所以先进行 &andfunction; 表达式的演算。 <literal>1 and 'first'</literal> 演算值为 <literal>'first'</literal>,然后 <literal>'first' or 'second'</literal> 的演算值为 <literal>'first'</literal>。</para>  
    525 525 </callout>  
    526 526 <callout arearefs="apihelper.andor.3.2">  
    527   <para><literal>0 and 'first'</literal> 演算值为&false;,然后 <literal>0 or 'second'</literal> 演算值为 <literal>'second'</literal>。</para>  
      527 <para><literal>0 and 'first'</literal> 演算值为 &false;,然后 <literal>0 or 'second'</literal> 演算值为 <literal>'second'</literal>。</para>  
    527 527 </callout>  
    528 528 </calloutlist>  
     
    543 543 </calloutlist>  
    544 544 </example>  
    545   <para>&andor; 技巧,<literal><replaceable>bool</replaceable> and <varname>a</varname> or <varname>b</varname></literal>表达式,当 <varname>a</varname> 在布尔上下文中的值为假时,不会像 &c; 语言表达式 &candor; 那样工作。</para>  
      545 <para>&andor; 技巧,<literal><replaceable>bool</replaceable> and <varname>a</varname> or <varname>b</varname></literal> 表达式,当 <varname>a</varname> 在布尔上下文中的值为假时,不会像 &c; 语言表达式 &candor; 那样工作。</para>  
    545 545 <para>在 &andor; 技巧后面真正的技巧是,确保 <varname>a</varname> 的值决不会为假。最常用的方式是使 <varname>a</varname> 成为 <literal>[<varname>a</varname>]</literal> 、 <varname>b</varname> 成为 <literal>[<varname>b</varname>]</literal>,然后使用返回值列表的第一个元素,应该是 <varname>a</varname> 或 <varname>b</varname>中的某一个。</para>  
    546 546 <example>  
     
    553 553 <calloutlist>  
    554 554 <callout arearefs="apihelper.andor.5.1">  
    555   <para>由于 <literal>[<varname>a</varname>]</literal> 是一个非空列表,所以它决不会为假。即使 <varname>a</varname> 是 &zero; 或者 <literal>''</literal> 或者其它假值,列表<literal>[<varname>a</varname>]</literal> 也为真,因为它有一个元素。</para>  
      555 <para>由于 <literal>[<varname>a</varname>]</literal> 是一个非空列表,所以它决不会为假。即使 <varname>a</varname> 是 &zero; 或者 <literal>''</literal> 或者其它假值,列表 <literal>[<varname>a</varname>]</literal> 也为真,因为它有一个元素。</para>  
    555 555 </callout>  
    556 556 </calloutlist>  
    557 557 </example>  
    558   <para>到现在为止,这个技巧可能看上去问题超过了它的价值。毕竟,使用&if; 语句可以完成相同的事情,那为什么要经历这些麻烦事呢?哦,在很多情况下,你要在两个常量值中进行选择,由于你知道 <varname>a</varname> 的值总是为真,所以你可以使用这种较为简单的语法而且不用担心。对于使用更为复杂的安全形式,依然有很好的理由要求这样做。例如,在&python; 语言的某些情况下 &if; 语句是不允许使用的,比如在&lambdafunction; 函数中。</para>  
      558 <para>到现在为止,这个技巧可能看上去问题超过了它的价值。毕竟,使用 &if;  语句可以完成相同的事情,那为什么要经历这些麻烦事呢?哦,在很多情况下,你要在两个常量值中进行选择,由于你知道 <varname>a</varname> 的值总是为真,所以你可以使用这种较为简单的语法而且不用担心。对于使用更为复杂的安全形式,依然有很好的理由要求这样做。例如,在 &python; 语言的某些情况下 &if; 语句是不允许使用的,比如在 &lambdafunction; 函数中。</para>  
    558 558 <itemizedlist role="furtherreading">  
    559 559 <title>进一步阅读</title>  
     
    585 585 <calloutlist>  
    586 586 <callout arearefs="apihelper.lambda.1.2">  
    587   <para>这是一个 &lambdafunction; 函数,完成成同上面普通函数相同的事情。注意这里的简短的语法:在参数列表周围没有括号,而且忽略了 <literal>return</literal> 关键字(隐含存在,因为整个函数只有一行).  而且,该函数没有函数名称,但是可以将它赋值给一个变量进行调用。</para>  
      587 <para>这是一个 &lambdafunction; 函数,完成同上面普通函数相同的事情。注意这里的简短的语法:在参数列表周围没有括号,而且忽略了 <literal>return</literal> 关键字(隐含存在,因为整个函数只有一行)。  而且,该函数没有函数名称,但是可以将它赋值给一个变量进行调用。</para>  
    587 587 </callout>  
    588 588 <callout arearefs="apihelper.lambda.1.3">  
    589   <para>你可以使用 &lambdafunction; 函数甚至不需要将它赋值给一个变量。这可能不是最有用的东西 in the world,它只是展示了 lambda 函数只是一个内联函数。</para>  
      589 <para>你可以使用 &lambdafunction; 函数甚至不需要将它赋值给一个变量。这可能不是世上最有用的东西,它只是展示了 &lambdafunction; 函数只是一个内联函数。</para>  
    589 589 </callout>  
    590 590 </calloutlist>  
    591 591 </example>  
    592   <para>总的来说,&lambdafunction; 函数可以接收任意多个参数(包括 <link linkend="apihelper.optional">可选参数</link>)并且返回单个表达式的值。&lambdafunction; 函数不能包含命令,包含的表达式不能超过一个。不要试图向 &lambdafunction; 函数中塞入太多的东西;如果你需要更复杂的东西,应该定义一个普通函数,然后想让它多长就多。</para>  
      592 <para>总的来说,&lambdafunction; 函数可以接收任意多个参数(包括 <link linkend="apihelper.optional">可选参数</link>)并且返回单个表达式的值。&lambdafunction; 函数不能包含命令,包含的表达式不能超过一个。不要试图向 &lambdafunction; 函数中塞入太多的东西;如果你需要更复杂的东西,应该定义一个普通函数,然后想让它多长就多。</para>  
    592 592 <note id="tip.lambda">  
    593 593 <title>&lambdafunction; 是可选的</title>  
     
    622 622 </callout>  
    623 623 <callout arearefs="apihelper.split.1.2">  
    624   <para>不带参数的&split; 按照空格进行分割。所以三个空格、一个回车和一个制表符都是一样的。</para>  
      624 <para>不带参数的 &split; 按照空格进行分割。所以三个空格、一个回车和一个制表符都是一样的。</para>  
    624 624 </callout>  
    625 625 <callout arearefs="apihelper.split.1.3">  
     
    636 636 </informalexample>  
    637 637 <para><varname>processFunc</varname> 现在是一个函数,但是它到底是哪一个函数还要取决于 <varname>collapse</varname> 变量。如果 <varname>collapse</varname> 为真,<literal><varname>processFunc</varname>(<replaceable>string</replaceable>)</literal> 将压缩空白;否则 <literal><varname>processFunc</varname>(<replaceable>string</replaceable>)</literal> 将返回未改变的参数。</para>  
    638   <para>在一个不很健壮的语言中实现它,像 &vb;,你很有可能要创建一个函数,接受一个字符串参数和一个 <parameter>collapse</parameter> 参数,并使用 &if; 语句确定是否压缩空白,然后再返回相应的值。这种方式是低效的,因为函数可能需要处理每一种可能的情况。每次你调用它,它将不得不在给出你所想要的东西之前,判断是否要压缩空白。在 &python; 中,你可以将决策逻辑拿到函数外面,而定义一个裁减过的 &lambdafunction; 函数提供确切的(唯一的)你想要的。这种方式更为高效、更为优雅,而且很少引起那些令人讨厌(哦,想到那些参数就头昏)的错误。</para>  
      638 <para>在一个不很健壮的语言中实现它,像 &vb;,你很有可能要创建一个函数,接受一个字符串参数和一个 <parameter>collapse</parameter> 参数,并使用 &if; 语句确定是否压缩空白,然后再返回相应的值。这种方式是低效的,因为函数可能需要处理每一种可能的情况。每次你调用它,它将不得不在给出你所想要的东西之前,判断是否要压缩空白。在 &python; 中,你可以将决策逻辑拿到函数外面,而定义一个裁减过的 &lambdafunction; 函数提供确切的(唯一的)你想要的。这种方式更为高效、更为优雅,而且很少引起那些令人讨厌(哦,想到那些参数就头昏)的错误。</para>  
    638 638 <itemizedlist role="furtherreading">  
    639 639 <title>&lambdafunction; 函数进一步阅读</title>  
     
    731 731 </calloutlist>  
    732 732 </example>  
    733   <para>几乎已经完成了。有了 &ljust; 方法填充过的方法名称和来自调用 <varname>processFunc</varname> 方法得到的 &docstring;(可能压缩过),你就可以将两者连接起来并得到单个字符串。因为对 <varname>methodList</varname> 进行了映射,最终你将获得一个字符串列表。利用<literal>"\n"</literal> 的 &join; 函数,将这个列表连接为单个字符串,列表中每个元素独占一行,接着打印出结果。</para>  
      733 <para>几乎已经完成了。有了 &ljust; 方法填充过的方法名称和来自调用 <varname>processFunc</varname> 方法得到的 &docstring;(可能压缩过),你就可以将两者连接起来并得到单个字符串。因为对 <varname>methodList</varname> 进行了映射,最终你将获得一个字符串列表。利用 <literal>"\n"</literal> 的 &join; 函数,将这个列表连接为单个字符串,列表中每个元素独占一行,接着打印出结果。</para>  
    733 733 <example>  
    734 734 <title>打印列表</title>  
    781 781 <para>在研究下一章前,确保你可以无困难的完成下面这些事情:</para>  
    782 782 <itemizedlist>  
    783   <listitem><para>用 <link linkend="apihelper.optional">可选和命名参数</link>定义和调用函数</para></listitem>  
      783 <listitem><para>用<link linkend="apihelper.optional">可选和命名参数</link>定义和调用函数</para></listitem>  
    783 783 <listitem><para>用 <link linkend="apihelper.str.intro">&str;</link> 强制转换任意值为字符串形式</para></listitem>  
    784 784 <listitem><para>用 <link linkend="apihelper.getattr">&getattr;</link> 动态得到函数和其它属性的引用</para></listitem>  
    785   <listitem><para>扩展列表解析语法来进行 <link linkend="apihelper.filter">列表过滤</link></para></listitem>  
      785 <listitem><para>扩展列表解析语法实现 <link linkend="apihelper.filter">列表过滤</link></para></listitem>  
    785 785 <listitem><para>识别 <link linkend="apihelper.andor">&andor; 技巧</link> 并安全的使用它</para></listitem>  
    786 786 <listitem><para>定义 <link linkend="apihelper.lambda">&lambdafunction; 函数</link></para></listitem>  
    787   <listitem><para><link linkend="apihelper.funcassign">将函数赋值给变量</link> 然后通过引用变量调用函数。我强调的已经够多了:这种思考方式对于提高对 &python; 的理解力至关重要。从这书中你会随处可见这种技术的更复杂的应用。</para></listitem>  
      787 <listitem><para><link linkend="apihelper.funcassign">将函数赋值给变量</link> 然后通过引用变量调用函数。我强调的已经够多了:这种思考方式对于提高对 &python; 的理解力至关重要。在本书中你会随处可见这种技术的更复杂的应用。</para></listitem>  
    787 787 </itemizedlist>  
    788 788 </highlights>