-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
569 lines (308 loc) · 548 KB
/
atom.xml
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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Keep Coding</title>
<subtitle>from a coder</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="https://yuechuanx.top/"/>
<updated>2020-07-22T08:25:04.000Z</updated>
<id>https://yuechuanx.top/</id>
<author>
<name>Yuechuan Xiao</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title><流畅的Python>:可迭代的对象、迭代器和生成器</title>
<link href="https://yuechuanx.top/Python/fluent-python-notes-chap-14/"/>
<id>https://yuechuanx.top/Python/fluent-python-notes-chap-14/</id>
<published>2020-07-22T08:25:04.000Z</published>
<updated>2020-07-22T08:25:04.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>当我在自己的程序中发现用到了模式,我觉得这就表明某个地方出错了。程序的形式应该仅仅反映它所要解决的问题。代码中其他任何外加的形式都是一个信号,(至少对我来说)表明我对问题的抽象还不够深——这通常意味着自己正在手动完成事情,本应该通过写代码来让宏的扩展自动实现。</p><p>——Paul Graham, Lisp 黑客和风险投资人</p></blockquote><p>Python 内置了迭代器模式,用于进行<strong>惰性运算</strong>,按需求一次获取一个数据项,避免不必要的提前计算。</p><p>迭代器在 Python 中并不是一个具体类型的对象,更多地使指一个具体<strong>协议</strong>。</p><ul><li>所有的生成器都是迭代器。它们都实现了迭代器接口,区别于迭代器用于从集合中取出元素,生成器用来生成元素。</li></ul><h2 id="迭代器协议"><a class="markdownIt-Anchor" href="#迭代器协议"></a> 迭代器协议</h2><p>Python 解释器在迭代一个对象时,会自动调用 <code>iter(x)</code>。<br />内置的 <code>iter</code> 函数会做以下操作:</p><ol><li>检查对象是否实现了 <code>__iter__</code> 方法(<code>abc.Iterable</code>),若实现,且返回的结果是个迭代器(<code>abc.Iterator</code>),则调用它,获取迭代器并返回;</li><li>若没实现,但实现了 <code>__getitem__</code> 方法(<code>abc.Sequence</code>),若实现则尝试从 0 开始按顺序获取元素并返回;</li><li>以上尝试失败,抛出 <code>TypeError</code>,表明对象不可迭代。</li></ol><p>判断一个对象是否可迭代,最好的方法不是用 <code>isinstance</code> 来判断,而应该直接尝试调用 <code>iter</code> 函数。</p><p>注:可迭代对象和迭代器不一样。从鸭子类型的角度看,可迭代对象 <code>Iterable</code> 要实现 <code>__iter__</code>,而迭代器 <code>Iterator</code> 要实现 <code>__next__</code>. 不过,迭代器上也实现了 <code>__iter__</code>,用于<a href="https://github.com/python/cpython/blob/3.7/Lib/_collections_abc.py#L268" target="_blank" rel="noopener">返回自身</a>。</p><h2 id="迭代器的具体实现"><a class="markdownIt-Anchor" href="#迭代器的具体实现"></a> 迭代器的具体实现</h2><p>《设计模式:可复用面向对象软件的基础》一书讲解迭代器设计模式时,在“适用性”一 节中说:<br />迭代器模式可用来:</p><ul><li><p>访问一个聚合对象的内容而无需暴露它的内部表示</p></li><li><p>支持对聚合对象的多种遍历</p></li><li><p>为遍历不同的聚合结构提供一个统一的接口(即支持多态迭代)</p></li></ul><p>为了“支持多种遍历”,必须能从同一个可迭代的实例中获取多个<strong>独立</strong>的迭代器,而且各个迭代器要能维护自身的内部状态,因此这一模式正确的实现方式是,每次调用 <code>iter(my_iterable)</code> 都新建一个独立的迭代器。</p><h3 id="序列可迭代的原因iter函数"><a class="markdownIt-Anchor" href="#序列可迭代的原因iter函数"></a> 序列可迭代的原因:<code>iter</code>函数</h3><p>解释器需要迭代对象 x 时,会自动调用 <code>iter(x)</code>:</p><ol><li><p>检查对象是否实现了 <code>__iter__</code> 方法并调用,获取到迭代器</p></li><li><p>如果没有实现<code>__iter__</code>, 检查是否有 <code>__getitem__</code> 函数,尝试按顺序下标获取元素</p></li><li><p>如果上述状况都不符合, 抛出 “C object is not iterable” 异常</p></li></ol><p>这就是为什么这个示例需要定义 <code>SentenceIterator</code> 类。所以,不应该把 Sentence 本身作为一个迭代器,否则每次调用 <code>iter(sentence)</code> 时返回的都是自身,就无法进行多次迭代了。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 通过实现迭代器协议,让一个对象变得可迭代</span></span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> abc</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sentence</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, sentence)</span>:</span></span><br><span class="line"> self.sentence = sentence</span><br><span class="line"> self.words = re.findall(<span class="string">r'\w+'</span>, sentence)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__iter__</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="string">"""返回 iter(self) 的结果"""</span></span><br><span class="line"> <span class="keyword">return</span> SentenceIterator(self.words)</span><br><span class="line"> </span><br><span class="line"></span><br><span class="line"><span class="comment"># 推荐的做法是对迭代器对象进行单独实现</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SentenceIterator</span><span class="params">(abc.Iterator)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, words)</span>:</span></span><br><span class="line"> self.words = words</span><br><span class="line"> self._index = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__next__</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="string">"""调用时返回下一个对象"""</span></span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> word = self.words[self._index]</span><br><span class="line"> <span class="keyword">except</span> IndexError:</span><br><span class="line"> <span class="keyword">raise</span> StopIteration()</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> self._index += <span class="number">1</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> word</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> </span><br><span class="line">sentence = Sentence(<span class="string">'Return a list of all non-overlapping matches in the string.'</span>)</span><br><span class="line"><span class="keyword">assert</span> isinstance(sentence, abc.Iterable) <span class="comment"># 实现了 __iter__,就支持 Iterable 协议</span></span><br><span class="line"><span class="keyword">assert</span> isinstance(iter(sentence), abc.Iterator)</span><br><span class="line"><span class="keyword">for</span> word <span class="keyword">in</span> sentence:</span><br><span class="line"> print(word, end=<span class="string">'·'</span>)</span><br></pre></td></tr></table></figure><pre><code>Return·a·list·of·all·non·overlapping·matches·in·the·string·</code></pre><p>上面的例子中,我们的 <code>SentenceIterator</code> 对象继承自 <code>abc.Iterator</code> 通过了迭代器测试。而且 <code>Iterator</code> 替我们实现了 <code>__iter__</code> 方法。<br />但是,如果我们不继承它,我们就需要同时实现 <code>__next__</code> 抽象方法和<em>实际迭代中并不会用到的</em> <code>__iter__</code> 非抽象方法,才能通过 <code>Iterator</code> 测试。</p><h2 id="可迭代对象与迭代器的比较"><a class="markdownIt-Anchor" href="#可迭代对象与迭代器的比较"></a> 可迭代对象与迭代器的比较</h2><ul><li><p>可迭代对象</p><p>使用 iter 内置函数可以获取迭代器的对象。</p></li><li><p>迭代器</p><p>迭代器是一种对象:实现了 <code>__next__</code> 方法,返回序列中的下一个元素,并在无元素可迭代时抛出 <code>StopIteration</code> 异常。</p></li></ul><h2 id="生成器函数"><a class="markdownIt-Anchor" href="#生成器函数"></a> 生成器函数</h2><h3 id="生成器函数的工作原理"><a class="markdownIt-Anchor" href="#生成器函数的工作原理"></a> 生成器函数的工作原理</h3><ul><li><p>只要函数的定义体中有 <code>yield</code> 关键字,该函数就是生成器函数。</p></li><li><p>调用生成器函数会返回生成器对象。</p></li></ul><p>如果懒得自己写一个迭代器,可以直接用 Python 的生成器函数来在调用 <code>__iter__</code> 时生成一个迭代器。</p><p>注:在 Python 社区中,大家并没有对“生成器”和“迭代器”两个概念做太多区分,很多人是混着用的。不过无所谓啦。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用生成器函数来帮我们创建迭代器</span></span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sentence</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, sentence)</span>:</span></span><br><span class="line"> self.sentence = sentence</span><br><span class="line"> self.words = re.findall(<span class="string">r'\w+'</span>, sentence)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__iter__</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">for</span> word <span class="keyword">in</span> self.words:</span><br><span class="line"> <span class="keyword">yield</span> word</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">sentence = Sentence(<span class="string">'Return a list of all non-overlapping matches in the string.'</span>)</span><br><span class="line"><span class="keyword">for</span> word <span class="keyword">in</span> sentence:</span><br><span class="line"> print(word, end=<span class="string">'·'</span>)</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用 re.finditer 来惰性生成值</span></span><br><span class="line"><span class="comment"># 使用生成器表达式(很久没用过了)</span></span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sentence</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, sentence)</span>:</span></span><br><span class="line"> self.re_word = re.compile(<span class="string">r'\w+'</span>)</span><br><span class="line"> self.sentence = sentence</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__iter__</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> (match.group()</span><br><span class="line"> <span class="keyword">for</span> match <span class="keyword">in</span> self.re_word.finditer(self.sentence))</span><br><span class="line"></span><br><span class="line">sentence = Sentence(<span class="string">'Return a list of all non-overlapping matches in the string.'</span>)</span><br><span class="line"><span class="keyword">for</span> word <span class="keyword">in</span> sentence:</span><br><span class="line"> print(word, end=<span class="string">'·'</span>)</span><br></pre></td></tr></table></figure><h2 id="案例使用-itertools模块生成等差数列"><a class="markdownIt-Anchor" href="#案例使用-itertools模块生成等差数列"></a> 案例:使用 <code>itertools</code>模块生成等差数列</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 实用模块</span></span><br><span class="line"><span class="keyword">import</span> itertools</span><br><span class="line"></span><br><span class="line"><span class="comment"># takewhile & dropwhile</span></span><br><span class="line">print(list(itertools.takewhile(<span class="keyword">lambda</span> x: x < <span class="number">3</span>, [<span class="number">1</span>, <span class="number">5</span>, <span class="number">2</span>, <span class="number">4</span>, <span class="number">3</span>])))</span><br><span class="line">print(list(itertools.dropwhile(<span class="keyword">lambda</span> x: x < <span class="number">3</span>, [<span class="number">1</span>, <span class="number">5</span>, <span class="number">2</span>, <span class="number">4</span>, <span class="number">3</span>])))</span><br><span class="line"><span class="comment"># zip</span></span><br><span class="line">print(list(zip(range(<span class="number">5</span>), range(<span class="number">3</span>))))</span><br><span class="line">print(list(itertools.zip_longest(range(<span class="number">5</span>), range(<span class="number">3</span>))))</span><br><span class="line"></span><br><span class="line"><span class="comment"># itertools.groupby</span></span><br><span class="line">animals = [<span class="string">'rat'</span>, <span class="string">'bear'</span>, <span class="string">'duck'</span>, <span class="string">'bat'</span>, <span class="string">'eagle'</span>, <span class="string">'shark'</span>, <span class="string">'dolphin'</span>, <span class="string">'lion'</span>]</span><br><span class="line"><span class="comment"># groupby 需要假定输入的可迭代对象已经按照分组标准进行排序(至少同组的元素要连在一起)</span></span><br><span class="line">print(<span class="string">'----'</span>)</span><br><span class="line"><span class="keyword">for</span> length, animal <span class="keyword">in</span> itertools.groupby(animals, len):</span><br><span class="line"> print(length, list(animal))</span><br><span class="line">print(<span class="string">'----'</span>)</span><br><span class="line">animals.sort(key=len)</span><br><span class="line"><span class="keyword">for</span> length, animal <span class="keyword">in</span> itertools.groupby(animals, len):</span><br><span class="line"> print(length, list(animal))</span><br><span class="line">print(<span class="string">'---'</span>)</span><br><span class="line"><span class="comment"># tee</span></span><br><span class="line">g1, g2 = itertools.tee(<span class="string">'abc'</span>, <span class="number">2</span>)</span><br><span class="line">print(list(zip(g1, g2)))</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用 yield from 语句可以在生成器函数中直接迭代一个迭代器</span></span><br><span class="line"><span class="keyword">from</span> itertools <span class="keyword">import</span> chain</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">my_itertools_chain</span><span class="params">(*iterators)</span>:</span></span><br><span class="line"> <span class="keyword">for</span> iterator <span class="keyword">in</span> iterators:</span><br><span class="line"> <span class="keyword">yield</span> <span class="keyword">from</span> iterator</span><br><span class="line"></span><br><span class="line">chain1 = my_itertools_chain([<span class="number">1</span>, <span class="number">2</span>], [<span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>])</span><br><span class="line">chain2 = chain([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>], [<span class="number">4</span>, <span class="number">5</span>])</span><br><span class="line">print(list(chain1), list(chain2))</span><br></pre></td></tr></table></figure><pre><code>[1, 2, 3, 4, 5] [1, 2, 3, 4, 5]</code></pre><p><code>iter</code> 函数还有一个鲜为人知的用法:传入两个参数,使用常规的函数或任何可调用的对象创建迭代器。这样使用时,第一个参数必须是可调用的对象,用于不断调用(没有参数),产出各个值;第二个值是哨符,这是个标记值,当可调用的对象返回这个值时,触发迭代器抛出 StopIteration 异常,而不产出哨符。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># iter 的神奇用法</span></span><br><span class="line"><span class="comment"># iter(callable, sentinel)</span></span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">rand</span><span class="params">()</span>:</span></span><br><span class="line"> <span class="keyword">return</span> random.randint(<span class="number">1</span>, <span class="number">6</span>)</span><br><span class="line"><span class="comment"># 不停调用 rand(), 直到产出一个 5</span></span><br><span class="line">print(list(iter(rand, <span class="number">5</span>)))</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>当我在自己的程序中发现用到了模式,我觉得这就表明某个地方出错了。程序的形式应该仅仅反映它所要解决的问题。代码中其他任何外加的形式都是一个信号,(至少对我来说)表明我对问题的抽象还不够深——这通常意味着自己正在手动完成事情,本应该通过写代码来让宏的扩
</summary>
<category term="Python" scheme="https://yuechuanx.top/categories/Python/"/>
</entry>
<entry>
<title>Python 中的方法解析顺序(MRO)</title>
<link href="https://yuechuanx.top/Python/python-mro/"/>
<id>https://yuechuanx.top/Python/python-mro/</id>
<published>2020-07-20T07:19:06.000Z</published>
<updated>2020-07-20T07:19:06.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>文章转载自:</p><p><a href="https://hanjianwei.com/2013/07/25/python-mro/" target="_blank" rel="noopener">https://hanjianwei.com/2013/07/25/python-mro/</a></p></blockquote><h2 id="什么是mro"><a class="markdownIt-Anchor" href="#什么是mro"></a> 什么是MRO</h2><p>对于支持继承的编程语言来说,其方法(属性)可能定义在当前类,也可能来自于基类,所以在方法调用时就需要对当前类和基类进行搜索以确定方法所在的位置。</p><p>而搜索的顺序就是所谓的「方法解析顺序」(Method Resolution Order,或MRO)。对于只支持单继承的语言来说,MRO 一般比较简单;而对于 Python 这种支持多继承的语言来说,MRO 就复杂很多。</p><p>先看一个「菱形继承」的例子:</p><p><img src="https://hanjianwei.com/2013/07/25/python-mro/class_diamond.svg" alt="菱形继承" /></p><p>如果 <code>x</code> 是 <code>D</code> 的一个实例,那么 <code>x.show()</code> 到底会调用哪个 <code>show</code> 方法呢?</p><p>如果按照 <code>[D, B, A, C]</code> 的搜索顺序,那么 <code>x.show()</code> 会调用 <code>A.show()</code>;</p><p>如果按照 <code>[D, B, C, A]</code> 的搜索顺序,那么 <code>x.show()</code> 会调用 <code>C.show()</code>。</p><p>由此可见,MRO 是把类的继承关系线性化的一个过程,而线性化方式决定了程序运行过程中具体会调用哪个方法。既然如此,那什么样的 MRO 才是最合理的?Python 中又是如何实现的呢?</p><p>Python 至少有<a href="http://python-history.blogspot.com/2010/06/method-resolution-order.html" target="_blank" rel="noopener">三种不同的 MRO</a>:</p><ol><li>经典类(classic class)的深度遍历。</li><li>Python 2.2 的新式类(new-style class)预计算。</li><li>Python 2.3 的新式类的<a href="http://en.wikipedia.org/wiki/C3_linearization" target="_blank" rel="noopener"> C3 算法</a>。它也是 Python 3 唯一支持的方式。</li></ol><h2 id="经典类的-mro"><a class="markdownIt-Anchor" href="#经典类的-mro"></a> 经典类的 MRO</h2><p>Python 有<a href="http://wiki.python.org/moin/NewClassVsClassicClass" target="_blank" rel="noopener">两种类</a>:经典类(classic class)和新式类(new-style class)。两者的不同之处在于新式类继承自 <code>object</code>。</p><p>在 Python 2.1 以前,经典类是唯一可用的形式;Python 2.2 引入了新式类,使得类和内置类型更加统一;在 Python 3 中,新式类是唯一支持的类。</p><p>经典类采用了一种很简单的 MRO 方法:从左至右的<a href="http://en.wikipedia.org/wiki/Depth-first_search" target="_blank" rel="noopener">深度优先遍历</a>。以上述「菱形继承」为例,其查找顺序为 <code>[D, B, A, C, A]</code>,如果只保留重复类的第一个则结果为 <code>[D, B, A, C]</code>。我们可以用 <code>inspect.getmro</code> 来获取类的 MRO:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">import</span> inspect</span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">A</span>:</span></span><br><span class="line"><span class="meta">... </span> <span class="function"><span class="keyword">def</span> <span class="title">show</span><span class="params">(self)</span>:</span></span><br><span class="line"><span class="meta">... </span> <span class="keyword">print</span> <span class="string">"A.show()"</span></span><br><span class="line">...</span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">B</span><span class="params">(A)</span>:</span> <span class="keyword">pass</span></span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">C</span><span class="params">(A)</span>:</span></span><br><span class="line"><span class="meta">... </span> <span class="function"><span class="keyword">def</span> <span class="title">show</span><span class="params">(self)</span>:</span></span><br><span class="line"><span class="meta">... </span> <span class="keyword">print</span> <span class="string">"C.show()"</span></span><br><span class="line">...</span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">D</span><span class="params">(B, C)</span>:</span> <span class="keyword">pass</span></span><br><span class="line"><span class="meta">>>> </span>inspect.getmro(D)</span><br><span class="line">(<class __main__.D at 0x105f0a6d0>, <class __main__.B at 0x105f0a600>, <class __main__.A at 0x105f0a668>, <class __main__.C at 0x105f0a738>)</span><br><span class="line"><span class="meta">>>> </span>x = D()</span><br><span class="line"><span class="meta">>>> </span>x.show()</span><br><span class="line">A.show()</span><br></pre></td></tr></table></figure><p>这种深度优先遍历对于简单的情况还能处理的不错,但是对于上述「菱形继承」其结果却不尽如人意:虽然 <code>C.show()</code> 是 <code>A.show()</code> 的更具体化版本(显示了更多的信息),但我们的 <code>x.show()</code> 没有调用它,而是调用了 <code>A.show()</code>。这显然不是我们希望的结果。</p><p>对于新式类而言,所有的类都继承自 <code>object</code>,所以「菱形继承」是非常普遍的现象,因此不可能采用这种 MRO 方式。</p><h2 id="python-22-的新式类-mro"><a class="markdownIt-Anchor" href="#python-22-的新式类-mro"></a> Python 2.2 的新式类 MRO</h2><p>为解决经典类 MRO 所存在的问题,Python 2.2 针对新式类提出了一种新的 MRO 计算方式:在定义类时就计算出该类的 MRO 并将其作为类的属性。因此新式类可以直接通过 <code>__mro__</code> 属性获取类的 MRO。</p><p>Python 2.2 的新式类 MRO 计算方式和经典类 MRO 的计算方式非常相似:它仍然采用从左至右的深度优先遍历,但是如果遍历中出现重复的类,只保留最后一个。重新考虑上面「菱形继承」的例子,由于新式类继承自 <code>object</code> 因此类图稍有改变:</p><p><img src="https://hanjianwei.com/2013/07/25/python-mro/newclass_diamond.svg" alt="新式类菱形继承" /></p><p>按照深度遍历,其顺序为 <code>[D, B, A, object, C, A, object]</code>,重复类只保留最后一个,因此变为 <code>[D, B, C, A, object]</code>。代码为:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">A</span><span class="params">(object)</span>:</span></span><br><span class="line"><span class="meta">... </span> <span class="function"><span class="keyword">def</span> <span class="title">show</span><span class="params">(self)</span>:</span></span><br><span class="line"><span class="meta">... </span> <span class="keyword">print</span> <span class="string">"A.show()"</span></span><br><span class="line">...</span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">B</span><span class="params">(A)</span>:</span> <span class="keyword">pass</span></span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">C</span><span class="params">(A)</span>:</span></span><br><span class="line"><span class="meta">... </span> <span class="function"><span class="keyword">def</span> <span class="title">show</span><span class="params">(self)</span>:</span></span><br><span class="line"><span class="meta">... </span> <span class="keyword">print</span> <span class="string">"C.show()"</span></span><br><span class="line">...</span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">D</span><span class="params">(B, C)</span>:</span> <span class="keyword">pass</span></span><br><span class="line"><span class="meta">>>> </span>D.__mro__</span><br><span class="line">(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)</span><br><span class="line"><span class="meta">>>> </span>x = D()</span><br><span class="line"><span class="meta">>>> </span>x.show()</span><br><span class="line">C.show()</span><br></pre></td></tr></table></figure><p>这种 MRO 方式已经能够解决「菱形继承」问题,再让我们看个稍微复杂点的例子:</p><p><img src="https://hanjianwei.com/2013/07/25/python-mro/class_conflict.svg" alt="类型冲突" /></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">X</span><span class="params">(object)</span>:</span> <span class="keyword">pass</span></span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">Y</span><span class="params">(object)</span>:</span> <span class="keyword">pass</span></span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">A</span><span class="params">(X, Y)</span>:</span> <span class="keyword">pass</span></span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">B</span><span class="params">(Y, X)</span>:</span> <span class="keyword">pass</span></span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">C</span><span class="params">(A, B)</span>:</span> <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><p>首先进行深度遍历,结果为 <code>[C, A, X, object, Y, object, B, Y, object, X, object]</code>;然后,只保留重复元素的最后一个,结果为 <code>[C, A, B, Y, X, object]</code>。Python 2.2 在实现该方法的时候进行了调整,使其更尊重基类中类出现的顺序,其实际结果为 <code>[C, A, B, X, Y, object]</code>。</p><p>这样的结果是否合理呢?首先我们看下各个类中的方法解析顺序:对于 <code>A</code> 来说,其搜索顺序为 <code>[A, X, Y, object]</code>;对于 <code>B</code>,其搜索顺序为 <code>[B, Y, X, object]</code>;对于 <code>C</code>,其搜索顺序为 <code>[C, A, B, X, Y, object]</code>。我们会发现,<code>B</code> 和 <code>C</code> 中 <code>X</code>、<code>Y</code> 的搜索顺序是相反的!也就是说,当 <code>B</code> 被继承时,它本身的行为竟然也发生了改变,这很容易导致不易察觉的错误。此外,即使把 <code>C</code> 搜索顺序中 <code>X</code> 和 <code>Y</code> 互换仍然不能解决问题,这时候它又会和 <code>A</code> 中的搜索顺序相矛盾。</p><p>事实上,不但上述特殊情况会出现问题,在<a href="http://mail.python.org/pipermail/python-dev/2002-October/029035.html" target="_blank" rel="noopener">其它情况</a>下也可能出问题。其原因在于,上述继承关系违反了线性化的「 <strong>单调性原则</strong> 」。<a href="http://www.python.org/download/releases/2.3/mro/" target="_blank" rel="noopener">Michele Simionato</a>对单调性的定义为:</p><blockquote><p>A MRO is monotonic when the following is true: if C1 precedes C2 in the linearization of C, then C1 precedes C2 in the linearization of any subclass of C. Otherwise, the innocuous operation of deriving a new class could change the resolution order of methods, potentially introducing very subtle bugs.</p></blockquote><p>也就是说,子类不能改变基类的方法搜索顺序。在 Python 2.2 的 MRO 算法中并不能保证这种单调性,它不会阻止程序员写出上述具有二义性的继承关系,因此很可能成为错误的根源。</p><p>除了单调性之外,Python 2.2 及 经典类的 MRO 也可能违反继承的「 <strong>局部优先级</strong> 」,具体例子可以参见<a href="http://www.python.org/download/releases/2.3/mro/#bad-method-resolution-orders" target="_blank" rel="noopener">官方文档</a>。采用一种更好的 MRO 方式势在必行。</p><h3 id="c3-mro"><a class="markdownIt-Anchor" href="#c3-mro"></a> C3 MRO</h3><p>为解决 Python 2.2 中 MRO 所存在的问题,Python 2.3以后采用了<a href="http://en.wikipedia.org/wiki/C3_linearization" target="_blank" rel="noopener"> C3 方法</a>来确定方法解析顺序。你如果在 Python 2.3 以后版本里输入上述代码,就会产生一个异常,禁止创建具有二义性的继承关系:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">C</span><span class="params">(A, B)</span>:</span> <span class="keyword">pass</span></span><br><span class="line">Traceback (most recent call last):</span><br><span class="line"> File <span class="string">"<ipython-input-8-01bae83dc806>"</span>, line <span class="number">1</span>, <span class="keyword">in</span> <module></span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">C</span><span class="params">(A, B)</span>:</span> <span class="keyword">pass</span></span><br><span class="line">TypeError: Error when calling the metaclass bases</span><br><span class="line"> Cannot create a consistent method resolution</span><br><span class="line">order (MRO) <span class="keyword">for</span> bases X, Y</span><br></pre></td></tr></table></figure><p>我们把类 <code>C</code> 的线性化(MRO)记为 <code>L[C] = [C1, C2,…,CN]</code>。其中 <code>C1</code> 称为 <code>L[C]</code> 的头,其余元素 <code>[C2,…,CN]</code> 称为尾。如果一个类 <code>C</code> 继承自基类 <code>B1</code>、<code>B2</code>、……、<code>BN</code>,那么我们可以根据以下两步计算出 <code>L[C]</code>:</p><ol><li><code>L[object] = [object]</code></li><li><code>L[C(B1…BN)] = [C] + merge(L[B1]…L[BN], [B1]…[BN])</code></li></ol><p>这里的关键在于 <code>merge</code>,其输入是一组列表,按照如下方式输出一个列表:</p><ol><li>检查第一个列表的头元素(如 <code>L[B1]</code> 的头),记作 <code>H</code>。</li><li>若 <code>H</code> 未出现在其它列表的尾部,则将其输出,并将其从所有列表中删除,然后回到步骤1;否则,取出下一个列表的头部记作 <code>H</code>,继续该步骤。</li><li>重复上述步骤,直至列表为空或者不能再找出可以输出的元素。如果是前一种情况,则算法结束;如果是后一种情况,说明无法构建继承关系,Python 会抛出异常。</li></ol><p>该方法有点类似于图的<a href="http://en.wikipedia.org/wiki/Topological_sorting" target="_blank" rel="noopener">拓扑排序</a>,但它同时还考虑了基类的出现顺序。我们用 C3 分析一下刚才的例子。</p><p><code>object</code>,<code>X</code>,<code>Y</code> 的线性化结果比较简单:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">L[object] = [object]</span><br><span class="line">L[X] = [X, object]</span><br><span class="line">L[Y] = [Y, object]</span><br></pre></td></tr></table></figure><p><code>A</code> 的线性化计算如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">L[A] = [A] + merge(L[X], L[Y], [X], [Y])</span><br><span class="line"> = [A] + merge([X, object], [Y, object], [X], [Y])</span><br><span class="line"> = [A, X] + merge([object], [Y, object], [Y])</span><br><span class="line"> = [A, X, Y] + merge([object], [object])</span><br><span class="line"> = [A, X, Y, object]</span><br></pre></td></tr></table></figure><p>注意第3步,<code>merge([object], [Y, object], [Y])</code> 中首先输出的是 <code>Y</code> 而不是 <code>object</code>。这是因为 <code>object</code> 虽然是第一个列表的头,但是它出现在了第二个列表的尾部。所以我们会跳过第一个列表,去检查第二个列表的头部,也就是 <code>Y</code>。<code>Y</code> 没有出现在其它列表的尾部,所以将其输出。</p><p>同理,<code>B</code> 的线性化结果为:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">L[B] = [B, Y, X, object]</span><br></pre></td></tr></table></figure><p>最后,我们看看 <code>C</code> 的线性化结果:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">L[C] = [C] + merge(L[A], L[B], [A], [B])</span><br><span class="line"> = [C] + merge([A, X, Y, object], [B, Y, X, object], [A], [B])</span><br><span class="line"> = [C, A] + merge([X, Y, object], [B, Y, X, object], [B])</span><br><span class="line"> = [C, A, B] + merge([X, Y, object], [Y, X, object])</span><br></pre></td></tr></table></figure><p>到了最后一步我们没有办法继续计算下去 了:<code>X</code> 虽然是第一个列表的头,但是它出现在了第二个列表的尾部;<code>Y</code> 虽然是第二个列表的头,但是它出现在了第一个列表的尾部。因此,我们无法构建一个没有二义性的继承关系,只能手工去解决(比如改变 <code>B</code> 基类中 <code>X</code>、<code>Y</code> 的顺序)。</p><p>我们再看一个没有冲突的例子:</p><p><img src="https://hanjianwei.com/2013/07/25/python-mro/c3_example.svg" alt="C3例子" /></p><p>计算过程如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">L[object] = [object]</span><br><span class="line">L[D] = [D, object]</span><br><span class="line">L[E] = [E, object]</span><br><span class="line">L[F] = [F, object]</span><br><span class="line">L[B] = [B, D, E, object]</span><br><span class="line">L[C] = [C, D, F, object]</span><br><span class="line">L[A] = [A] + merge(L[B], L[C], [B], [C])</span><br><span class="line"> = [A] + merge([B, D, E, object], [C, D, F, object], [B], [C])</span><br><span class="line"> = [A, B] + merge([D, E, object], [C, D, F, object], [C])</span><br><span class="line"> = [A, B, C] + merge([D, E, object], [D, F, object])</span><br><span class="line"> = [A, B, C, D] + merge([E, object], [F, object])</span><br><span class="line"> = [A, B, C, D, E] + merge([object], [F, object])</span><br><span class="line"> = [A, B, C, D, E, F] + merge([object], [object])</span><br><span class="line"> = [A, B, C, D, E, F, object]</span><br></pre></td></tr></table></figure><p>当然,可以用代码验证类的 MRO,上面的例子可以写作:</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">D</span><span class="params">(object)</span>:</span> <span class="keyword">pass</span></span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">E</span><span class="params">(object)</span>:</span> <span class="keyword">pass</span></span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">F</span><span class="params">(object)</span>:</span> <span class="keyword">pass</span></span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">B</span><span class="params">(D, E)</span>:</span> <span class="keyword">pass</span></span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">C</span><span class="params">(D, F)</span>:</span> <span class="keyword">pass</span></span><br><span class="line"><span class="meta">>>> </span><span class="class"><span class="keyword">class</span> <span class="title">A</span><span class="params">(B, C)</span>:</span> <span class="keyword">pass</span></span><br><span class="line"><span class="meta">>>> </span>A.__mro__</span><br><span class="line">(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>文章转载自:</p>
<p><a href="https://hanjianwei.com/2013/07/25/python-mro/" target="_blank" rel="noopener">https://hanjianwei.com/
</summary>
<category term="Python" scheme="https://yuechuanx.top/categories/Python/"/>
</entry>
<entry>
<title><流畅的Python> 接口:从协议到抽象基类</title>
<link href="https://yuechuanx.top/Python/fluent-python-notes-chap-11/"/>
<id>https://yuechuanx.top/Python/fluent-python-notes-chap-11/</id>
<published>2020-07-09T08:24:11.000Z</published>
<updated>2020-07-09T08:24:11.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>抽象类表示接口。<br />——Bjarne Stroustrup, C++ 之父</p></blockquote><p>本章讨论的话题是接口:</p><p>从<strong>鸭子类型</strong>的代表特征动态协议,到使接口更明确、能验证实现是否符合规定的抽象基类(Abstract Base Class, ABC)。</p><blockquote><p>接口的定义:对象公开方法的子集,让对象在系统中扮演特定的角色。<br />协议是接口,但不是正式的(只由文档和约定定义),因此协议不能像正式接口那样施加限制。<br />允许一个类上只实现部分接口。</p></blockquote><h2 id="接口与协议"><a class="markdownIt-Anchor" href="#接口与协议"></a> 接口与协议</h2><ul><li><p>什么是接口</p><p>对象公开方法的子集,让对象在系统中扮演特定的角色。</p></li><li><p>鸭子类型与动态协议</p></li><li><p>受保护的类型与私有类型不能在接口中</p></li><li><p>可以把公开的数据属性放在接口中</p></li></ul><h2 id="案例通过实现-getitem-方法支持序列操作"><a class="markdownIt-Anchor" href="#案例通过实现-getitem-方法支持序列操作"></a> 案例:通过实现 <strong>getitem</strong> 方法支持序列操作</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__getitem__</span><span class="params">(self, pos)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> range(<span class="number">0</span>, <span class="number">30</span>, <span class="number">10</span>)[pos]</span><br><span class="line"> </span><br><span class="line">f = Foo()</span><br><span class="line">print(f[<span class="number">1</span>])</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> f:</span><br><span class="line"> print(i)</span><br></pre></td></tr></table></figure><p>Foo 实现了序列协议的 <code>__getitem__</code> 方法。因此可支持下标操作。</p><p>Foo 实例是可迭代的对象,因此可以使用 in 操作符</p><h2 id="案例在运行时实现协议猴子补丁"><a class="markdownIt-Anchor" href="#案例在运行时实现协议猴子补丁"></a> 案例:在运行时实现协议——猴子补丁</h2><p>FrenchDeck 类见前面章节。</p><p>FrenchDeck 实例的行为像序列,那么其实可以用 random 的 <code>shuffle</code> 方法来代替在类中实现的方法。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> random <span class="keyword">import</span> shuffle</span><br><span class="line"><span class="keyword">from</span> frenchdeck <span class="keyword">import</span> FrenchDeck</span><br><span class="line"></span><br><span class="line">deck = FrenchDeck()</span><br><span class="line">shuffle(deck)</span><br><span class="line"><span class="comment"># TypeError: 'FrenchDeck' object does not support item assigment</span></span><br></pre></td></tr></table></figure><p>FrenchDeck 对象不支持元素赋值。这是因为它只实现了不可变的序列协议,可变的序列还必须提供 <code>__setitem__</code> 方法。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">set_card</span><span class="params">(deck, pos, card)</span>:</span></span><br><span class="line"> deck._cards[pos] = card</span><br><span class="line"> </span><br><span class="line">FrenchDeck.__setitem__ = set_card</span><br><span class="line">shuffle(deck)</span><br><span class="line">print(deck[:<span class="number">5</span>])</span><br><span class="line">print(deck[:<span class="number">5</span>])</span><br></pre></td></tr></table></figure><p>这种技术叫做猴子补丁:在运行是修改类或程序,而不改动源码。缺陷是补丁代码与要打补丁的程序耦合紧密。</p><h2 id="抽象基类abc"><a class="markdownIt-Anchor" href="#抽象基类abc"></a> 抽象基类(abc)</h2><p>抽象基类是一个非常实用的功能,可以使用抽象基类来检测某个类是否实现了某种协议,而这个类并不需要继承自这个抽象类。<br /><a href="https://docs.python.org/3/library/collections.abc.html" target="_blank" rel="noopener"><code>collections.abc</code></a> 和 <a href="https://docs.python.org/3/library/numbers.html" target="_blank" rel="noopener"><code>numbers</code></a> 模块中提供了许多常用的抽象基类以用于这种检测。</p><p>有了这个功能,我们在自己实现函数时,就不需要非常关心外面传进来的参数的<strong>具体类型</strong>(<code>isinstance(param, list)</code>),只需要关注这个参数是否支持我们<strong>需要的协议</strong>(<code>isinstance(param, abc.Sequence</code>)以保障正常使用就可以了。</p><p>但是注意:从 Python 简洁性考虑,最好不要自己创建新的抽象基类,而应尽量考虑使用现有的抽象基类。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 抽象基类</span></span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> abc</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span>:</span></span><br><span class="line"> <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">B</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__len__</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">assert</span> <span class="keyword">not</span> isinstance(A(), abc.Sized)</span><br><span class="line"><span class="keyword">assert</span> isinstance(B(), abc.Sized)</span><br><span class="line"><span class="keyword">assert</span> abc.Sequence <span class="keyword">not</span> <span class="keyword">in</span> list.__bases__ <span class="comment"># list 并不是 Sequence 的子类</span></span><br><span class="line"><span class="keyword">assert</span> isinstance([], abc.Sequence) <span class="comment"># 但是 list 实例支持序列协议</span></span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在抽象基类上进行自己的实现</span></span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> abc</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FailedSized</span><span class="params">(abc.Sized)</span>:</span></span><br><span class="line"> <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NormalSized</span><span class="params">(abc.Sized)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__len__</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">n = NormalSized()</span><br><span class="line">print(len(n))</span><br><span class="line">f = FailedSized() <span class="comment"># 基类的抽象协议未实现,Python 会阻止对象实例化</span></span><br></pre></td></tr></table></figure><p>有一点需要注意:抽象基类上的方法并不都是抽象方法。<br />换句话说,想继承自抽象基类,只需要实现它上面<strong>所有的抽象方法</strong>即可,有些方法的实现是可选的。<br />比如 <a href="https://github.com/python/cpython/blob/3.7/Lib/_collections_abc.py#L889" target="_blank" rel="noopener"><code>Sequence.__contains__</code></a>,Python 对此有自己的实现(使用 <code>__iter__</code> 遍历自身,查找是否有相等的元素)。但如果你在 <code>Sequence</code> 之上实现的序列是有序的,则可以使用二分查找来覆盖 <code>__contains__</code> 方法,从而提高查找效率。</p><p>我们可以使用 <code>__abstractmethods__</code> 属性来查看某个抽象基类上的抽象方法。这个抽象基类的子类必须实现这些方法,才可以被正常实例化。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 自己定义一个抽象基类</span></span><br><span class="line"><span class="keyword">import</span> abc</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用元类的定义方式是 class SomeABC(metaclass=abc.ABCMeta)</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SomeABC</span><span class="params">(abc.ABC)</span>:</span></span><br><span class="line"><span class="meta"> @abc.abstractmethod</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">some_method</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">raise</span> NotImplementedError</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">IllegalClass</span><span class="params">(SomeABC)</span>:</span></span><br><span class="line"> <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">LegalClass</span><span class="params">(SomeABC)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">some_method</span><span class="params">(self)</span>:</span></span><br><span class="line"> print(<span class="string">'Legal class OK'</span>)</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line">l = LegalClass()</span><br><span class="line">l.some_method()</span><br><span class="line">il = IllegalClass() <span class="comment"># Raises</span></span><br></pre></td></tr></table></figure><h2 id="虚拟子类"><a class="markdownIt-Anchor" href="#虚拟子类"></a> 虚拟子类</h2><p>使用 <a href="https://docs.python.org/3/library/abc.html#abc.ABCMeta.register" target="_blank" rel="noopener"><code>register</code></a> 接口可以将某个类注册为某个 ABC 的“虚拟子类”。支持 <code>register</code> 直接调用注册,以及使用 <code>@register</code> 装饰器方式注册(其实这俩是一回事)。<br />注册后,使用 <code>isinstance</code> 以及实例化时,解释器将不会对虚拟子类做任何方法检查。<br />注意:虚拟子类不是子类,所以虚拟子类不会继承抽象基类的任何方法。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 虚拟子类</span></span><br><span class="line"><span class="keyword">import</span> abc</span><br><span class="line"><span class="keyword">import</span> traceback</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SomeABC</span><span class="params">(abc.ABC)</span>:</span></span><br><span class="line"><span class="meta"> @abc.abstractmethod</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">some_method</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">raise</span> NotImplementedError</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">another_method</span><span class="params">(self)</span>:</span></span><br><span class="line"> print(<span class="string">'Another'</span>)</span><br><span class="line"> </span><br><span class="line"><span class="meta"> @classmethod</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__subclasshook__</span><span class="params">(cls, subcls)</span>:</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> 在 register 或者进行 isinstance 判断时进行子类检测</span></span><br><span class="line"><span class="string"> https://docs.python.org/3/library/abc.html#abc.ABCMeta.__subclasshook__</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> print(<span class="string">'Subclass:'</span>, subcls)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">IllegalClass</span>:</span></span><br><span class="line"> <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">SomeABC.register(IllegalClass) <span class="comment"># 注册</span></span><br><span class="line">il = IllegalClass()</span><br><span class="line"><span class="keyword">assert</span> isinstance(il, IllegalClass)</span><br><span class="line"><span class="keyword">assert</span> SomeABC <span class="keyword">not</span> <span class="keyword">in</span> IllegalClass.__mro__ <span class="comment"># isinstance 会返回 True,但 IllegalClass 并不是 SomeABC 的子类</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line"> il.some_method() <span class="comment"># 虚拟子类不是子类,不会从抽象基类上继承任何方法</span></span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line"> traceback.print_exc()</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line"> il.another_method()</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line"> traceback.print_exc()</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>抽象类表示接口。<br />
——Bjarne Stroustrup, C++ 之父</p>
</blockquote>
<p>本章讨论的话题是接口:</p>
<p>从<strong>鸭子类型</strong>的代表特征动态协议,到使接口更明确、能
</summary>
<category term="Python" scheme="https://yuechuanx.top/categories/Python/"/>
<category term="python" scheme="https://yuechuanx.top/tags/python/"/>
</entry>
<entry>
<title>计算机科学自学指南</title>
<link href="https://yuechuanx.top/into-cs/"/>
<id>https://yuechuanx.top/into-cs/</id>
<published>2020-06-24T02:48:36.000Z</published>
<updated>2020-06-24T02:48:36.000Z</updated>
<content type="html"><![CDATA[<h1 id="自学计算机科学"><a class="markdownIt-Anchor" href="#自学计算机科学"></a> 自学计算机科学</h1><blockquote><p>本文档是对<a href="https://teachyourselfcs.com" target="_blank" rel="noopener">TeachYourselfCS</a>内容的中文翻译,原作者为<a href="https://twitter.com/oznova_" target="_blank" rel="noopener">Ozan Onay</a>和<a href="https://twitter.com/quackingduck" target="_blank" rel="noopener">Myles Byrne</a>。如需了解翻译相关信息或帮助改进翻译,请参见<a href="#%E8%BF%99%E4%BB%BD%E6%8C%87%E5%BC%95%E6%98%AF%E8%B0%81%E7%BF%BB%E8%AF%91%E7%9A%84">本文档结尾</a>。</p><p>This document is a Chinese translation of <a href="https://teachyourselfcs.com" target="_blank" rel="noopener">TeachYourselfCS</a>, which is written by <a href="https://twitter.com/oznova_" target="_blank" rel="noopener">Ozan Onay</a> and <a href="https://twitter.com/quackingduck" target="_blank" rel="noopener">Myles Byrne</a>. For more information about this translation, please refer to <a href="#%E8%BF%99%E4%BB%BD%E6%8C%87%E5%BC%95%E6%98%AF%E8%B0%81%E7%BF%BB%E8%AF%91%E7%9A%84">the end of this document</a>.</p></blockquote><p>如果你是一个自学成才的工程师,或者从编程培训班毕业,那么你很有必要学习计算机科学。幸运的是,不必为此花上数年光阴和不菲费用去攻读一个学位:仅仅依靠自己,你就可以获得世界一流水平的教育💸。</p><p>互联网上,到处都有许多的学习资源,然而精华与糟粕并存。你所需要的,不是一个诸如“200+免费在线课程”的清单,而是以下问题的答案:</p><ul><li>你应当学习<strong>哪些科目</strong>,为什么?</li><li>对于这些科目,<strong>最好的书籍或者视频课程</strong>是什么?</li></ul><p>在这份指引中,我们尝试对这些问题做出确定的回答。</p><h2 id="简而言之"><a class="markdownIt-Anchor" href="#简而言之"></a> 简而言之</h2><p>大致按照列出的顺序,借助我们所建议的教材或者视频课程(但是最好二者兼用),学习如下的九门科目。目标是先花100到200个小时学习完每一个科目,然后在你职业生涯中,不时温习其中的精髓🚀。</p><table><thead><tr><th>科目</th><th>为何要学?</th><th>最佳书籍</th><th>最佳视频</th></tr></thead><tbody><tr><td><strong><a href="#%E7%BC%96%E7%A8%8B">编程</a></strong></td><td>不要做一个“永远没彻底搞懂”诸如递归等概念的程序员。</td><td><em><a href="https://book.douban.com/subject/1148282/" target="_blank" rel="noopener">《计算机程序的构造和解释》</a></em></td><td>Brian Harvey’s Berkeley CS 61A</td></tr><tr><td><strong><a href="#%E8%AE%A1%E7%AE%97%E6%9C%BA%E6%9E%B6%E6%9E%84">计算机架构</a></strong></td><td>如果你对于计算机如何工作没有具体的概念,那么你所做出的所有高级抽象都是空中楼阁。</td><td><em><a href="https://book.douban.com/subject/26912767/" target="_blank" rel="noopener">《深入理解计算机系统》</a></em></td><td>Berkeley CS 61C</td></tr><tr><td><strong><a href="#%E7%AE%97%E6%B3%95%E5%92%8C%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84">算法与数据结构</a></strong></td><td>如果你不懂得如何使用栈、队列、树、图等常见数据结构,遇到有难度的问题时,你将束手无策。</td><td><em><a href="https://book.douban.com/subject/4048566/" target="_blank" rel="noopener">《算法设计手册》</a></em></td><td>Steven Skiena’s lectures</td></tr><tr><td><strong><a href="#%E6%95%B0%E5%AD%A6%E7%9F%A5%E8%AF%86">数学知识</a></strong></td><td>计算机科学基本上是应用数学的一个“跑偏的”分支,因此学习数学将会给你带来竞争优势。</td><td><em><a href="https://book.douban.com/subject/33396340/" target="_blank" rel="noopener">《计算机科学中的数学》</a></em></td><td>Tom Leighton’s MIT 6.042J</td></tr><tr><td><strong><a href="#%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F">操作系统</a></strong></td><td>你所写的代码,基本上都由操作系统来运行,因此你应当了解其运作的原理。</td><td><em><a href="https://book.douban.com/subject/33463930/" target="_blank" rel="noopener">《操作系统导论》</a></em></td><td>Berkeley CS 162</td></tr><tr><td><strong><a href="#%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C">计算机网络</a></strong></td><td>互联网已然势不可挡:理解工作原理才能解锁全部潜力。</td><td><em><a href="https://book.douban.com/subject/30280001/" target="_blank" rel="noopener">《计算机网络:自顶向下方法》</a></em></td><td>Stanford CS 144</td></tr><tr><td><strong><a href="#%E6%95%B0%E6%8D%AE%E5%BA%93">数据库</a></strong></td><td>对于多数重要程序,数据是其核心,然而很少人理解数据库系统的工作原理。</td><td><em><a href="https://book.douban.com/subject/2256069/" target="_blank" rel="noopener">《Readings in Database Systems》</a> (暂无中译本)</em></td><td>Joe Hellerstein’s Berkeley CS 186</td></tr><tr><td><strong><a href="#%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%E4%B8%8E%E7%BC%96%E8%AF%91%E5%99%A8">编程语言与编译器</a></strong></td><td>若你懂得编程语言和编译器如何工作,你就能写出更好的代码,更轻松地学习新的编程语言。</td><td><em><a href="https://craftinginterpreters.com/" target="_blank" rel="noopener">《Crafting Interpreters》</a></em></td><td>Alex Aiken’s course on Lagunita</td></tr><tr><td><strong><a href="#%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F">分布式系统</a></strong></td><td>如今,<em>多数</em> 系统都是分布式的。</td><td><em><a href="https://book.douban.com/subject/30329536/" target="_blank" rel="noopener">《数据密集型应用系统设计》</a></em></td><td>MIT 6.824</td></tr></tbody></table><h2 id="还是太多"><a class="markdownIt-Anchor" href="#还是太多"></a> 还是太多?</h2><p>如果花几年时间自学 9 门科目让人望而却步,我们建议你只专注于两本书:<em>《深入理解计算机系统》</em> 和 <em>《数据密集型应用系统设计》</em>。根据我们的经验,投入到这两本书的时间可以获得极高的回报率,特别适合从事网络应用开发的自学工程师。这两本书也可以作为上面表格中其他科目的纲领。</p><h2 id="为什么要学习计算机科学"><a class="markdownIt-Anchor" href="#为什么要学习计算机科学"></a> 为什么要学习计算机科学?</h2><p>软件工程师分为两种:一种充分理解了计算机科学,从而有能力应对充满挑战的创造性工作;另一种仅仅凭着对一些高级工具的熟悉而勉强应付。</p><p>这两种人都自称软件工程师,都能在职业生涯早期挣到差不多的工资。然而,随着时间流逝,第一种工程师不断成长,所做的事情将会越来越有意义且更为高薪,不论是有价值的商业工作、突破性的开源项目、技术上的领导力或者高质量的个人贡献。</p><blockquote><p>全球短信系统每日收发约200亿条信息,而仅仅靠57名工程师,现在的 WhatsApp 每日收发420亿条。</p><p>— Benedict Evans (@BenedictEvans) <a href="https://twitter.com/BenedictEvans/status/694342874729545729" target="_blank" rel="noopener">2016年2月2日</a></p></blockquote><p>第一种工程师总是寻求深入学习计算机科学的方法,或是通过传统的方法学习,或是在职业生涯中永无止息地学习;第二种工程师<br />通常浮于表面,只学习某些特定的工具和技术,而不研究其底层的基本原理,仅仅在技术潮流的风向改变时学习新的技能。</p><p>如今,涌入计算机行业的人数激增,然而计算机专业的毕业生数量基本上未曾改变。第二种工程师的供过于求正在开始减少他们的工作机会,使他们无法涉足行业内更加有意义的工作。对你而言,不论正在努力成为第一种工程师,还是只想让自己的职业生涯更加安全,学习计算机科学是唯一可靠的途径。</p><blockquote><p>23333 然而他们… <a href="https://t.co/XVNYlXAHar" target="_blank" rel="noopener">pic.twitter.com/XVNYlXAHar</a></p><p>— Jenna Bilotta (@jenna) <a href="https://twitter.com/jenna/status/838161631662092289" target="_blank" rel="noopener">2017年3月4日</a></p></blockquote><h2 id="分科目指引"><a class="markdownIt-Anchor" href="#分科目指引"></a> 分科目指引</h2><h3 id="编程"><a class="markdownIt-Anchor" href="#编程"></a> 编程</h3><p>大多数计算机专业本科教学以程序设计“导论”作为开始。这类课程的最佳版本不仅能满足初学者的需要,还适用于那些在初学编程阶段遗漏了某些有益的概念和程序设计模式的人。</p><p>对于这部分内容,我们的标准推荐是这部经典著作:<a href="https://book.douban.com/subject/1148282/" target="_blank" rel="noopener">《计算机程序的构造和解释》</a>。在网络上,这本书既可供<a href="https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book.html" target="_blank" rel="noopener">免费阅读(英文版)</a>,也作为<a href="http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-001-structure-and-interpretation-of-computer-programs-spring-2005/video-lectures/" target="_blank" rel="noopener">MIT的免费视频课程</a>。不过尽管这些视频课程很不错,我们对于视频课程的推荐实际上是<a href="https://archive.org/details/ucberkeley-webcast-PL3E89002AA9B9879E?sort=titleSorter" target="_blank" rel="noopener">Brian Harvey 开设的 SICP 课程</a>(即 Berkeley 的 61A 课程)。比起MIT的课程,它更加完善,更适用于初学者。</p><p>我们建议至少学完SICP的前三章,并完成配套的习题。如果需要额外的练习,可以去解决一些小的程序设计问题,比如<a href="http://exercism.io" target="_blank" rel="noopener">exercism</a>。</p><blockquote><p><strong>中文翻译新增:</strong></p><ul><li>关于SICP国内视频观看地址<ul><li><a href="https://www.bilibili.com/video/av8515129/" target="_blank" rel="noopener">MIT的免费视频课程(中英字幕)</a></li><li><a href="https://www.bilibili.com/video/av40460492/" target="_blank" rel="noopener">Brian Harvey 开设的 SICP 课程(英文字幕)</a></li></ul></li><li>Scheme 学习的相关资源参见:<a href="https://github.com/DeathKing/Learning-SICP" target="_blank" rel="noopener">https://github.com/DeathKing/Learning-SICP</a></li><li>更详细的补充说明:<a href="https://github.com/keithnull/TeachYourselfCS-CN/issues/3" target="_blank" rel="noopener">#3</a></li></ul></blockquote><p>自从 2016 年首次发布这份指南以来,最常被问到的一个问题是,我们是否推荐由 John DeNero 讲授的更新的 CS 61A 课程,以及配套的书籍 <em><a href="https://composingprograms.com/" target="_blank" rel="noopener">《Composing Programs》</a></em>,这本书“继承自 SICP” 但使用 Python 讲解。我们认为 DeNero 的课程也很不错,有的学生可能更喜欢,但我们还是建议把 SICP, Scheme 和 Brian Harvey 的视频课程作为首选。</p><p>为什么这么说呢?因为 SICP 是独一无二的,它可以——至少很有可能——改变你对计算机和编程的基本认识。不是每个人都有这样的体验。有的人讨厌这本书,有的人看了前几页就放弃了。但潜在的回报让它值得一读。</p><p>如果你觉得SICP过于难,试试 <em>《Composing Programs》</em>。如果还是不合适,那我们推荐 <em>《程序设计方法》(<a href="https://book.douban.com/subject/1140942/" target="_blank" rel="noopener">中文版</a>,<a href="http://www.htdp.org/" target="_blank" rel="noopener">英文版</a>)</em> ;如果你觉得SICP过于简单,那我们推荐 <em><a href="https://book.douban.com/subject/1782316/" target="_blank" rel="noopener">《Concepts, Techniques, and Models of Computer Programming》</a></em> 。如果读这些书让你觉得没有收获,也行你应该先学习其他科目,一两年后再重新审视编程的理念。</p><blockquote><p>新版原文删除了对 <em>《Concepts, Techniques, and Models of Computer Programming》</em> 一书的推荐,但这本书对各种编程模型有深入的见解,值得一读。所以译文中依然保留。<br />— 译者注</p></blockquote><p>最后,有一点要说明的是:本指南<strong>不适用</strong>于完全不懂编程的新手。我们假定你是一个没有计算机专业背景的程序员,希望填补一些知识空白。事实上,我们把“编程”章节包括进来只是提醒你还有更多知识需要学习。对于那些从来没有学过编程,但又想学的人来说,这份<a href="https://www.reddit.com/r/learnprogramming/wiki/faq#wiki_getting_started" target="_blank" rel="noopener">指南</a>更合适。</p><p><a href="https://book.douban.com/subject/1148282/" target="_blank" rel="noopener"><img src="https://user-images.githubusercontent.com/20233656/66758473-ef7bff80-eed0-11e9-944a-15ae5c8542ca.jpg" alt="计算机程序的构造和解释" /></a></p><h3 id="计算机架构"><a class="markdownIt-Anchor" href="#计算机架构"></a> 计算机架构</h3><p>计算机架构——有时候又被称为“计算机系统”或者“计算机组成”——是了解软件底层的的重要视角。根据我们的经验,这是自学的软件工程师最容易忽视的领域。</p><p>我们最喜欢的入门书是 <em><a href="https://book.douban.com/subject/26912767/" target="_blank" rel="noopener">《深入理解计算机系统》</a></em>。典型的<a href="http://csapp.cs.cmu.edu/3e/courses.html" target="_blank" rel="noopener">计算机体系结构导论课程</a>会涵盖本书的 1 - 6 章。</p><p>我们喜爱《深入理解计算机系统》,因为它的实用性,并且站在程序员的视角。虽然计算机体系结构的内容比本书所涉及的内容多得多,但对于那些想了解计算机系统以求编写更快、更高效、更可靠的软件的人来说,这本书是很好的起点。</p><p>对于那些既想了解这个主题又想兼顾硬件和软件的知识的人来说,我们推荐 <em><a href="https://book.douban.com/subject/1998341/" target="_blank" rel="noopener">《计算机系统要素》</a></em>,又名“从与非门到俄罗斯方块”(“Nand2Tetris”),这本书规模宏大,让读者对计算机内的所有部分如何协同工作有完全的认识。这本书的每一章节对应如何构建计算机整体系统中的一小部分,从用HDL(硬件描述语言)写基本的逻辑门电路出发,途经CPU和汇编,最终抵达诸如俄罗斯方块这般规模的应用程序。</p><p>我们推荐把此书的前六章读完,并完成对应的项目练习。这么做,你将更加深入地理解,计算机架构和运行其上的软件之间的关系。</p><p>这本书的前半部分(包括所有对应的项目)均可从<a href="http://www.nand2tetris.org" target="_blank" rel="noopener">Nand2Tetris的网站上</a>免费获得。同时,在Coursera上,这是一门<a href="https://www.coursera.org/learn/build-a-computer" target="_blank" rel="noopener">视频课程</a>。</p><p>为了追求简洁和紧凑,这本书牺牲了内容上的深度。尤其值得注意的是,流水线和存储层次结构是现代计算机架构中极其重要的两个概念,然而这本书对此几乎毫无涉及。</p><p>当你掌握了Nand2Tetris的内容后,我们推荐要么回到《深入理解计算机系统》,或者考虑Patterson和Hennessy二人所著的 <em><a href="https://book.douban.com/subject/26604008/" target="_blank" rel="noopener">《计算机组成与设计》</a></em>,一本优秀的经典著作。这本书中的不同章节重要程度不一,因此我们建议根据Berkeley的<a href="http://inst.eecs.berkeley.edu/~cs61c/sp15/" target="_blank" rel="noopener">CS61C课程</a> “计算机架构中的伟大思想”来着重阅读一些章节。这门课的笔记和实验在网络上可以免费获得,并且在<a href="https://archive.org/details/ucberkeley-webcast-PL-XXv-cvA_iCl2-D-FS5mk0jFF6cYSJs_" target="_blank" rel="noopener">互联网档案</a>中有这门课程的过往资料。</p><p><a href="https://book.douban.com/subject/26912767/" target="_blank" rel="noopener"><img src="https://user-images.githubusercontent.com/20510068/82109944-12270d00-976d-11ea-85a9-774e4f762ec9.png" alt="深入理解计算机系统" /></a> <a href="http://www.nand2tetris.org" target="_blank" rel="noopener"><img src="https://user-images.githubusercontent.com/20233656/66758695-60231c00-eed1-11e9-8422-a4acfb10a390.jpg" alt="计算机系统要素" /></a></p><blockquote><p>硬件是平台。</p><p>— Mike Acton, Engine Director at Insomniac Games<br />(<a href="https://www.youtube.com/watch?v=rX0ItVEVjHc" target="_blank" rel="noopener">观看他在CppCon上的演说</a>)</p></blockquote><h3 id="算法与数据结构"><a class="markdownIt-Anchor" href="#算法与数据结构"></a> 算法与数据结构</h3><p>正如几十年来的共识,我们认为,计算机科学教育所赋予人们的最大能量在于对常见算法和数据结构的熟悉。此外,这也可以训练一个人对于各种问题的解决能力,有助于其他领域的学习。</p><p>关于算法与数据结构,有成百上千的书可供使用,但是我们的最爱是Steven Skiena编写的 <em><a href="https://book.douban.com/subject/4048566/" target="_blank" rel="noopener">《算法设计手册》</a></em>。显而易见,他对此充满热爱,迫不及待地想要帮助其他人理解。在我们看来,这本书给人一种焕然一新的体验,完全不同于那些更加经常被推荐的书(比如Cormen,Leiserson,Rivest 和 Stein,或者 Sedgewick的书,后两者充斥着过多的证明,不适合以 <em>解决问题</em> 为导向的学习)。</p><p>如果你更喜欢视频课程,<a href="https://www.youtube.com/watch?v=A2bFN3MyNDA&list=PLOtl7M3yp-DX32N0fVIyvn7ipWKNGmwpp" target="_blank" rel="noopener">Skiena慷慨地提供了他的课程</a>。此外,Tim Roughgarden的课程也很不错,<br />在Stanford的MOOC平台Lagunita,或者<a href="https://www.coursera.org/specializations/algorithms" target="_blank" rel="noopener">Coursera</a>上均可获得。Skiena和Roughgarden的这两门课程没有优劣之分,选择何者取决于个人品味。</p><p>至于练习,我们推荐学生在<a href="https://leetcode.com" target="_blank" rel="noopener">Leetcode</a>上解决问题。Leetcode上的问题往往有趣且带有良好的解法和讨论。此外,在竞争日益激烈的软件行业,这些问题可以帮助你评估自己应对技术面试中常见问题的能力。我们建议解决大约100道随机挑选的Leetcode问题,作为学习的一部分。</p><p>最后,我们强烈推荐 <em><a href="https://book.douban.com/subject/2124114/" target="_blank" rel="noopener">《怎样解题》</a></em>。这本书极为优秀且独特,指导人们解决广义上的问题,因而一如其适用于数学,它适用于计算机科学。</p><p><a href="https://book.douban.com/subject/4048566/" target="_blank" rel="noopener"><img src="https://user-images.githubusercontent.com/20233656/66759121-361e2980-eed2-11e9-913c-8fc48c67122a.jpg" alt="算法设计手册" /></a> <a href="https://book.douban.com/subject/2124114/" target="_blank" rel="noopener"><img src="https://user-images.githubusercontent.com/20233656/66759282-8e552b80-eed2-11e9-89de-16b1f8d82e78.jpg" alt="怎样解题" /></a></p><blockquote><p>我可以广泛推荐的方法只有一个: 写之前先思考。</p><p>— Richard Hamming</p></blockquote><h3 id="数学知识"><a class="markdownIt-Anchor" href="#数学知识"></a> 数学知识</h3><p>从某个角度说,计算机科学是应用数学的一个“发育过度”的分支。尽管许多软件工程师试图——并且在不同程度上成功做到——忽视这一点,我们鼓励你用学习来拥抱数学。如若成功,比起那些没有掌握数学的人,你将获得巨大的竞争优势。</p><p>对于计算机科学,数学中最相关的领域是“离散数学”,其中的“离散”与“连续”相对立,大致上指的是应用数学中那些有趣的主题,而不是微积分之类的。由于定义比较含糊,试图掌握离散数学的全部内容是没有意义的。较为现实的学习目标是,了解逻辑、排列组合、概率论、集合论、图论以及密码学相关的一些数论知识。考虑到线性代数在计算机图形学和机器学习中的重要性,该领域同样值得学习。</p><p>学习离散数学,我们建议从<a href="http://www.cs.elte.hu/~lovasz/dmbook.ps" target="_blank" rel="noopener">László Lovász的课程笔记</a>开始。Lovász教授成功地让这些内容浅显易懂且符合直觉,因此,比起正式的教材,这更适合初学者。</p><p>对于更加高阶的学习,我们推荐 <em><a href="https://book.douban.com/subject/33396340/" target="_blank" rel="noopener">《计算机科学中的数学》</a></em>,MIT同名课程的课程笔记,篇幅与书籍相当(事实上,现已出版)。这门课程的视频同样<a href="https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-042j-mathematics-for-computer-science-fall-2010/video-lectures/" target="_blank" rel="noopener">可免费获得</a>,是我们所推荐的学习视频。</p><p>对于线性代数,我们建议从<a href="https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab" target="_blank" rel="noopener">Essence of linear algebra</a>系列视频开始,然后再去学习Gilbert Strang的<a href="https://book.douban.com/subject/34820335/" target="_blank" rel="noopener">《线性代数导论》</a>和<a href="https://ocw.mit.edu/courses/mathematics/18-06-linear-algebra-spring-2010/video-lectures/" target="_blank" rel="noopener">视频课程</a>。</p><p><a href="https://book.douban.com/subject/33396340/" target="_blank" rel="noopener"><img src="https://user-images.githubusercontent.com/20233656/66759673-4387e380-eed3-11e9-8469-3e677d108e91.jpg" alt="计算机科学中的数学" /></a></p><blockquote><p>如果人们不相信数学是简单的,那么只能是因为他们没有意识到生活有多么复杂。</p><p>— John von Neumann</p></blockquote><h3 id="操作系统"><a class="markdownIt-Anchor" href="#操作系统"></a> 操作系统</h3><p><em><a href="https://book.douban.com/subject/30297919/" target="_blank" rel="noopener">《操作系统概念》</a></em> (“恐龙书”)和 <em><a href="https://book.douban.com/subject/27096665/" target="_blank" rel="noopener">《现代操作系统》</a></em> 是操作系统领域的经典书籍。二者都因为写作风格和对学生不友好而招致了一些批评。</p><p><em><a href="https://book.douban.com/subject/33463930/" target="_blank" rel="noopener">《操作系统导论》(Operating Systems: Three Easy Pieces)</a></em> 是一个不错的替代品,并且<a href="http://pages.cs.wisc.edu/~remzi/OSTEP/" target="_blank" rel="noopener">可在网上免费获得(英文版)</a>。我们格外喜欢这本书的结构,并且认为这本书的习题很值得一做。</p><p>在读完《操作系统导论》后,我们鼓励你探索特定操作系统的设计。可以借助“{OS name} Internals”风格的书籍,比如 <em><a href="https://www.amazon.com/Lions-Commentary-Unix-John/dp/1573980137/" target="_blank" rel="noopener">Lion’s commentary on Unix</a></em>, <em><a href="https://www.amazon.com/Design-Implementation-FreeBSD-Operating-System/dp/0321968972/" target="_blank" rel="noopener">The Design and Implementation of the FreeBSD Operating System</a></em>,以及 <em><a href="https://www.amazon.com/Mac-OS-Internals-Systems-Approach/dp/0321278542/" target="_blank" rel="noopener">Mac OS X Internals</a></em>。对于 Linux ,我们推荐 Robert Love 的 <em><a href="https://book.douban.com/subject/6097773/" target="_blank" rel="noopener">《Linux内核设计与实现》</a></em>。</p><p>为了巩固对操作系统的理解,阅读小型系统内核的代码并且为其增加特性是一个很不错的方法。比如,<a href="https://pdos.csail.mit.edu/6.828/2016/xv6.html" target="_blank" rel="noopener">xv6</a>,由MIT的一门课程所维护的从Unix V6到ANSI C和x86的移植,就是一个很棒的选择。《操作系统导论》有一个附录,记载了一些可能的<a href="http://pages.cs.wisc.edu/~remzi/OSTEP/lab-projects-xv6.pdf" target="_blank" rel="noopener">xv6实验项目</a>,其中充满了关于潜在项目的很棒想法。</p><p><a href="https://book.douban.com/subject/33463930/" target="_blank" rel="noopener"><img src="https://user-images.githubusercontent.com/20233656/66759780-78943600-eed3-11e9-8eb5-6472c318c265.jpg" alt="操作系统导论" /></a></p><h3 id="计算机网络"><a class="markdownIt-Anchor" href="#计算机网络"></a> 计算机网络</h3><p>鉴于有那么多关于网络服务端和客户端的软件工程,计算机网络是计算机科学中价值最为“立竿见影”的领域之一。我们的学生,系统性地学习了计算机网络,最终能够理解那些曾困扰他们多年的术语、概念和协议。</p><p>在这一主题上,我们最爱的书籍是 <em><a href="https://book.douban.com/subject/30280001/" target="_blank" rel="noopener">《计算机网络:自顶向下方法》</a></em>。书中的小项目和习题相当值得练习,尤其是其中的“Wireshark labs”(<a href="http://www-net.cs.umass.edu/wireshark-labs/" target="_blank" rel="noopener">这部分在网上可以获得</a>)。</p><p>如果更喜欢视频课程,我们推荐Stanford的<a href="https://lagunita.stanford.edu/courses/Engineering/Networking-SP/SelfPaced/about" target="_blank" rel="noopener"><em>Introduction to Computer Networking</em></a>,可在他们的MOOC平台Lagunita上免费观看。</p><p>对于计算机网络的学习,做项目比完成小的习题更有益。一些可能的项目有:HTTP服务器,基于UDP的聊天APP,<a href="http://jvns.ca/blog/2014/08/12/what-happens-if-you-write-a-tcp-stack-in-python/" target="_blank" rel="noopener">迷你TCP栈</a>,代理,负载均衡器,或者分布式哈希表。</p><p><a href="https://book.douban.com/subject/30280001/" target="_blank" rel="noopener"><img src="https://user-images.githubusercontent.com/20233656/66760004-d9bc0980-eed3-11e9-9b3f-74bf54b9571f.jpg" alt="《计算机网络:自顶向下方法》" /></a></p><blockquote><p>你无法盯着水晶球预见未来,未来的互联网何去何从取决于社会。</p><p>— Bob Kahn</p></blockquote><h3 id="数据库"><a class="markdownIt-Anchor" href="#数据库"></a> 数据库</h3><p>比起其他主题,自学数据库系统需要更多的付出。这是一个相对年轻的研究领域,并且出于很强的商业动机,研究者把想法藏在紧闭的门后。此外,许多原本有潜力写出优秀教材的作者反而选择了加入或创立公司。</p><p>鉴于如上情况,我们鼓励自学者大体上抛弃教材,而是从<a href="https://archive.org/details/UCBerkeley_Course_Computer_Science_186" target="_blank" rel="noopener">2015年春季学期的CS 186课程</a>(Joe Hellerstein在Berkeley的数据库课程)开始,然后前往阅读论文。</p><p>对于初学者,有一篇格外值得提及的论文:“<a href="http://db.cs.berkeley.edu/papers/fntdb07-architecture.pdf" target="_blank" rel="noopener">Architecture of a Database System</a>”。这篇论文提供了独特的对关系型数据库管理系统(RDBMS)如何工作的高层次观点,是后续学习的实用梗概。</p><p><em><a href="https://book.douban.com/subject/2256069/" target="_blank" rel="noopener">《Readings in Database Systems》</a></em>,或者以<a href="http://www.redbook.io/" target="_blank" rel="noopener">数据库“红书”</a>更为人知,是由Peter Bailis,Joe Hellerstein和Michael Stonebraker编纂的论文合集。对于那些想要在CS 186课程的水平更进一步的学习者,“红书”应当是下一步。</p><p>如果你坚持一定要一本导论教材,那我们推荐Ramakrishnan和Gehrke所著的 <em><a href="https://book.douban.com/subject/1155934/" target="_blank" rel="noopener">《数据库管理系统:原理与设计》</a></em>。如需更深一步,Jim Gray的经典著作 <em><a href="https://book.douban.com/subject/2586390/" target="_blank" rel="noopener">《Transaction Processing: Concepts and Techniques》</a></em> 值得一读,不过我们不建议把这本书当作首要资源。</p><p>如果没有编写足够数量的代码,很难巩固数据库理论。CS 186课程的学生给Spark添加特性,倒是不错的项目,不过我们仅仅建议从零实现一个简单的关系型数据库管理系统。自然,它将不会有太多的特性,但是即便只实现典型的关系型数据库管理系统每个方面最基础的功能,也是相当有启发的。</p><p>最后,数据模型往往是数据库中一个被忽视的、教学不充分的方面。关于这个主题,我们推荐的书籍是 <em><a href="https://book.douban.com/subject/17915870/" target="_blank" rel="noopener">Data and Reality: A Timeless Perspective on Perceiving and Managing Information in Our Imprecise World</a></em>。</p><p><a href="https://book.douban.com/subject/2256069/" target="_blank" rel="noopener"><img src="https://user-images.githubusercontent.com/20233656/66760126-08d27b00-eed4-11e9-82c6-46c571036aa1.jpg" alt="Readings in Database Systems" /></a> <a href="https://book.douban.com/subject/1155934/" target="_blank" rel="noopener"><img src="https://user-images.githubusercontent.com/20233656/66760358-85655980-eed4-11e9-9130-66d2ecea5700.jpg" alt="数据库管理系统:原理与设计" /></a></p><h3 id="编程语言与编译器"><a class="markdownIt-Anchor" href="#编程语言与编译器"></a> 编程语言与编译器</h3><p>多数程序员学习编程语言的知识,而多数计算机科学家学习编程语言 <em>相关</em> 的知识。这使得计算机科学家比起程序员拥有显著的优势,即便在编程领域!因为他们的知识可以推而广之:相较只学习过特定编程语言的人,他们可以更深入更快速地理解新的编程语言。</p><p>我们推荐的入门书是 Bob Nystrom 所著的优秀的 <em><a href="https://craftinginterpreters.com/contents.html" target="_blank" rel="noopener">Crafting Interpreters</a></em>,可在网上免费获取。这本书条理清晰,富有趣味性,非常适合那些想要更好地理解语言和语言工具的人。我们建议你花时间读完整本书,并尝试任何一个感兴趣的“挑战”。</p><p>另一本更为传统的推荐书籍是 <em><a href="https://book.douban.com/subject/3296317/" target="_blank" rel="noopener">《编译原理》</a></em>,通常称为“龙书”。不幸的是,这本书不是为自学者而设计的,而是供教师从中挑选一些主题用于1-2学期的教学。</p><p>如果你选择使用龙书进行自学,你需要从中甄选主题,而且最好是在导师的帮助下。我们建议依据某个视频课程来设定学习的结构,然后按需从龙书中获取深入的内容。我们推荐的在线课程是<a href="https://www.edx.org/course/compilers" target="_blank" rel="noopener">Alex Aiken在MOOC平台 edX 所开设的</a>。</p><p><a href="https://book.douban.com/subject/3296317/" target="_blank" rel="noopener"><img src="https://user-images.githubusercontent.com/20233656/66760486-ca898b80-eed4-11e9-80ba-df298ac8d5da.jpg" alt="编译原理" /></a></p><blockquote><p>不要做一个只写样板代码的程序员。相反,给用户和其他程序员创造工具。从纺织工业和钢铁工业中学习历史教训:你想制造机器和工具,还是操作这些机器?</p><p>— Ras Bodik 在他的编译器课程伊始</p></blockquote><h3 id="分布式系统"><a class="markdownIt-Anchor" href="#分布式系统"></a> 分布式系统</h3><p>随着计算机在数量上的增加,计算机同样开始 <em>分散</em>。尽管商业公司过去愿意购买越来越大的大型机,现在的典型情况是,甚至很小的应用程序都同时在多台机器上运行。思考这样做的利弊权衡,即是分布式系统的研究所在,也是越来越重要的一项技能。</p><p>我们推荐的自学参考书是 Martin Kleppmann 的 <em><a href="https://book.douban.com/subject/30329536/" target="_blank" rel="noopener">《数据密集型应用系统设计》</a></em>。与传统的教科书相比,它是一本为实践者设计的具有很高的可读性的书,并且保持了深度和严谨性。</p><p>对于那些偏爱传统教材,或者希望可以从网上免费获取的人,我们推荐的教材是Maarten van Steen和Andrew Tanenbaum所著的 <em>《分布式系统原理与范型》(<a href="https://book.douban.com/subject/3108801/" target="_blank" rel="noopener">中文第二版</a>,<a href="https://book.douban.com/subject/26979326/" target="_blank" rel="noopener">英文第三版</a>)</em>。</p><p>对于喜欢视频课程的人,<a href="https://www.youtube.com/watch?v=cQP8WApzIQQ&list=PLrw6a1wE39_tb2fErI4-WkMbsvGQk9_UB" target="_blank" rel="noopener">MIT的6.824</a> 是一门很好的在线视频课程,由 Robert Morris 教授的研究生课程,在<a href="https://pdos.csail.mit.edu/6.824/schedule.html" target="_blank" rel="noopener">这里</a>可以看到课程安排。</p><p>不管选择怎样的教材或者其他辅助资料,学习分布式系统必然要求阅读论文。<a href="http://dsrg.pdos.csail.mit.edu/papers/" target="_blank" rel="noopener">这里</a>有一个不错的论文清单,而且我们强烈建议你出席你当地的<a href="http://paperswelove.org/" target="_blank" rel="noopener">Papers We Love</a>(仅限美国)。</p><p><a href="https://book.douban.com/subject/30329536/" target="_blank" rel="noopener"><img src="https://user-images.githubusercontent.com/20510068/82111034-94ff9600-9774-11ea-9d49-90b00f746659.png" alt="数据密集型应用系统设计" /></a></p><h2 id="常见问题解答"><a class="markdownIt-Anchor" href="#常见问题解答"></a> 常见问题解答</h2><h3 id="这份指引的目标受众是"><a class="markdownIt-Anchor" href="#这份指引的目标受众是"></a> 这份指引的目标受众是?</h3><p>我们面向自学的软件工程师、培训班学生、“早熟的”高中生或者想要通过自学补充正式教育的大学生。关于何时开启这段自学旅程,完全取决于个人,不过多数人在有一定的职业经历后深入学习计算机科学理论会获益匪浅。比如,我们注意到,如果学生在工作中曾经使用过数据库,他们会 <em>喜爱</em> 学习数据库系统课程;如果学生从事过一两个Web项目,他们会 <em>喜爱</em> 学习计算机网络。</p><h3 id="人工智能计算机图形学xx主题怎么样"><a class="markdownIt-Anchor" href="#人工智能计算机图形学xx主题怎么样"></a> 人工智能/计算机图形学/XX主题怎么样?</h3><p>我们试图把计算机科学主题清单限制到那些我们认为 <em>每一个软件工程师</em> 都应该了解的内容,不限于专业或行业。拥有了这些基础,你将能更加轻松地挑选教材或论文,然而无需指引地学习核心概念。在这里,我们给出一些其他常见主题的自学起点:</p><ul><li>人工智能:通过观看视频并完成Pacman项目来学习<a href="http://ai.berkeley.edu/" target="_blank" rel="noopener">Berkeley的AI课程</a>。至于教材,使用Russell和Norvig编写的 <em><a href="https://book.douban.com/subject/25796281/" target="_blank" rel="noopener">《人工智能:一种现代方法》</a></em>。</li><li>机器学习:学习吴恩达在Coursera上的课程。耐心学习,先确保理解了基础概念再奔向类如深度学习的诱人新主题。</li><li>计算机图形学:学习<a href="http://inst.eecs.berkeley.edu/~cs184/fa12/onlinelectures.html" target="_blank" rel="noopener">Berkeley CS 184课程</a>的材料,使用<a href="https://book.douban.com/subject/30402778/" target="_blank" rel="noopener">《计算机图形学:原理及实践》</a>作为教材。</li></ul><h3 id="一定要严格遵守推荐的学习次序吗"><a class="markdownIt-Anchor" href="#一定要严格遵守推荐的学习次序吗"></a> 一定要严格遵守推荐的学习次序吗?</h3><p>事实上,所有主题之间都有一定程度的重叠,彼此循环引用。以离散数学和算法的关系为例:先学习数学可以帮助你更深入地分析和理解算法,然而先学习算法可以为学习离散数学提供更大的动力和应用背景。理想情况下,你将在你的职业生涯多次重温二者。</p><p>因此,我们所推荐的次序主要是为了帮助你 <em>起步</em>……如果你出于某种强烈的原因而倾向以不同的顺序学习,那也没有关系,勇敢开始吧!不过在我们看来,最重要的“先决条件”是:先学计算机架构再学操作系统或数据库,先学计算机网络和操作系统再学分布式系统。</p><h3 id="和open-source-society-freecodecamp-curricula等比起来这份指引"><a class="markdownIt-Anchor" href="#和open-source-society-freecodecamp-curricula等比起来这份指引"></a> 和Open Source Society、freeCodeCamp curricula等比起来,这份指引?</h3><p><a href="https://github.com/open-source-society/computer-science" target="_blank" rel="noopener">OSS指引</a>涵盖太多主题,在许多主题中推荐劣质资源,没有就特定课程哪些方面有价值提供原因或指引。我们努力对这份指引中的课程加以限制,仅仅包括那些你作为软件工程师 <em>确实需要了解的</em>,不论你的专业方向,并且对每门课程为何必要做出了解释以帮助你理解。</p><p>FreeCodeCamp主要关注编程,而不是计算机科学。至于你为什么要学习计算机科学,参见<a href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E5%AD%A6%E4%B9%A0%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6">上文</a>。如果你是个新手,我们建议先学 freeCodeCamp 的课程,一两年后再回归本指南。</p><h3 id="xx编程语言怎么样"><a class="markdownIt-Anchor" href="#xx编程语言怎么样"></a> XX编程语言怎么样?</h3><p>学习一门特定的编程语言和学习计算机科学的一个领域完全不在一个维度——相比之下,学习语言 <em>容易</em> 且 <em>缺乏价值</em>。如果你已经了解了一些语言,我们强烈建议遵照我们的指引,然后在学习的空当中习得语言,或者暂且不管以后再说。如果你已经把编程学得不错了(比如学完了 <em>《计算机程序的构造和解释》</em>),尤其是如果你学习过编译器,那么面对一门新的语言,你只需要花一个周末稍多的时间即可基本掌握,之后你可以在工作中学习相关的类库/工具/生态。</p><h3 id="xx流行技术怎么样"><a class="markdownIt-Anchor" href="#xx流行技术怎么样"></a> XX流行技术怎么样?</h3><p>没有任何一种技术的重要程度可以达到学习其使用足以成为计算机科学教学的核心部分。不过,你对学习那门技术充满热情,这很不错。诀窍是先从特定的技术回退到基本的领域或概念,判断这门流行技术在技术的宏观大局中位于何处,然后才深入学习这门技术。</p><h3 id="为什么你们还在推荐sicp"><a class="markdownIt-Anchor" href="#为什么你们还在推荐sicp"></a> 为什么你们还在推荐SICP?</h3><p>先尝试读一下,有些人觉得 SICP 让人神魂颠倒,这在其他书很少见。如果你不喜欢,你可以尝试其他的东西,也许以后再回到 SICP。</p><h3 id="为什么你们还在推荐龙书"><a class="markdownIt-Anchor" href="#为什么你们还在推荐龙书"></a> 为什么你们还在推荐龙书?</h3><p>龙书依旧是内容最为完整的编译器单本书籍。由于过分强调一些如今不够时新的主题的细节,比如解析,这本书招致了恶评。然而事实上,这本书从未打算供人一页一页的学习,而仅仅是为了给教师准备一门课程提供足够的材料。类似地,自学者可以从书中量身按需挑选主题,或者最好依照公开课授课教师在课程大纲中的建议。</p><h3 id="如何便宜获取教材"><a class="markdownIt-Anchor" href="#如何便宜获取教材"></a> 如何便宜获取教材?</h3><p>我们所建议的许多教材在网上都可以免费获得,这多亏了作者们的慷慨。对于那些不免费的书籍,我们建议购买旧版本的二手书籍。广而言之,如果一本教材有多个版本,旧版本大概率是完全足够使用的。即便新版本的价格是旧版本的10倍,新版本也绝不可能比旧版本好10倍!</p><p><strong>中文翻译新增:</strong> 事实上,比起美国,在国内购买技术书籍可以说是相当“廉价”了。如果仍旧寻求更加便宜的购买渠道,可以参考这篇V2EX上的<a href="https://www.v2ex.com/t/578615" target="_blank" rel="noopener">讨论帖子</a>,其中提到了一些不错的购买渠道。</p><h3 id="这份指引是谁写的"><a class="markdownIt-Anchor" href="#这份指引是谁写的"></a> 这份指引是谁写的?</h3><p>这份指引由<a href="https://bradfieldcs.com" target="_blank" rel="noopener">Bradfield School of Computer Science</a>(旧金山)的两位教员:<a href="https://twitter.com/oznova_" target="_blank" rel="noopener">Ozan Onay</a>和<a href="https://twitter.com/quackingduck" target="_blank" rel="noopener">Myles Byrne</a>编写,并由 Oz 于 2020 年更新。这份指引基于我们对数千名自学成才的工程师和培训班学生教授计算机科学基础的经验。感谢我们所有学生对自学资源的持续反馈。</p><p>只要有足够的时间和动力,我们非常有信心,你可以自学完以上所有课程。如果你喜欢一个集中式、结构化、由教师指导的课程,你可能对我们的<a href="https://bradfieldcs.com/csi/" target="_blank" rel="noopener">计算机科学强化班</a>感兴趣。我们<a href="https://ozwrites.com/masters/" target="_blank" rel="noopener">不建议</a>你去攻读硕士学位。</p><h3 id="这份指引是谁翻译的"><a class="markdownIt-Anchor" href="#这份指引是谁翻译的"></a> 这份指引是谁翻译的?</h3><p>这份指引的中文翻译是<a href="https://github.com/keithnull/TeachYourselfCS-CN/" target="_blank" rel="noopener">社区共同贡献的成果</a>,我们欢迎任何反馈和改进!</p><iframe id="blockbyte-bs-sidebar" class="notranslate noAnimations" style="opacity: 0; pointer-events: none; position: fixed; top: 0px; left: 0px; width: 350px; max-width: none; height: 0px; z-index: 2147483646; border: none; transform: translate3d(-350px, 0px, 0px); transition: none 0s ease 0s !important; background-color: rgba(0, 0, 0, 0.6) !important; display: block !important; color: rgb(0, 0, 0); font-family: Arial; font-size: medium; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"></iframe>]]></content>
<summary type="html">
<h1 id="自学计算机科学"><a class="markdownIt-Anchor" href="#自学计算机科学"></a> 自学计算机科学</h1>
<blockquote>
<p>本文档是对<a href="https://teachyourselfcs.com" t
</summary>
<category term="methodology" scheme="https://yuechuanx.top/tags/methodology/"/>
</entry>
<entry>
<title>使用Graphviz创建图表</title>
<link href="https://yuechuanx.top/create-diagrams-with-code-using-graphviz/"/>
<id>https://yuechuanx.top/create-diagrams-with-code-using-graphviz/</id>
<published>2020-06-15T06:53:31.000Z</published>
<updated>2020-06-15T06:53:31.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>转载自:<a href="https://ncona.com/2020/06/create-diagrams-with-code-using-graphviz/" target="_blank" rel="noopener">https://ncona.com/2020/06/create-diagrams-with-code-using-graphviz/</a></p></blockquote><p>您是否曾为绘制过架构图时重复的单击和拖动而感到乏味?</p><p>您是否需要对该图进行修改发现改动却很复杂?</p><p><a href="https://www.graphviz.org/" target="_blank" rel="noopener">Graphviz</a> 是一个开源的图形可视化软件,它使我们能够使用代码描述图表,并为我们自动绘制。如果将来需要修改该图,我们只需要修改描述代码,节点和边将自动为我们重新定位。</p><h2 id="绘制图形"><a class="markdownIt-Anchor" href="#绘制图形"></a> 绘制图形</h2><p>在开始编写图形之前,我们需要学习如何将代码转换为图像,以便可以测试正在做的事情。</p><p><a href="http://www.webgraphviz.com/" target="_blank" rel="noopener">Webgraphviz.com</a> 可用于从浏览器绘制图形。</p><p>我们可以使用 apt 在 Ubuntu 中安装命令行工具:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1 sudo apt install graphviz</span><br></pre></td></tr></table></figure><p>在 macOS 环境 使用 brew 安装</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install graphviz</span><br></pre></td></tr></table></figure><p>除其他外,这将安装 <code>dot</code> CLI,该CLI可用于从文本文件生成图像:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1 dot -Tpng input.gv -o output.png</span><br></pre></td></tr></table></figure><p>在上面的示例中,我们将 png 指定为output(<code>-Tpng</code>),但是有许多<a href="https://www.graphviz.org/doc/info/output.html" target="_blank" rel="noopener">可用的选项</a>。如我们所见,输入文件通常使用<code>gv</code>扩展名。</p><h2 id="dot"><a class="markdownIt-Anchor" href="#dot"></a> DOT</h2><p>DOT是用于描述要由Graphviz解析的图形的最常见格式。</p><h3 id="基本"><a class="markdownIt-Anchor" href="#基本"></a> 基本</h3><p>一个简单的图形具有以下形式:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">graph MyGraph { </span><br><span class="line">begin -- end </span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-basic.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-basic.png" alt="具有两个节点的基本图" /></a></p><p>如果要使用有向图(带箭头),则需要使用<code>digraph</code>:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph { </span><br><span class="line">begin -> end </span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-directed-graph.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-directed-graph.png" alt="基本有向图" /></a></p><p>箭头可以单向或双向:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph { </span><br><span class="line"> a -> b </span><br><span class="line"> a -> c [dir=both] </span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-bidirectional-arrow.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-bidirectional-arrow.png" alt="带有双向箭头的图" /></a></p><h3 id="形状"><a class="markdownIt-Anchor" href="#形状"></a> 形状</h3><p>如果我们不喜欢椭圆形,可以使用其他形状:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> a [shape=box]</span><br><span class="line"> b [shape=polygon,sides=6]</span><br><span class="line"> c [shape=triangle]</span><br><span class="line"> d [shape=invtriangle]</span><br><span class="line"> e [shape=polygon,sides=4,skew=.5]</span><br><span class="line"> f [shape=polygon,sides=4,distortion=.5]</span><br><span class="line"> g [shape=diamond]</span><br><span class="line"> h [shape=Mdiamond]</span><br><span class="line"> i [shape=Msquare]</span><br><span class="line"> a -> b</span><br><span class="line"> a -> c</span><br><span class="line"> a -> d</span><br><span class="line"> a -> e</span><br><span class="line"> a -> f</span><br><span class="line"> a -> g</span><br><span class="line"> a -> h</span><br><span class="line"> a -> i</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-shapes.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-shapes.png" alt="节点形状" /></a></p><p>可以在<a href="https://www.graphviz.org/doc/info/shapes.html" target="_blank" rel="noopener">其文档</a>的“ <a href="https://www.graphviz.org/doc/info/shapes.html" target="_blank" rel="noopener">节点形状”部分中</a>找到不同的受支持形状。</p><p>我们还可以向节点添加一些颜色和样式:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> a [style=filled,color=green]</span><br><span class="line"> b [peripheries=4,color=blue]</span><br><span class="line"> c [fontcolor=crimson]</span><br><span class="line"> d [style=filled,fillcolor=dodgerblue,color=coral4,penwidth=3]</span><br><span class="line"> e [style=dotted]</span><br><span class="line"> f [style=dashed]</span><br><span class="line"> g [style=diagonals]</span><br><span class="line"> h [style=filled,color="#333399"]</span><br><span class="line"> i [style=filled,color="#ff000055"]</span><br><span class="line"> j [shape=box,style=striped,fillcolor="red:green:blue"]</span><br><span class="line"> k [style=wedged,fillcolor="green:white:red"]</span><br><span class="line"> a -> b</span><br><span class="line"> a -> c</span><br><span class="line"> a -> d</span><br><span class="line"> a -> e</span><br><span class="line"> b -> f</span><br><span class="line"> b -> g</span><br><span class="line"> b -> h</span><br><span class="line"> b -> i</span><br><span class="line"> d -> j</span><br><span class="line"> j -> k</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-shapes-styles.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-shapes-styles.png" alt="节点形状样式" /></a></p><p>可以在 <a href="http://www.graphviz.org/doc/info/colors.html" target="_blank" rel="noopener">颜色名称文档</a> 中找到不同的 <a href="http://www.graphviz.org/doc/info/colors.html" target="_blank" rel="noopener">颜色名称</a>。</p><h3 id="箭头"><a class="markdownIt-Anchor" href="#箭头"></a> 箭头</h3><p>箭头的尾巴和头部也可以修改:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> a -> b [dir=both,arrowhead=open,arrowtail=inv]</span><br><span class="line"> a -> c [dir=both,arrowhead=dot,arrowtail=invdot]</span><br><span class="line"> a -> d [dir=both,arrowhead=odot,arrowtail=invodot]</span><br><span class="line"> a -> e [dir=both,arrowhead=tee,arrowtail=empty]</span><br><span class="line"> a -> f [dir=both,arrowhead=halfopen,arrowtail=crow]</span><br><span class="line"> a -> g [dir=both,arrowhead=diamond,arrowtail=box]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-arrows.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-arrows.png" alt="箭" /></a></p><p>可以在<a href="https://www.graphviz.org/doc/info/arrows.html" target="_blank" rel="noopener">箭头形状文档中</a>找到不同的箭头类型。</p><p>以及向箭头线添加样式:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> a -> b [color="black:red:blue"]</span><br><span class="line"> a -> c [color="black:red;0.5:blue"]</span><br><span class="line"> a -> d [dir=none,color="green:red:blue"]</span><br><span class="line"> a -> e [dir=none,color="green:red;.3:blue"]</span><br><span class="line"> a -> f [dir=none,color="orange"]</span><br><span class="line"> d -> g [arrowsize=2.5]</span><br><span class="line"> d -> h [style=dashed]</span><br><span class="line"> d -> i [style=dotted]</span><br><span class="line"> d -> j [penwidth=5]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-arrows-styles.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-arrows-styles.png" alt="箭" /></a></p><p>如果我们注意上面的代码和图表,我们可以看到,当我们为箭头指定多种颜色时,如果不指定任何权重,每种颜色将只有一行。如果我们想要一个带有多种颜色的箭头,则至少一种颜色必须指定要覆盖的线条的重量百分比:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1 a -> e [dir=none,color="green:red;.3:blue"]</span><br></pre></td></tr></table></figure><h3 id="标签"><a class="markdownIt-Anchor" href="#标签"></a> 标签</h3><p>我们可以向节点添加标签:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> begin [label="This is the beginning"]</span><br><span class="line"> end [label="It ends here"]</span><br><span class="line"> begin -> end</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-labels.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-labels.png" alt="标签" /></a></p><p>以及顶点:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> begin</span><br><span class="line"> end</span><br><span class="line"> begin -> end [label="Beginning to end"]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-vertix-label.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-vertix-label.png" alt="Vertix标签" /></a></p><p>我们可以设置标签样式:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> begin [label="This is the beginning",fontcolor=green,fontsize=10]</span><br><span class="line"> end [label="It ends here",fontcolor=red,fontsize=10]</span><br><span class="line"> begin -> end [label="Beginning to end",fontcolor=gray,fontsize=16]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-label-styles.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-label-styles.png" alt="标签样式" /></a></p><h3 id="集群"><a class="markdownIt-Anchor" href="#集群"></a> 集群</h3><p>聚类也称为子图。集群的名称必须以开头<code>cluster_</code>,否则将不会包含在框中。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> subgraph cluster_a {</span><br><span class="line"> b</span><br><span class="line"> c -> d</span><br><span class="line"> }</span><br><span class="line"> a -> b</span><br><span class="line"> d -> e</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-cluster.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-cluster.png" alt="集群" /></a></p><p>集群可以根据需要嵌套:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> subgraph cluster_a {</span><br><span class="line"> subgraph cluster_b {</span><br><span class="line"> subgraph cluster_c {</span><br><span class="line"> d</span><br><span class="line"> }</span><br><span class="line"> c -> d</span><br><span class="line"> }</span><br><span class="line"> b -> c</span><br><span class="line"> }</span><br><span class="line"> a -> b</span><br><span class="line"> d -> e</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-clusters-nested.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-clusters-nested.png" alt="嵌套集群" /></a></p><h3 id="html"><a class="markdownIt-Anchor" href="#html"></a> HTML</h3><p>HTML使我们可以创建更复杂的节点,这些节点可以分为多个部分。可以在图中独立地引用每个部分:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> a [shape=plaintext,label=<span class="tag"><</span></span><br><span class="line"> <table></span><br><span class="line"> <span class="tag"><<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span>></span>Hello<span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span>></span>world!<span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span> <span class="attr">colspan</span>=<span class="string">"2"</span> <span class="attr">port</span>=<span class="string">"a1"</span>></span>are you ok?<span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">table</span>></span></span><br><span class="line"> >]</span><br><span class="line"> b [shape=plaintext,label=<span class="tag"><</span></span><br><span class="line"> <table border="0" cellborder="1" cellspacing="0"></span><br><span class="line"> <span class="tag"><<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span> <span class="attr">rowspan</span>=<span class="string">"3"</span>></span>left<span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span>></span>top<span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span> <span class="attr">rowspan</span>=<span class="string">"3"</span> <span class="attr">port</span>=<span class="string">"b2"</span>></span>right<span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span> <span class="attr">port</span>=<span class="string">"b1"</span>></span>center<span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span>></span>bottom<span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">table</span>></span></span><br><span class="line"> >]</span><br><span class="line"></span><br><span class="line"> a:a1 -> b:b1</span><br><span class="line"> a:a1 -> b:b2</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-html.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-html.png" alt="HTML节点" /></a></p><p>只有HTML的一个子集可用于创建节点,并且规则非常严格。为了使节点正确显示,我们需要将设置<code>shape</code>为<code>plaintext</code>。</p><p>需要注意的另一件事是<code>port</code>属性,它使我们可以使用冒号(<code>a:a1</code>)来引用该特定单元格。</p><p>我们可以设置HTML节点的样式,但只能使用HTML的子集:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> a [shape=plaintext,label=<span class="tag"><</span></span><br><span class="line"> <table></span><br><span class="line"> <span class="tag"><<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span> <span class="attr">color</span>=<span class="string">"#ff0000"</span> <span class="attr">bgcolor</span>=<span class="string">"#008822"</span>></span><span class="tag"><<span class="name">font</span> <span class="attr">color</span>=<span class="string">"#55ff00"</span>></span>Hello<span class="tag"></<span class="name">font</span>></span><span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span>></span>world!<span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span> <span class="attr">colspan</span>=<span class="string">"2"</span> <span class="attr">color</span>=<span class="string">"#00ff00"</span> <span class="attr">bgcolor</span>=<span class="string">"#ff0000"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">font</span> <span class="attr">color</span>=<span class="string">"#ffffff"</span>></span>are you ok?<span class="tag"></<span class="name">font</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">table</span>></span></span><br><span class="line"> >]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-html-style.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-html-style.png" alt="HTML节点样式" /></a></p><h3 id="图片"><a class="markdownIt-Anchor" href="#图片"></a> 图片</h3><p>有时我们想为节点使用指定图标,这可以通过<code>image</code>属性来完成:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> ec2 [shape=none,label="",image="icons/ec2.png"]</span><br><span class="line"> igw [shape=none,label="",image="icons/igw.png"]</span><br><span class="line"> rds [shape=none,label="",image="icons/rds.png"]</span><br><span class="line"> vpc [shape=none,label="",image="icons/vpc.png"]</span><br><span class="line"></span><br><span class="line"> subgraph cluster_vpc {</span><br><span class="line"> label="VPC"</span><br><span class="line"></span><br><span class="line"> subgraph cluster_public_subnet {</span><br><span class="line"> label="Public Subnet"</span><br><span class="line"> ec2</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> subgraph cluster_private_subnet {</span><br><span class="line"> label="Private Subnet"</span><br><span class="line"> ec2 -> rds</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> vpc</span><br><span class="line"> igw -> ec2</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> users -> igw</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-images.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-images.png" alt="节点图片" /></a></p><h3 id="rank"><a class="markdownIt-Anchor" href="#rank"></a> Rank</h3><p>等级是最难理解的事情之一,因为它们会改变渲染引擎的工作方式。在这里,我将介绍一些我认为有用的基本知识。</p><p>图表通常会从上到下呈现:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> a -> b</span><br><span class="line"> b -> c</span><br><span class="line"> a -> d</span><br><span class="line"> a -> c</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-top-bottom.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-top-bottom.png" alt="上下图" /></a></p><p>使用<code>rankdir</code>属性,我们可以从左到右渲染它:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> rankdir=LR</span><br><span class="line"></span><br><span class="line"> a -> b</span><br><span class="line"> b -> c</span><br><span class="line"> a -> d</span><br><span class="line"> a -> c</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-left-right.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-left-right.png" alt="左右图" /></a></p><p>排名还可以用于强制一个节点与另一个节点处于同一级别:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> rankdir=LR</span><br><span class="line"></span><br><span class="line"> a -> b</span><br><span class="line"> b -> c</span><br><span class="line"> a -> d</span><br><span class="line"> a -> c</span><br><span class="line"></span><br><span class="line"> {rank=same;c;b}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-rank-same.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-rank-same.png" alt="等级=相同" /></a></p><p>在上面的示例中,我们用于<code>rank=same</code>将node <code>c</code>与node 对齐<code>b</code>。</p><p>该<code>rankdir</code>属性是全局属性,因此无法在集群内部更改,但是使用<code>rank</code>我们可以模拟<code>LR</code>集群内部的方向:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> subgraph cluster_A {</span><br><span class="line"> a1 -> a2</span><br><span class="line"> a2 -> a3</span><br><span class="line"></span><br><span class="line"> {rank=same;a1;a2;a3}</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> subgraph cluster_B {</span><br><span class="line"> a3 -> b1</span><br><span class="line"> b1 -> b2</span><br><span class="line"> b2 -> b3</span><br><span class="line"></span><br><span class="line"> {rank=same;b1;b2;b3}</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> begin -> a1</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-rank-cluster.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-rank-cluster.png" alt="等级=集群内部相同" /></a></p><p>我们可以结合<code>rank</code>使用<code>constraint=false</code>以创建更紧凑的图形:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> subgraph cluster_A {</span><br><span class="line"> a1</span><br><span class="line"> a2</span><br><span class="line"> a3</span><br><span class="line"> {rank=same;a1;a2;a3}</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> subgraph cluster_B {</span><br><span class="line"> b1</span><br><span class="line"> b2</span><br><span class="line"> b3</span><br><span class="line"></span><br><span class="line"> {rank=same;b1;b2;b3}</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> begin -> a1</span><br><span class="line"> a1 -> a2 [constraint=false]</span><br><span class="line"> a2 -> a3 [constraint=false]</span><br><span class="line"> a3 -> b1</span><br><span class="line"> b1 -> b2</span><br><span class="line"> b2 -> b3</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-constrained.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-constraint.png" alt="Graphviz约束" /></a></p><p>等级还可以用于指定每个节点之间的距离:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">digraph MyGraph {</span><br><span class="line"> rankdir=LR</span><br><span class="line"> ranksep=1</span><br><span class="line"> a -> b</span><br><span class="line"> b -> c</span><br><span class="line"> c -> d</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><a href="https://ncona.com/images/posts/graphviz-ranksep.png" target="_blank" rel="noopener"><img src="https://ncona.com/images/posts/graphviz-ranksep.png" alt="朗塞普" /></a></p><p>其缺省值<code>ranksep</code>是<code>.5</code>。</p><h2 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> 总结</h2><p>在这篇文章中,我们学习了如何使用 Graphviz 基于声明性语言生成图。这使我在将来更容易绘制架构图并对其进行修改。</p><p>我介绍了我认为对于日常使用最重要的功能,但是坦率地说,很多功能我仍还不了解。</p>]]></content>
<summary type="html">
<blockquote>
<p>转载自:<a href="https://ncona.com/2020/06/create-diagrams-with-code-using-graphviz/" target="_blank" rel="noopener">https://nco
</summary>
<category term="graph" scheme="https://yuechuanx.top/tags/graph/"/>
<category term="tool" scheme="https://yuechuanx.top/tags/tool/"/>
</entry>
<entry>
<title>用 Prettytable 在终端输出漂亮的表格</title>
<link href="https://yuechuanx.top/Python/prettytable/"/>
<id>https://yuechuanx.top/Python/prettytable/</id>
<published>2020-06-05T07:07:28.000Z</published>
<updated>2020-06-05T07:07:28.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>转载来源:<a href="https://linuxops.org/blog/python/prettytable.html" target="_blank" rel="noopener">https://linuxops.org/blog/python/prettytable.html</a></p></blockquote><h2 id="一-前言"><a class="markdownIt-Anchor" href="#一-前言"></a> 一、前言</h2><p>最近在用python写一个小工具,这个工具主要就是用来管理各种资源的信息,比如阿里云的ECS等信息,因为我工作的电脑使用的是LINUX,所以就想着用python写一个命令行的管理工具,基本的功能就是同步阿里云的资源的信息到数据库,然后可以使用命令行查询。</p><p>因为信息是展现在命令行中的,众所周知,命令行展现复杂的文本看起来着实累人,于是就想着能像表格那样展示,那看起来就舒服多了。</p><p>prettytable库就是这么一个工具,prettytable可以打印出美观的表格,并且对中文支持相当好(如果有试图自己实现打印表格,你就应该知道处理中文是多么的麻烦)</p><blockquote><p>说明:本文使用Markdown语法编写,为了展示方便,以及复制方便,所以本文中没有使用截图,因为格式控制的问题,文章中的运行结果会出现一些分割线的偏移,在终端中呈现并此问题,请各位手动去操作验证。</p></blockquote><h2 id="二-安装"><a class="markdownIt-Anchor" href="#二-安装"></a> 二、安装</h2><p>prettytable并非python的内置库,通过 <code>pip install prettytable</code>即可安装。</p><h2 id="三-一个小示例"><a class="markdownIt-Anchor" href="#三-一个小示例"></a> 三、一个小示例</h2><p>我们先来看一个示例:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python</span></span><br><span class="line"><span class="comment">#**coding:utf-8**</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">from</span> prettytable <span class="keyword">import</span> PrettyTable</span><br><span class="line">reload(sys)</span><br><span class="line">sys.setdefaultencoding(<span class="string">'utf8'</span>)</span><br><span class="line"></span><br><span class="line">table = PrettyTable([<span class="string">'编号'</span>,<span class="string">'云编号'</span>,<span class="string">'名称'</span>,<span class="string">'IP地址'</span>])</span><br><span class="line">table.add_row([<span class="string">'1'</span>,<span class="string">'server01'</span>,<span class="string">'服务器01'</span>,<span class="string">'172.16.0.1'</span>])</span><br><span class="line">table.add_row([<span class="string">'2'</span>,<span class="string">'server02'</span>,<span class="string">'服务器02'</span>,<span class="string">'172.16.0.2'</span>])</span><br><span class="line">table.add_row([<span class="string">'3'</span>,<span class="string">'server03'</span>,<span class="string">'服务器03'</span>,<span class="string">'172.16.0.3'</span>])</span><br><span class="line">table.add_row([<span class="string">'4'</span>,<span class="string">'server04'</span>,<span class="string">'服务器04'</span>,<span class="string">'172.16.0.4'</span>])</span><br><span class="line">table.add_row([<span class="string">'5'</span>,<span class="string">'server05'</span>,<span class="string">'服务器05'</span>,<span class="string">'172.16.0.5'</span>])</span><br><span class="line">table.add_row([<span class="string">'6'</span>,<span class="string">'server06'</span>,<span class="string">'服务器06'</span>,<span class="string">'172.16.0.6'</span>])</span><br><span class="line">table.add_row([<span class="string">'7'</span>,<span class="string">'server07'</span>,<span class="string">'服务器07'</span>,<span class="string">'172.16.0.7'</span>])</span><br><span class="line">table.add_row([<span class="string">'8'</span>,<span class="string">'server08'</span>,<span class="string">'服务器08'</span>,<span class="string">'172.16.0.8'</span>])</span><br><span class="line">table.add_row([<span class="string">'9'</span>,<span class="string">'server09'</span>,<span class="string">'服务器09'</span>,<span class="string">'172.16.0.9'</span>])</span><br><span class="line">print(table)</span><br></pre></td></tr></table></figure><p>以上示例运行结果如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">linuxops@deepin:~$ python p.py </span><br><span class="line">+------+----------+----------+------------+</span><br><span class="line">| 编号 | 云编号 | 名称 | IP地址 |</span><br><span class="line">+------+----------+----------+------------+</span><br><span class="line">| 1 | server01 | 服务器01 | 172.16.0.1 |</span><br><span class="line">| 2 | server02 | 服务器02 | 172.16.0.2 |</span><br><span class="line">| 3 | server03 | 服务器03 | 172.16.0.3 |</span><br><span class="line">| 4 | server04 | 服务器04 | 172.16.0.4 |</span><br><span class="line">| 5 | server05 | 服务器05 | 172.16.0.5 |</span><br><span class="line">| 6 | server06 | 服务器06 | 172.16.0.6 |</span><br><span class="line">| 7 | server07 | 服务器07 | 172.16.0.7 |</span><br><span class="line">| 8 | server08 | 服务器08 | 172.16.0.8 |</span><br><span class="line">| 9 | server09 | 服务器09 | 172.16.0.9 |</span><br><span class="line">+------+----------+----------+------------+</span><br></pre></td></tr></table></figure><p>在以上的示例中,我们通过<code>form</code>导入了表格库。 <code>table</code>实例化了一个表格库,并且添加了<code>['编号','云编号','名称','IP地址']</code>为表头,如果没有添加表头,那么会以默认的Field+编号显示,例如:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">+---------+----------+----------+------------+</span><br><span class="line">| Field 1 | Field 2 | Field 3 | Field 4 |</span><br><span class="line">+---------+----------+----------+------------+</span><br></pre></td></tr></table></figure><p>所以为更直观看出每一列的意义,还是要添加表头的。</p><h2 id="四-添加数据"><a class="markdownIt-Anchor" href="#四-添加数据"></a> 四、添加数据</h2><p>prettytable提供了多种的添加数据的方式,最常用的应该就是按行按列添加数据了。</p><h3 id="a-按行添加数据-tableadd_row"><a class="markdownIt-Anchor" href="#a-按行添加数据-tableadd_row"></a> A、按行添加数据 table.add_row</h3><p>在上面简单的示例中,我们就是按行添加数据的。</p><p>添加的数据必须要是列表的形式,而且数据的列表长度要和表头的长度一样。在实际的使用中,我们应该要关注到添加的数据是否和表头对应,这一点很重要。</p><h3 id="b-按列添加数据-tableadd_column"><a class="markdownIt-Anchor" href="#b-按列添加数据-tableadd_column"></a> B、按列添加数据 table.add_column()</h3><p>看下面的示例:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python</span></span><br><span class="line"><span class="comment">#**coding:utf-8**</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">from</span> prettytable <span class="keyword">import</span> PrettyTable</span><br><span class="line">reload(sys)</span><br><span class="line">sys.setdefaultencoding(<span class="string">'utf8'</span>)</span><br><span class="line"></span><br><span class="line">table = PrettyTable()</span><br><span class="line">table.add_column(<span class="string">'项目'</span>, [<span class="string">'编号'</span>,<span class="string">'云编号'</span>,<span class="string">'名称'</span>,<span class="string">'IP地址'</span>])</span><br><span class="line">table.add_column(<span class="string">'值'</span>, [<span class="string">'1'</span>,<span class="string">'server01'</span>,<span class="string">'服务器01'</span>,<span class="string">'172.16.0.1'</span>])</span><br><span class="line">print(table)</span><br></pre></td></tr></table></figure><p>运行结果如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">+-------+--------+------------+</span><br><span class="line">| index | 项目 | 值 |</span><br><span class="line">+-------+--------+------------+</span><br><span class="line">| 1 | 编号 | 1 |</span><br><span class="line">| 2 | 云编号 | server01 |</span><br><span class="line">| 3 | 名称 | 服务器01 |</span><br><span class="line">| 4 | IP地址 | 172.16.0.1 |</span><br><span class="line">+-------+--------+------------+</span><br></pre></td></tr></table></figure><p>以上示例中,我们通过<code>add_column</code>来按列添加数据,按列添加数据不需要在实例化表格的时候制定表头,它的表头是在添加列的时候指定的。</p><p><code>table.add_column('项目', ['编号','云编号','名称','IP地址'])</code> 这一行代码为例,<code>项目</code>指定了这个列的表头名为"项目",<code>['编号','云编号','名称','IP地址']</code>为列的值,同样为列表。</p><h3 id="c-从csv文件添加数据"><a class="markdownIt-Anchor" href="#c-从csv文件添加数据"></a> C、从csv文件添加数据</h3><p>PrettyTable不仅提供了手动按行按列添加数据,也支持直接从csv文件中读取数据。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python</span></span><br><span class="line"><span class="comment">#**coding:utf-8**</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">from</span> prettytable <span class="keyword">import</span> PrettyTable</span><br><span class="line"><span class="keyword">from</span> prettytable <span class="keyword">import</span> from_csv </span><br><span class="line">reload(sys)</span><br><span class="line">sys.setdefaultencoding(<span class="string">'utf8'</span>)</span><br><span class="line"></span><br><span class="line">table = PrettyTable()</span><br><span class="line">fp = open(<span class="string">"res.csv"</span>, <span class="string">"r"</span>) </span><br><span class="line">table = from_csv(fp) </span><br><span class="line">print(table)</span><br><span class="line">fp.close()</span><br></pre></td></tr></table></figure><p>如果要读取cvs文件数据,必须要先导入<code>from_csv</code>,否则无法运行。上面的示例运行结果如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">+------+----------+----------+------------+</span><br><span class="line">| 编号 | 云编号 | 名称 | IP地址 |</span><br><span class="line">+------+----------+----------+------------+</span><br><span class="line">| 1 | server01 | 服务器01 | 172.16.0.1 |</span><br><span class="line">| 2 | server02 | 服务器02 | 172.16.0.2 |</span><br><span class="line">| 3 | server03 | 服务器03 | 172.16.0.3 |</span><br><span class="line">| 4 | server04 | 服务器04 | 172.16.0.4 |</span><br><span class="line">| 5 | server05 | 服务器05 | 172.16.0.5 |</span><br><span class="line">| 6 | server06 | 服务器06 | 172.16.0.6 |</span><br><span class="line">| 7 | server07 | 服务器07 | 172.16.0.7 |</span><br><span class="line">| 8 | server08 | 服务器08 | 172.16.0.8 |</span><br><span class="line">| 9 | server09 | 服务器09 | 172.16.0.9 |</span><br><span class="line">+------+----------+----------+------------+</span><br></pre></td></tr></table></figure><blockquote><p>csv文件不能通过xls直接重命名得到,会报错。如果是xls文件,请用另存为csv获得csv文件</p></blockquote><h3 id="d-从sql查询值添加"><a class="markdownIt-Anchor" href="#d-从sql查询值添加"></a> D、从sql查询值添加</h3><p>从数据库查询出来的数据可以直接导入到表格打印,下面的例子使用了<code>sqlite3</code>,如果使用的是mysql也是一样的,只要能查询到数据就能导入到表格中</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python</span></span><br><span class="line"><span class="comment">#**coding:utf-8**</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">from</span> prettytable <span class="keyword">import</span> PrettyTable</span><br><span class="line"><span class="keyword">from</span> prettytable <span class="keyword">import</span> from_db_cursor </span><br><span class="line"><span class="keyword">import</span> sqlite3</span><br><span class="line">reload(sys)</span><br><span class="line">sys.setdefaultencoding(<span class="string">'utf8'</span>)</span><br><span class="line"></span><br><span class="line">conn = sqlite3.connect(<span class="string">"/tmp/aliyun.db"</span>)</span><br><span class="line">cur = conn.cursor()</span><br><span class="line">cur.execute(<span class="string">"SELECT * FROM res"</span>) </span><br><span class="line">table = from_db_cursor(cur)</span><br><span class="line">print(table)</span><br></pre></td></tr></table></figure><p>运行结果如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">+------+----------+----------+------------+</span><br><span class="line">| 编号 | 云编号 | 名称 | IP地址 |</span><br><span class="line">+------+----------+----------+------------+</span><br><span class="line">| 1 | server01 | 服务器01 | 172.16.0.1 |</span><br><span class="line">| 2 | server02 | 服务器02 | 172.16.0.2 |</span><br><span class="line">| 3 | server03 | 服务器03 | 172.16.0.3 |</span><br><span class="line">| 4 | server04 | 服务器04 | 172.16.0.4 |</span><br><span class="line">| 5 | server05 | 服务器05 | 172.16.0.5 |</span><br><span class="line">| 6 | server06 | 服务器06 | 172.16.0.6 |</span><br><span class="line">| 7 | server07 | 服务器07 | 172.16.0.7 |</span><br><span class="line">| 8 | server08 | 服务器08 | 172.16.0.8 |</span><br><span class="line">| 9 | server09 | 服务器09 | 172.16.0.9 |</span><br><span class="line">+------+----------+----------+------------+</span><br></pre></td></tr></table></figure><h3 id="e-从html导入数据"><a class="markdownIt-Anchor" href="#e-从html导入数据"></a> E、从HTML导入数据</h3><p>支持从html的表格中导入,请看下面这个例子:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python</span></span><br><span class="line"><span class="comment">#**coding:utf-8**</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">from</span> prettytable <span class="keyword">import</span> PrettyTable</span><br><span class="line"><span class="keyword">from</span> prettytable <span class="keyword">import</span> from_html </span><br><span class="line">reload(sys)</span><br><span class="line">sys.setdefaultencoding(<span class="string">'utf8'</span>)</span><br><span class="line"></span><br><span class="line">html_string=<span class="string">'''<table></span></span><br><span class="line"><span class="string"><tr></span></span><br><span class="line"><span class="string"><th>编号</th></span></span><br><span class="line"><span class="string"><th>云编号</th></span></span><br><span class="line"><span class="string"><th>名称</th></span></span><br><span class="line"><span class="string"><th>IP地址</th></span></span><br><span class="line"><span class="string"></tr></span></span><br><span class="line"><span class="string"><tr></span></span><br><span class="line"><span class="string"><td>1</td></span></span><br><span class="line"><span class="string"><td>server01</td></span></span><br><span class="line"><span class="string"><td>服务器01</td></span></span><br><span class="line"><span class="string"><td>172.16.0.1</td></span></span><br><span class="line"><span class="string"></tr></span></span><br><span class="line"><span class="string"><tr></span></span><br><span class="line"><span class="string"><td>2</td></span></span><br><span class="line"><span class="string"><td>server02</td></span></span><br><span class="line"><span class="string"><td>服务器02</td></span></span><br><span class="line"><span class="string"><td>172.16.0.2</td></span></span><br><span class="line"><span class="string"></tr></span></span><br><span class="line"><span class="string"></table>'''</span></span><br><span class="line"></span><br><span class="line">table = from_html(html_string)</span><br><span class="line"></span><br><span class="line">print(table[<span class="number">0</span>])</span><br></pre></td></tr></table></figure><p>运行结果如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">+------+----------+----------+------------+</span><br><span class="line">| 编号 | 云编号 | 名称 | IP地址 |</span><br><span class="line">+------+----------+----------+------------+</span><br><span class="line">| 1 | server01 | 服务器01 | 172.16.0.1 |</span><br><span class="line">| 2 | server02 | 服务器02 | 172.16.0.2 |</span><br><span class="line">+------+----------+----------+------------+</span><br></pre></td></tr></table></figure><p>如上示例中,我们可以导入html的表格,但是不一样的地方是<code>print</code>语句,使用html表格导入数据的时候print的必须是列表中的第一个元素,否则有可能会报<code>[<prettytable.PrettyTable object at 0x7fa87feba590>]</code>这样的错误。</p><p>这是因为<code>table</code>并不是PrettyTable对象,而是包含单个PrettyTable对象的列表,它通过解析html而来,所以无法直接打印<code>table</code>,而需要打印<code>table[0]</code></p><h2 id="五-表格输出格式"><a class="markdownIt-Anchor" href="#五-表格输出格式"></a> 五、表格输出格式</h2><p>正如支持多种输入一样,表格的输出也支持多种格式,我们在上面中的例子中已经使用了print的方式输出,这是一种常用的输出方式。</p><h3 id="a-print"><a class="markdownIt-Anchor" href="#a-print"></a> A、print</h3><p>直接通过<code>print</code>打印出表格。这种方式打印出的表格会带边框。</p><h3 id="b-输出html格式的表格"><a class="markdownIt-Anchor" href="#b-输出html格式的表格"></a> B、输出HTML格式的表格</h3><p><code>print(table.get_html_string())</code>可以打印出html标签的表格。</p><p>在上面的例子中,使用<code>print(table.get_html_string())</code>会打印出如下结果:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><table></span><br><span class="line"> <tr></span><br><span class="line"> <th>编号</th></span><br><span class="line"> <th>云编号</th></span><br><span class="line"> <th>名称</th></span><br><span class="line"> <th>IP地址</th></span><br><span class="line"> </tr></span><br><span class="line"> <tr></span><br><span class="line"> <td>1</td></span><br><span class="line"> <td>server01</td></span><br><span class="line"> <td>服务器01</td></span><br><span class="line"> <td>172.16.0.1</td></span><br><span class="line"> </tr></span><br><span class="line"> <tr></span><br><span class="line"> <td>2</td></span><br><span class="line"> <td>server02</td></span><br><span class="line"> <td>服务器02</td></span><br><span class="line"> <td>172.16.0.2</td></span><br><span class="line"> </tr></span><br><span class="line"></table></span><br></pre></td></tr></table></figure><h2 id="六-选择性输出"><a class="markdownIt-Anchor" href="#六-选择性输出"></a> 六、选择性输出</h2><p>prettytable在创建表格之后,你依然可以有选择的输出某些特定的行.</p><h3 id="a-输出指定的列"><a class="markdownIt-Anchor" href="#a-输出指定的列"></a> A、输出指定的列</h3><p><code>print table.get_string(fields=["编号", "IP地址"])</code>可以输出指定的列</p><h3 id="b-输出前两行"><a class="markdownIt-Anchor" href="#b-输出前两行"></a> B、输出前两行</h3><p>通过<code>print(table.get_string(start = 0, end = 2))</code>的可以打印出指定的列,当然<code>start</code>和<code>end</code>参数让我可以自由控制显示区间。当然区间中包含<code>start</code>不包含<code>end</code>,是不是很熟悉这样的用法?</p><blockquote><p>根据输出指定行列的功能,我们可以同时指定行和列来输出,这里就不说明了。</p></blockquote><h3 id="c-将表格切片"><a class="markdownIt-Anchor" href="#c-将表格切片"></a> C、将表格切片</h3><p>从上面的输出区间,我们做一个大胆的假设,既然区间包含<code>start</code>不包含<code>end</code>这种规则和切片的一样,我们可以不可通过切片来生成一个新的表格然后将其打印。</p><p>事实上是可以的。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">new_table = table[<span class="number">0</span>:<span class="number">2</span>]</span><br><span class="line">print(new_table)</span><br></pre></td></tr></table></figure><p>如上代码段中,我们就可以打印出0到1行共2行的表格,python的切片功能异常强大,配合切片我们可以自由的输入任意的行。</p><h3 id="d-输出排序"><a class="markdownIt-Anchor" href="#d-输出排序"></a> D、输出排序</h3><p>有时候我们需要对输出的表格进行排序,使用<code>print table.get_string(sortby="编号", reversesort=True)</code>就可以对表格进行排序,其中<code>reversesort</code>指定了是否倒序排序,默认为<code>False</code>,即默认正序列排序。</p><p><code>sortby</code>指定了排序的字段。</p><h2 id="七-表格的样式"><a class="markdownIt-Anchor" href="#七-表格的样式"></a> 七、表格的样式</h2><h3 id="a-内置样式"><a class="markdownIt-Anchor" href="#a-内置样式"></a> A、内置样式</h3><p>通过<code>set_style()</code>可以设置表格样式,prettytable内置了多种的样式个人觉得<code>MSWORD_FRIENDLY</code>,<code>PLAIN_COLUMNS</code>,<code>DEFAULT</code> 这三种样式看起来比较清爽,在终端下显示表格本来看起就很累,再加上一下花里胡哨的东西看起来就更累。</p><p>除了以上推荐的三种样式以外,还有一种样式不得不说,那就是<code>RANDOM</code>,这是一种随机的样式,每一次打印都会在内置的样式中随机选择一个,比较好玩。</p><p>具体内置了几种样式,请各位参考官网完整自己尝试输出看看。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python</span></span><br><span class="line"><span class="comment">#**coding:utf-8**</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">from</span> prettytable <span class="keyword">import</span> PrettyTable</span><br><span class="line"><span class="keyword">from</span> prettytable <span class="keyword">import</span> MSWORD_FRIENDLY</span><br><span class="line"><span class="keyword">from</span> prettytable <span class="keyword">import</span> PLAIN_COLUMNS</span><br><span class="line"><span class="keyword">from</span> prettytable <span class="keyword">import</span> RANDOM</span><br><span class="line"><span class="keyword">from</span> prettytable <span class="keyword">import</span> DEFAULT</span><br><span class="line"></span><br><span class="line">reload(sys)</span><br><span class="line">sys.setdefaultencoding(<span class="string">'utf8'</span>)</span><br><span class="line"></span><br><span class="line">table = PrettyTable([<span class="string">'编号'</span>,<span class="string">'云编号'</span>,<span class="string">'名称'</span>,<span class="string">'IP地址'</span>])</span><br><span class="line">table.add_row([<span class="string">'1'</span>,<span class="string">'server01'</span>,<span class="string">'服务器01'</span>,<span class="string">'172.16.0.1'</span>])</span><br><span class="line">table.add_row([<span class="string">'3'</span>,<span class="string">'server03'</span>,<span class="string">'服务器03'</span>,<span class="string">'172.16.0.3'</span>])</span><br><span class="line">table.add_row([<span class="string">'2'</span>,<span class="string">'server02'</span>,<span class="string">'服务器02'</span>,<span class="string">'172.16.0.2'</span>])</span><br><span class="line">table.add_row([<span class="string">'9'</span>,<span class="string">'server09'</span>,<span class="string">'服务器09'</span>,<span class="string">'172.16.0.9'</span>])</span><br><span class="line">table.add_row([<span class="string">'4'</span>,<span class="string">'server04'</span>,<span class="string">'服务器04'</span>,<span class="string">'172.16.0.4'</span>])</span><br><span class="line">table.add_row([<span class="string">'5'</span>,<span class="string">'server05'</span>,<span class="string">'服务器05'</span>,<span class="string">'172.16.0.5'</span>])</span><br><span class="line">table.add_row([<span class="string">'6'</span>,<span class="string">'server06'</span>,<span class="string">'服务器06'</span>,<span class="string">'172.16.0.6'</span>])</span><br><span class="line">table.add_row([<span class="string">'8'</span>,<span class="string">'server08'</span>,<span class="string">'服务器08'</span>,<span class="string">'172.16.0.8'</span>])</span><br><span class="line">table.add_row([<span class="string">'7'</span>,<span class="string">'server07'</span>,<span class="string">'服务器07'</span>,<span class="string">'172.16.0.7'</span>])</span><br><span class="line">table.set_style(DEFAULT)</span><br><span class="line"></span><br><span class="line">print(table)</span><br></pre></td></tr></table></figure><h3 id="b-自定义样式"><a class="markdownIt-Anchor" href="#b-自定义样式"></a> B、自定义样式</h3><p>除了内置的样式以外,PrettyTable也提供了用户自定义,例如对齐方式,数字输出格式,边框连接符等等</p><h3 id="c-设置对齐方式"><a class="markdownIt-Anchor" href="#c-设置对齐方式"></a> C、设置对齐方式</h3><p><code>align</code>提供了用户设置对齐的方式,值有<code>l</code>,<code>r</code>,<code>c</code>方便代表左对齐,右对齐和居中 如果不设置,默认居中对齐。</p><h3 id="d-控制边框样式"><a class="markdownIt-Anchor" href="#d-控制边框样式"></a> D、控制边框样式</h3><p>在PrettyTable中,边框由三个部分组成,横边框,竖边框,和边框连接符(横竖交叉的链接符号)</p><p>如下示例:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python</span></span><br><span class="line"><span class="comment">#**coding:utf-8**</span></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">from</span> prettytable <span class="keyword">import</span> PrettyTable</span><br><span class="line"></span><br><span class="line">reload(sys)</span><br><span class="line">sys.setdefaultencoding(<span class="string">'utf8'</span>)</span><br><span class="line"></span><br><span class="line">table = PrettyTable([<span class="string">'编号'</span>,<span class="string">'云编号'</span>,<span class="string">'名称'</span>,<span class="string">'IP地址'</span>])</span><br><span class="line">table.add_row([<span class="string">'1'</span>,<span class="string">'server01'</span>,<span class="string">'服务器01'</span>,<span class="string">'172.16.0.1'</span>])</span><br><span class="line">table.add_row([<span class="string">'3'</span>,<span class="string">'server03'</span>,<span class="string">'服务器03'</span>,<span class="string">'172.16.0.3'</span>])</span><br><span class="line">table.add_row([<span class="string">'2'</span>,<span class="string">'server02'</span>,<span class="string">'服务器02'</span>,<span class="string">'172.16.0.2'</span>])</span><br><span class="line">table.add_row([<span class="string">'9'</span>,<span class="string">'server09'</span>,<span class="string">'服务器09'</span>,<span class="string">'172.16.0.9'</span>])</span><br><span class="line">table.add_row([<span class="string">'4'</span>,<span class="string">'server04'</span>,<span class="string">'服务器04'</span>,<span class="string">'172.16.0.4'</span>])</span><br><span class="line">table.add_row([<span class="string">'5'</span>,<span class="string">'server05'</span>,<span class="string">'服务器05'</span>,<span class="string">'172.16.0.5'</span>])</span><br><span class="line">table.add_row([<span class="string">'6'</span>,<span class="string">'server06'</span>,<span class="string">'服务器06'</span>,<span class="string">'172.16.0.6'</span>])</span><br><span class="line">table.add_row([<span class="string">'8'</span>,<span class="string">'server08'</span>,<span class="string">'服务器08'</span>,<span class="string">'172.16.0.8'</span>])</span><br><span class="line">table.add_row([<span class="string">'7'</span>,<span class="string">'server07'</span>,<span class="string">'服务器07'</span>,<span class="string">'172.16.0.7'</span>])</span><br><span class="line">table.align[<span class="number">1</span>] = <span class="string">'l'</span></span><br><span class="line"></span><br><span class="line">table.border = <span class="literal">True</span></span><br><span class="line">table.junction_char=<span class="string">'$'</span></span><br><span class="line">table.horizontal_char = <span class="string">'+'</span></span><br><span class="line">table.vertical_char = <span class="string">'%'</span></span><br><span class="line"></span><br><span class="line">print(table)</span><br><span class="line">table.border`控制是否显示边框,默认是`<span class="literal">True</span></span><br></pre></td></tr></table></figure><p><code>table.junction_char</code>控制边框连接符</p><p><code>table.horizontal_char</code>控制横边框符号</p><p><code>table.vertical_char</code>控制竖边框符号</p><p>上例运行如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">$++++++$++++++++++$++++++++++$++++++++++++$</span><br><span class="line">% 编号 % 云编号 % 名称 % IP地址 %</span><br><span class="line">$++++++$++++++++++$++++++++++$++++++++++++$</span><br><span class="line">% 1 % server01 % 服务器01 % 172.16.0.1 %</span><br><span class="line">% 3 % server03 % 服务器03 % 172.16.0.3 %</span><br><span class="line">% 2 % server02 % 服务器02 % 172.16.0.2 %</span><br><span class="line">% 9 % server09 % 服务器09 % 172.16.0.9 %</span><br><span class="line">% 4 % server04 % 服务器04 % 172.16.0.4 %</span><br><span class="line">% 5 % server05 % 服务器05 % 172.16.0.5 %</span><br><span class="line">% 6 % server06 % 服务器06 % 172.16.0.6 %</span><br><span class="line">% 8 % server08 % 服务器08 % 172.16.0.8 %</span><br><span class="line">% 7 % server07 % 服务器07 % 172.16.0.7 %</span><br><span class="line">$++++++$++++++++++$++++++++++$++++++++++++$</span><br></pre></td></tr></table></figure><p>以上简单介绍了表格常用的一些样式设置,具体的请参考官方网站。</p><p><a href="https://github.com/jazzband/prettytable" target="_blank" rel="noopener">https://github.com/jazzband/prettytable</a></p><p><a href="https://code.google.com/archive/p/prettytable/wikis/Tutorial.wiki" target="_blank" rel="noopener">https://code.google.com/archive/p/prettytable/wikis/Tutorial.wiki</a></p>]]></content>
<summary type="html">
<blockquote>
<p>转载来源:<a href="https://linuxops.org/blog/python/prettytable.html" target="_blank" rel="noopener">https://linuxops.org/blog/py
</summary>
<category term="Python" scheme="https://yuechuanx.top/categories/Python/"/>
<category term="python" scheme="https://yuechuanx.top/tags/python/"/>
<category term="linux" scheme="https://yuechuanx.top/tags/linux/"/>
</entry>
<entry>
<title><流畅的Python> 函数装饰器与闭包</title>
<link href="https://yuechuanx.top/Python/fluent-python-notes-chap-07/"/>
<id>https://yuechuanx.top/Python/fluent-python-notes-chap-07/</id>
<published>2020-04-29T08:14:34.000Z</published>
<updated>2020-04-29T08:14:34.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>有很多人抱怨,把这个特性命名为“装饰器”不好。主要原因是,这个名称与 GoF 书使用的不一致。<strong>装饰器</strong>这个名称可能更适合在编译器领域使用,因为它会遍历并注解语法书。<br />—“PEP 318 — Decorators for Functions and Methods”</p></blockquote><p>本章的最终目标是解释清楚函数装饰器的工作原理,包括最简单的注册装饰器和较复杂的参数化装饰器。</p><p>讨论内容:</p><ul><li>Python 如何计算装饰器语法</li><li>Python 如何判断变量是不是局部的</li><li>闭包存在的原因和工作原理</li><li><code>nonlocal</code> 能解决什么问题</li><li>实现行为良好的装饰器</li><li>标准库中有用的装饰器</li><li>实现一个参数化的装饰器</li></ul><h2 id="装饰器基础"><a class="markdownIt-Anchor" href="#装饰器基础"></a> 装饰器基础</h2><p>装饰器是可调用的对象,其参数是一个函数(被装饰的函数)。</p><p>装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。</p><p>装饰器两大特性:</p><ol><li>能把被装饰的函数替换成其他函数</li><li>装饰器在加载模块时立即执行</li></ol><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 装饰器通常会把函数替换成另一个函数</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">decorate</span><span class="params">(func)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">wrapped</span><span class="params">()</span>:</span></span><br><span class="line"> print(<span class="string">'Running wrapped()'</span>)</span><br><span class="line"> <span class="keyword">return</span> wrapped</span><br><span class="line"></span><br><span class="line"><span class="meta">@decorate</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">target</span><span class="params">()</span>:</span></span><br><span class="line"> print(<span class="string">'running target()'</span>)</span><br><span class="line">target()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 以上写法等同于</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">target</span><span class="params">()</span>:</span></span><br><span class="line"> print(<span class="string">'running target()'</span>)</span><br><span class="line">target = decorate(target)</span><br><span class="line">target()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 这里真正调用的是装饰器返回的函数</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">deco</span><span class="params">(func)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">inner</span><span class="params">()</span>:</span></span><br><span class="line"> print(<span class="string">'running iner()'</span>)</span><br><span class="line"> <span class="keyword">return</span> inner</span><br><span class="line"></span><br><span class="line"><span class="meta">@deco</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">target</span><span class="params">()</span>:</span></span><br><span class="line"> print(<span class="string">'running target()'</span>)</span><br><span class="line">target()</span><br><span class="line"><span class="comment"># target 现在是 inner 的引用</span></span><br><span class="line">target</span><br></pre></td></tr></table></figure><h2 id="python-何时执行装饰器"><a class="markdownIt-Anchor" href="#python-何时执行装饰器"></a> <em>Python</em> 何时执行装饰器</h2><p>装饰器在导入时(模块加载时)立即执行</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># registration.py</span></span><br><span class="line"></span><br><span class="line">registry = []</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">register</span><span class="params">(func)</span>:</span></span><br><span class="line"> print(<span class="string">'running register {}'</span>.format(func))</span><br><span class="line"> registry.append(func)</span><br><span class="line"> <span class="keyword">return</span> func</span><br><span class="line"></span><br><span class="line"><span class="meta">@register</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">f1</span><span class="params">()</span>:</span></span><br><span class="line"> print(<span class="string">'running f1()'</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@register</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">f2</span><span class="params">()</span>:</span></span><br><span class="line"> print(<span class="string">'running f2()'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">f3</span><span class="params">()</span>:</span></span><br><span class="line"> print(<span class="string">'running f3()'</span>)</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">main</span><span class="params">()</span>:</span></span><br><span class="line"> print(<span class="string">'running main()'</span>)</span><br><span class="line"> print(<span class="string">'registry ->'</span>, registry)</span><br><span class="line"> f1()</span><br><span class="line"> f2()</span><br><span class="line"> f3()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__==<span class="string">'__main__'</span>:</span><br><span class="line"> main()</span><br><span class="line"> </span><br><span class="line"><span class="comment"># python3 registration.py</span></span><br><span class="line"><span class="comment"># output:</span></span><br><span class="line"><span class="comment"># running register <function f1 at 0x10b4194d0></span></span><br><span class="line"><span class="comment"># running register <function f2 at 0x10b419ef0></span></span><br><span class="line"><span class="comment"># running main()</span></span><br><span class="line"><span class="comment"># registry -> [<function f1 at 0x10b4194d0>, <function f2 at 0x10b419ef0>]</span></span><br><span class="line"><span class="comment"># running f1()</span></span><br><span class="line"><span class="comment"># running f2()</span></span><br><span class="line"><span class="comment"># running f3()</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># import registration</span></span><br><span class="line"><span class="comment"># running register <function f1 at 0x10d89e950></span></span><br><span class="line"><span class="comment"># running register <function f2 at 0x10d89e050></span></span><br></pre></td></tr></table></figure><p>通过上面的例子,强调装饰器函数在导入模块式立即执行,而普通函数在被调用时运行。导入时和运行时的区别。</p><ul><li>装饰器函数通常与被装饰函数不在同一模块。</li><li>register 装饰器返回的函数没有变化</li></ul><p>上面的装饰器会原封不动地返回被装饰的函数,而不一定会对函数做修改。<br />这种装饰器叫注册装饰器,通过使用它来中心化地注册函数,例如把 URL 模式映射到生成 HTTP 响应的函数上的注册处。</p><h2 id="使用装饰器"><a class="markdownIt-Anchor" href="#使用装饰器"></a> 使用装饰器</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">promos = []</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">promotion</span><span class="params">(promo_func)</span>:</span></span><br><span class="line"> promos.append(promo_func)</span><br><span class="line"> <span class="keyword">return</span> promo_func</span><br><span class="line"></span><br><span class="line"><span class="meta">@promotion</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">fidelity</span><span class="params">(order)</span>:</span></span><br><span class="line"> <span class="string">"""积分 >= 1000 提供 5% 折扣"""</span></span><br><span class="line"> <span class="keyword">return</span> order.total() * <span class="number">.05</span> <span class="keyword">if</span> order.customer.fidelity >= <span class="number">1000</span> <span class="keyword">else</span> <span class="number">0</span></span><br></pre></td></tr></table></figure><h2 id="变量作用域规则"><a class="markdownIt-Anchor" href="#变量作用域规则"></a> 变量作用域规则</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 比较两个例子</span></span><br><span class="line"></span><br><span class="line">b = <span class="number">6</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">f1</span><span class="params">(a)</span>:</span></span><br><span class="line"> print(a)</span><br><span class="line"> print(b)</span><br><span class="line">f1(<span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">f2</span><span class="params">(a)</span>:</span></span><br><span class="line"> print(a)</span><br><span class="line"> print(b)</span><br><span class="line"> b = <span class="number">9</span> <span class="comment"># b 此时为局部变量</span></span><br><span class="line">f2(<span class="number">3</span>)</span><br></pre></td></tr></table></figure><p><em>Python</em> 假定在函数体内部的变量为局部变量。如果未在局部变量中找到,会逐级向上查找变量。</p><p>如果想在函数中赋值时让解释器把 b 当做全局变量,用 global 关键字</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">f3</span><span class="params">(a)</span>:</span></span><br><span class="line"> <span class="keyword">global</span> b</span><br><span class="line"> print(a)</span><br><span class="line"> print(b)</span><br><span class="line"> b = <span class="number">9</span> </span><br><span class="line">f3(<span class="number">3</span>)</span><br></pre></td></tr></table></figure><h2 id="闭包"><a class="markdownIt-Anchor" href="#闭包"></a> 闭包</h2><p>闭包和匿名函数常被弄混。只有涉及到嵌套函数时才有闭包问题。</p><p>闭包指延伸了作用域的函数,其中包含函数定义体中的引用,但非定义体中定义的非全局变量。和函数是否匿名无关。关键是能访问定义体之外定义的非全局变量。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Averager</span><span class="params">()</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></span><br><span class="line"> self.series = []</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__call__</span><span class="params">(self, new_value)</span>:</span></span><br><span class="line"> self.series.append(new_value)</span><br><span class="line"> total = sum(self.series)</span><br><span class="line"> <span class="keyword">return</span> total/len(self.series)</span><br><span class="line"> </span><br><span class="line">avg = Averager()</span><br><span class="line">avg(<span class="number">10</span>)</span><br><span class="line">avg(<span class="number">11</span>)</span><br><span class="line">avg(<span class="number">12</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">make_averager</span><span class="params">()</span>:</span></span><br><span class="line"> series = [] <span class="comment"># 自由变量</span></span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">averager</span><span class="params">(new_value)</span>:</span></span><br><span class="line"> series.append(new_value)</span><br><span class="line"> total = sum(series)</span><br><span class="line"> <span class="keyword">return</span> total/len(series)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> averager</span><br><span class="line"></span><br><span class="line">avg = make_averager()</span><br><span class="line">avg(<span class="number">10</span>)</span><br><span class="line">avg(<span class="number">11</span>)</span><br><span class="line">avg(<span class="number">12</span>)</span><br><span class="line"></span><br><span class="line">avg.__code__.co_varnames</span><br><span class="line">avg.__code__.co_freevars</span><br><span class="line">avg.__closure__</span><br><span class="line">avg.__closure__[<span class="number">0</span>].cell_contents</span><br></pre></td></tr></table></figure><p>在 averager 函数中,series 是自由变量,指未在本地作用域绑定的变量。</p><p>通过 <code>__code__.co_freevars</code> <code>__closure__</code> 查看自由变量和闭包</p><p>闭包是一种函数,保留定义函数时存在的自由变量的绑定。调用函数时,虽然定义作用域不可用了,但仍能使用那些绑定</p><blockquote><p>只有嵌套在其他函数中的函数才可能需要处理不在全局作用域的外部变量</p></blockquote><h2 id="nonlocal-声明"><a class="markdownIt-Anchor" href="#nonlocal-声明"></a> nonlocal 声明</h2><p>下面一个例子有缺陷:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">make_averager</span><span class="params">()</span>:</span></span><br><span class="line"> count = <span class="number">0</span></span><br><span class="line"> total = <span class="number">0</span></span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">averager</span><span class="params">(new_value)</span>:</span></span><br><span class="line"> count += <span class="number">1</span></span><br><span class="line"> total += new_value</span><br><span class="line"> <span class="keyword">return</span> total / count</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> averager</span><br><span class="line"></span><br><span class="line">avg = make_averager()</span><br><span class="line">avg(<span class="number">10</span>)</span><br></pre></td></tr></table></figure><p>注意 count, total 的赋值语句使它们成为局部变量,在赋值是会隐式创建局部变量,这样它们就不是自由变量了,因此不会保存在闭包中。</p><p>为解决这个问题,<em>Python3</em> 引入了 nonlocal 声明,作用是吧变量标记为自由变量,即使在函数中为变量新值了,也会变成自由变量。在闭包中的绑定也会更新</p><blockquote><p>对于没有 nonlocal 的 <em>Python2</em> PEP3104</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">make_averager</span><span class="params">()</span>:</span></span><br><span class="line"> count = <span class="number">0</span></span><br><span class="line"> total = <span class="number">0</span></span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">averager</span><span class="params">(new_value)</span>:</span></span><br><span class="line"> <span class="keyword">nonlocal</span> count, total</span><br><span class="line"> count += <span class="number">1</span></span><br><span class="line"> total += new_value</span><br><span class="line"> <span class="keyword">return</span> total / count</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> averager</span><br><span class="line"></span><br><span class="line">avg = make_averager()</span><br><span class="line">avg(<span class="number">10</span>)</span><br></pre></td></tr></table></figure><h2 id="实现一个简单的装饰器"><a class="markdownIt-Anchor" href="#实现一个简单的装饰器"></a> 实现一个简单的装饰器</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">clock</span><span class="params">(func)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">clocked</span><span class="params">(*args)</span>:</span></span><br><span class="line"> t0 = time.perf_counter()</span><br><span class="line"> result = func(*args)</span><br><span class="line"> elapsed = time.perf_counter() - t0</span><br><span class="line"> name = func.__name__</span><br><span class="line"> arg_str = <span class="string">', '</span>.join(repr(arg) <span class="keyword">for</span> arg <span class="keyword">in</span> args)</span><br><span class="line"> print(<span class="string">'[%0.8fs] %s(%s) -> % r'</span> %(elapsed, name, arg_str, result))</span><br><span class="line"> <span class="keyword">return</span> result</span><br><span class="line"> <span class="keyword">return</span> clocked</span><br><span class="line"></span><br><span class="line"><span class="meta">@clock </span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">snooze</span><span class="params">(seconds)</span>:</span></span><br><span class="line"> time.sleep(seconds)</span><br><span class="line"> </span><br><span class="line"><span class="meta">@clock</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">factorial</span><span class="params">(n)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span> <span class="keyword">if</span> n < <span class="number">2</span> <span class="keyword">else</span> n * factorial(n<span class="number">-1</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__==<span class="string">'__main__'</span>:</span><br><span class="line"> print(<span class="string">'*'</span> * <span class="number">40</span>, <span class="string">'Calling snooze(.123)'</span>)</span><br><span class="line"> snooze(<span class="number">.123</span>)</span><br><span class="line"> print(<span class="string">'*'</span> * <span class="number">40</span>, <span class="string">'Calling factorial(6)'</span>)</span><br><span class="line"> print(<span class="string">'6! ='</span>, factorial(<span class="number">6</span>))</span><br></pre></td></tr></table></figure><p>装饰器的典型行为:把被装饰的函数替换成新函数,二者接受相同的参数,而且(通常)返回被装装饰函数本该返回的值,同时做一些额外操作</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">factorial.__name__</span><br></pre></td></tr></table></figure><p>上述实现的 clock 装饰器有几个缺点:不支持关键字参数,而且遮盖了被装饰函数的 <code>__name__</code>, <code>__doc__</code> 属性</p><p>functools.wraps 装饰器把相关属性从 func 复制到 clocked 中,还能正确处理关键字函数</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> functools</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">clock</span><span class="params">(func)</span>:</span></span><br><span class="line"><span class="meta"> @functools.wraps(func)</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">clocked</span><span class="params">(*args, **kwargs)</span>:</span></span><br><span class="line"> t0 = time.perf_counter()</span><br><span class="line"> result = func(*args, **kwargs)</span><br><span class="line"> elapsed = time.perf_counter() - t0</span><br><span class="line"> name = func.__name__</span><br><span class="line"> arg_lst = []</span><br><span class="line"> <span class="keyword">if</span> args:</span><br><span class="line"> arg_str = <span class="string">', '</span>.join(repr(arg) <span class="keyword">for</span> arg <span class="keyword">in</span> args)</span><br><span class="line"> <span class="keyword">if</span> kwargs:</span><br><span class="line"> pairs = [<span class="string">'%s=%s'</span> % (k, w) <span class="keyword">for</span> k, w <span class="keyword">in</span> sorted(kwargs.items())]</span><br><span class="line"> arg_lst.append(<span class="string">', '</span>.join(pairs))</span><br><span class="line"> arg_str = <span class="string">', '</span>.join(arg_lst)</span><br><span class="line"> print(<span class="string">'[%0.8fs] %s(%s) -> % r'</span> %(elapsed, name, arg_str, result))</span><br><span class="line"> <span class="keyword">return</span> result</span><br><span class="line"> <span class="keyword">return</span> clocked</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__==<span class="string">'__main__'</span>:</span><br><span class="line"> print(<span class="string">'*'</span> * <span class="number">40</span>, <span class="string">'Calling snooze(.123)'</span>)</span><br><span class="line"> snooze(<span class="number">.123</span>)</span><br><span class="line"> print(<span class="string">'*'</span> * <span class="number">40</span>, <span class="string">'Calling factorial(6)'</span>)</span><br><span class="line"> print(<span class="string">'6! ='</span>, factorial(<span class="number">6</span>))</span><br></pre></td></tr></table></figure><h2 id="标准库中的装饰器"><a class="markdownIt-Anchor" href="#标准库中的装饰器"></a> 标准库中的装饰器</h2><p>Python 内置的三个装饰器分别为 <code>property</code>, <code>classmethod</code> 和 <code>staticmethod</code>.</p><p>但 Python 内置的库中,有两个装饰器很常用,分别为 <code>functools.lru_cache</code> 和 <a href="https://docs.python.org/3/library/functools.html#functools.singledispatch" target="_blank" rel="noopener"><code>functools.singledispatch</code></a>.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@clock</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">fibonacci</span><span class="params">(n)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> n < <span class="number">2</span>:</span><br><span class="line"> <span class="keyword">return</span> n</span><br><span class="line"> <span class="keyword">return</span> fibonacci(n<span class="number">-2</span>) + fibonacci(n<span class="number">-1</span>)</span><br><span class="line"></span><br><span class="line">print(fibonacci(<span class="number">6</span>))</span><br><span class="line"></span><br><span class="line"><span class="meta">@functools.lru_cache() # () 是因为 lru_cache 可以接受配置参数</span></span><br><span class="line"><span class="comment"># functools.lru_cache(maxsize=128, typed=False)</span></span><br><span class="line"><span class="meta">@clock # 叠放装饰器</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">fibonacci</span><span class="params">(n)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> n < <span class="number">2</span>:</span><br><span class="line"> <span class="keyword">return</span> n</span><br><span class="line"> <span class="keyword">return</span> fibonacci(n<span class="number">-2</span>) + fibonacci(n<span class="number">-1</span>)</span><br><span class="line"></span><br><span class="line">print(fibonacci(<span class="number">6</span>))</span><br></pre></td></tr></table></figure><h2 id="单分派反函数"><a class="markdownIt-Anchor" href="#单分派反函数"></a> 单分派反函数</h2><p><em>Python</em> 不支持重载方法或函数,所以我们不能使用不同的签名定义 htmlize 的辩题,也无法使用不同的方式处理不同的数据类型。</p><p>一种常见的方法是把 htmlize 编程一个分派函数,使用 if-elif-else 分别调用专门的函数。但这样不便于模块的拓展,而且臃肿</p><p>functoos.singledispatch 装饰器可以把整体方案拆分成多个模块,甚至可以为你无法修改的类提供专门的函数。<br />使用 functoos.singledispatch 装饰的普通函数会变成反函数。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 生成 HTML 显示不同类型的 python 对象</span></span><br><span class="line"><span class="keyword">import</span> html</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">htmlize</span><span class="params">(obj)</span>:</span></span><br><span class="line"> content = html.escape(repr(obj))</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'<pre>{}</pre>'</span>.format(content)</span><br><span class="line"></span><br><span class="line"><span class="comment"># htmlize({1, 2, 3})</span></span><br><span class="line"><span class="comment"># htmlize(abs)</span></span><br><span class="line"><span class="comment"># htmlize('hwimich & Co.\n- a game')</span></span><br><span class="line"><span class="comment"># htmlize(42)</span></span><br><span class="line"><span class="comment"># print(htmlize(['alpha', 66, {3, 2, 1}]))</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> singledispatch</span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> abc </span><br><span class="line"><span class="keyword">import</span> numbers</span><br><span class="line"></span><br><span class="line"><span class="meta">@singledispatch # 标记处理 object 类型的基函数</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">htmlize</span><span class="params">(obj)</span>:</span></span><br><span class="line"> content = html.escape(repr(obj))</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'<pre>{}</pre>'</span>.format(content)</span><br><span class="line"></span><br><span class="line"><span class="meta">@htmlize.register(str)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_</span><span class="params">(text)</span>:</span></span><br><span class="line"> content = html.escape(text).replace(<span class="string">'\n'</span>, <span class="string">'<br>\n'</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'<p>{0}</p>'</span>.format(content)</span><br><span class="line"></span><br><span class="line"><span class="meta">@htmlize.register(numbers.Integral) # Integral 是 int 的虚拟超类</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_</span><span class="params">(n)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">'<pre>{0} (0x{0:x})</pre>'</span>.format(n)</span><br><span class="line"></span><br><span class="line"><span class="meta">@htmlize.register(tuple)</span></span><br><span class="line"><span class="meta">@htmlize.register(abc.MutableSequence)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_</span><span class="params">(seq)</span>:</span></span><br><span class="line"> inner = <span class="string">'</li>\n<li>'</span>.join(htmlize(item) <span class="keyword">for</span> item <span class="keyword">in</span> seq)</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'<ul>\n<li>'</span> + inner + <span class="string">'</li>\n<ul>'</span></span><br><span class="line"></span><br><span class="line">htmlize({<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>})</span><br><span class="line">htmlize(abs)</span><br><span class="line">htmlize(<span class="string">'hwimich & Co.\n- a game'</span>)</span><br><span class="line">htmlize(<span class="number">42</span>)</span><br><span class="line">print(htmlize([<span class="string">'alpha'</span>, <span class="number">66</span>, {<span class="number">3</span>, <span class="number">2</span>, <span class="number">1</span>}]))</span><br></pre></td></tr></table></figure><p>只要可能,注册的专门函数应该处理抽象基类(numbers.Integral, abc.MutableSequence), 不要处理具体实现(int,list)</p><p>这样代码支持的兼容类型更广泛。</p><p>使用 singledispatch 可以在系统的任何地方和任何模块注册专门函数。</p><h2 id="叠放装饰器"><a class="markdownIt-Anchor" href="#叠放装饰器"></a> 叠放装饰器</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@d1</span></span><br><span class="line"><span class="meta">@d2</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">func</span><span class="params">()</span>:</span></span><br><span class="line"> <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 等同于</span></span><br><span class="line">func = d1(d2(func))</span><br></pre></td></tr></table></figure><h2 id="参数化装饰器"><a class="markdownIt-Anchor" href="#参数化装饰器"></a> 参数化装饰器</h2><p>为了方便理解,可以把参数化装饰器看成一个函数:这个函数接受任意参数,返回一个装饰器(参数为 func 的另一个函数)。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 参数化的注册装饰器</span></span><br><span class="line">registry = set()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 这是一个工厂函数,用来构建装饰器函数</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">register</span><span class="params">(active=True)</span>:</span></span><br><span class="line"> <span class="comment"># decorate 是真正的装饰器</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">decorate</span><span class="params">(func)</span>:</span></span><br><span class="line"> print(<span class="string">'running register(active=%s)->decorate(%s)'</span> % (active, func))</span><br><span class="line"> <span class="keyword">if</span> active:</span><br><span class="line"> registry.add(func)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> registry.discard(func) </span><br><span class="line"> <span class="keyword">return</span> func</span><br><span class="line"> <span class="keyword">return</span> decorate</span><br><span class="line"></span><br><span class="line"><span class="meta">@register(active=False)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">f1</span><span class="params">()</span>:</span></span><br><span class="line"> print(<span class="string">'running f1()'</span>)</span><br><span class="line"> </span><br><span class="line"><span class="meta">@register()</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">f2</span><span class="params">()</span>:</span></span><br><span class="line"> print(<span class="string">'running f2()'</span>)</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">f3</span><span class="params">()</span>:</span></span><br><span class="line"> print(<span class="string">'running f3()'</span>)</span><br><span class="line"> </span><br><span class="line">f1()</span><br><span class="line">f2()</span><br><span class="line">f3()</span><br><span class="line">register()(f3)</span><br><span class="line">registry</span><br><span class="line">register(active=<span class="literal">False</span>)(f2)</span><br></pre></td></tr></table></figure><h2 id="参数化-clock-装饰器"><a class="markdownIt-Anchor" href="#参数化-clock-装饰器"></a> 参数化 clock 装饰器</h2><p>为 clock 装饰器添加一个功能,让用户传入一个格式化字符串,控制被装饰函数的输出。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">DEFAULT_FMT = <span class="string">'[{elapsed:0.8f}s] {name}({args}) -> {result}'</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">clock</span><span class="params">(fmt=DEFAULT_FMT)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">decorate</span><span class="params">(func)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">clocked</span><span class="params">(*_args)</span>:</span></span><br><span class="line"> t0 = time.time()</span><br><span class="line"> _result = func(*_args)</span><br><span class="line"> elapsed = time.time() - t0</span><br><span class="line"> name = func.__name__</span><br><span class="line"> args = <span class="string">', '</span>.join(repr(arg) <span class="keyword">for</span> arg <span class="keyword">in</span> _args)</span><br><span class="line"> result = repr(_result)</span><br><span class="line"> print(fmt.format(**locals()))</span><br><span class="line"> <span class="keyword">return</span> _result</span><br><span class="line"> <span class="keyword">return</span> clocked</span><br><span class="line"> <span class="keyword">return</span> decorate</span><br><span class="line"></span><br><span class="line"><span class="comment"># @clock()</span></span><br><span class="line"><span class="comment"># @clock('{name}: {elapsed}s')</span></span><br><span class="line"><span class="meta">@clock('{name}{args} dt={elapsed:0.3f}s')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">snooze</span><span class="params">(seconds)</span>:</span></span><br><span class="line"> time.sleep(seconds)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">3</span>):</span><br><span class="line"> snooze(<span class="number">.123</span>)</span><br></pre></td></tr></table></figure><h2 id="小结"><a class="markdownIt-Anchor" href="#小结"></a> 小结</h2><p>本节先编写了一个没有内部函数的 @register 装饰器。 然后实现了有两层嵌套函数的参数化装饰器 @clock()</p><p>参数化装饰器基本上设计至少两层嵌套函数。</p><p>标准库 functools 提供两个非常重要的装饰器 @lru_cache() 和 @singledispatch</p><p>理解装饰器,需要区分导入时、运行时、变量作用域,闭包等。</p><p>推荐阅读:<a href="http://decorator.readthedocs.io/en/latest/" target="_blank" rel="noopener">decorator 第三方库</a></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>有很多人抱怨,把这个特性命名为“装饰器”不好。主要原因是,这个名称与 GoF 书使用的不一致。<strong>装饰器</strong>这个名称可能更适合在编译器领域使用,因为它会遍历并注解语法书。<br />
—“PEP 318 — Decorat
</summary>
<category term="Python" scheme="https://yuechuanx.top/categories/Python/"/>
<category term="python" scheme="https://yuechuanx.top/tags/python/"/>
</entry>
<entry>
<title><流畅的 Python> 一等函数</title>
<link href="https://yuechuanx.top/Python/fluent-python-notes-chap-05/"/>
<id>https://yuechuanx.top/Python/fluent-python-notes-chap-05/</id>
<published>2020-04-22T08:38:39.000Z</published>
<updated>2020-04-22T08:38:39.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>不管别人怎么说或怎么想,我从未觉得 Python 受到来自函数式语言的太多影响。我非常熟悉命令式语言,如 C 和 Algol 68,虽然我把函数定为一等对象,但是我并不把 Python 当作函数式编程语言。<br />—— Guido van Rossum: Python 仁慈的独裁者</p></blockquote><p>在 Python 中,函数是一等对象。<br />编程语言理论家把“一等对象”定义为满足下述条件的程序实体:</p><ul><li>在运行时创建</li><li>能赋值给变量或数据结构中的元素</li><li>能作为参数传给函数</li><li>能作为函数的返回结果</li></ul><h2 id="函数即为对象"><a class="markdownIt-Anchor" href="#函数即为对象"></a> 函数即为对象</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">factorial</span><span class="params">(n)</span>:</span></span><br><span class="line"> <span class="string">"""returns n!"""</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span> <span class="keyword">if</span> n < <span class="number">2</span> <span class="keyword">else</span> n * factorial(n<span class="number">-1</span>)</span><br><span class="line"></span><br><span class="line">factorial(<span class="number">42</span>)</span><br><span class="line">factorial.__doc__</span><br><span class="line">type(factorial)</span><br></pre></td></tr></table></figure><p>通过 <code>type(factorial)</code> 可以看到 <code>function</code> 是一种类型,或者说,函数也是对象,可以通过<code>__doc__</code> 去访问它的属性。</p><p>那么作为对象的函数,也能作为参数被传递。函数式风格编程也基于此</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">fact = factorial</span><br><span class="line">fact</span><br><span class="line">fact(<span class="number">5</span>)</span><br><span class="line">map(factorial, range(<span class="number">11</span>))</span><br><span class="line">list(map(fact, range(<span class="number">11</span>)))</span><br></pre></td></tr></table></figure><h2 id="高阶函数"><a class="markdownIt-Anchor" href="#高阶函数"></a> 高阶函数</h2><p>输入或者输出是函数的即为<em>高阶函数</em>(higher order function)。例如:<code>map</code>, <code>sorted</code>。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">fruits = [<span class="string">'strawberry'</span>, <span class="string">'fig'</span>, <span class="string">'apple'</span>, <span class="string">'cherry'</span>, <span class="string">'raspberry'</span>, <span class="string">'banana'</span>]</span><br><span class="line"><span class="comment"># function len() as key</span></span><br><span class="line">sorted(fruits, key=len)</span><br></pre></td></tr></table></figure><h2 id="map-filter-和-reduce-的替代品"><a class="markdownIt-Anchor" href="#map-filter-和-reduce-的替代品"></a> map、filter 和 reduce 的替代品</h2><p>函数式语言通常提供 <code>map</code>、<code>filter</code> 和 <code>reduce</code> 三个高阶函数。<br />在 <em>Python</em> 中引入了列表推导和生成式表达式,可以替代它们且更容易阅读。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">list(map(fact, range(<span class="number">6</span>)))</span><br><span class="line">[fact(n) <span class="keyword">for</span> n <span class="keyword">in</span> range(<span class="number">6</span>)]</span><br><span class="line">list(map(factorial, filter(<span class="keyword">lambda</span> n : n % <span class="number">2</span>, range(<span class="number">6</span>))))</span><br><span class="line">[factorial(n) <span class="keyword">for</span> n <span class="keyword">in</span> range(<span class="number">6</span>) <span class="keyword">if</span> n % <span class="number">2</span>]</span><br></pre></td></tr></table></figure><p><code>map</code> 和 <code>filter</code> 返回生成器,可用生成器表达式替代<br /><code>reduce</code> 常用求和,目前最好使用 <code>sum</code> 替代</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> reduce</span><br><span class="line"><span class="keyword">from</span> operator <span class="keyword">import</span> add</span><br><span class="line"></span><br><span class="line">reduce(add, range(<span class="number">100</span>))</span><br><span class="line">sum(range(<span class="number">100</span>))</span><br></pre></td></tr></table></figure><p><code>sum</code> 和 <code>reduce</code> 把操作连续应用在序列元素上,得到返回值</p><p><code>all(iterable)</code>, <code>any(iterable)</code> 也是规约函数</p><ul><li><code>all(iterable)</code>: 每个元素为真,返回真</li><li><code>any(iterable)</code>: 存在一个元素为真,返回真</li></ul><h2 id="匿名函数"><a class="markdownIt-Anchor" href="#匿名函数"></a> 匿名函数</h2><p><em>Python</em> 支持 <em>lambda</em> 表达式。 它是函数对象,在句法上被限制只能用存表达式。</p><p>参数列表中最适合使用匿名函数。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 根据单词末尾字符排序</span></span><br><span class="line"></span><br><span class="line">fruits = [<span class="string">'strawberry'</span>, <span class="string">'fig'</span>, <span class="string">'apple'</span>, <span class="string">'cherry'</span>, <span class="string">'raspberry'</span>, <span class="string">'banana'</span>]</span><br><span class="line">sorted(fruits, key=<span class="keyword">lambda</span> word: word[::<span class="number">-1</span>])</span><br></pre></td></tr></table></figure><p>Python 的可调用对象</p><ul><li>用户定义的函数:使用 <code>def</code> 或 <code>lambda</code> 创建</li><li>内置函数:如 <code>len</code> 或 <code>time.strfttime</code></li><li>内置方法:如 <code>dict.get</code></li><li>类:先调用 <code>__new__</code> 创建实例,再对实例运行 <code>__init__</code> 方法</li><li>类的实例:如果类上定义了 <code>__call__</code> 方法,则实例可以作为函数调用</li><li>生成器函数:使用 yield 关键字的函数或方法,调用生成器函数会返回生成器对象</li></ul><p>判断对象是否能调用,使用内置的 <code>callable()</code> 函数</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">abs, str, <span class="number">13</span></span><br><span class="line">[callable(obj) <span class="keyword">for</span> obj <span class="keyword">in</span> (abs, str, <span class="number">13</span>)]</span><br></pre></td></tr></table></figure><h2 id="用户定义的可调用类型"><a class="markdownIt-Anchor" href="#用户定义的可调用类型"></a> 用户定义的可调用类型</h2><p>任何 <em>Python</em> 对象都可以表现得像函数,只需实现实例方法 <code>__call__</code></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> random </span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BingoCage</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, items)</span>:</span></span><br><span class="line"> self._items = list(items)</span><br><span class="line"> random.shuffle(self._items)</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">pick</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="keyword">return</span> self._items.pop()</span><br><span class="line"> <span class="keyword">except</span> IndexError:</span><br><span class="line"> <span class="keyword">raise</span> LookupError(<span class="string">'pick from empty BingoCage'</span>)</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__call__</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> self.pick()</span><br><span class="line"> </span><br><span class="line">bingo = BingoCage(range(<span class="number">3</span>))</span><br><span class="line">bingo.pick()</span><br><span class="line">bingo()</span><br><span class="line">callable(bingo)</span><br></pre></td></tr></table></figure><p>实现 <code>__call__</code> 方法的类是创建函数类对象的简便方式。<br />函数类对象有自己的状态,即为实例变量。装饰器函数也可以有.</p><h2 id="函数内省"><a class="markdownIt-Anchor" href="#函数内省"></a> 函数内省</h2><p>内省(introspection)可以查看函数内部的细节,函数有许多属性。使用 dir 函数可以查看,或使用 <strong>code</strong> 属性</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">dir(factorial)</span><br><span class="line"><span class="comment"># factorial.__code__.co_varnames</span></span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># eg:5-9</span></span><br><span class="line"><span class="comment"># 列出常规对象没有而函数有的属性</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">C</span>:</span> <span class="keyword">pass</span></span><br><span class="line">obj = C()</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">func</span><span class="params">()</span>:</span> <span class="keyword">pass</span></span><br><span class="line">sorted(set(dir(func)) - set(dir(obj)))</span><br></pre></td></tr></table></figure><p>函数属性说明<br />// 插入表格</p><h2 id="从定位参数到仅限关键字参数"><a class="markdownIt-Anchor" href="#从定位参数到仅限关键字参数"></a> 从定位参数到仅限关键字参数</h2><p>本节讨论 python 参数处理机制。py3 提供了仅限关键字参数(keyword-only argument)<br />调用函数使用 * 和 ** 展开可迭代对象。</p><ul><li>positional argument 位置参数</li><li>keyword-only argument 仅限关键字参数</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">tag</span><span class="params">(name, *content, cls=None, **attrs)</span>:</span></span><br><span class="line"> <span class="string">"""生成一个或多个 HTML 标签"""</span></span><br><span class="line"> <span class="keyword">if</span> cls <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line"> attrs[<span class="string">'class'</span>] = cls</span><br><span class="line"> <span class="keyword">if</span> attrs:</span><br><span class="line"> attr_str = <span class="string">''</span>.join(<span class="string">' %s="%s"'</span> % (attr, value)</span><br><span class="line"> <span class="keyword">for</span> attr, value <span class="keyword">in</span> sorted(attrs.items()))</span><br><span class="line"> <span class="keyword">else</span>: </span><br><span class="line"> attr_str = <span class="string">''</span></span><br><span class="line"> <span class="keyword">if</span> content:</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'\n'</span>.join(<span class="string">'<%s%s>%s</%s>'</span> % (name, attr_str, c, name) <span class="keyword">for</span> c <span class="keyword">in</span> content)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'<%s%s />'</span> % (name, attr_str)</span><br><span class="line"> </span><br><span class="line">tag(<span class="string">'br'</span>)</span><br><span class="line">tag(<span class="string">'p'</span>, <span class="string">'hello'</span>)</span><br><span class="line">tag(<span class="string">'p'</span>, <span class="string">'hello'</span>, <span class="string">'world'</span>) <span class="comment"># 'hello', 'world' -> *content</span></span><br><span class="line">tag(<span class="string">'p'</span>, <span class="string">'hello'</span>, id=<span class="number">33</span>) <span class="comment"># id=33 -> **attrs</span></span><br><span class="line">tag(<span class="string">'p'</span>, <span class="string">'hello'</span>, <span class="string">'world'</span>, cls=<span class="string">'sidebar'</span>)</span><br><span class="line">tag(content=<span class="string">'testing'</span>, name=<span class="string">"img"</span>)</span><br><span class="line">my_tag = {</span><br><span class="line"> <span class="string">'name'</span>: <span class="string">'img'</span>,</span><br><span class="line"> <span class="string">'title'</span>: <span class="string">'Sunset Boulevard'</span>,</span><br><span class="line"> <span class="string">'src'</span>: <span class="string">'sunset.jpg'</span>,</span><br><span class="line"> <span class="string">'cls'</span>: <span class="string">'framed'</span></span><br><span class="line">}</span><br><span class="line">tag(**my_tag)</span><br></pre></td></tr></table></figure><p><code>cls</code> 参数只能通过关键字指定,而不能通过位置参数指定。</p><p>定义函数时若只想定仅限关键字参数,要把它放在带有 * 参数后面,如果不想支持数量不定的位置参数,但支持 keyowrd-only, 在函数签名中放一个 *</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">f</span><span class="params">(a, *, b)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> a, b</span><br><span class="line"></span><br><span class="line"><span class="comment"># f(1, 2)</span></span><br><span class="line">f(<span class="number">1</span>, b=<span class="number">2</span>)</span><br></pre></td></tr></table></figure><h2 id="获取关于参数的信息"><a class="markdownIt-Anchor" href="#获取关于参数的信息"></a> 获取关于参数的信息</h2><p>上面提到,函数内省可以查看函数内部信息,通过 HTTP 微框架 Bobo 作为例子来看下</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># eg: 5-12</span></span><br><span class="line"><span class="comment"># hello.py</span></span><br><span class="line"><span class="keyword">import</span> bobo</span><br><span class="line"></span><br><span class="line"><span class="meta">@bobo.query('/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">hello</span><span class="params">(person)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">'Hello %s!'</span> % person</span><br><span class="line"></span><br><span class="line"><span class="comment"># 在环境中执行 bobo -f hello.py, 若运行端口为 http://localhost:8080/</span></span><br><span class="line"><span class="comment"># 没有传入参数</span></span><br><span class="line"><span class="comment"># curl -i http://localhost:8080/</span></span><br><span class="line"><span class="comment"># HTTP/1.0 403 Forbidden</span></span><br><span class="line"><span class="comment"># Date: Wed, 22 Apr 2020 06:23:33 GMT</span></span><br><span class="line"><span class="comment"># Server: WSGIServer/0.2 CPython/3.7.4</span></span><br><span class="line"><span class="comment"># Content-Type: text/html; charset=UTF-8</span></span><br><span class="line"><span class="comment"># Content-Length: 103</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># <html></span></span><br><span class="line"><span class="comment"># <head><title>Missing parameter</title></head></span></span><br><span class="line"><span class="comment"># <body>Missing form variable person</body></span></span><br><span class="line"><span class="comment"># </html></span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 传入参数</span></span><br><span class="line"><span class="comment"># curl -i http://localhost:8080/?person=Jim</span></span><br><span class="line"><span class="comment"># HTTP/1.0 200 OK</span></span><br><span class="line"><span class="comment"># Date: Wed, 22 Apr 2020 06:24:47 GMT</span></span><br><span class="line"><span class="comment"># Server: WSGIServer/0.2 CPython/3.7.4</span></span><br><span class="line"><span class="comment"># Content-Type: text/html; charset=UTF-8</span></span><br><span class="line"><span class="comment"># Content-Length: 10</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Hello Jim!%</span></span><br></pre></td></tr></table></figure><p>Bobo 如何知道函数需要哪个参数呢?</p><p>函数对象有 <code>__defaults__</code> 属性,其值为一个元祖,保存着位置参数和关键字参数的默认值。</p><p>keyword-only 参数默认值保存在 <code>__kwdefaults__</code> 属性中。</p><p>参数的名称在 <code>__code__</code> 属性中,其值为 <em>code</em> 对象的引用。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">clip</span><span class="params">(text, max_len=<span class="number">80</span>)</span>:</span></span><br><span class="line"> <span class="string">"""在 max_len 前后的第一个空格处截断文本"""</span></span><br><span class="line"> end = <span class="literal">None</span></span><br><span class="line"> <span class="keyword">if</span> (len(text)) > max_len:</span><br><span class="line"> space_before = text.rfind(<span class="string">' '</span>, <span class="number">0</span>, max_len)</span><br><span class="line"> <span class="keyword">if</span> space_before >= <span class="number">0</span>:</span><br><span class="line"> end = space_before</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> space_after = text.rfind(<span class="string">' '</span>, max_len)</span><br><span class="line"> <span class="keyword">if</span> space_after >= <span class="number">0</span>:</span><br><span class="line"> end = space_after</span><br><span class="line"> <span class="keyword">if</span> end <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line"> end = len(text)</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> text[:end].rstrip()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">clip.__defaults__</span><br><span class="line"><span class="comment"># clip.__code__</span></span><br><span class="line"><span class="comment"># clip.__code__.co_varnames</span></span><br><span class="line"><span class="comment"># clip.__code__.co_argcount</span></span><br></pre></td></tr></table></figure><p>函数签名信息,参数和默认值是分开的。可以使用 inspect 模块提取这些信息</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> inspect <span class="keyword">import</span> signature</span><br><span class="line"></span><br><span class="line">sig = signature(clip)</span><br><span class="line">sig</span><br><span class="line">str(sig)</span><br><span class="line"><span class="keyword">for</span> name, param <span class="keyword">in</span> sig.parameters.items():</span><br><span class="line"> print(param.kind, <span class="string">':'</span>, name, <span class="string">'='</span>, param.default)</span><br></pre></td></tr></table></figure><p>kind 属性值在 <code>_Parameterkind</code> 类中,列举如下:</p><ul><li>POSTIONAL_OR_KEYWORD</li><li>VAR_POSITIONAL</li><li>VAR_KEYWORD</li><li>KEYWORD-ONLY</li><li>POSITIONAL_ONLY</li></ul><p><em>inspect.Signature</em> 有 <code>bind</code> 方法,可以把任意个参数绑定在签名中的形参上。</p><p>框架可以使用此方法在调用函数前验证参数</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> inspect</span><br><span class="line">sig = inspect.signature(tag)</span><br><span class="line">my_tag = {</span><br><span class="line"> <span class="string">'name'</span>: <span class="string">'img'</span>,</span><br><span class="line"> <span class="string">'title'</span>: <span class="string">'Sunset Boulevard'</span>,</span><br><span class="line"> <span class="string">'src'</span>: <span class="string">'sunset.jpg'</span>,</span><br><span class="line"> <span class="string">'cls'</span>: <span class="string">'framed'</span>}</span><br><span class="line">bound_args = sig.bind(**my_tag)</span><br><span class="line">bound_args</span><br><span class="line"></span><br><span class="line"><span class="keyword">del</span> my_tag[<span class="string">'name'</span>]</span><br><span class="line"><span class="comment"># missing argument error</span></span><br><span class="line">bound_args = sig.bind(**my_tag)</span><br></pre></td></tr></table></figure><p>框架和 IDE 工具可以使用这些信息验证代码</p><h2 id="函数注解annotation"><a class="markdownIt-Anchor" href="#函数注解annotation"></a> 函数注解(annotation)</h2><p>各个参数可以在 : 后添加注解表达式。</p><p>参数有默认值,注解放在参数名和 = 号之间,注解返回值在函数声明末尾添加 -> 和表达式</p><p>注解不会做任何处理,只存储在函数 <code>__annotations__</code> 属性中。</p><p>注解只是元数据,可以供 IDE,框架和装饰器等工具使用</p><p><code>inspect.signature()</code> 函数知道怎么提取注解</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">clip</span><span class="params">(text: str, max_len: <span class="string">'int > 0'</span> = <span class="number">80</span>)</span> -> str:</span></span><br><span class="line"> <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line">clip.__annotations__</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> inspect <span class="keyword">import</span> signature</span><br><span class="line"></span><br><span class="line">sig = signature(clip)</span><br><span class="line">sig.return_annotation</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> param <span class="keyword">in</span> sig.parameters.values():</span><br><span class="line"> note = repr(param.annotation).ljust(<span class="number">13</span>)</span><br><span class="line"> print(note, <span class="string">':'</span>, param.name, <span class="string">'='</span>, param.default)</span><br></pre></td></tr></table></figure><h2 id="支持函数式编程的包"><a class="markdownIt-Anchor" href="#支持函数式编程的包"></a> 支持函数式编程的包</h2><h3 id="operator-模块"><a class="markdownIt-Anchor" href="#operator-模块"></a> operator 模块</h3><p><code>operator</code> 里有很多函数,对应着 Python 中的内置运算符,使用它们可以避免编写很多无趣的 <code>lambda</code> 函数,如:</p><ul><li><code>add</code>: <code>lambda a, b: a + b</code></li><li><code>or_</code>: <code>lambda a, b: a or b</code></li><li><code>itemgetter</code>: <code>lambda a, b: a[b]</code></li><li><code>attrgetter</code>: <code>lambda a, b: getattr(a, b)</code></li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> reduce</span><br><span class="line"><span class="keyword">from</span> operator <span class="keyword">import</span> mul</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">fact</span><span class="params">(n)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> reduce(<span class="keyword">lambda</span> a, b: a*b, range(<span class="number">1</span>, n+<span class="number">1</span>))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">fact</span><span class="params">(n)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> reduce(mul, range(<span class="number">1</span>, n+<span class="number">1</span>))</span><br></pre></td></tr></table></figure><p>还有一类函数,能替代从序列中取出或读取元素属性的 <em>lambda</em> 表达式。如 <code>itemgetter</code>,<code>attrgetter</code></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">metro_data = [</span><br><span class="line"> (<span class="string">'Tokyo'</span>, <span class="string">'JP'</span>, <span class="number">36.933</span>, (<span class="number">35.689722</span>, <span class="number">139.691667</span>)), <span class="comment"># <1></span></span><br><span class="line"> (<span class="string">'Delhi NCR'</span>, <span class="string">'IN'</span>, <span class="number">21.935</span>, (<span class="number">28.613889</span>, <span class="number">77.208889</span>)),</span><br><span class="line"> (<span class="string">'Mexico City'</span>, <span class="string">'MX'</span>, <span class="number">20.142</span>, (<span class="number">19.433333</span>, <span class="number">-99.133333</span>)),</span><br><span class="line"> (<span class="string">'New York-Newark'</span>, <span class="string">'US'</span>, <span class="number">20.104</span>, (<span class="number">40.808611</span>, <span class="number">-74.020386</span>)),</span><br><span class="line"> (<span class="string">'Sao Paulo'</span>, <span class="string">'BR'</span>, <span class="number">19.649</span>, (<span class="number">-23.547778</span>, <span class="number">-46.635833</span>)),</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> operator <span class="keyword">import</span> itemgetter</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> city <span class="keyword">in</span> sorted(metro_data, key=<span class="keyword">lambda</span> fields: fields[<span class="number">1</span>]):</span><br><span class="line"> print(city)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> city <span class="keyword">in</span> sorted(metro_data, key=itemgetter(<span class="number">1</span>)):</span><br><span class="line"> print(city)</span><br><span class="line"></span><br><span class="line"><span class="comment"># itemgetter 返回提取的值构成的元祖,可以用来提取指定字段或调整元祖顺序</span></span><br><span class="line">cc_name = itemgetter(<span class="number">1</span>, <span class="number">0</span>)</span><br><span class="line"><span class="keyword">for</span> city <span class="keyword">in</span> metro_data:</span><br><span class="line"> print(cc_name(city))</span><br></pre></td></tr></table></figure><p>itemgetter 使用 <code>[]</code> 运算符,因为它不仅支持序列,还支持映射和任何实现 <code>__getitem__</code> 的类</p><p>attrgetter 作用相似,它创建的函数根据名称提取对象的属性。包含 <code>.</code> 的,会进入到嵌套对象提取属性</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> namedtuple</span><br><span class="line"></span><br><span class="line">LatLong = namedtuple(<span class="string">'Latlong'</span>, <span class="string">'lat long'</span>)</span><br><span class="line">Metorpolis = namedtuple(<span class="string">'Metorpolis'</span>, <span class="string">'name cc pop coord'</span>)</span><br><span class="line"></span><br><span class="line">metro_areas = [Metorpolis(name, cc, pop, LatLong(lat, long)) </span><br><span class="line"> <span class="keyword">for</span> name, cc, pop, (lat, long) <span class="keyword">in</span> metro_data]</span><br><span class="line"></span><br><span class="line">metro_areas[<span class="number">0</span>]</span><br><span class="line">metro_areas[<span class="number">0</span>].coord.lat</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> operator <span class="keyword">import</span> attrgetter</span><br><span class="line">name_lat = attrgetter(<span class="string">'name'</span>, <span class="string">'coord.lat'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> city <span class="keyword">in</span> sorted(metro_areas, key=attrgetter(<span class="string">'coord.lat'</span>)):</span><br><span class="line"> print(name_lat(city))</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> operator</span><br><span class="line">[name <span class="keyword">for</span> name <span class="keyword">in</span> dir(operator) <span class="keyword">if</span> <span class="keyword">not</span> name.startswith(<span class="string">'_'</span>)]</span><br></pre></td></tr></table></figure><p><em>operator</em> 模块的函数可以通过 <code>dir(operator)</code> 查看。</p><p>介绍 methodcaller, 它的作用与前两个函数相似,它创建的函数会在对象调用参数指定的方法</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> operator <span class="keyword">import</span> methodcaller</span><br><span class="line"></span><br><span class="line">s = <span class="string">'The time has come'</span></span><br><span class="line">upcase = methodcaller(<span class="string">'upper'</span>)</span><br><span class="line">upcase(s)</span><br><span class="line">hiphenate = methodcaller(<span class="string">'replace'</span>, <span class="string">' '</span>, <span class="string">'-'</span>)</span><br><span class="line">hiphenate(s)</span><br></pre></td></tr></table></figure><h2 id="使用-functoolspartial-冻结参数"><a class="markdownIt-Anchor" href="#使用-functoolspartial-冻结参数"></a> 使用 functools.partial 冻结参数</h2><p><code>functools</code> 最常用的函数有 <code>reduce</code>,之前已经介绍过。余下函数中最有用的是 <code>partial</code> 及其变体 <code>partialmethod</code></p><p>它的作用是:把原函数某些参数固定。</p><p><code>partial</code> 第一个函数是可调用对象,后面跟任意个位置参数和关键字参数</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> operator <span class="keyword">import</span> mul</span><br><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> partial</span><br><span class="line"></span><br><span class="line">triple = partial(mul, <span class="number">3</span>)</span><br><span class="line">triple(<span class="number">7</span>)</span><br><span class="line"></span><br><span class="line">list(map(triple, range(<span class="number">1</span>, <span class="number">10</span>)))</span><br><span class="line"></span><br><span class="line">picture = partial(tag, <span class="string">'img'</span>, cls=<span class="string">'pic-frame'</span>)</span><br><span class="line">picture(src=<span class="string">'wumpus.jepg'</span>)</span><br><span class="line">picture</span><br><span class="line">picture.func</span><br><span class="line">picture.args</span><br><span class="line">picture.keywords</span><br></pre></td></tr></table></figure><p>functoos.partialmethod 作用与 partial 一样,不过适用于处理方法的</p><h2 id="小结"><a class="markdownIt-Anchor" href="#小结"></a> 小结</h2><p>探讨 <em>Python</em> 函数的一等特性。意味着可以把函数赋值给变量,传入其他函数,存储于数据结构中,以及访问函数属性。</p><p>高阶函数是函数式编程的重要组成。</p><p><code>Python</code> 的可调用对象: 7种</p><p>函数及其注解有丰富的特性。可通过 <code>inspect</code> 模块读取</p><p>最后介绍了 <code>operator</code> 模块中的一些函数,可以替换掉功能有限的 <em>lambda</em> 表达式。</p>]]></content>
<summary type="html">
<blockquote>
<p>不管别人怎么说或怎么想,我从未觉得 Python 受到来自函数式语言的太多影响。我非常熟悉命令式语言,如 C 和 Algol 68,虽然我把函数定为一等对象,但是我并不把 Python 当作函数式编程语言。<br />
—— Guido van R
</summary>
<category term="Python" scheme="https://yuechuanx.top/categories/Python/"/>
<category term="python" scheme="https://yuechuanx.top/tags/python/"/>
</entry>
<entry>
<title>Zen of Python(Python之禅)</title>
<link href="https://yuechuanx.top/Python/zen-of-python/"/>
<id>https://yuechuanx.top/Python/zen-of-python/</id>
<published>2020-04-15T08:54:29.000Z</published>
<updated>2020-04-15T08:54:29.000Z</updated>
<content type="html"><![CDATA[<p>Beautiful is better than ugly. (优美比丑陋好)</p><p>Explicit is better than implicit.(清晰比晦涩好)</p><p>Simple is better than complex.(简单比复杂好)</p><p>Complex is better than complicated.(复杂比错综复杂好)</p><p>Flat is better than nested.(扁平比嵌套好)</p><p>Sparse is better than dense.(稀疏比密集好)</p><p>Readability counts.(可读性很重要)</p><p>Special cases aren’t special enough to break the rules.(特殊情况也不应该违反这些规则)</p><p>Although practicality beats purity.(但现实往往并不那么完美)</p><p>Errors should never pass silently.(异常不应该被静默处理)</p><p>Unless explicitly silenced.(除非你希望如此)</p><p>In the face of ambiguity, refuse the temptation to guess.(遇到模棱两可的地方,不要胡乱猜测)</p><p>There should be one-- and preferably only one --obvious way to do it.(肯定有一种通常也是唯一一种最佳的解决方案)</p><p>Although that way may not be obvious at first unless you’re Dutch.(虽然这种方案并不是显而易见的,因为你不是那个荷兰人<sup>这里指的是Python之父Guido</sup>)</p><p>Now is better than never.(现在开始做比不做好)</p><p>Although never is often better than *right* now.(不做比盲目去做好<sup>极限编程中的YAGNI原则</sup>)</p><p>If the implementation is hard to explain, it’s a bad idea.(如果一个实现方案难于理解,它就不是一个好的方案)</p><p>If the implementation is easy to explain, it may be a good idea.(如果一个实现方案易于理解,它很有可能是一个好的方案)</p><p>Namespaces are one honking great idea – let’s do more of those!(命名空间非常有用,我们应当多加利用)</p>]]></content>
<summary type="html">
<p>Beautiful is better than ugly. (优美比丑陋好)</p>
<p>Explicit is better than implicit.(清晰比晦涩好)</p>
<p>Simple is better than complex.(简单比复杂好)</p
</summary>
<category term="Python" scheme="https://yuechuanx.top/categories/Python/"/>
</entry>
<entry>
<title>Django 导出和导入数据</title>
<link href="https://yuechuanx.top/django-dumpdata-and-loaddata/"/>
<id>https://yuechuanx.top/django-dumpdata-and-loaddata/</id>
<published>2020-04-13T10:57:03.000Z</published>
<updated>2020-04-13T10:57:03.000Z</updated>
<content type="html"><![CDATA[<p><code>dumpdata</code> 命令:</p><p>它可以用来备份(导出)模型实例或整个数据库</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">./manage.py dumpdata --<span class="built_in">help</span></span><br><span class="line">usage: manage.py dumpdata [-h] [--format FORMAT] [--indent INDENT] [--database DATABASE] [-e EXCLUDE] [--natural-foreign] [--natural-primary] [-a] [--pks PRIMARY_KEYS]</span><br><span class="line"> [-o OUTPUT] [--version] [-v {0,1,2,3}] [--settings SETTINGS] [--pythonpath PYTHONPATH] [--traceback] [--no-color] [--force-color]</span><br><span class="line"> [app_label[.ModelName] [app_label[.ModelName] ...]]</span><br><span class="line"></span><br><span class="line">Output the contents of the database as a fixture of the given format (using each model<span class="string">'s default manager unless --all is specified).</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">positional arguments:</span></span><br><span class="line"><span class="string"> app_label[.ModelName]</span></span><br><span class="line"><span class="string"> Restricts dumped data to the specified app_label or app_label.ModelName.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">optional arguments:</span></span><br><span class="line"><span class="string"> -h, --help show this help message and exit</span></span><br><span class="line"><span class="string"> --format FORMAT Specifies the output serialization format for fixtures.</span></span><br><span class="line"><span class="string"> --indent INDENT Specifies the indent level to use when pretty-printing output.</span></span><br><span class="line"><span class="string"> --database DATABASE Nominates a specific database to dump fixtures from. Defaults to the "default" database.</span></span><br><span class="line"><span class="string"> -e EXCLUDE, --exclude EXCLUDE</span></span><br><span class="line"><span class="string"> An app_label or app_label.ModelName to exclude (use multiple --exclude to exclude multiple apps/models).</span></span><br><span class="line"><span class="string"> --natural-foreign Use natural foreign keys if they are available.</span></span><br><span class="line"><span class="string"> --natural-primary Use natural primary keys if they are available.</span></span><br><span class="line"><span class="string"> -a, --all Use Django'</span>s base manager to dump all models stored <span class="keyword">in</span> the database, including those that would otherwise be filtered or modified by a custom manager.</span><br><span class="line"> --pks PRIMARY_KEYS Only dump objects with given primary keys. Accepts a comma-separated list of keys. This option only works when you specify one model.</span><br><span class="line"> -o OUTPUT, --output OUTPUT</span><br><span class="line"> Specifies file to <span class="built_in">which</span> the output is written.</span><br><span class="line"> --version show program<span class="string">'s version number and exit</span></span><br><span class="line"><span class="string"> -v {0,1,2,3}, --verbosity {0,1,2,3}</span></span><br><span class="line"><span class="string"> Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output</span></span><br><span class="line"><span class="string"> --settings SETTINGS The Python path to a settings module, e.g. "myproject.settings.main". If this isn'</span>t provided, the DJANGO_SETTINGS_MODULE environment variable will be</span><br><span class="line"> used.</span><br><span class="line"> --pythonpath PYTHONPATH</span><br><span class="line"> A directory to add to the Python path, e.g. <span class="string">"/home/djangoprojects/myproject"</span>.</span><br><span class="line"> --traceback Raise on CommandError exceptions</span><br><span class="line"> --no-color Don<span class="string">'t colorize the command output.</span></span><br><span class="line"><span class="string"> --force-color Force colorization of the command output.</span></span><br></pre></td></tr></table></figure><ul><li><p>基础数据库导出</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./manage.py dumpdata > db.json</span><br></pre></td></tr></table></figure><p>这会导出整个数据库到 <em>db.json</em></p></li><li><p>备份指定的 <em>app</em></p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./manage.py dumpdata admin > admin.json</span><br></pre></td></tr></table></figure><p>这会导出 <em>admin</em> 应用的内容到 <em>admin.json</em></p></li><li><p>备份指定的数据表</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./manage.py dumpdata admin.logentry > logentry.json</span><br></pre></td></tr></table></figure><p>这会导出 <em>admin.logentry</em> 数据表的所有数据</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./manage.py dumpdata auth.user > user.json</span><br></pre></td></tr></table></figure><p>这会导出 <em>auth.user</em> 数据表的所有数据</p></li><li><p><code>dumpdata —exclude</code></p><p><code>—exclude</code> 选项用来指定无需被导出的 <em>apps/tables</em></p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./manage.py dumpdata --exclude auth.permission > db.json</span><br></pre></td></tr></table></figure><p>这会导出整个数据库,但不包括 <em>auth.permisson</em></p></li><li><p><code>dumpdata —intent</code></p><p>默认情况,<code>dumpdata</code> 的输出会挤在同一行,可读性很差。使用 —indent 可以设定缩进美化输出</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./manage.py dumpdata auth.user --indent 2 > user.json</span><br></pre></td></tr></table></figure><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line">{</span><br><span class="line"> <span class="attr">"model"</span>: <span class="string">"auth.user"</span>,</span><br><span class="line"> <span class="attr">"pk"</span>: <span class="number">1</span>,</span><br><span class="line"> <span class="attr">"fields"</span>: {</span><br><span class="line"> <span class="attr">"password"</span>: <span class="string">"pbkdf2_sha256$150000$i8oET981EnSJ$d2RCpfY76gFHbwUs1HekSK+pOLYMJFcJ1wFcuyf6R28="</span>,</span><br><span class="line"> <span class="attr">"last_login"</span>: <span class="string">"2020-04-13T09:21:34.639Z"</span>,</span><br><span class="line"> <span class="attr">"is_superuser"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"username"</span>: <span class="string">"xiao"</span>,</span><br><span class="line"> <span class="attr">"first_name"</span>: <span class="string">""</span>,</span><br><span class="line"> <span class="attr">"last_name"</span>: <span class="string">""</span>,</span><br><span class="line"> <span class="attr">"email"</span>: <span class="string">"[email protected]"</span>,</span><br><span class="line"> <span class="attr">"is_staff"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"is_active"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"date_joined"</span>: <span class="string">"2020-04-13T08:59:01.310Z"</span>,</span><br><span class="line"> <span class="attr">"groups"</span>: [],</span><br><span class="line"> <span class="attr">"user_permissions"</span>: []</span><br><span class="line"> }</span><br><span class="line">},</span><br><span class="line">{</span><br><span class="line"> <span class="attr">"model"</span>: <span class="string">"auth.user"</span>,</span><br><span class="line"> <span class="attr">"pk"</span>: <span class="number">2</span>,</span><br><span class="line"> <span class="attr">"fields"</span>: {</span><br><span class="line"> <span class="attr">"password"</span>: <span class="string">"pbkdf2_sha256$150000$PgBKh5sMAE1y$xdFkYi+gprF1v2rlOyw2OOsRn87zSeTVLJ9dGfoXzIw="</span>,</span><br><span class="line"> <span class="attr">"last_login"</span>: <span class="literal">null</span>,</span><br><span class="line"> <span class="attr">"is_superuser"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"username"</span>: <span class="string">"qa"</span>,</span><br><span class="line"> <span class="attr">"first_name"</span>: <span class="string">""</span>,</span><br><span class="line"> <span class="attr">"last_name"</span>: <span class="string">""</span>,</span><br><span class="line"> <span class="attr">"email"</span>: <span class="string">"[email protected]"</span>,</span><br><span class="line"> <span class="attr">"is_staff"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"is_active"</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">"date_joined"</span>: <span class="string">"2020-04-13T08:59:16.279Z"</span>,</span><br><span class="line"> <span class="attr">"groups"</span>: [],</span><br><span class="line"> <span class="attr">"user_permissions"</span>: []</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">]</span><br></pre></td></tr></table></figure></li><li><p><code>dumpdata —format</code></p><p>默认输出格式为 <em>JSON</em>。使用 <code>—format</code> 可以指定输出格式</p><ul><li>json</li><li>xml</li><li>yaml</li></ul><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./manage.py dumpdata auth.user --indent 2 --format xml > user.xml</span><br></pre></td></tr></table></figure><p>这会输出 <em>xml</em> 文件</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?xml version="1.0" encoding="utf-8"?></span></span><br><span class="line"><span class="tag"><<span class="name">django-objects</span> <span class="attr">version</span>=<span class="string">"1.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">object</span> <span class="attr">model</span>=<span class="string">"auth.user"</span> <span class="attr">pk</span>=<span class="string">"1"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"password"</span> <span class="attr">type</span>=<span class="string">"CharField"</span>></span>pbkdf2_sha256$150000$i8oET981EnSJ$d2RCpfY76gFHbwUs1HekSK+pOLYMJFcJ1wFcuyf6R28=<span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"last_login"</span> <span class="attr">type</span>=<span class="string">"DateTimeField"</span>></span>2020-04-13T09:21:34.639297+00:00<span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"is_superuser"</span> <span class="attr">type</span>=<span class="string">"BooleanField"</span>></span>True<span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"username"</span> <span class="attr">type</span>=<span class="string">"CharField"</span>></span>xiao<span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"first_name"</span> <span class="attr">type</span>=<span class="string">"CharField"</span>></span><span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"last_name"</span> <span class="attr">type</span>=<span class="string">"CharField"</span>></span><span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"email"</span> <span class="attr">type</span>=<span class="string">"CharField"</span>></span>[email protected]<span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"is_staff"</span> <span class="attr">type</span>=<span class="string">"BooleanField"</span>></span>True<span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"is_active"</span> <span class="attr">type</span>=<span class="string">"BooleanField"</span>></span>True<span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"date_joined"</span> <span class="attr">type</span>=<span class="string">"DateTimeField"</span>></span>2020-04-13T08:59:01.310568+00:00<span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"groups"</span> <span class="attr">rel</span>=<span class="string">"ManyToManyRel"</span> <span class="attr">to</span>=<span class="string">"auth.group"</span>></span><span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"user_permissions"</span> <span class="attr">rel</span>=<span class="string">"ManyToManyRel"</span> <span class="attr">to</span>=<span class="string">"auth.permission"</span>></span><span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">object</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">object</span> <span class="attr">model</span>=<span class="string">"auth.user"</span> <span class="attr">pk</span>=<span class="string">"2"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"password"</span> <span class="attr">type</span>=<span class="string">"CharField"</span>></span>pbkdf2_sha256$150000$PgBKh5sMAE1y$xdFkYi+gprF1v2rlOyw2OOsRn87zSeTVLJ9dGfoXzIw=<span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"last_login"</span> <span class="attr">type</span>=<span class="string">"DateTimeField"</span>></span><span class="tag"><<span class="name">None</span>></span><span class="tag"></<span class="name">None</span>></span><span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"is_superuser"</span> <span class="attr">type</span>=<span class="string">"BooleanField"</span>></span>True<span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"username"</span> <span class="attr">type</span>=<span class="string">"CharField"</span>></span>qa<span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"first_name"</span> <span class="attr">type</span>=<span class="string">"CharField"</span>></span><span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"last_name"</span> <span class="attr">type</span>=<span class="string">"CharField"</span>></span><span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"email"</span> <span class="attr">type</span>=<span class="string">"CharField"</span>></span>[email protected]<span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"is_staff"</span> <span class="attr">type</span>=<span class="string">"BooleanField"</span>></span>True<span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"is_active"</span> <span class="attr">type</span>=<span class="string">"BooleanField"</span>></span>True<span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"date_joined"</span> <span class="attr">type</span>=<span class="string">"DateTimeField"</span>></span>2020-04-13T08:59:16.279788+00:00<span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"groups"</span> <span class="attr">rel</span>=<span class="string">"ManyToManyRel"</span> <span class="attr">to</span>=<span class="string">"auth.group"</span>></span><span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">field</span> <span class="attr">name</span>=<span class="string">"user_permissions"</span> <span class="attr">rel</span>=<span class="string">"ManyToManyRel"</span> <span class="attr">to</span>=<span class="string">"auth.permission"</span>></span><span class="tag"></<span class="name">field</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">object</span>></span></span><br><span class="line"><span class="tag"></<span class="name">django-objects</span>></span></span><br></pre></td></tr></table></figure></li><li><p><code>loaddata</code> 命令</p><p>用来导入 fixtures(dumpdata 导出的数据)到数据库</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./manage.py loaddata user.json</span><br></pre></td></tr></table></figure><p>这会导入 user.json 里的内容到数据库</p></li><li><p>恢复 fresh database</p><p>当你通过 dumpdata 命令备份整个数据库时,它将备份所有数据表。若使用 dump 文件导入到另外的 Django 项目,会导致 <code>IntegrityError</code>。</p><p>可以通过备份时加入选项 <code>—exclude</code> <em>contenttypes</em> 和 <em>auth.permissions</em> 数据表修复此问题</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./manage.py dumpdata --exclude auth.permission --exclude contenttypes > db.json</span><br></pre></td></tr></table></figure><p>现在再用 loaddata 命令导入 fresh dababase</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./manage.py loaddata db.json</span><br></pre></td></tr></table></figure></li></ul>]]></content>
<summary type="html">
<p><code>dumpdata</code> 命令:</p>
<p>它可以用来备份(导出)模型实例或整个数据库</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="l
</summary>
<category term="django" scheme="https://yuechuanx.top/tags/django/"/>
</entry>
<entry>
<title>Python代码规范 Pep8</title>
<link href="https://yuechuanx.top/Python/python-codestyle-pep8/"/>
<id>https://yuechuanx.top/Python/python-codestyle-pep8/</id>
<published>2020-04-01T06:12:38.000Z</published>
<updated>2020-04-01T06:12:38.000Z</updated>
<content type="html"><![CDATA[<p>以下所有内容包含在官方 PEP(Python Enhancement Proposals) 链接为 [pep8][<a href="https://www.python.org/dev/peps/pep-0008/" target="_blank" rel="noopener">https://www.python.org/dev/peps/pep-0008/</a>]</p><h2 id="简要版本"><a class="markdownIt-Anchor" href="#简要版本"></a> 简要版本</h2><ul><li><p>代码编排</p><ul><li><p>缩进。4个空格的缩进(编辑器都可以完成此功能),不使用Tap,更不能混合使用Tap和空格。</p><blockquote><p>针对不同编辑器兼容性,对 tab 可能有不同的标准,导致样式不统一。</p></blockquote></li><li><p>每行最大长度79,换行可以使用反斜杠,最好使用圆括号。换行点要在操作符的后边敲回车。</p><blockquote><p>早期 unix 主机终端只能显示 80 个字符。</p><p>通过限制所需的编辑器窗口宽度,可以并排打开多个文件,并且在使用在相邻列中显示两个版本的代码查看工具时,效果很好。</p></blockquote></li><li><p>类和top-level函数定义之间空两行;</p><p>类中的方法定义之间空一行;</p><p>函数内逻辑无关段落之间空一行;</p><p>其他地方尽量不要再空行。</p></li></ul></li><li><p>文档编排</p><ul><li><p>模块内容的顺序:</p><p>模块说明和docstring</p><p>import</p><p>globals&constants</p><p>其他定义。</p><p>其中import部分,又按标准、三方和自己编写顺序依次排放,之间空一行。</p></li><li><p>不要在一句import中多个库,比如<code>import os, sys</code>不推荐。<br />如果采用from XX import XX引用库,可以省略‘module.’,都是可能出现命名冲突,这时就要采用import XX。</p><p>如果有命名冲突。可以使用 <code>from X import Y as Z</code></p></li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="comment">#!/bin/python3</span></span><br><span class="line"><span class="comment"># -------------------------------------------------------------------------</span></span><br><span class="line"><span class="comment"># Author: Yuechuan Xiao</span></span><br><span class="line"><span class="comment"># @Date: 2020-01-09 14:56:57</span></span><br><span class="line"><span class="comment"># @LastEditors: Yuechuan Xiao</span></span><br><span class="line"><span class="comment"># @LastEditTime: 2020-03-30 16:33:48</span></span><br><span class="line"><span class="comment"># @Description:</span></span><br><span class="line"><span class="comment"># report.py: gen build's jira issues html report.</span></span><br><span class="line"><span class="comment"># -------------------------------------------------------------------------</span></span><br><span class="line"></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">Docstring</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">reporter.py is used to generate a html report for specific build.</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Standard library</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> namedtuple</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># Third party lib</span></span><br><span class="line"><span class="comment"># Import multi-subcass from A package.</span></span><br><span class="line"><span class="keyword">from</span> jinja2 <span class="keyword">import</span> (</span><br><span class="line"> Environment,</span><br><span class="line"> FileSystemLoader,</span><br><span class="line"> Template,</span><br><span class="line"> select_autoescape)</span><br><span class="line"><span class="keyword">from</span> jira <span class="keyword">import</span> JIRA</span><br><span class="line"></span><br><span class="line"><span class="comment"># If you have lcoal import </span></span><br><span class="line"><span class="comment"># from .utils import X</span></span><br><span class="line"><span class="comment"># from . import utils</span></span><br></pre></td></tr></table></figure></li><li><p>空格的使用<br />总体原则,避免不必要的空格。</p><ul><li>各种右括号前不要加空格。</li><li>逗号、冒号、分号前不要加空格。</li><li>函数的左括号前不要加空格。如<code>func(1)</code>。</li><li>序列的左括号前不要加空格。如<code>list[2]</code>。</li><li>操作符左右各加一个空格,不要为了对齐增加空格。</li><li>函数默认参数使用的赋值符左右省略空格。</li><li>不要将多句语句写在同一行,尽管使用‘;’允许。</li><li>if/for/while语句中,即使执行语句只有一句,也必须另起一行。</li></ul></li><li><p>命名规范<br />总体原则,新编代码必须按下面命名风格进行,现有库的编码尽量保持风格。</p><ul><li>尽量单独使用小写字母<code>l</code>,大写字母<code>O</code>等容易混淆的字母。</li><li>模块命名尽量短小,使用全部小写的方式,可以使用下划线。</li><li>包命名尽量短小,使用全部小写的方式,不可以使用下划线。</li><li>类的命名使用<code>CapWords</code>的方式,模块内部使用的类采用_CapWords的方式。_</li><li>异常命名使用CapWords+Error后缀的方式。</li><li>全局变量尽量只在模块内有效,类似C语言中的static。实现方法有两种,一是__all__机制;二是前缀一个下划线</li><li>函数命名使用全部小写的方式,可以使用下划线。</li><li>常量命名使用全部大写的方式,可以使用下划线。</li><li>类的属性(方法和变量)命名使用全部小写的方式,可以使用下划线。</li><li>类的属性有3种作用域public、non-public和subclass API,可以理解成C++中的public、private、protected,non-public属性前,前缀一条下划线。</li><li>类的属性若与关键字名字冲突,后缀一下划线,尽量不要使用缩略等其他方式。</li><li>为避免与子类属性命名冲突,在类的一些属性前,前缀两条下划线。比如:类Foo中声明__a,访问时,只能通过Foo._Foo__a,避免歧义。如果子类也叫Foo,那就无能为力了。</li><li>类的方法第一个参数必须是self,而静态方法第一个参数必须是cls。</li></ul></li><li><p>注释<br />总体原则,错误的注释不如没有注释。所以当一段代码发生变化时,第一件事就是要修改注释!<br />针对团队情况(是否国际化),注释倾向使用英文,最好是完整的句子,首字母大写,句后要有结束符,结束符后跟两个空格,开始下一句。如果是短语,可以省略结束符。</p><ul><li>块注释,在一段代码前增加的注释。在‘#’后加一空格。段落之间以只有‘#’的行间隔。比如:</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"># Description : Module config.</span><br><span class="line">#</span><br><span class="line"></span><br><span class="line"># Input : None</span><br><span class="line">#</span><br><span class="line"># Output : None</span><br></pre></td></tr></table></figure><ul><li>行注释,在一句代码后加注释。比如:x = x + 1 # Increment x<br />但是这种方式尽量少使用。可以在 Magic Number 时使用。</li><li>避免无谓的注释。</li></ul></li><li><p>文档描述<br />1 为所有的共有模块、函数、类、方法写docstrings;非共有的没有必要,但是可以写注释(在def的下一行)。<br />2 如果docstring要换行,参考如下例子,详见PEP 257</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">"""Return a foobang</span><br><span class="line"></span><br><span class="line">Optional plotz says to frobnicate the bizbaz first.</span><br><span class="line"></span><br><span class="line">"""</span><br></pre></td></tr></table></figure></li><li><p>编码建议</p><ul><li><p>编码中考虑到其他python实现的效率等问题,比如运算符‘+’在CPython(Python)中效率很高,都是Jython中却非常低,所以应该采用.join()的方式。<br />2 尽可能使用<code>i</code></p></li><li><p><code>s</code> <code>is not</code>取代<code>==</code>,比如<code>if x is not None</code> 要优于<code>if x</code>。<br />3 使用基于类的异常,每个模块或包都有自己的异常类,此异常类继承自Exception。<br />4 异常中不要使用裸露的except,except后跟具体的exceptions。<br />5 异常中try的代码尽可能少。比如:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>:</span><br><span class="line">value = collection[key]</span><br><span class="line"><span class="keyword">except</span> KeyError:</span><br><span class="line"><span class="keyword">return</span> key_not_found(key)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="keyword">return</span> handle_value(value)</span><br><span class="line"></span><br><span class="line">要优于</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line"><span class="comment"># Too broad!</span></span><br><span class="line"><span class="keyword">return</span> handle_value(collection[key])</span><br><span class="line"><span class="keyword">except</span> KeyError:</span><br><span class="line"><span class="comment"># Will also catch KeyError raised by handle_value()</span></span><br><span class="line"><span class="keyword">return</span> key_not_found(key)</span><br></pre></td></tr></table></figure></li><li><p>使用startswith() and endswith()代替切片进行序列前缀或后缀的检查。比如</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Yes: <span class="keyword">if</span> foo.startswith(‘bar’):优于</span><br><span class="line">No: <span class="keyword">if</span> foo[:<span class="number">3</span>] == ‘bar’:</span><br><span class="line">- 使用isinstance()比较对象的类型。比如</span><br><span class="line">Yes: <span class="keyword">if</span> isinstance(obj, int): 优于</span><br><span class="line">No: <span class="keyword">if</span> type(obj) <span class="keyword">is</span> type(<span class="number">1</span>):</span><br></pre></td></tr></table></figure></li><li><p>判断序列空或不空,有如下规则</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Yes: <span class="keyword">if</span> <span class="keyword">not</span> seq:</span><br><span class="line"><span class="keyword">if</span> seq:</span><br><span class="line">优于</span><br><span class="line">No: <span class="keyword">if</span> len(seq)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> len(seq)</span><br></pre></td></tr></table></figure></li><li><p>字符串不要以空格收尾。</p></li><li><p>二进制数据判断使用 <code>if boolvalue</code>的方式。</p></li></ul></li></ul><h2 id="reference"><a class="markdownIt-Anchor" href="#reference"></a> Reference</h2><ul><li><p>[PEP8][<a href="https://www.python.org/dev/peps/pep-0008/" target="_blank" rel="noopener">https://www.python.org/dev/peps/pep-0008/</a>]</p></li><li><p><a href="https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/contents/" target="_blank" rel="noopener">Google Python 开源项目风格指南</a></p></li></ul>]]></content>
<summary type="html">
<p>以下所有内容包含在官方 PEP(Python Enhancement Proposals) 链接为 [pep8][<a href="https://www.python.org/dev/peps/pep-0008/" target="_blank" rel="noopene
</summary>
<category term="Python" scheme="https://yuechuanx.top/categories/Python/"/>
<category term="python" scheme="https://yuechuanx.top/tags/python/"/>
</entry>
<entry>
<title>Django 项目后端模板</title>
<link href="https://yuechuanx.top/django-backend-template/"/>
<id>https://yuechuanx.top/django-backend-template/</id>
<published>2020-02-16T06:55:49.000Z</published>
<updated>2020-02-16T06:55:49.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>Django 项目本身可以通过 <code>django-admin</code> 或者直接运行 <code>python manage.py ARGS</code> 来进行脚手架生成。但是生成的项目框架层次不算太好。</p></blockquote><p>首先生成一个 Django 项目:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">django-admin startproject backend</span><br></pre></td></tr></table></figure><p>生成的项目框架如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">backend</span><br><span class="line">├── backend</span><br><span class="line">│ ├── __init__.py</span><br><span class="line">│ ├── settings.py</span><br><span class="line">│ ├── urls.py</span><br><span class="line">│ └── wsgi.py</span><br><span class="line">└── manage.py</span><br></pre></td></tr></table></figure><p>其中的两个 <code>backend</code> 分别表示项目,以及 app 全局配置</p><p>建立文件夹 <code>apps</code> 用来放置应用,把内层 <code>backend</code> 改为 <code>conf</code></p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">backend</span><br><span class="line">├── apps</span><br><span class="line">├── conf</span><br><span class="line">│ ├── __init__.py</span><br><span class="line">│ ├── settings.py</span><br><span class="line">│ ├── urls.py</span><br><span class="line">│ └── wsgi.py</span><br><span class="line">└── manage.py</span><br></pre></td></tr></table></figure><p>注意这里需要配置以下几个文件:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># manage.py </span></span><br><span class="line">...</span><br><span class="line"><span class="comment"># os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')</span></span><br><span class="line">os.environ.setdefault(<span class="string">'DJANGO_SETTINGS_MODULE'</span>, <span class="string">'conf.settings'</span>)</span><br><span class="line">...</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># settings.py</span></span><br><span class="line">...</span><br><span class="line"><span class="comment"># ROOT_URLCONF = 'backend.urls'</span></span><br><span class="line">ROOT_URLCONF = <span class="string">'conf.urls'</span></span><br><span class="line">...</span><br><span class="line"><span class="comment"># WSGI_APPLICATION = 'backend.wsgi.application'</span></span><br><span class="line">WSGI_APPLICATION = <span class="string">'conf.wsgi.application'</span></span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>现在可以测试 <code>python manage.py runserver</code> 是否可以起来。</p><p>接下来新建 Apps</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mkdir apps/login</span><br><span class="line">python manage.py startapp login apps/login</span><br></pre></td></tr></table></figure><p>注册 app</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># settings.py</span></span><br><span class="line"></span><br><span class="line">TEMPLATES = [</span><br><span class="line"> {</span><br><span class="line"> <span class="string">'BACKEND'</span>: <span class="string">'django.template.backends.django.DjangoTemplates'</span>,</span><br><span class="line"> <span class="string">'DIRS'</span>: [<span class="string">'apps'</span>], <span class="comment"># 添加 apps 文件夹</span></span><br><span class="line"> <span class="string">'APP_DIRS'</span>: <span class="literal">True</span>,</span><br><span class="line"> <span class="string">'OPTIONS'</span>: {</span><br><span class="line"> <span class="string">'context_processors'</span>: [</span><br><span class="line"> <span class="string">'django.template.context_processors.debug'</span>,</span><br><span class="line"> <span class="string">'django.template.context_processors.request'</span>,</span><br><span class="line"> <span class="string">'django.contrib.auth.context_processors.auth'</span>,</span><br><span class="line"> <span class="string">'django.contrib.messages.context_processors.messages'</span>,</span><br><span class="line"> ],</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">INSTALLED_APPS = [</span><br><span class="line"> <span class="string">'django.contrib.admin'</span>,</span><br><span class="line"> <span class="string">'django.contrib.auth'</span>,</span><br><span class="line"> <span class="string">'django.contrib.contenttypes'</span>,</span><br><span class="line"> <span class="string">'django.contrib.sessions'</span>,</span><br><span class="line"> <span class="string">'django.contrib.messages'</span>,</span><br><span class="line"> <span class="string">'django.contrib.staticfiles'</span>,</span><br><span class="line"></span><br><span class="line"> <span class="string">'apps.login'</span>,</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>导入 URL</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="keyword">from</span> apps.login <span class="keyword">import</span> urls <span class="keyword">as</span> login_urls</span><br><span class="line"></span><br><span class="line">urlpatterns = [</span><br><span class="line"> path(<span class="string">'admin/'</span>, admin.site.urls),</span><br><span class="line"> path(<span class="string">'login/'</span>, include(login_urls)),</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>现在一个基本的项目结构就建立好了。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">backend</span><br><span class="line">├── apps</span><br><span class="line">│ └── login</span><br><span class="line">│ ├── __init__.py</span><br><span class="line">│ ├── admin.py</span><br><span class="line">│ ├── apps.py</span><br><span class="line">│ ├── migrations</span><br><span class="line">│ │ └── __init__.py</span><br><span class="line">│ ├── models.py</span><br><span class="line">│ ├── tests.py</span><br><span class="line">│ └── views.py</span><br><span class="line">├── conf</span><br><span class="line">│ ├── __init__.py</span><br><span class="line">│ ├── settings.py</span><br><span class="line">│ ├── urls.py</span><br><span class="line">│ └── wsgi.py</span><br><span class="line">├── db.sqlite3</span><br><span class="line">└── manage.py</span><br></pre></td></tr></table></figure><p>相比起来层次更清晰,而且也更适合用作前后端分离的命名</p>]]></content>
<summary type="html">
<blockquote>
<p>Django 项目本身可以通过 <code>django-admin</code> 或者直接运行 <code>python manage.py ARGS</code> 来进行脚手架生成。但是生成的项目框架层次不算太好。</p>
</blockquo
</summary>
<category term="web" scheme="https://yuechuanx.top/tags/web/"/>
<category term="django" scheme="https://yuechuanx.top/tags/django/"/>
</entry>
<entry>
<title>如何理解递归</title>
<link href="https://yuechuanx.top/understand-recursion/"/>
<id>https://yuechuanx.top/understand-recursion/</id>
<published>2019-12-25T06:19:03.000Z</published>
<updated>2019-12-25T06:19:03.000Z</updated>
<content type="html"><![CDATA[<p>转载一篇对递归理解有帮助的小故事</p><blockquote><p>对递归的理解的要点主要在于放弃!</p><p>放弃你对于理解和跟踪递归全程的企图,只理解递归两层之间的交接,以及递归终结的条件。</p><p>想象你来到某个热带丛林,意外发现了十层之高的汉诺塔。正当你苦苦思索如何搬动它时,林中出来一个土著,毛遂自荐要帮你搬塔。他名叫二傻,戴着一个草帽,草帽上有一个2字,号称会把一到二号盘搬到任意柱。</p><p>你灵机一动,问道:“你该不会有个兄弟叫三傻吧?”<br />“对对,老爷你咋知道的?他会搬一到三号盘。“<br />”那你去把他叫来,我不需要你了。“<br />于是三傻来了,他也带着个草帽,上面有个3字。</p><p>你说:”三傻,你帮我把头三个盘子移到c柱吧。“<br />三傻沉吟了一会,走进树林,你听见他大叫:”二傻,出来帮我把头两个盘子搬到C!“</p><p>由于天气炎热你开始打瞌睡。朦胧中你没看见二傻是怎么工作的,二傻干完以后,走入林中大叫一声:“老三,我干完了!”</p><p>三傻出来,把三号盘从A搬到B,然后又去叫二傻:“老二,帮我把头两个盘子搬回A!”</p><p>余下的我就不多说了,总之三傻其实只搬三号盘,其他叫二傻出来干。最后一步是三傻把三号盘搬到C,然后呼叫二傻来把头两个盘子搬回C</p><p>事情完了之后你把三傻叫来,对他说:“其实你不知道怎么具体一步一步把三个盘子搬到C,是吧?”</p><p>三傻不解地说:“我不是把任务干完了?”</p><p>你说:“可你其实叫你兄弟二傻干了大部分工作呀?”</p><p>三傻说:“我外包给他和你屁相干?”</p><p>你问到:“二傻是不是也外包给了谁?“</p><p>三傻笑了:“这跟我有屁相干?”</p><p>你苦苦思索了一夜,第二天,你走入林中大叫:“十傻,你在哪?”</p><p>一个头上带着10号草帽的人,十傻,应声而出:“老爷,你有什么事?”</p><p>“我要你帮把1到10号盘子搬到C柱“</p><p>“好的,老爷。“十傻转身就向林内走。</p><p>“慢着,你该不是回去叫你兄弟九傻吧“</p><p>“老爷你怎么知道的?“</p><p>“所以你使唤他把头九个盘子搬过来搬过去,你只要搬几次十号盘就好了,对吗?“</p><p>“对呀!“</p><p>“你知不知道他是怎么干的?“</p><p>“这和我有屁相干?“</p><p>你叹了一口气,决定放弃。十傻开始干活。树林里充满了此起彼伏的叫声:“九傻,来一下!“ “老八,到你了!““五傻!。。。“”三傻!。。。“”大傻!“</p><p>你注意到大傻从不叫人,但是大傻的工作也最简单,他只是把一号盘搬来搬去。</p><p>若干年后,工作结束了。十傻来到你面前。你问十傻:“是谁教给你们这么干活的?“</p><p>十傻说:“我爸爸。他给我留了这张纸条。”</p><p>他从口袋里掏出一张小纸条,上面写着:“照你帽子的号码搬盘子到目标柱。如果有盘子压住你,叫你上面一位哥哥把他搬走。如果有盘子占住你要去的柱子,叫你哥哥把它搬到不碍事的地方。等你的盘子搬到了目标,叫你哥哥把该压在你上面的盘子搬回到你上头。“</p><p>你不解地问:“那大傻没有哥哥怎么办?“</p><p>十傻笑了:“他只管一号盘,所以永远不会碰到那两个‘如果’,也没有盘子该压在一号上啊。”</p><p>但这时他忽然变了颜色,好像泄漏了巨大的机密。他惊慌地看了你一眼,飞快地逃入树林。</p><p>第二天,你到树林里去搜寻这十兄弟。他们已经不知去向。你找到了一个小屋,只容一个人居住,但是屋里有十顶草帽,写着一到十号的号码。</p><p>作者:Fireman A<br />链接:<a href="https://www.zhihu.com/question/24385418/answer/257751077" target="_blank" rel="noopener">https://www.zhihu.com/question/24385418/answer/257751077</a></p></blockquote>]]></content>
<summary type="html">
<p>转载一篇对递归理解有帮助的小故事</p>
<blockquote>
<p>对递归的理解的要点主要在于放弃!</p>
<p>放弃你对于理解和跟踪递归全程的企图,只理解递归两层之间的交接,以及递归终结的条件。</p>
<p>想象你来到某个热带丛林,意外发现了十层之高的汉诺塔。正
</summary>
<category term="recursion" scheme="https://yuechuanx.top/tags/recursion/"/>
</entry>
<entry>
<title><流畅的Python> 字典和集合</title>
<link href="https://yuechuanx.top/Python/fluent-python-notes-chap-03/"/>
<id>https://yuechuanx.top/Python/fluent-python-notes-chap-03/</id>
<published>2019-12-24T08:08:14.000Z</published>
<updated>2019-12-24T08:08:14.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>字典这个数据结构活跃在所有 Python 程序的背后,即便你的源码里并没有直接用到它。<br />——A. M. Kuchling</p></blockquote><p><code>dict</code> 是 Python 语言的基石。</p><p>可散列对象需要实现 <code>__hash__</code> 和 <code>__eq__</code> 函数。<br />如果两个可散列对象是相等的,那么它们的散列值一定是一样的。</p><h2 id="范映射类型"><a class="markdownIt-Anchor" href="#范映射类型"></a> 范映射类型</h2><p>collections.abc 模块中有 Mapping 和 MutableMapping 两个抽象基类,起作用是为 dict 和其他类似的类型定义形式接口。</p><p>//pic</p><p>但非抽象映射类型一般不会直接继承这些抽象基类,而是直接对 dict 或 collections.User.Dict 进行扩展。</p><p>这些抽象基类的主要作用是作为形式化的文档,以及跟 isinstance 一起被用来判定某个数据是否为广义上的映射类型。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">my_dict = {}</span><br><span class="line">isinstance(my_dict, collections.abc.Mapping)</span><br></pre></td></tr></table></figure><pre><code>True</code></pre><blockquote><p>用 instance 而不是用 type 是用来避免参数可能不是 dict 而是其他的映射类型</p></blockquote><p>标准库的所有映射类型都是利用 dict 实现。</p><p>什么是可散列的数据类型?</p><p>字典的提供了多种构造方法<br />link</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 字典提供了很多种构造方法</span></span><br><span class="line">a = dict(one=<span class="number">1</span>, two=<span class="number">2</span>, three=<span class="number">3</span>)</span><br><span class="line">b = {<span class="string">'one'</span>: <span class="number">1</span>, <span class="string">'two'</span>: <span class="number">2</span>, <span class="string">'three'</span>: <span class="number">3</span>} </span><br><span class="line">c = dict(zip([<span class="string">'one'</span>, <span class="string">'two'</span>, <span class="string">'three'</span>], [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])) </span><br><span class="line">d = dict([(<span class="string">'two'</span>, <span class="number">2</span>), (<span class="string">'one'</span>, <span class="number">1</span>), (<span class="string">'three'</span>, <span class="number">3</span>)]) </span><br><span class="line">e = dict({<span class="string">'three'</span>: <span class="number">3</span>, <span class="string">'one'</span>: <span class="number">1</span>, <span class="string">'two'</span>: <span class="number">2</span>})</span><br><span class="line">a == b == c == d == e</span><br></pre></td></tr></table></figure><pre><code>True</code></pre><h2 id="字典推导"><a class="markdownIt-Anchor" href="#字典推导"></a> 字典推导</h2><p>字典推导(dictcomp)可以从任何以键值对为元素的可迭代对象构建出字典</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">DIAL_CODES = [</span><br><span class="line"> (<span class="number">86</span>, <span class="string">'China'</span>),</span><br><span class="line"> (<span class="number">91</span>, <span class="string">'India'</span>),</span><br><span class="line"> (<span class="number">1</span>, <span class="string">'United States'</span>)</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">country_code = {country: code <span class="keyword">for</span> code, country <span class="keyword">in</span> DIAL_CODES}</span><br><span class="line">country_code</span><br></pre></td></tr></table></figure><pre><code>{'China': 86, 'India': 91, 'United States': 1}</code></pre><h2 id="常见的映射方法"><a class="markdownIt-Anchor" href="#常见的映射方法"></a> 常见的映射方法</h2><p>dict、defaultdict、OrderedDict 的常见方法,后两个数据类型是 dict 的变种,位于 collections 模块内。</p><ul><li><p>setdefault 处理找不到的键</p><p>d[k] 无法找到正确的键时,会抛出异常。</p><p>用 d.get(k, default) 来代替 d[k], 可以对找不到的键设置默认返回值。</p></li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">03-dict-set/index0.py</span></span><br><span class="line"><span class="string">创建一个从单词到其出现频率的映射</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line">WORD_RE = re.compile(<span class="string">r'\w+'</span>)</span><br><span class="line"></span><br><span class="line">index = {}</span><br><span class="line"><span class="keyword">with</span> open(sys.argv[<span class="number">1</span>], encoding=<span class="string">'uft-8'</span>) <span class="keyword">as</span> fp:</span><br><span class="line"> <span class="keyword">for</span> line_no, line <span class="keyword">in</span> enumerate(fp, <span class="number">1</span>):</span><br><span class="line"> <span class="keyword">for</span> match <span class="keyword">in</span> WORD_RE.finditer(line):</span><br><span class="line"> word = match.group()</span><br><span class="line"> column_no = match.start() + <span class="number">1</span></span><br><span class="line"> location = (line_no, column_no)</span><br><span class="line"> <span class="comment"># 提取单词出现情况,如果没有出现过返回 []</span></span><br><span class="line"> occurences = index.get(word, [])</span><br><span class="line"> occurences.append(location)</span><br><span class="line"> index[word] = occurences</span><br><span class="line"></span><br><span class="line"><span class="comment"># 以字符顺序打印结果</span></span><br><span class="line"><span class="keyword">for</span> word <span class="keyword">in</span> sorted(index, key=str.upper):</span><br><span class="line"> print(word, index[word])</span><br></pre></td></tr></table></figure><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">$ python index0.py zen.txt</span><br><span class="line">a [(19, 48), (20, 53)]</span><br><span class="line">Although [(11, 1), (16, 1), (18, 1)]</span><br><span class="line">ambiguity [(14, 16)]</span><br><span class="line">and [(15, 23)]</span><br><span class="line">are [(21, 12)]</span><br><span class="line">aren [(10, 15)]</span><br><span class="line">at [(16, 38)]</span><br><span class="line">bad [(19, 50)]</span><br><span class="line">be [(15, 14), (16, 27), (20, 50)]</span><br><span class="line">beats [(11, 23)]</span><br><span class="line">Beautiful [(3, 1)]</span><br><span class="line">better [(3, 14), (4, 13), (5, 11), (6, 12), (7, 9), (8, 11), (17, 8), (18, 25)]</span><br><span class="line"><span class="built_in">break</span> [(10, 40)]</span><br><span class="line">by [(1, 20)]</span><br><span class="line">cases [(10, 9)]</span><br><span class="line">complex [(5, 23)]</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>使用 dict.setdefault</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">03-dict-set/index.py</span></span><br><span class="line"><span class="string">创建一个从单词到其出现频率的映射</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line">WORD_RE = re.compile(<span class="string">r'\w+'</span>)</span><br><span class="line"></span><br><span class="line">index = {}</span><br><span class="line"><span class="keyword">with</span> open(sys.argv[<span class="number">1</span>], encoding=<span class="string">'uft-8'</span>) <span class="keyword">as</span> fp:</span><br><span class="line"> <span class="keyword">for</span> line_no, line <span class="keyword">in</span> enumerate(fp, <span class="number">1</span>):</span><br><span class="line"> <span class="keyword">for</span> match <span class="keyword">in</span> WORD_RE.finditer(line):</span><br><span class="line"> word = match.group()</span><br><span class="line"> column_no = match.start() + <span class="number">1</span></span><br><span class="line"> location = (line_no, column_no)</span><br><span class="line"> <span class="comment"># 注意这行与上面的区别</span></span><br><span class="line"> index.setdefault(word, []).append(location)</span><br><span class="line"> <span class="comment"># 效果等同于:</span></span><br><span class="line"> <span class="comment"># if key not in my_dict:</span></span><br><span class="line"> <span class="comment"># my_dict[key] = []</span></span><br><span class="line"> <span class="comment"># my_dict[key].append(new_value)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 以字符顺序打印结果</span></span><br><span class="line"><span class="keyword">for</span> word <span class="keyword">in</span> sorted(index, key=str.upper):</span><br><span class="line"> print(word, index[word])</span><br></pre></td></tr></table></figure><h2 id="映射的弹性键查询"><a class="markdownIt-Anchor" href="#映射的弹性键查询"></a> 映射的弹性键查询</h2><p>某个键不存在时,希望读取时能得到一个默认值,有两个方式:</p><ul><li>通过 defaultdict 类型</li><li>自定义 dict 子类</li></ul><h3 id="defaultdict-处理找不到的键"><a class="markdownIt-Anchor" href="#defaultdict-处理找不到的键"></a> defaultdict 处理找不到的键</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">03-dict-set/index_default.py</span></span><br><span class="line"><span class="string">创建一个从单词到其出现频率的映射</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"><span class="keyword">import</span> collections</span><br><span class="line"></span><br><span class="line">WORD_RE = re.compile(<span class="string">r'\w+'</span>)</span><br><span class="line"></span><br><span class="line">index = collections.defaultdict(list) </span><br><span class="line"><span class="keyword">with</span> open(sys.argv[<span class="number">1</span>], encoding=<span class="string">'utf-8'</span>) <span class="keyword">as</span> fp:</span><br><span class="line"> <span class="keyword">for</span> line_no, line <span class="keyword">in</span> enumerate(fp, <span class="number">1</span>):</span><br><span class="line"> <span class="keyword">for</span> match <span class="keyword">in</span> WORD_RE.finditer(line):</span><br><span class="line"> word = match.group()</span><br><span class="line"> column_no = match.start()+<span class="number">1</span></span><br><span class="line"> location = (line_no, column_no)</span><br><span class="line"> <span class="comment"># index 如何没有 word 的记录, default_factory 会被调用,这里是创建一个空列表返回</span></span><br><span class="line"> index[word].append(location) </span><br><span class="line"></span><br><span class="line"><span class="comment"># print in alphabetical order</span></span><br><span class="line"><span class="keyword">for</span> word <span class="keyword">in</span> sorted(index, key=str.upper):</span><br><span class="line"> print(word, index[word])</span><br></pre></td></tr></table></figure><p>defaultdict 里的 default_factory 只在 <strong>getitem</strong> 里调用。<br />实际上,上面的机制是通过特殊方法 <strong>missing</strong> 支持的。</p><h3 id="missing"><a class="markdownIt-Anchor" href="#missing"></a> <strong>missing</strong></h3><p>如果 dict 继承类提供了 <strong>missing</strong> 方法,且 <strong>getitem</strong> 遇到找不到键的情况是会自动调用它,而不是抛出异常</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">StrKeyDict0</span><span class="params">(dict)</span>:</span> <span class="comment"># <1></span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__missing__</span><span class="params">(self, key)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> isinstance(key, str): <span class="comment"># <2></span></span><br><span class="line"> <span class="keyword">raise</span> KeyError(key)</span><br><span class="line"> <span class="keyword">return</span> self[str(key)] <span class="comment"># <3></span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">get</span><span class="params">(self, key, default=None)</span>:</span></span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="keyword">return</span> self[key] <span class="comment"># <4></span></span><br><span class="line"> <span class="keyword">except</span> KeyError:</span><br><span class="line"> <span class="keyword">return</span> default <span class="comment"># <5></span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__contains__</span><span class="params">(self, key)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> key <span class="keyword">in</span> self.keys() <span class="keyword">or</span> str(key) <span class="keyword">in</span> self.keys() <span class="comment"># <6></span></span><br><span class="line"></span><br><span class="line">d = StrKeyDict0([(<span class="string">'2'</span>, <span class="string">'Two'</span>), (<span class="string">'4'</span>, <span class="string">'Four'</span>)])</span><br><span class="line">print(d[<span class="string">'2'</span>])</span><br><span class="line">print(d[<span class="string">'4'</span>])</span><br><span class="line"><span class="comment"># d[1] error</span></span><br><span class="line"></span><br><span class="line">print(d.get(<span class="string">'2'</span>))</span><br><span class="line">print(d.get(<span class="string">'4'</span>))</span><br><span class="line">print(d.get(<span class="number">1</span>, <span class="string">'N/A'</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># defaultdcit & __missing__</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">mydefaultdict</span><span class="params">(dict)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, value, value_factory)</span>:</span></span><br><span class="line"> super().__init__(value)</span><br><span class="line"> self._value_factory = value_factory</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__missing__</span><span class="params">(self, key)</span>:</span></span><br><span class="line"> <span class="comment"># 要避免循环调用</span></span><br><span class="line"> <span class="comment"># return self[key]</span></span><br><span class="line"> self[key] = self._value_factory()</span><br><span class="line"> <span class="keyword">return</span> self[key]</span><br><span class="line"></span><br><span class="line">d = mydefaultdict({<span class="number">1</span>:<span class="number">1</span>}, list)</span><br><span class="line">print(d[<span class="number">1</span>])</span><br><span class="line">print(d[<span class="number">2</span>])</span><br><span class="line">d[<span class="number">3</span>].append(<span class="number">1</span>)</span><br><span class="line">print(d)</span><br></pre></td></tr></table></figure><pre><code>TwoFourTwoFour'N/A'</code></pre><h2 id="字典的变种"><a class="markdownIt-Anchor" href="#字典的变种"></a> 字典的变种</h2><blockquote><p>此节总结了标准库 collections 模块中,除了 defaultdict 之外的不同映射类型</p></blockquote><ul><li><p>collections.OrderedDict</p></li><li><p>collections.ChainMap</p><p>容纳多个不同的映射对象,然后在进行键查找操作时会从前到后逐一查找,直到被找到为止</p></li><li><p>collections.Counter</p></li><li><p>collections.UserDict</p><p>dict 的纯 Python 实现,让用户集成写子类的</p></li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># UserDict</span></span><br><span class="line"><span class="comment"># 定制化字典时,尽量继承 UserDict 而不是 dict</span></span><br><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> UserDict</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">mydict</span><span class="params">(UserDict)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__getitem__</span><span class="params">(self, key)</span>:</span></span><br><span class="line"> print(<span class="string">'Getting key'</span>, key)</span><br><span class="line"> <span class="keyword">return</span> super().__getitem__(key)</span><br><span class="line"></span><br><span class="line">d = mydict({<span class="number">1</span>:<span class="number">1</span>})</span><br><span class="line">print(d[<span class="number">1</span>], d[<span class="number">2</span>])</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># MyppingProxyType 用于构建 Mapping 的只读实例</span></span><br><span class="line"><span class="keyword">from</span> types <span class="keyword">import</span> MappingProxyType</span><br><span class="line"></span><br><span class="line">d = {<span class="number">1</span>: <span class="number">1</span>}</span><br><span class="line">d_proxy = MappingProxyType(d)</span><br><span class="line">print(d_proxy[<span class="number">1</span>])</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line"> d_proxy[<span class="number">1</span>] = <span class="number">1</span></span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line"> print(repr(e))</span><br><span class="line"></span><br><span class="line">d[<span class="number">1</span>] = <span class="number">2</span></span><br><span class="line">print(d_proxy[<span class="number">1</span>])</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># set 的操作</span></span><br><span class="line"><span class="comment"># 子集 & 真子集</span></span><br><span class="line">a, b = {<span class="number">1</span>, <span class="number">2</span>}, {<span class="number">1</span>, <span class="number">2</span>}</span><br><span class="line">print(a <= b, a < b)</span><br><span class="line"></span><br><span class="line"><span class="comment"># discard</span></span><br><span class="line">a = {<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line">a.discard(<span class="number">3</span>)</span><br><span class="line">print(a)</span><br><span class="line"></span><br><span class="line"><span class="comment"># pop</span></span><br><span class="line">print(a.pop(), a.pop())</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line"> a.pop()</span><br><span class="line"><span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line"> print(repr(e))</span><br></pre></td></tr></table></figure><h3 id="集合字面量"><a class="markdownIt-Anchor" href="#集合字面量"></a> 集合字面量</h3><p>除空集之外,集合的字面量——<code>{1}</code>、<code>{1, 2}</code>,等等——看起来跟它的数学形式一模一样。<strong>如果是空集,那么必须写成 <code>set()</code> 的形式</strong>,否则它会变成一个 <code>dict</code>.<br />跟 <code>list</code> 一样,字面量句法会比 <code>set</code> 构造方法要更快且更易读。</p><h3 id="集合和字典的实现"><a class="markdownIt-Anchor" href="#集合和字典的实现"></a> 集合和字典的实现</h3><p>集合和字典采用散列表来实现:</p><ol><li>先计算 key 的 <code>hash</code>, 根据 hash 的某几位(取决于散列表的大小)找到元素后,将该元素与 key 进行比较</li><li>若两元素相等,则命中</li><li>若两元素不等,则发生散列冲突,使用线性探测再散列法进行下一次查询。</li></ol><p>这样导致的后果:</p><ol><li>可散列对象必须支持 <code>hash</code> 函数;</li><li>必须支持 <code>__eq__</code> 判断相等性;</li><li>若 <code>a == b</code>, 则必须有 <code>hash(a) == hash(b)</code>。</li></ol><p>注:所有由用户自定义的对象都是可散列的,因为他们的散列值由 id() 来获取,而且它们都是不相等的。</p><h3 id="字典的空间开销"><a class="markdownIt-Anchor" href="#字典的空间开销"></a> 字典的空间开销</h3><p>由于字典使用散列表实现,所以字典的空间效率低下。使用 <code>tuple</code> 代替 <code>dict</code> 可以有效降低空间消费。<br />不过:内存太便宜了,不到万不得已也不要开始考虑这种优化方式,<strong>因为优化往往是可维护性的对立面</strong>。</p><p>往字典中添加键时,如果有散列表扩张的情况发生,则已有键的顺序也会发生改变。所以,<strong>不应该在迭代字典的过程各种对字典进行更改</strong>。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 字典中就键的顺序取决于添加顺序</span></span><br><span class="line"></span><br><span class="line">keys = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">dict_ = {}</span><br><span class="line"><span class="keyword">for</span> key <span class="keyword">in</span> keys:</span><br><span class="line"> dict_[key] = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> key, dict_key <span class="keyword">in</span> zip(keys, dict_):</span><br><span class="line"> print(key, dict_key)</span><br><span class="line"> <span class="keyword">assert</span> key == dict_key</span><br><span class="line"></span><br><span class="line"><span class="comment"># 字典中键的顺序不会影响字典比较</span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>字典这个数据结构活跃在所有 Python 程序的背后,即便你的源码里并没有直接用到它。<br />
——A. M. Kuchling</p>
</blockquote>
<p><code>dict</code> 是 Python 语言的基石。</p
</summary>
<category term="Python" scheme="https://yuechuanx.top/categories/Python/"/>
<category term="python" scheme="https://yuechuanx.top/tags/python/"/>
</entry>
<entry>
<title>Docker 高级特性</title>
<link href="https://yuechuanx.top/DevOps/docker-advanced-feature/"/>
<id>https://yuechuanx.top/DevOps/docker-advanced-feature/</id>
<published>2019-12-19T07:43:32.000Z</published>
<updated>2019-12-19T07:43:32.000Z</updated>
<content type="html"><![CDATA[<p>本次分享给大家介绍Docker 的高级特性与相应的工具。 它们就是Docker 三剑客,Compose、Machine和Swarm</p><h2 id="compose"><a class="markdownIt-Anchor" href="#compose"></a> Compose</h2><h3 id="介绍"><a class="markdownIt-Anchor" href="#介绍"></a> 介绍</h3><p>Docker Compose 是Docker 官方编排(Orchestration)项目之一,负责快速的部署分布式应用。</p><p>Compose 定位是 「定义和运行多个Docker 容器的应用(Defining and running multi-container Docker applications)」</p><p>其前身是开源项目Fig。其代码目前在https://github.com/docker/compose 上开源。</p><h3 id="安装"><a class="markdownIt-Anchor" href="#安装"></a> 安装</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install -U docker-compose</span><br></pre></td></tr></table></figure><p>或</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo curl -L <span class="string">"https://github.com/docker/compose/releases/download/1.24.1/docker-compose-<span class="variable">$(uname -s)</span>-<span class="variable">$(uname -m)</span>"</span> -o /usr/<span class="built_in">local</span>/bin/docker-compose</span><br></pre></td></tr></table></figure><h3 id="使用"><a class="markdownIt-Anchor" href="#使用"></a> 使用</h3><p><code>Dockerfile</code></p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> python:<span class="number">3.7</span>-slim</span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> . /app</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> pip install flask -i https://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com </span></span><br><span class="line"></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">80</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">ENV</span> NAME World</span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> [<span class="string">"python"</span>, <span class="string">"app.py"</span>]</span></span><br></pre></td></tr></table></figure><p><code>app.py</code></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route("/")</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">hello</span><span class="params">()</span>:</span></span><br><span class="line"> html = <span class="string">"<h3>Hello {name}!</h3>"</span> \</span><br><span class="line"> <span class="string">"<b>Hostname:</b> {hostname}"</span></span><br><span class="line"> <span class="keyword">return</span> html.format(name=os.getenv(<span class="string">"NAME"</span>, <span class="string">"world"</span>),</span><br><span class="line"> hostname=socket.gethostname())</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line"> app.run(host=<span class="string">'0.0.0.0'</span>, port=<span class="number">80</span>)</span><br></pre></td></tr></table></figure><p><code>docker-compose.yml</code></p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">version:</span> <span class="string">"3"</span></span><br><span class="line"><span class="attr">services:</span></span><br><span class="line"><span class="attr"> myapp:</span></span><br><span class="line"> <span class="comment"># build: .</span></span><br><span class="line"><span class="attr"> image:</span> <span class="attr">friendlyhello:v2</span></span><br><span class="line"><span class="attr"> container_name:</span> <span class="string">myapp</span></span><br><span class="line"><span class="attr"> ports:</span></span><br><span class="line"><span class="bullet"> -</span> <span class="string">"5000:80"</span></span><br><span class="line"><span class="attr"> environment:</span></span><br><span class="line"><span class="attr"> NAME:</span> <span class="string">World</span></span><br><span class="line"></span><br><span class="line"><span class="attr"> redis:</span></span><br><span class="line"><span class="attr"> image:</span> <span class="string">redis</span></span><br><span class="line"><span class="attr"> container_name:</span> <span class="string">web</span></span><br></pre></td></tr></table></figure><p>执行 <code>docker-compose build</code> 可生成镜像</p><p>执行 <code>docker-compose up</code> 启动容器运行</p><p>浏览器访问</p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/073759.png" alt="local-access" style="zoom:50%;" /><h3 id="命令说明"><a class="markdownIt-Anchor" href="#命令说明"></a> 命令说明</h3><div align="center"><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/wudoe.png" alt="docker-compose-commands" style="zoom: 50%; " /></div><h2 id="machine"><a class="markdownIt-Anchor" href="#machine"></a> Machine</h2><h3 id="介绍-2"><a class="markdownIt-Anchor" href="#介绍-2"></a> 介绍</h3><p>Docker Machine 是<code>Docker</code> 官方编排(Orchestration)项目之一,负责在多种平台上快速安装<code>Docker</code>环境。</p><div align="center"><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/073839.png" alt="docker-machine-architecture" style="zoom:100%"></div><h3 id="使用-2"><a class="markdownIt-Anchor" href="#使用-2"></a> 使用</h3><p>使用 <code>virtualbox</code> 类型的驱动,创建一台<code>Docker</code> 主机,命名为 <code>manager</code>。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker-machine create -d virtualbox manager</span><br></pre></td></tr></table></figure><p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/073845.png" alt="create-manager" /></p><p>可以在创建时加上如下参数,来配置主机或者主机上的<code>Docker</code>。</p> <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">--engine-opt dns=114.114.114.114 配置Docker 的默认DNS</span><br><span class="line"></span><br><span class="line">--engine-registry-mirror https://registry.docker-cn.com 配置Docker 的仓库镜像</span><br><span class="line"></span><br><span class="line">--virtualbox-memory 2048 配置主机内存</span><br><span class="line"></span><br><span class="line">--virtualbox-cpu-count 2 配置主机CPU</span><br></pre></td></tr></table></figure><p>更多参数请使用 <code>docker-machine create —help</code> 命令查看。</p><p><code>docker-machine ls</code> 查看主机</p><p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/073854.png" alt="docker-machine-ls" /></p><p><code>docker-machine env manager</code> 查看环境变量</p><p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/0bct6.png" alt="docker-machine-env-manager" /></p><p>切换 <code>docker</code> 主机 <code>manager</code> 为操作对象</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">eval</span> $(docker-machine env manager)</span><br></pre></td></tr></table></figure><p>或者可以 <code>ssh</code> 登录到 <code>docker</code> 主机</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker-machine ssh manager</span><br></pre></td></tr></table></figure><p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/gbw2f.png" alt="docker-machine-ssh-manager" /></p><h3 id="命令说明-2"><a class="markdownIt-Anchor" href="#命令说明-2"></a> 命令说明</h3><p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/dgvi9.png" alt="docker-machine-commands" /></p><h2 id="swarm"><a class="markdownIt-Anchor" href="#swarm"></a> Swarm</h2><p><code>Swarm</code> 是使用<code>SwarmKit</code> 构建的<code>Docker</code> 引擎内置(原生)的集群管理和编排工具。</p><p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/73kns.png" alt="docker-swarm-architecture" /></p><h3 id="使用-3"><a class="markdownIt-Anchor" href="#使用-3"></a> 使用</h3><p>初始化集群</p><p>在上节介绍 <code>docker-machine</code> 的时候,我们创建了<code>manager</code>节点,而初始化集群需要在管理节点内执行</p><p><code>docker swarm init --advertise-addr=IP_ADDR</code> <img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/u74f8.png" alt="docker-swarm-init" /></p><p>现在来创建两个工作节点<code>worker1</code>, <code>worker2</code>并加入集群</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">docker-machine create -d virtualbox worker1</span><br><span class="line"></span><br><span class="line"><span class="built_in">eval</span> $(docker-machine env worker1)</span><br><span class="line"></span><br><span class="line">docker swarm join --token SWMTKN-1-59qol34ustn06wtqs6bnsgar4j170k5aj24weu5yegq8qp66cb-26aroyxll4zh9pl8cdwuo7vm4 192.168.99.101:2377</span><br></pre></td></tr></table></figure><p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/jv19u.png" alt="docker-swarm-join" /></p><p>同理<code>worker2</code> 节点</p><p>进入<code>manager</code> 节点执行</p><p><code>docker node ls</code></p><p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/w64b8.png" alt="docker-node-ls" /></p><p>由此,我们就得到了一个最小化的集群。</p><h3 id="命令说明-docker-swarm-commands"><a class="markdownIt-Anchor" href="#命令说明-docker-swarm-commands"></a> 命令说明 <img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/wycws.png" alt="docker-swarm-commands" /></h3><h2 id="疑难解答"><a class="markdownIt-Anchor" href="#疑难解答"></a> 疑难解答</h2><ul><li>在<code>docker stack deploy –c docker-compose.yml</code> 后,在<code>docker ps</code> 中无法看到端口映射?</li></ul><p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/odmm7.png" alt="Q1" /></p><p>关于docker swarm mode 部署后端口的问题,可以使用<code>docker service ls</code>来查看端口是否正确暴露,因为此时是通过service来暴露的,并不是直接在container上暴露,所以此时用<code>docker ps</code>是看不到的,但暴露的端口依旧可以访问,这样实现和k8s里的service实现是有些相似的。</p><ul><li>执行<code>docker-compose -f docker-compose.yml up -d</code>,返回</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Pulling myapp (friendlyhello:v2)...</span><br><span class="line"></span><br><span class="line">ERROR: Get https://registry-1.docker.i... net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)</span><br></pre></td></tr></table></figure><p>compose文件中如果已经build过,就用image直接指定这个image,注释掉build的指令。如果没有build过,就放开build指令,执行<code>docker-compose</code>的build它,当然也可以使用<code>docker build</code>来构建它。因为这一块在上一章节已经提到过,所以对于部分这次直接切入的同学可能会有疑惑。而到了docker stack时,已经不支持<code>docker stack</code>来build它了,需要统一使用docker build来构建镜像。</p>]]></content>
<summary type="html">
<p>本次分享给大家介绍Docker 的高级特性与相应的工具。 它们就是Docker 三剑客,Compose、Machine和Swarm</p>
<h2 id="compose"><a class="markdownIt-Anchor" href="#compose"></a>
</summary>
<category term="DevOps" scheme="https://yuechuanx.top/categories/DevOps/"/>
<category term="docker" scheme="https://yuechuanx.top/tags/docker/"/>
</entry>
<entry>
<title>Docker 基础使用</title>
<link href="https://yuechuanx.top/DevOps/docker-basic/"/>
<id>https://yuechuanx.top/DevOps/docker-basic/</id>
<published>2019-12-19T07:43:19.000Z</published>
<updated>2019-12-19T07:43:19.000Z</updated>
<content type="html"><![CDATA[<h2 id="安装"><a class="markdownIt-Anchor" href="#安装"></a> 安装</h2><p><a href="https://docs.docker.com/install/" target="_blank" rel="noopener">官方安装文档</a></p><h3 id="windows"><a class="markdownIt-Anchor" href="#windows"></a> Windows</h3><p><a href="https://www.runoob.com/docker/windows-docker-install.html" target="_blank" rel="noopener">安装</a></p><h3 id="debianubuntu"><a class="markdownIt-Anchor" href="#debianubuntu"></a> Debian/Ubuntu</h3><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 官方</span></span><br><span class="line">curl -sSL https://get.docker.com/ | sh</span><br><span class="line"></span><br><span class="line"><span class="comment"># 阿里云</span></span><br><span class="line">curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/internet | sh -</span><br><span class="line"></span><br><span class="line"><span class="comment"># Daocloud</span></span><br><span class="line">curl -sSL https://get.daocloud.io/docker | sh</span><br></pre></td></tr></table></figure><h3 id="macos"><a class="markdownIt-Anchor" href="#macos"></a> MacOS</h3><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew cask install docker</span><br></pre></td></tr></table></figure><h2 id="简单指令"><a class="markdownIt-Anchor" href="#简单指令"></a> 简单指令</h2><ul><li><p>查看 Docker 版本</p><p>版本信息:<code>docker --version</code></p><p>配置信息: <code>docker info</code></p><p>help 信息: <code>docker --help</code></p></li><li><p>运行第一个 Docker 镜像</p><p><code>docker run hello-world</code></p></li></ul><h2 id="docker-命令行工具"><a class="markdownIt-Anchor" href="#docker-命令行工具"></a> Docker 命令行工具</h2><p>Docker CLI 指令分为两类,一类是 Management Commands,一类是镜像与容器 Commands。</p><p>你可以通过 <code>docker –help</code> 进行查看。</p><p>Docker 的命令格式为:</p><p>docker [OPTIONS] COMMAND</p><p>这里我们选取常用的几个命令进行示例:</p><p><code>docker pull</code> : 从镜像仓库中拉取镜像</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 从镜像仓库中拉取 Ubuntu 镜像</span></span><br><span class="line">docker pull Ubuntu</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行 Ubuntu 镜像,分配 tty 进入交互模式</span></span><br><span class="line"><span class="comment"># -i : interactive mode </span></span><br><span class="line"><span class="comment"># -t: 分配 tty </span></span><br><span class="line">docker run –it ubuntu:latest</span><br></pre></td></tr></table></figure><p>Docker-CLI 与 Linux 语法具有相似性,例如:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 列出所有镜像</span></span><br><span class="line">docker image ls </span><br><span class="line"></span><br><span class="line"><span class="comment"># 列出所有容器</span></span><br><span class="line">docker container ls</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看容器状态</span></span><br><span class="line">docker container ps</span><br></pre></td></tr></table></figure><p>如果你有 Linux 基础,那么相信对于 Docker-CLI 上手还是比较容易的。</p><h2 id="try-it-out-1"><a class="markdownIt-Anchor" href="#try-it-out-1"></a> TRY IT OUT #1</h2><p><code>docker run -d -P daocloud.io/daocloud/dao-2048</code></p><blockquote><p>-d 表示容器启动后在后台运行</p></blockquote><p>用 docker ps 查看容器运行状态如图:</p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/071731.png" alt="image-20191219151718661" style="zoom:50%;" /><blockquote><p>看到端口映射关系 0.0.0.0:32768->80。指宿主机的 32768 端口映射到容器的 80 端口</p></blockquote><p>用浏览器打开 localhost:32768</p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/071802.png" alt="image-20191219151800494" style="zoom:50%;" /><h2 id="dockerfile-简介"><a class="markdownIt-Anchor" href="#dockerfile-简介"></a> Dockerfile 简介</h2><p>Docker 可以从 Dockerfile 文件中构建镜像.</p><p>Dockerfile 语法请参考:<a href="https://docs.docker.com/engine/reference/builder/" target="_blank" rel="noopener">https://docs.docker.com/engine/reference/builder/</a></p><p>下面列出一些最常用的语法:</p><ul><li><p>FROM : 这会从 Docker Hub 中拉取镜像,目的镜像基于所拉取的镜像进行搭建</p></li><li><p>WORKDIR: RUN, CMD, ENTRYPOINT, COPY, ADD以此为工作路径</p></li><li><p>COPY: 拷贝文件或文件夹到指定路径</p></li><li><p>RUN:镜像的最上层执行命令,执行后的结果会被提交,作为后续操作基于的镜像。</p></li><li><p>EXPOSE:暴露端口号</p></li><li><p>ENV: 设置环境变量</p></li><li><p>CMD [“executable”,“param1”,“param2”]:一个 Dockerfile 应该只有一处 CMD 命令,如果有多处,则最后一处有效。</p></li></ul><p>#TRy it out #2</p><p>首先准备一个 Dockerfile 文件 与一个 <a href="http://app.py" target="_blank" rel="noopener">app.py</a> 文件</p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/071920.png" alt="image-20191219151919172" style="zoom:50%;" /><p>分别执行</p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/071936.png" alt="image-20191219151933441" style="zoom:50%;" /><p>中间打印输出</p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/072002.png" alt="image-20191219152000816" style="zoom:50%;" /><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/072016.png" alt="image-20191219152012608" style="zoom:50%;" /><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/072024.png" alt="image-20191219152022398" style="zoom:50%;" /><p>Docker 生成 container时会生成一个唯一的 container-id,在上图中 stop 命令用到了 container-id。当然,你可以使用 docker tag 命令对 container 进行重命名。</p><blockquote><p>-p 4000:80 : 指的是从宿主机端口 4000 映射到容器端口 80</p></blockquote><p>现在打开浏览器访问 <code>localhost:4000</code>:</p><img src="https://blog-1252790741.cos.ap-shanghai.myqcloud.com/imgs/072039.png" alt="image-20191219152037355" style="zoom:50%;" /><h2 id="reference"><a class="markdownIt-Anchor" href="#reference"></a> Reference</h2><p><a href="https://docs.docker.com/" target="_blank" rel="noopener">docker 官方文档</a></p>]]></content>
<summary type="html">
<h2 id="安装"><a class="markdownIt-Anchor" href="#安装"></a> 安装</h2>
<p><a href="https://docs.docker.com/install/" target="_blank" rel="noopener
</summary>
<category term="DevOps" scheme="https://yuechuanx.top/categories/DevOps/"/>
<category term="docker" scheme="https://yuechuanx.top/tags/docker/"/>
</entry>
<entry>
<title><流畅的Python> 序列构成的数组</title>
<link href="https://yuechuanx.top/Python/fluent-python-notes-chap-02/"/>
<id>https://yuechuanx.top/Python/fluent-python-notes-chap-02/</id>
<published>2019-12-16T12:27:00.000Z</published>
<updated>2019-12-16T12:27:00.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>你可能注意到了,之前提到的几个操作可以无差别地应用于文本、列表和表格上。<br />我们把文本、列表和表格叫作数据火车……FOR 命令通常能作用于数据火车上。<br />——Geurts、Meertens 和 Pemberton<br /><em>ABC Programmer’s Handbook</em></p></blockquote><h2 id="内置序列类型概览"><a class="markdownIt-Anchor" href="#内置序列类型概览"></a> 内置序列类型概览</h2><ul><li>容器序列<br /><code>list</code>、<code>tuple</code> 和 <code>collections.deque</code> 这些序列能存放不同类型的数据。</li><li>扁平序列<br /><code>str</code>、<code>bytes</code>、<code>bytearray</code>、<code>memoryview</code> 和 <code>array.array</code>,这类序列只能容纳一种类型。</li></ul><p>容器序列存放的是它们所包含的任意类型的对象的<strong>引用</strong>,而扁平序列里存放的<strong>是值而不是引用</strong>。换句话说,扁平序列其实是一段连续的内存空间。由此可见扁平序列其实更加紧凑,但是它里面只能存放诸如字符、字节和数值这种基础类型。</p><p>序列类型还能按照能否被修改来分类。</p><ul><li>可变序列<br /><code>list</code>、<code>bytearray</code>、<code>array.array</code>、<code>collections.deque</code> 和 <code>memoryview</code>。</li><li>不可变序列<br /><code>tuple</code>、<code>str</code> 和 <code>bytes</code></li></ul><h2 id="列表推导和生成器表达式"><a class="markdownIt-Anchor" href="#列表推导和生成器表达式"></a> 列表推导和生成器表达式</h2><h3 id="列表推导和可读性"><a class="markdownIt-Anchor" href="#列表推导和可读性"></a> 列表推导和可读性</h3><p>列表推导是构建列表(list)的快捷方式,生成器表达式用来穿件其他任何类型的序列。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 比较两段代码</span></span><br><span class="line">symbols = <span class="string">'abcde'</span></span><br><span class="line"><span class="comment"># 1</span></span><br><span class="line">codes = []</span><br><span class="line"><span class="keyword">for</span> symbol <span class="keyword">in</span> symbols:</span><br><span class="line"> codes.append(ord(symbol)) </span><br><span class="line">print(codes)</span><br><span class="line"><span class="comment"># 2</span></span><br><span class="line">codes = [ord(symbol) <span class="keyword">for</span> symbol <span class="keyword">in</span> symbols]</span><br><span class="line">print(codes)</span><br></pre></td></tr></table></figure><p>列表推导能够提升可读性。<br />只用列表推导来创建新的列表,并尽量保持简短(不要超过一行)</p><h3 id="列表推导同-filter-和-map-的比较"><a class="markdownIt-Anchor" href="#列表推导同-filter-和-map-的比较"></a> 列表推导同 filter 和 map 的比较</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">symbols = <span class="string">'abcde'</span></span><br><span class="line"></span><br><span class="line">beyond_ascii = [ord(s) <span class="keyword">for</span> s <span class="keyword">in</span> symbols <span class="keyword">if</span> ord(s) > <span class="number">100</span>]</span><br><span class="line">print(beyond_ascii)</span><br><span class="line"></span><br><span class="line">beyond_ascii = list(filter(<span class="keyword">lambda</span> c: c > <span class="number">100</span>, map(ord, symbols)))</span><br><span class="line">print(beyond_ascii)</span><br></pre></td></tr></table></figure><pre><code>[101][101]</code></pre><h3 id="笛卡尔积"><a class="markdownIt-Anchor" href="#笛卡尔积"></a> 笛卡尔积</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">colors = [<span class="string">'black'</span>, <span class="string">'white'</span>] </span><br><span class="line">sizes = [<span class="string">'S'</span>, <span class="string">'M'</span>, <span class="string">'L'</span>]</span><br><span class="line"></span><br><span class="line">tshirts = [(color, size) <span class="keyword">for</span> color <span class="keyword">in</span> colors </span><br><span class="line"> <span class="keyword">for</span> size <span class="keyword">in</span> sizes]</span><br><span class="line">print(tshirts)</span><br><span class="line"></span><br><span class="line">tshirts = [(color, size) <span class="keyword">for</span> size <span class="keyword">in</span> sizes</span><br><span class="line"> <span class="keyword">for</span> color <span class="keyword">in</span> colors]</span><br><span class="line">print(tshirts)</span><br><span class="line"><span class="comment"># 注意顺序是依照 for-loop</span></span><br></pre></td></tr></table></figure><pre><code>[('black', 'S'), ('black', 'M'), ('black', 'L'), ('white', 'S'), ('white', 'M'), ('white', 'L')][('black', 'S'), ('white', 'S'), ('black', 'M'), ('white', 'M'), ('black', 'L'), ('white', 'L')]</code></pre><h3 id="生成器表达式"><a class="markdownIt-Anchor" href="#生成器表达式"></a> 生成器表达式</h3><p>列表推导与生成器表达式的区别:</p><ul><li>生成器表达式遵守实现了迭代器接口,可以逐个地产出元素。列表推导是先建立一个完整的列表,再将这个列表传递到构造函数里。</li><li>语法上近似,方括号换成圆括号</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># symbols = 'abcde'</span></span><br><span class="line">print(tuple(ord(symbol) <span class="keyword">for</span> symbol <span class="keyword">in</span> symbols))</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> array</span><br><span class="line">print(array.array(<span class="string">'I'</span>, (ord(symbol) <span class="keyword">for</span> symbol <span class="keyword">in</span> symbols)))</span><br></pre></td></tr></table></figure><ul><li>如果生成器表达式是一个函数调用过程中的唯一参数,则不需要额外括号</li><li>生成器会在 for-loop 运行时才生成一个组合。逐个产出元素</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">colors = [<span class="string">'black'</span>, <span class="string">'white'</span>] </span><br><span class="line">sizes = [<span class="string">'S'</span>, <span class="string">'M'</span>, <span class="string">'L'</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> tshirt <span class="keyword">in</span> (<span class="string">'%s %s'</span> %(c, s) <span class="keyword">for</span> c <span class="keyword">in</span> colors <span class="keyword">for</span> s <span class="keyword">in</span> sizes):</span><br><span class="line"> print(tshirt)</span><br></pre></td></tr></table></figure><pre><code>black Sblack Mblack Lwhite Swhite Mwhite L</code></pre><h2 id="元祖不仅仅是不可变的列表"><a class="markdownIt-Anchor" href="#元祖不仅仅是不可变的列表"></a> 元祖不仅仅是不可变的列表</h2><h3 id="元祖与记录"><a class="markdownIt-Anchor" href="#元祖与记录"></a> 元祖与记录</h3><ul><li>元祖是对数据的记录</li><li>元祖的位置信息为数据赋予了意义。对元祖内元素排序,位置信息将丢失</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># LA 国际机场经纬度</span></span><br><span class="line">lax_coordinates = (<span class="number">33.9425</span>, <span class="number">-118.408056</span>)</span><br><span class="line"><span class="comment"># 城市,年份,人口(单位:百万),人口变化(单位:百分比),面积</span></span><br><span class="line">city, year, pop, chg, area = (<span class="string">'Tokyo'</span>, <span class="number">2003</span>, <span class="number">32450</span>, <span class="number">0.66</span>, <span class="number">8014</span>)</span><br><span class="line"><span class="comment"># country_code, passport number</span></span><br><span class="line">traveler_ids = [(<span class="string">'USA'</span>, <span class="string">'31195855'</span>), (<span class="string">'BBA'</span>, <span class="string">'CE342567'</span>), (<span class="string">'ESP'</span>, <span class="string">'XDA205856'</span>)]</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> passport <span class="keyword">in</span> sorted(traveler_ids):</span><br><span class="line"> print(<span class="string">'%s%s'</span> % passport)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 拆包(unpacking)</span></span><br><span class="line"><span class="keyword">for</span> country, _ <span class="keyword">in</span> traveler_ids:</span><br><span class="line"> print(country)</span><br></pre></td></tr></table></figure><pre><code>BBACE342567ESPXDA205856USA31195855USABBAESP</code></pre><h3 id="元祖拆包"><a class="markdownIt-Anchor" href="#元祖拆包"></a> 元祖拆包</h3><ul><li>平行赋值</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">lax_coordinates = (<span class="number">33.9425</span>, <span class="number">-118.408056</span>)</span><br><span class="line"><span class="comment"># 元祖拆包</span></span><br><span class="line">latitude, longtitude = lax_coordinates</span><br><span class="line">print(latitude)</span><br><span class="line">print(longtitude)</span><br></pre></td></tr></table></figure><pre><code>33.9425-118.408056</code></pre><ul><li>交换变量值,不使用中间变量</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">a = <span class="number">3</span></span><br><span class="line">b = <span class="number">4</span></span><br><span class="line">b, a = a, b</span><br><span class="line">print(a)</span><br><span class="line">print(b)</span><br></pre></td></tr></table></figure><pre><code>43</code></pre><ul><li><code>*</code> 运算符,把一个可迭代对象拆开作为函数参数</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">divmod(<span class="number">20</span>, <span class="number">8</span>)</span><br><span class="line"></span><br><span class="line">t = (<span class="number">20</span>, <span class="number">8</span>)</span><br><span class="line">divmod(*t)</span><br><span class="line"></span><br><span class="line">quotient, remainder = divmod(*t)</span><br><span class="line">print(quotient)</span><br><span class="line">print(remainder)</span><br></pre></td></tr></table></figure><pre><code>24</code></pre><ul><li>函数用元祖形式返回多个值</li></ul><blockquote><p>_ 用作占位符,可以用来处理不需要的数据</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line">_, filename = os.path.split(<span class="string">'/home/xiao/.ssh/id_rsa.pub'</span>)</span><br><span class="line">print(filename)</span><br></pre></td></tr></table></figure><pre><code>id_rsa.pub</code></pre><ul><li>用<code>*</code> 处理省下的元素</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">a, b, *rest = range(<span class="number">5</span>)</span><br><span class="line">print(a, b, rest)</span><br><span class="line"></span><br><span class="line">a, b, *rest = range(<span class="number">3</span>)</span><br><span class="line">print(a, b, rest)</span><br><span class="line"></span><br><span class="line">a, b, *rest = range(<span class="number">2</span>)</span><br><span class="line">print(a, b, rest)</span><br><span class="line"></span><br><span class="line"><span class="comment"># * 前缀只能用在一个变量前,该变量可出现在赋值表达式中任意位置</span></span><br><span class="line">a, *body, c, d = range(<span class="number">5</span>)</span><br><span class="line">print(a, body, c, d)</span><br><span class="line"></span><br><span class="line">*head, b, c, d = range(<span class="number">5</span>)</span><br><span class="line">print(head, b, c, d)</span><br></pre></td></tr></table></figure><pre><code>0 1 [2, 3, 4]0 1 [2]0 1 []0 [1, 2] 3 4[0, 1] 2 3 4</code></pre><h3 id="嵌套元祖拆包"><a class="markdownIt-Anchor" href="#嵌套元祖拆包"></a> 嵌套元祖拆包</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">metro_areas = [</span><br><span class="line"> (<span class="string">'Tokyo'</span>, <span class="string">'JP'</span>, <span class="number">36.933</span>, (<span class="number">35.689722</span>, <span class="number">139.691667</span>)), <span class="comment"># <1></span></span><br><span class="line"> (<span class="string">'Delhi NCR'</span>, <span class="string">'IN'</span>, <span class="number">21.935</span>, (<span class="number">28.613889</span>, <span class="number">77.208889</span>)),</span><br><span class="line"> (<span class="string">'Mexico City'</span>, <span class="string">'MX'</span>, <span class="number">20.142</span>, (<span class="number">19.433333</span>, <span class="number">-99.133333</span>)),</span><br><span class="line"> (<span class="string">'New York-Newark'</span>, <span class="string">'US'</span>, <span class="number">20.104</span>, (<span class="number">40.808611</span>, <span class="number">-74.020386</span>)),</span><br><span class="line"> (<span class="string">'Sao Paulo'</span>, <span class="string">'BR'</span>, <span class="number">19.649</span>, (<span class="number">-23.547778</span>, <span class="number">-46.635833</span>)),</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">print(<span class="string">'{:15} | {:^9} | {:^9}'</span>.format(<span class="string">''</span>, <span class="string">'lat.'</span>, <span class="string">'long.'</span>))</span><br><span class="line">fmt = <span class="string">'{:15} | {:9.4f} | {:9.4f}'</span></span><br><span class="line"><span class="keyword">for</span> name, cc, pop, (latitude, longitude) <span class="keyword">in</span> metro_areas: <span class="comment"># <2></span></span><br><span class="line"> <span class="keyword">if</span> longitude <= <span class="number">0</span>: <span class="comment"># <3></span></span><br><span class="line"> print(fmt.format(name, latitude, longitude))</span><br></pre></td></tr></table></figure><pre><code> | lat. | long. Mexico City | 19.4333 | -99.1333New York-Newark | 40.8086 | -74.0204Sao Paulo | -23.5478 | -46.6358</code></pre><p>将元祖作为记录仍缺少一个功能:字段命名</p><h3 id="具名元祖numedtuple"><a class="markdownIt-Anchor" href="#具名元祖numedtuple"></a> 具名元祖(numedtuple)</h3><p><code>collections.namedtuple</code> 是一个工厂函数,用来构建带字段名的元祖和一个有名字的</p><blockquote><p>namedtuple 构建的类的实例所消耗的内存和元祖是一样的,因为字段名都存在对应的类里。<br />实例和普通的对象实例小一点,因为 Python 不会用 <code>__dict__</code> 存放实例的属性</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> collections <span class="keyword">import</span> namedtuple</span><br><span class="line"></span><br><span class="line"><span class="comment"># 需要两个参数,类名和类各个字段的名字</span></span><br><span class="line">City = namedtuple(<span class="string">'City'</span>, <span class="string">'name country population coordinates'</span>)</span><br><span class="line">tokyo = City(<span class="string">'Tokyo'</span>, <span class="string">'JP'</span>, <span class="number">36.933</span>, (<span class="number">35.689722</span>, <span class="number">129.691667</span>))</span><br><span class="line">print(tokyo)</span><br><span class="line">print(tokyo.population)</span><br><span class="line">print(tokyo.coordinates)</span><br></pre></td></tr></table></figure><pre><code>City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 129.691667))36.933(35.689722, 129.691667)</code></pre><p><code>namedtuple</code> 除了从普通元祖继承的属性外,还有一些专有属性。<br />常用的有:</p><ul><li><code>_fields</code> 类属性</li><li><code>_make(iterable)</code> 类方法</li><li><code>_asdict()</code> 实例方法</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">print(City._fields)</span><br><span class="line">LatLong = namedtuple(<span class="string">'LatLong'</span>, <span class="string">'lat long'</span>)</span><br><span class="line">delhi_data = (<span class="string">'Delhi NCR'</span>, <span class="string">'IN'</span>, <span class="number">21.935</span>, LatLong(<span class="number">28.613889</span>, <span class="number">77.208889</span>))</span><br><span class="line">delhi = City._make(delhi_data)</span><br><span class="line">print(delhi._asdict())</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> key, value <span class="keyword">in</span> delhi._asdict().items():</span><br><span class="line"> print(key + <span class="string">':'</span>, value)</span><br></pre></td></tr></table></figure><pre><code>('name', 'country', 'population', 'coordinates')OrderedDict([('name', 'Delhi NCR'), ('country', 'IN'), ('population', 21.935), ('coordinates', LatLong(lat=28.613889, long=77.208889))])name: Delhi NCRcountry: INpopulation: 21.935coordinates: LatLong(lat=28.613889, long=77.208889)</code></pre><h3 id="作为不可变列表的元祖"><a class="markdownIt-Anchor" href="#作为不可变列表的元祖"></a> 作为不可变列表的元祖</h3><p>对比列表和元祖的方法<br />// 插入表格</p><p>结论:</p><ul><li>除了增减元素相关的方法和<code>__reversed__</code> 外,元祖支持列表的其他所有方法。</li></ul><h2 id="切片"><a class="markdownIt-Anchor" href="#切片"></a> 切片</h2><p>在 Python 里, 列表(list),元祖(tuple)和字符串(str)这类序列类型都支持切片操作</p><h3 id="为什么切片的区间会忽略最后一个元素"><a class="markdownIt-Anchor" href="#为什么切片的区间会忽略最后一个元素"></a> 为什么切片的区间会忽略最后一个元素</h3><ul><li>Python 以0 作为起始下标</li><li>当只有后一个位置信息时,可以快速导出切片和区间的元素数量</li><li>当起止位置信息课件是,可以快速计算出切片和区间的长度 (stop - start)</li><li>可利用任意一个下标把序列分割成不重叠的两部分。<code>my_list[:x]</code> <code>my_list[x:]</code></li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">### 对对象进行切片</span></span><br><span class="line"></span><br><span class="line">- 可以通过 s[a:b:c] 的形式对 s 在 a 和 b 区间以 c 为间隔取值</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">s = <span class="string">'bicycle'</span></span><br><span class="line">print(s[::<span class="number">3</span>])</span><br><span class="line">print(s[::<span class="number">-1</span>])</span><br><span class="line">print(s[::<span class="number">-2</span>])</span><br></pre></td></tr></table></figure><pre><code>byeelcycibeccb</code></pre><h3 id="多维切片和省略"><a class="markdownIt-Anchor" href="#多维切片和省略"></a> 多维切片和省略</h3><p><code>[]</code> 运算符可以使用以逗号分开的多个索引或切片。</p><p>如 <code>a[i, j]</code>,<code>a[m:n, k:1]</code>得到二维切片</p><p>要正确处理<code>[]</code> 运算符,对象的特殊方法 <code>__getitem__</code>,<code>__setitem__</code> 需要以元祖的形式来接受 <code>a[i, j]</code>的索引。</p><h3 id="给切片赋值"><a class="markdownIt-Anchor" href="#给切片赋值"></a> 给切片赋值</h3><p>切片放在赋值语句左边,或作为 del 操作对象,可以对序列进行嫁接、切除或就地修改</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">l = list(range(<span class="number">10</span>))</span><br><span class="line">print(l)</span><br><span class="line"></span><br><span class="line">l[<span class="number">2</span>:<span class="number">5</span>] = [<span class="number">20</span>, <span class="number">30</span>]</span><br><span class="line">print(l)</span><br><span class="line"></span><br><span class="line"><span class="keyword">del</span> l[<span class="number">5</span>:<span class="number">7</span>]</span><br><span class="line">print(l)</span><br><span class="line"></span><br><span class="line">l[<span class="number">3</span>::<span class="number">2</span>] = [<span class="number">11</span>, <span class="number">22</span>]</span><br><span class="line">print(l)</span><br><span class="line"></span><br><span class="line"><span class="comment"># l[2:5] = 100 WRONG</span></span><br></pre></td></tr></table></figure><pre><code>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9][0, 1, 20, 30, 5, 6, 7, 8, 9][0, 1, 20, 30, 5, 8, 9][0, 1, 20, 11, 5, 22, 9]</code></pre><h2 id="对序列使用-和"><a class="markdownIt-Anchor" href="#对序列使用-和"></a> 对序列使用 + 和 *</h2><ul><li><code>+</code> 和 <code>*</code> 不修改原有的操作对象,而是构建一个新的序列</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">l = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">print(l * <span class="number">5</span>)</span><br><span class="line"></span><br><span class="line">print(<span class="number">5</span> * <span class="string">'abcd'</span>)</span><br></pre></td></tr></table></figure><pre><code>[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]abcdabcdabcdabcdabcd</code></pre><h3 id="建立由列表组成的列表"><a class="markdownIt-Anchor" href="#建立由列表组成的列表"></a> 建立由列表组成的列表</h3><blockquote><p>a * n,如果在序列 a 中存在对其他可变变量的引用的话,得到的序列中包含的是 n 个对指向同一地址的引用</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">board = [[<span class="string">'_'</span>] * <span class="number">3</span> <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">3</span>)]</span><br><span class="line"><span class="comment"># 换一种形式</span></span><br><span class="line"><span class="comment"># board = []</span></span><br><span class="line"><span class="comment"># for i in range(3):</span></span><br><span class="line"><span class="comment"># row = ['_'] * 3</span></span><br><span class="line"><span class="comment"># board.append(row)</span></span><br><span class="line">print(board)</span><br><span class="line">board[<span class="number">1</span>][<span class="number">2</span>] = <span class="string">'X'</span></span><br><span class="line">print(board)</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"><span class="comment"># weird_board = [['_'] * 3] * 3</span></span><br><span class="line"><span class="comment"># 换一种形式</span></span><br><span class="line">weird_board = []</span><br><span class="line">row = [<span class="string">'_'</span>] * <span class="number">3</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">3</span>):</span><br><span class="line"> weird_board.append(row)</span><br><span class="line">weird_board[<span class="number">1</span>][<span class="number">2</span>] = <span class="string">'O'</span></span><br><span class="line"><span class="comment"># 会发现 3 个指向同一列表的引用</span></span><br><span class="line">print(weird_board)</span><br></pre></td></tr></table></figure><pre><code>[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']][['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']][['_', '_', 'O'], ['_', '_', 'O'], ['_', '_', 'O']]</code></pre><h2 id="序列的增量赋值"><a class="markdownIt-Anchor" href="#序列的增量赋值"></a> 序列的增量赋值 +=、*=</h2><ul><li><code>+=</code> 背后的特殊方法是 <code>__iadd__</code> 方法,没有则退一步调用 <code>__add__</code></li><li>同理 <code>*=</code> 的特殊方法是 <code>__imul__</code></li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">l = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">print(id(l))</span><br><span class="line"></span><br><span class="line">l *= <span class="number">2</span></span><br><span class="line">print(l)</span><br><span class="line"><span class="comment"># 列表ID 无改变</span></span><br><span class="line">print(id(l))</span><br><span class="line"></span><br><span class="line">t = (<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line">print(id(t))</span><br><span class="line">t *= <span class="number">2</span></span><br><span class="line">print(t)</span><br><span class="line"><span class="comment"># 新元祖被创建</span></span><br><span class="line">print(id(t))</span><br></pre></td></tr></table></figure><pre><code>4534358344[1, 2, 3, 1, 2, 3]45343583444536971408(1, 2, 3, 1, 2, 3)4546754024</code></pre><h2 id="listsort方法和内置函数sorted"><a class="markdownIt-Anchor" href="#listsort方法和内置函数sorted"></a> list.sort方法和内置函数sorted</h2><ul><li>list.sort 会就地排序列表,方法返回值为 None</li><li>sorted 会新建一个列表作为返回值</li><li>两个方法都有 reverse 和 key 作为可选的关键字参数<br />reserve 为 True 时,降序输出。默认为 false<br />key 只有一个参数的函数,将被用在序列的每一个元素上,其结果作为排序算法依赖的对比关键字</li></ul><h2 id="用bisect管理已排序的序列"><a class="markdownIt-Anchor" href="#用bisect管理已排序的序列"></a> 用bisect管理已排序的序列</h2><p>bisect 模块有两个主要函数:</p><ul><li>bisect</li><li>insort<br />都利用二分查找法来在有序序列中查找或插入人元素</li></ul><h3 id="用-bisect-来搜索"><a class="markdownIt-Anchor" href="#用-bisect-来搜索"></a> 用 bisect 来搜索</h3><p>bisect(haystack, needle) 默认为升序,haystack 需要保持有序。<br />使用方法:<br />bisect(index, needle) 查找位置 index,再使用 haystack.insert(index, needle) 插入新值</p><p>也可以用 insort 来一步到位,且后者速度更快</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># BEGIN BISECT_DEMO</span></span><br><span class="line"><span class="keyword">import</span> bisect</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line">HAYSTACK = [<span class="number">1</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">8</span>, <span class="number">12</span>, <span class="number">15</span>, <span class="number">20</span>, <span class="number">21</span>, <span class="number">23</span>, <span class="number">23</span>, <span class="number">26</span>, <span class="number">29</span>, <span class="number">30</span>]</span><br><span class="line">NEEDLES = [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">5</span>, <span class="number">8</span>, <span class="number">10</span>, <span class="number">22</span>, <span class="number">23</span>, <span class="number">29</span>, <span class="number">30</span>, <span class="number">31</span>]</span><br><span class="line"></span><br><span class="line">ROW_FMT = <span class="string">'{0:2d} @ {1:2d} {2}{0:<2d}'</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">demo</span><span class="params">(bisect_fn)</span>:</span></span><br><span class="line"> <span class="keyword">for</span> needle <span class="keyword">in</span> reversed(NEEDLES):</span><br><span class="line"> position = bisect_fn(HAYSTACK, needle) <span class="comment"># <1></span></span><br><span class="line"> offset = position * <span class="string">' |'</span> <span class="comment"># <2></span></span><br><span class="line"> print(ROW_FMT.format(needle, position, offset)) <span class="comment"># <3></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> sys.argv[<span class="number">-1</span>] == <span class="string">'left'</span>: <span class="comment"># <4></span></span><br><span class="line"> bisect_fn = bisect.bisect_left</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> bisect_fn = bisect.bisect</span><br><span class="line"></span><br><span class="line"> print(<span class="string">'DEMO:'</span>, bisect_fn.__name__) <span class="comment"># <5></span></span><br><span class="line"> print(<span class="string">'haystack ->'</span>, <span class="string">' '</span>.join(<span class="string">'%2d'</span> % n <span class="keyword">for</span> n <span class="keyword">in</span> HAYSTACK))</span><br><span class="line"> demo(bisect_fn)</span><br><span class="line"></span><br><span class="line"><span class="comment"># END BISECT_DEMO</span></span><br></pre></td></tr></table></figure><pre><code>DEMO: bisect_righthaystack -> 1 4 5 6 8 12 15 20 21 23 23 26 29 3031 @ 14 | | | | | | | | | | | | | |3130 @ 14 | | | | | | | | | | | | | |3029 @ 13 | | | | | | | | | | | | |2923 @ 11 | | | | | | | | | | |2322 @ 9 | | | | | | | | |2210 @ 5 | | | | |10 8 @ 5 | | | | |8 5 @ 3 | | |5 2 @ 1 |2 1 @ 1 |1 0 @ 0 0 </code></pre><h3 id="array"><a class="markdownIt-Anchor" href="#array"></a> Array</h3><blockquote><p>虽然列表既灵活又简单,但面对各类需求时,我们可能会有更好的选择。比如,要存放 1000 万个浮点数的话,数组(array)的效率要高得多,因为数组在背后存的并不是 float 对象,而是数字的机器翻译,也就是字节表述。这一点就跟 C 语言中的数组一样。再比如说,如果需要频繁对序列做先进先出的操作,deque(双端队列)的速度应该会更快。</p></blockquote><p><code>array.tofile</code> 和 <code>fromfile</code> 可以将数组以二进制格式写入文件,速度要比写入文本文件快很多,文件的体积也小。</p><blockquote><p>另外一个快速序列化数字类型的方法是使用 pickle(<a href="https://docs.python.org/3/library/pickle.html%EF%BC%89%E6%A8%A1%E5%9D%97%E3%80%82pickle.dump" target="_blank" rel="noopener">https://docs.python.org/3/library/pickle.html)模块。pickle.dump</a> 处理浮点数组的速度几乎跟array.tofile 一样快。不过前者可以处理几乎所有的内置数字类型,包含复数、嵌套集合,甚至用户自定义的类。前提是这些类没有什么特别复杂的实现。</p></blockquote><p>array 具有 <code>type code</code> 来表示数组类型:具体可见 <a href="https://docs.python.org/3/library/array.html" target="_blank" rel="noopener">array 文档</a>.</p><h3 id="memoryview"><a class="markdownIt-Anchor" href="#memoryview"></a> memoryview</h3><blockquote><p>memoryview.cast 的概念跟数组模块类似,能用不同的方式读写同一块内存数据,而且内容字节不会随意移动。</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> array</span><br><span class="line"></span><br><span class="line">arr = array.array(<span class="string">'h'</span>, [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])</span><br><span class="line">memv_arr = memoryview(arr)</span><br><span class="line"><span class="comment"># 把 signed short 的内存使用 char 来呈现</span></span><br><span class="line">memv_char = memv_arr.cast(<span class="string">'B'</span>) </span><br><span class="line">print(<span class="string">'Short'</span>, memv_arr.tolist())</span><br><span class="line">print(<span class="string">'Char'</span>, memv_char.tolist())</span><br><span class="line">memv_char[<span class="number">1</span>] = <span class="number">2</span> <span class="comment"># 更改 array 第一个数的高位字节</span></span><br><span class="line"><span class="comment"># 0x1000000001</span></span><br><span class="line">print(memv_arr.tolist(), arr)</span><br><span class="line">print(<span class="string">'-'</span> * <span class="number">10</span>)</span><br><span class="line">bytestr = <span class="string">b'123'</span></span><br><span class="line"><span class="comment"># bytes 是不允许更改的</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line"> bytestr[<span class="number">1</span>] = <span class="string">'3'</span></span><br><span class="line"><span class="keyword">except</span> TypeError <span class="keyword">as</span> e:</span><br><span class="line"> print(repr(e))</span><br><span class="line">memv_byte = memoryview(bytestr)</span><br><span class="line">print(<span class="string">'Memv_byte'</span>, memv_byte.tolist())</span><br><span class="line"><span class="comment"># 同样这块内存也是只读的</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line"> memv_byte[<span class="number">1</span>] = <span class="number">1</span></span><br><span class="line"><span class="keyword">except</span> TypeError <span class="keyword">as</span> e:</span><br><span class="line"> print(repr(e))</span><br></pre></td></tr></table></figure><h3 id="deque"><a class="markdownIt-Anchor" href="#deque"></a> Deque</h3><p><code>collections.deque</code> 是比 <code>list</code> 效率更高,且<strong>线程安全</strong>的双向队列实现。</p><p>除了 collections 以外,以下 Python 标准库也有对队列的实现:</p><ul><li>queue.Queue (可用于线程间通信)</li><li>multiprocessing.Queue (可用于进程间通信)</li><li>asyncio.Queue</li><li>heapq</li></ul>]]></content>
<summary type="html">
<blockquote>
<p>你可能注意到了,之前提到的几个操作可以无差别地应用于文本、列表和表格上。<br />
我们把文本、列表和表格叫作数据火车……FOR 命令通常能作用于数据火车上。<br />
——Geurts、Meertens 和 Pemberton<br />
<
</summary>
<category term="Python" scheme="https://yuechuanx.top/categories/Python/"/>
<category term="python" scheme="https://yuechuanx.top/tags/python/"/>
</entry>
<entry>
<title>Markdown 语法总结</title>
<link href="https://yuechuanx.top/markdown-syntax/"/>
<id>https://yuechuanx.top/markdown-syntax/</id>
<published>2019-12-11T08:03:39.000Z</published>
<updated>2019-12-11T08:03:39.000Z</updated>
<content type="html"><![CDATA[<hr /><h2 id="斜体和粗体"><a class="markdownIt-Anchor" href="#斜体和粗体"></a> 斜体和粗体</h2><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="emphasis">*斜体*</span>或<span class="emphasis">_斜体_</span></span><br><span class="line"><span class="strong">**粗体**</span></span><br><span class="line"><span class="strong">***加粗斜体**</span>*</span><br><span class="line">~~删除线~~</span><br></pre></td></tr></table></figure><p><em>斜体</em>或_斜体_</p><p><strong>粗体</strong></p><p><em><strong>加粗斜体</strong></em></p><p><s>删除线</s></p><hr /><h2 id="分级标题"><a class="markdownIt-Anchor" href="#分级标题"></a> 分级标题</h2><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="section"># 一级标题</span></span><br><span class="line"><span class="section">## 二级标题</span></span><br><span class="line"><span class="section">###三级标题</span></span><br><span class="line"><span class="section">### 四级标题</span></span><br><span class="line"><span class="section">#### 五级标题</span></span><br><span class="line"><span class="section">##### 六级标题</span></span><br></pre></td></tr></table></figure><p>一级标题字号最大,依级递减。</p><hr /><h2 id="超链接"><a class="markdownIt-Anchor" href="#超链接"></a> 超链接</h2><p>Markdown 支持两种形式的链接语法: 行内式和参考式两种形式,行内式一般使用较多。</p><h3 id="行内式"><a class="markdownIt-Anchor" href="#行内式"></a> 行内式</h3><p>语法说明:<code>[文字](链接)</code></p><p>[]里写链接文字,()里写链接地址, ()中的”“中可以为链接指定title属性,title属性可加可不加。title属性的效果是鼠标悬停在链接上会出现指定的 title文字。链接地址与链接标题前有一个空格。</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">欢迎来到[<span class="string">Django</span>](<span class="link">https://docs.djangoproject.com/zh-hans/3.0/</span>)</span><br><span class="line"></span><br><span class="line">欢迎来到[<span class="string">Django</span>](<span class="link">https://docs.djangoproject.com/zh-hans/3.0/ “Django”</span>)</span><br></pre></td></tr></table></figure><p>欢迎来到<a href="https://docs.djangoproject.com/zh-hans/3.0/" target="_blank" rel="noopener">Django</a></p><p>欢迎来到<a href="https://docs.djangoproject.com/zh-hans/3.0/" target="_blank" rel="noopener" title="Django">Django</a></p><h3 id="参考式"><a class="markdownIt-Anchor" href="#参考式"></a> 参考式</h3><p>参考式超链接一般用在学术论文上面,或者另一种情况,如果某一个链接在文章中多处使用,那么使用引用 的方式创建链接将非常好,它可以让你对链接进行统一的管理。</p><p>语法说明: <code>[文字][链接文字]</code><br />参考式链接分为两部分,文中的写法 [链接文字][链接标记],在文本的任意位置添加[链接标记]:链接地址 “链接标题”,链接地址与链接标题前有一个空格。</p><p>如果链接文字本身可以做为链接标记,你也可以写成[链接文字][]<br />[链接文字]:链接地址的形式,见代码的最后一行。</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">我经常去的几个网站[<span class="string">Google</span>][<span class="symbol">1</span>],[<span class="string">技术博客1</span>][<span class="symbol">2</span>],[<span class="string">技术博客2</span>][<span class="symbol"></span>]。</span><br><span class="line"></span><br><span class="line">[<span class="symbol">1</span>]:<span class="link">http://www.google.com</span></span><br><span class="line">[<span class="symbol">2</span>]:<span class="link">https://yuechuanx.top "技术博客"</span></span><br><span class="line">[<span class="symbol">技术博客</span>]:<span class="link">https://yuechuanx.top/</span></span><br></pre></td></tr></table></figure><p>我经常去的几个网站<a href="http://www.google.com" target="_blank" rel="noopener">Google</a>,<a href="https://yuechuanx.top" title="技术博客">Demi的随笔和技术空间</a>,[Demi的随笔和技术空间][]。</p><p>我经常去的几个网站<a href="http://www.google.com" target="_blank" rel="noopener">Google</a>,<a href="https://yuechuanx.top" title="技术博客">技术博客1</a>,[技术博客2][]。</p><h3 id="自动链接"><a class="markdownIt-Anchor" href="#自动链接"></a> 自动链接</h3><p>语法说明:<br />Markdown 支持以比较简短的自动链接形式来处理网址和电子邮件信箱,只要是用<>包起来, Markdown 就会自动把它转成链接。一般网址的链接文字就和链接地址一样,例如:</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="tag"><<span class="name">http:</span>//<span class="attr">example.com</span>/></span></span></span><br><span class="line"><span class="xml"><span class="tag"><<span class="name">[email protected]</span>></span></span></span><br></pre></td></tr></table></figure><p><a href="http://example.com/" target="_blank" rel="noopener">http://example.com/</a><br /><a href="mailto:[email protected]">[email protected]</a></p><hr /><h2 id="锚点"><a class="markdownIt-Anchor" href="#锚点"></a> 锚点</h2><p>网页中,锚点其实就是页内超链接,也就是链接本文档内部的某些元素,实现当前页面中的跳转。比如我这里写下一个锚点,点击回到目录,就能跳转到目录。 在目录中点击这一节,就能跳过来。还有下一节的注脚。这些根本上都是用锚点来实现的。</p><p>注意:</p><ol><li>Markdown Extra 只支持在标题后插入锚点,其它地方无效。</li><li>Leanote 编辑器右侧显示效果区域暂时不支持锚点跳转,所以点来点去发现没有跳转不必惊慌,但是你发布成笔记或博文后是支持跳转的。</li></ol><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">## 跳转测试{#index}</span></span><br><span class="line"></span><br><span class="line">跳转到[<span class="string">跳转测试</span>](<span class="link">#index</span>)</span><br></pre></td></tr></table></figure><hr /><h2 id="列表"><a class="markdownIt-Anchor" href="#列表"></a> 列表</h2><h3 id="无序列表"><a class="markdownIt-Anchor" href="#无序列表"></a> 无序列表</h3><p>使用 *,+,- 表示无序列表。</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">- </span>无序列表项 一</span><br><span class="line"><span class="bullet">- </span>无序列表项 二</span><br><span class="line"><span class="bullet">- </span>无序列表项 三</span><br></pre></td></tr></table></figure><ul><li>无序列表项 一</li><li>无序列表项 二</li><li>无序列表项 三</li></ul><h3 id="有序列表"><a class="markdownIt-Anchor" href="#有序列表"></a> 有序列表</h3><p>有序列表则使用数字接着一个英文句点。</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">1. </span>有序列表项 一</span><br><span class="line"><span class="bullet">2. </span>有序列表项 二</span><br><span class="line"><span class="bullet">3. </span>有序列表项 三</span><br></pre></td></tr></table></figure><ol><li>有序列表项 一</li><li>有序列表项 二</li><li>有序列表项 三</li></ol><h3 id="定义型列表"><a class="markdownIt-Anchor" href="#定义型列表"></a> 定义型列表</h3><p>语法说明:</p><p>定义型列表由名词和解释组成。一行写上定义,紧跟一行写上解释。解释的写法:紧跟一个缩进(Tab)</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">代码块 1 Markdown</span><br><span class="line">: 轻量级文本标记语言,可以转换成html,pdf等格式(左侧有一个可见的冒号和四个不可见的空格)</span><br><span class="line"></span><br><span class="line">代码块 2</span><br><span class="line">: 这是代码块的定义(左侧有一个可见的冒号和四个不可见的空格)</span><br><span class="line"></span><br><span class="line"><span class="code"> 代码块(左侧有八个不可见的空格)</span></span><br></pre></td></tr></table></figure><p>view</p><dl><dt>代码块 1 Markdown</dt><dd><p>轻量级文本标记语言,可以转换成html,pdf等格式(左侧有一个可见的冒号和四个不可见的空格)</p></dd><dt>代码块 2</dt><dd><p>这是代码块的定义(左侧有一个可见的冒号和四个不可见的空格)</p><pre><code> 代码块(左侧有八个不可见的空格)</code></pre></dd></dl><h3 id="列表缩进"><a class="markdownIt-Anchor" href="#列表缩进"></a> 列表缩进</h3><p>语法说明:</p><p>列表项目标记通常是放在最左边,但是其实也可以缩进,最多 3 个空格,项目标记后面则一定要接着至少一个空格或制表符。</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">* </span>轻轻的我走了, 正如我轻轻的来; 我轻轻的招手, 作别西天的云彩。</span><br><span class="line">那河畔的金柳, 是夕阳中的新娘; 波光里的艳影, 在我的心头荡漾。 </span><br><span class="line">软泥上的青荇, 油油的在水底招摇; 在康河的柔波里, 我甘心做一条水草! </span><br><span class="line"><span class="bullet">* </span>那榆荫下的一潭, 不是清泉, 是天上虹; 揉碎在浮藻间, 沉淀着彩虹似的梦。 </span><br><span class="line">寻梦?撑一支长篙, 向青草更青处漫溯; 满载一船星辉, 在星辉斑斓里放歌。 </span><br><span class="line">但我不能放歌, 悄悄是别离的笙箫; 夏虫也为我沉默, 沉默是今晚的康桥! </span><br><span class="line">悄悄的我走了, 正如我悄悄的来; 我挥一挥衣袖, 不带走一片云彩。</span><br></pre></td></tr></table></figure><p>view</p><ul><li>轻轻的我走了, 正如我轻轻的来; 我轻轻的招手, 作别西天的云彩。<br />那河畔的金柳, 是夕阳中的新娘; 波光里的艳影, 在我的心头荡漾。<br />软泥上的青荇, 油油的在水底招摇; 在康河的柔波里, 我甘心做一条水草!</li><li>那榆荫下的一潭, 不是清泉, 是天上虹; 揉碎在浮藻间, 沉淀着彩虹似的梦。<br />寻梦?撑一支长篙, 向青草更青处漫溯; 满载一船星辉, 在星辉斑斓里放歌。<br />但我不能放歌, 悄悄是别离的笙箫; 夏虫也为我沉默, 沉默是今晚的康桥!<br />悄悄的我走了, 正如我悄悄的来; 我挥一挥衣袖, 不带走一片云彩。</li></ul><h3 id="包含段落的列表"><a class="markdownIt-Anchor" href="#包含段落的列表"></a> 包含段落的列表</h3><p>语法说明:</p><p>列表项目可以包含多个段落,每个项目下的段落都必须缩进 4 个空格或是 1 个制表符(显示效果与代码一致):</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">* </span>轻轻的我走了, 正如我轻轻的来; 我轻轻的招手, 作别西天的云彩。</span><br><span class="line">那河畔的金柳, 是夕阳中的新娘; 波光里的艳影, 在我的心头荡漾。 </span><br><span class="line">软泥上的青荇, 油油的在水底招摇; 在康河的柔波里, 我甘心做一条水草!</span><br><span class="line"></span><br><span class="line"><span class="code"> 那榆荫下的一潭, 不是清泉, 是天上虹; 揉碎在浮藻间, 沉淀着彩虹似的梦。 </span></span><br><span class="line">寻梦?撑一支长篙, 向青草更青处漫溯; 满载一船星辉, 在星辉斑斓里放歌。 </span><br><span class="line">但我不能放歌, 悄悄是别离的笙箫; 夏虫也为我沉默, 沉默是今晚的康桥!</span><br><span class="line"></span><br><span class="line"><span class="bullet">* </span>悄悄的我走了, 正如我悄悄的来; 我挥一挥衣袖, 不带走一片云彩。</span><br></pre></td></tr></table></figure><p>view</p><ul><li><p>轻轻的我走了, 正如我轻轻的来; 我轻轻的招手, 作别西天的云彩。<br />那河畔的金柳, 是夕阳中的新娘; 波光里的艳影, 在我的心头荡漾。<br />软泥上的青荇, 油油的在水底招摇; 在康河的柔波里, 我甘心做一条水草!</p><p>那榆荫下的一潭, 不是清泉, 是天上虹; 揉碎在浮藻间, 沉淀着彩虹似的梦。<br />寻梦?撑一支长篙, 向青草更青处漫溯; 满载一船星辉, 在星辉斑斓里放歌。<br />但我不能放歌, 悄悄是别离的笙箫; 夏虫也为我沉默, 沉默是今晚的康桥!</p></li><li><p>悄悄的我走了, 正如我悄悄的来; 我挥一挥衣袖, 不带走一片云彩。</p></li></ul><h3 id="包含引用的列表"><a class="markdownIt-Anchor" href="#包含引用的列表"></a> 包含引用的列表</h3><p>语法说明:</p><p>如果要在列表项目内放进引用,那 > 就需要缩进:</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">* </span>阅读的方法:</span><br><span class="line"><span class="code"> > 打开书本。</span></span><br><span class="line"><span class="code"> > 打开电灯。</span></span><br></pre></td></tr></table></figure><p>view</p><ul><li>阅读的方法:<blockquote><p>打开书本。<br />打开电灯。</p></blockquote></li></ul><h3 id="包含代码区块的引用"><a class="markdownIt-Anchor" href="#包含代码区块的引用"></a> 包含代码区块的引用</h3><p>语法说明:<br />如果要放代码区块的话,该区块就需要缩进两次,也就是 8 个空格或是 2 个制表符:</p><h3 id="一个特殊情况"><a class="markdownIt-Anchor" href="#一个特殊情况"></a> 一个特殊情况</h3><p>在特殊情况下,项目列表很可能会不小心产生,像是下面这样的写法:</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">1986. </span>What a great season.</span><br></pre></td></tr></table></figure><p>会显示成:</p><ol start="1986"><li>What a great season.</li></ol><p>换句话说,也就是在行首出现数字-句点-空白,要避免这样的状况,你可以在句点前面加上反斜杠:</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1986\. What a great season.</span><br></pre></td></tr></table></figure><p>才会正常显示成:</p><p>1986. What a great season.</p><hr /><h2 id="引用"><a class="markdownIt-Anchor" href="#引用"></a> 引用</h2><p>语法说明:<br />引用需要在被引用的文本前加上>符号。</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="quote">> 这是一个有两段文字的引用,</span></span><br><span class="line">无意义的占行文字1.</span><br><span class="line">无意义的占行文字2.</span><br><span class="line"></span><br><span class="line"><span class="quote">> 无意义的占行文字3.</span></span><br><span class="line">无意义的占行文字4.</span><br></pre></td></tr></table></figure><p>view</p><blockquote><p>这是一个有两段文字的引用,<br />无意义的占行文字1.<br />无意义的占行文字2.</p></blockquote><blockquote><p>无意义的占行文字3.<br />无意义的占行文字4.</p></blockquote><h3 id="引用的多层嵌套"><a class="markdownIt-Anchor" href="#引用的多层嵌套"></a> 引用的多层嵌套</h3><p>区块引用可以嵌套(例如:引用内的引用),只要根据层次加上不同数量的 > :</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">>>> 请问 Markdwon 怎么用? - 小白</span><br><span class="line"></span><br><span class="line">>> 自己看教程! - 愤青</span><br><span class="line"></span><br><span class="line"><span class="quote">> 教程在哪? - 小白</span></span><br></pre></td></tr></table></figure><blockquote><blockquote><blockquote><p>请问 Markdwon 怎么用? - 小白</p></blockquote></blockquote></blockquote><blockquote><blockquote><p>自己看教程! - 愤青</p></blockquote></blockquote><blockquote><p>教程在哪? - 小白</p></blockquote><h3 id="引用其它要素"><a class="markdownIt-Anchor" href="#引用其它要素"></a> 引用其它要素</h3><p>引用的区块内也可以使用其他的 Markdown 语法,包括标题、列表、代码区块等:</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="quote">> 1. 这是第一行列表项。</span></span><br><span class="line"><span class="quote">> 2. 这是第二行列表项。</span></span><br><span class="line"><span class="quote">> </span></span><br><span class="line"><span class="quote">> 给出一些例子代码:</span></span><br><span class="line"><span class="quote">> </span></span><br><span class="line"><span class="quote">> return shell_exec("echo $input | $markdown_script");</span></span><br></pre></td></tr></table></figure><blockquote><ol><li>这是第一行列表项。</li><li>这是第二行列表项。</li></ol><p>给出一些例子代码:</p><pre><code>return shell_exec("echo $input | $markdown_script");</code></pre></blockquote><hr /><h2 id="插入图像"><a class="markdownIt-Anchor" href="#插入图像"></a> 插入图像</h2><p>图片的创建方式与超链接相似,而且和超链接一样也有两种写法,行内式和参考式写法。</p><p>语法中图片Alt的意思是如果图片因为某些原因不能显示,就用定义的图片Alt文字来代替图片。 图片Title则和链接中的Title一样,表示鼠标悬停与图片上时出现的文字。 Alt 和 Title 都不是必须的,可以省略,但建议写上。</p><h3 id="行内式-2"><a class="markdownIt-Anchor" href="#行内式-2"></a> 行内式</h3><p>语法说明:![图片Alt](图片地址 “图片Title”)</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">美丽风景: </span><br><span class="line">![<span class="string">美丽风景</span>](<span class="link">https://yuhongjun.github.io/assets/media/scenery.jpeg "美丽风景"</span>)</span><br></pre></td></tr></table></figure><p>view<br />美丽风景:<br /><img src="https://yuhongjun.github.io/assets/media/scenery.jpeg" alt="美丽风景" title="美丽风景" /></p><h3 id="参考式-2"><a class="markdownIt-Anchor" href="#参考式-2"></a> 参考式</h3><p>语法说明:</p><p>在文档要插入图片的地方写![图片Alt][标记]</p><p>在文档的最后写上[标记]:图片地址 “Title”</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">![<span class="string">美丽风景</span>](<span class="link">https://yuhongjun.github.io/assets/media/scenery.jpeg "美丽风景"</span>)</span><br><span class="line"></span><br><span class="line">[<span class="symbol">scenery</span>]:<span class="link">https://yuhongjun.github.io/assets/media/scenery.jpeg "美丽风景"</span></span><br></pre></td></tr></table></figure><p>view<br /><img src="https://yuhongjun.github.io/assets/media/scenery.jpeg" alt="美丽风景" title="美丽风景" /></p><hr /><h2 id="内容目录"><a class="markdownIt-Anchor" href="#内容目录"></a> 内容目录</h2><p>在段落中填写 <code>{toc}</code> 以显示全文内容的目录结构。</p><p>效果参见最上方的目录</p><hr /><h2 id="注脚"><a class="markdownIt-Anchor" href="#注脚"></a> 注脚</h2><p>语法说明:</p><p>在需要添加注脚的文字后加上脚注名字[^注脚名字],称为加注。 然后在文本的任意位置(一般在最后)添加脚注,脚注前必须有对应的脚注名字。</p><p>注意:经测试注脚与注脚之间必须空一行,不然会失效。成功后会发现,即使你没有把注脚写在文末,经Markdown转换后,也会自动归类到文章的最后。</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">使用 Markdown[^1]可以效率的书写文档, 直接转换成 HTML[^2], 你可以使用 Leanote[^Le] 编辑器进行书写。</span><br><span class="line"></span><br><span class="line">[<span class="symbol">^1</span>]:<span class="link">Markdown是一种纯文本标记语言</span></span><br><span class="line"></span><br><span class="line">[<span class="symbol">^2</span>]:<span class="link">HyperText Markup Language 超文本标记语言</span></span><br><span class="line"></span><br><span class="line">[<span class="symbol">^Le</span>]:<span class="link">开源笔记平台,支持Markdown和笔记直接发为博文</span></span><br></pre></td></tr></table></figure><p>view</p><p>使用 Markdown<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>可以效率的书写文档, 直接转换成 HTML<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>, 你可以使用 Leanote<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup> 编辑器进行书写。</p><p>注:脚注自动被搬运到最后面,请到文章末尾查看,并且脚注后方的链接可以直接跳转回到加注的地方。</p><hr /><h2 id="latex-公式"><a class="markdownIt-Anchor" href="#latex-公式"></a> LaTeX 公式</h2><h3 id="表示行内公式"><a class="markdownIt-Anchor" href="#表示行内公式"></a> <code>$</code> 表示行内公式:</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">质能守恒方程可以用一个很简洁的方程式 $E=mc^2$ 来表达。</span><br></pre></td></tr></table></figure><p>view<br />质能守恒方程可以用一个很简洁的方程式 <span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>E</mi><mo>=</mo><mi>m</mi><msup><mi>c</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">E=mc^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord mathdefault" style="margin-right:0.05764em;">E</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.8141079999999999em;vertical-align:0em;"></span><span class="mord mathdefault">m</span><span class="mord"><span class="mord mathdefault">c</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span> 来表达。</p><h3 id="表示整行公式"><a class="markdownIt-Anchor" href="#表示整行公式"></a> <code>$$</code> 表示整行公式:</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$$\sum<span class="emphasis">_{i=1}^n a_</span>i=0$$</span><br><span class="line">$$f(x<span class="emphasis">_1,x_</span>x,\ldots,x<span class="emphasis">_n) = x_</span>1^2 + x<span class="emphasis">_2^2 + \cdots + x_</span>n^2 $$</span><br><span class="line">$$\sum^{j-1}<span class="emphasis">_{k=0}{\widehat{\gamma}_</span>{kj} z_k}$$</span><br></pre></td></tr></table></figure><p class='katex-block'><span class="katex-display"><span class="katex"><span class="katex-mathml"><math><semantics><mrow><munderover><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>n</mi></munderover><msub><mi>a</mi><mi>i</mi></msub><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\sum_{i=1}^n a_i=0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:2.929066em;vertical-align:-1.277669em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.6513970000000002em;"><span style="top:-1.872331em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight">i</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.050005em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∑</span></span></span><span style="top:-4.3000050000000005em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">n</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:1.277669em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.31166399999999994em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">i</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:0.64444em;vertical-align:0em;"></span><span class="mord">0</span></span></span></span></span></p><p class='katex-block'><span class="katex-display"><span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><msub><mi>x</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>x</mi><mi>x</mi></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>x</mi><mi>n</mi></msub><mo stretchy="false">)</mo><mo>=</mo><msubsup><mi>x</mi><mn>1</mn><mn>2</mn></msubsup><mo>+</mo><msubsup><mi>x</mi><mn>2</mn><mn>2</mn></msubsup><mo>+</mo><mo>⋯</mo><mo>+</mo><msubsup><mi>x</mi><mi>n</mi><mn>2</mn></msubsup></mrow><annotation encoding="application/x-tex">f(x_1,x_x,\ldots,x_n) = x_1^2 + x_2^2 + \cdots + x_n^2 </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.30110799999999993em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.151392em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">x</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="minner">…</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.151392em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">n</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2777777777777778em;"></span></span><span class="base"><span class="strut" style="height:1.1111079999999998em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.1111079999999998em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.66666em;vertical-align:-0.08333em;"></span><span class="minner">⋯</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:1.1111079999999998em;vertical-align:-0.247em;"></span><span class="mord"><span class="mord mathdefault">x</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-2.4530000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight">n</span></span></span><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.247em;"><span></span></span></span></span></span></span></span></span></span></span></p><p class='katex-block'><span class="katex-display"><span class="katex"><span class="katex-mathml"><math><semantics><mrow><munderover><mo>∑</mo><mrow><mi>k</mi><mo>=</mo><mn>0</mn></mrow><mrow><mi>j</mi><mo>−</mo><mn>1</mn></mrow></munderover><mrow><msub><mover accent="true"><mi>γ</mi><mo stretchy="true">^</mo></mover><mrow><mi>k</mi><mi>j</mi></mrow></msub><msub><mi>z</mi><mi>k</mi></msub></mrow></mrow><annotation encoding="application/x-tex">\sum^{j-1}_{k=0}{\widehat{\gamma}_{kj} z_k}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:3.16089em;vertical-align:-1.302113em;"></span><span class="mop op-limits"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.8587770000000001em;"><span style="top:-1.8478869999999998em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.03148em;">k</span><span class="mrel mtight">=</span><span class="mord mtight">0</span></span></span></span><span style="top:-3.0500049999999996em;"><span class="pstrut" style="height:3.05em;"></span><span><span class="mop op-symbol large-op">∑</span></span></span><span style="top:-4.347113em;margin-left:0em;"><span class="pstrut" style="height:3.05em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.05724em;">j</span><span class="mbin mtight">−</span><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:1.302113em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.16666666666666666em;"></span><span class="mord"><span class="mord"><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.67056em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.05556em;">γ</span></span></span><span class="svg-align" style="top:-3.43056em;"><span class="pstrut" style="height:3em;"></span><span style="height:0.24em;"><svg width='100%' height='0.24em' viewBox='0 0 1062 239' preserveAspectRatio='none'><path d='M529 0h5l519 115c5 1 9 5 9 10 0 1-1 2-1 3l-4 22c-1 5-5 9-11 9h-2L532 67 19 159h-2c-5 0-9-4-11-9l-5-22c-1-6 2-12 8-13z'/></svg></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.19444em;"><span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361079999999999em;"><span style="top:-2.5500000000000003em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathdefault mtight" style="margin-right:0.03148em;">k</span><span class="mord mathdefault mtight" style="margin-right:0.05724em;">j</span></span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.286108em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathdefault" style="margin-right:0.04398em;">z</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.33610799999999996em;"><span style="top:-2.5500000000000003em;margin-left:-0.04398em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathdefault mtight" style="margin-right:0.03148em;">k</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></span></span></p><p>访问 <a href="https://math.meta.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference" target="_blank" rel="noopener" title="MathJax basic tutorial and quick reference">MathJax</a> 参考更多使用方法。</p><hr /><h2 id="流程图"><a class="markdownIt-Anchor" href="#流程图"></a> 流程图</h2><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">st=>start: Start|past:>https://yuhongjun.github.io[blank]</span><br><span class="line">e=>end: End:>https://yuhongjun.github.io</span><br><span class="line">op1=>operation: My Operation|past</span><br><span class="line">op2=>operation: Stuff|current</span><br><span class="line">sub1=>subroutine: My Subroutine|invalid</span><br><span class="line">cond=>condition: Yes</span><br><span class="line">or No?|approved:>https://yuhongjun.github.io</span><br><span class="line">c2=>condition: Good idea|rejected</span><br><span class="line">io=>inputoutput: catch something...|request</span><br><span class="line">st->op1(right)->cond</span><br><span class="line">cond(yes, right)->c2</span><br><span class="line">cond(no)->sub1(left)->op1</span><br><span class="line">c2(yes)->io->e</span><br><span class="line">c2(no)->op2->e</span><br></pre></td></tr></table></figure><div id="flowchart-0" class="flow-chart"></div><p>更多语法参考:<a href="https://flowchart.js.org/" target="_blank" rel="noopener" title="flowchart">流程图语法参考</a></p><hr /><h2 id="表格"><a class="markdownIt-Anchor" href="#表格"></a> 表格</h2><p>语法说明:</p><p>不管是哪种方式,第一行为表头,第二行分隔表头和主体部分,第三行开始每一行为一个表格行。<br />列于列之间用管道符|隔开。原生方式的表格每一行的两边也要有管道符。<br />第二行还可以为不同的列指定对齐方向。默认为左对齐,在-右边加上:就右对齐。</p><ol><li>简单方式写表格:</li></ol><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">学号|姓名|分数</span><br><span class="line">-|-|-</span><br><span class="line">小明|男|75</span><br><span class="line">小红|女|79</span><br><span class="line">小陆|男|92</span><br></pre></td></tr></table></figure><p>2.原生方式写表格:</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">|学号|姓名|分数|</span><br><span class="line">|-|-|-|</span><br><span class="line">|小明|男|75|</span><br><span class="line">|小红|女|79|</span><br><span class="line">|小陆|男|92|</span><br></pre></td></tr></table></figure><p>3.为表格第二列指定方向:</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">产品|价格</span><br><span class="line">-|-:</span><br><span class="line">Leanote 高级账号|60元/年</span><br><span class="line">Leanote 超级账号|120元/年</span><br></pre></td></tr></table></figure><p>view</p><ol><li>简单方式写表格:</li></ol><table><thead><tr><th>学号</th><th>姓名</th><th>分数</th></tr></thead><tbody><tr><td>小明</td><td>男</td><td>75</td></tr><tr><td>小红</td><td>女</td><td>79</td></tr><tr><td>小陆</td><td>男</td><td>92</td></tr></tbody></table><p>2.原生方式写表格:</p><table><thead><tr><th>学号</th><th>姓名</th><th>分数</th></tr></thead><tbody><tr><td>小明</td><td>男</td><td>75</td></tr><tr><td>小红</td><td>女</td><td>79</td></tr><tr><td>小陆</td><td>男</td><td>92</td></tr></tbody></table><p>3.为表格第二列指定方向:</p><table><thead><tr><th>产品</th><th style="text-align:right">价格</th></tr></thead><tbody><tr><td>Leanote 高级账号</td><td style="text-align:right">60元/年</td></tr><tr><td>Leanote 超级账号</td><td style="text-align:right">120元/年</td></tr></tbody></table><h2 id="分隔线"><a class="markdownIt-Anchor" href="#分隔线"></a> 分隔线</h2><p>你可以在一行中用三个以上的星号、减号、底线来建立一个分隔线,行内不能有其他东西。你也可以在星号或是减号中间插入空格。下面每种写法都可以建立分隔线:</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">* </span><span class="bullet">* *</span></span><br><span class="line"></span><br><span class="line"><span class="emphasis">***</span></span><br><span class="line"></span><br><span class="line"><span class="strong">*****</span></span><br><span class="line"></span><br><span class="line"><span class="bullet">- </span>- -</span><br><span class="line"></span><br><span class="line">---------------------------------------</span><br></pre></td></tr></table></figure><p>显示效果都一样</p><hr /><h2 id="代码"><a class="markdownIt-Anchor" href="#代码"></a> 代码</h2><p>对于程序员来说这个功能是必不可少的,插入程序代码的方式有两种,一种是利用缩进(Tab), 另一种是利用 ` 符号(一般在ESC键下方)包裹代码。</p><p>语法说明:</p><ol><li>插入行内代码,即插入一个单词或者一句代码的情况,使用<code>code</code>这样的形式插入。</li><li>插入多行代码,可以使用缩进或者“<code>code “</code>,具体看示例。</li></ol><p><strong>注意: 缩进式插入前方必须有空行</strong></p><h3 id="行内式-3"><a class="markdownIt-Anchor" href="#行内式-3"></a> 行内式</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C语言里的函数 <span class="code">`scanf()`</span> 怎么使用?</span><br></pre></td></tr></table></figure><p>view<br />C语言里的函数 <code>scanf()</code> 怎么使用?</p><h3 id="缩进式多行代码"><a class="markdownIt-Anchor" href="#缩进式多行代码"></a> 缩进式多行代码</h3><p>缩进 4 个空格或是 1 个制表符</p><p>一个代码区块会一直持续到没有缩进的那一行(或是文件结尾)。</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">#include <stdio.h></span></span><br><span class="line">int main(void)</span><br><span class="line">{</span><br><span class="line"><span class="code"> printf("Hello world\n");</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>view</p><pre><code>#include <stdio.h>int main(void){ printf("Hello world\n");}</code></pre><h3 id="包裹多行代码"><a class="markdownIt-Anchor" href="#包裹多行代码"></a> 包裹多行代码</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="comment"><!-- 用 ``` 或 ~~~ 包裹多行代码 --></span></span></span><br><span class="line"><span class="code">` `</span> `</span><br><span class="line"><span class="section">#include <stdio.h></span></span><br><span class="line">int main(void)</span><br><span class="line">{</span><br><span class="line"><span class="code"> printf("Hello world\n");</span></span><br><span class="line">}</span><br><span class="line">、、、</span><br></pre></td></tr></table></figure><h3 id="html-原始码"><a class="markdownIt-Anchor" href="#html-原始码"></a> HTML 原始码</h3><p>在代码区块里面, & 、 < 和 > 会自动转成 HTML 实体,这样的方式让你非常容易使用 Markdown 插入范例用的 HTML 原始码,只需要复制贴上,剩下的 Markdown 都会帮你处理,例如:</p><p>第一个例子:</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"footer"</span>></span></span></span><br><span class="line"> © 2016 <span class="emphasis">***</span></span><br><span class="line"><span class="xml"><span class="tag"></<span class="name">div</span>></span></span></span><br></pre></td></tr></table></figure><p>view</p><div class=""> © 2016 ***</div><script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.2.7/raphael.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/flowchart/1.6.5/flowchart.min.js"></script><textarea id="flowchart-0-code" style="display: none">chartst=>start: Start|past:>https://yuhongjun.github.io[blank]e=>end: End:>https://yuhongjun.github.ioop1=>operation: My Operation|pastop2=>operation: Stuff|currentsub1=>subroutine: My Subroutine|invalidcond=>condition: Yesor No?|approved:>https://yuhongjun.github.ioc2=>condition: Good idea|rejectedio=>inputoutput: catch something...|requestst->op1(right)->condcond(yes, right)->c2cond(no)->sub1(left)->op1c2(yes)->io->ec2(no)->op2->e</textarea><textarea id="flowchart-0-options" style="display: none">{"scale":1,"line-width":2,"line-length":50,"text-margin":10,"font-size":12}</textarea><script> var code = document.getElementById("flowchart-0-code").value; var options = JSON.parse(decodeURIComponent(document.getElementById("flowchart-0-options").value)); var diagram = flowchart.parse(code); diagram.drawSVG("flowchart-0", options);</script><hr class="footnotes-sep" /><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p>Markdown是一种纯文本标记语言 <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p>HyperText Markup Language 超文本标记语言 <a href="#fnref2" class="footnote-backref">↩︎</a></p></li><li id="fn3" class="footnote-item"><p>开源笔记平台,支持Markdown和笔记直接发为博文 <a href="#fnref3" class="footnote-backref">↩︎</a></p></li></ol></section>]]></content>
<summary type="html">
<hr />
<h2 id="斜体和粗体"><a class="markdownIt-Anchor" href="#斜体和粗体"></a> 斜体和粗体</h2>
<figure class="highlight markdown"><table><tr><td class="gu
</summary>
<category term="markdown" scheme="https://yuechuanx.top/tags/markdown/"/>
</entry>
<entry>
<title><流畅的Python> Python 数据类型</title>
<link href="https://yuechuanx.top/Python/fluent-python-notes-chap-01/"/>
<id>https://yuechuanx.top/Python/fluent-python-notes-chap-01/</id>
<published>2019-12-11T06:21:56.000Z</published>
<updated>2019-12-11T06:21:56.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>Guido 对语言设计美学的深入理解让人震惊。我认识不少很不错的编程语言设计者,他们设计出来的东西确实很精彩,但是从来都不会有用户。Guido 知道如何在理论上做出一定妥协,设计出来的语言让使用者觉得如沐春风,这真是不可多得。<br />——Jim Hugunin<br />Jython 的作者,AspectJ 的作者之一,.NET DLR 架构师</p></blockquote><p>Python 最好的品质之一是<strong>一致性</strong>:你可以轻松理解 Python 语言,并通过 Python 的语言特性在类上定义<strong>规范的接口</strong>,来支持 Python 的核心语言特性,从而写出具有“Python 风格”的对象。</p><p>Python 解释器在碰到特殊的句法时,会使用特殊方法(我们称之为魔术方法)去激活一些基本的对象操作。</p><blockquote><p><code>__getitem__</code> 以双下划线开头的特殊方法,称为 dunder-getitem。特殊方法也称为双下方法(dunder-method)</p></blockquote><p>如 <code>my_c[key]</code> 语句执行时,就会调用 <code>my_c.__getitem__</code> 函数。这些特殊方法名能让你自己的对象实现和支持一下的语言构架,并与之交互:</p><ul><li>迭代</li><li>集合类</li><li>属性访问</li><li>运算符重载</li><li>函数和方法的调用</li><li>对象的创建和销毁</li><li>字符串表示形式和格式化</li><li>管理上下文(即 <code>with</code> 块)</li></ul><h2 id="实现一个-pythonic-的牌组"><a class="markdownIt-Anchor" href="#实现一个-pythonic-的牌组"></a> 实现一个 Pythonic 的牌组</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 通过实现魔术方法,来让内置函数支持你的自定义对象</span></span><br><span class="line"><span class="comment"># https://github.com/fluentpython/example-code/blob/master/01-data-model/frenchdeck.py</span></span><br><span class="line"><span class="keyword">import</span> collections</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"></span><br><span class="line">Card = collections.namedtuple(<span class="string">'Card'</span>, [<span class="string">'rank'</span>, <span class="string">'suit'</span>])</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FrenchDeck</span>:</span></span><br><span class="line"> ranks = [str(n) <span class="keyword">for</span> n <span class="keyword">in</span> range(<span class="number">2</span>, <span class="number">11</span>)] + list(<span class="string">'JQKA'</span>)</span><br><span class="line"> suits = <span class="string">'spades diamonds clubs hearts'</span>.split()</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></span><br><span class="line"> self._cards = [Card(rank, suit) <span class="keyword">for</span> suit <span class="keyword">in</span> self.suits</span><br><span class="line"> <span class="keyword">for</span> rank <span class="keyword">in</span> self.ranks]</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__len__</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> len(self._cards)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__getitem__</span><span class="params">(self, position)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> self._cards[position]</span><br><span class="line"> </span><br><span class="line">deck = FrenchDeck()</span><br></pre></td></tr></table></figure><p>可以容易地获得一个纸牌对象</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">beer_card = Card(<span class="string">'7'</span>, <span class="string">'diamonds'</span>)</span><br><span class="line">print(beer_card)</span><br></pre></td></tr></table></figure><p>和标准 Python 集合类型一样,使用 <code>len()</code> 查看一叠纸牌有多少张</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">deck = FrenchDeck()</span><br><span class="line"><span class="comment"># 实现 __len__ 以支持下标操作</span></span><br><span class="line">print(len(deck))</span><br></pre></td></tr></table></figure><p>可选取特定一张纸牌,这是由 <code>__getitem__</code> 方法提供的</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 实现 __getitem__ 以支持下标操作</span></span><br><span class="line">print(deck[<span class="number">1</span>])</span><br><span class="line">print(deck[<span class="number">5</span>::<span class="number">13</span>])</span><br></pre></td></tr></table></figure><p>随机抽取一张纸牌,使用 python 内置函数 <code>random.choice</code></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> random <span class="keyword">import</span> choice</span><br><span class="line"><span class="comment"># 可以多运行几次观察</span></span><br><span class="line">choice(deck)</span><br></pre></td></tr></table></figure><p>实现特殊方法的两个好处:</p><ul><li>对于标准操作有固定命名</li><li>更方便利用 Python 标准库</li></ul><p><code>__getitem__</code> 方法把 [] 操作交给了 <code>self._cards</code> 列表,deck 类自动支持切片操作</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">deck[<span class="number">12</span>::<span class="number">13</span>]</span><br><span class="line">deck[:<span class="number">3</span>]</span><br></pre></td></tr></table></figure><p>同时 deck 类支持迭代</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> card <span class="keyword">in</span> deck:</span><br><span class="line"> print(card)</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 反向迭代</span></span><br><span class="line"><span class="keyword">for</span> card <span class="keyword">in</span> reversed(deck):</span><br><span class="line"> print(card)</span><br></pre></td></tr></table></figure><p>迭代通常是隐式的,如果一个集合没有实现 <code>__contains__</code> 方法,那么 in 运算符会顺序做一次迭代搜索。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Card(<span class="string">'Q'</span>, <span class="string">'hearts'</span>) <span class="keyword">in</span> deck </span><br><span class="line">Card(<span class="string">'7'</span>, <span class="string">'beasts'</span>) <span class="keyword">in</span> deck</span><br></pre></td></tr></table></figure><pre><code>False</code></pre><p>进行排序,排序规则:<br />2 最小,A最大。花色 黑桃 > 红桃 > 方块 > 梅花</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">card.rank</span><br></pre></td></tr></table></figure><pre><code>'A'</code></pre><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">suit_values = dict(spades=<span class="number">3</span>, hearts=<span class="number">2</span>, diamonds=<span class="number">1</span>, clubs=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">spades_high</span><span class="params">(card)</span>:</span></span><br><span class="line"> rank_value = FrenchDeck.ranks.index(card.rank)</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> rank_value * len(suit_values) + suit_values[card.suit]</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> card <span class="keyword">in</span> sorted(deck, key=spades_high):</span><br><span class="line"> print(card)</span><br></pre></td></tr></table></figure><p>FrenchDeck 继承了 object 类。通过 <code>__len__</code>, <code>__getitem__</code> 方法,FrenchDeck和 Python 自有序列数据类型一样,可体现 Python 核心语言特性(如迭代和切片),</p><p>Python 支持的所有魔术方法,可以参见 Python 文档 <a href="https://docs.python.org/3/reference/datamodel.html" target="_blank" rel="noopener">Data Model</a> 部分。</p><p>比较重要的一点:不要把 <code>len</code>,<code>str</code> 等看成一个 Python 普通方法:由于这些操作的频繁程度非常高,所以 Python 对这些方法做了特殊的实现:它可以让 Python 的内置数据结构走后门以提高效率;但对于自定义的数据结构,又可以在对象上使用通用的接口来完成相应工作。但在代码编写者看来,<code>len(deck)</code> 和 <code>len([1,2,3])</code> 两个实现可能差之千里的操作,在 Python 语法层面上是高度一致的。</p><h2 id="如何使用特殊方法"><a class="markdownIt-Anchor" href="#如何使用特殊方法"></a> 如何使用特殊方法</h2><p>特殊方法的存在是为了被 Python 解释器调用<br />除非大量元编程,通常代码无需直接使用特殊方法<br />通过内置函数来使用特殊方法是最好的选择</p><h3 id="模拟数值类型"><a class="markdownIt-Anchor" href="#模拟数值类型"></a> 模拟数值类型</h3><p>实现一个二维向量(Vector)类</p><p><img src="attachment:image.png" alt="image.png" /></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> math <span class="keyword">import</span> hypot</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Vector</span>:</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, x=<span class="number">0</span>, y=<span class="number">0</span>)</span>:</span></span><br><span class="line"> self.x = x</span><br><span class="line"> self.y = y</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__repr__</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">'Vector(%r, %r)'</span> % (self.x, self.y)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__abs__</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> hypot(self.x, self.y)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__bool__</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> bool(abs(self))</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__add__</span><span class="params">(self, other)</span>:</span></span><br><span class="line"> x = self.x + other.x</span><br><span class="line"> y = self.y + other.y</span><br><span class="line"> <span class="keyword">return</span> Vector(x, y)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__mul__</span><span class="params">(self, scalar)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> Vector(self.x * scalar, self.y * scalar)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 + 运算符</span></span><br><span class="line">v1 = Vector(<span class="number">2</span>, <span class="number">4</span>)</span><br><span class="line">v2 = Vector(<span class="number">2</span>, <span class="number">1</span>)</span><br><span class="line">v1 + v2</span><br><span class="line"></span><br><span class="line"><span class="comment"># 调用 abs 内置函数</span></span><br><span class="line">v = Vector(<span class="number">3</span>, <span class="number">4</span>)</span><br><span class="line">abs(v)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 * 运算符</span></span><br><span class="line">v * <span class="number">3</span></span><br></pre></td></tr></table></figure><p>Vector 类中 6 个方法(除 <code>__init__</code> 外)并不会在类自身的代码中调用。一般只有解释器会频繁调用这些方法</p><h3 id="字符串的表示形式"><a class="markdownIt-Anchor" href="#字符串的表示形式"></a> 字符串的表示形式</h3><p>内置函数 repr, 通过 <code>__repr__</code> 特殊方法来得到一个对象的字符串表示形式。</p><h3 id="算数运算符"><a class="markdownIt-Anchor" href="#算数运算符"></a> 算数运算符</h3><p>通过 <code>__add__</code>, <code>__mul__</code>, 向量类能够操作 + 和 * 两个算数运算符。</p><blockquote><p>运算符操作对象不发生改变,返回一个产生的新值</p></blockquote><h3 id="自定义的布尔值"><a class="markdownIt-Anchor" href="#自定义的布尔值"></a> 自定义的布尔值</h3><ul><li>任何对象可用于需要布尔值的上下文中(if, while 语句, and, or, not 运算符)</li><li>Python 调用 bool(x) 判定一个值 x,bool(x) 只能返回 True 或者 False</li><li>如果类没有实现 <code>__bool__</code>,则调用 <code>__len__</code>, 若返回 0,则 bool 返回 False</li></ul><h2 id="特殊方法一览"><a class="markdownIt-Anchor" href="#特殊方法一览"></a> 特殊方法一览</h2><p><a href="https://docs.python.org/3/reference/datamodel.html" target="_blank" rel="noopener">Reference</a></p><h2 id="为何-len-不是普通方法"><a class="markdownIt-Anchor" href="#为何-len-不是普通方法"></a> 为何 len 不是普通方法</h2><blockquote><h2 id="实用胜于纯粹"><a class="markdownIt-Anchor" href="#实用胜于纯粹"></a> “实用胜于纯粹“</h2><p><em>The Zen of Python</em></p></blockquote><p>为了让 Python 自带的数据结构走后门, CPython 会直接从结构体读取对象的长度,而不会调用方法.<br />这种处理方式在保持内置类型的效率和语言一致性保持了一个平衡.</p><h2 id="小结"><a class="markdownIt-Anchor" href="#小结"></a> 小结</h2><ul><li>通过实现特殊方法,自定义数据类型可以像内置类型一样处理</li><li>合理的字符串表示形式是Python对象的基本要求。<code>__repr__</code>, <code>__str__</code></li><li>序列类型的模拟是特殊方法最常用的地方</li></ul>]]></content>
<summary type="html">
<blockquote>
<p>Guido 对语言设计美学的深入理解让人震惊。我认识不少很不错的编程语言设计者,他们设计出来的东西确实很精彩,但是从来都不会有用户。Guido 知道如何在理论上做出一定妥协,设计出来的语言让使用者觉得如沐春风,这真是不可多得。<br />
——Ji
</summary>
<category term="Python" scheme="https://yuechuanx.top/categories/Python/"/>
<category term="python" scheme="https://yuechuanx.top/tags/python/"/>
</entry>
<entry>
<title>如何编写最佳的Dockerfile</title>
<link href="https://yuechuanx.top/DevOps/how-to-write-excellent-dockerfile/"/>
<id>https://yuechuanx.top/DevOps/how-to-write-excellent-dockerfile/</id>
<published>2019-12-01T08:29:15.000Z</published>
<updated>2019-12-01T08:29:15.000Z</updated>
<content type="html"><![CDATA[<blockquote><p><strong>译者按:</strong> Dockerfile 的语法非常简单,然而如何加快镜像构建速度,如何减少 Docker 镜像的大小却不是那么直观,需要积累实践经验。这篇博客可以帮助你快速掌握编写 Dockerfile 的技巧。</p><ul><li>原文: <a href="https://rock-it.pl/how-to-write-excellent-dockerfiles/" target="_blank" rel="noopener">How to write excellent Dockerfiles</a></li><li>译者: <a href="https://www.fundebug.com/" target="_blank" rel="noopener">Fundebug</a></li></ul><p><strong>本文采用意译,版权归原作者所有</strong></p></blockquote><hr /><p>我已经使用 Docker 有一段时间了,其中编写 Dockerfile 是非常重要的一部分工作。在这篇博客中,我打算分享一些建议,帮助大家编写更好的 Dockerfile。</p><h3 id="目标"><a class="markdownIt-Anchor" href="#目标"></a> <strong>目标</strong></h3><ul><li>更快的构建速度</li><li>更小的 Docker 镜像大小</li><li>更少的 Docker 镜像层</li><li>充分利用镜像缓存</li><li>增加 Dockerfile 可读性</li><li>让 Docker 容器使用起来更简单</li></ul><h3 id="总结"><a class="markdownIt-Anchor" href="#总结"></a> <strong>总结</strong></h3><ul><li>编写.dockerignore 文件</li><li>容器只运行单个应用</li><li>将多个 RUN 指令合并为一个</li><li>基础镜像的标签不要用 latest</li><li>每个 RUN 指令后删除多余文件</li><li>选择合适的基础镜像(alpine 版本最好)</li><li>设置 WORKDIR 和 CMD</li><li>使用 ENTRYPOINT (可选)</li><li>在 entrypoint 脚本中使用 exec</li><li>COPY 与 ADD 优先使用前者</li><li>合理调整 COPY 与 RUN 的顺序</li><li>设置默认的环境变量,映射端口和数据卷</li><li>使用 LABEL 设置镜像元数据</li><li>添加 HEALTHCHECK</li></ul><h3 id="示例"><a class="markdownIt-Anchor" href="#示例"></a> <strong>示例</strong></h3><p>示例 Dockerfile 犯了几乎所有的错(当然我是故意的)。接下来,我会一步步优化它。假设我们需要使用 Docker 运行一个 Node.js 应用,下面就是它的 Dockerfile(CMD 指令太复杂了,所以我简化了,它是错误的,仅供参考)。</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> ubuntu</span><br><span class="line"></span><br><span class="line"><span class="keyword">ADD</span><span class="bash"> . /app</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> apt-get update</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> apt-get upgrade -y</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> apt-get install -y nodejs ssh mysql</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> <span class="built_in">cd</span> /app && npm install</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># this should start three processes, mysql and ssh</span></span><br><span class="line"><span class="comment"># in the background and node app in foreground</span></span><br><span class="line"><span class="comment"># isn't it beautifully terrible? <3</span></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> mysql & sshd & npm start</span></span><br></pre></td></tr></table></figure><p>构建镜像:</p><p><code>docker build -t wtf</code></p><h3 id="编写dockerignore-文件"><a class="markdownIt-Anchor" href="#编写dockerignore-文件"></a> <strong>编写.dockerignore 文件</strong></h3><p>构建镜像时,Docker 需要先准备context ,将所有需要的文件收集到进程中。默认的context包含 Dockerfile 目录中的所有文件,但是实际上,<strong>我们并不需要.git 目录,node_modules 目录等内容</strong>。 .dockerignore 的作用和语法类似于 .gitignore,可以忽略一些不需要的文件,这样可以有效加快镜像构建时间,同时减少 Docker 镜像的大小。示例如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.git/</span><br><span class="line">node_modules/</span><br><span class="line">dist/</span><br></pre></td></tr></table></figure><h3 id="容器只运行单个应用"><a class="markdownIt-Anchor" href="#容器只运行单个应用"></a> <strong>容器只运行单个应用</strong></h3><p>从技术角度讲,你可以在 Docker 容器中运行多个进程。你可以将数据库,前端,后端,ssh,supervisor 都运行在同一个 Docker 容器中。但是,这会让你非常痛苦:</p><ul><li>非常长的构建时间(修改前端之后,整个后端也需要重新构建)</li><li>非常大的镜像大小</li><li>多个应用的日志难以处理(不能直接使用 stdout,否则多个应用的日志会混合到一起)</li><li>横向扩展时非常浪费资源(不同的应用需要运行的容器数并不相同)</li><li>僵尸进程问题 - 你需要选择合适的 init 进程</li></ul><p>因此,我建议大家为每个应用构建单独的 Docker 镜像,然后使用 <a href="https://docs.docker.com/compose/" target="_blank" rel="noopener">Docker Compose</a> 运行多个 Docker 容器。</p><p>现在,我从 Dockerfile 中删除一些不需要的安装包,另外,SSH 可以用<a href="https://docs.docker.com/engine/reference/commandline/exec/" target="_blank" rel="noopener">docker exec</a>替代。示例如下:</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> ubuntu</span><br><span class="line"></span><br><span class="line"><span class="keyword">ADD</span><span class="bash"> . /app</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> apt-get update</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> apt-get upgrade -y</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># we should remove ssh and mysql, and use</span></span><br><span class="line"><span class="comment"># separate container for database </span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> apt-get install -y nodejs <span class="comment"># ssh mysql</span></span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> <span class="built_in">cd</span> /app && npm install</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> npm start</span></span><br></pre></td></tr></table></figure><h3 id="将多个-run-指令合并为一个"><a class="markdownIt-Anchor" href="#将多个-run-指令合并为一个"></a> <strong>将多个 RUN 指令合并为一个</strong></h3><p>Docker 镜像是分层的,下面这些知识点非常重要:</p><ul><li>Dockerfile 中的每个指令都会创建一个新的镜像层。</li><li>镜像层将被缓存和复用</li><li>当 Dockerfile 的指令修改了,复制的文件变化了,或者构建镜像时指定的变量不同了,对应的镜像层缓存就会失效</li><li>某一层的镜像缓存失效之后,它之后的镜像层缓存都会失效</li><li>镜像层是不可变的,如果我们再某一层中添加一个文件,然后在下一层中删除它,则镜像中依然会包含该文件(只是这个文件在 Docker 容器中不可见了)。</li></ul><p>Docker 镜像类似于洋葱。它们都有很多层。为了修改内层,则需要将外面的层都删掉。记住这一点的话,其他内容就很好理解了。</p><p>现在,我们<strong>将所有的</strong><a href="https://docs.docker.com/engine/reference/builder/#run" target="_blank" rel="noopener">RUN</a><strong>指令合并为一个</strong>。同时把apt-get upgrade删除,因为它会使得镜像构建非常不确定(我们只需要依赖基础镜像的更新就好了)</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> ubuntu</span><br><span class="line"></span><br><span class="line"><span class="keyword">ADD</span><span class="bash"> . /app</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> apt-get update \</span></span><br><span class="line"><span class="bash"> && apt-get install -y nodejs \</span></span><br><span class="line"><span class="bash"> && <span class="built_in">cd</span> /app \</span></span><br><span class="line"><span class="bash"> && npm install</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> npm start</span></span><br></pre></td></tr></table></figure><p>记住一点,我们只能将变化频率一样的指令合并在一起。将 node.js 安装与 npm 模块安装放在一起的话,则每次修改源代码,都需要重新安装 node.js,这显然不合适。因此,正确的写法是这样的:</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> ubuntu</span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> apt-get update && apt-get install -y nodejs </span></span><br><span class="line"><span class="keyword">ADD</span><span class="bash"> . /app</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> <span class="built_in">cd</span> /app && npm install</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> npm start</span></span><br></pre></td></tr></table></figure><h3 id="基础镜像的标签不要用-latest"><a class="markdownIt-Anchor" href="#基础镜像的标签不要用-latest"></a> <strong>基础镜像的标签不要用 latest</strong></h3><p>当镜像没有指定标签时,将默认使用latest 标签。因此, FROM ubuntu 指令等同于FROM ubuntu:latest。当时,当镜像更新时,latest 标签会指向不同的镜像,这时构建镜像有可能失败。如果你的确需要使用最新版的基础镜像,可以使用 latest 标签,否则的话,最好指定确定的镜像标签。</p><p>示例 Dockerfile 应该使用16.04作为标签。</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> ubuntu:<span class="number">16.04</span> <span class="comment"># it's that easy!</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> apt-get update && apt-get install -y nodejs </span></span><br><span class="line"><span class="keyword">ADD</span><span class="bash"> . /app</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> <span class="built_in">cd</span> /app && npm install</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> npm start</span></span><br></pre></td></tr></table></figure><h3 id="每个-run-指令后删除多余文件"><a class="markdownIt-Anchor" href="#每个-run-指令后删除多余文件"></a> <strong>每个 RUN 指令后删除多余文件</strong></h3><p>假设我们更新了 apt-get 源,下载,解压并安装了一些软件包,它们都保存在/var/lib/apt/lists/目录中。但是,运行应用时 Docker 镜像中并不需要这些文件。我们最好将它们删除,因为它会使 Docker 镜像变大。</p><p>示例 Dockerfile 中,我们可以删除/var/lib/apt/lists/目录中的文件(它们是由 apt-get update 生成的)。</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> ubuntu:<span class="number">16.04</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> apt-get update \</span></span><br><span class="line"><span class="bash"> && apt-get install -y nodejs \</span></span><br><span class="line"><span class="bash"> <span class="comment"># added lines</span></span></span><br><span class="line"> && rm -rf /var/lib/apt/lists/*</span><br><span class="line"></span><br><span class="line"><span class="keyword">ADD</span><span class="bash"> . /app</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> <span class="built_in">cd</span> /app && npm install</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> npm start</span></span><br></pre></td></tr></table></figure><h3 id="选择合适的基础镜像alpine-版本最好"><a class="markdownIt-Anchor" href="#选择合适的基础镜像alpine-版本最好"></a> <strong>选择合适的基础镜像(alpine 版本最好)</strong></h3><p>在示例中,我们选择了ubuntu作为基础镜像。但是我们只需要运行 node 程序,有必要使用一个通用的基础镜像吗?node镜像应该是更好的选择。</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> node</span><br><span class="line"></span><br><span class="line"><span class="keyword">ADD</span><span class="bash"> . /app</span></span><br><span class="line"><span class="comment"># we don't need to install node </span></span><br><span class="line"><span class="comment"># anymore and use apt-get</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> <span class="built_in">cd</span> /app && npm install</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> npm start</span></span><br></pre></td></tr></table></figure><p>更好的选择是 alpine 版本的node镜像。alpine 是一个极小化的 Linux 发行版,只有 4MB,这让它非常适合作为基础镜像。</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> node:<span class="number">7</span>-alpine</span><br><span class="line"></span><br><span class="line"><span class="keyword">ADD</span><span class="bash"> . /app</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> <span class="built_in">cd</span> /app && npm install</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> npm start</span></span><br></pre></td></tr></table></figure><p><a href="https://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management" target="_blank" rel="noopener">apk</a>是 Alpine 的包管理工具。它与apt-get有些不同,但是非常容易上手。另外,它还有一些非常有用的特性,比如no-cache和 --virtual选项,它们都可以帮助我们减少镜像的大小。</p><h3 id="设置-workdir-和-cmd"><a class="markdownIt-Anchor" href="#设置-workdir-和-cmd"></a> <strong>设置 WORKDIR 和 CMD</strong></h3><p><a href="https://docs.docker.com/engine/reference/builder/#workdir" target="_blank" rel="noopener">WORKDIR</a>指令可以设置默认目录,也就是运行RUN / CMD / ENTRYPOINT指令的地方。</p><p><a href="https://docs.docker.com/engine/reference/builder/#cmd" target="_blank" rel="noopener">CMD</a>指令可以设置容器创建是执行的默认命令。另外,你应该讲命令写在一个数组中,数组中每个元素为命令的每个单词(参考<a href="https://docs.docker.com/engine/reference/builder/#cmd" target="_blank" rel="noopener">官方文档</a>)。</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> node:<span class="number">7</span>-alpine</span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="bash"> /app</span></span><br><span class="line"><span class="keyword">ADD</span><span class="bash"> . /app</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> npm install</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> [<span class="string">"npm"</span>, <span class="string">"start"</span>]</span></span><br></pre></td></tr></table></figure><h3 id="使用-entrypoint-可选"><a class="markdownIt-Anchor" href="#使用-entrypoint-可选"></a> <strong>使用 ENTRYPOINT (可选)</strong></h3><p><a href="https://docs.docker.com/engine/reference/builder/#entrypoint" target="_blank" rel="noopener">ENTRYPOINT</a>指令并不是必须的,因为它会增加复杂度。ENTRYPOINT是一个脚本,它会默认执行,并且将指定的命令错误其参数。它通常用于构建可执行的 Docker 镜像。<a href="http://entrypoint.sh" target="_blank" rel="noopener">entrypoint.sh</a> 如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/usr/bin/env sh</span></span><br><span class="line"><span class="comment"># $0 is a script name, </span></span><br><span class="line"><span class="comment"># $1, $2, $3 etc are passed arguments</span></span><br><span class="line"><span class="comment"># $1 is our command</span></span><br><span class="line">CMD=<span class="variable">$1</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> <span class="string">"<span class="variable">$CMD</span>"</span> <span class="keyword">in</span></span><br><span class="line"> <span class="string">"dev"</span> )</span><br><span class="line"> npm install</span><br><span class="line"> <span class="built_in">export</span> NODE_ENV=development</span><br><span class="line"> <span class="built_in">exec</span> npm run dev</span><br><span class="line"> ;;</span><br><span class="line"></span><br><span class="line"> <span class="string">"start"</span> )</span><br><span class="line"> <span class="comment"># we can modify files here, using ENV variables passed in </span></span><br><span class="line"> <span class="comment"># "docker create" command. It can't be done during build process.</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"db: <span class="variable">$DATABASE_ADDRESS</span>"</span> >> /app/config.yml</span><br><span class="line"> <span class="built_in">export</span> NODE_ENV=production</span><br><span class="line"> <span class="built_in">exec</span> npm start</span><br><span class="line"> ;;</span><br><span class="line"></span><br><span class="line"> * )</span><br><span class="line"> <span class="comment"># Run custom command. Thanks to this line we can still use </span></span><br><span class="line"> <span class="comment"># "docker run our_image /bin/bash" and it will work</span></span><br><span class="line"> <span class="built_in">exec</span> <span class="variable">$CMD</span> <span class="variable">${@:2}</span></span><br><span class="line"> ;;</span><br><span class="line"><span class="keyword">esac</span></span><br></pre></td></tr></table></figure><p>示例 Dockerfile:</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> node:<span class="number">7</span>-alpine</span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="bash"> /app</span></span><br><span class="line"><span class="keyword">ADD</span><span class="bash"> . /app</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> npm install</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="bash"> [<span class="string">"./entrypoint.sh"</span>]</span></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> [<span class="string">"start"</span>]</span></span><br></pre></td></tr></table></figure><p>可以使用如下命令运行该镜像:</p><p><code>docker run our-app dev</code></p><p><code>docker run out-app start</code></p><p><code>docker run -ti out-app /bin/bash</code></p><h3 id="在-entrypoint-脚本中使用-exec"><a class="markdownIt-Anchor" href="#在-entrypoint-脚本中使用-exec"></a> <strong>在 entrypoint 脚本中使用 exec</strong></h3><p>在前文的 entrypoint 脚本中,我使用了exec命令运行 node 应用。不使用exec的话,我们则不能顺利地关闭容器,因为 SIGTERM 信号会被 bash 脚本进程吞没。exec命令启动的进程可以取代脚本进程,因此所有的信号都会正常工作。</p><h3 id="copy-与-add-优先使用前者"><a class="markdownIt-Anchor" href="#copy-与-add-优先使用前者"></a> <strong>COPY 与 ADD 优先使用前者</strong></h3><p><a href="https://docs.docker.com/engine/reference/builder/#copy" target="_blank" rel="noopener">COPY</a>指令非常简单,仅用于将文件拷贝到镜像中。<a href="https://docs.docker.com/engine/reference/builder/#add" target="_blank" rel="noopener">ADD</a>相对来讲复杂一些,可以用于下载远程文件以及解压压缩包(参考<a href="https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#add-or-copy" target="_blank" rel="noopener">官方文档</a>)。</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> node:<span class="number">7</span>-alpine</span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> . /app</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> npm install</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="bash"> [<span class="string">"./entrypoint.sh"</span>]</span></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> [<span class="string">"start"</span>]</span></span><br></pre></td></tr></table></figure><h3 id="合理调整-copy-与-run-的顺序"><a class="markdownIt-Anchor" href="#合理调整-copy-与-run-的顺序"></a> <strong>合理调整 COPY 与 RUN 的顺序</strong></h3><p>我们应该<strong>把变化最少的部分放在 Dockerfile 的前面</strong>,这样可以充分利用镜像缓存。</p><p>示例中,源代码会经常变化,则每次构建镜像时都需要重新安装 NPM 模块,这显然不是我们希望看到的。因此我们可以先拷贝package.json,然后安装 NPM 模块,最后才拷贝其余的源代码。这样的话,即使源代码变化,也不需要重新安装 NPM 模块。</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> node:<span class="number">7</span>-alpine</span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="bash"> /app</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> package.json /app</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> npm install</span></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> . /app</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="bash"> [<span class="string">"./entrypoint.sh"</span>]</span></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> [<span class="string">"start"</span>]</span></span><br></pre></td></tr></table></figure><h3 id="设置默认的环境变量映射端口和数据卷"><a class="markdownIt-Anchor" href="#设置默认的环境变量映射端口和数据卷"></a> <strong>设置默认的环境变量,映射端口和数据卷</strong></h3><p>运行 Docker 容器时很可能需要一些环境变量。在 Dockerfile 设置默认的环境变量是一种很好的方式。另外,我们应该在 Dockerfile 中设置映射端口和数据卷。示例如下:</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> node:<span class="number">7</span>-alpine</span><br><span class="line"></span><br><span class="line"><span class="comment"># env variables required during build</span></span><br><span class="line"><span class="keyword">ENV</span> PROJECT_DIR=/app</span><br><span class="line"></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="bash"> <span class="variable">$PROJECT_DIR</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> package.json <span class="variable">$PROJECT_DIR</span></span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> npm install</span></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> . <span class="variable">$PROJECT_DIR</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment"># env variables that can change</span></span><br><span class="line"><span class="comment"># volume and port settings</span></span><br><span class="line"><span class="comment"># and defaults for our application</span></span><br><span class="line"><span class="keyword">ENV</span> MEDIA_DIR=/media \</span><br><span class="line"> NODE_ENV=production \</span><br><span class="line"> APP_PORT=<span class="number">3000</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">VOLUME</span><span class="bash"> <span class="variable">$MEDIA_DIR</span></span></span><br><span class="line"><span class="keyword">EXPOSE</span> $APP_PORT</span><br><span class="line"></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="bash"> [<span class="string">"./entrypoint.sh"</span>]</span></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> [<span class="string">"start"</span>]</span></span><br></pre></td></tr></table></figure><p><a href="https://docs.docker.com/engine/reference/builder/#env" target="_blank" rel="noopener">ENV</a>指令指定的环境变量在容器中可以使用。如果你只是需要指定构建镜像时的变量,你可以使用<a href="https://docs.docker.com/engine/reference/builder/#arg" target="_blank" rel="noopener">ARG</a>指令。</p><h3 id="使用-label-设置镜像元数据"><a class="markdownIt-Anchor" href="#使用-label-设置镜像元数据"></a> <strong>使用 LABEL 设置镜像元数据</strong></h3><p>使用<a href="https://docs.docker.com/engine/reference/builder/#label" target="_blank" rel="noopener">LABEL</a>指令,可以为镜像设置元数据,例如<strong>镜像创建者</strong>或者<strong>镜像说明</strong>。旧版的 Dockerfile 语法使用<a href="https://docs.docker.com/engine/reference/builder/#maintainer-deprecated" target="_blank" rel="noopener">MAINTAINER</a>指令指定镜像创建者,但是它已经被弃用了。有时,一些外部程序需要用到镜像的元数据,例如<a href="https://github.com/NVIDIA/nvidia-docker" target="_blank" rel="noopener">nvidia-docker</a>需要用到<code>com.nvidia.volumes.needed</code>。</p><p>示例如下:</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> node:<span class="number">7</span>-alpine</span><br><span class="line"><span class="keyword">LABEL</span><span class="bash"> maintainer <span class="string">"[email protected]"</span></span></span><br><span class="line">...</span><br></pre></td></tr></table></figure><h3 id="添加-healthcheck"><a class="markdownIt-Anchor" href="#添加-healthcheck"></a> <strong>添加 HEALTHCHECK</strong></h3><p>运行容器时,可以指定–restart always选项。这样的话,容器崩溃时,Docker 守护进程(docker daemon)会重启容器。对于需要长时间运行的容器,这个选项非常有用。但是,如果容器的确在运行,但是不可(陷入死循环,配置错误)用怎么办?使用<a href="https://docs.docker.com/engine/reference/builder/#healthcheck" target="_blank" rel="noopener">HEALTHCHECK</a>指令可以让 Docker 周期性的检查容器的健康状况。我们只需要指定一个命令,如果一切正常的话返回 0,否则返回 1。对 HEALTHCHECK 感兴趣的话,可以参考<a href="https://blog.newrelic.com/2016/08/24/docker-health-check-instruction/" target="_blank" rel="noopener">这篇博客</a>。示例如下:</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> node:<span class="number">7</span>-alpine</span><br><span class="line"><span class="keyword">LABEL</span><span class="bash"> maintainer <span class="string">"[email protected]"</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">ENV</span> PROJECT_DIR=/app</span><br><span class="line"><span class="keyword">WORKDIR</span><span class="bash"> <span class="variable">$PROJECT_DIR</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> package.json <span class="variable">$PROJECT_DIR</span></span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> npm install</span></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> . <span class="variable">$PROJECT_DIR</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">ENV</span> MEDIA_DIR=/media \</span><br><span class="line"> NODE_ENV=production \</span><br><span class="line"> APP_PORT=<span class="number">3000</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">VOLUME</span><span class="bash"> <span class="variable">$MEDIA_DIR</span></span></span><br><span class="line"><span class="keyword">EXPOSE</span> $APP_PORT</span><br><span class="line"><span class="keyword">HEALTHCHECK</span><span class="bash"> CMD curl --fail http://localhost:<span class="variable">$APP_PORT</span> || <span class="built_in">exit</span> 1</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">ENTRYPOINT</span><span class="bash"> [<span class="string">"./entrypoint.sh"</span>]</span></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> [<span class="string">"start"</span>]</span></span><br></pre></td></tr></table></figure><p>当请求失败时,<code>curl —fail</code> 命令返回非 0 状态。</p><h3 id="对进一步了解的使用者"><a class="markdownIt-Anchor" href="#对进一步了解的使用者"></a> <strong>对进一步了解的使用者</strong></h3><p>如果你想要了解更多,请参阅 <a href="https://docs.docker.com/engine/reference/builder/#stopsignal" target="_blank" rel="noopener">STOPSIGNAL</a>, <a href="https://docs.docker.com/engine/reference/builder/#onbuild" target="_blank" rel="noopener">ONBUILD</a>, 和 <a href="https://docs.docker.com/engine/reference/builder/#shell" target="_blank" rel="noopener">SHELL</a> 指令。还要提到在构建镜像中一个非常有用的指令 <code>--no-cache</code> (特别是在 CI 服务器上),以及<code>--squash</code> <a href="https://docs.docker.com/engine/reference/commandline/build/#squash-an-images-layers---squash-experimental-only" target="_blank" rel="noopener">here</a>).</p><p>以上,Have fun 😃</p>]]></content>
<summary type="html">
<blockquote>
<p><strong>译者按:</strong> Dockerfile 的语法非常简单,然而如何加快镜像构建速度,如何减少 Docker 镜像的大小却不是那么直观,需要积累实践经验。这篇博客可以帮助你快速掌握编写 Dockerfile 的技巧。</p>
</summary>
<category term="DevOps" scheme="https://yuechuanx.top/categories/DevOps/"/>
<category term="docker" scheme="https://yuechuanx.top/tags/docker/"/>
</entry>
</feed>