This repository has been archived by the owner on Dec 10, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 12
/
special-method-names.html
594 lines (540 loc) · 51.1 KB
/
special-method-names.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
<!DOCTYPE html>
<meta charset=utf-8>
<title>特殊方法名称 - 深入 Python 3</title><!--[if IE]><script src=j/html5.js></script><![endif]-->
<link rel=stylesheet href="dip3.css">
<style>
h1:before{counter-increment:h1;content:'Appendix B. '}
h2:before{counter-increment:h2;content:'B.' counter(h2) '. '}
h3:before{counter-increment:h3;content:'B.' counter(h2) '.' counter(h3) '. '}
</style>
<link rel=stylesheet media='only screen and (max-device-width: 480px)' href="http://woodpecker.org.cn/diveintopython3/mobile.css">
<link rel=stylesheet media=print href="http://woodpecker.org.cn/diveintopython3/print.css">
<meta name=viewport content='initial-scale=1.0'>
<form action=http://www.google.com/cse><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8> <input type=search name=q size=25 placeholder="powered by Google™"> <input type=submit name=sa value=搜索></div></form>
<p>当前位置: <a href="index.html">首页</a> <span class=u>‣</span> <a href="table-of-contents.html#special-method-names">深入 Python 3</a> <span class=u>‣</span>
<p id=level>难度级别: <span class=u title=pro>♦♦♦♦♦</span>
<h1>特殊方法名称</h1>
<blockquote class=q>
<p><span class=u>❝</span> My specialty is being right when other people are wrong. <span class=u>❞</span><br>— <a href=http://en.wikiquote.org/wiki/George_Bernard_Shaw>George Bernard Shaw</a>
</blockquote>
<p id=toc>
<h2 id=divingin>深入</h2>
<p class=f>在本书其它几处,我们已经见识过一些特殊方法——即在使用某些语法时 Python 所调用的“神奇”方法。使用特殊方法,类用起来如同序列、字典、函数、迭代器,或甚至像个数字!本附录为我们已经见过特殊方法提供了参考,并对一些更加深奥的特殊方法进行了简要介绍。<h2 id=basics>基础知识</h2>
<p>如果曾阅读 <a href="iterators.html#divingin">《类的简介》</a>一章,你可能已经见识过了最常见的特殊方法: <code>__init__()</code> 方法。盖章结束时,我写的类多数需要进行一些初始化工作。还有一些其它的基础特殊方法对调试自定义类也特别有用。<table>
<tr><th>序号</th><th>目的</th><th>所编写代码</th><th>Python 实际调用</th></tr><tr><th>①
<td>初始化一个实例</td><td><code class=pp>x = MyClass()</code>
</td><td><a href=http://docs.python.org/3.1/reference/datamodel.html#object.__init__><code>x.<dfn>__init__</dfn>()</code></a>
</td></th></tr><tr><th>②
<td>字符串的“官方”表现形式</td><td><code class=pp><dfn>repr</dfn>(x)</code>
</td><td><a href=http://docs.python.org/3.1/reference/datamodel.html#object.__repr__><code>x.<dfn>__repr__</dfn>()</code></a>
</td></th></tr><tr><th>③
<td>字符串的“非正式”值</td><td><a href=http://docs.python.org/3.1/reference/datamodel.html#object.__str__><code><dfn>str</dfn>(x)</code></a>
</td><td><code class=pp>x.<dfn>__str__</dfn>()</code>
</td></th></tr><tr><th>④
<td>字节数组的“非正式”值</td><td><code class=pp><dfn>bytes</dfn>(x)</code>
</td><td><code class=pp>x.<dfn>__bytes__</dfn>()</code>
</td></th></tr><tr><th>⑤
<td>格式化字符串的值</td><td><code class=pp>format(x, <var>format_spec</var>)</code>
</td><td><a href=http://docs.python.org/3.1/reference/datamodel.html#object.__format__><code>x.<dfn>__format__</dfn>(<var>format_spec</var>)</code></a>
</td></th></tr></table>
<ol>
<li>对 <code>__init__()</code> 方法的调用发生在实例被创建 <em>之后</em> 。如果要控制实际创建进程,请使用 <a href="special-method-names.html#esoterica"><code>__new__()</code> 方法</a>。</li><li>按照约定, <code>__repr__()</code> 方法所返回的字符串为合法的 Python 表达式。</li><li>在调用 <code>print(x)</code> 的同时也调用了 <code>__str__()</code> 方法。</li><li>由于 <code>bytes</code> 类型的引入而<em>从 Python 3 开始出现</em>。</li><li>按照约定,<var>format_spec</var> 应当遵循 <a href=http://www.python.org/doc/3.1/library/string.html#formatspec>迷你语言格式规范【Format Specification Mini-Language】</a>。Python 标准类库中的 <code>decimal.py</code> 提供了自己的 <code>__format__()</code> 方法。</li></ol>
<h2 id=acts-like-iterator>行为方式与迭代器类似的类</h2>
<p>在 <a href="iterators.html">《迭代器》一章中</a>,我们已经学习了如何使用 <code>__iter__()</code> 和 <code>__next__()</code> 方法从零开始创建迭代器。<table>
<tr><th>序号</th><th>目的</th><th>所编写代码</th><th>Python 实际调用</th></tr><tr><th>①
<td>遍历某个序列</td><td><code class=pp><dfn>iter</dfn>(seq)</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__iter__><code>seq.<dfn>__iter__</dfn>()</code></a>
</td></th></tr><tr><th>②
<td>从迭代器中获取下一个值</td><td><code class=pp><dfn>next</dfn>(seq)</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__next__><code>seq.<dfn>__next__</dfn>()</code></a>
</td></th></tr><tr><th>③
<td>按逆序创建一个迭代器</td><td><code class=pp><dfn>reversed</dfn>(seq)</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__reversed__><code>seq.<dfn>__reversed__</dfn>()</code></a>
</td></th></tr></table>
<ol>
<li>无论何时创建迭代器都将调用 <code>__iter__()</code> 方法。这是用初始值对迭代器进行初始化的绝佳之处。</li><li>无论何时从迭代器中获取下一个值都将调用 <code>__next__()</code> 方法。</li><li><code>__reversed__()</code> 方法并不常用。它以一个现有序列为参数,并将该序列中所有元素从尾到头以逆序排列生成一个新的迭代器。</li></ol>
<p>正如我们在 <a href="iterators.html#a-fibonacci-iterator">《迭代器》一章</a>中看到的,<code>for</code> 循环也可用作迭代器。在下面的循环中:<pre class='nd pp'><code>for x in seq:
print(x)</code></pre>
<p>Python 3 将会调用 <code>seq.__iter__()</code> 以创建一个迭代器,然后对迭代器调用 <code>__next__()</code> 方法以获取 <var>x</var> 的每个值。当 <code>__next__()</code> 方法引发 <code>StopIteration</code> 例外时, <code>for</code> 循环正常结束。<h2 id=computed-attributes>计算属性</h2>
<table>
<tr><th>序号</th><th>目的</th><th>所编写代码</th><th>Python 实际调用</th></tr><tr><th>①
<td>获取一个计算属性(无条件的)</td><td><code class=pp>x.my_property</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__getattribute__><code>x.<dfn>__getattribute__</dfn>(<var>'my_property'</var>)</code></a>
</td></th></tr><tr><th>②
<td>获取一个计算属性(后备)</td><td><code class=pp>x.my_property</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__getattr__><code>x.<dfn>__getattr__</dfn>(<var>'my_property'</var>)</code></a>
</td></th></tr><tr><th>③
<td>设置某属性</td><td><code class=pp>x.my_property = value</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__setattr__><code>x.<dfn>__setattr__</dfn>(<var>'my_property'</var>, <var>value</var>)</code></a>
</td></th></tr><tr><th>④
<td>删除某属性</td><td><code class=pp>del x.my_property</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__delattr__><code>x.<dfn>__delattr__</dfn>(<var>'my_property'</var>)</code></a>
</td></th></tr><tr><th>⑤
<td>列出所有属性和方法</td><td><code class=pp>dir(x)</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__dir__><code>x.<dfn>__dir__</dfn>()</code></a>
</td></th></tr></table>
<ol>
<li>如果某个类定义了 <code>__getattribute__()</code> 方法,在 <em>每次引用属性或方法名称时</em> Python 都调用它(特殊方法名称除外,因为那样将会导致讨厌的无限循环)。</li><li>如果某个类定义了 <code>__getattr__()</code> 方法,Python 将只在正常的位置查询属性时才会调用它。如果实例 <var>x</var> 定义了属性 <var>color</var>, <code>x.color</code> 将 <em>不会</em> 调用 <code>x.__getattr__('color')</code>;而只会返回 <var>x.color</var> 已定义好的值。</li><li>无论何时给属性赋值,都会调用 <code>__setattr__()</code> 方法。</li><li>无论何时删除一个属性,都将调用 <code>__delattr__()</code> 方法。</li><li>如果定义了 <code>__getattr__()</code> 或 <code>__getattribute__()</code> 方法, <code>__dir__()</code> 方法将非常有用。通常,调用 <code>dir(x)</code> 将只显示正常的属性和方法。如果 <code>__getattr()__</code> 方法动态处理 <var>color</var> 属性, <code>dir(x)</code> 将不会将 <var>color</var> 列为可用属性。可通过覆盖 <code>__dir__()</code> 方法允许将 <var>color</var> 列为可用属性,对于想使用你的类但却不想深入其内部的人来说,该方法非常有益。</li></ol>
<p><code>__getattr__()</code> 和 <code>__getattribute__()</code> 方法的区别非常细微,但非常重要。可以用两个例子来解释一下:<pre class=screen>
<code>class Dynamo:
def __getattr__(self, key):
<a> if key == 'color': <span class=u>①</span></a>
return 'PapayaWhip'
else:
<a> raise AttributeError <span class=u>②</span></a></code>
<samp class=p>>>> </samp><kbd class=pp>dyn = Dynamo()</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>dyn.color</kbd> <span class=u>③</span></a>
<samp class=pp>'PapayaWhip'</samp>
<samp class=p>>>> </samp><kbd class=pp>dyn.color = 'LemonChiffon'</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>dyn.color</kbd> <span class=u>④</span></a>
<samp class=pp>'LemonChiffon'</samp></pre>
<ol>
<li>属性名称以字符串的形式传入 <code>__getattr()__</code> 方法。如果名称为 <code>'color'</code>,该方法返回一个值。(在此情况下,它只是一个硬编码的字符串,但可以正常地进行某些计算并返回结果。)</li><li>如果属性名称未知, <code>__getattr()__</code> 方法必须引发一个 <code>AttributeError</code> 例外,否则在访问未定义属性时,代码将只会默默地失败。(从技术角度而言,如果方法不引发例外或显式地返回一个值,它将返回 <code>None</code> ——Python 的空值。这意味着 <em>所有</em> 未显式定义的属性将为 <code>None</code>,几乎可以肯定这不是你想看到的。)</li><li><var>dyn</var> 实例没有名为 <var>color</var> 的属性,因此在提供计算值时将调用 <code>__getattr__()</code> 。</li><li>在显式地设置 <var>dyn.color</var> 之后,将不再为提供 <var>dyn.color</var> 的值而调用 <code>__getattr__()</code> 方法,因为 <var>dyn.color</var> 已在该实例中定义。</li></ol>
<p>另一方面,<code>__getattribute__()</code> 方法是绝对的、无条件的。<pre class=screen>
<code>class SuperDynamo:
def __getattribute__(self, key):
if key == 'color':
return 'PapayaWhip'
else:
raise AttributeError</code>
<samp class=p>>>> </samp><kbd class=pp>dyn = SuperDynamo()</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>dyn.color</kbd> <span class=u>①</span></a>
<samp class=pp>'PapayaWhip'</samp>
<samp class=p>>>> </samp><kbd class=pp>dyn.color = 'LemonChiffon'</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>dyn.color</kbd> <span class=u>②</span></a>
<samp class=pp>'PapayaWhip'</samp></pre>
<ol>
<li>在获取 <var>dyn.color</var> 的值时将调用 <code>__getattribute__()</code> 方法。</li><li>即便已经显式地设置 <var>dyn.color</var>,在获取 <var>dyn.color</var> 的值时, <em>仍将调用</em> <code>__getattribute__()</code> 方法。如果存在 <code>__getattribute__()</code> 方法,将在每次查找属性和方法时 <em>无条件地调用</em> 它,哪怕在创建实例之后已经显式地设置了属性。</li></ol>
<blockquote class=note>
<p><span class=u>☞</span> 如果定义了类的 <code>__getattribute__()</code> 方法,你可能还想定义一个 <code>__setattr__()</code> 方法,并在两者之间进行协同,以跟踪属性的值。否则,在创建实例之后所设置的值将会消失在黑洞中。</blockquote>
<p>必须特别小心 <code>__getattribute__()</code> 方法,因为 Python 在查找类的方法名称时也将对其进行调用。<pre class=screen>
<code>class Rastan:
def __getattribute__(self, key):
<a> raise AttributeError <span class=u>①</span></a>
def swim(self):
pass</code>
<samp class=p>>>> </samp><kbd class=pp>hero = Rastan()</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>hero.swim()</kbd> <span class=u>②</span></a>
<samp class=traceback>Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __getattribute__
AttributeError</samp></pre>
<ol>
<li>该类定义了一个总是引发 <code>AttributeError</code> 例外的 <code>__getattribute__()</code> 方法。没有属性或方法的查询会成功。</li><li>调用 <code>hero.swim()</code> 时,Python 将在 <code>Rastan</code> 类中查找 <code>swim()</code> 方法。该查找将执行整个 <code>__getattribute__()</code> 方法,因为所有的属性和方法查找都通过 <code>__getattribute__()</code> 方法。在此例中, <code>__getattribute__()</code> 方法引发 <code>AttributeError</code> 例外,因此该方法查找过程将会失败,而方法调用也将失败。</li></ol>
<h2 id=acts-like-function>行为方式与函数类似的类</h2>
<p>可以让类的实例变得可调用——就像函数可以调用一样——通过定义 <code>__call__()</code> 方法。<table>
<tr><th>序号</th><th>目的</th><th>所编写代码</th><th>Python 实际调用</th></tr><tr><th>
<td>像调用函数一样“调用”一个实例</td><td><code class=pp>my_instance()</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__call__><code>my_instance.<dfn>__call__</dfn>()</code></a>
</td></th></tr></table>
<p><a href=http://docs.python.org/3.1/library/zipfile.html><code>zipfile</code> 模块</a> 通过该方式定义了一个可以使用给定密码<dfn>解密</dfn> <dfn>经加密</dfn> <dfn>zip</dfn> 文件的类。该 zip <dfn>解密</dfn> 算法需要在解密的过程中保存状态。通过将解密器定义为类,使我们得以在 decryptor 类的单个实例中对该状态进行维护。状态在 <code>__init__()</code> 方法中进行初始化,如果文件 <dfn>经加密</dfn> 则进行更新。但由于该类像函数一样“可调用”,因此可以将实例作为 <code>map()</code> 函数的第一个参数传入,代码如下:<pre class=pp><code># excerpt from zipfile.py
class _ZipDecrypter:
.
.
.
def __init__(self, pwd):
<a> self.key0 = 305419896 <span class=u>①</span></a>
self.key1 = 591751049
self.key2 = 878082192
for p in pwd:
self._UpdateKeys(p)
<a> def __call__(self, c): <span class=u>②</span></a>
assert isinstance(c, int)
k = self.key2 | 2
c = c ^ (((k * (k^1)) >> 8) & 255)
self._UpdateKeys(c)
return c
.
.
.
<a>zd = _ZipDecrypter(pwd) <span class=u>③</span></a>
bytes = zef_file.read(12)
<a>h = list(map(zd, bytes[0:12])) <span class=u>④</span></a></code></pre>
<ol>
<li><code>_ZipDecryptor</code> 类维护了以三个旋转密钥形式出现的状态,该状态稍后将在 <code>_UpdateKeys()</code> 方法中更新(此处未展示)。</li><li>该类定义了一个 <code>__call__()</code> 方法,使得该类可像函数一样调用。在此例中,<code>__call__()</code> 对 zip 文件的单个字节进行解密,然后基于经解密的字节对旋转密码进行更新。</li><li><var>zd</var> 是 <code>_ZipDecryptor</code> 类的一个实例。变量 <var>pwd</var> 被传入 <code>__init__()</code> 方法,并在其中被存储和用于首次旋转密码更新。</li><li>给出 zip 文件的头 12 个字节,将这些字节映射给 <var>zd</var> 进行解密,实际上这将导致调用 <code>__call__()</code> 方法 12 次,也就是 更新内部状态并返回结果字节 12 次。</li></ol>
<h2 id=acts-like-list>行为方式与序列类似的类</h2>
<p>如果类作为一系列值的容器出现——也就是说如果对某个类来说,是否“包含”某值是件有意义的事情——那么它也许应该定义下面的特殊方法已,让它的行为方式与序列类似。<table>
<tr><th>序号</th><th>目的</th><th>所编写代码</th><th>Python 实际调用</th></tr><tr><th>
<td>序列的长度</td><td><code class=pp><dfn>len</dfn>(seq)</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__len__><code>seq.<dfn>__len__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>了解某序列是否包含特定的值</td><td><code class=pp>x in seq</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__contains__><code>seq.<dfn>__contains__</dfn>(<var>x</var>)</code></a>
</td></th></tr></table>
<p id=acts-like-list-example><a href=http://docs.python.org/3.1/library/cgi.html><code>cgi</code> 模块</a> 在其 <code>FieldStorage</code> 类中使用了这些方法,该类用于表示提交给动态网页的所有表单字段或查询参数。<pre class=pp><code># A script which responds to http://example.com/search?q=cgi
import cgi
fs = cgi.FieldStorage()
<a>if 'q' in fs: <span class=u>①</span></a>
do_search()
# An excerpt from cgi.py that explains how that works
class FieldStorage:
.
.
.
<a> def __contains__(self, key): <span class=u>②</span></a>
if self.list is None:
raise TypeError('not indexable')
<a> return any(item.name == key for item in self.list) <span class=u>③</span></a>
<a> def __len__(self): <span class=u>④</span></a>
<a> return len(self.keys()) <span class=u>⑤</span></a></code></pre>
<ol>
<li>一旦创建了 <code>cgi.FieldStorage</code> 类的实例,就可以使用 “<code>in</code>” 运算符来检查查询字符串中是否包含了某个特定参数。</li><li>而 <code>__contains__()</code> 方法是令该魔法生效的主角。</li><li>如果代码为 <code>if 'q' in fs</code>,Python 将在 <var>fs</var> 对象中查找 <code>__contains__()</code> 方法,而该方法在 <code>cgi.py</code> 中已经定义。<code>'q'</code> 的值被当作 <var>key</var> 参数传入 <code>__contains__()</code> 方法。</li><li>同样的 <code>FieldStorage</code> 类还支持返回其长度,因此可以编写代码 <code>len(<var>fs</var>)</code> 而其将调用 <code>FieldStorage</code> 的 <code>__len__()</code> 方法,并返回其识别的查询参数个数。</li><li><code>self.keys()</code> 方法检查 <code>self.list is None</code> 是否为真值,因此 <code>__len__</code> 方法无需重复该错误检查。</li></ol>
<h2 id=acts-like-dict>行为方式与字典类似的类</h2>
<p>在前一节的基础上稍作拓展,就不仅可以对 “<code>in</code>” 运算符和 <code>len()</code> 函数进行响应,还可像全功能字典一样根据键来返回值。<table>
<tr><th>序号</th><th>目的</th><th>所编写代码</th><th>Python 实际调用</th></tr><tr><th>
<td>通过键来获取值</td><td><code class=pp>x[key]</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__getitem__><code>x.<dfn>__getitem__</dfn>(<var>key</var>)</code></a>
</td></th></tr><tr><th>
<td>通过键来设置值</td><td><code class=pp>x[key] = value</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__setitem__><code>x.<dfn>__setitem__</dfn>(<var>key</var>, <var>value</var>)</code></a>
</td></th></tr><tr><th>
<td>删除一个键值对</td><td><code class=pp>del x[key]</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__delitem__><code>x.<dfn>__delitem__</dfn>(<var>key</var>)</code></a>
</td></th></tr><tr><th>
<td>为缺失键提供默认值</td><td><code class=pp>x[nonexistent_key]</code>
</td><td><a href=http://docs.python.org/3.1/library/collections.html#collections.defaultdict.__missing__><code>x.<dfn>__missing__</dfn>(<var>nonexistent_key</var>)</code></a>
</td></th></tr></table>
<p><a href=http://docs.python.org/3.1/library/cgi.html><code>cgi</code> 模块</a> 的 <a href="special-method-names.html#acts-like-list-example"><code>FieldStorage</code> 类</a> 同样定义了这些特殊方法,也就是说可以像下面这样编码:<pre class=pp><code># A script which responds to http://example.com/search?q=cgi
import cgi
fs = cgi.FieldStorage()
if 'q' in fs:
<a> do_search(fs['q']) <span class=u>①</span></a>
# An excerpt from cgi.py that shows how it works
class FieldStorage:
.
.
.
<a> def __getitem__(self, key): <span class=u>②</span></a>
if self.list is None:
raise TypeError('not indexable')
found = []
for item in self.list:
if item.name == key: found.append(item)
if not found:
raise KeyError(key)
if len(found) == 1:
return found[0]
else:
return found</code></pre>
<ol>
<li><var>fs</var> 对象是 <code>cgi.FieldStorage</code> 类的一个实例,但仍然可以像 <code>fs['q']</code> 这样估算表达式。</li><li><code>fs['q']</code> 将 <var>key</var> 参数设置为 <code>'q'</code> 来调用 <code>__getitem__()</code> 方法。然后它将在其内部维护的查询参数列表 (<var>self.list</var>) 中查找一个 <code>.name</code> 与给定键相符的字典项。</li></ol>
<h2 id=acts-like-number>行为方式与数值类似的类</h2>
<p>使用适当的特殊方法,可以将类的行为方式定义为与数字相仿。也就是说,可以进行相加、相减,并进行其它数学运算。这就是 <dfn>分数</dfn> 的实现方式—— <code><dfn>Fraction</dfn></code> 类实现了这些特殊方法,然后就可以进行下列运算了:<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>from fractions import Fraction</kbd>
<samp class=p>>>> </samp><kbd class=pp>x = Fraction(1, 3)</kbd>
<samp class=p>>>> </samp><kbd class=pp>x / 3</kbd>
<samp class=pp>Fraction(1, 9)</samp></pre>
<p>以下是实现“类数字”类的完整特殊方法清单:<table>
<tr><th>序号</th><th>目的</th><th>所编写代码</th><th>Python 实际调用</th></tr><tr><th>
<td>加法</td><td><code class=pp>x + y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__add__><code>x.<dfn>__add__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>减法</td><td><code class=pp>x - y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__sub__><code>x.<dfn>__sub__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>乘法</td><td><code class=pp>x * y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__mul__><code>x.<dfn>__mul__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>除法</td><td><code class=pp>x / y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__truediv__><code>x.<dfn>__truediv__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>地板除</td><td><code>x // y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__floordiv__><code>x.<dfn>__floordiv__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>取模(取余)</td><td><code class=pp>x % y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__mod__><code>x.<dfn>__mod__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>地板除 <i class=baa>&</i> 取模</td><td><code class=pp>divmod(x, y)</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__divmod__><code>x.<dfn>__divmod__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>乘幂</td><td><code class=pp>x ** y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__pow__><code>x.<dfn>__pow__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>左位移</td><td><code class=pp>x << y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__lshift__><code>x.<dfn>__lshift__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>右位移</td><td><code class=pp>x >> y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__rshift__><code>x.<dfn>__rshift__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>按位 <code>and</code>
</td><td><code class=pp>x & y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__and__><code>x.<dfn>__and__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>按位 <code>xor</code>
</td><td><code class=pp>x ^ y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__xor__><code>x.<dfn>__xor__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>按位 <code>or</code>
</td><td><code class=pp>x | y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__or__><code>x.<dfn>__or__</dfn>(<var>y</var>)</code></a>
</td></th></tr></table>
<p>如果 <var>x</var> 是某个实现了所有这些方法的类的实例,那么万事大吉。但如果未实现其中之一呢?或者更糟,如果实现了,但却无法处理某几类参数会怎么样?例如:<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>from fractions import Fraction</kbd>
<samp class=p>>>> </samp><kbd class=pp>x = Fraction(1, 3)</kbd>
<samp class=p>>>> </samp><kbd class=pp>1 / x</kbd>
<samp class=pp>Fraction(3, 1)</samp></pre>
<p>这并 <em>不是</em> 传入一个 <code>分数</code> 并将其除以一个整数(如前例那样)的情况。前例中的情况非常直观: <code>x / 3</code> 调用 <code>x.__truediv__(3)</code>,而<code>Fraction</code> 的 <code>__truediv__()</code> 方法处理所有的数学运算。但整数并不“知道”如何对分数进行数学计算。因此本例该如何运作呢?<p>和 <i>反映操作</i> 相关的还有第二部分算数特殊方法。给定一个二元算术运算 (<i>例如:</i> <code>x / y</code>),有两种方法来实现它:<ol>
<li>告诉 <var>x</var> 将自己除以 <var>y</var>,或者</li><li>告诉 <var>y</var> 去除 <var>x</var>
</li></ol>
<p>之前提到的特殊方法集合采用了第一种方式:对于给定 <code>x / y</code>,它们为 <var>x</var> 提供了一种途径来表述“我知道如何将自己除以 <var>y</var>。”下面的特殊方法集合采用了第二种方法:它们向 <var>y</var> 提供了一种途径来表述“我知道如何成为分母,并用自己去除 <var>x</var>。”<table>
<tr><th>序号</th><th>目的</th><th>所编写代码</th><th>Python 实际调用</th></tr><tr><th>
<td>加法</td><td><code class=pp>x + y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__radd__><code>y.<dfn>__radd__</dfn>(<var>x</var>)</code></a>
</td></th></tr><tr><th>
<td>减法</td><td><code class=pp>x - y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__rsub__><code>y.<dfn>__rsub__</dfn>(<var>x</var>)</code></a>
</td></th></tr><tr><th>
<td>乘法</td><td><code class=pp>x * y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__rmul__><code>y.<dfn>__rmul__</dfn>(<var>x</var>)</code></a>
</td></th></tr><tr><th>
<td>除法</td><td><code class=pp>x / y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__rtruediv__><code>y.<dfn>__rtruediv__</dfn>(<var>x</var>)</code></a>
</td></th></tr><tr><th>
<td>地板除</td><td><code>x // y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__rfloordiv__><code>y.<dfn>__rfloordiv__</dfn>(<var>x</var>)</code></a>
</td></th></tr><tr><th>
<td>取模(取余)</td><td><code class=pp>x % y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__rmod__><code>y.<dfn>__rmod__</dfn>(<var>x</var>)</code></a>
</td></th></tr><tr><th>
<td>地板除 <i class=baa>&</i> 取模</td><td><code class=pp>divmod(x, y)</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__rdivmod__><code>y.<dfn>__rdivmod__</dfn>(<var>x</var>)</code></a>
</td></th></tr><tr><th>
<td>乘幂</td><td><code class=pp>x ** y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__rpow__><code>y.<dfn>__rpow__</dfn>(<var>x</var>)</code></a>
</td></th></tr><tr><th>
<td>左位移</td><td><code class=pp>x << y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__rlshift__><code>y.<dfn>__rlshift__</dfn>(<var>x</var>)</code></a>
</td></th></tr><tr><th>
<td>右位移</td><td><code class=pp>x >> y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__rrshift__><code>y.<dfn>__rrshift__</dfn>(<var>x</var>)</code></a>
</td></th></tr><tr><th>
<td>按位 <code>and</code>
</td><td><code class=pp>x & y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__rand__><code>y.<dfn>__rand__</dfn>(<var>x</var>)</code></a>
</td></th></tr><tr><th>
<td>按位 <code>xor</code>
</td><td><code class=pp>x ^ y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__rxor__><code>y.<dfn>__rxor__</dfn>(<var>x</var>)</code></a>
</td></th></tr><tr><th>
<td>按位 <code>or</code>
</td><td><code class=pp>x | y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__ror__><code>y.<dfn>__ror__</dfn>(<var>x</var>)</code></a>
</td></th></tr></table>
<p>但是等一下!还有更多特殊方法!如果在进行“原地”操作,如: <code>x /= 3</code>,还可定义更多的特殊方法。<table>
<tr><th>序号</th><th>目的</th><th>所编写代码</th><th>Python 实际调用</th></tr><tr><th>
<td>原地加法</td><td><code class=pp>x += y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__iadd__><code>x.<dfn>__iadd__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>原地减法</td><td><code class=pp>x -= y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__isub__><code>x.<dfn>__isub__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>原地乘法</td><td><code class=pp>x *= y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__imul__><code>x.<dfn>__imul__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>原地除法</td><td><code class=pp>x /= y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__itruediv__><code>x.<dfn>__itruediv__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>原地地板除法</td><td><code>x //= y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__ifloordiv__><code>x.<dfn>__ifloordiv__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>原地取模</td><td><code class=pp>x %= y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__imod__><code>x.<dfn>__imod__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>原地乘幂</td><td><code class=pp>x **= y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__ipow__><code>x.<dfn>__ipow__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>原地左位移</td><td><code class=pp>x <<= y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__ilshift__><code>x.<dfn>__ilshift__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>原地右位移</td><td><code class=pp>x >>= y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__irshift__><code>x.<dfn>__irshift__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>原地按位 <code>and</code>
</td><td><code class=pp>x &= y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__iand__><code>x.<dfn>__iand__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>原地按位 <code>xor</code>
</td><td><code class=pp>x ^= y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__ixor__><code>x.<dfn>__ixor__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>原地按位 <code>or</code>
</td><td><code class=pp>x |= y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__ior__><code>x.<dfn>__ior__</dfn>(<var>y</var>)</code></a>
</td></th></tr></table>
<p>注意:多数情况下,并不需要原地操作方法。如果未对特定运算定义“就地”方法,Python 将会试着使用(普通)方法。例如,为执行表达式 <code>x /= y</code>,Python 将会:<ol>
<li>试着调用 <code>x.__itruediv__(<var>y</var>)</code>。如果该方法已经定义,并返回了 <code>NotImplemented</code> 之外的值,那已经大功告成了。</li><li>试图调用 <code>x.__truediv__(<var>y</var>)</code>。如果该方法已定义并返回一个 <code>NotImplemented</code> 之外的值, <var>x</var> 的旧值将被丢弃,并将所返回的值替代它,就像是进行了 <code> x = x / y</code> 运算。</li><li>试图调用 <code>y.__rtruediv__(<var>x</var>)</code>。如果该方法已定义并返回了一个 <code>NotImplemented</code> 之外的值,<var>x</var> 的旧值将被丢弃,并用所返回值进行替换。</li></ol>
<p>因此如果想对原地运算进行优化,仅需像 <code>__itruediv__()</code> 方法一样定义“原地”方法。否则,基本上 Python 将会重新生成原地运算公式,以使用常规的运算及变量赋值。<p>还有一些“一元”数学运算,可以对“类-数字”对象自己执行。<table>
<tr><th>序号</th><th>目的</th><th>所编写代码</th><th>Python 实际调用</th></tr><tr><th>
<td>负数</td><td><code class=pp>-x</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__neg__><code>x.<dfn>__neg__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>正数</td><td><code class=pp>+x</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__pos__><code>x.<dfn>__pos__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>绝对值</td><td><code class=pp>abs(x)</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__abs__><code>x.<dfn>__abs__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>取反</td><td><code class=pp>~x</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__invert__><code>x.<dfn>__invert__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>复数</td><td><code class=pp>complex(x)</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__complex__><code>x.<dfn>__complex__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>整数转换</td><td><code class=pp>int(x)</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__int__><code>x.<dfn>__int__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>浮点数</td><td><code class=pp>float(x)</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__float__><code>x.<dfn>__float__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>四舍五入至最近的整数</td><td><code class=pp>round(x)</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__round__><code>x.<dfn>__round__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>四舍五入至最近的 <var>n</var> 位小数</td><td><code class=pp>round(x, n)</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__round__><code>x.<dfn>__round__</dfn>(n)</code></a>
</td></th></tr><tr><th>
<td><code>>= x</code> 的最小整数
</td><td><code class=pp>math.ceil(x)</code>
</td><td><a href=http://docs.python.org/3.1/library/math.html#math.ceil><code>x.<dfn>__ceil__</dfn>()</code></a>
</td></th></tr><tr><th>
<td><code><= x</code>的最大整数
</td><td><code class=pp>math.floor(x)</code>
</td><td><a href=http://docs.python.org/3.1/library/math.html#math.floor><code>x.<dfn>__floor__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>对 <code>x</code> 朝向 0 取整</td><td><code class=pp>math.trunc(x)</code>
</td><td><a href=http://docs.python.org/3.1/library/math.html#math.trunc><code>x.<dfn>__trunc__</dfn>()</code></a>
</td></th></tr><tr><th><span class=inherit><a href=http://www.python.org/dev/peps/pep-0357/>PEP 357</a></span>
<td>作为列表索引的数字</td><td><code class=pp>a_list[<var>x</var>]</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__index__><code>a_list[x.<dfn>__index__</dfn>()]</code></a>
</td></th></tr></table>
<h2 id=rich-comparisons>可比较的类</h2>
<p>我将此内容从前一节中拿出来使其单独成节,是因为“比较”操作并不局限于数字。许多数据类型都可以进行比较——字符串、列表,甚至字典。如果要创建自己的类,且对象之间的比较有意义,可以使用下面的特殊方法来实现比较。<table>
<tr><th>序号</th><th>目的</th><th>所编写代码</th><th>Python 实际调用</th></tr><tr><th>
<td>相等</td><td><code class=pp>x == y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__eq__><code>x.<dfn>__eq__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>不相等</td><td><code class=pp>x != y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__ne__><code>x.<dfn>__ne__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>小于</td><td><code class=pp>x < y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__lt__><code>x.<dfn>__lt__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>小于或等于</td><td><code class=pp>x <= y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__le__><code>x.<dfn>__le__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>大于</td><td><code class=pp>x > y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__gt__><code>x.<dfn>__gt__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>大于或等于</td><td><code class=pp>x >= y</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__ge__><code>x.<dfn>__ge__</dfn>(<var>y</var>)</code></a>
</td></th></tr><tr><th>
<td>布尔上上下文环境中的真值</td><td><code class=pp>if x:</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__bool__><code>x.<dfn>__bool__</dfn>()</code></a>
</td></th></tr></table>
<blockquote class=note>
<p><span class=u>☞</span>如果定义了 <code>__lt__()</code> 方法但没有定义 <code>__gt__()</code> 方法,Python 将通过经交换的算子调用 <code>__lt__()</code> 方法。然而,Python 并不会组合方法。例如,如果定义了 <code>__lt__()</code> 方法和 <code>__eq()__</code> 方法,并试图测试是否 <code>x <= y</code>,Python 不会按顺序调用 <code>__lt__()</code> 和 <code>__eq()__</code> 。它将只调用 <code>__le__()</code> 方法。</blockquote>
<h2 id=pickle>可序列化的类</h2><!--see http://docs.python.org/3.1/library/pickle.html:-->
<p>Python 支持 <a href="serializing.html">任意对象的序列化和反序列化</a>。(多数 Python 参考资料称该过程为 “pickling” 和 “unpickling”)。该技术对与将状态保存为文件并在稍后恢复它非常有意义。所有的 <a href="native-datatypes.html">内置数据类型</a> 均已支持 pickling 。如果创建了自定义类,且希望它能够 pickle,阅读 <a href=http://docs.python.org/3.1/library/pickle.html> pickle 协议</a> 了解下列特殊方法何时以及如何被调用。<table>
<tr><th>序号</th><th>目的</th><th>所编写代码</th><th>Python 实际调用</th></tr><tr><th>
<td>自定义对象的复制</td><td><code class=pp>copy.copy(x)</code>
</td><td><a href=http://docs.python.org/3.1/library/copy.html><code>x.<dfn>__copy__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>自定义对象的深度复制</td><td><code class=pp>copy.deepcopy(x)</code>
</td><td><a href=http://docs.python.org/3.1/library/copy.html><code>x.<dfn>__deepcopy__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>在 pickling 之前获取对象的状态</td><td><code class=pp>pickle.dump(x, <var>file</var>)</code>
</td><td><a href=http://docs.python.org/3.1/library/pickle.html#pickle-state><code>x.<dfn>__getstate__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>序列化某对象</td><td><code class=pp>pickle.dump(x, <var>file</var>)</code>
</td><td><a href=http://docs.python.org/3.1/library/pickle.html#pickling-class-instances><code>x.<dfn>__reduce__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>序列化某对象(新 pickling 协议)</td><td><code class=pp>pickle.dump(x, <var>file</var>, <var>protocol_version</var>)</code>
</td><td><a href=http://docs.python.org/3.1/library/pickle.html#pickling-class-instances><code>x.<dfn>__reduce_ex__</dfn>(<var>protocol_version</var>)</code></a>
</td></th></tr><tr><th>*
<td>控制 unpickling 过程中对象的创建方式</td><td><code class=pp>x = pickle.load(<var>file</var>)</code>
</td><td><a href=http://docs.python.org/3.1/library/pickle.html#pickling-class-instances><code>x.<dfn>__getnewargs__</dfn>()</code></a>
</td></th></tr><tr><th>*
<td>在 unpickling 之后还原对象的状态</td><td><code class=pp>x = pickle.load(<var>file</var>)</code>
</td><td><a href=http://docs.python.org/3.1/library/pickle.html#pickle-state><code>x.<dfn>__setstate__</dfn>()</code></a>
</td></th></tr></table>
<p>* 要重建序列化对象,Python 需要创建一个和被序列化的对象看起来一样的新对象,然后设置新对象的所有属性。<code>__getnewargs__()</code> 方法控制新对象的创建过程,而 <code>__setstate__()</code> 方法控制属性值的还原方式。<h2 id=context-managers>可在 <code>with</code> 语块中使用的类</h2>
<p><code>with</code> 语块定义了 <a href=http://www.python.org/doc/3.1/library/stdtypes.html#typecontextmanager>运行时刻上下文环境</a>;在执行 <code>with</code> 语句时将“进入”该上下文环境,而执行该语块中的最后一条语句将“退出”该上下文环境。<table>
<tr><th>序号</th><th>目的</th><th>所编写代码</th><th>Python 实际调用</th></tr><tr><th>
<td>在进入 <code>with</code> 语块时进行一些特别操作</td><td><code class=pp>with x:</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__enter__><code>x.<dfn>__enter__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>在退出 <code>with</code> 语块时进行一些特别操作</td><td><code class=pp>with x:</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__exit__><code>x.<dfn>__exit__</dfn>()</code></a>
</td></th></tr></table>
<p>以下是 <a href="files.html#with"><code>with <var>file</var></code> 习惯用法</a> 的运作方式:<pre class=pp><code># excerpt from io.py:
def _checkClosed(self, msg=None):
'''Internal: raise an ValueError if file is closed
'''
if self.closed:
raise ValueError('I/O operation on closed file.'
if msg is None else msg)
def __enter__(self):
'''Context management protocol. Returns self.'''
<a> self._checkClosed() <span class=u>①</span></a>
<a> return self <span class=u>②</span></a>
def __exit__(self, *args):
'''Context management protocol. Calls close()'''
<a> self.close() <span class=u>③</span></a></code></pre>
<ol>
<li>该文件对象同时定义了一个 <code>__enter__()</code> 和一个 <code>__exit__()</code> 方法。该 <code>__enter__()</code> 方法检查文件是否处于打开状态;如果没有, <code>_checkClosed()</code> 方法引发一个例外。</li><li><code>__enter__()</code> 方法将始终返回 <var>self</var> —— 这是 <code>with</code> 语块将用于调用属性和方法的对象</li><li>在 <code>with</code> 语块结束后,文件对象将自动关闭。怎么做到的?在 <code>__exit__()</code> 方法中调用了 <code>self.close()</code> .</li></ol>
<blockquote class=note>
<p><span class=u>☞</span>该 <code>__exit__()</code> 方法将总是被调用,哪怕是在 <code>with</code> 语块中引发了例外。实际上,如果引发了例外,该例外信息将会被传递给 <code>__exit__()</code> 方法。查阅 <a href=http://www.python.org/doc/3.1/reference/datamodel.html#with-statement-context-managers>With 状态上下文环境管理器</a> 了解更多细节。</blockquote>
<p>要了解关于上下文管理器的更多内容,请查阅 <a href="files.html#with">《自动关闭文件》</a> 和 <a href="files.html#redirect">《重定向标准输出》</a>。<h2 id=esoterica>真正神奇的东西</h2>
<p>如果知道自己在干什么,你几乎可以完全控制类是如何比较的、属性如何定义,以及类的子类是何种类型。<table>
<tr><th>序号</th><th>目的</th><th>所编写代码</th><th>Python 实际调用</th></tr><tr><th>
<td>类构造器</td><td><code class=pp>x = MyClass()</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__new__><code>x.<dfn>__new__</dfn>()</code></a>
</td></th></tr><tr><th>*
<td>类析构器</td><td><code class=pp>del x</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__del__><code>x.<dfn>__del__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>只定义特定集合的某些属性</td><td>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__slots__><code>x.<dfn>__slots__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>自定义散列值</td><td><code class=pp>hash(x)</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__hash__><code>x.<dfn>__hash__</dfn>()</code></a>
</td></th></tr><tr><th>
<td>获取某个属性的值</td><td><code class=pp>x.color</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__get__><code>type(x).<dfn>__dict__</dfn>['color'].__get__(x, type(x))</code></a>
</td></th></tr><tr><th>
<td>设置某个属性的值</td><td><code class=pp>x.color = 'PapayaWhip'</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__set__><code>type(x).<dfn>__dict__</dfn>['color'].__set__(x, 'PapayaWhip')</code></a>
</td></th></tr><tr><th>
<td>删除某个属性</td><td><code class=pp>del x.color</code>
</td><td><a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__delete__><code>type(x).<dfn>__dict__</dfn>['color'].__del__(x)</code></a>
</td></th></tr><tr><th>
<td>控制某个对象是否是该对象的实例 your class</td><td><code class=pp>isinstance(x, MyClass)</code>
</td><td><a href=http://www.python.org/dev/peps/pep-3119/#overloading-isinstance-and-issubclass><code>MyClass.<dfn>__instancecheck__</dfn>(x)</code></a>
</td></th></tr><tr><th>
<td>控制某个类是否是该类的子类</td><td><code class=pp>issubclass(C, MyClass)</code>
</td><td><a href=http://www.python.org/dev/peps/pep-3119/#overloading-isinstance-and-issubclass><code>MyClass.<dfn>__subclasscheck__</dfn>(C)</code></a>
</td></th></tr><tr><th>
<td>控制某个类是否是该抽象基类的子类</td><td><code class=pp>issubclass(C, MyABC)</code>
</td><td><a href=http://docs.python.org/3.1/library/abc.html#abc.ABCMeta.__subclasshook__><code>MyABC.<dfn>__subclasshook__</dfn>(C)</code></a>
</td></th></tr></table>
<p><sup>*</sup> 确切掌握 Python 何时调用 <code>__del__()</code> 特别方法 <a href=http://www.python.org/doc/3.1/reference/datamodel.html#object.__del__>是件难以置信的复杂</a>事情。要想完全理解它,必须清楚 <a href=http://www.python.org/doc/3.1/reference/datamodel.html#objects-values-and-types>Python 如何在内存中跟踪对象</a>。以下有一篇好文章介绍 <a href=http://www.electricmonk.nl/log/2008/07/07/python-destructor-and-garbage-collection-notes/>Python 垃圾收集和类析构器</a>。还可以阅读 <a href=http://mindtrove.info/articles/python-weak-references/>《弱引用》</a>、<a href=http://docs.python.org/3.1/library/weakref.html>《<code>weakref</code> 模块》</a>,还可以将 <a href=http://www.python.org/doc/3.1/library/gc.html>《<code>gc</code> 模块》</a> 当作补充阅读材料。<h2 id=furtherreading>深入阅读</h2>
<p>本附录中提到的模块:<ul>
<li><a href=http://docs.python.org/3.1/library/zipfile.html><code>zipfile</code> 模块</a>
</li><li><a href=http://docs.python.org/3.1/library/cgi.html><code>cgi</code> 模块</a>
</li><li><a href=http://www.python.org/doc/3.1/library/collections.html><code>collections</code> 模块</a>
</li><li><a href=http://docs.python.org/3.1/library/math.html><code>math[数学]</code> 模块</a>
</li><li><a href=http://docs.python.org/3.1/library/pickle.html><code>pickle</code> 模块</a>
</li><li><a href=http://docs.python.org/3.1/library/copy.html><code>copy</code> 模块</a>
</li><li><a href=http://docs.python.org/3.1/library/abc.html><code>abc</code> (“抽象基类”) 模块</a>
</li></ul>
<p>其它启发式阅读:<ul>
<li><a href=http://www.python.org/doc/3.1/library/string.html#formatspec>迷你语言格式规范</a>
</li><li><a href=http://www.python.org/doc/3.1/reference/datamodel.html>Python 数据模型</a>
</li><li><a href=http://www.python.org/doc/3.1/library/stdtypes.html>内建类型</a>
</li><li><a href=http://www.python.org/dev/peps/pep-0357/><abbr>PEP</abbr> 357: 使任何对象可以使用切片</a>
</li><li><a href=http://www.python.org/dev/peps/pep-3119/><abbr>PEP</abbr> 3119: 抽象基类简介</a>
</li></ul>
<p class=v><a href="porting-code-to-python-3-with-2to3.html" rel=prev title="back to “Porting code to Python 3 with 2to3”"><span class=u>☜</span></a> <a rel=next href="where-to-go-from-here.html" title="onward to “Where To Go From Here”"><span class=u>☞</span></a>
<p class=c>© 2001–9 <a href="about.html">Mark Pilgrim</a>
<script src="j/jquery.js"></script>
<script src="j/prettify.js"></script>
<script src="j/dip3.js"></script>