Attachment 'apihelper.xml'
Download 1 <?xml version="1.0" encoding="utf-8"?>
2 <chapter id="apihelper">
3 <?dbhtml filename="power_of_introspection/index.html"?>
4 <title>自省的威力</title>
5 <titleabbrev id="apihelper.numberonly">Chapter 4</titleabbrev>
6 <abstract>
7 <title/>
8 <para>本章论述了 &python; 众多强大功能之一:自省。正如你所知道的,<link linkend="odbchelper.objects"> &python; 中任何东西都是对象</link>,自省是指代码可以查看内存中以对象形式存在的其它模块和函数,获取它们的信息,并对它们进行操作。用这种方法, 你可以定义没有名称的函数,不按函数声明的参数顺序调用函数,甚至引用事先并不知道名称的函数。</para>
9 </abstract>
10 <section id="apihelper.divein">
11 <title>接触</title>
12 <abstract>
13 <title/>
14 <para>下面是一个完整可运行的 &python; 程序。大概看一下这段程序,你应该可以理解不少了。用数字标出的行阐述了<xref linkend="odbchelper"/>中涉及的一些概念。如果剩下来的代码看起来有点奇怪,不用担心,通过阅读本章你将会理解所有这些。</para>
15 </abstract>
16 <example>
17 <title>&apihelper_filename;</title>
18 ¶_download;
19 <programlisting>
20 &apihelper_funcdef; <co id="apihelper.intro.1.1"/> <co id="apihelper.intro.1.2"/> <co id="apihelper.intro.1.3"/>
21 &apihelper_docstring;
22 &apihelper_docstring2;
23 &apihelper_docstring3;
24 &apihelper_methods;
25 &apihelper_lambda;
26 &apihelper_main;
27
28 &apihelper_ifname; <co id="apihelper.intro.1.4"/> <co id="apihelper.intro.1.5"/>
29 &apihelper_printdoc;</programlisting>
30 <calloutlist>
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>
33 </callout>
34 <callout arearefs="apihelper.intro.1.2">
35 <para>&info;函数有一个多行的<link linkend="odbchelper.docstring">&docstring;</link>,简要地描述了函数的功能。注意这里并没有提到返回值;单独使用这个函数只是为了这个函数产生的效果,并不是为了它的返回值。</para>
36 </callout>
37 <callout arearefs="apihelper.intro.1.3">
38 <para>函数内的代码是<link linkend="odbchelper.indenting">缩进</link>形式的。</para>
39 </callout>
40 <callout arearefs="apihelper.intro.1.4">
41 <para><literal>&if; &name;</literal> <link linkend="odbchelper.ifnametrick">技巧</link>允许这个程序在自己独立运行时做些有用的事情,同时又不妨碍作为其它程序的模块使用。 在这个例子中,程序只是简单地打印出&info; 函数的 &docstring;。</para>
42 </callout>
43 <callout arearefs="apihelper.intro.1.5">
44 <para><link linkend="odbchelper.ifnametrick">&if;语句</link> 使用 &comparisonequals; 进行比较, 而且不需要括号。</para>
45 </callout>
46 </calloutlist>
47 </example>
48 <para>&info; 函数的设计意图是提供给工作在&python; &ide; 中的开发人员使用,它可以使用任何含有函数或者方法的对象(比如模块,含有函数,又比如list,含有方法)作为参数,并打印出对象的所有函数和它们的 &docstring;。</para>
49 <example>
50 <title>&apihelper_filename; 的用法示例</title>
51 <screen>&apihelper_output;</screen>
52 </example>
53 <para>缺省地,程序输出进行了格式化处理易于阅读。多行 &docstring; 被合并到单行中,要改变这个选项需要指定<parameter>collapse</parameter>参数的值为<constant>0</constant>。如果函数名称长于10个字符,你可以将<parameter>spacing</parameter>参数的值指定为更大的值以使输出更容易阅读。</para>
54 <example>
55 <title>&apihelper_filename; 的高级用法</title>
56 <screen>&prompt;<userinput>import &odbchelper_name;</userinput>
57 &prompt;<userinput>info(&odbchelper_name;)</userinput>
58 <computeroutput>&odbchelper_funcname; Build a connection string from a dictionary Returns string.</computeroutput>
59 &prompt;<userinput>info(&odbchelper_name;, 30)</userinput>
60 <computeroutput>&odbchelper_funcname; Build a connection string from a dictionary Returns string.</computeroutput>
61 &prompt;<userinput>info(&odbchelper_name;, 30, 0)</userinput>
62 <computeroutput>&odbchelper_funcname; Build a connection string from a dictionary
63
64 Returns string.
65 </computeroutput></screen>
66 </example>
67 </section>
68 <section id="apihelper.optional">
69 <?dbhtml filename="power_of_introspection/optional_arguments.html"?>
70 <title>使用可选参数和命名参数</title>
71 <abstract>
72 <title/>
73 <para>&python; 允许函数参数有缺省值;如果调用函数时不使用参数,参数将获得它的缺省值。此外,通过使用命名参数还可以以任意顺序指定参数。&sqlserver; &tsql; 中的存储过程也可以做到这些;如果你是脚本高手,你可以略过这部分。</para>
74 </abstract>
75 <informalexample>
76 <!--<title>&info;, a Function with Two Optional Arguments</title>-->
77 <para>&info; 函数就是这样一个例子,它有两个可选参数。</para>
78 <programlisting>
79 &apihelper_funcdef;</programlisting>
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>
83 <example>
84 <title>&info; 的有效调用</title>
85 <programlisting>
86 info(&odbchelper_name;) <co id="apihelper_args.1.1"/>
87 info(&odbchelper_name;, 12) <co id="apihelper_args.1.2"/>
88 info(&odbchelper_name;, collapse=0) <co id="apihelper_args.1.3"/>
89 info(spacing=15, object=&odbchelper_name;) <co id="apihelper_args.1.4"/></programlisting>
90 <calloutlist>
91 <callout arearefs="apihelper_args.1.1">
92 <para>只使用一个参数,<varname>spacing</varname> 使用缺省值 <literal>10</literal> ,<varname>collapse</varname> 使用缺省值 &one;。</para>
93 </callout>
94 <callout arearefs="apihelper_args.1.2">
95 <para>使用两个参数, <varname>collapse</varname> 使用缺省值 &one;。</para>
96 </callout>
97 <callout arearefs="apihelper_args.1.3">
98 <para>这里你显式命名了<varname>collapse</varname>并指定了它的值。<varname>spacing</varname> 将依然使用它的缺省值 <literal>10</literal>。</para>
99 </callout>
100 <callout arearefs="apihelper_args.1.4">
101 <para>甚至必备参数 (例如 <varname>object</varname>,没有指定缺省值) 也可以采用命名参数的方式, 而且命名参数可以以任意顺序出现。</para>
102 </callout>
103 </calloutlist>
104 </example>
105 <para>这些看上去非常累,除非你意识到参数不过是一个字典。<quote>通常</quote> 不使用参数名称的函数调用只是一个简写的形式,&python; 按照函数声明中定义的的参数顺序将参数值和参数名称匹配起来。大部分时间,你会使用<quote>通常</quote>方式调用函数,但是如果你需要总是可以提供附加的灵活性。</para>
106 <note id="tip.arguments">
107 <title>灵活的函数调用</title>
108 <para>调用函数时唯一需要做的事情就是为每一个必备参数指定值(以某种方式);以何种具体的方式和顺序都取决于你。</para>
109 </note>
110 <itemizedlist role="furtherreading">
111 <title>进一步阅读</title>
112 <listitem><para>&pythontutorial; 确切地讨论了 <ulink url="&url_pythontutorial;node6.html#SECTION006710000000000000000">何时、如何进行缺省参数赋值</ulink>,这都和缺省值是一个list还是一个具有副作用的表达式有关。</para></listitem>
113 </itemizedlist>
114 </section>
115 <section id="apihelper.builtin">
116 <?dbhtml filename="power_of_introspection/built_in_functions.html"?>
117 <title>使用&type;、&str;、&dir; 和其它内置函数</title>
118 <abstract>
119 <title/>
120 <para>&python; 有小部分相当有用的内置函数。除这些函数之外,其它所有的函数都被分到了各个模块中。其实这是一个非常明智的设计决策,避免了核心语言变得像其它脚本语言一样臃肿(咳 咳,&vb;)。</para>
121 </abstract>
122 <section>
123 <title>&type; 函数</title>
124 <para>&type; 函数返回任意对象的数据型别。在<filename class="headerfile">types</filename>模块中列出了可能的型别。这对于帮助者函数处理不同种型别的数据非常有用。</para>
125 <example id="apihelper.type.intro">
126 <title>&type; 介绍</title>
127 <screen>&prompt;<userinput>type(1)</userinput> <co id="apihelper.builtin.1.1"/>
128 <computeroutput><type 'int'></computeroutput>
129 &prompt;<userinput>li = []</userinput>
130 &prompt;<userinput>type(li)</userinput> <co id="apihelper.builtin.1.2"/>
131 <computeroutput><type 'list'></computeroutput>
132 &prompt;<userinput>import odbchelper</userinput>
133 &prompt;<userinput>type(odbchelper)</userinput> <co id="apihelper.builtin.1.3"/>
134 <computeroutput><type 'module'></computeroutput>
135 &prompt;<userinput>import types</userinput> <co id="apihelper.builtin.1.4"/>
136 &prompt;<userinput>type(odbchelper) == types.ModuleType</userinput>
137 <computeroutput>True</computeroutput></screen>
138 <calloutlist>
139 <callout arearefs="apihelper.builtin.1.1">
140 <para>&type; 可以接收任何东西作为参数 -- 我的意思是任何东西 -- 并返回它的数据型别。整型、字符串、列表、字典、元组、函数、类、模块、甚至类型对象都可以作为参数被type函数接受。</para>
141 </callout>
142 <callout arearefs="apihelper.builtin.1.2">
143 <para>&type; 可以接收变量作为参数,并返回它的数据型别。</para>
144 </callout>
145 <callout arearefs="apihelper.builtin.1.3">
146 <para>&type; 还可以作用于模块。</para>
147 </callout>
148 <callout arearefs="apihelper.builtin.1.4">
149 <para>你可以使用<filename class="headerfile">types</filename>模块中的常量来进行对象型别的比较。这就是&info; 函数所做的,很快你就会看到。</para>
150 </callout>
151 </calloutlist>
152 </example>
153 </section>
154 <section>
155 <title>&str; 函数</title>
156 <para>&str; 将数据强制转型为字符串。每种数据型别都可以强制转型为字符串。</para>
157 <example id="apihelper.str.intro">
158 <title>&str; 介绍</title>
159 <screen>
160 &prompt;<userinput>str(1)</userinput> <co id="apihelper.builtin.2.1"/>
161 <computeroutput>'1'</computeroutput>
162 &prompt;<userinput>horsemen = ['war', 'pestilence', 'famine']</userinput>
163 &prompt;<userinput>horsemen</userinput>
164 <computeroutput>['war', 'pestilence', 'famine']</computeroutput>
165 &prompt;<userinput>horsemen.append('&powerbuilder;')</userinput>
166 &prompt;<userinput>str(horsemen)</userinput> <co id="apihelper.builtin.2.2"/>
167 <computeroutput>"['war', 'pestilence', 'famine', '&powerbuilder;']"</computeroutput>
168 &prompt;<userinput>str(odbchelper)</userinput> <co id="apihelper.builtin.2.3"/>
169 <computeroutput>"<module 'odbchelper' from 'c:\\docbook\\dip\\py\\odbchelper.py'>"</computeroutput>
170 &prompt;<userinput>str(None)</userinput> <co id="apihelper.builtin.2.4"/>
171 <computeroutput>'None'</computeroutput></screen>
172 <calloutlist>
173 <callout arearefs="apihelper.builtin.2.1">
174 <para>对于简单的数据型别比如整型,你可以预料到 &str; 的正常工作,因为几乎每种语言都有一个将整型转化为字符串的函数。</para>
175 </callout>
176 <callout arearefs="apihelper.builtin.2.2">
177 <para>然而 &str; 可以作用于任何型别的任何对象。这里它作用于一个零碎构建的列表。</para>
178 </callout>
179 <callout arearefs="apihelper.builtin.2.3">
180 <para>&str; 还允许作用于模块。注意模块的字符串形式表示包含了模块在磁盘上的路径名,所以你的显示结果将会有所不同。</para>
181 </callout>
182 <callout arearefs="apihelper.builtin.2.4">
183 <para>&str; 的一个细小但重要的行为是它可以作用于&none;,&none; 是&python;的null值。这个调用返回字符串<literal>'None'</literal>。你将会使用这一点来提高你的 &info; 函数,这一点你很快就会看到。</para>
184 </callout>
185 </calloutlist>
186 </example>
187 <para>&info; 函数的核心是强大的 &dir; 函数。&dir;函数返回任意对象的属性和方法列表,包括模块对象、函数对象、字符串对象、列表对象、字典对象...相当多的东西。</para>
188 <example id="apihelper.dir.intro">
189 <title>&dir; 介绍</title>
190 <screen>&prompt;<userinput>li = []</userinput>
191 &prompt;<userinput>dir(li)</userinput> <co id="apihelper.builtin.3.1"/>
192 <computeroutput>['append', 'count', 'extend', 'index', 'insert',
193 'pop', 'remove', 'reverse', 'sort']</computeroutput>
194 &prompt;<userinput>d = {}</userinput>
195 &prompt;<userinput>dir(d)</userinput> <co id="apihelper.builtin.3.2"/>
196 <computeroutput>['clear', 'copy', 'get', 'has_key', 'items', 'keys', 'setdefault', 'update', 'values']</computeroutput>
197 &prompt;<userinput>import &odbchelper_name;</userinput>
198 &prompt;<userinput>dir(&odbchelper_name;)</userinput> <co id="apihelper.builtin.3.3"/>
199 <computeroutput>['__builtins__', '__doc__', '__file__', '__name__', '&odbchelper_funcname;']</computeroutput></screen>
200 <calloutlist>
201 <callout arearefs="apihelper.builtin.3.1">
202 <para><varname>li</varname> 是一个列表,所以<literal>&dir;(<varname>li</varname>)</literal> 返回所有列表方法的一个列表。注意返回的列表只包含了字符串形式的方法名称,而不是方法对象本身。</para>
203 </callout>
204 <callout arearefs="apihelper.builtin.3.2">
205 <para><varname>d</varname> 是一个字典,所以<literal>&dir;(<varname>d</varname>)</literal> 返回字典方法的名称列表。其中至少有一个方法,<link linkend="odbchelper.items">&keys;</link>,看起来还是挺熟悉的。</para>
206 </callout>
207 <callout arearefs="apihelper.builtin.3.3">
208 <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>
209 </callout>
210 </calloutlist>
211 </example>
212 <para>最后是 &callable; 函数,它接收任何对象作为参数,如果参数对象是可调用的那么返回&true;,否则返回&false;。可调用对象包括函数、类方法,甚至类自身。(下一章将更多的关注类)。</para>
213 <example id="apihelper.builtin.callable">
214 <title>&callable; 介绍</title>
215 <screen>
216 &prompt;<userinput>import string</userinput>
217 &prompt;<userinput>string.punctuation</userinput> <co id="apihelper.builtin.4.1"/>
218 <computeroutput>'!"#$&pct;&\'()*+,-./:;<=>?@[\\]^_`{|}~'</computeroutput>
219 &prompt;<userinput>string.join</userinput> <co id="apihelper.builtin.4.2"/>
220 <computeroutput><function join at 00C55A7C></computeroutput>
221 &prompt;<userinput>callable(string.punctuation)</userinput> <co id="apihelper.builtin.4.3"/>
222 <computeroutput>False</computeroutput>
223 &prompt;<userinput>callable(string.join)</userinput> <co id="apihelper.builtin.4.4"/>
224 <computeroutput>True</computeroutput>
225 &prompt;<userinput>print string.join.__doc__</userinput> <co id="apihelper.builtin.4.5"/>
226 <computeroutput>join(list [,sep]) -> string
227
228 Return a string composed of the words in list, with
229 intervening occurrences of sep. The default separator is a
230 single space.
231
232 (joinfields and join are synonymous)</computeroutput></screen>
233 <calloutlist>
234 <callout arearefs="apihelper.builtin.4.1">
235 <para>&string; 模块中的函数现在已经不赞成使用了(尽管很多人现在仍然还在使用&join;函数),但是在这个模块中包含了许多有用的变量比如<varname>string.punctuation</varname>, 这个模块包含了所有标准的标点符号字符</para>
236 </callout>
237 <callout arearefs="apihelper.builtin.4.2">
238 <para><link linkend="odbchelper.join"><function>string.join</function></link>是一个用于连接字符串列表的函数。</para>
239 </callout>
240 <callout arearefs="apihelper.builtin.4.3">
241 <para><varname>string.punctuation</varname>是不可调用的对象;它是一个字符串。(字符串确有可调用的方法,但是字符串本身不是可调用的。)</para>
242 </callout>
243 <callout arearefs="apihelper.builtin.4.4">
244 <para><function>string.join</function>是可调用的;这个函数可以接受两个参数。</para>
245 </callout>
246 <callout arearefs="apihelper.builtin.4.5">
247 <para>任何可调用的对象都有 &docstring;。通过将 &callable; 函数作用于一个对象的每个属性,可以确定哪些属性(方法、函数、类)是你要关注的,哪些属性(常量等等)是你可以忽略、之前不需要知道的。</para>
248 </callout>
249 </calloutlist>
250 </example>
251 </section>
252 <section>
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>
256 <example id="apihelper.builtin.list">
257 <title>内置属性和内置函数</title>
258 <screen>&prompt;<userinput>from apihelper import info</userinput>
259 &prompt;<userinput>import __builtin__</userinput>
260 &prompt;<userinput>info(__builtin__, 20)</userinput>
261 <computeroutput>ArithmeticError Base class for arithmetic errors.
262 AssertionError Assertion failed.
263 AttributeError Attribute not found.
264 EOFError Read beyond end of file.
265 EnvironmentError Base class for I/O related errors.
266 Exception Common base class for all exceptions.
267 FloatingPointError Floating point operation failed.
268 IOError I/O operation failed.
269
270 [...snip...]</computeroutput></screen>
271 </example>
272 <note id="tip.manuals">
273 <title>&python; 是自文档化的</title>
274 <para>&python; 提供了很多出色的参考手册,你应该好好的精读一下所有 &python; 提供的必备 模块。对于其它大部分语言,你会发现自己要常常回头参考手册或者man页来提醒自己如何使用这些模块,但是&python;不同于此,它很大程度上是自文档化的。</para>
275 </note>
276 <itemizedlist role="furtherreading">
277 <title>进一步阅读</title>
278 <listitem><para>&pythonlibraryreference; 对<ulink url="&url_pythonlibraryreference;built-in-funcs.html">所有的内置函数</ulink> 和 <ulink url="&url_pythonlibraryreference;module-exceptions.html">所有的内置异常</ulink>都进行了文档化。</para></listitem>
279 </itemizedlist>
280 </section>
281 </section>
282 <section id="apihelper.getattr">
283 <?dbhtml filename="power_of_introspection/getattr.html"?>
284 <title>通过&getattr;获取对象引用</title>
285 <abstract>
286 <title/>
287 <para>你已经知道<link linkend="odbchelper.objects">&python; 函数是对象</link>。 你不知道的是,使用&getattr;函数,可以得到一个直到运行时才知道名称的函数的引用.</para>
288 </abstract>
289 <example id="apihelper.getattr.intro">
290 <title>&getattr;介绍</title>
291 <screen>&prompt;<userinput>li = ["Larry", "Curly"]</userinput>
292 &prompt;<userinput>li.pop</userinput> <co id="apihelper.getattr.1.1"/>
293 <computeroutput><built-in method pop of list object at 010DF884></computeroutput>
294 &prompt;<userinput>getattr(li, "pop")</userinput> <co id="apihelper.getattr.1.2"/>
295 <computeroutput><built-in method pop of list object at 010DF884></computeroutput>
296 &prompt;<userinput>getattr(li, "append")("Moe")</userinput> <co id="apihelper.getattr.1.3"/>
297 &prompt;<userinput>li</userinput>
298 <computeroutput>["Larry", "Curly", "Moe"]</computeroutput>
299 &prompt;<userinput>getattr({}, "clear")</userinput> <co id="apihelper.getattr.1.4"/>
300 <computeroutput><built-in method clear of dictionary object at 00F113D4></computeroutput>
301 &prompt;<userinput>getattr((), "pop")</userinput> <co id="apihelper.getattr.1.5"/>
302 <computeroutput role="traceback">&traceback;
303 AttributeError: 'tuple' object has no attribute 'pop'</computeroutput></screen>
304 <calloutlist>
305 <callout arearefs="apihelper.getattr.1.1">
306 <para>该语句获取列表的 &pop; 方法的引用。注意该语句并不是调用 &pop; 方法;调用pop方法应该是 <literal>li.pop()</literal>。这里指的是方法对象本身。</para>
307 </callout>
308 <callout arearefs="apihelper.getattr.1.2">
309 <para>该语句也是返回 &pop; 方法的引用,但是此时,方法名称是作为一个字符串参数传递给&getattr;函数的。&getattr;是一个有用到令人无法致信的内置函数,可以返回任何对象的任何属性。在这个例子中,对象是一个list,属性是 &pop; 方法。</para>
310 </callout>
311 <callout arearefs="apihelper.getattr.1.3">
312 <para>如果不深信它是多么的有用,试试这个:&getattr; 的返回值<emphasis>是</emphasis> 方法,然后你就可以调用它就像直接使用 <literal>li.append("Moe")</literal>一样。但是实际上你没有直接调用函数;只是以字符串形式指定了函数名称。</para>
313 </callout>
314 <callout arearefs="apihelper.getattr.1.4">
315 <para>&getattr; 也可以作用于字典。</para>
316 </callout>
317 <callout arearefs="apihelper.getattr.1.5">
318 <para>理论上, &getattr; 可以作用于元组,但是由于<link linkend="odbchelper.tuplemethods">元组没有方法</link>, 所以不管你指定什么属性名称&getattr; 都会引发一个异常。</para>
319 </callout>
320 </calloutlist>
321 </example>
322 <section>
323 <title>用于模块的 &getattr;</title>
324 <para>&getattr; 不仅仅适用于内置数据型别,也可作用于模块。</para>
325 <example id="apihelper.getattr.example">
326 <title>&apihelper_filename; 中的&getattr; 函数</title>
327 <screen>&prompt;<userinput>import &odbchelper_name;</userinput>
328 &prompt;<userinput>&odbchelper_name;.&odbchelper_funcname;</userinput> <co id="apihelper.getattr.2.1"/>
329 <computeroutput><function &odbchelper_funcname; at 00D18DD4></computeroutput>
330 &prompt;<userinput>getattr(&odbchelper_name;, "&odbchelper_funcname;")</userinput> <co id="apihelper.getattr.2.2"/>
331 <computeroutput><function &odbchelper_funcname; at 00D18DD4></computeroutput>
332 &prompt;<userinput>object = &odbchelper_name;</userinput>
333 &prompt;<userinput>method = "&odbchelper_funcname;"</userinput>
334 &prompt;<userinput>getattr(object, method)</userinput> <co id="apihelper.getattr.2.3"/>
335 <computeroutput><function &odbchelper_funcname; at 00D18DD4></computeroutput>
336 &prompt;<userinput>type(getattr(object, method))</userinput> <co id="apihelper.getattr.2.4"/>
337 <computeroutput><type 'function'></computeroutput>
338 &prompt;<userinput>import types</userinput>
339 &prompt;<userinput>type(getattr(object, method)) == types.FunctionType</userinput>
340 <computeroutput>True</computeroutput>
341 &prompt;<userinput>callable(getattr(object, method))</userinput> <co id="apihelper.getattr.2.5"/>
342 <computeroutput>True</computeroutput></screen>
343 <calloutlist>
344 <callout arearefs="apihelper.getattr.2.1">
345 <para>该语句返回 &odbchelper_modulename; 模块中 &odbchelper_function; 函数的引用,<xref linkend="odbchelper"/>你已经研习过这个方法了。(你看到的这个十六进制地址是我机器上的;你的输出结果会有所不同。)</para>
346 </callout>
347 <callout arearefs="apihelper.getattr.2.2">
348 <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>
349 </callout>
350 <callout arearefs="apihelper.getattr.2.3">
351 <para>接下来的是你真正用在 &info; 函数中的东西。<varname>object</varname> 作为一个参数传递给函数; <varname>method</varname> 是方法或者函数的名称字符串。</para>
352 </callout>
353 <callout arearefs="apihelper.getattr.2.4">
354 <para>在这个例子中, <varname>method</varname> 是函数的名称,通过获取 <link linkend="apihelper.type.intro">&type;</link> 可以进行验证。</para>
355 </callout>
356 <callout arearefs="apihelper.getattr.2.5">
357 <para>由于 <varname>method</varname> 是一个函数,所以它是<link linkend="apihelper.builtin.callable">可调用的</link>。</para>
358 </callout>
359 </calloutlist>
360 </example>
361 </section>
362 <section>
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>
366 <example id="apihelper.getattr.dispatch">
367 <title>使用&getattr; 创建分发者</title>
368 <programlisting>
369 import statsout
370
371 def output(data, format="text"): <co id="apihelper.getattr.3.1"/>
372 output_function = getattr(statsout, "output_%s" % format) <co id="apihelper.getattr.3.2"/>
373 return output_function(data) <co id="apihelper.getattr.3.3"/>
374 </programlisting>
375 </example>
376 <calloutlist>
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>
379 </callout>
380 <callout arearefs="apihelper.getattr.3.2">
381 <para>你可以连接<varname>format</varname> 参数值和"output_"来创建一个函数名称作为参数值,然后从<filename>statsout</filename>模块中取得该函数。这种方式允许今后很容易的扩展程序以支持其它的输出格式,而且无需修改分发函数。所要做的仅仅是向<filename>statsout</filename>中添加一个函数,比如<function>output_pdf</function>,之后只要将“pdf”作为<varname>format</varname>的参数值传递给<function>output</function> 函数即可。</para>
382 </callout>
383 <callout arearefs="apihelper.getattr.3.3">
384 <para>现在你可以简单的调用输出函数就像调用其它函数一样了。<varname>output_function</varname> 变量是指向<filename>statsout</filename> 模块中相应函数的引用。</para>
385 </callout>
386 </calloutlist>
387 <para>你是否发现前面示例的一个Bug?即字符串和函数之间的松耦合,而且没有错误检查。如果用户传入一个格式参数,但是在<filename>statsout</filename>中没有定义相应的格式输出函数,会发生什么呢?还好,&getattr; 会返回&none;,它会取代一个有效函数并被赋值给<varname>output_function</varname>,然后下一行调用函数的语句将会失败并抛出一个异常。这种方式不好。</para>
388 <para>值得庆幸的是,&getattr; 能够使用可选的第三个参数,一个缺省返回值。</para>
389 <example id="apihelper.getattr.default">
390 <title>&getattr; 缺省值</title>
391 <programlisting>
392 import statsout
393
394 def output(data, format="text"):
395 output_function = getattr(statsout, "output_%s" % format, statsout.output_text)
396 return output_function(data) <co id="apihelper.getattr.4.1"/>
397 </programlisting>
398 <calloutlist>
399 <callout arearefs="apihelper.getattr.4.1">
400 <para>这个函数调用确保可以工作,因为你在调用&getattr; 时添加了第三个参数。第三个参数是一个缺省返回值,如果第二个参数指定的属性或者方法没有找到将返回这个缺省返回值。</para>
401 </callout>
402 </calloutlist>
403 </example>
404 <para>正如你所看到,&getattr; 是相当强大的。它是自省的核心,在后面的章节中你将看到它更强大的示例。</para>
405 </section>
406 </section>
407 <section id="apihelper.filter">
408 <?dbhtml filename="power_of_introspection/filtering_lists.html"?>
409 <title>过滤列表</title>
410 <abstract>
411 <title/>
412 <para>如你所知,&python; 具有通过列表解析(<xref linkend="odbchelper.map"/>)将列表映射到其它列表的强大能力。这种能力同过滤机制结合使用,使列表中的有些元素可以被映射而有些则一概跳过。</para>
413 </abstract>
414 <informalexample>
415 <!--<title>List Filtering Syntax</title>-->
416 <para>过滤列表语法:</para>
417 <programlisting>
418 [<replaceable>mapping-expression</replaceable> for <replaceable>element</replaceable> in <replaceable>source-list</replaceable> if <replaceable>filter-expression</replaceable>]</programlisting>
419 </informalexample>
420 <para>这是你所知所爱的<link linkend="odbchelper.map">列表解析</link>的扩展。前三部分都是相同的;最后一部分,以&if;开头的是过滤器表达式。过滤器表达式可以是返回值为真或者假(在&python;是<link linkend="tip.boolean">几乎任何东西</link>)的任何表达式。任何经过滤器表达式演算值为元素的真都可以包含在映射中。其它的元素都将忽略,它们不会进入映射表达式,更不会包含在输出列表中。</para>
421 <example>
422 <title>列表过滤介绍</title>
423 <screen>&prompt;<userinput>li = ["a", "mpilgrim", "foo", "b", "c", "b", "d", "d"]</userinput>
424 &prompt;<userinput>[elem for elem in li if len(elem) > 1]</userinput> <co id="apihelper.filter.1.1"/>
425 <computeroutput>['mpilgrim', 'foo']</computeroutput>
426 &prompt;<userinput>[elem for elem in li if elem != "b"]</userinput> <co id="apihelper.filter.1.2"/>
427 <computeroutput>['a', 'mpilgrim', 'foo', 'c', 'd', 'd']</computeroutput>
428 &prompt;<userinput>[elem for elem in li if li.count(elem) == 1]</userinput> <co id="apihelper.filter.1.3"/>
429 <computeroutput>['a', 'mpilgrim', 'foo', 'c']</computeroutput></screen>
430 <calloutlist>
431 <callout arearefs="apihelper.filter.1.1">
432 <para>这里的映射表达式很简单(只是返回每个元素的值),所以请把注意力集中到过滤器表达式上。由于&python; 会遍历整个列表,它将对每个元素执行过滤器表达式。如果过滤器表达式演算值为真,该元素就会被映射,同时映射表达式的结果将包含在返回的列表中。这里,你过滤掉了所有单字符的字符串,留下了一个都是长字符串的列表。</para>
433 </callout>
434 <callout arearefs="apihelper.filter.1.2">
435 <para>这里你过滤掉了一个特定值<literal>b</literal>。 注意这个过滤器会过滤掉所有的 <literal>b</literal>,因为每次取出 <literal>b</literal>,过滤表达式都将为假。</para>
436 </callout>
437 <callout arearefs="apihelper.filter.1.3">
438 <para>&count; 是一个列表方法,返回某个值在列表中出现的次数。你可以认为这个过滤器将从列表中剔除重复元素,返回一个只包含了在原始列表中有着唯一值拷贝的列表。但是不是这样的,因为在原始列表中出现两次的值(在本例中,<literal>b</literal> 和 <literal>d</literal>)被完全剔除了。存在从一个列表排除重复值的方法,但过滤并非解决之道。</para>
439 </callout>
440 </calloutlist>
441 </example>
442 <informalexample id="apihelper.filter.care">
443 <!--<title>Filtering a List in &apihelper_filename;</title>-->
444 <para>回到 &apihelper_filename; 中的这一行:</para>
445 <programlisting>
446 &apihelper_methods;</programlisting>
447 </informalexample>
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">∈</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 <itemizedlist role="furtherreading">
452 <title>进一步阅读</title>
453 <listitem><para>&pythontutorial; 讨论了<ulink url="&url_pythontutorial;node7.html#SECTION007130000000000000000">使用内置<function>过滤器</function>函数</ulink>过滤列表的另一种方式。</para></listitem>
454 </itemizedlist>
455 </section>
456 <section id="apihelper.andor">
457 <?dbhtml filename="power_of_introspection/and_or.html"?>
458 <title>&andfunction; 和 &orfunction; 的特殊性质</title>
459 <abstract>
460 <title/>
461 <para>在&python; 中,&andfunction; 和 &orfunction; 执行布尔逻辑演算,如你所期待的一样,但是它们并不返回布尔值;而是,返回它们实际进行比较的值之一。</para>
462 </abstract>
463 <example id="apihelper.andor.intro.example">
464 <title>&andfunction; 介绍</title>
465 <screen>&prompt;<userinput>'a' and 'b'</userinput> <co id="apihelper.andor.1.1"/>
466 <computeroutput>'b'</computeroutput>
467 &prompt;<userinput>'' and 'b'</userinput> <co id="apihelper.andor.1.2"/>
468 <computeroutput>''</computeroutput>
469 &prompt;<userinput>'a' and 'b' and 'c'</userinput> <co id="apihelper.andor.1.3"/>
470 <computeroutput>'c'</computeroutput></screen>
471 <calloutlist>
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>
474 </callout>
475 <callout arearefs="apihelper.andor.1.2">
476 <para>如果布尔上下文中的某个值为假,则 &andfunction; 返回第一个假值。在这个例子中,<literal>''</literal> 是第一个假值。</para>
477 </callout>
478 <callout arearefs="apihelper.andor.1.3">
479 <para>所有值都为真,所以 &andfunction; 返回最后一个真值,<literal>'c'</literal>。</para>
480 </callout>
481 </calloutlist>
482 </example>
483 <example>
484 <title>&orfunction; 介绍</title>
485 <screen>&prompt;<userinput>'a' or 'b'</userinput> <co id="apihelper.andor.2.1"/>
486 <computeroutput>'a'</computeroutput>
487 &prompt;<userinput>'' or 'b'</userinput> <co id="apihelper.andor.2.2"/>
488 <computeroutput>'b'</computeroutput>
489 &prompt;<userinput>'' or [] or {}</userinput> <co id="apihelper.andor.2.3"/>
490 <computeroutput>{}</computeroutput>
491 &prompt;<userinput>def sidefx():</userinput>
492 &continuationprompt;<userinput>print "in sidefx()"</userinput>
493 &continuationprompt;<userinput>return 1</userinput>
494 &prompt;<userinput>'a' or sidefx()</userinput> <co id="apihelper.andor.2.4"/>
495 <computeroutput>'a'</computeroutput></screen>
496 <calloutlist>
497 <callout arearefs="apihelper.andor.2.1">
498 <para>使用 &orfunction; 时,在布尔上下文中从左到右演算值,就像 &andfunction; 一样。如果有一个值为真,&orfunction; 立刻返回该值。本例中,<literal>'a'</literal> 是第一个真值。</para>
499 </callout>
500 <callout arearefs="apihelper.andor.2.2">
501 <para>&orfunction; 演算 <literal>''</literal> 的值为假,然后演算 <literal>'b'</literal> 的值为真,于是返回 <literal>'b'</literal> 。</para>
502 </callout>
503 <callout arearefs="apihelper.andor.2.3">
504 <para>如果所有的值都为假,&orfunction; 返回最后一个假值。&orfunction; 演算 <literal>''</literal> 的值为假,然后演算 <literal>[]</literal> 的值为假,依次演算 <literal>{}</literal> 的值为假,最终返回 <literal>{}</literal> 。</para>
505 </callout>
506 <callout arearefs="apihelper.andor.2.4">
507 <para>注意 &orfunction; 在布尔上下文中会一直进行表达式演算直到找到第一个真值,然后就会忽略剩余的比较值。如果某些值具有副作用,这种特性就非常重要了。在这里,函数<function>sidefx</function> 永远都不会被调用,因为 &orfunction; 演算 <literal>'a'</literal> 的值为真,所以紧接着就立刻返回 <literal>'a'</literal> 了。</para>
508 </callout>
509 </calloutlist>
510 </example>
511 <para>如果你是一名 &c; 语言黑客,肯定很熟悉&candor; 表达式,如果 <replaceable>bool</replaceable> 为真,表达式演算值为 <varname>a</varname>,否则为 <varname>b</varname>。基于 &python; 中 &andfunction; 和 &orfunction; 的工作方式,你可以完成相同的事情。</para>
512 <section>
513 <title>使用 &andor; 技巧</title>
514 <example id="apihelper.andortrick.intro">
515 <title>&andor; 技巧介绍</title>
516 <screen>&prompt;<userinput>a = "first"</userinput>
517 &prompt;<userinput>b = "second"</userinput>
518 &prompt;<userinput>1 and a or b</userinput> <co id="apihelper.andor.3.1"/>
519 <computeroutput>'first'</computeroutput>
520 &prompt;<userinput>0 and a or b</userinput> <co id="apihelper.andor.3.2"/>
521 <computeroutput>'second'</computeroutput>
522 </screen>
523 <calloutlist>
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>
526 </callout>
527 <callout arearefs="apihelper.andor.3.2">
528 <para><literal>0 and 'first'</literal> 演算值为&false;,然后 <literal>0 or 'second'</literal> 演算值为 <literal>'second'</literal>。</para>
529 </callout>
530 </calloutlist>
531 </example>
532 <para>然而,由于这种 &python; 表达式单单只是进行布尔逻辑运算,并不是语言的特定构成,这是 &andor; 技巧和 &c; 语言中的 &candor; 语法非常重要的不同。如果 <varname>a</varname> 为假,表达式就不会按你期望的那样工作了。(你能知道我被这个问题折腾过吗?不只一次?)</para>
533 <example>
534 <title>&andor; 技巧无效的场合</title>
535 <screen>&prompt;<userinput>a = ""</userinput>
536 &prompt;<userinput>b = "second"</userinput>
537 &prompt;<userinput>1 and a or b</userinput> <co id="apihelper.andor.4.1"/>
538 <computeroutput>'second'</computeroutput></screen>
539 <calloutlist>
540 <callout arearefs="apihelper.andor.4.1">
541 <para>由于 <varname>a</varname> 是一个空字符串,在 &python; 的布尔上下文中空字符串被认为是假的,<literal>1 and ''</literal> 的演算值为 <literal>''</literal>,最后 <literal>'' or 'second'</literal> 的演算值为 <literal>'second'</literal>。噢!这个值并不是你想要的。</para>
542 </callout>
543 </calloutlist>
544 </example>
545 <para>&andor; 技巧,<literal><replaceable>bool</replaceable> and <varname>a</varname> or <varname>b</varname></literal>表达式,当 <varname>a</varname> 在布尔上下文中的值为假时,不会像 &c; 语言表达式 &candor; 那样工作。</para>
546 <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>
547 <example>
548 <title>安全使用 &andor; 技巧</title>
549 <screen>&prompt;<userinput>a = ""</userinput>
550 &prompt;<userinput>b = "second"</userinput>
551 &prompt;<userinput>(1 and [a] or [b])[0]</userinput> <co id="apihelper.andor.5.1"/>
552 <computeroutput>''</computeroutput></screen>
553 <calloutlist>
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>
556 </callout>
557 </calloutlist>
558 </example>
559 <para>到现在为止,这个技巧可能看上去问题超过了它的价值。毕竟,使用&if; 语句可以完成相同的事情,那为什么要经历这些麻烦事呢?哦,在很多情况下,你要在两个常量值中进行选择,由于你知道 <varname>a</varname> 的值总是为真,所以你可以使用这种较为简单的语法而且不用担心。对于使用更为复杂的安全形式,依然有很好的理由要求这样做。例如,在&python; 语言的某些情况下 &if; 语句是不允许使用的,比如在&lambdafunction; 函数中。</para>
560 <itemizedlist role="furtherreading">
561 <title>进一步阅读</title>
562 <listitem><para>&pythoncookbook; 讨论了 <ulink url="&url_pythoncookbook;Recipe/52310">其它的 &andor; 技巧</ulink>。</para></listitem>
563 </itemizedlist>
564 </section>
565 </section>
566 <section id="apihelper.lambda">
567 <?dbhtml filename="power_of_introspection/lambda_functions.html"?>
568 <title>使用 &lambdafunction; 函数</title>
569 <abstract>
570 <title/>
571 <para>&python; 支持一种有趣的语法,它允许你快速定义单行的最小函数。这些叫做 &lambdafunction; 的函数,是从 &lisp; 借用来的,可以用在任何需要函数的地方。</para>
572 </abstract>
573 <example>
574 <title>&lambdafunction; 函数介绍</title>
575 <screen>&prompt;<userinput>def f(x):</userinput>
576 &continuationprompt;<userinput>return x*2</userinput>
577 &continuationprompt;<userinput/>
578 &prompt;<userinput>f(3)</userinput>
579 <computeroutput>6</computeroutput>
580 &prompt;<userinput>g = lambda x: x*2</userinput> <co id="apihelper.lambda.1.2"/>
581 &prompt;<userinput>g(3)</userinput>
582 <computeroutput>6</computeroutput>
583 &prompt;<userinput>(lambda x: x*2)(3)</userinput> <co id="apihelper.lambda.1.3"/>
584 <computeroutput>6</computeroutput></screen>
585 <calloutlist>
586 <callout arearefs="apihelper.lambda.1.2">
587 <para>这是一个 &lambdafunction; 函数,完成成同上面普通函数相同的事情。注意这里的简短的语法:在参数列表周围没有括号,而且忽略了 <literal>return</literal> 关键字() (it is implied, since the entire function can only be one expression). 而且,该函数没有函数名称,但是可以将它赋值给一个变量进行调用。</para>
588 </callout>
589 <callout arearefs="apihelper.lambda.1.3">
590 <para>你可以使用 &lambdafunction; 函数甚至不需要将它赋值给一个变量。这可能不是最有用的东西 in the world,它只是展示了 lambda 函数只是一个内联函数。</para>
591 </callout>
592 </calloutlist>
593 </example>
594 <para>总的来说,&lambdafunction; 函数可以接收任意多个参数(包括 <link linkend="apihelper.optional">可选参数</link>)并且返回单个表达式的值。&lambdafunction; 函数不能包含命令,包含的表达式不能超过一个。不要试图向 &lambdafunction; 函数中塞入太多的东西;如果你需要更复杂的东西,应该定义一个普通函数,然后想让它多长就多常。</para>
595 <note id="tip.lambda">
596 <title>&lambdafunction; 是可选的</title>
597 <para>&lambdafunction; 函数是一种风格问题。不一定非要使用它们;任何能够使用它们的地方,都可以定义一个单独的普通函数来进行替换。我将它们用在需要封装特殊的、非重用的代码上 without littering my code with a lot of little one-line functions.</para>
598 </note>
599 <section>
600 <title>真实世界中的 &lambdafunction; 函数</title>
601 <informalexample>
602 <!--<title>&lambdafunction; Functions in &apihelper_filename;</title>-->
603 <para>&apihelper_filename; 中的 &lambdafunction; 函数:</para>
604 <programlisting>
605 &apihelper_lambda;</programlisting>
606 </informalexample>
607 <para>注意这里使用了 <link linkend="apihelper.andor">&andor;</link> 技巧的简单形式,它是没问题的,因为 &lambdafunction; 函数 <link linkend="tip.boolean">在布尔上下文中</link>总是为真。(这并不意味这 &lambdafunction; 函数不能返回假值。这个函数对象的布尔值为真;它的返回值可以是任何东西。)</para>
608 <para>还要注意的是使用了没有参数的 &split; 函数。你已经看到过它带<link linkend="odbchelper.split.example">一个或者两个参数</link>的使用,但是不带参数它按空白进行分割。</para>
609 <example>
610 <title>&split; 不带参数</title>
611 <screen>&prompt;<userinput>s = "this is\na\ttest"</userinput> <co id="apihelper.split.1.1"/>
612 &prompt;<userinput>print s</userinput>
613 <computeroutput>this is
614 a test</computeroutput>
615 &prompt;<userinput>print s.split()</userinput> <co id="apihelper.split.1.2"/>
616 <computeroutput>['this', 'is', 'a', 'test']</computeroutput>
617 &prompt;<userinput>print " ".join(s.split())</userinput> <co id="apihelper.split.1.3"/>
618 <computeroutput>'this is a test'</computeroutput></screen>
619 <calloutlist>
620 <callout arearefs="apihelper.split.1.1">
621 <para>这是一个多行字符串,通过使用转义字符的定义代替了 <link linkend="odbchelper.triplequotes">三重引号</link>。<literal>\n</literal> 是一个回车,<literal>\t</literal> 是一个制表符。</para>
622 </callout>
623 <callout arearefs="apihelper.split.1.2">
624 <para>不带参数的&split; 按照空格进行分割。所以三个空格、一个回车和一个制表符都是一样的。</para>
625 </callout>
626 <callout arearefs="apihelper.split.1.3">
627 <para>通过 &split; 分割字符串你可以将空格统一化;然后再以单个空格作为分隔符用 &join; 将其重新连接起来。这也就是 &info; 函数将多行 &docstring; 合并成单行所做的事情。</para>
628 </callout>
629 </calloutlist>
630 </example>
631 <para>那么 &info; 函数到底用这些 &lambdafunction; 函数、&split; 函数和 &andor; 技巧做了些什么呢?</para>
632 <informalexample id="apihelper.funcassign">
633 <!--<title>Assigning a Function to a Variable</title>-->
634 <programlisting>
635 &apihelper_lambda;</programlisting>
636 </informalexample>
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>
639 <itemizedlist role="furtherreading">
640 <title>&lambdafunction; 函数进一步阅读</title>
641 <listitem><para>&pythonknowledgebase; 讨论了使用 &lambdafunction; 来<ulink url="&url_pythonknowledgebase;view.phtml/aid/6081/fid/241">间接调用函数</ulink>。</para></listitem>
642 <listitem><para>&pythontutorial; 演示了如何 <ulink url="&url_pythontutorial;node6.html#SECTION006740000000000000000">从一个 &lambdafunction; 函数内部访问外部变量</ulink>。(<ulink url="&url_pythonpeps;pep-0227.html"><acronym>PEP</acronym> 227</ulink> 解释了在 &python; 的未来版本中将如何变化。)</para></listitem>
643 <listitem><para>&pythonfaq; 有关于 <ulink url="&url_pythonorg;cgi-bin/faqw.py?query=4.15&querytype=simple&casefold=yes&req=search">令人模糊的使用 &lambdafunction; 单行语句</ulink>的例子。</para></listitem>
644 </itemizedlist>
645 </section>
646 </section>
647 <section id="apihelper.alltogether">
648 <?dbhtml filename="power_of_introspection/all_together.html"?>
649 <title>全部放在一起</title>
650 <abstract>
651 <title/>
652 <para>最一行代码是唯一还没有解释过的,它完成全部的工作。尽管如此,到目前为止工作都还很顺利,因为所需要的每件事都已经按照需求建立好了。所有的多米诺骨牌已经就位,到了将它们推倒的时候了。</para>
653 </abstract>
654 <informalexample>
655 <!--<title>The Meat of &apihelper_filename;</title>-->
656 <para>下面是 &apihelper_filename; 的根本</para>
657 <programlisting>
658 &apihelper_main;</programlisting>
659 </informalexample>
660 <para>注意这是一条命令,被分隔成了多行,但是并没有使用续行符(<literal>\</literal>)。还记得我说过 <link linkend="tip.implicitmultiline">一些表达式可以分割成多行</link> 而不需要使用反斜线吗?列表解析就是这些表达式之一,因为整个表达式包括在方括号里。</para>
661 <para>现在,让我们从后向前分析。这个</para><programlisting>
662 for method in methodList</programlisting><para>告诉我们这是一个 <link linkend="odbchelper.map">列表解析</link>。 如你所知 <varname>methodList</varname> 是 <varname>object</varname> 中 <link linkend="apihelper.filter.care">所有你关心的方法</link> 的一个列表。所以你正在使用 <varname>method</varname> 遍历列表。</para>
663 <example>
664 <title>动态得到 &docstring; </title>
665 <screen>&prompt;<userinput>import &odbchelper_name;</userinput>
666 &prompt;<userinput>object = &odbchelper_name;</userinput> <co id="apihelper.alltogether.1.1"/>
667 &prompt;<userinput>method = '&odbchelper_funcname;'</userinput> <co id="apihelper.alltogether.1.2"/>
668 &prompt;<userinput>getattr(object, method)</userinput> <co id="apihelper.alltogether.1.3"/>
669 <computeroutput><function buildConnectionString at 010D6D74></computeroutput>
670 &prompt;<userinput>print getattr(object, method).__doc__</userinput> <co id="apihelper.alltogether.1.4"/>
671 <computeroutput>&odbchelper_docstring;</computeroutput></screen>
672 <calloutlist>
673 <callout arearefs="apihelper.alltogether.1.1">
674 <para>在 &info; 函数中,<varname>object</varname> 是要得到帮助的对象,作为一个参数传入。</para>
675 </callout>
676 <callout arearefs="apihelper.alltogether.1.2">
677 <para>在你遍历 <varname>methodList</varname> 时,<varname>method</varname> 是当前方法的名称。</para>
678 </callout>
679 <callout arearefs="apihelper.alltogether.1.3">
680 <para>通过 <link linkend="apihelper.getattr">&getattr;</link> 函数,你可以得到 <replaceable>object</replaceable> 模块中 <replaceable>method</replaceable> 函数的引用。</para>
681 </callout>
682 <callout arearefs="apihelper.alltogether.1.4">
683 <para>现在,很容易就可以打印出方法的 &docstring; 。</para>
684 </callout>
685 </calloutlist>
686 </example>
687 <para>接下来令人困惑的是 &docstring; 周围 &str; 的使用。你可能记得,&str; 是一个内置函数,它可以 <link linkend="apihelper.builtin">强制将数据转化为字符串</link>。但是一个 &docstring; 应该总是一个字符串,为什么还要费事的使用 &str; 函数呢?答案就是:不是每个函数都有 &docstring; ,如果没有,这个 &doc; 属性为 &none;。</para>
688 <example>
689 <title>为什么对一个 &docstring; 使用 &str; ?</title>
690 <screen>&prompt;<userinput>>>> def foo(): print 2</userinput>
691 &prompt;<userinput>>>> foo()</userinput>
692 <computeroutput>2</computeroutput>
693 &prompt;<userinput>>>> foo.__doc__</userinput> <co id="apihelper.alltogether.2.1"/>
694 &prompt;<userinput>foo.__doc__ == None</userinput> <co id="apihelper.alltogether.2.2"/>
695 <computeroutput>True</computeroutput>
696 &prompt;<userinput>str(foo.__doc__)</userinput> <co id="apihelper.alltogether.2.3"/>
697 <computeroutput>'None'</computeroutput>
698 </screen>
699 <calloutlist>
700 <callout arearefs="apihelper.alltogether.2.1">
701 <para>你可以很容易的定义一个没有 &docstring; 的函数,这种情况下它的 &doc; 属性为 &none;。 令人迷惑的是,如果你直接演算 &doc; 属性的值,&python; &ide; 什么都不会打印,如果你只是考虑它,是有意义的,但是却没有什么用。</para>
702 </callout>
703 <callout arearefs="apihelper.alltogether.2.2">
704 <para>你可以直接通过 &doc; 属性和 &none; 的比较验证 &doc; 属性的值。</para>
705 </callout>
706 <callout arearefs="apihelper.alltogether.2.3">
707 <para>&str; 函数可以接收值为 null 的参数,然后返回它的字符串表示,<literal>'None'</literal>。</para>
708 </callout>
709 </calloutlist>
710 </example>
711 <note id="compare.isnull.sql" role="compare" vendor="sql">
712 <title>&python; &vs; &sql;:的 null 值比较</title>
713 <para>在 &sql; 中,你必须使用 <literal>IS NULL</literal> 代替 <literal>= NULL</literal> 进行 null 值比较。在 &python;,你可以使用 <literal>== None</literal> 或者 <literal>is None</literal> 进行比较,但是 <literal>is None</literal> 更快。</para>
714 </note>
715 <para>现在你确保有了一个字符串,可以把这个字符串传给 <varname>processFunc</varname>,这个函数 <link linkend="apihelper.lambda">已经定义</link> 为了一个既可以压缩空白也可以不压缩空白的函数。现在你可以明白为什么使用 &str; 将 &none; 转化为一个字符串很重要了。<varname>processFunc</varname> 假设接收到一个字符串参数然后调用 &split; 方法,如果你传入 &none; ,将导致程序崩溃,因为 &none; 没有 &split; 方法。</para>
716 <para>再往回走一步,你再一次使用了字符串格式化来连接 <varname>processFunc</varname> 的返回值 和 <varname>method</varname> 的 &ljust; 方法的返回值。&ljust; 是一个你之前没有见过的新字符串方法。</para>
717 <example>
718 <title>&ljust; 方法介绍</title>
719 <screen>&prompt;<userinput>s = 'buildConnectionString'</userinput>
720 &prompt;<userinput>s.ljust(30)</userinput> <co id="apihelper.alltogether.3.1"/>
721 <computeroutput>'buildConnectionString '</computeroutput>
722 &prompt;<userinput>s.ljust(20)</userinput> <co id="apihelper.alltogether.3.2"/>
723 <computeroutput>'buildConnectionString'</computeroutput></screen>
724 <calloutlist>
725 <callout arearefs="apihelper.alltogether.3.1">
726 <para>&ljust; 用空格填充字符串以符合指定的长度。<function>info</function> 函数使用它生成了两列输出并将所有在第二列的 &docstring; 纵向对齐。</para>
727 </callout>
728 <callout arearefs="apihelper.alltogether.3.2">
729 <para>如果指定的长度小于字符串的长度,&ljust; 将简单的返回未变化的字符串。它决不会截断字符串。</para>
730 </callout>
731 </calloutlist>
732 </example>
733 <para>几乎已经完成了。有了 &ljust; 方法填充过的方法名称和来自调用 <varname>processFunc</varname> 方法得到的 &docstring;(可能压缩过),你就可以将两者连接起来并得到单个字符串。因为对 <varname>methodList</varname> 进行了映射,最终你将获得一个字符串列表。利用<literal>"\n"</literal> 的 &join; 函数,将这个列表连接为单个字符串,列表中每个元素独占一行,接着打印出结果。</para>
734 <example>
735 <title>打印列表</title>
736 <screen>&prompt;<userinput>li = ['a', 'b', 'c']</userinput>
737 &prompt;<userinput>print "\n".join(li)</userinput> <co id="apihelper.alltogether.4.1"/>
738 <computeroutput>a
739 b
740 c</computeroutput></screen>
741 <calloutlist>
742 <callout arearefs="apihelper.alltogether.4.1">
743 <para>在你处理列表时,这确实是一个有用的调试技巧。还有一点在 &python; 中,你总是在操纵列表。</para>
744 </callout>
745 </calloutlist>
746 </example>
747 <para>上述就是最后一个令人困惑的地方了。但是现在你应该已经理解这段代码了。</para>
748 <informalexample>
749 <!--<title>The Meat of &apihelper_filename;, Revisited</title>-->
750 <programlisting>
751 &apihelper_main;</programlisting>
752 </informalexample>
753 </section>
754 <section id="apihelper.summary">
755 <?dbhtml filename="power_of_introspection/summary.html"?>
756 <title>小结</title>
757 <abstract>
758 <title/>
759 <para>&apihelper_filename; 程序和它的输出现在应该非常清晰了。</para>
760 </abstract>
761 <informalexample>
762 <!--<title>&apihelper_filename;</title>-->
763 <programlisting>
764 &apihelper_funcdef;
765 &apihelper_docstring;
766 &apihelper_docstring2;
767 &apihelper_docstring3;
768 &apihelper_methods;
769 &apihelper_lambda;
770 &apihelper_main;
771
772 &apihelper_ifname;
773 &apihelper_printdoc;</programlisting>
774 </informalexample>
775 <informalexample>
776 <!--<title>Output of &apihelper_filename;</title>-->
777 <para>&apihelper_filename; 的输出:</para>
778 <screen>&apihelper_output;</screen>
779 </informalexample>
780 <highlights>
781 <para>在研究下一章前,确保你可以无困难的完成下面这些事情:</para>
782 <itemizedlist>
783 <listitem><para>用 <link linkend="apihelper.optional">可选和命名参数</link>定义和调用函数</para></listitem>
784 <listitem><para>用 <link linkend="apihelper.str.intro">&str;</link> 强制转换任意值为字符串形式</para></listitem>
785 <listitem><para>用 <link linkend="apihelper.getattr">&getattr;</link> 动态得到函数和其它属性的引用</para></listitem>
786 <listitem><para>扩展列表解析语法来进行 <link linkend="apihelper.filter">列表过滤</link></para></listitem>
787 <listitem><para>识别 <link linkend="apihelper.andor">&andor; 技巧</link> 并安全的使用它</para></listitem>
788 <listitem><para>定义 <link linkend="apihelper.lambda">&lambdafunction; 函数</link></para></listitem>
789 <listitem><para><link linkend="apihelper.funcassign">将函数赋值给变量</link> 然后通过引用变量调用函数。我强调的已经够多了:这种思考方式对于提高对 &python; 的理解力至关重要。从这书中你会随处可见这种技术的更复杂的应用。</para></listitem>
790 </itemizedlist>
791 </highlights>
792 </section>
793 </chapter>
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.