代码拉取完成,页面将自动刷新
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>PyLixm的技术博客</title>
<link href="/atom.xml" rel="self"/>
<link href="http://pylixm.cc/"/>
<updated>2018-08-15T06:50:45.296Z</updated>
<id>http://pylixm.cc/</id>
<author>
<name>PyLixm</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>【 python 基础系列 】 - GIL锁的来龙去脉</title>
<link href="http://pylixm.cc/posts/2018-08-10-python-GIL.html"/>
<id>http://pylixm.cc/posts/2018-08-10-python-GIL.html</id>
<published>2018-08-09T16:00:00.000Z</published>
<updated>2018-08-15T06:50:45.296Z</updated>
<content type="html"><![CDATA[<p>Python中GIL锁,一直是一个不大不小的问题。下面这篇文章呢,可以说把它的来龙去脉说的简单易懂,特转载记录备忘。</p><blockquote><p>作者:卢钧轶(cenalulu)<br>本文原文地址:<a href="http://cenalulu.github.io/python/gil-in-python/" target="_blank" rel="noopener">http://cenalulu.github.io/python/gil-in-python/</a></p></blockquote><h2 id="GIL是什么"><a href="#GIL是什么" class="headerlink" title="GIL是什么"></a>GIL是什么</h2><p>首先需要明确的一点是<code>GIL</code>并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把<code>GIL</code>归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL</p><p>那么CPython实现中的GIL又是什么呢?GIL全称<code>Global Interpreter Lock</code>为了避免误导,我们还是来看一下官方给出的解释:</p><blockquote><p>In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)</p></blockquote><p>好吧,是不是看上去很糟糕?一个防止多线程并发执行机器码的一个Mutex,乍一看就是个BUG般存在的全局锁嘛!别急,我们下面慢慢的分析。</p><hr><h2 id="为什么会有GIL"><a href="#为什么会有GIL" class="headerlink" title="为什么会有GIL"></a>为什么会有GIL</h2><p>由于物理上得限制,各CPU厂商在核心频率上的比赛已经被多核所取代。为了更有效的利用多核处理器的性能,就出现了多线程的编程方式,而随之带来的就是线程间数据一致性和状态同步的困难。<a href="linux/all-about-cpu-cache/">即使在CPU内部的Cache也不例外</a>,为了有效解决多份缓存之间的数据同步时各厂商花费了不少心思,也不可避免的带来了一定的性能损失。</p><p>Python当然也逃不开,为了利用多核,Python开始支持多线程。<em>而解决多线程之间数据完整性和状态同步的最简单方法自然就是加锁。</em> 于是有了GIL这把超级大锁,而当越来越多的代码库开发者接受了这种设定后,他们开始大量依赖这种特性(即默认python内部对象是thread-safe的,无需在实现时考虑额外的内存锁和同步操作)。</p><p>慢慢的这种实现方式被发现是蛋疼且低效的。但当大家试图去拆分和去除GIL的时候,发现大量库代码开发者已经重度依赖GIL而非常难以去除了。有多难?做个类比,像MySQL这样的“小项目”为了把Buffer Pool Mutex这把大锁拆分成各个小锁也花了从5.5到5.6再到5.7多个大版为期近5年的时间,并且仍在继续。MySQL这个背后有公司支持且有固定开发团队的产品走的如此艰难,那又更何况Python这样核心开发和代码贡献者高度社区化的团队呢?</p><p>所以简单的说GIL的存在更多的是历史原因。如果推到重来,多线程的问题依然还是要面对,但是至少会比目前GIL这种方式会更优雅。</p><hr><h2 id="GIL的影响"><a href="#GIL的影响" class="headerlink" title="GIL的影响"></a>GIL的影响</h2><p>从上文的介绍和官方的定义来看,GIL无疑就是一把全局排他锁。毫无疑问全局锁的存在会对多线程的效率有不小影响。甚至就几乎等于Python是个单线程的程序。<br>那么读者就会说了,全局锁只要释放的勤快效率也不会差啊。只要在进行耗时的IO操作的时候,能释放GIL,这样也还是可以提升运行效率的嘛。或者说再差也不会比单线程的效率差吧。理论上是这样,而实际上呢?Python比你想的更糟。</p><p>下面我们就对比下Python在多线程和单线程下得效率对比。测试方法很简单,一个循环1亿次的计数器函数。一个通过单线程执行两次,一个多线程执行。最后比较执行总时间。测试环境为双核的Mac pro。注:为了减少线程库本身性能损耗对测试结果带来的影响,这里单线程的代码同样使用了线程。只是顺序的执行两次,模拟单线程。</p><h4 id="顺序执行的单线程-single-thread-py"><a href="#顺序执行的单线程-single-thread-py" class="headerlink" title="顺序执行的单线程(single_thread.py)"></a>顺序执行的单线程(single_thread.py)</h4><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><br><span class="line"><span class="comment">#! /usr/bin/python</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> threading <span class="keyword">import</span> Thread</span><br><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">my_counter</span><span class="params">()</span>:</span></span><br><span class="line"> i = <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> _ <span class="keyword">in</span> range(<span class="number">100000000</span>):</span><br><span class="line"> i = i + <span class="number">1</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">True</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"> thread_array = {}</span><br><span class="line"> start_time = time.time()</span><br><span class="line"> <span class="keyword">for</span> tid <span class="keyword">in</span> range(<span class="number">2</span>):</span><br><span class="line"> t = Thread(target=my_counter)</span><br><span class="line"> t.start()</span><br><span class="line"> t.join()</span><br><span class="line"> end_time = time.time()</span><br><span class="line"> print(<span class="string">"Total time: {}"</span>.format(end_time - start_time))</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></pre></td></tr></table></figure><h4 id="同时执行的两个并发线程-multi-thread-py"><a href="#同时执行的两个并发线程-multi-thread-py" class="headerlink" title="同时执行的两个并发线程(multi_thread.py)"></a>同时执行的两个并发线程(multi_thread.py)</h4><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><br><span class="line"><span class="keyword">from</span> threading <span class="keyword">import</span> Thread</span><br><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">my_counter</span><span class="params">()</span>:</span></span><br><span class="line"> i = <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> _ <span class="keyword">in</span> range(<span class="number">100000000</span>):</span><br><span class="line"> i = i + <span class="number">1</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">True</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"> thread_array = {}</span><br><span class="line"> start_time = time.time()</span><br><span class="line"> <span class="keyword">for</span> tid <span class="keyword">in</span> range(<span class="number">2</span>):</span><br><span class="line"> t = Thread(target=my_counter)</span><br><span class="line"> t.start()</span><br><span class="line"> thread_array[tid] = t</span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">2</span>):</span><br><span class="line"> thread_array[i].join()</span><br><span class="line"> end_time = time.time()</span><br><span class="line"> print(<span class="string">"Total time: {}"</span>.format(end_time - start_time))</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></pre></td></tr></table></figure><p>下图就是测试结果</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://ws1.sinaimg.cn/large/8697aaedly1fuadzatia1j22yh1617wh.jpg" alt="" title=""> </div> <div class="image-caption"></div> </figure><p>可以看到python在多线程的情况下居然比单线程整整慢了45%。按照之前的分析,即使是有GIL全局锁的存在,串行化的多线程也应该和单线程有一样的效率才对。那么怎么会有这么糟糕的结果呢?</p><p>让我们通过GIL的实现原理来分析这其中的原因。</p><hr><h2 id="当前GIL设计的缺陷"><a href="#当前GIL设计的缺陷" class="headerlink" title="当前GIL设计的缺陷"></a>当前GIL设计的缺陷</h2><h4 id="基于pcode数量的调度方式"><a href="#基于pcode数量的调度方式" class="headerlink" title="基于pcode数量的调度方式"></a>基于pcode数量的调度方式</h4><p>按照Python社区的想法,操作系统本身的线程调度已经非常成熟稳定了,没有必要自己搞一套。所以Python的线程就是C语言的一个pthread,并通过操作系统调度算法进行调度(例如linux是CFS)。为了让各个线程能够平均利用CPU时间,python会计算当前已执行的微代码数量,达到一定阈值后就强制释放GIL。而这时也会触发一次操作系统的线程调度(当然是否真正进行上下文切换由操作系统自主决定)。</p><p>伪代码</p><figure class="highlight c"><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">while</span> True:</span><br><span class="line"> acquire GIL</span><br><span class="line"> <span class="keyword">for</span> i in <span class="number">1000</span>:</span><br><span class="line"> <span class="keyword">do</span> something</span><br><span class="line"> release GIL</span><br><span class="line"> <span class="comment">/* Give Operating System a chance to do thread scheduling */</span></span><br></pre></td></tr></table></figure><p>这种模式在只有一个CPU核心的情况下毫无问题。任何一个线程被唤起时都能成功获得到GIL(因为只有释放了GIL才会引发线程调度)。但当CPU有多个核心的时候,问题就来了。从伪代码可以看到,从<code>release GIL</code>到<code>acquire GIL</code>之间几乎是没有间隙的。所以当其他在其他核心上的线程被唤醒时,大部分情况下主线程已经又再一次获取到GIL了。这个时候被唤醒执行的线程只能白白的浪费CPU时间,看着另一个线程拿着GIL欢快的执行着。然后达到切换时间后进入待调度状态,再被唤醒,再等待,以此往复恶性循环。</p><p>PS:当然这种实现方式是原始而丑陋的,Python的每个版本中也在逐渐改进GIL和线程调度之间的互动关系。例如先尝试持有GIL在做线程上下文切换,在IO等待时释放GIL等尝试。但是无法改变的是GIL的存在使得操作系统线程调度的这个本来就昂贵的操作变得更奢侈了。<br><a href="http://www.dabeaz.com/GIL/" target="_blank" rel="noopener">关于GIL影响的扩展阅读</a></p><p>为了直观的理解GIL对于多线程带来的性能影响,这里直接借用的一张测试结果图(见下图)。图中表示的是两个线程在双核CPU上得执行情况。两个线程均为CPU密集型运算线程。绿色部分表示该线程在运行,且在执行有用的计算,红色部分为线程被调度唤醒,但是无法获取GIL导致无法进行有效运算等待的时间。<br><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="http://www.dabeaz.com/images/GIL_2cpu.png" alt="GIL Performance" title=""> </div> <div class="image-caption">GIL Performance</div> </figure><br>由图可见,GIL的存在导致多线程无法很好的立即多核CPU的并发处理能力。</p><p>那么Python的IO密集型线程能否从多线程中受益呢?我们来看下面这张测试结果。颜色代表的含义和上图一致。白色部分表示IO线程处于等待。可见,当IO线程收到数据包引起终端切换后,仍然由于一个CPU密集型线程的存在,导致无法获取GIL锁,从而进行无尽的循环等待。<br><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="http://www.dabeaz.com/images/GIL_ioclose.png" alt="GIL IO Performance" title=""> </div> <div class="image-caption">GIL IO Performance</div> </figure></p><p>简单的总结下就是:Python的多线程在多核CPU上,只对于IO密集型计算产生正面效果;而当有至少有一个CPU密集型线程存在,那么多线程效率会由于GIL而大幅下降。</p><hr><h2 id="如何避免受到GIL的影响"><a href="#如何避免受到GIL的影响" class="headerlink" title="如何避免受到GIL的影响"></a>如何避免受到GIL的影响</h2><p>说了那么多,如果不说解决方案就仅仅是个科普帖,然并卵。GIL这么烂,有没有办法绕过呢?我们来看看有哪些现成的方案。</p><h4 id="用multiprocessing替代Thread"><a href="#用multiprocessing替代Thread" class="headerlink" title="用multiprocessing替代Thread"></a>用multiprocessing替代Thread</h4><p>multiprocessing库的出现很大程度上是为了弥补thread库因为GIL而低效的缺陷。它完整的复制了一套thread所提供的接口方便迁移。唯一的不同就是它使用了多进程而不是多线程。每个进程有自己的独立的GIL,因此也不会出现进程之间的GIL争抢。</p><p>当然multiprocessing也不是万能良药。它的引入会增加程序实现时线程间数据通讯和同步的困难。就拿计数器来举例子,如果我们要多个线程累加同一个变量,对于thread来说,申明一个global变量,用thread.Lock的context包裹住三行就搞定了。而multiprocessing由于进程之间无法看到对方的数据,只能通过在主线程申明一个Queue,put再get或者用share memory的方法。这个额外的实现成本使得本来就非常痛苦的多线程程序编码,变得更加痛苦了。具体难点在哪有兴趣的读者可以扩展阅读<a href="http://www.jeffknupp.com/blog/2013/06/30/pythons-hardest-problem-revisited/" target="_blank" rel="noopener">这篇文章</a></p><h4 id="用其他解析器"><a href="#用其他解析器" class="headerlink" title="用其他解析器"></a>用其他解析器</h4><p>之前也提到了既然GIL只是CPython的产物,那么其他解析器是不是更好呢?没错,像JPython和IronPython这样的解析器由于实现语言的特性,他们不需要GIL的帮助。然而由于用了Java/C#用于解析器实现,他们也失去了利用社区众多C语言模块有用特性的机会。所以这些解析器也因此一直都比较小众。毕竟功能和性能大家在初期都会选择前者,<code>Done is better than perfect</code>。</p><h4 id="所以没救了么?"><a href="#所以没救了么?" class="headerlink" title="所以没救了么?"></a>所以没救了么?</h4><p>当然Python社区也在非常努力的不断改进GIL,甚至是尝试去除GIL。并在各个小版本中有了不少的进步。有兴趣的读者可以扩展阅读<a href="http://www.dabeaz.com/python/UnderstandingGIL.pdf" target="_blank" rel="noopener">这个Slide</a><br>另一个改进<a href="https://mail.python.org/pipermail/python-dev/2009-October/093321.html" target="_blank" rel="noopener">Reworking the GIL</a></p><ul><li>将切换颗粒度从基于opcode计数改成基于时间片计数</li><li>避免最近一次释放GIL锁的线程再次被立即调度</li><li>新增线程优先级功能(高优先级线程可以迫使其他线程释放所持有的GIL锁)</li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>Python GIL其实是功能和性能之间权衡后的产物,它尤其存在的合理性,也有较难改变的客观因素。从本分的分析中,我们可以做以下一些简单的总结:</p><ul><li>因为GIL的存在,只有IO Bound场景下得多线程会得到较好的性能</li><li>如果对并行计算性能较高的程序可以考虑把核心部分也成C模块,或者索性用其他语言实现</li><li>GIL在较长一段时间内将会继续存在,但是会不断对其进行改进</li></ul><h4 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h4><ul><li><a href="http://www.jeffknupp.com/blog/2013/06/30/pythons-hardest-problem-revisited/" target="_blank" rel="noopener">Python’s hardest problem</a></li><li><a href="https://wiki.python.org/moin/GlobalInterpreterLock" target="_blank" rel="noopener">Official documents about GIL</a></li><li><a href="http://dabeaz.blogspot.com/2010/02/revisiting-thread-priorities-and-new.html" target="_blank" rel="noopener">Revisiting thread priorities and the new GIL</a></li></ul>]]></content>
<summary type="html">
<p>Python中GIL锁,一直是一个不大不小的问题。下面这篇文章呢,可以说把它的来龙去脉说的简单易懂,特转载记录备忘。</p>
<blockquote>
<p>作者:卢钧轶(cenalulu)<br>本文原文地址:<a href="http://cenalulu.github
</summary>
<category term="python" scheme="http://pylixm.cc/categories/python/"/>
<category term="python" scheme="http://pylixm.cc/tags/python/"/>
<category term="GIL" scheme="http://pylixm.cc/tags/GIL/"/>
</entry>
<entry>
<title>Linux基础系列 - jq</title>
<link href="http://pylixm.cc/posts/2018-06-26-Linux-jq.html"/>
<id>http://pylixm.cc/posts/2018-06-26-Linux-jq.html</id>
<published>2018-06-25T16:00:00.000Z</published>
<updated>2018-08-03T08:05:45.733Z</updated>
<content type="html"><![CDATA[<h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p><code>jq</code> 是linux下的一个解析JSON格式字符串的一个命令行工具。可直接使用 <code>yum install jq</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><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">jq - commandline JSON processor [version 1.5]</span><br><span class="line">Usage: jq [options] <jq filter> [file...]</span><br><span class="line"></span><br><span class="line">jq is a tool <span class="keyword">for</span> processing JSON inputs, applying the</span><br><span class="line">given filter to its JSON text inputs and producing the</span><br><span class="line">filter<span class="string">'s results as JSON on standard output.</span></span><br><span class="line"><span class="string">The simplest filter is ., which is the identity filter,</span></span><br><span class="line"><span class="string">copying jq'</span>s input to its output unmodified (except <span class="keyword">for</span></span><br><span class="line">formatting).</span><br><span class="line">For more advanced filters see the jq(1) manpage (<span class="string">"man jq"</span>)</span><br><span class="line">and/or https://stedolan.github.io/jq</span><br><span class="line"></span><br><span class="line">Some of the options include:</span><br><span class="line"> -ccompact instead of pretty-printed output;</span><br><span class="line"> -nuse `null` as the single input value;</span><br><span class="line"> -e<span class="built_in">set</span> the <span class="built_in">exit</span> status code based on the output;</span><br><span class="line"> -s<span class="built_in">read</span> (slurp) all inputs into an array; apply filter to it;</span><br><span class="line"> -routput raw strings, not JSON texts;</span><br><span class="line"> -R<span class="built_in">read</span> raw strings, not JSON texts;</span><br><span class="line"> -Ccolorize JSON;</span><br><span class="line"> -Mmonochrome (don<span class="string">'t colorize JSON);</span></span><br><span class="line"><span class="string"> -Ssort keys of objects on output;</span></span><br><span class="line"><span class="string"> --tabuse tabs for indentation;</span></span><br><span class="line"><span class="string"> --arg a vset variable $a to value <v>;</span></span><br><span class="line"><span class="string"> --argjson a vset variable $a to JSON value <v>;</span></span><br><span class="line"><span class="string"> --slurpfile a fset variable $a to an array of JSON texts read from <f>;</span></span><br><span class="line"><span class="string">See the manpage for more options.</span></span><br></pre></td></tr></table></figure><h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><h3 id="格式化"><a href="#格式化" class="headerlink" title="格式化"></a>格式化</h3><figure class="highlight bash"><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">[root@pylixm-web ~]<span class="comment"># cat test.txt</span></span><br><span class="line">{<span class="string">"name"</span>:<span class="string">"Google"</span>,<span class="string">"location"</span>:{<span class="string">"street"</span>:<span class="string">"1600 Amphitheatre Parkway"</span>,<span class="string">"city"</span>:<span class="string">"Mountain View"</span>,<span class="string">"state"</span>:<span class="string">"California"</span>,<span class="string">"country"</span>:<span class="string">"US"</span>},<span class="string">"employees"</span>:[{<span class="string">"name"</span>:<span class="string">"Michael"</span>,<span class="string">"division"</span>:<span class="string">"Engineering"</span>},{<span class="string">"name"</span>:<span class="string">"Laura"</span>,<span class="string">"division"</span>:<span class="string">"HR"</span>},{<span class="string">"name"</span>:<span class="string">"Elise"</span>,<span class="string">"division"</span>:<span class="string">"Marketing"</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><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">[root@pylixm-web ~]<span class="comment"># cat test.txt |jq .</span></span><br><span class="line">{</span><br><span class="line"> <span class="string">"name"</span>: <span class="string">"Google"</span>,</span><br><span class="line"> <span class="string">"location"</span>: {</span><br><span class="line"> <span class="string">"street"</span>: <span class="string">"1600 Amphitheatre Parkway"</span>,</span><br><span class="line"> <span class="string">"city"</span>: <span class="string">"Mountain View"</span>,</span><br><span class="line"> <span class="string">"state"</span>: <span class="string">"California"</span>,</span><br><span class="line"> <span class="string">"country"</span>: <span class="string">"US"</span></span><br><span class="line"> },</span><br><span class="line"> <span class="string">"employees"</span>: [</span><br><span class="line"> {</span><br><span class="line"> <span class="string">"name"</span>: <span class="string">"Michael"</span>,</span><br><span class="line"> <span class="string">"division"</span>: <span class="string">"Engineering"</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="string">"name"</span>: <span class="string">"Laura"</span>,</span><br><span class="line"> <span class="string">"division"</span>: <span class="string">"HR"</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="string">"name"</span>: <span class="string">"Elise"</span>,</span><br><span class="line"> <span class="string">"division"</span>: <span class="string">"Marketing"</span></span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="解析"><a href="#解析" class="headerlink" title="解析"></a>解析</h3><p><strong>可直接通过key获取到JSON的value</strong></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></pre></td><td class="code"><pre><span class="line">[root@pylixm-web ~]<span class="comment"># cat test.txt |jq .name</span></span><br><span class="line"><span class="string">"Google"</span></span><br><span class="line">[root@pylixm-web ~]<span class="comment"># cat test.txt |jq .test</span></span><br><span class="line">null</span><br></pre></td></tr></table></figure><p>当key不存在时,会返回<code>null</code>。</p><p><strong>嵌套解析</strong></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></pre></td><td class="code"><pre><span class="line">[root@pylixm-web ~]<span class="comment"># cat test.txt |jq .location.city</span></span><br><span class="line"><span class="string">"Mountain View"</span></span><br><span class="line">[root@pylixm-web ~]<span class="comment"># cat test.txt |jq .employees[]</span></span><br><span class="line">{</span><br><span class="line"> <span class="string">"name"</span>: <span class="string">"Michael"</span>,</span><br><span class="line"> <span class="string">"division"</span>: <span class="string">"Engineering"</span></span><br><span class="line">}</span><br><span class="line">{</span><br><span class="line"> <span class="string">"name"</span>: <span class="string">"Laura"</span>,</span><br><span class="line"> <span class="string">"division"</span>: <span class="string">"HR"</span></span><br><span class="line">}</span><br><span class="line">{</span><br><span class="line"> <span class="string">"name"</span>: <span class="string">"Elise"</span>,</span><br><span class="line"> <span class="string">"division"</span>: <span class="string">"Marketing"</span></span><br><span class="line">}</span><br><span class="line">[root@pylixm-web ~]<span class="comment"># cat test.txt |jq .employees[0]</span></span><br><span class="line">{</span><br><span class="line"> <span class="string">"name"</span>: <span class="string">"Michael"</span>,</span><br><span class="line"> <span class="string">"division"</span>: <span class="string">"Engineering"</span></span><br><span class="line">}</span><br><span class="line">[root@pylixm-web ~]<span class="comment"># cat test.txt |jq .employees[0].name</span></span><br><span class="line"><span class="string">"Michael"</span></span><br></pre></td></tr></table></figure><p>可使用key 连续获取嵌套的json数据。当值为数组时,可通过下标来获取对应位置的json数据。</p><h3 id="内建函数"><a href="#内建函数" class="headerlink" title="内建函数"></a>内建函数</h3><p><strong>keys</strong></p><p>获取最外层的关键字keys,返回一个数组。只能在最外层使用。</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">[root@pylixm-web ~]<span class="comment"># cat test.txt |jq keys</span></span><br><span class="line">[</span><br><span class="line"> <span class="string">"employees"</span>,</span><br><span class="line"> <span class="string">"location"</span>,</span><br><span class="line"> <span class="string">"name"</span></span><br><span class="line">]</span><br><span class="line">[root@pylixm-web ~]<span class="comment"># cat test.txt |jq .location.keys</span></span><br><span class="line">null</span><br></pre></td></tr></table></figure><p><strong>has</strong></p><p>判断是否含有某个key,返回一个布尔值的字符串类型。</p><figure class="highlight bash"><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">[root@pylixm-web ~]<span class="comment"># cat test.txt |jq 'has("test")'</span></span><br><span class="line"><span class="literal">false</span></span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://blog.csdn.net/yanbingquan/article/details/50770911" target="_blank" rel="noopener">https://blog.csdn.net/yanbingquan/article/details/50770911</a></li></ul>]]></content>
<summary type="html">
<h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p><code>jq</code> 是linux下的一个解析JSON格式字符串的一个命令行工具。可直接使用 <code>yum install
</summary>
<category term="Linux" scheme="http://pylixm.cc/categories/Linux/"/>
<category term="Linux" scheme="http://pylixm.cc/tags/Linux/"/>
<category term="运维" scheme="http://pylixm.cc/tags/%E8%BF%90%E7%BB%B4/"/>
</entry>
<entry>
<title>架构师之路 - RPC 理解</title>
<link href="http://pylixm.cc/posts/2018-06-25-Chief-Software-Architect-RPC.html"/>
<id>http://pylixm.cc/posts/2018-06-25-Chief-Software-Architect-RPC.html</id>
<published>2018-06-24T16:00:00.000Z</published>
<updated>2018-08-03T08:05:45.726Z</updated>
<content type="html"><![CDATA[<p>本文是阅读老钱的《深入理解RPC:基于Python自建分布式高并发RPC服务》的笔记及摘录。</p><p>最早了解到的系统之间交互有Web service,那还是在学生时代。还没有彻底搞明白其运行原理,便接触了一种新的系统交互方式,或者叫准则更合适。那便是 RestFull。这种系统交互方式伴随了我的职业生涯好久,直到现在系统之间的交互,我的首选还是Restfull。它是一种简单交互方式,将请求信息看做资源,使用JSON这种数据结构作为传输格式,基于http协议,简单易用。只要把我们的http返回页面改为JSON格式的数据,并遵循一定的Restfull准则即可。Restfull 并没有严格的去限制你的使用,更多是提供了一种以http协议为基础的交互思路。</p><p>后来,接触到了<code>RPC</code>这个词,知道它是也是一种远程过程调用方式,并且呢在微服务和分布式系统中使用的比较广泛。看到有这样一本书,试读了下可以学到东西,便买入深入研读下。</p><h2 id="RPC-介绍"><a href="#RPC-介绍" class="headerlink" title="RPC 介绍"></a>RPC 介绍</h2><p>RPC(Remote Procedure Call),远程过程调用,是一种分布式系统中常用跨机器的通信方法,更多的是指跨机器交互中的长连接交互。</p><p>RPC,传统意义上讲是指长连接数据交互,区别于<code>http</code>即用即走的短连接,严格来说<code>http</code>也算是一种特殊的RPC服务,随着<code>http1.1</code>中对长连接的支持,区别已经越来越小。</p><p>RPC的应用非常广泛,主要应用在大中型分布式系统中的组件交互,可以这样理解:各种夸网络协议的「长连接」系统交互中都使用了RPC或者类RPC。</p><p>其他分布式通信解决方案还有:分布式消息队列、HTTP请求调用、数据库和分布式缓存等。</p><p>各大厂开源的RPC框架有:</p><ul><li>Google: gRPC </li><li>Facebook: Thrift </li><li>Twitter: Finagle </li><li>百度:bRPC </li><li>腾讯:Tars</li><li>阿里:Dubbo、SOFA</li><li>新浪:Motan</li></ul><h2 id="RPC-交互流程"><a href="#RPC-交互流程" class="headerlink" title="RPC 交互流程"></a>RPC 交互流程</h2><p>RPC 是两个子系统之间进行的直接消息交互,它使用操作系统提供的套接字(sockect)来作为消息的载体,以特定的消息格式(需要序列化)来定义消息内容和边界。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://ws1.sinaimg.cn/large/8697aaedly1fspizbsjknj20ow0dmq3e.jpg" alt="" title=""> </div> <div class="image-caption"></div> </figure><p>可以这样理解,RPC是对底层通信和交互协议的一个封装,便于上层使用。</p><h2 id="RPC-协议构成"><a href="#RPC-协议构成" class="headerlink" title="RPC 协议构成"></a>RPC 协议构成</h2><p>RPC 消息协议组成:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://ws1.sinaimg.cn/large/8697aaedly1fsppmxl87mj20fs09rdg3.jpg" alt="" title=""> </div> <div class="image-caption"></div> </figure><h3 id="消息边界"><a href="#消息边界" class="headerlink" title="消息边界"></a>消息边界</h3><p>因为 RPC 是在一条TCP链路上进行多次消息的交互,所以交互过程中为了区分一条一条消息的消息,必须将消息的边界定义好。</p><p>消息边界分隔有两种方式:</p><ul><li>特殊符号法:使用特殊符号,最长用的是 <code>\r\n</code>。可读性强,但是只能传输文本。</li><li>长度前缀法:在消息前加 4 字节长度的整数值,标记消息体的长度。此方式常用于二进制类型的消息。可读性差,可以传输文本和内容。</li></ul><h3 id="消息结构"><a href="#消息结构" class="headerlink" title="消息结构"></a>消息结构</h3><p>消息结构分为两种:</p><ul><li><p>显示的消息结构:消息的结构有自身决定,可读性高,但是为了表示结构,传输时冗余字段多,消耗更多的流量。如:JSON格式。</p></li><li><p>隐式的消息结构:在TCP链接创建时,服务端和客户端便规定好消息结构,以后交互直接发送消息的值即可。消息的可读性差,但是确实节省了不少传输流量。</p></li></ul><h3 id="消息压缩"><a href="#消息压缩" class="headerlink" title="消息压缩"></a>消息压缩</h3><p>消息在传输过程中为了节省带宽需要压缩。但是是否需要压缩,要根据具体业务场景。不要为了压缩占用大量系统资源,导致正常系统服务出现问题。</p><h3 id="流量的优化"><a href="#流量的优化" class="headerlink" title="流量的优化"></a>流量的优化</h3><p>消息传递中,必然是占用字节越少,效率的流量越少,传输速度也越快了。优化流量这里思路便是,尽量减少消息的字节占用。</p><ul><li>使用变长整数varint,来表示整数。</li><li>使用 zigzag 编码 来表示负数。</li></ul><h2 id="RPC-通信协议列举分析"><a href="#RPC-通信协议列举分析" class="headerlink" title="RPC 通信协议列举分析"></a>RPC 通信协议列举分析</h2><h3 id="Redis-通信协议分析"><a href="#Redis-通信协议分析" class="headerlink" title="Redis 通信协议分析"></a>Redis 通信协议分析</h3><p>Redis 作者自己设计了一套本文通信协议 RESP。按照RPC消息结构来分析如下:</p><ul><li>消息边界:RESP使用特殊符号<code>\r\n</code>来区分多次消息。</li><li>消息结构:使用文本形式来传送消息。</li><li>流量优化:网络流量倾斜进行极致优化,而是选择了照顾协议的直观性、可理解性。</li></ul><p>更多协议详情,可参考<a href="https://gist.github.com/antirez/2bc68a9e9e45395e297d288453d5d54c" target="_blank" rel="noopener">官方相关文档</a>。</p><h3 id="Protobuf"><a href="#Protobuf" class="headerlink" title="Protobuf"></a>Protobuf</h3><p>Protobuf 协议是 Google 开源的二进制 RPC 通讯协议,它可能是互联网开源项目中使用最为广泛的 RPC 协议。</p><ul><li>消息边界:没有定义消息边界,也就是没有消息头。消息头一般由用户自己定义,通常使用长度前缀法来定义边界</li><li>消息结构:使用二进制流传送消息。</li><li>流量优化:通过对消息格式的设计优化,充分压榨了消息体积,减小了传输使用的流量。</li></ul><p>扩展阅读:</p><ul><li><a href="https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html" target="_blank" rel="noopener">Google Protocol Buffer 的使用和原理</a></li><li><a href="https://developers.google.com/protocol-buffers/docs/overview" target="_blank" rel="noopener">官方文档</a></li></ul><h2 id="RPC-vs-HTTP-vs-WebService"><a href="#RPC-vs-HTTP-vs-WebService" class="headerlink" title="RPC vs HTTP vs WebService"></a>RPC vs HTTP vs WebService</h2><p><code>RPC</code> 远程过程调用,就是在另外一台服务器上有一段代码(函数),你可以通过网络远程调用它。用什么协议(http,tcp,udp…),传输什么数据格式(json,xml,二进制…)你都可以自己控制。</p><p><code>HTTP API</code> 基于应用层的HTTP协议,通常是以一种web的方式,对外提供以JSON或字符串作为数据格式的接口服务,例如著名的 RestFull规则。</p><p><code>WebService</code> 是一种SOAP方式的web服务。SOAP用来描述传递信息的格式, WSDL用来描述如何访问具体的接口,UDDI用来管理,分发,查询webService。基于http协议,使用xml格式来传递数据。</p><p>现阶段个人理解:</p><blockquote><ul><li>RPC 广义上来讲,是远程过程调用,即跨机器的函数调用。传输协议和格式可自己控制。包括基于TCP协议的一些通信协议实现的过程调用和基于http协议实现的过程调用。如:Restfull API、WebService及gRPC等一些框架实现。</li><li>RPC 狭义上来讲的话,通常是基于TCP/IP协议,通过二进制流或文本的数据格式来传输的一些通信方法,如:gRPC。</li></ul></blockquote>]]></content>
<summary type="html">
<p>本文是阅读老钱的《深入理解RPC:基于Python自建分布式高并发RPC服务》的笔记及摘录。</p>
<p>最早了解到的系统之间交互有Web service,那还是在学生时代。还没有彻底搞明白其运行原理,便接触了一种新的系统交互方式,或者叫准则更合适。那便是 RestFul
</summary>
<category term="Software_Architect" scheme="http://pylixm.cc/categories/Software-Architect/"/>
<category term="architect" scheme="http://pylixm.cc/tags/architect/"/>
<category term="rpc" scheme="http://pylixm.cc/tags/rpc/"/>
</entry>
<entry>
<title>Flask 目录结构分析</title>
<link href="http://pylixm.cc/posts/2018-06-04-Flask-base.html"/>
<id>http://pylixm.cc/posts/2018-06-04-Flask-base.html</id>
<published>2018-06-03T16:00:00.000Z</published>
<updated>2018-08-03T08:05:45.733Z</updated>
<content type="html"><![CDATA[<p>到目前为止,Flask 最新版本为<code>1.0.3</code>,我们来以此版本做分析,以便可以快速回忆各组件关系。</p><p>基本使用,可参阅<a href="http://flask.pocoo.org/docs/1.0/" target="_blank" rel="noopener">官方文档</a>,已非常详尽。</p><p>我们知道Flask,是一个<code>微</code>框架,只所以叫<code>微</code>是因为它没有像Django那样把所有的事情都帮你处理了。它只提供给我们web开发很核心的部分,其他的像数据库处理、模板引擎的选择等都交给了外部的插件处理。这也是Flask的一大特点,插件化。我们可以很灵活的组织我们的项目框架。但有选择,就有问题,这是一柄双刃剑。随着Flask插件生态的繁荣,它已经可以满足大多数的项目需求。甚至github的收藏数已超过Django,稳居python web开发框架之首。一切事物都是相对的,框架亦是如此,没有优劣,只有合适与否。</p><p>那么Flask的插件是如何运作的,一个基本的Flask开发框架都需要什么模块呢?让我们带着这些问题,展开今天的分析。</p><h3 id="一个最小的Flask应用"><a href="#一个最小的Flask应用" class="headerlink" title="一个最小的Flask应用"></a>一个最小的Flask应用</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></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">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_world</span><span class="params">()</span>:</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">'Hello World!'</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"> app.run()</span><br></pre></td></tr></table></figure><p>分析:</p><ul><li>1、首先初始化了Flask引用的实例<code>app</code>,这个实例可以提供给<code>wsgi</code>协议的web服务器来运行。也可通过如上的<code>run()</code>函数自己运行,不过这个只限开发,性能非常低下。</li><li>2、接下来,在这个<code>app</code>上注册了路由,和处理函数。我们大部分的业务逻辑应该在这个处理函数中。</li><li>3、最后,直接返回数据,并没有涉及模板和数据库。</li></ul><h3 id="完整项目分析"><a href="#完整项目分析" class="headerlink" title="完整项目分析"></a>完整项目分析</h3><p>以 <code>cookiecutter</code> 模板为例:<a href="https://github.com/pylixm/flask_boilerplate" target="_blank" rel="noopener">flask_boilerplate</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><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><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line">$ tree</span><br><span class="line">.</span><br><span class="line">├── LICENSE</span><br><span class="line">├── Procfile </span><br><span class="line">├── README.rst</span><br><span class="line">├── assets # 静态文件,提供给webpack打包使用</span><br><span class="line">│ ├── css </span><br><span class="line">│ │ └── style.css</span><br><span class="line">│ ├── img</span><br><span class="line">│ └── js</span><br><span class="line">│ ├── main.js</span><br><span class="line">│ ├── plugins.js</span><br><span class="line">│ └── script.js</span><br><span class="line">├── autoapp.py # flask app 启动入口</span><br><span class="line">├── flask_boilerplate # flask 代码目录 </span><br><span class="line">│ ├── __init__.py </span><br><span class="line">│ ├── app.py # flask app 创建代码</span><br><span class="line">│ ├── commands.py # flask 命令扩展代码</span><br><span class="line">│ ├── compat.py # python 2和3 的兼容代码</span><br><span class="line">│ ├── database.py # 数据库 model 代码,可根据复杂程度,分拆多个模块文件</span><br><span class="line">│ ├── extensions.py # 扩展统一实例化,方便控制前后顺序,防止循环引用</span><br><span class="line">│ ├── public # 蓝图</span><br><span class="line">│ │ ├── __init__.py </span><br><span class="line">│ │ ├── forms.py </span><br><span class="line">│ │ └── views.py</span><br><span class="line">│ ├── settings.py # 项目整体配置</span><br><span class="line">│ ├── static # 静态文件,Flask 默认目录</span><br><span class="line">│ │ └── build</span><br><span class="line">│ ├── templates # 模板文件,flask 默认目录</span><br><span class="line">│ │ ├── 401.html</span><br><span class="line">│ │ ├── 404.html</span><br><span class="line">│ │ ├── 500.html</span><br><span class="line">│ │ ├── footer.html</span><br><span class="line">│ │ ├── layout.html</span><br><span class="line">│ │ ├── nav.html</span><br><span class="line">│ │ ├── public</span><br><span class="line">│ │ │ ├── about.html</span><br><span class="line">│ │ │ ├── home.html</span><br><span class="line">│ │ │ └── register.html</span><br><span class="line">│ │ └── users</span><br><span class="line">│ │ └── members.html</span><br><span class="line">│ ├── user # 蓝图</span><br><span class="line">│ │ ├── __init__.py</span><br><span class="line">│ │ ├── forms.py</span><br><span class="line">│ │ ├── models.py</span><br><span class="line">│ │ └── views.py</span><br><span class="line">│ └── utils.py 工具包</span><br><span class="line">├── package.json # npm 配置文件</span><br><span class="line">├── requirements # python 模块列表</span><br><span class="line">│ ├── dev.txt</span><br><span class="line">│ └── prod.txt</span><br><span class="line">├── requirements.txt</span><br><span class="line">├── setup.cfg</span><br><span class="line">├── tests</span><br><span class="line">└── webpack.config.js # webpack配置文件</span><br></pre></td></tr></table></figure><p>我们先忽略其他最外层的配置文件,从入口<code>autoapp.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></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="string">"""Create an application instance."""</span></span><br><span class="line"><span class="keyword">from</span> flask.helpers <span class="keyword">import</span> get_debug_flag</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> flask_boilerplate.app <span class="keyword">import</span> create_app</span><br><span class="line"><span class="keyword">from</span> flask_boilerplate.settings <span class="keyword">import</span> DevConfig, ProdConfig</span><br><span class="line"></span><br><span class="line">CONFIG = DevConfig <span class="keyword">if</span> get_debug_flag() <span class="keyword">else</span> ProdConfig</span><br><span class="line"></span><br><span class="line">app = create_app(CONFIG)</span><br></pre></td></tr></table></figure><p>分析:</p><ul><li>1、实例化了Flask 应用,供外部启动使用。</li><li>2、使用全局的配置文件<code>CONFIG</code>,并根据是否调式调用不同的配置项。</li></ul><p>我们继续,到<code>app</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="string">"""The app module, containing the app factory function."""</span></span><br><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask, render_template</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> flask_boilerplate <span class="keyword">import</span> commands, public, user</span><br><span class="line"><span class="keyword">from</span> flask_boilerplate.extensions <span class="keyword">import</span> bcrypt, cache, csrf_protect, db, \</span><br><span class="line"> debug_toolbar, login_manager, migrate, webpack</span><br><span class="line"><span class="keyword">from</span> flask_boilerplate.settings <span class="keyword">import</span> ProdConfig</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">create_app</span><span class="params">(config_object=ProdConfig)</span>:</span></span><br><span class="line"> <span class="string">"""An application factory, as explained here: http://flask.pocoo.org/docs/patterns/appfactories/.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :param config_object: The configuration object to use.</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> app = Flask(__name__.split(<span class="string">'.'</span>)[<span class="number">0</span>])</span><br><span class="line"> app.config.from_object(config_object)</span><br><span class="line"> register_extensions(app)</span><br><span class="line"> register_blueprints(app)</span><br><span class="line"> register_errorhandlers(app)</span><br><span class="line"> register_shellcontext(app)</span><br><span class="line"> register_commands(app)</span><br><span class="line"> <span class="keyword">return</span> app</span><br><span class="line"></span><br><span class="line">.....</span><br></pre></td></tr></table></figure><p>分析:</p><ul><li><code>create_app</code> 函数,在实例化app后,往app上注册了一些基本的扩展和蓝图,以及错误处理和命令上下文等。充分体现了插件的思想。</li></ul><p>那么各模块便体现在 <code>register_extensions</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">register_extensions</span><span class="params">(app)</span>:</span></span><br><span class="line"> <span class="string">"""Register Flask extensions."""</span></span><br><span class="line"> bcrypt.init_app(app)</span><br><span class="line"> cache.init_app(app)</span><br><span class="line"> db.init_app(app)</span><br><span class="line"> csrf_protect.init_app(app)</span><br><span class="line"> login_manager.init_app(app)</span><br><span class="line"> debug_toolbar.init_app(app)</span><br><span class="line"> migrate.init_app(app, db)</span><br><span class="line"> webpack.init_app(app)</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">None</span></span><br></pre></td></tr></table></figure><p>可以根据命名开出,其中许多必备的扩展插件,如数据库插件。</p><p>除了从功能上来进行扩展外,我们的业务逻辑也可通过<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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">register_blueprints</span><span class="params">(app)</span>:</span></span><br><span class="line"> <span class="string">"""Register Flask blueprints."""</span></span><br><span class="line"> app.register_blueprint(public.views.blueprint)</span><br><span class="line"> app.register_blueprint(user.views.blueprint)</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">None</span></span><br></pre></td></tr></table></figure><p>我们的业务逻辑便在这些蓝图中实现。这样我们整个Flask框架整个开发架构便清晰了:</p><ul><li>通过扩展来扩展框架功能</li><li>通过蓝图来模块化我们的业务逻辑</li></ul>]]></content>
<summary type="html">
<p>到目前为止,Flask 最新版本为<code>1.0.3</code>,我们来以此版本做分析,以便可以快速回忆各组件关系。</p>
<p>基本使用,可参阅<a href="http://flask.pocoo.org/docs/1.0/" target="_blank" r
</summary>
<category term="Flask" scheme="http://pylixm.cc/categories/Flask/"/>
<category term="python" scheme="http://pylixm.cc/tags/python/"/>
<category term="Flask" scheme="http://pylixm.cc/tags/Flask/"/>
</entry>
<entry>
<title>Docker学习笔记</title>
<link href="http://pylixm.cc/posts/2018-05-31-Docker-all.html"/>
<id>http://pylixm.cc/posts/2018-05-31-Docker-all.html</id>
<published>2018-05-30T16:00:00.000Z</published>
<updated>2018-08-03T08:05:45.727Z</updated>
<content type="html"><![CDATA[<blockquote><p>原文地址:<a href="https://blog.opskumu.com/docker.html" target="_blank" rel="noopener">https://blog.opskumu.com/docker.html</a><br>本文对原文进行了更新和补充,转载备查。</p></blockquote><h2 id="一、Docker-简介"><a href="#一、Docker-简介" class="headerlink" title="一、Docker 简介"></a>一、Docker 简介</h2><p>Docker 是一种轻量级的虚拟化技术,是一种Linux容器(Linux Containers,缩写为 LXC)技术的封装。</p><p>大多数人可能知道虚拟机,可以在一台硬件机器上虚拟出另一台计算机,有它自己的cpu、硬盘等各种虚拟的硬件。而 Linux 容器技术也是一种虚拟技术,但是它并非直接从硬件上来虚拟,而是通过软件技术对进程及资源进行隔离,从而达到虚拟化的目的。Docker 就是一种这样隔离虚拟化技术。</p><p>Docker的发展由来大致如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://ws1.sinaimg.cn/large/8697aaedly1frtdhgj1ilj20k00dxaaj.jpg" alt="" title=""> </div> <div class="image-caption"></div> </figure><p>(图片来源:<a href="https://zhuanlan.zhihu.com/p/34732608)" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/34732608)</a></p><p>Docker 与传统虚拟化技术不同如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://ws1.sinaimg.cn/large/8697aaedly1frtfbkjvuxj211i0e0k14.jpg" alt="" title=""> </div> <div class="image-caption"></div> </figure><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://ws1.sinaimg.cn/large/8697aaedly1frtfbjwwm5j211g0aek1r.jpg" alt="" title=""> </div> <div class="image-caption"></div> </figure><p>(图片来源:Docker 从入门到实践)</p><p>Docker 发展之迅速,除了分布式和微服务的大潮外,还得益于其优秀的特性。</p><p><strong>更高效的利用系统资源</strong></p><p>Docker 容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。</p><p><strong>更快速的启动时间</strong></p><p>Docker 直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。</p><p><strong>一致的运行环境</strong></p><p>Docker 容器中有镜像的概念,Docker 容器有镜像生成,镜像保证了除内核外的运行环境。</p><p><strong>持续交付和部署</strong></p><p>也是因为镜像技术,可以使Devops人员实现持续集成、持续交付、部署,一次构建可在任意地方运行。</p><p><strong>更轻松的迁移</strong></p><p>Docker 容器封装了软件运行环境,使其不依赖系统,使其更容易移植。</p><p><strong>更轻松的维护和扩展</strong></p><p>Docker 使用分层存储和镜像技术,使得镜像可重复使用,维护和扩展更轻松。</p><p><strong>Docker相较虚拟机优势明显</strong></p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://ws1.sinaimg.cn/large/8697aaedly1frtffbs8p2j21540aqq58.jpg" alt="" title=""> </div> <div class="image-caption"></div> </figure><p>Docker 两个主要部件:</p><ul><li>Docker: 开源的容器虚拟化平台</li><li>Docker Hub: 用于分享、管理 Docker 容器镜像的 Docker SaaS 平台 – Docker Hub</li></ul><p>Docker 使用客户端-服务器 (C/S) 架构模式。Docker 客户端会与 Docker 守护进程进行通信。Docker 守护进程会处理复杂繁重的任务,例如建立、运行、发布你的 Docker 容器。Docker 客户端和守护进程可以运行在同一个系统上,当然你也可以使用 Docker 客户端去连接一个远程的 Docker 守护进程。Docker 客户端和守护进程之间通过 socket 或者 RESTful API 进行通信。</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://ws1.sinaimg.cn/large/8697aaedly1frul61gqzvj20g10djaac.jpg" alt="" title=""> </div> <div class="image-caption"></div> </figure><h3 id="1-1-Docker-守护进程"><a href="#1-1-Docker-守护进程" class="headerlink" title="1.1 Docker 守护进程"></a>1.1 Docker 守护进程</h3><p>如上图所示,Docker 守护进程运行在一台主机上。用户并不直接和守护进程进行交互,而是通过 Docker 客户端间接和其通信。</p><h3 id="1-2-Docker-客户端"><a href="#1-2-Docker-客户端" class="headerlink" title="1.2 Docker 客户端"></a>1.2 Docker 客户端</h3><p>Docker 客户端,实际上是 docker 的二进制程序,是主要的用户与 Docker 交互方式。它接收用户指令并且与背后的 Docker 守护进程通信,如此来回往复。</p><h3 id="1-3-Docker-内部"><a href="#1-3-Docker-内部" class="headerlink" title="1.3 Docker 内部"></a>1.3 Docker 内部</h3><p>要理解 Docker 内部构建,需要理解以下三种部件:</p><ul><li>Docker 镜像 - Docker images</li><li>Docker 仓库 - Docker registeries</li><li>Docker 容器 - Docker containers</li></ul><h4 id="Docker-镜像"><a href="#Docker-镜像" class="headerlink" title="Docker 镜像"></a>Docker 镜像</h4><p>Docker 镜像是 Docker 容器运行时的只读模板,每一个镜像由一系列的层 (layers) 组成。Docker 使用 UnionFS(一种Linux 下的文件系统,Linux 内核4.x以后不再支持,Docker公司开发了自己的文件系统如OverlayFS和overlay2。) 来将这些层联合到单独的镜像中。UnionFS 允许独立文件系统中的文件和文件夹(称之为分支)被透明覆盖,形成一个单独连贯的文件系统。正因为有了这些层的存在,Docker 是如此的轻量。当你改变了一个 Docker 镜像,比如升级到某个程序到新的版本,一个新的层会被创建。因此,不用替换整个原先的镜像或者重新建立(在使用虚拟机的时候你可能会这么做),只是一个新的层被添加或升级了。现在你不用重新发布整个镜像,只需要“升级”,层使得分发 Docker 镜像变得简单和快速。</p><p>Docker有各种存储驱动程序(<a href="https://docs.docker.com/engine/userguide/storagedriver/selectadriver/)。唯一得到广泛支持的驱动程序就是AUFS。" target="_blank" rel="noopener">https://docs.docker.com/engine/userguide/storagedriver/selectadriver/)。唯一得到广泛支持的驱动程序就是AUFS。</a></p><p>镜像具有以下特点:</p><ul><li>类似虚拟机的快照,但更轻量,非常非常轻量。</li><li>镜像拥有唯一ID,以及一个供人阅读的名字和标签对。</li><li>只读层被称为镜像,一个镜像是永久不会变的。</li><li>由于 Docker 使用一个统一文件系统,Docker 进程认为整个文件系统是以读写方式挂载的。 但是所有的变更都发生顶层的可写层,而下层的原始的只读镜像文件并未变化。由于镜像不可写,所以镜像是无状态的。</li></ul><h4 id="Docker-仓库"><a href="#Docker-仓库" class="headerlink" title="Docker 仓库"></a>Docker 仓库</h4><p>Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。同样的,Docker 仓库也有公有和私有的概念。公有的 Docker 仓库名字是 Docker Hub。Docker Hub 提供了庞大的镜像集合供使用。这些镜像可以是自己创建,或者在别人的镜像基础上创建。Docker 仓库是 Docker 的分发部分。</p><h4 id="Docker-容器"><a href="#Docker-容器" class="headerlink" title="Docker 容器"></a>Docker 容器</h4><p>Docker 容器和文件夹很类似,一个Docker容器包含了所有的某个应用运行所需要的环境。每一个 Docker 容器都是从 Docker 镜像创建的。Docker 容器可以运行、开始、停止、移动和删除。每一个 Docker 容器都是独立和安全的应用平台,Docker 容器是 Docker 的运行部分。</p><h3 id="1-4-libcontainer"><a href="#1-4-libcontainer" class="headerlink" title="1.4 libcontainer"></a>1.4 libcontainer</h3><p>Docker 从 0.9 版本开始使用 libcontainer 替代 lxc,libcontainer 和 Linux 系统的交互图如下:</p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://ws1.sinaimg.cn/large/8697aaedly1frulxcgv3rj20le0g1dhy.jpg" alt="" title=""> </div> <div class="image-caption"></div> </figure><p>图片来源: Docker 0.9: introducing execution drivers and libcontainer</p><h3 id="1-5-命名空间「Namespaces」"><a href="#1-5-命名空间「Namespaces」" class="headerlink" title="1.5 命名空间「Namespaces」"></a>1.5 命名空间「Namespaces」</h3><h4 id="pid-namespace"><a href="#pid-namespace" class="headerlink" title="pid namespace"></a>pid namespace</h4><p>不同用户的进程就是通过 pid namespace 隔离开的,且不同 namespace 中可以有相同 PID。具有以下特征:</p><ul><li>每个 namespace 中的 pid 是有自己的 pid=1 的进程(类似 /sbin/init 进程)</li><li>每个 namespace 中的进程只能影响自己的同一个 namespace 或子 namespace 中的进程</li><li>因为 /proc 包含正在运行的进程,因此在 container 中的 pseudo-filesystem 的 /proc 目录只能看到自己 namespace 中的进程</li><li>因为 namespace 允许嵌套,父 namespace 可以影响子 namespace 的进程,所以子 namespace 的进程可以在父 namespace 中看到,但是具有不同的 pid</li></ul><p>参考文档:<a href="https://blog.jtlebi.fr/2014/01/05/introduction-to-linux-namespaces-part-3-pid/" target="_blank" rel="noopener">Introduction to Linux namespaces – Part 3: PID</a></p><h4 id="mnt-namespace"><a href="#mnt-namespace" class="headerlink" title="mnt namespace"></a>mnt namespace</h4><p>类似 chroot,将一个进程放到一个特定的目录执行。mnt namespace 允许不同 namespace 的进程看到的文件结构不同,这样每个 namespace 中的进程所看到的文件目录就被隔离开了。同 chroot 不同,每个 namespace 中的 container 在 /proc/mounts 的信息只包含所在 namespace 的 mount point。</p><h4 id="net-namespace"><a href="#net-namespace" class="headerlink" title="net namespace"></a>net namespace</h4><p>网络隔离是通过 net namespace 实现的, 每个 net namespace 有独立的 network devices, IP addresses, IP routing tables, /proc/net 目录。这样每个 container 的网络就能隔离开来。 docker 默认采用 veth 的方式将 container 中的虚拟网卡同 host 上的一个 docker bridge 连接在一起。</p><p>参考文档:<a href="https://blog.jtlebi.fr/2014/01/19/introduction-to-linux-namespaces-part-5-net/" target="_blank" rel="noopener">Introduction to Linux namespaces – Part 5: NET</a></p><h4 id="uts-namespace"><a href="#uts-namespace" class="headerlink" title="uts namespace"></a>uts namespace</h4><p>UTS (“UNIX Time-sharing System”) namespace 允许每个 container 拥有独立的 hostname 和 domain name, 使其在网络上可以被视作一个独立的节点而非 Host 上的一个进程。</p><p>参考文档:<a href="https://blog.jtlebi.fr/2013/12/22/introduction-to-linux-namespaces-part-1-uts/" target="_blank" rel="noopener">Introduction to Linux namespaces – Part 1: UTS</a></p><h4 id="ipc-namespace"><a href="#ipc-namespace" class="headerlink" title="ipc namespace"></a>ipc namespace</h4><p>container 中进程交互还是采用 Linux 常见的进程间交互方法 (interprocess communication - IPC), 包括常见的信号量、消息队列和共享内存。然而同 VM 不同,container 的进程间交互实际上还是 host 上具有相同 pid namespace 中的进程间交互,因此需要在IPC资源申请时加入 namespace 信息 - 每个 IPC 资源有一个唯一的 32bit ID。</p><p>参考文档:<a href="https://blog.jtlebi.fr/2013/12/28/introduction-to-linux-namespaces-part-2-ipc/" target="_blank" rel="noopener">Introduction to Linux namespaces – Part 2: IPC</a></p><h4 id="user-namespace"><a href="#user-namespace" class="headerlink" title="user namespace"></a>user namespace</h4><p>每个 container 可以有不同的 user 和 group id, 也就是说可以以 container 内部的用户在 container 内部执行程序而非 Host 上的用户。</p><p>有了以上 6 种 namespace 从进程、网络、IPC、文件系统、UTS 和用户角度的隔离,一个 container 就可以对外展现出一个独立计算机的能力,并且不同 container 从 OS 层面实现了隔离。 然而不同 namespace 之间资源还是相互竞争的,仍然需要类似 ulimit 来管理每个 container 所能使用的资源 - cgroup。</p><p>####Reference</p><ul><li><a href="http://tiewei.github.io/cloud/Docker-Getting-Start/" target="_blank" rel="noopener">Docker Getting Start: Related Knowledge</a></li><li><a href="https://ruby-china.org/topics/22004" target="_blank" rel="noopener">Docker 介绍以及其相关术语、底层原理和技术</a></li></ul><h3 id="1-6-资源配额「cgroups」"><a href="#1-6-资源配额「cgroups」" class="headerlink" title="1.6 资源配额「cgroups」"></a>1.6 资源配额「cgroups」</h3><p>cgroups 实现了对资源的配额和度量。 cgroups 的使用非常简单,提供类似文件的接口,在 /cgroup 目录下新建一个文件夹即可新建一个 group,在此文件夹中新建 task 文件,并将 pid 写入该文件,即可实现对该进程的资源控制。具体的资源配置选项可以在该文件夹中新建子 subsystem ,<子系统前缀>.<资源项> 是典型的配置方法, 如 memory.usage_in_bytes 就定义了该 group 在 subsystem memory 中的一个内存限制选项。 另外,cgroups 中的 subsystem 可以随意组合,一个 subsystem 可以在不同的 group 中,也可以一个 group 包含多个 subsystem - 也就是说一个 subsystem。</p><ul><li>memory<ul><li>内存相关的限制</li></ul></li><li>cpu<ul><li>在 cgroup 中,并不能像硬件虚拟化方案一样能够定义 CPU 能力,但是能够定义 CPU 轮转的优先级,因此具有较高 CPU 优先级的进程会更可能得到 CPU 运算。 通过将参数写入 cpu.shares ,即可定义改 cgroup 的 CPU 优先级 - 这里是一个相对权重,而非绝对值</li></ul></li><li>blkio<ul><li>block IO 相关的统计和限制,byte/operation 统计和限制 (IOPS 等),读写速度限制等,但是这里主要统计的都是同步 IO</li></ul></li><li>devices<ul><li>设备权限限制</li></ul></li></ul><p>参考文档:<a href="http://tiewei.github.io/devops/howto-use-cgroup/" target="_blank" rel="noopener">how to use cgroup</a></p><h2 id="二、Docker-安装"><a href="#二、Docker-安装" class="headerlink" title="二、Docker 安装"></a>二、Docker 安装</h2><p>docker 的相关安装方法这里不作介绍,具体安装参考 <a href="https://docs.docker.com/installation/" target="_blank" rel="noopener">官档</a></p><p>获取当前 docker 版本</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">$ docker version</span><br><span class="line">Client:</span><br><span class="line"> Version: 18.03.1-ce</span><br><span class="line"> API version: 1.37</span><br><span class="line"> Go version: go1.9.5</span><br><span class="line"> Git commit: 9ee9f40</span><br><span class="line"> Built: Thu Apr 26 07:13:02 2018</span><br><span class="line"> OS/Arch: darwin/amd64</span><br><span class="line"> Experimental: <span class="literal">false</span></span><br><span class="line"> Orchestrator: swarm</span><br><span class="line"></span><br><span class="line">Server:</span><br><span class="line"> Engine:</span><br><span class="line"> Version: 18.03.1-ce</span><br><span class="line"> API version: 1.37 (minimum version 1.12)</span><br><span class="line"> Go version: go1.9.5</span><br><span class="line"> Git commit: 9ee9f40</span><br><span class="line"> Built: Thu Apr 26 07:22:38 2018</span><br><span class="line"> OS/Arch: linux/amd64</span><br><span class="line"> Experimental: <span class="literal">false</span></span><br></pre></td></tr></table></figure><h2 id="三、Docker-基础用法"><a href="#三、Docker-基础用法" class="headerlink" title="三、Docker 基础用法"></a>三、Docker 基础用法</h2><p><a href="https://registry.hub.docker.com/" target="_blank" rel="noopener">Docker HUB</a> : Docker镜像首页,包括官方镜像和其它公开镜像</p><p>因为国情的原因,国内下载 Docker HUB 官方的相关镜像比较慢,可以使用 <a href="http://daocloud.com/" target="_blank" rel="noopener">Daocloud</a> 镜像加速。</p><h3 id="3-1-Search-images"><a href="#3-1-Search-images" class="headerlink" title="3.1 Search images"></a>3.1 Search images</h3><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">$ sudo docker search ubuntu</span><br></pre></td></tr></table></figure><h3 id="3-2-Pull-images"><a href="#3-2-Pull-images" class="headerlink" title="3.2 Pull images"></a>3.2 Pull images</h3><figure class="highlight plain"><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">$ sudo docker pull ubuntu # 获取 ubuntu 官方镜像</span><br><span class="line">$ sudo docker images # 查看当前镜像列表</span><br></pre></td></tr></table></figure><h3 id="3-3-Running-an-interactive-shell"><a href="#3-3-Running-an-interactive-shell" class="headerlink" title="3.3 Running an interactive shell"></a>3.3 Running an interactive shell</h3><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">$ sudo docker run -i -t ubuntu:14.04 /bin/bash</span><br></pre></td></tr></table></figure><ul><li>docker run - 运行一个容器</li><li>-t - 分配一个(伪)tty (link is external)</li><li>-i - 交互模式 (so we can interact with it)</li><li>ubuntu:14.04 - 使用 ubuntu 基础镜像 14.04</li><li>/bin/bash - 运行命令 bash shell</li></ul><p>注: ubuntu 会有多个版本,通过指定 tag 来启动特定的版本 [image]:[tag]</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">$ sudo docker ps # 查看当前运行的容器, ps -a 列出当前系统所有的容器</span><br><span class="line">CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES</span><br><span class="line">6c9129e9df10 ubuntu:14.04 /bin/bash 6 minutes ago Up 6 minutes cranky_babbage</span><br></pre></td></tr></table></figure><h3 id="3-4-相关快捷键"><a href="#3-4-相关快捷键" class="headerlink" title="3.4 相关快捷键"></a>3.4 相关快捷键</h3><ul><li>退出:Ctrl-D or exit</li><li>detach:Ctrl-P + Ctrl-Q</li><li>attach: docker attach CONTAINER-ID</li></ul><h2 id="四、Docker-常用命令"><a href="#四、Docker-常用命令" class="headerlink" title="四、Docker 常用命令"></a>四、Docker 常用命令</h2><h3 id="4-1-docker-help"><a href="#4-1-docker-help" class="headerlink" title="4.1 docker help"></a>4.1 docker help</h3><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">$ sudo docker # docker 命令帮助</span><br></pre></td></tr></table></figure><h3 id="4-2-docker-search"><a href="#4-2-docker-search" class="headerlink" title="4.2 docker search"></a>4.2 docker search</h3><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">$ sudo docker search --help</span><br></pre></td></tr></table></figure><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"># 查找 star 数至少为 100 的镜像,找出只有官方镜像 start 数超过 100,默认不加 s 选项找出所有相关 ubuntu 镜像</span><br><span class="line">$ docker search -s 100 ubuntu</span><br><span class="line">Flag --stars has been deprecated, use --filter=stars=3 instead</span><br><span class="line">NAME DESCRIPTION STARS OFFICIAL AUTOMATED</span><br><span class="line">ubuntu Ubuntu is a Debian-based Linux operating sys… 7755 [OK]</span><br><span class="line">dorowu/ubuntu-desktop-lxde-vnc Ubuntu with openssh-server and NoVNC 185 [OK]</span><br><span class="line">rastasheep/ubuntu-sshd Dockerized SSH service, built on top of offi… 151 [OK]</span><br></pre></td></tr></table></figure><h3 id="4-3-docker-info"><a href="#4-3-docker-info" class="headerlink" title="4.3 docker info"></a>4.3 docker info</h3><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><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><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line">$ sudo docker info</span><br><span class="line">Containers: 0 # 容器个数</span><br><span class="line"> Running: 0</span><br><span class="line"> Paused: 0</span><br><span class="line"> Stopped: 0</span><br><span class="line">Images: 20 # 镜像个数</span><br><span class="line">Server Version: 18.05.0-ce</span><br><span class="line">Storage Driver: overlay2 # 存储驱动</span><br><span class="line"> Backing Filesystem: extfs</span><br><span class="line"> Supports d_type: true</span><br><span class="line"> Native Overlay Diff: true</span><br><span class="line">Logging Driver: json-file</span><br><span class="line">Cgroup Driver: cgroupfs</span><br><span class="line">Plugins:</span><br><span class="line"> Volume: local</span><br><span class="line"> Network: bridge host macvlan null overlay</span><br><span class="line"> Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog</span><br><span class="line">Swarm: inactive</span><br><span class="line">Runtimes: runc</span><br><span class="line">Default Runtime: runc</span><br><span class="line">Init Binary: docker-init</span><br><span class="line">containerd version: 773c489c9c1b21a6d78b5c538cd395416ec50f88</span><br><span class="line">runc version: 4fc53a81fb7c994640722ac585fa9ca548971871</span><br><span class="line">init version: 949e6fa</span><br><span class="line">Security Options:</span><br><span class="line"> seccomp</span><br><span class="line"> Profile: default</span><br><span class="line">Kernel Version: 3.10.0-693.5.2.el7.x86_64 # 主机内核</span><br><span class="line">Operating System: CentOS Linux 7 (Core)</span><br><span class="line">OSType: linux</span><br><span class="line">Architecture: x86_64</span><br><span class="line">CPUs: 1</span><br><span class="line">Total Memory: 992.4MiB</span><br><span class="line">Name: xxxxx</span><br><span class="line">ID: xxxxxx</span><br><span class="line">Docker Root Dir: /var/lib/docker</span><br><span class="line">Debug Mode (client): false</span><br><span class="line">Debug Mode (server): false</span><br><span class="line">Registry: https://index.docker.io/v1/</span><br><span class="line">Labels:</span><br><span class="line">Experimental: false</span><br><span class="line">Insecure Registries:</span><br><span class="line"> 127.0.0.0/8</span><br><span class="line">Live Restore Enabled: false</span><br></pre></td></tr></table></figure><h3 id="4-4-docker-pull-amp-amp-docker-push"><a href="#4-4-docker-pull-amp-amp-docker-push" class="headerlink" title="4.4 docker pull && docker push"></a>4.4 docker pull && docker push</h3><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">$ sudo docker pull <images-name>:<tag> # pull 拉取镜像</span><br></pre></td></tr></table></figure><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">$ sudo docker pull ubuntu # 下载官方 ubuntu docker 镜像,默认下载所有 ubuntu 官方库镜像</span><br><span class="line">$ sudo docker pull ubuntu:14.04 # 下载指定版本 ubuntu 官方镜像</span><br><span class="line">$ sudo docker push 192.168.0.100:5000/ubuntu # 推送镜像库到私有源[可注册 docker 官方账户,推送到官方自有账户]</span><br><span class="line">$ sudo docker push 192.168.0.100:5000/ubuntu:14.04 # 推送指定镜像到私有源</span><br></pre></td></tr></table></figure><h3 id="4-5-docker-images"><a href="#4-5-docker-images" class="headerlink" title="4.5 docker images"></a>4.5 docker images</h3><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">$ sudo docker images <images-name>:<tag></span><br></pre></td></tr></table></figure><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">$ sudo docker images # 显示当前系统镜像,不包括过渡层镜像</span><br><span class="line">$ sudo docker images -a # 显示当前系统所有镜像,包括过渡层镜像</span><br><span class="line">$ sudo docker images ubuntu # 显示当前系统 docker ubuntu 库中的所有镜像</span><br><span class="line">REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE</span><br><span class="line">ubuntu 12.04 ebe4be4dd427 4 weeks ago 210.6 MB</span><br><span class="line">ubuntu 14.04 e54ca5efa2e9 4 weeks ago 276.5 MB</span><br><span class="line">ubuntu 14.04-ssh 6334d3ac099a 7 weeks ago 383.2 MB</span><br></pre></td></tr></table></figure><h3 id="4-6-docker-rmi"><a href="#4-6-docker-rmi" class="headerlink" title="4.6 docker rmi"></a>4.6 docker rmi</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">$ sudo docker rmi --help</span><br><span class="line"></span><br><span class="line">Usage: docker rmi IMAGE [IMAGE...]</span><br><span class="line"></span><br><span class="line">Remove one or more images</span><br><span class="line"></span><br><span class="line"> -f, --force=false Force removal of the image # 强制移除镜像不管是否有容器使用该镜像</span><br><span class="line"> --no-prune=false Do not delete untagged parents # 不要删除未标记的父镜像</span><br></pre></td></tr></table></figure><h3 id="4-7-docker-run"><a href="#4-7-docker-run" class="headerlink" title="4.7 docker run"></a>4.7 docker run</h3><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></pre></td><td class="code"><pre><span class="line">$ sudo docker images ubuntu</span><br><span class="line">REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE</span><br><span class="line">ubuntu 14.04 e54ca5efa2e9 4 weeks ago 276.5 MB</span><br><span class="line">... ...</span><br><span class="line">$ sudo docker run -t -i -c 100 -m 512MB -h test1 -d --name="docker_test1" ubuntu /bin/bash</span><br><span class="line"># 创建一个 cpu 优先级为 100,内存限制 512MB,主机名为 test1,名为 docker_test1 后台运行 bash 的容器</span><br><span class="line">a424ca613c9f2247cd3ede95adfbaf8d28400cbcb1d5f9b69a7b56f97b2b52e5</span><br><span class="line">$ sudo docker ps</span><br><span class="line">CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES</span><br><span class="line">a424ca613c9f ubuntu:14.04 /bin/bash 6 seconds ago Up 5 seconds docker_test1</span><br><span class="line">$ sudo docker attach docker_test1</span><br><span class="line">root@test1:/# pwd</span><br><span class="line">/</span><br><span class="line">root@test1:/# exit</span><br><span class="line">exit</span><br></pre></td></tr></table></figure><p>关于cpu优先级:</p><blockquote><p>By default all groups have 1024 shares. A group with 100 shares will get a ~10% portion of the CPU time -archlinux cgroups</p></blockquote><h3 id="4-8-其他-docker"><a href="#4-8-其他-docker" class="headerlink" title="4.8 其他 docker"></a>4.8 其他 docker</h3><ul><li>docker create -t -i fedora bash<ul><li>创建容器,但不启动</li></ul></li><li>docker start CONTAINER [CONTAINER…]<ul><li>运行一个或多个停止的容器</li></ul></li><li>docker stop CONTAINER [CONTAINER…]<ul><li>停掉一个或多个运行的容器 -t 选项可指定超时时间</li></ul></li><li>docker kill [OPTIONS] CONTAINER [CONTAINER…]<ul><li>默认 kill 发送 SIGKILL 信号 -s 可以指定发送 kill 信号类型</li></ul></li><li>docker restart [OPTIONS] CONTAINER [CONTAINER…]<ul><li>重启一个或多个运行的容器 -t 选项可指定超时时间</li></ul></li><li>docker pause CONTAINER<ul><li>暂停一个容器,方便 commit</li></ul></li><li>docker unpause CONTAINER<ul><li>继续暂停的容器</li></ul></li><li>docker rm [OPTIONS] CONTAINER [CONTAINER…]<ul><li>移除一个或多个容器</li><li>-f, –force=false Force removal of running container</li><li>-l, –link=false Remove the specified link and not the underlying container</li><li>-v, –volumes=false Remove the volumes associated with the container</li></ul></li><li>docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]<ul><li>提交指定容器为镜像</li><li>-a, –author=”” Author (e.g., “John Hannibal Smith <a href="mailto:hannibal@a-team.com" target="_blank" rel="noopener">hannibal@a-team.com</a>”)</li><li>-m, –message=”” Commit message</li><li>-p, –pause=true Pause container during commit</li><li>默认 commit 是暂停状态</li></ul></li><li>docker inspect CONTAINER|IMAGE [CONTAINER|IMAGE…]<ul><li>查看容器或者镜像的详细信息</li></ul></li><li>docker logs CONTAINER<ul><li>输出指定容器日志信息</li><li>-f, –follow=false Follow log output</li><li>类似 tail -f</li><li>-t, –timestamps=false Show timestamps</li><li>–tail=”all” Output the specified number of lines at the end of logs (defaults to all logs)</li></ul></li><li>docker exec -it ubuntu_bash bash<ul><li>进入容器 docker 1.3 后新增 </li></ul></li><li>docker attach <ul><li>进入容器, exit 退出后,容器会关闭</li></ul></li></ul><p>参考文档:<a href="https://docs.docker.com/reference/run/" target="_blank" rel="noopener">Docker Run Reference</a></p><h2 id="五、Docker-端口映射"><a href="#五、Docker-端口映射" class="headerlink" title="五、Docker 端口映射"></a>五、Docker 端口映射</h2><figure class="highlight plain"><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"># Find IP address of container with ID <container_id> 通过容器 id 获取 ip</span><br><span class="line">$ sudo docker inspect <container_id> | grep IPAddress | cut -d ’"’ -f 4</span><br></pre></td></tr></table></figure><p>无论如何,这些 ip 是基于本地系统的并且容器的端口非本地主机是访问不到的。此外,除了端口只能本地访问外,对于容器的另外一个问题是这些 ip 在容器每次启动的时候都会改变。</p><p>Docker 解决了容器的这两个问题,并且给容器内部服务的访问提供了一个简单而可靠的方法。Docker 通过端口绑定主机系统的接口,允许非本地客户端访问容器内部运行的服务。为了简便的使得容器间通信,Docker 提供了这种连接机制。</p><h3 id="5-1-自动映射端口"><a href="#5-1-自动映射端口" class="headerlink" title="5.1 自动映射端口"></a>5.1 自动映射端口</h3><p>-P 使用时需要指定 –expose 选项,指定需要对外提供服务的端口</p><figure class="highlight plain"><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">$ sudo docker run -t -P --expose 22 --name server ubuntu:14.04</span><br><span class="line"># 使用 docker run -P 自动绑定所有对外提供服务的容器端口,映射的端口将会从没有使用的端口池中 (49000..49900) 自动选择,你可以通过docker ps 、docker inspect <container_id> 或者 docker port <container_id> <port> 确定具体的绑定信息。</span><br></pre></td></tr></table></figure><h3 id="5-2-绑定端口到指定接口"><a href="#5-2-绑定端口到指定接口" class="headerlink" title="5.2 绑定端口到指定接口"></a>5.2 绑定端口到指定接口</h3><figure class="highlight plain"><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">$ sudo docker run -p [([<host_interface>:[host_port]])|(<host_port>):]<container_port>[/udp] <image> <cmd></span><br><span class="line"># 默认不指定绑定 ip 则监听所有网络接口。</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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 绑定 TCP 端口</span></span><br><span class="line"><span class="comment"># Bind TCP port 8080 of the container to TCP port 80 on 127.0.0.1 of the host machine.</span></span><br><span class="line">$ sudo docker run -p 127.0.0.1:80:8080 <image> <cmd></span><br><span class="line"><span class="comment"># Bind TCP port 8080 of the container to a dynamically allocated TCP port on 127.0.0.1 of the host machine.</span></span><br><span class="line">$ sudo docker run -p 127.0.0.1::8080 <image> <cmd></span><br><span class="line"><span class="comment"># Bind TCP port 8080 of the container to TCP port 80 on all available interfaces of the host machine.</span></span><br><span class="line">$ sudo docker run -p 80:8080 <image> <cmd></span><br><span class="line"><span class="comment"># Bind TCP port 8080 of the container to a dynamically allocated TCP port on all available interfaces</span></span><br><span class="line">$ sudo docker run -p 8080 <image> <cmd></span><br><span class="line"><span class="comment"># 绑定 UDP 端口</span></span><br><span class="line"><span class="comment"># Bind UDP port 5353 of the container to UDP port 53 on 127.0.0.1 of the host machine.</span></span><br><span class="line">$ sudo docker run -p 127.0.0.1:53:5353/udp <image> <cmd></span><br></pre></td></tr></table></figure><h2 id="六、Docker-网络配置"><a href="#六、Docker-网络配置" class="headerlink" title="六、Docker 网络配置"></a>六、Docker 网络配置</h2><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://ws1.sinaimg.cn/large/8697aaedly1frunbbzgcjj20ee09xdge.jpg" alt="" title=""> </div> <div class="image-caption"></div> </figure><p>图: <a href="http://www.slideshare.net/janghoonsim/docker-container-and-lightweight-virtualization" target="_blank" rel="noopener">Docker - container and lightweight virtualization</a></p><p>Dokcer 通过使用 Linux 桥接提供容器之间的通信,docker0 桥接接口的目的就是方便 Docker 管理。当 Docker daemon 启动时需要做以下操作:</p><ul><li>creates the docker0 bridge if not present # 如果 docker0 不存在则创建</li><li>searches for an IP address range which doesn’t overlap with an existing route # 搜索一个与当前路由不冲突的 ip 段</li><li>picks an IP in the selected range # 在确定的范围中选择 ip</li><li>assigns this IP to the docker0 bridge # 绑定 ip 到 docker0</li></ul><h3 id="6-1-Docker-四种网络模式"><a href="#6-1-Docker-四种网络模式" class="headerlink" title="6.1 Docker 四种网络模式"></a>6.1 Docker 四种网络模式</h3><p>四种网络模式摘自 <a href="http://www.infoq.com/cn/articles/docker-network-and-pipework-open-source-explanation-practice" target="_blank" rel="noopener">Docker 网络详解及 pipework 源码解读与实践</a></p><p>docker run 创建 Docker 容器时,可以用 –net 选项指定容器的网络模式,Docker 有以下 4 种网络模式:</p><ul><li>host 模式,使用 –net=host 指定。</li><li>container 模式,使用 –net=container:NAME_or_ID 指定。</li><li>none 模式,使用 –net=none 指定。</li><li>bridge 模式,使用 –net=bridge 指定,默认设置。</li></ul><h4 id="host-模式"><a href="#host-模式" class="headerlink" title="host 模式"></a>host 模式</h4><p>如果启动容器的时候使用 host 模式,那么这个容器将不会获得一个独立的 Network Namespace,而是和宿主机共用一个 Network Namespace。容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。</p><p>例如,我们在 10.10.101.105/24 的机器上用 host 模式启动一个含有 web 应用的 Docker 容器,监听 tcp 80 端口。当我们在容器中执行任何类似 ifconfig 命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用 10.10.101.105:80 即可,不用任何 NAT 转换,就如直接跑在宿主机中一样。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。</p><h4 id="container-模式"><a href="#container-模式" class="headerlink" title="container 模式"></a>container 模式</h4><p>这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。</p><h4 id="none模式"><a href="#none模式" class="headerlink" title="none模式"></a>none模式</h4><p>这个模式和前两个不同。在这种模式下,Docker 容器拥有自己的 Network Namespace,但是,并不为 Docker容器进行任何网络配置。也就是说,这个 Docker 容器没有网卡、IP、路由等信息。需要我们自己为 Docker 容器添加网卡、配置 IP 等。</p><h4 id="bridge模式"><a href="#bridge模式" class="headerlink" title="bridge模式"></a>bridge模式</h4><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://ws1.sinaimg.cn/large/8697aaedly1frungck2m5j20f8098jrg.jpg" alt="" title=""> </div> <div class="image-caption"></div> </figure><p>图:<a href="http://www.wickedawesometech.us/2014/07/the-container-world-part-2-networking.html" target="_blank" rel="noopener">The Container World Part 2 Networking</a></p><p>bridge 模式是 Docker 默认的网络设置,此模式会为每一个容器分配 Network Namespace、设置 IP 等,并将一个主机上的 Docker 容器连接到一个虚拟网桥上。当 Docker server 启动时,会在主机上创建一个名为 docker0 的虚拟网桥,此主机上启动的 Docker 容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。接下来就要为容器分配 IP 了,Docker 会从 RFC1918 所定义的私有 IP 网段中,选择一个和宿主机不同的IP地址和子网分配给 docker0,连接到 docker0 的容器就从这个子网中选择一个未占用的 IP 使用。如一般 Docker 会使用 172.17.0.0/16 这个网段,并将 172.17.42.1/16 分配给 docker0 网桥(在主机上使用 ifconfig 命令是可以看到 docker0 的,可以认为它是网桥的管理接口,在宿主机上作为一块虚拟网卡使用)</p><h3 id="6-2-列出当前主机网桥"><a href="#6-2-列出当前主机网桥" class="headerlink" title="6.2 列出当前主机网桥"></a>6.2 列出当前主机网桥</h3><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">$ sudo brctl show <span class="comment"># brctl 工具依赖 bridge-utils 软件包</span></span><br><span class="line">bridge name bridge id STP enabled interfaces</span><br><span class="line">docker0 8000.000000000000 no</span><br></pre></td></tr></table></figure><h3 id="6-3-查看当前-docker0-ip"><a href="#6-3-查看当前-docker0-ip" class="headerlink" title="6.3 查看当前 docker0 ip"></a>6.3 查看当前 docker0 ip</h3><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">$ sudo ifconfig docker0</span><br><span class="line">docker0 Link encap:Ethernet HWaddr xx:xx:xx:xx:xx:xx</span><br><span class="line">inet addr:172.17.42.1 Bcast:0.0.0.0 Mask:255.255.0.0</span><br></pre></td></tr></table></figure><p>在容器运行时,每个容器都会分配一个特定的虚拟机口并桥接到 docker0。每个容器都会配置同 docker0 ip 相同网段的专用 ip 地址,docker0 的 IP 地址被用于所有容器的默认网关。</p><h3 id="6-4-运行一个容器"><a href="#6-4-运行一个容器" class="headerlink" title="6.4 运行一个容器"></a>6.4 运行一个容器</h3><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">$ sudo docker run -t -i -d ubuntu /bin/bash</span><br><span class="line">52f811c5d3d69edddefc75aff5a4525fc8ba8bcfa1818132f9dc7d4f7c7e78b4</span><br><span class="line">$ sudo brctl show</span><br><span class="line">bridge name bridge id STP enabled interfaces</span><br><span class="line">docker0 8000.fef213db5a66 no vethQCDY1N</span><br></pre></td></tr></table></figure><p>以上, docker0 扮演着 52f811c5d3d6 container 这个容器的虚拟接口 vethQCDY1N interface 桥接的角色。</p><p><strong>使用特定范围的 IP</strong></p><p>Docker 会尝试寻找没有被主机使用的 ip 段,尽管它适用于大多数情况下,但是它不是万能的,有时候我们还是需要对 ip 进一步规划。Docker 允许你管理 docker0 桥接或者通过 -b 选项自定义桥接网卡,需要安装 bridge-utils 软件包。</p><p>基本步骤如下:</p><ul><li>ensure Docker is stopped # 确保 docker 的进程是停止的</li><li>create your own bridge (bridge0 for example) # 创建自定义网桥</li><li>assign a specific IP to this bridge # 给网桥分配特定的 ip</li><li>start Docker with the -b=bridge0 parameter # 以 -b 的方式指定网桥</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><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"># Stopping Docker and removing docker0</span><br><span class="line"></span><br><span class="line">$ sudo service docker stop</span><br><span class="line">$ sudo ip link set dev docker0 down</span><br><span class="line">$ sudo brctl delbr docker0</span><br><span class="line"></span><br><span class="line"># Create our own bridge</span><br><span class="line"></span><br><span class="line">$ sudo brctl addbr bridge0</span><br><span class="line">$ sudo ip addr add 192.168.5.1/24 dev bridge0</span><br><span class="line">$ sudo ip link set dev bridge0 up</span><br><span class="line"></span><br><span class="line"># Confirming that our bridge is up and running</span><br><span class="line"></span><br><span class="line">$ ip addr show bridge0</span><br><span class="line">4: bridge0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state UP group default</span><br><span class="line"> link/ether 66:38:d0:0d:76:18 brd ff:ff:ff:ff:ff:ff</span><br><span class="line"> inet 192.168.5.1/24 scope global bridge0</span><br><span class="line"> valid_lft forever preferred_lft forever</span><br><span class="line"></span><br><span class="line"># Tell Docker about it and restart (on Ubuntu)</span><br><span class="line"></span><br><span class="line">$ echo 'DOCKER_OPTS="-b=bridge0"' >> /etc/default/docker</span><br><span class="line">$ sudo service docker start</span><br></pre></td></tr></table></figure><p>参考文档: <a href="https://docs.docker.com/articles/networking/" target="_blank" rel="noopener">Network Configuration</a></p><h3 id="6-5-不同主机间容器通信"><a href="#6-5-不同主机间容器通信" class="headerlink" title="6.5 不同主机间容器通信"></a>6.5 不同主机间容器通信</h3><p>不同容器之间的通信可以借助于 pipework 这个工具:</p><figure class="highlight plain"><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">$ git clone https://github.com/jpetazzo/pipework.git</span><br><span class="line">$ sudo cp -rp pipework/pipework /usr/local/bin/</span><br></pre></td></tr></table></figure><p><strong>安装相应依赖软件</strong></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">$ sudo apt-get install iputils-arping bridge-utils -y</span><br></pre></td></tr></table></figure><p><strong>桥接网络</strong></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"># brctl show</span><br><span class="line">bridge name bridge id STP enabled interfaces</span><br><span class="line">br0 8000.000c291412cd no eth0</span><br><span class="line">docker0 8000.56847afe9799 no vetheb48029</span><br></pre></td></tr></table></figure><p>可以删除 docker0,直接把 docker 的桥接指定为 br0。也可以保留使用默认的配置,这样单主机容器之间的通信可以通过 docker0,而跨主机不同容器之间通过 pipework 新建 docker 容器的网卡桥接到 br0,这样跨主机容器之间就可以通信了。</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"><span class="comment"># ubuntu</span></span><br><span class="line">$ sudo service docker stop</span><br><span class="line">$ sudo ip link <span class="built_in">set</span> dev docker0 down</span><br><span class="line">$ sudo brctl delbr docker0</span><br><span class="line">$ <span class="built_in">echo</span> <span class="string">'DOCKER_OPTS="-b=br0"'</span> >> /etc/default/docker</span><br><span class="line">$ sudo service docker start</span><br><span class="line"></span><br><span class="line"><span class="comment"># CentOS 7/RHEL 7</span></span><br><span class="line">$ sudo systemctl stop docker</span><br><span class="line">$ sudo ip link <span class="built_in">set</span> dev docker0 down</span><br><span class="line">$ sudo brctl delbr docker0</span><br><span class="line">$ cat /etc/sysconfig/docker | grep <span class="string">'OPTIONS='</span></span><br><span class="line">OPTIONS=--selinux-enabled -b=br0 -H fd://</span><br><span class="line">$ sudo systemctl start docker</span><br></pre></td></tr></table></figure><p><strong>pipework</strong></p><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://ws1.sinaimg.cn/large/8697aaedly1frunpughzyj20im0f10t6.jpg" alt="" title=""> </div> <div class="image-caption"></div> </figure><p>不同容器之间的通信可以借助于 pipework 这个工具给 docker 容器新建虚拟网卡并绑定 IP 桥接到 br0</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">$ git <span class="built_in">clone</span> https://github.com/jpetazzo/pipework.git</span><br><span class="line">$ sudo cp -rp pipework/pipework /usr/<span class="built_in">local</span>/bin/</span><br><span class="line">$ pipework</span><br><span class="line">Syntax:</span><br><span class="line">pipework <hostinterface> [-i containerinterface] <guest> <ipaddr>/<subnet>[@default_gateway] [macaddr][@vlan]</span><br><span class="line">pipework <hostinterface> [-i containerinterface] <guest> dhcp [macaddr][@vlan]</span><br><span class="line">pipework --<span class="built_in">wait</span> [-i containerinterface]</span><br></pre></td></tr></table></figure><p>如果删除了默认的 docker0 桥接,把 docker 默认桥接指定到了 br0,则最好在创建容器的时候加上 –net=none,防止自动分配的 IP 在局域网中有冲突。</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><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">$ sudo docker run --rm -ti --net=none ubuntu:14.04 /bin/bash</span><br><span class="line">root@a46657528059:/#</span><br><span class="line">$ # Ctrl-P + Ctrl-Q 回到宿主机 shell,容器 detach 状态</span><br><span class="line">$ sudo docker ps</span><br><span class="line">CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES</span><br><span class="line">a46657528059 ubuntu:14.04 "/bin/bash" 4 minutes ago Up 4 minutes hungry_lalande</span><br><span class="line">$ sudo pipework br0 -i eth0 a46657528059 192.168.115.10/24@192.168.115.2</span><br><span class="line"># 默认不指定网卡设备名,则默认添加为 eth1</span><br><span class="line"># 另外 pipework 不能添加静态路由,如果有需求则可以在 run 的时候加上 --privileged=true 权限在容器中手动添加,</span><br><span class="line"># 但这种安全性有缺陷,可以通过 ip netns 操作</span><br><span class="line">$ sudo docker attach a46657528059</span><br><span class="line">root@a46657528059:/# ifconfig eth0</span><br><span class="line">eth0 Link encap:Ethernet HWaddr 86:b6:6b:e8:2e:4d</span><br><span class="line"> inet addr:192.168.115.10 Bcast:0.0.0.0 Mask:255.255.255.0</span><br><span class="line"> inet6 addr: fe80::84b6:6bff:fee8:2e4d/64 Scope:Link</span><br><span class="line"> UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1</span><br><span class="line"> RX packets:8 errors:0 dropped:0 overruns:0 frame:0</span><br><span class="line"> TX packets:9 errors:0 dropped:0 overruns:0 carrier:0</span><br><span class="line"> collisions:0 txqueuelen:1000</span><br><span class="line"> RX bytes:648 (648.0 B) TX bytes:690 (690.0 B)</span><br><span class="line"></span><br><span class="line">root@a46657528059:/# route -n</span><br><span class="line">Kernel IP routing table</span><br><span class="line">Destination Gateway Genmask Flags Metric Ref Use Iface</span><br><span class="line">0.0.0.0 192.168.115.2 0.0.0.0 UG 0 0 0 eth0</span><br><span class="line">192.168.115.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0</span><br></pre></td></tr></table></figure><p>使用 ip netns 添加静态路由,避免创建容器使用 –privileged=true 选项造成一些不必要的安全问题:<br><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">$ docker inspect --format="< .State.Pid >" a46657528059 # 获取指定容器 pid</span><br><span class="line">6350</span><br><span class="line">$ sudo ln -s /proc/6350/ns/net /var/run/netns/6350</span><br><span class="line">$ sudo ip netns exec 6350 ip route add 192.168.0.0/16 dev eth0 via 192.168.115.2</span><br><span class="line">$ sudo ip netns exec 6350 ip route # 添加成功</span><br><span class="line">192.168.0.0/16 via 192.168.115.2 dev eth0</span><br><span class="line">... ...</span><br></pre></td></tr></table></figure></p><p>在其它宿主机进行相应的配置,新建容器并使用 pipework 添加虚拟网卡桥接到 br0,测试通信情况即可。</p><p>另外,pipework 可以创建容器的 vlan 网络,这里不作过多的介绍了,官方文档已经写的很清楚了,可以查看以下两篇文章:</p><ul><li><a href="https://github.com/jpetazzo/pipework" target="_blank" rel="noopener">Pipework 官方文档</a></li><li><a href="http://www.infoq.com/cn/articles/docker-network-and-pipework-open-source-explanation-practice" target="_blank" rel="noopener">Docker 网络详解及 pipework 源码解读与实践</a></li></ul><h2 id="七、Dockerfile"><a href="#七、Dockerfile" class="headerlink" title="七、Dockerfile"></a>七、Dockerfile</h2><p>Docker 可以通过 Dockerfile 的内容来自动构建镜像。Dockerfile 是一个包含创建镜像所有命令的文本文件,通过 docker build 命令可以根据 Dockerfile 的内容构建镜像,在介绍如何构建之前先介绍下 Dockerfile 的基本语法结构。</p><p>Dockerfile 有以下指令选项:</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">FROM</span><br><span class="line">MAINTAINER</span><br><span class="line">RUN</span><br><span class="line">CMD</span><br><span class="line">EXPOSE</span><br><span class="line">ENV</span><br><span class="line">ADD</span><br><span class="line">COPY</span><br><span class="line">ENTRYPOINT</span><br><span class="line">VOLUME</span><br><span class="line">USER</span><br><span class="line">WORKDIR</span><br><span class="line">ONBUILD</span><br></pre></td></tr></table></figure><h3 id="7-1-FROM"><a href="#7-1-FROM" class="headerlink" title="7.1 FROM"></a>7.1 FROM</h3><p>用法:<br><code>FROM <image></code></p><ul><li>FROM 指定构建镜像的基础源镜像,如果本地没有指定的镜像,则会自动从 Docker 的公共库 pull 镜像下来。</li><li>FROM 必须是 Dockerfile 中非注释行的第一个指令,即一个 Dockerfile 从 FROM 语句开始。</li><li>FROM 可以在一个 Dockerfile 中出现多次,如果有需求在一个 Dockerfile 中创建多个镜像。<br>如果 FROM 语句没有指定镜像标签,则默认使用 latest 标签。</li></ul><h3 id="7-2-MAINTAINER"><a href="#7-2-MAINTAINER" class="headerlink" title="7.2 MAINTAINER"></a>7.2 MAINTAINER</h3><p>用法:<br><code>MAINTAINER <name></code></p><p>指定创建镜像的用户</p><h3 id="7-3-RUN-有两种使用方式"><a href="#7-3-RUN-有两种使用方式" class="headerlink" title="7.3 RUN 有两种使用方式"></a>7.3 RUN 有两种使用方式</h3><p>RUN (the command is run in a shell - /bin/sh -c - shell form)<br>RUN [“executable”, “param1”, “param2”] (exec form)<br>每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像,后续的 RUN 都在之前 RUN 提交后的镜像为基础,镜像是分层的,可以通过一个镜像的任何一个历史提交点来创建,类似源码的版本控制。</p><p>exec 方式会被解析为一个 JSON 数组,所以必须使用双引号而不是单引号。exec 方式不会调用一个命令 shell,所以也就不会继承相应的变量,如:</p><p><code>RUN [ "echo", "$HOME" ]</code></p><p>这种方式是不会达到输出 HOME 变量的,正确的方式应该是这样的</p><p><code>RUN [ "sh", "-c", "echo", "$HOME" ]</code></p><p>RUN 产生的缓存在下一次构建的时候是不会失效的,会被重用,可以使用 –no-cache 选项,即 docker build –no-cache,如此便不会缓存。</p><h3 id="7-4-CMD"><a href="#7-4-CMD" class="headerlink" title="7.4 CMD"></a>7.4 CMD</h3><p>CMD 有三种使用方式:</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">CMD [“executable”,”param1”,”param2”] (exec form, this is the preferred form, 优先选择)</span><br><span class="line">CMD [“param1”,”param2”] (as default parameters to ENTRYPOINT)</span><br><span class="line">CMD command param1 param2 (shell form)</span><br></pre></td></tr></table></figure><p>CMD 指定在 Dockerfile 中只能使用一次,如果有多个,则只有最后一个会生效。</p><p>CMD 的目的是为了在启动容器时提供一个默认的命令执行选项。如果用户启动容器时指定了运行的命令,则会覆盖掉 CMD 指定的命令。</p><blockquote><p>CMD 会在启动容器的时候执行,build 时不执行,而 RUN 只是在构建镜像的时候执行,后续镜像构建完成之后,启动容器就与 RUN 无关了,这个初学者容易弄混这个概念,这里简单注解一下。</p></blockquote><h3 id="7-5-EXPOSE"><a href="#7-5-EXPOSE" class="headerlink" title="7.5 EXPOSE"></a>7.5 EXPOSE</h3><p><code>EXPOSE <port> [<port>...]</code></p><p>告诉 Docker 服务端容器对外映射的本地端口,需要在 docker run 的时候使用 -p 或者 -P 选项生效。</p><h3 id="7-6-ENV"><a href="#7-6-ENV" class="headerlink" title="7.6 ENV"></a>7.6 ENV</h3><figure class="highlight plain"><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">ENV <key> <value> # 只能设置一个变量</span><br><span class="line">ENV <key>=<value> ... # 允许一次设置多个变量</span><br></pre></td></tr></table></figure><p>指定一个环节变量,会被后续 RUN 指令使用,并在容器运行时保留。</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></pre></td><td class="code"><pre><span class="line">ENV myName=<span class="string">"John Doe"</span> myDog=Rex\ The\ Dog \</span><br><span class="line"> myCat=fluffy</span><br><span class="line"><span class="comment"># 等同于</span></span><br><span class="line"></span><br><span class="line">ENV myName John Doe</span><br><span class="line">ENV myDog Rex The Dog</span><br><span class="line">ENV myCat fluffy</span><br></pre></td></tr></table></figure><h3 id="7-7-ADD"><a href="#7-7-ADD" class="headerlink" title="7.7 ADD"></a>7.7 ADD</h3><p><code>ADD <src>... <dest></code></p><p>ADD 复制本地主机文件、目录或者远程文件 URLS 从 <src> 并且添加到容器指定路径中 <dest>。</dest></src></p><p><src> 支持通过 GO 的正则模糊匹配,具体规则可参见 Go filepath.Match</src></p><figure class="highlight plain"><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">ADD hom* /mydir/ # adds all files starting with "hom"</span><br><span class="line">ADD hom?.txt /mydir/ # ? is replaced with any single character</span><br></pre></td></tr></table></figure><ul><li><dest> 路径可以绝对路径也可以是相对于工作路径的相对路径,如果 <dest> 不存在,会自动创建对应目录</dest></dest></li><li><src> 路径必须是 Dockerfile 所在路径的相对路径,或路径是 build 上下文 context 的路径或子路径。</src></li><li><src> 如果是一个目录,只会复制目录下的内容,而目录本身则不会被复制</src></li></ul><h3 id="7-7-COPY"><a href="#7-7-COPY" class="headerlink" title="7.7 COPY"></a>7.7 COPY</h3><p><code>COPY <src>... <dest></code></p><p>COPY 复制新文件或者目录从 <src> 添加到容器指定路径中 <dest>。用法同 ADD,唯一的不同是不能指定远程文件 URLS。</dest></src></p><h3 id="7-8-ENTRYPOINT"><a href="#7-8-ENTRYPOINT" class="headerlink" title="7.8 ENTRYPOINT"></a>7.8 ENTRYPOINT</h3><p>有2中执行方式:<br><figure class="highlight plain"><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">ENTRYPOINT [“executable”, “param1”, “param2”] (the preferred exec form,优先选择)</span><br><span class="line">ENTRYPOINT command param1 param2 (shell form)</span><br></pre></td></tr></table></figure></p><p>配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖,而 CMD 是可以被覆盖的。如果需要覆盖,则可以使用 docker run –entrypoint 选项。</p><p>每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个生效。</p><p><strong>Exec form ENTRYPOINT 例子</strong></p><p>通过 ENTRYPOINT 使用 exec form 方式设置稳定的默认命令和选项,而使用 CMD 添加默认之外经常被改动的选项。</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">FROM ubuntu</span><br><span class="line">ENTRYPOINT ["top", "-b"]</span><br><span class="line">CMD ["-c"]</span><br></pre></td></tr></table></figure><p>通过 Dockerfile 使用 ENTRYPOINT 展示前台运行 Apache 服务</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">FROM debian:stable</span><br><span class="line">RUN apt-get update && apt-get install -y --force-yes apache2</span><br><span class="line">EXPOSE 80 443</span><br><span class="line">VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]</span><br><span class="line">ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]</span><br></pre></td></tr></table></figure><p><strong>Shell form ENTRYPOINT 例子</strong></p><p>这种方式会在 /bin/sh -c 中执行,会忽略任何 CMD 或者 docker run 命令行选项,为了确保 docker stop 能够停止长时间运行 ENTRYPOINT 的容器,确保执行的时候使用 exec 选项。</p><figure class="highlight plain"><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">FROM ubuntu</span><br><span class="line">ENTRYPOINT exec top -b</span><br></pre></td></tr></table></figure><p>如果在 ENTRYPOINT 忘记使用 exec 选项,则可以使用 CMD 补上:</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">FROM ubuntu</span><br><span class="line">ENTRYPOINT top -b</span><br><span class="line">CMD --ignored-param1 # --ignored-param2 ... --ignored-param3 ... 依此类推</span><br></pre></td></tr></table></figure><h3 id="7-9-VOLUME"><a href="#7-9-VOLUME" class="headerlink" title="7.9 VOLUME"></a>7.9 VOLUME</h3><p><code>VOLUME ["/data"]</code></p><p>创建一个可以从本地主机或其他容器挂载的挂载点,后续具体介绍。</p><h3 id="7-10-USER"><a href="#7-10-USER" class="headerlink" title="7.10 USER"></a>7.10 USER</h3><p><code>USER daemon</code></p><p>指定运行容器时的用户名或 UID,后续的 RUN、CMD、ENTRYPOINT 也会使用指定用户。</p><h3 id="7-11-WORKDIR"><a href="#7-11-WORKDIR" class="headerlink" title="7.11 WORKDIR"></a>7.11 WORKDIR</h3><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">WORKDIR /path/to/workdir</span><br></pre></td></tr></table></figure><p>为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录。可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。</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">WORKDIR /a</span><br><span class="line">WORKDIR b</span><br><span class="line">WORKDIR c</span><br><span class="line">RUN pwd</span><br></pre></td></tr></table></figure><p>最终路径是 /a/b/c。</p><p>WORKDIR 指令可以在 ENV 设置变量之后调用环境变量:</p><figure class="highlight plain"><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">ENV DIRPATH /path</span><br><span class="line">WORKDIR $DIRPATH/$DIRNAME</span><br></pre></td></tr></table></figure><p>最终路径则为 /path/$DIRNAME。</p><h3 id="7-12-ONBUILD"><a href="#7-12-ONBUILD" class="headerlink" title="7.12 ONBUILD"></a>7.12 ONBUILD</h3><p><code>ONBUILD [INSTRUCTION]</code><br>配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。</p><p>例如,Dockerfile 使用如下的内容创建了镜像 image-A:</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">[...]</span><br><span class="line">ONBUILD ADD . /app/src</span><br><span class="line">ONBUILD RUN /usr/local/bin/python-build --dir /app/src</span><br><span class="line">[...]</span><br></pre></td></tr></table></figure><p>如果基于 image-A 创建新的镜像时,新的 Dockerfile 中使用 FROM image-A 指定基础镜像时,会自动执行 ONBUILD 指令内容,等价于在后面添加了两条指令。</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"># Automatically run the following</span><br><span class="line">ADD . /app/src</span><br><span class="line">RUN /usr/local/bin/python-build --dir /app/src</span><br></pre></td></tr></table></figure><p>使用 ONBUILD 指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild。</p><h3 id="7-13-Dockerfile-Examples"><a href="#7-13-Dockerfile-Examples" class="headerlink" title="7.13 Dockerfile Examples"></a>7.13 Dockerfile Examples</h3><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></pre></td><td class="code"><pre><span class="line"># Nginx</span><br><span class="line">#</span><br><span class="line"># VERSION 0.0.1</span><br><span class="line"></span><br><span class="line">FROM ubuntu</span><br><span class="line">MAINTAINER Victor Vieux <victor@docker.com></span><br><span class="line"></span><br><span class="line">RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server</span><br><span class="line"></span><br><span class="line"># Firefox over VNC</span><br><span class="line">#</span><br><span class="line"># VERSION 0.3</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">FROM ubuntu</span><br><span class="line"></span><br><span class="line"># Install vnc, xvfb in order to create a 'fake' display and firefox</span><br><span class="line">RUN apt-get update && apt-get install -y x11vnc xvfb firefox</span><br><span class="line">RUN mkdir ~/.vnc</span><br><span class="line"># Setup a password</span><br><span class="line">RUN x11vnc -storepasswd 1234 ~/.vnc/passwd</span><br><span class="line"># Autostart firefox (might not be the best way, but it does the trick)</span><br><span class="line">RUN bash -c 'echo "firefox" >> /.bashrc'</span><br><span class="line"></span><br><span class="line">EXPOSE 5900</span><br><span class="line">CMD ["x11vnc", "-forever", "-usepw", "-create"]</span><br></pre></td></tr></table></figure><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"># Multiple images example</span><br><span class="line">#</span><br><span class="line"># VERSION 0.1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">FROM ubuntu</span><br><span class="line">RUN echo foo > bar</span><br><span class="line"># Will output something like ===> 907ad6c2736f</span><br></pre></td></tr></table></figure><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">FROM ubuntu</span><br><span class="line">RUN echo moo > oink</span><br><span class="line"># Will output something like ===> 695d7793cbe4</span><br><span class="line"></span><br><span class="line"># You᾿ll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with</span><br><span class="line"># /oink.</span><br></pre></td></tr></table></figure><h3 id="7-14-docker-build"><a href="#7-14-docker-build" class="headerlink" title="7.14 docker build"></a>7.14 docker build</h3><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">$ docker build -t <container-name>:<tag> -f Dockerfile .</span><br></pre></td></tr></table></figure><p>参考文档: <a href="https://docs.docker.com/reference/builder/" target="_blank" rel="noopener">Dockerfile Reference</a></p><h3 id="7-15-dockerfile-最佳实践"><a href="#7-15-dockerfile-最佳实践" class="headerlink" title="7.15 dockerfile 最佳实践"></a>7.15 dockerfile 最佳实践</h3><ul><li><p>使用 .dockerignore 文件<br>为了在 docker build 过程中更快上传和更加高效,应该使用一个 .dockerignore 文件用来排除构建镜像时不需要的文件或目录。例如,除非 .git 在构建过程中需要用到,否则你应该将它添加到 .dockerignore 文件中,这样可以节省很多时间。</p></li><li><p>避免安装不必要的软件包<br>为了降低复杂性、依赖性、文件大小以及构建时间,应该避免安装额外的或不必要的包。例如,不需要在一个数据库镜像中安装一个文本编辑器。</p></li><li><p>每个容器都跑一个进程<br>在大多数情况下,一个容器应该只单独跑一个程序。解耦应用到多个容器使其更容易横向扩展和重用。如果一个服务依赖另外一个服务,可以参考 Linking Containers Together。</p></li><li><p>最小化层<br>我们知道每执行一个指令,都会有一次镜像的提交,镜像是分层的结构,对于 Dockerfile,应该找到可读性和最小化层之间的平衡。</p></li><li><p>多行参数排序<br>如果可能,通过字母顺序来排序,这样可以避免安装包的重复并且更容易更新列表,另外可读性也会更强,添加一个空行使用 \ 换行:</p></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">RUN apt-get update && apt-get install -y \</span><br><span class="line"> bzr \</span><br><span class="line"> cvs \</span><br><span class="line"> git \</span><br><span class="line"> mercurial \</span><br><span class="line"> subversion</span><br></pre></td></tr></table></figure><ul><li>创建缓存<br>镜像构建过程中会按照 Dockerfile 的顺序依次执行,每执行一次指令 Docker 会寻找是否有存在的镜像缓存可复用,如果没有则创建新的镜像。如果不想使用缓存,则可以在 docker build 时添加 –no-cache=true 选项。</li></ul><p>从基础镜像开始就已经在缓存中了,下一个指令会对比所有的子镜像寻找是否执行相同的指令,如果没有则缓存失效。在大多数情况下只对比 Dockerfile 指令和子镜像就足够了。ADD 和 COPY 指令除外,执行 ADD 和 COPY 时存放到镜像的文件也是需要检查的,完成一个文件的校验之后再利用这个校验在缓存中查找,如果检测的文件改变则缓存失效。RUN apt-get -y update 命令只检查命令是否匹配,如果匹配就不会再执行更新了。</p><blockquote><p>为了有效地利用缓存,你需要保持你的 Dockerfile 一致,并且尽量在末尾修改。</p></blockquote><ul><li><p>Dockerfile 指令</p><ul><li>FROM: 只要可能就使用官方镜像库作为基础镜像</li><li><p>RUN: 为保持可读性、方便理解、可维护性,把长或者复杂的 RUN 语句使用 \ 分隔符分成多行</p><ul><li>不建议 RUN apt-get update 独立成行,否则如果后续包有更新,那么也不会再执行更新</li><li>避免使用 RUN apt-get upgrade 或者 dist-upgrade,很多必要的包在一个非 privileged 权限的容器里是无法升级的。如果知道某个包更新,使用 apt-get install -y xxx</li><li><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">RUN apt-get update && apt-get install -y package-bar package-foo</span><br></pre></td></tr></table></figure><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">RUN apt-get update && apt-get install -y \</span><br><span class="line"> aufs-tools \</span><br><span class="line"> automake \</span><br><span class="line"> btrfs-tools \</span><br><span class="line"> build-essential \</span><br><span class="line"> curl \</span><br><span class="line"> dpkg-sig \</span><br><span class="line"> git \</span><br><span class="line"> iptables \</span><br><span class="line"> libapparmor-dev \</span><br><span class="line"> libcap-dev \</span><br><span class="line"> libsqlite3-dev \</span><br><span class="line"> lxc=1.0* \</span><br><span class="line"> mercurial \</span><br><span class="line"> parallel \</span><br><span class="line"> reprepro \</span><br><span class="line"> ruby1.9.1 \</span><br><span class="line"> ruby1.9.1-dev \</span><br><span class="line"> s3cmd=1.1.0*</span><br></pre></td></tr></table></figure></li></ul></li><li><p>CMD: 推荐使用 CMD [“executable”, “param1”, “param2”…] 这种格式,CMD [“param”, “param”] 则配合 ENTRYPOINT 使用</p></li><li>EXPOSE: Dockerfile 指定要公开的端口,使用 docker run 时指定映射到宿主机的端口即可</li><li><p>ENV: 为了使新的软件更容易运行,可以使用 ENV 更新 PATH 变量。如 ENV PATH /usr/local/nginx/bin:$PATH 确保 CMD [“nginx”] 即可运行</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">ENV 也可以这样定义变量:</span><br><span class="line"></span><br><span class="line">ENV PG_MAJOR 9.3</span><br><span class="line">ENV PG_VERSION 9.3.4</span><br><span class="line">RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …</span><br><span class="line">ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH</span><br></pre></td></tr></table></figure></li><li><p>ADD or COPY: ADD 比 COPY 多一些特性「tar 文件自动解包和支持远程 URL」,不推荐添加远程 URL</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></pre></td><td class="code"><pre><span class="line"># 如不推荐这种方式:</span><br><span class="line"></span><br><span class="line">ADD http://example.com/big.tar.xz /usr/src/things/</span><br><span class="line">RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things</span><br><span class="line">RUN make -C /usr/src/things all</span><br><span class="line"></span><br><span class="line"># 推荐使用 curl 或者 wget 替换,使用如下方式:</span><br><span class="line"></span><br><span class="line">RUN mkdir -p /usr/src/things \</span><br><span class="line"> && curl -SL http://example.com/big.tar.gz \</span><br><span class="line"> | tar -xJC /usr/src/things \</span><br><span class="line"> && make -C /usr/src/things all</span><br><span class="line"></span><br><span class="line"># 如果不需要添加 tar 文件,推荐使用 COPY。</span><br></pre></td></tr></table></figure></li></ul></li></ul><p>参考文档:</p><ul><li><a href="https://docs.docker.com/articles/dockerfile_best-practices/" target="_blank" rel="noopener">Best practices for writing Dockerfiles</a></li><li><a href="http://dockerone.com/article/131" target="_blank" rel="noopener">Dockerfile最佳实践(一)</a></li><li><a href="http://dockerone.com/article/132" target="_blank" rel="noopener">Dockerfile最佳实践(二)</a></li></ul><h2 id="八、容器数据管理"><a href="#八、容器数据管理" class="headerlink" title="八、容器数据管理"></a>八、容器数据管理</h2><p>docker管理数据的方式有两种:</p><ul><li>数据卷</li><li>数据卷容器</li></ul><h3 id="8-1-数据卷"><a href="#8-1-数据卷" class="headerlink" title="8.1 数据卷"></a>8.1 数据卷</h3><p>数据卷是一个或多个容器专门指定绕过 Union File System 的目录,为持续性或共享数据提供一些有用的功能:</p><ul><li>数据卷可以在容器间共享和重用</li><li>数据卷数据改变是直接修改的</li><li>数据卷数据改变不会被包括在容器中</li><li>数据卷是持续性的,直到没有容器使用它们</li></ul><h4 id="添加一个数据卷"><a href="#添加一个数据卷" class="headerlink" title="添加一个数据卷"></a>添加一个数据卷</h4><p>你可以使用 -v 选项添加一个数据卷,或者可以使用多次 -v 选项为一个 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><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">$ sudo docker run --name data -v /data -t -i ubuntu:14.04 /bin/bash</span><br><span class="line"># 创建数据卷绑定到到新建容器,新建容器中会创建 /data 数据卷</span><br><span class="line">bash-4.1# ls -ld /data/</span><br><span class="line">drwxr-xr-x 2 root root 4096 Jul 23 06:59 /data/</span><br><span class="line">bash-4.1# df -Th</span><br><span class="line">Filesystem Type Size Used Avail Use% Mounted on</span><br><span class="line">... ...</span><br><span class="line"> ext4 91G 4.6G 82G 6% /data</span><br></pre></td></tr></table></figure><p>创建的数据卷可以通过 docker inspect 获取宿主机对应路径</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">$ sudo docker inspect data</span><br><span class="line">... ...</span><br><span class="line"> "Volumes": {</span><br><span class="line"> "/data": "/var/lib/docker/vfs/dir/151de401d268226f96d824fdf444e77a4500aed74c495de5980c807a2ffb7ea9"</span><br><span class="line"> }, # 可以看到创建的数据卷宿主机路径</span><br><span class="line">... ...</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">$ sudo docker inspect --format="< .Volumes >" data</span><br><span class="line">map[/data: /var/lib/docker/vfs/dir/151de401d268226f96d824fdf444e77a4500aed74c495de5980c807a2ffb7ea9]</span><br><span class="line">挂载宿主机目录为一个数据卷</span><br><span class="line">-v 选项除了可以创建卷,也可以挂载当前主机的一个目录到容器中。</span><br><span class="line"></span><br><span class="line">$ sudo docker run --name web -v /source/:/web -t -i ubuntu:14.04 /bin/bash</span><br><span class="line">bash-4.1# ls -ld /web/</span><br><span class="line">drwxr-xr-x 2 root root 4096 Jul 23 06:59 /web/</span><br><span class="line">bash-4.1# df -Th</span><br><span class="line">... ...</span><br><span class="line"> ext4 91G 4.6G 82G 6% /web</span><br><span class="line">bash-4.1# exit</span><br></pre></td></tr></table></figure><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">$ sudo docker run --rm --name test -v /source/:/test:ro -t -i ubuntu:14.04 /bin/bash</span><br></pre></td></tr></table></figure><h3 id="8-2-创建和挂载一个数据卷容器"><a href="#8-2-创建和挂载一个数据卷容器" class="headerlink" title="8.2 创建和挂载一个数据卷容器"></a>8.2 创建和挂载一个数据卷容器</h3><p>如果你有一些持久性的数据并且想在容器间共享,或者想用在非持久性的容器上,最好的方法是创建一个数据卷容器,然后从此容器上挂载数据。</p><h4 id="创建数据卷容器"><a href="#创建数据卷容器" class="headerlink" title="创建数据卷容器"></a>创建数据卷容器</h4><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">$ sudo docker run -t -i -d -v /test --name test ubuntu:14.04 echo hello</span><br></pre></td></tr></table></figure><p>使用 –volumes-from 选项在另一个容器中挂载 /test 卷。不管 test 容器是否运行,其它容器都可以挂载该容器数据卷,当然如果只是单独的数据卷是没必要运行容器的。</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">$ sudo docker run -t -i -d --volumes-from test --name test1 ubuntu:14.04 /bin/bash</span><br></pre></td></tr></table></figure><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">$ sudo docker run -t -i -d --volumes-from test --name test2 ubuntu:14.04 /bin/bash</span><br></pre></td></tr></table></figure><p>也可以继承其它挂载有 /test 卷的容器</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">$ sudo docker run -t -i -d --volumes-from test1 --name test3 ubuntu:14.04 /bin/bash</span><br></pre></td></tr></table></figure><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="https://ws1.sinaimg.cn/large/8697aaedly1fruojmwb2aj20i405tt8l.jpg" alt="" title=""> </div> <div class="image-caption"></div> </figure><h3 id="8-3-备份、恢复或迁移数据卷"><a href="#8-3-备份、恢复或迁移数据卷" class="headerlink" title="8.3 备份、恢复或迁移数据卷"></a>8.3 备份、恢复或迁移数据卷</h3><h4 id="备份"><a href="#备份" class="headerlink" title="备份"></a>备份</h4><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">$ sudo docker run --rm --volumes-from test -v $(pwd):/backup ubuntu:14.04 tar cvf /backup/test.tar /test</span><br><span class="line">tar: Removing leading `/' from member names</span><br><span class="line">/test/</span><br><span class="line">/test/b</span><br><span class="line">/test/d</span><br><span class="line">/test/c</span><br><span class="line">/test/a</span><br></pre></td></tr></table></figure><p>启动一个新的容器并且从 test 容器中挂载卷,然后挂载当前目录到容器中为 backup,并备份 test 卷中所有的数据为 test.tar,执行完成之后删除容器 –rm,此时备份就在当前的目录下,名为 test.tar。</p><figure class="highlight plain"><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">$ ls # 宿主机当前目录下产生了 test 卷的备份文件 test.tar</span><br><span class="line">test.tar</span><br></pre></td></tr></table></figure><h4 id="恢复"><a href="#恢复" class="headerlink" title="恢复"></a>恢复</h4><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">$ sudo docker run -t -i -d -v /test --name test4 ubuntu:14.04 /bin/bash</span><br><span class="line">$ sudo docker run --rm --volumes-from test4 -v $(pwd):/backup ubuntu:14.04 tar xvf /backup/test.tar -C /</span><br><span class="line"># 恢复之前的文件到新建卷中,执行完后自动删除容器</span><br><span class="line">test/</span><br><span class="line">test/b</span><br><span class="line">test/d</span><br><span class="line">test/c</span><br><span class="line">test/a</span><br></pre></td></tr></table></figure><h3 id="8-4-删除-Volumes"><a href="#8-4-删除-Volumes" class="headerlink" title="8.4 删除 Volumes"></a>8.4 删除 Volumes</h3><p>Volume 只有在下列情况下才能被删除:</p><figure class="highlight plain"><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">docker rm -v 删除容器时添加了 -v 选项</span><br><span class="line">docker run --rm 运行容器时添加了 --rm 选项</span><br></pre></td></tr></table></figure><p>否则,会在 /var/lib/docker/vfs/dir 目录中遗留很多不明目录。</p><p>参考文档:</p><ul><li><a href="http://docs.docker.com/userguide/dockervolumes/#data-volumes" target="_blank" rel="noopener">Managing Data in Containers</a></li><li><a href="http://dockerone.com/article/128" target="_blank" rel="noopener">深入理解Docker Volume(一)</a></li><li><a href="http://dockerone.com/article/129" target="_blank" rel="noopener">深入理解Docker Volume(二)</a></li></ul><h2 id="九、链接容器"><a href="#九、链接容器" class="headerlink" title="九、链接容器"></a>九、链接容器</h2><p>docker 允许把多个容器连接在一起,相互交互信息。docker 链接会创建一种容器父子级别的关系,其中父容器可以看到其子容器提供的信息。</p><h3 id="9-1-容器命名"><a href="#9-1-容器命名" class="headerlink" title="9.1 容器命名"></a>9.1 容器命名</h3><p>在创建容器时,如果不指定容器的名字,则默认会自动创建一个名字,这里推荐给容器命名:</p><p>1、给容器命名方便记忆,如命名运行 web 应用的容器为 web<br>2、为 docker 容器提供一个参考,允许方便其他容器调用,如把容器 web 链接到容器 db<br>可以通过 –name 选项给容器自定义命名:</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">$ sudo docker run -d -t -i --name test ubuntu:14.04 bash</span><br><span class="line">$ sudo docker inspect --format="<.Nmae>" test</span><br><span class="line">/test</span><br></pre></td></tr></table></figure><blockquote><p>注:容器名称必须唯一,即你只能命名一个叫 test 的容器。如果你想复用容器名,则必须在创建新的容器前通过 docker rm 删除旧的容器或者创建容器时添加 –rm 选项。</p></blockquote><h3 id="9-2-链接容器"><a href="#9-2-链接容器" class="headerlink" title="9.2 链接容器"></a>9.2 链接容器</h3><p>链接允许容器间安全通信,使用 –link 选项创建链接。</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">$ sudo docker run -d --name db training/postgres</span><br><span class="line"># 基于 training/postgres 镜像创建一个名为 db 的容器,然后下面创建一个叫做 web 的容器,并且将它与 db 相互连接在一起</span><br><span class="line"></span><br><span class="line">$ sudo docker run -d -P --name web --link db:db training/webapp python app.py</span><br><span class="line"># --link <name or id>:alias 选项指定链接到的容器。</span><br><span class="line">``` </span><br><span class="line"></span><br><span class="line">查看 web 容器的链接关系:</span><br></pre></td></tr></table></figure><p>$ sudo docker inspect -f “< .HostConfig.Links >” web<br>[/db:/web/db]<br><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"></span><br><span class="line">可以看到 web 容器被链接到 db 容器为 /web/db,这允许 web 容器访问 db 容器的信息。</span><br><span class="line"></span><br><span class="line">容器之间的链接实际做了什么?一个链接允许一个源容器提供信息访问给一个接收容器。在本例中,web 容器作为一个接收者,允许访问源容器 db 的相关服务信息。Docker 创建了一个安全隧道而不需要对外公开任何端口给外部容器,因此不需要在创建容器的时候添加 -p 或 -P 指定对外公开的端口,这也是链接容器的最大好处,本例为 PostgreSQL 数据库。</span><br><span class="line"></span><br><span class="line">Docker 主要通过以下两个方式提供连接信息给接收容器:</span><br><span class="line"></span><br><span class="line">- 环境变量</span><br><span class="line">- 更新 /etc/hosts 文件</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">当两个容器链接,Docker 会在目标容器上设置一些环境变量,以获取源容器的相关信息。</span><br><span class="line"></span><br><span class="line">首先,Docker 会在每个通过 --link 选项指定别名的目标容器上设置一个 <alias>_NAME 环境变量。如果一个名为 web 的容器通过 --link db:webdb 被链接到一个名为 db 的数据库容器,那么 web 容器上会设置一个环境变量为 WEBDB_NAME=/web/webdb.</span><br><span class="line"></span><br><span class="line">以之前的为例,Docker 还会设置端口变量:</span><br></pre></td></tr></table></figure></p><p>$ sudo docker run –rm –name web2 –link db:db training/webapp env<br>. . .<br>DB_NAME=/web2/db<br>DB_PORT=tcp://172.17.0.5:5432<br>DB_PORT_5432_TCP=tcp://172.17.0.5:5432 # <name><em>PORT</em><port>_<protocol> 协议可以是 TCP 或 UDP<br>DB_PORT_5432_TCP_PROTO=tcp<br>DB_PORT_5432_TCP_PORT=5432<br>DB_PORT_5432_TCP_ADDR=172.17.0.5<br>. . .<br><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"></span><br><span class="line">>注:这些环境变量只设置给容器中的第一个进程,类似一些守护进程 (如 sshd ) 当他们派生 shells 时会清除这些变量</span><br><span class="line"></span><br><span class="line">#### 更新 /etc/hosts 文件</span><br><span class="line"></span><br><span class="line">除了环境变量,Docker 会在目标容器上添加相关主机条目到 /etc/hosts 中,上例中就是 web 容器。</span><br></pre></td></tr></table></figure></protocol></port></name></p><p>$ sudo docker run -t -i –rm –link db:db training/webapp /bin/bash<br>root@aed84ee21bde:/opt/webapp# cat /etc/hosts<br>172.17.0.7 aed84ee21bde<br>. . .<br>172.17.0.5 db<br><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></pre></td><td class="code"><pre><span class="line">/etc/host 文件在源容器被重启之后会自动更新 IP 地址,而环境变量中的 IP 地址则不会自动更新的。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">## 十、构建私有库</span><br><span class="line">Docker 官方提供了 docker registry 的构建方法 docker-registry</span><br><span class="line"></span><br><span class="line">### 10.1 快速构建</span><br><span class="line">快速构建 docker registry 通过以下两步:</span><br><span class="line"></span><br><span class="line">- 安装 docker</span><br><span class="line">- 运行 registry: docker run -p 5000:5000 registry</span><br><span class="line">这种方法通过 Docker hub 使用官方镜像 [official image from the Docker hub](https://registry.hub.docker.com/_/registry/)</span><br><span class="line"></span><br><span class="line">### 10.2 不使用容器构建 registry</span><br><span class="line"></span><br><span class="line">#### 安装必要的软件</span><br></pre></td></tr></table></figure></p><p>$ sudo apt-get install build-essential python-dev libevent-dev python-pip liblzma-dev<br><figure class="highlight plain"><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">#### 配置 docker-registry</span><br></pre></td></tr></table></figure></p><p>sudo pip install docker-registry<br><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">或者 使用 github clone 手动安装</span><br></pre></td></tr></table></figure></p><p>$ git clone <a href="https://github.com/dotcloud/docker-registry.git" target="_blank" rel="noopener">https://github.com/dotcloud/docker-registry.git</a><br>$ cd docker-registry/<br>$ cp config/config_sample.yml config/config.yml<br>$ mkdir /data/registry -p<br>$ pip install .<br><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"></span><br><span class="line">#### 运行</span><br><span class="line">`docker-registry`</span><br><span class="line"></span><br><span class="line">#### 高级启动方式 [不推荐]</span><br><span class="line"></span><br><span class="line">使用 gunicorn 控制:</span><br></pre></td></tr></table></figure></p><p>gunicorn -c contrib/gunicorn_config.py docker_registry.wsgi:application<br><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">或者对外监听开放</span><br></pre></td></tr></table></figure></p><p>gunicorn –access-logfile - –error-logfile - -k gevent -b 0.0.0.0:5000 -w 4 –max-requests 100 docker_registry.wsgi:application<br><code>`</code></p><h3 id="10-3-提交指定容器到私有库"><a href="#10-3-提交指定容器到私有库" class="headerlink" title="10.3 提交指定容器到私有库"></a>10.3 提交指定容器到私有库</h3><p>$ docker tag ubuntu:12.04 私有库IP:5000/ubuntu:12.04<br>$ docker push 私有库IP:5000/ubuntu<br>更多的配置选项推荐阅读官方文档:</p><ul><li><a href="https://github.com/docker/docker-registry/blob/master/README.md" target="_blank" rel="noopener">Docker-Registry README</a></li><li><a href="https://github.com/docker/docker-registry/blob/master/ADVANCED.md" target="_blank" rel="noopener">Docker-Registry advanced use</a></li></ul>]]></content>
<summary type="html">
<blockquote>
<p>原文地址:<a href="https://blog.opskumu.com/docker.html" target="_blank" rel="noopener">https://blog.opskumu.com/docker.html</a><
</summary>
<category term="docker" scheme="http://pylixm.cc/categories/docker/"/>
<category term="devops" scheme="http://pylixm.cc/tags/devops/"/>
<category term="自动化运维" scheme="http://pylixm.cc/tags/%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4/"/>
<category term="docker" scheme="http://pylixm.cc/tags/docker/"/>
</entry>
<entry>
<title>如何整理个人资料</title>
<link href="http://pylixm.cc/posts/2018-05-02-How-to-arrange-note.html"/>
<id>http://pylixm.cc/posts/2018-05-02-How-to-arrange-note.html</id>
<published>2018-05-01T16:00:00.000Z</published>
<updated>2018-05-14T10:18:11.698Z</updated>
<content type="html"><![CDATA[<p>在现如今信息爆炸的时代,资料整理的方法显得越来越重要。好的资料整理方法可以让收集的资料发挥出它应有的价值,否则便和没有收集无异。</p><p>在平时工作学习中,看到好的技术文档或文章忍不住把它放到收藏夹或使用印象笔记类似的工具裁剪到笔记里,想着日后细细品读。可大多数是没有日后的,这便造成了收集的资料越来越多,越来越混乱,有时候还整理下,但随着数量的增多,发现整理这些东西也是需要很大的时间成本的。这些资料没有发挥其应用的价值,但还舍不得删掉。</p><p>之前一直想整理自己的知识体系,想着形成自己的一个知识库,可以随时翻阅。当需要用到哪方便的知识时,可以通过翻阅很快的回忆起来,立即上手。近几年有写博客的习惯,把一些读书笔记和技术的使用过程记录了下来,发挥了些知识库的作用。但总感觉不是那么完美,最近查了下资料,看了些有关资料和个人知识系统整理的文章,有些想法,梳理下加深印象。</p><h2 id="整体流程"><a href="#整体流程" class="headerlink" title="整体流程"></a>整体流程</h2><p>整个过程应该是这样的:<br><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="/static/imgs/know.png" alt="" title=""> </div> <div class="image-caption"></div> </figure></p><h2 id="资料的收集"><a href="#资料的收集" class="headerlink" title="资料的收集"></a>资料的收集</h2><p>在现如今网络如此遍历,资料的获取已经不成问题。而如何有效的获取,成为关键。即如何在海一样的信息中,找到我们需要的。现如今我的资料收集方法如下:</p><ul><li>搜索引擎 通过搜索引擎关键字,找到自己需要的资料。关键字很重要,必要的时候可以直接使用英文搜索。</li><li>微信公众号 通过平时碎片化的阅读,可收集到一些自己认为有价值的文章。</li><li>各技术社区 现如今国内各技术社区活跃,文章水平残次不齐。推荐先根据标题略读,发现好的文章后再精度。我们会发现一些高质量文章的产出作者,我们可以订阅他们,之后可重点关注他们的文章。</li></ul><h2 id="整理分类"><a href="#整理分类" class="headerlink" title="整理分类"></a>整理分类</h2><p>根据我自己的资料,大致分如下几种:</p><ul><li>新技术的文档:各种框架文档</li><li>技术学习文章:微信公众号文章、各技术社区文章</li><li>技术问题:stackoverflow/技术问题解决记录</li><li>大牛博客</li><li>其他工具性网址资料</li><li>产出的联系或功能代码</li></ul><p>针对以上资料,该如何整理呢?</p><p>工欲善其事必先利其器,整理以上众多资料,整理方法及使用工具如下:</p><ul><li>xmind 构建自己的知识体系脑图,作为整个知识库的索引。</li><li>evernote 主要存储技术文章和不能公开的自己产出的文档笔记。可按语言和功能所属划分类别,再为文章打好tag便于检索。建立<code>临时笔记本</code>暂存未读完的或不知归类的文章,待后续阅读整理。</li><li>bookmarks 主要存储技术文档链接、牛人博客地址、工具性网址。同样分好类别,便于检索。</li><li>百度云盘 存储开源pdf书籍,方便各设备同步。</li><li>github 存储自己的周边项目。</li><li>博客 产出自己的想法和收获。</li></ul><h2 id="消化,产出价值"><a href="#消化,产出价值" class="headerlink" title="消化,产出价值"></a>消化,产出价值</h2><p>以上资源分类保存好后,便是消化产出价值了。</p><p>第一,在此阶段重要的还是<code>整理</code>,通过阅读我们知道了每篇文章的价值,是否需要留存待日后查询。原则如下:</p><ul><li>可以在网络上轻易找到的,直接删除</li><li>过期的资料直接删除</li></ul><p><strong>第二,除了平时碎片化时间的阅读,还需要沉下心来花大块的时间,系统的来学习某项技术</strong>。</p><p>如何高效的学习消化,日后再整理篇,本篇暂不展开。</p><p>通过以上各流程方法,便构建了自己的知识库。完成了以下目标:</p><ul><li>梳理自己的知识图谱</li><li>通过知识库,可以方便的回忆起某特定技术使用方法。</li><li>形成自己的QA</li><li>记录下自己学习的周边项目,作为工作参考</li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://www.zhihu.com/question/21929143" target="_blank" rel="noopener">知乎 - 如何有效地进行资料整理?</a></li><li><a href="https://www.zhihu.com/question/36349460" target="_blank" rel="noopener">知乎 - 信息爆炸的时代,如何静心学习?</a></li></ul>]]></content>
<summary type="html">
<p>在现如今信息爆炸的时代,资料整理的方法显得越来越重要。好的资料整理方法可以让收集的资料发挥出它应有的价值,否则便和没有收集无异。</p>
<p>在平时工作学习中,看到好的技术文档或文章忍不住把它放到收藏夹或使用印象笔记类似的工具裁剪到笔记里,想着日后细细品读。可大多数是没有
</summary>
<category term="个人管理" scheme="http://pylixm.cc/categories/%E4%B8%AA%E4%BA%BA%E7%AE%A1%E7%90%86/"/>
<category term="个人管理" scheme="http://pylixm.cc/tags/%E4%B8%AA%E4%BA%BA%E7%AE%A1%E7%90%86/"/>
</entry>
<entry>
<title>【 Go语言学习笔记 】 - Golang 语法进阶</title>
<link href="http://pylixm.cc/posts/2018-04-26-Go-advance.html"/>
<id>http://pylixm.cc/posts/2018-04-26-Go-advance.html</id>
<published>2018-04-25T16:00:00.000Z</published>
<updated>2018-05-14T10:17:10.778Z</updated>
<content type="html"><![CDATA[<h2 id="编码"><a href="#编码" class="headerlink" title="编码"></a>编码</h2><ul><li><p>Go 原生支持UTF-8, 可直接打印非ASCII码字符。</p><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Printf(<span class="string">"Hello, world or 你好,世界 or καλημ ́ρα κóσμ or こんにちはせかい\n"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>可以使用任意UTF-8 字符作为标识符,但不建议。</p></li></ul><h2 id="导入"><a href="#导入" class="headerlink" title="导入"></a>导入</h2><ul><li>Go 以包作为代码的最小组织单位,通过包来实现代码的导入、权限的控制。</li><li>每个go文件必须从以<code>package</code> 包声明开头,声明包名称最好和目录相同,也可以不同。不同时,注意<code>import</code> 引入时,为目录名,在引入后的调用使用中,使用该引入包的 <code>package</code> 声明名称。</li><li><p>三种导入方式:</p><figure class="highlight go"><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"><span class="keyword">import</span> str <span class="string">"strings"</span> </span><br><span class="line">str.HasPrefix(<span class="string">"abc"</span>,<span class="string">"a"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 本地化的导入, 使用导入包中的函数时可不写模块名</span></span><br><span class="line"><span class="keyword">import</span> . <span class="string">"strings"</span> </span><br><span class="line">HasPrefix(<span class="string">"abc"</span>,<span class="string">"a"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 仅执行代码包中的初始化函数</span></span><br><span class="line"><span class="keyword">import</span> _ <span class="string">"strings"</span></span><br></pre></td></tr></table></figure></li><li><p>一个package级别的func, type, 变量, 常量, 在package内部下无论大小,无需导入,可以随意访问。此时需要run整个包的编译文件:</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></pre></td><td class="code"><pre><span class="line">awesomeProject</span><br><span class="line">├── awesomeProject <span class="comment"># go build 后产生</span></span><br><span class="line">├── main.go</span><br><span class="line">└── second.go</span><br></pre></td></tr></table></figure></li></ul><p>如下运行:<br><figure class="highlight plain"><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">go run *.go </span><br><span class="line">./awesomeProject</span><br></pre></td></tr></table></figure></p><h2 id="Go-基础"><a href="#Go-基础" class="headerlink" title="Go 基础"></a>Go 基础</h2><h3 id="基本数据类型"><a href="#基本数据类型" class="headerlink" title="基本数据类型"></a>基本数据类型</h3><ul><li>字符串类型是不可变的,无法使用下标的方式访问。</li></ul><h3 id="声明"><a href="#声明" class="headerlink" title="声明"></a>声明</h3><ul><li>简要声明只能在函数中使用。</li><li>区分简要声明与赋值,防止简要声明造成的全局变量重新声明的问题。</li></ul><h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><ul><li><p>Go 函数支持可变参数</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">myfunc</span><span class="params">(arg ...<span class="keyword">int</span>)</span></span> {}</span><br></pre></td></tr></table></figure></li><li><p>参数为值类型:其实是传了一个参数的副本,函数中修改参数的值,并不会影响函数外参数的值。</p></li><li>参数为指针类型或引用类型:传入了一个指向数据值得一个指针副本,当改变该指针对应的值时,实际上是改变了函数外指针对应的值。</li><li>传指针比较轻量级 (8bytes),当传入函数的参数占用较大内存时,使用指针参数,会节省参数赋值的时间和内存开销,提升效率。常用于结构体作为参数传入函数时。</li><li>Go语言中channel,slice,map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变slice的长度,则仍需要取地址传递指针)</li><li>函数也可作为参数来传递<figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> testInt <span class="function"><span class="keyword">func</span><span class="params">(<span class="keyword">int</span>)</span> <span class="title">bool</span> // 声明了一个函数类型</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">func</span> <span class="title">isOdd</span><span class="params">(integer <span class="keyword">int</span>)</span> <span class="title">bool</span></span> {</span><br><span class="line"> <span class="keyword">if</span> integer%<span class="number">2</span> == <span class="number">0</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line"> }</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="function"><span class="keyword">func</span> <span class="title">isEven</span><span class="params">(integer <span class="keyword">int</span>)</span> <span class="title">bool</span></span> {</span><br><span class="line"> <span class="keyword">if</span> integer%<span class="number">2</span> == <span class="number">0</span> {</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 class="keyword">return</span> <span class="literal">false</span></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><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">filter</span><span class="params">(slice []<span class="keyword">int</span>, f testInt)</span> []<span class="title">int</span></span> {</span><br><span class="line"> <span class="keyword">var</span> result []<span class="keyword">int</span></span><br><span class="line"> <span class="keyword">for</span> _, value := <span class="keyword">range</span> slice {</span><br><span class="line"> <span class="keyword">if</span> f(value) {</span><br><span class="line"> result = <span class="built_in">append</span>(result, value)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line"> slice := []<span class="keyword">int</span> {<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 class="number">7</span>}</span><br><span class="line"> fmt.Println(<span class="string">"slice = "</span>, slice)</span><br><span class="line"> odd := filter(slice, isOdd) <span class="comment">// 函数当做值来传递了</span></span><br><span class="line"> fmt.Println(<span class="string">"Odd elements of slice are: "</span>, odd)</span><br><span class="line"> even := filter(slice, isEven) <span class="comment">// 函数当做值来传递了</span></span><br><span class="line"> fmt.Println(<span class="string">"Even elements of slice are: "</span>, even)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><h2 id="Struct-结构体"><a href="#Struct-结构体" class="headerlink" title="Struct 结构体"></a>Struct 结构体</h2><ul><li>结构体是类型的集合</li><li>结构体和通过<code>匿名结构体</code>来实现结构体字段的继承<figure class="highlight go"><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">package</span> main</span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Human <span class="keyword">struct</span> {</span><br><span class="line"> name <span class="keyword">string</span></span><br><span class="line"> age <span class="keyword">int</span></span><br><span class="line"> weight <span class="keyword">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Student <span class="keyword">struct</span> {</span><br><span class="line"> Human <span class="comment">// 匿名字段,那么默认Student就包含了Human的所有字段</span></span><br><span class="line"> speciality <span class="keyword">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="comment">// 我们初始化一个学生</span></span><br><span class="line"> mark := Student{Human{<span class="string">"Mark"</span>, <span class="number">25</span>, <span class="number">120</span>}, <span class="string">"Computer Science"</span>}</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 我们访问相应的字段</span></span><br><span class="line"> fmt.Println(<span class="string">"His name is "</span>, mark.name)</span><br><span class="line"> fmt.Println(<span class="string">"His age is "</span>, mark.age)</span><br><span class="line"> fmt.Println(<span class="string">"His weight is "</span>, mark.weight)</span><br><span class="line"> fmt.Println(<span class="string">"His speciality is "</span>, mark.speciality)</span><br><span class="line"> <span class="comment">// 修改对应的备注信息</span></span><br><span class="line"> mark.speciality = <span class="string">"AI"</span></span><br><span class="line"> fmt.Println(<span class="string">"Mark changed his speciality"</span>)</span><br><span class="line"> fmt.Println(<span class="string">"His speciality is "</span>, mark.speciality)</span><br><span class="line"> <span class="comment">// 修改他的年龄信息</span></span><br><span class="line"> fmt.Println(<span class="string">"Mark become old"</span>)</span><br><span class="line"> mark.age = <span class="number">46</span></span><br><span class="line"> fmt.Println(<span class="string">"His age is"</span>, mark.age)</span><br><span class="line"> <span class="comment">// 修改他的体重信息</span></span><br><span class="line"> fmt.Println(<span class="string">"Mark is not an athlet anymore"</span>)</span><br><span class="line"> mark.weight += <span class="number">60</span></span><br><span class="line"> fmt.Println(<span class="string">"His weight is"</span>, mark.weight)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="static/imgs/student_struct.png" alt="" title=""> </div> <div class="image-caption"></div> </figure><h2 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h2><ul><li>方法多了一个接受者(所属者)的func: <code>func (r ReceiverType) funcName(parameters) (results)</code></li><li>receiver 可以是任何你自定义的类型、内置类型等各种类型。</li><li>当指针作为receiver时,引用其方法可以直接使用指针对应的对象。</li><li>方法可以通过匿名结构体来继承。</li><li>方法的调用顺序是,先调用当前结构体的方法,再调用其继承的方法。所以,可以通过重新本结构体的方法,来达到方法重写的目的。<figure class="highlight go"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Human <span class="keyword">struct</span> {</span><br><span class="line"> name <span class="keyword">string</span></span><br><span class="line"> age <span class="keyword">int</span></span><br><span class="line"> phone <span class="keyword">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Student <span class="keyword">struct</span> {</span><br><span class="line"> Human <span class="comment">//匿名字段</span></span><br><span class="line"> school <span class="keyword">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Employee <span class="keyword">struct</span> {</span><br><span class="line"> Human <span class="comment">//匿名字段</span></span><br><span class="line"> company <span class="keyword">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//Human定义method</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(h *Human)</span> <span class="title">SayHi</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Printf(<span class="string">"Hi, I am %s you can call me on %s\n"</span>, h.name, h.phone)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//Employee的method重写Human的method</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(e *Employee)</span> <span class="title">SayHi</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Printf(<span class="string">"Hi, I am %s, I work at %s. Call me on %s\n"</span>, e.name,</span><br><span class="line"> e.company, e.phone) <span class="comment">//Yes you can split into 2 lines here.</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> mark := Student{Human{<span class="string">"Mark"</span>, <span class="number">25</span>, <span class="string">"222-222-YYYY"</span>}, <span class="string">"MIT"</span>}</span><br><span class="line"> sam := Employee{Human{<span class="string">"Sam"</span>, <span class="number">45</span>, <span class="string">"111-888-XXXX"</span>}, <span class="string">"Golang Inc"</span>}</span><br><span class="line"></span><br><span class="line"> mark.SayHi()</span><br><span class="line"> sam.SayHi()</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><h2 id="接口"><a href="#接口" class="headerlink" title="接口"></a>接口</h2><ul><li>接口为一组抽象方法的集合,它必须由其他非interface类型实现,而不能自我实现。</li><li>空接口 <code>interface(interface{})</code>, 可认为任何类型都实现了空接口。</li><li>接口变量可以存储任何实现了接口的类型,空接口变量可以存储任何类型数据。</li><li><p>当接口作为函数参数时,则该参数可以为任何实现了该接口的数据类型。</p><figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"strconv"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Element <span class="keyword">interface</span>{}</span><br><span class="line"><span class="keyword">type</span> List [] Element</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Person <span class="keyword">struct</span> {</span><br><span class="line"> name <span class="keyword">string</span></span><br><span class="line"> age <span class="keyword">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//定义了String方法,实现了fmt.Stringer接口</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(p Person)</span> <span class="title">String</span><span class="params">()</span> <span class="title">string</span></span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"(name: "</span> + p.name + <span class="string">" - age: "</span>+strconv.Itoa(p.age)+ <span class="string">" years)"</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> list := <span class="built_in">make</span>(List, <span class="number">3</span>)</span><br><span class="line"> list[<span class="number">0</span>] = <span class="number">1</span> <span class="comment">// an int</span></span><br><span class="line"> list[<span class="number">1</span>] = <span class="string">"Hello"</span> <span class="comment">// a string</span></span><br><span class="line"> list[<span class="number">2</span>] = Person{<span class="string">"Dennis"</span>, <span class="number">70</span>}</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> index, element := <span class="keyword">range</span> list {</span><br><span class="line"> <span class="comment">// value, ok = element.(T) 判断是是否是T类型</span></span><br><span class="line"> <span class="keyword">if</span> value, ok := element.(<span class="keyword">int</span>); ok {</span><br><span class="line"> fmt.Printf(<span class="string">"list[%d] is an int and its value is %d\n"</span>, index, value)</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> value, ok := element.(<span class="keyword">string</span>); ok {</span><br><span class="line"> fmt.Printf(<span class="string">"list[%d] is a string and its value is %s\n"</span>, index, value)</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> value, ok := element.(Person); ok {</span><br><span class="line"> fmt.Printf(<span class="string">"list[%d] is a Person and its value is %s\n"</span>, index, value)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> fmt.Printf(<span class="string">"list[%d] is of a different type\n"</span>, index)</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>接口可通过嵌套接口来继承接口的方法。</p></li><li>Go 语言支持反射<figure class="highlight go"><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">t := reflect.TypeOf(i) <span class="comment">//得到类型的元数据,通过t我们能获取类型定义里面的所有元素</span></span><br><span class="line">v := reflect.ValueOf(i) <span class="comment">//得到实际的值,通过v我们获取存储在里面的值,还可以去改变值</span></span><br></pre></td></tr></table></figure></li></ul><h2 id="并发"><a href="#并发" class="headerlink" title="并发"></a>并发</h2><ul><li><p>线程同步:</p><figure class="highlight go"><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><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用 sync.Mutex</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> mu sync.Mutex</span><br><span class="line"></span><br><span class="line"> mu.Lock()</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span>{</span><br><span class="line"> fmt.Println(<span class="string">"你好, 世界"</span>)</span><br><span class="line"> mu.Unlock()</span><br><span class="line"> }()</span><br><span class="line"></span><br><span class="line"> mu.Lock()</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 使用无缓存channal</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> done := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span>{</span><br><span class="line"> fmt.Println(<span class="string">"你好, 世界"</span>)</span><br><span class="line"> <-done</span><br><span class="line"> }()</span><br><span class="line"></span><br><span class="line"> done <- <span class="number">1</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">// 使用有缓存channal</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> done := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>, <span class="number">10</span>) <span class="comment">// 带 10 个缓存</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 开N个后台打印线程</span></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="built_in">cap</span>(done); i++ {</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span>{</span><br><span class="line"> fmt.Println(<span class="string">"你好, 世界"</span>)</span><br><span class="line"> done <- <span class="number">1</span></span><br><span class="line"> }()</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 等待N个后台线程完成</span></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="built_in">cap</span>(done); i++ {</span><br><span class="line"> <-done</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 对于阻塞N个线程完成后再继续执行,可使用 WaitGroup </span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 开N个后台打印线程</span></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">10</span>; i++ {</span><br><span class="line"> wg.Add(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(<span class="string">"你好, 世界"</span>)</span><br><span class="line"> wg.Done()</span><br><span class="line"> }()</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 等待N个后台线程完成</span></span><br><span class="line"> wg.Wait()</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>开启多个并发,只要有1个返回,既退出住线程。</p><figure class="highlight go"><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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">string</span>, <span class="number">32</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> ch <- searchByBing(<span class="string">"golang"</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> ch <- searchByGoogle(<span class="string">"golang"</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> ch <- searchByBaidu(<span class="string">"golang"</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> fmt.Println(<-ch)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>生产者消费者模型</p><figure class="highlight go"><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><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Package pubsub implements a simple multi-topic pub-sub library.</span></span><br><span class="line"><span class="keyword">package</span> pubsub</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"sync"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> (</span><br><span class="line"> subscriber <span class="keyword">chan</span> <span class="keyword">interface</span>{} <span class="comment">// 订阅者为一个管道</span></span><br><span class="line"> topicFunc <span class="function"><span class="keyword">func</span><span class="params">(v <span class="keyword">interface</span>{})</span> <span class="title">bool</span> // 主题为一个过滤器</span></span><br><span class="line"><span class="function">)</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">// 发布者对象</span></span><br><span class="line"><span class="function"><span class="title">type</span> <span class="title">Publisher</span> <span class="title">struct</span></span> {</span><br><span class="line"> m sync.RWMutex <span class="comment">// 读写锁</span></span><br><span class="line"> buffer <span class="keyword">int</span> <span class="comment">// 订阅队列的缓存大小</span></span><br><span class="line"> timeout time.Duration <span class="comment">// 发布超时时间</span></span><br><span class="line"> subscribers <span class="keyword">map</span>[subscriber]topicFunc <span class="comment">// 订阅者信息</span></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="function"><span class="keyword">func</span> <span class="title">NewPublisher</span><span class="params">(publishTimeout time.Duration, buffer <span class="keyword">int</span>)</span> *<span class="title">Publisher</span></span> {</span><br><span class="line"> <span class="keyword">return</span> &Publisher{</span><br><span class="line"> buffer: buffer,</span><br><span class="line"> timeout: publishTimeout,</span><br><span class="line"> subscribers: <span class="built_in">make</span>(<span class="keyword">map</span>[subscriber]topicFunc),</span><br><span class="line"> }</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="function"><span class="keyword">func</span> <span class="params">(p *Publisher)</span> <span class="title">Subscribe</span><span class="params">()</span> <span class="title">chan</span> <span class="title">interface</span></span>{} {</span><br><span class="line"> <span class="keyword">return</span> p.SubscribeTopic(<span class="literal">nil</span>)</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="function"><span class="keyword">func</span> <span class="params">(p *Publisher)</span> <span class="title">SubscribeTopic</span><span class="params">(topic topicFunc)</span> <span class="title">chan</span> <span class="title">interface</span></span>{} {</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">interface</span>{}, p.buffer)</span><br><span class="line"> p.m.Lock()</span><br><span class="line"> p.subscribers[ch] = topic</span><br><span class="line"> p.m.Unlock()</span><br><span class="line"> <span class="keyword">return</span> ch</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="function"><span class="keyword">func</span> <span class="params">(p *Publisher)</span> <span class="title">Evict</span><span class="params">(sub <span class="keyword">chan</span> <span class="keyword">interface</span>{})</span></span> {</span><br><span class="line"> p.m.Lock()</span><br><span class="line"> <span class="keyword">defer</span> p.m.Unlock()</span><br><span class="line"></span><br><span class="line"> <span class="built_in">delete</span>(p.subscribers, sub)</span><br><span class="line"> <span class="built_in">close</span>(sub)</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="function"><span class="keyword">func</span> <span class="params">(p *Publisher)</span> <span class="title">Publish</span><span class="params">(v <span class="keyword">interface</span>{})</span></span> {</span><br><span class="line"> p.m.RLock()</span><br><span class="line"> <span class="keyword">defer</span> p.m.RUnlock()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"> <span class="keyword">for</span> sub, topic := <span class="keyword">range</span> p.subscribers {</span><br><span class="line"> wg.Add(<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">go</span> p.sendTopic(sub, topic, v, &wg)</span><br><span class="line"> }</span><br><span class="line"> wg.Wait()</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="function"><span class="keyword">func</span> <span class="params">(p *Publisher)</span> <span class="title">Close</span><span class="params">()</span></span> {</span><br><span class="line"> p.m.Lock()</span><br><span class="line"> <span class="keyword">defer</span> p.m.Unlock()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> sub := <span class="keyword">range</span> p.subscribers {</span><br><span class="line"> <span class="built_in">delete</span>(p.subscribers, sub)</span><br><span class="line"> <span class="built_in">close</span>(sub)</span><br><span class="line"> }</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="function"><span class="keyword">func</span> <span class="params">(p *Publisher)</span> <span class="title">sendTopic</span><span class="params">(sub subscriber, topic topicFunc, v <span class="keyword">interface</span>{}, wg *sync.WaitGroup)</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> wg.Done()</span><br><span class="line"> <span class="keyword">if</span> topic != <span class="literal">nil</span> && !topic(v) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> sub <- v:</span><br><span class="line"> <span class="keyword">case</span> <-time.After(p.timeout):</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// main.go </span></span><br><span class="line"><span class="keyword">import</span> <span class="string">"path/to/pubsub"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> p := pubsub.NewPublisher(<span class="number">100</span>*time.Millisecond, <span class="number">10</span>)</span><br><span class="line"> <span class="keyword">defer</span> p.Close()</span><br><span class="line"></span><br><span class="line"> all := p.Subscribe()</span><br><span class="line"> golang := p.SubscribeTopic(<span class="function"><span class="keyword">func</span><span class="params">(v <span class="keyword">interface</span>{})</span> <span class="title">bool</span></span> {</span><br><span class="line"> <span class="keyword">if</span> s, ok := v.(<span class="keyword">string</span>); ok {</span><br><span class="line"> <span class="keyword">return</span> strings.Contains(s, <span class="string">"golang"</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> p.Publish(<span class="string">"hello, world!"</span>)</span><br><span class="line"> p.Publish(<span class="string">"hello, golang!"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">for</span> msg := <span class="keyword">range</span> all {</span><br><span class="line"> fmt.Println(<span class="string">"all:"</span>, msg)</span><br><span class="line"> }</span><br><span class="line"> } ()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">for</span> msg := <span class="keyword">range</span> golang {</span><br><span class="line"> fmt.Println(<span class="string">"golang:"</span>, msg)</span><br><span class="line"> }</span><br><span class="line"> } ()</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 运行一定时间后退出</span></span><br><span class="line"> time.Sleep(<span class="number">3</span> * time.Second)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://chai2010.gitbooks.io/advanced-go-programming-book/content/ch1-basic/ch1-06-goroutine.html" target="_blank" rel="noopener">https://chai2010.gitbooks.io/advanced-go-programming-book/content/ch1-basic/ch1-06-goroutine.html</a></li><li><a href="https://www.kancloud.cn/kancloud/web-application-with-golang/44111" target="_blank" rel="noopener">https://www.kancloud.cn/kancloud/web-application-with-golang/44111</a></li></ul>]]></content>
<summary type="html">
<h2 id="编码"><a href="#编码" class="headerlink" title="编码"></a>编码</h2><ul>
<li><p>Go 原生支持UTF-8, 可直接打印非ASCII码字符。</p>
<figure class="highlight go
</summary>
<category term="golang" scheme="http://pylixm.cc/categories/golang/"/>
<category term="golang" scheme="http://pylixm.cc/tags/golang/"/>
<category term="语言学习" scheme="http://pylixm.cc/tags/%E8%AF%AD%E8%A8%80%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>Linux基础系列 - 命令ssh-keygen</title>
<link href="http://pylixm.cc/posts/2018-04-24-Linux-ssh-keygen.html"/>
<id>http://pylixm.cc/posts/2018-04-24-Linux-ssh-keygen.html</id>
<published>2018-04-23T16:00:00.000Z</published>
<updated>2018-04-28T08:07:34.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>内容来自网络,由<a href="http://pylixm.cc">pylixm</a>整理。</p></blockquote><h2 id="命令介绍"><a href="#命令介绍" class="headerlink" title="命令介绍"></a>命令介绍</h2><p><strong>ssh-keygen</strong></p><p>生成、管理和转换认证密钥,包括 RSA 和 DSA 两种密钥。</p><p>参数如下:<br><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><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">-a trials 在使用 -T 对 DH-GEX 候选素数进行安全筛选时需要执行的基本测试数量。</span><br><span class="line">-B 显示指定的公钥/私钥文件的 bubblebabble 摘要。</span><br><span class="line">-b bits 指定密钥长度。对于RSA密钥,最小要求768位,默认是2048位。DSA密钥必须恰好是1024位(FIPS 186-2 标准的要求)。</span><br><span class="line">-C comment 提供一个新注释</span><br><span class="line">-c 要求修改私钥和公钥文件中的注释。本选项只支持 RSA1 密钥。序程将提示输入私钥文件名、密语(如果存在)、新注释。</span><br><span class="line">-D reader 下载存储在智能卡 reader 里的 RSA 公钥。</span><br><span class="line">-e 读取OpenSSH的私钥或公钥文件,并以 RFC 4716 SSH 公钥文件格式在 stdout 上显示出来。该选项能够为多种商业版本的 SSH 输出密钥。</span><br><span class="line">-F hostname 在 known_hosts 文件中搜索指定的 hostname ,并列出所有的匹配项。这个选项主要用于查找散列过的主机名/ip地址,还可以和 -H 选项联用打印找到的公钥的散列值。</span><br><span class="line">-f filename 指定密钥文件名。</span><br><span class="line">-G output_file 为 DH-GEX 产生候选素数。这些素数必须在使用之前使用 -T 选项进行安全筛选。</span><br><span class="line">-g 在使用 -r 打印指纹资源记录的时候使用通用的 DNS 格式。</span><br><span class="line">-H 对 known_hosts 文件进行散列计算。这将把文件中的所有主机名/ip地址替换为相应的散列值。原来文件的内容将会添加一个<span class="string">".old"</span>后缀后保存。这些散列值只能被 ssh 和 sshd 使用。这个选项不会修改已经经过散列的主机名/ip地址,因此可以在部分公钥已经散列过的文件上安全使用。</span><br><span class="line">-i 读取未加密的SSH-2兼容的私钥/公钥文件,然后在 stdout 显示OpenSSH兼容的私钥/公钥。该选项主要用于从多种商业版本的SSH中导入密钥。</span><br><span class="line">-l 显示公钥文件的指纹数据。它也支持 RSA1 的私钥。对于RSA和DSA密钥,将会寻找对应的公钥文件,然后显示其指纹数据。</span><br><span class="line">-M memory 指定在生成 DH-GEXS 候选素数的时候最大内存用量(MB)。</span><br><span class="line">-N new_passphrase 提供一个新的密语。</span><br><span class="line">-P passphrase 提供(旧)密语。</span><br><span class="line">-p 要求改变某私钥文件的密语而不重建私钥。程序将提示输入私钥文件名、原来的密语、以及两次输入新密语。</span><br><span class="line">-q 安静模式。用于在 /etc/rc 中创建新密钥的时候。</span><br><span class="line">-R hostname 从 known_hosts 文件中删除所有属于 hostname 的密钥。这个选项主要用于删除经过散列的主机(参见 -H 选项)的密钥。</span><br><span class="line">-r hostname 打印名为 hostname 的公钥文件的 SSHFP 指纹资源记录。</span><br><span class="line">-S start 指定在生成 DH-GEX 候选模数时的起始点(16进制)。</span><br><span class="line">-T output_file 测试 Diffie-Hellman group exchange 候选素数(由 -G 选项生成)的安全性。</span><br><span class="line">-t <span class="built_in">type</span> 指定要创建的密钥类型。可以使用:<span class="string">"rsa1"</span>(SSH-1) <span class="string">"rsa"</span>(SSH-2) <span class="string">"dsa"</span>(SSH-2)</span><br><span class="line">-U reader 把现存的RSA私钥上传到智能卡 reader</span><br><span class="line">-v 详细模式。ssh-keygen 将会输出处理过程的详细调试信息。常用于调试模数的产生过程。重复使用多个 -v 选项将会增加信息的详细程度(最大3次)。</span><br><span class="line">-W generator 指定在为 DH-GEX 测试候选模数时想要使用的 generator</span><br><span class="line">-y 读取OpenSSH专有格式的公钥文件,并将OpenSSH公钥显示在 stdout 上。</span><br></pre></td></tr></table></figure></p><h2 id="常用命令组合"><a href="#常用命令组合" class="headerlink" title="常用命令组合"></a>常用命令组合</h2><ol><li>生产密钥对:<code>ssh-keygen -t rsa -C "pyli.xm@gmail.com"</code></li></ol><p>有关ssh原理及免密登录可参考<a href="http://pylixm.cc/posts/2016-11-16-ssh-how-to-use.html">这里</a></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="http://killer-jok.iteye.com/blog/1853451" target="_blank" rel="noopener">http://killer-jok.iteye.com/blog/1853451</a></li></ul>]]></content>
<summary type="html">
<blockquote>
<p>内容来自网络,由<a href="http://pylixm.cc">pylixm</a>整理。</p>
</blockquote>
<h2 id="命令介绍"><a href="#命令介绍" class="headerlink" title="命
</summary>
<category term="Linux" scheme="http://pylixm.cc/categories/Linux/"/>
<category term="Linux" scheme="http://pylixm.cc/tags/Linux/"/>
<category term="运维" scheme="http://pylixm.cc/tags/%E8%BF%90%E7%BB%B4/"/>
</entry>
<entry>
<title>Linux基础系列 - 命令ps</title>
<link href="http://pylixm.cc/posts/2018-04-23-Linux-ps.html"/>
<id>http://pylixm.cc/posts/2018-04-23-Linux-ps.html</id>
<published>2018-04-22T16:00:00.000Z</published>
<updated>2018-04-28T08:07:34.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>内容来自网络,由<a href="http://pylixm.cc">pylixm</a>整理。</p></blockquote><h2 id="命令介绍"><a href="#命令介绍" class="headerlink" title="命令介绍"></a>命令介绍</h2><p><code>ps</code> 是Linux系统自带的查看进程信息的命令。<code>ps</code> 命令支持三种使用的语法格式:</p><ul><li>UNIX 风格,选项可以组合在一起,并且选项前必须有“-”连字符</li><li>BSD 风格,选项可以组合在一起,但是选项前不能有“-”连字符</li><li>GNU 风格的长选项,选项前有两个“-”连字符</li></ul><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><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><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line">ps [-aAcdefHjlmNVwy][acefghLnrsSTuvxX][-C <指令名称>][-g <群组名称>]</span><br><span class="line"></span><br><span class="line">[-G <群组识别码>][-p <进程识别码>][p <进程识别码>][-s <阶段作业>]</span><br><span class="line"></span><br><span class="line">[-t <终端机编号>][t <终端机编号>][-u <用户识别码>][-U <用户识别码>]</span><br><span class="line"></span><br><span class="line">[U <用户名称>][-<进程识别码>][--cols <每列字符数>]</span><br><span class="line"></span><br><span class="line">[--columns <每列字符数>][--cumulative][--deselect][--forest]</span><br><span class="line"></span><br><span class="line">[--headers][--<span class="built_in">help</span>][-- info][--lines <显示列数>][--no-headers]</span><br><span class="line"></span><br><span class="line">[--group <群组名称>][-Group <群组识别码>][--pid <进程识别码>]</span><br><span class="line"></span><br><span class="line">[--rows <显示列数>][--sid <阶段作业>][--tty <终端机编号>]</span><br><span class="line"></span><br><span class="line">[--user <用户名称>][--User <用户识别码>][--version]</span><br><span class="line"></span><br><span class="line">[--width <每列字符数>]</span><br><span class="line"></span><br><span class="line">参数说明:</span><br><span class="line"></span><br><span class="line"> -a 显示所有终端机下执行的进程,除了阶段作业领导者之外。</span><br><span class="line"> a 显示现行终端机下的所有进程,包括其他用户的进程。</span><br><span class="line"> -A 显示所有进程。</span><br><span class="line"> -c 显示CLS和PRI栏位。</span><br><span class="line"> c 列出进程时,显示每个进程真正的指令名称,而不包含路径,参数或常驻服务的标示。</span><br><span class="line"> -C <指令名称> 指定执行指令的名称,并列出该指令的进程的状况。</span><br><span class="line"> -d 显示所有进程,但不包括阶段作业领导者的进程。</span><br><span class="line"> -e 此参数的效果和指定<span class="string">"A"</span>参数相同。</span><br><span class="line"> e 列出进程时,显示每个进程所使用的环境变量。</span><br><span class="line"> -f 显示UID,PPIP,C与STIME栏位。</span><br><span class="line"> f 用ASCII字符显示树状结构,表达进程间的相互关系。</span><br><span class="line"> -g<群组名称> 此参数的效果和指定<span class="string">"-G"</span>参数相同,当亦能使用阶段作业领导者的名称来指定。</span><br><span class="line"> g 显示现行终端机下的所有进程,包括群组领导者的进程。</span><br><span class="line"> -G<群组识别码> 列出属于该群组的进程的状况,也可使用群组名称来指定。</span><br><span class="line"> h 不显示标题列。</span><br><span class="line"> -H 显示树状结构,表示进程间的相互关系。</span><br><span class="line"> -j或j 采用工作控制的格式显示进程状况。</span><br><span class="line"> -l或l 采用详细的格式来显示进程状况。</span><br><span class="line"> L 列出栏位的相关信息。</span><br><span class="line"> -m或m 显示所有的执行绪。</span><br><span class="line"> n 以数字来表示USER和WCHAN栏位。</span><br><span class="line"> -N 显示所有的进程,除了执行ps指令终端机下的进程之外。</span><br><span class="line"> -p<进程识别码> 指定进程识别码,并列出该进程的状况。</span><br><span class="line"> p<进程识别码> 此参数的效果和指定<span class="string">"-p"</span>参数相同,只在列表格式方面稍有差异。</span><br><span class="line"> r 只列出现行终端机正在执行中的进程。</span><br><span class="line"> -s<阶段作业> 指定阶段作业的进程识别码,并列出隶属该阶段作业的进程的状况。</span><br><span class="line"> s 采用进程信号的格式显示进程状况。</span><br><span class="line"> S 列出进程时,包括已中断的子进程资料。</span><br><span class="line"> -t<终端机编号> 指定终端机编号,并列出属于该终端机的进程的状况。</span><br><span class="line"> t<终端机编号> 此参数的效果和指定<span class="string">"-t"</span>参数相同,只在列表格式方面稍有差异。</span><br><span class="line"> -T 显示现行终端机下的所有进程。</span><br><span class="line"> -u<用户识别码> 此参数的效果和指定<span class="string">"-U"</span>参数相同。</span><br><span class="line"> u 以用户为主的格式来显示进程状况。</span><br><span class="line"> -U<用户识别码> 列出属于该用户的进程的状况,也可使用用户名称来指定。</span><br><span class="line"> U<用户名称> 列出属于该用户的进程的状况。</span><br><span class="line"> v 采用虚拟内存的格式显示进程状况。</span><br><span class="line"> -V或V 显示版本信息。</span><br><span class="line"> -w或w 采用宽阔的格式来显示进程状况。 </span><br><span class="line"> x 显示所有进程,不以终端机来区分。</span><br><span class="line"> X 采用旧式的Linux i386登陆格式显示进程状况。</span><br><span class="line"> -y 配合参数<span class="string">"-l"</span>使用时,不显示F(flag)栏位,并以RSS栏位取代ADDR栏位</span><br><span class="line"> -<进程识别码> 此参数的效果和指定<span class="string">"p"</span>参数相同。</span><br><span class="line"> --cols<每列字符数> 设置每列的最大字符数。</span><br><span class="line"> --columns<每列字符数> 此参数的效果和指定<span class="string">"--cols"</span>参数相同。</span><br><span class="line"> --cumulative 此参数的效果和指定<span class="string">"S"</span>参数相同。</span><br><span class="line"> --deselect 此参数的效果和指定<span class="string">"-N"</span>参数相同。</span><br><span class="line"> --forest 此参数的效果和指定<span class="string">"f"</span>参数相同。</span><br><span class="line"> --headers 重复显示标题列。</span><br><span class="line"> --<span class="built_in">help</span> 在线帮助。</span><br><span class="line"> --info 显示排错信息。</span><br><span class="line"> --lines<显示列数> 设置显示画面的列数。</span><br><span class="line"> --no-headers 此参数的效果和指定<span class="string">"h"</span>参数相同,只在列表格式方面稍有差异。</span><br><span class="line"> --group<群组名称> 此参数的效果和指定<span class="string">"-G"</span>参数相同。</span><br><span class="line"> --Group<群组识别码> 此参数的效果和指定<span class="string">"-G"</span>参数相同。</span><br><span class="line"> --pid<进程识别码> 此参数的效果和指定<span class="string">"-p"</span>参数相同。</span><br><span class="line"> --rows<显示列数> 此参数的效果和指定<span class="string">"--lines"</span>参数相同。</span><br><span class="line"> --sid<阶段作业> 此参数的效果和指定<span class="string">"-s"</span>参数相同。</span><br><span class="line"> --tty<终端机编号> 此参数的效果和指定<span class="string">"-t"</span>参数相同。</span><br><span class="line"> --user<用户名称> 此参数的效果和指定<span class="string">"-U"</span>参数相同。</span><br><span class="line"> --User<用户识别码> 此参数的效果和指定<span class="string">"-U"</span>参数相同。</span><br><span class="line"> --version 此参数的效果和指定<span class="string">"-V"</span>参数相同。</span><br><span class="line"> --widty<每列字符数> 此参数的效果和指定<span class="string">"-cols"</span>参数相同。</span><br></pre></td></tr></table></figure><h2 id="常用命令组合"><a href="#常用命令组合" class="headerlink" title="常用命令组合"></a>常用命令组合</h2><ol><li>显示当前所有进程:<code>ps -ax</code></li></ol><ul><li>-a 所有进程</li><li>-x 同时显示没有控制终端的进程</li></ul><ol start="2"><li><p>显示进程cpu和内容信息,不区分终端:<code>ps -aux</code></p><ul><li>通过cpu信息来排序:<code>ps -aux --sort -pcpu</code></li><li>通过内存信息来排序:<code>ps -aux --sort -pmem</code></li><li>通过cpu和内存信息来排序,只显示前10条:<code>ps -aux --sort -pcpu,+pmem | head -n 10</code></li></ul></li><li><p>进程名和PID过滤进程:<code>ps -C <进程名></code></p></li><li>根据线程来过滤进程:<code>ps -L 1234</code></li><li>树形显示进程:<code>ps -axjf</code> or <code>pstree</code></li><li><p>显示安全信息: <code>ps -eo pid,user,args</code><br>-e 显示所有进程信息,-o 参数控制输出。Pid,User 和 Args参数显示PID,运行应用的用户和该应用。</p></li><li><p>显示某用户下的所有进程:<code>ps -u pylixm</code></p></li><li>查看进程并按内存使用大小排列:<code>ps -e -o "%C : %p :%z : %a"|sort -k5 -nr</code></li></ol><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="http://linux.51yip.com/search/ps" target="_blank" rel="noopener">http://linux.51yip.com/search/ps</a></li><li><a href="https://linux.cn/article-4743-1.html" target="_blank" rel="noopener">https://linux.cn/article-4743-1.html</a></li></ul>]]></content>
<summary type="html">
<blockquote>
<p>内容来自网络,由<a href="http://pylixm.cc">pylixm</a>整理。</p>
</blockquote>
<h2 id="命令介绍"><a href="#命令介绍" class="headerlink" title="命
</summary>
<category term="Linux" scheme="http://pylixm.cc/categories/Linux/"/>
<category term="Linux" scheme="http://pylixm.cc/tags/Linux/"/>
<category term="运维" scheme="http://pylixm.cc/tags/%E8%BF%90%E7%BB%B4/"/>
</entry>
<entry>
<title>【 python 基础系列 】 Pickle 的使用</title>
<link href="http://pylixm.cc/posts/2018-02-02-python-pickle.html"/>
<id>http://pylixm.cc/posts/2018-02-02-python-pickle.html</id>
<published>2018-02-01T16:00:00.000Z</published>
<updated>2018-05-14T08:24:54.974Z</updated>
<content type="html"><![CDATA[<blockquote><p>原文:<a href="https://pycoders-weekly-chinese.readthedocs.io/en/latest/issue6/a-guide-to-pythons-magic-methods.html#id22" target="_blank" rel="noopener">储存你的对象</a></p></blockquote><p>如果你接触过其他的 python 开发者,你可能已经听说过 Pickle 了, Pickle 是用来序列化 Python 数据结构的模块,在你需要暂时存储一个对象的时候(比如缓存),这个模块非常的有用,不过这同时也是隐患的诞生地。</p><p>序列化数据是一个非常重要的功能,所以他不仅仅拥有相关的模块( Pickle , cPickle ),还有自己的协议以及魔术方法,不过首先,我们先讨论下关于序列化内建数据结构的方法。</p><h2 id="Pickling-简单例子"><a href="#Pickling-简单例子" class="headerlink" title="Pickling: 简单例子"></a>Pickling: 简单例子</h2><p>让我们深入研究 Pickle,比如说你现在需要临时储存一个字典,你可以把它写入到一个文件里,并且要小心翼翼的确保格式正确,之后再用 exec() 或者处理文件输入来恢复数据,实际上这是很不安全的,如果你使用文本存储了一些重要的数据,任何方式的改变都可能会影响到你的程序,轻则程序崩溃,重则被恶意程序利用,所以,让我们用 Pickle 代替这种方式:</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">import</span> pickle</span><br><span class="line"></span><br><span class="line">data = {<span class="string">'foo'</span>: [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>],</span><br><span class="line"> <span class="string">'bar'</span>: (<span class="string">'Hello'</span>, <span class="string">'world!'</span>),</span><br><span class="line"> <span class="string">'baz'</span>: <span class="keyword">True</span>}</span><br><span class="line">jar = open(<span class="string">'data.pkl'</span>, <span class="string">'wb'</span>)</span><br><span class="line">pickle.dump(data, jar) <span class="comment"># write the pickled data to the file jar</span></span><br><span class="line">jar.close()</span><br><span class="line">嗯,过了几个小时之后,我们需要用到它了,只需把它 unpickle 了就行了:</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> pickle</span><br><span class="line"></span><br><span class="line">pkl_file = open(<span class="string">'data.pkl'</span>, <span class="string">'rb'</span>) <span class="comment"># connect to the pickled data</span></span><br><span class="line">data = pickle.load(pkl_file) <span class="comment"># load it into a variable</span></span><br><span class="line"><span class="keyword">print</span> data</span><br><span class="line">pkl_file.close()</span><br></pre></td></tr></table></figure><p>正如你期望的,数据原封不动的回来了!</p><h2 id="忠告"><a href="#忠告" class="headerlink" title="忠告"></a>忠告</h2><p>同时要给你一句忠告: pickle 并不是很完美, Pickle 文件很容易被不小心或者故意损坏, Pickle 文件比纯文本文件要稍微安全一点,但是还是可以被利用运行恶意程序。 Pickle 不是跨版本兼容的(译注:最近刚好在 《Python Cookbook》上看到相关讨论,书中描述的 Pickle 是跨版本兼容的,此点待验证),所以尽量不要去分发 Pickle 过的文本,因为别人并不一定能够打开。不过在做缓存或者其他需要序列化数据的时候, Pickle 还是很有用处的。</p>]]></content>
<summary type="html">
<blockquote>
<p>原文:<a href="https://pycoders-weekly-chinese.readthedocs.io/en/latest/issue6/a-guide-to-pythons-magic-methods.html#id22" targ
</summary>
<category term="python" scheme="http://pylixm.cc/categories/python/"/>
<category term="python" scheme="http://pylixm.cc/tags/python/"/>
<category term="Pickle" scheme="http://pylixm.cc/tags/Pickle/"/>
</entry>
<entry>
<title>【 python 基础系列 】 - python 魔法方法</title>
<link href="http://pylixm.cc/posts/2018-02-01-python-magic-method.html"/>
<id>http://pylixm.cc/posts/2018-02-01-python-magic-method.html</id>
<published>2018-01-31T16:00:00.000Z</published>
<updated>2018-05-14T08:25:33.511Z</updated>
<content type="html"><![CDATA[<p>本文参考 pycoder’s weekly 的文章,做了下python 魔术方法的使用总结,记录备查。</p><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>魔术方法,英文 Magic Methods。顾名思义,它的使用方式不同于一般python方法的使用,它们的使用显得让人捉摸不透,就像魔法一样。在某些场景下python会隐式的调用相应的魔术方法,来完成对应的功能。我们可以重新这些方法来实现与python内建类型相同的对象,或改变python内建类型的相关行为。</p><p>魔术方法,通常以双下划线包围,例如我们最熟悉的 <code>__init__</code> 就是一个魔术方法。</p><p><strong>扩展:python 下划线</strong></p><ul><li>单下划线开头(例:_test): 为符合编码惯例,通常认为是私有变量,无法使用 <code>from <模块> import *</code> 引入,除非在 <code>__all__</code>中包含了。类及实例均可访问。</li><li>双下划线开头,例: __test : 类的私有属性, 只有类及其方法内可以访问。</li><li>前后双下划线:python 内置函数,如魔法函数 <code>__init__</code></li></ul><p>下面分类别记录下python中的魔术方法,方便使用备查。</p><h2 id="常用魔术方法"><a href="#常用魔术方法" class="headerlink" title="常用魔术方法"></a>常用魔术方法</h2><h3 id="类初始化"><a href="#类初始化" class="headerlink" title="类初始化"></a>类初始化</h3><ul><li><p><code>__new__</code> 类初始化时执行,在 <code>__init__</code> 函数之前,它的第一个参数是这个类,其他的参数是用来直接传递给 <strong>init</strong> 方法。详解,<a href="https://www.python.org/download/releases/2.2/descrintro/#__new__" target="_blank" rel="noopener">python文档</a></p></li><li><p><code>__init__</code> 类的初始化方法。new 和 init 可以理解为构造函数。</p></li><li><code>__del__</code> 析构器,它不实现语句 del x ,定义的是当一个对象进行垃圾回收时候的行为。</li></ul><p><strong>new vs init</strong></p><ul><li><code>__new__</code>是用来创建类并返回这个类的实例, 而<code>__init__</code>只是将传入的参数来初始化该实例.</li><li><code>__new__</code>在创建一个实例的过程中必定会被调用,但<code>__init__</code>就不一定,比如通过pickle.load的方式反序列化一个实例时就不会调用<code>__init__</code>。</li><li><code>__new__</code>方法总是需要返回该类的一个实例,而<code>__init__</code>不能返回除了None的任何值。</li></ul><h3 id="类的表现"><a href="#类的表现" class="headerlink" title="类的表现"></a>类的表现</h3><ul><li><code>__str__</code> 定义当 str() 调用的时候的返回值。</li><li><code>__repr__</code> 定义 repr() 被调用的时候的返回值, str() 和 repr() 的主要区别在于 repr() 返回的是机器可读的输出,而 str() 返回的是人类可读的。</li><li><code>__unicode__(self)</code> 定义当 unicode() 调用的时候的返回值, unicode() 和 str() 很相似,但是返回的是unicode字符串</li><li><code>__hash__(self)</code> 定义当 hash() 调用的时候的返回值,它返回一个整形。</li></ul><h3 id="属性控制访问"><a href="#属性控制访问" class="headerlink" title="属性控制访问"></a>属性控制访问</h3><ul><li><code>__getarr__(self, name)</code> 你可以定义当用户试图获取一个不存在的属性时的行为 这适用于对普通拼写错误的获取和重定向,对获取一些不建议的属性时候给出警告(如果你愿意你也可以计算并且给出一个值)或者处理一个 AttributeError</li><li><code>__setatt__(self, name, value)</code> 定义了你对属性进行赋值和修改操作时的行为, 避免”无限递归”的错误</li><li><code>__delattr__(self, name)</code> 定义了删除属性时的行为</li><li><code>__getattribute__(self, name)</code> 定义了你的属性被访问时的行为,同样要避免”无限递归”的错误。需要提醒的是,最好不要尝试去实现<code>__getattribute__</code>,因为很少见到这种做法,而且很容易出bug</li></ul><h3 id="容器类"><a href="#容器类" class="headerlink" title="容器类"></a>容器类</h3><ul><li><code>__len__(self)</code> 需要返回数值类型,以表示容器的长度。该方法在可变容器和不可变容器中必须实现。</li><li><code>__getitem__(self, key)</code> 当你执行self[key]的时候,调用的就是该方法。该方法在可变容器和不可变容器中也都必须实现。调用的时候,如果key的类型错误,该方法应该抛出TypeError;如果没法返回key对应的数值时,该方法应该抛出ValueError。</li><li><code>__setitem__(self, key, value)</code> 当你执行self[key] = value时,调用的是该方法。</li><li><code>__delitem__(self, key)</code> 当你执行del self[key]的时候,调用的是该方法。</li><li><code>__iter__(self)</code> 该方法需要返回一个迭代器(iterator)。当你执行for x in container: 或者使用iter(container)时,该方法被调用。</li><li><code>__reversed__(self)</code> 如果想要该数据结构被內建函数reversed()支持,就还需要实现该方法。</li><li><code>__contains__(self, item)</code> 如果定义了该方法,那么在执行item in container 或者 item not in container时该方法就会被调用。如果没有定义,那么Python会迭代容器中的元素来一个一个比较,从而决定返回True或者False。</li><li><code>__missing__(self, key)</code> dict字典类型会有该方法,它定义了key如果在容器中找不到时触发的行为。比如d = {‘a’: 1}, 当你执行d[notexist]时,d.<strong>missing</strong>[‘notexist’]就会被调用。</li><li><code>__concat__(self, other)</code> 来定义当用其他的来连接两个序列时候的行为, 当 + 操作符被调用时候会返回一个 self 和 other.<strong>concat</strong> 被调用后的结果产生的新序列。 </li></ul><h3 id="调用对象"><a href="#调用对象" class="headerlink" title="调用对象"></a>调用对象</h3><ul><li><code>__call__(self, [args...])</code> 允许一个类的实例像函数一样被调用。在那些类的实例经常改变状态的时候会非常有效。调用这个实例是一种改变这个对象状态的直接和优雅的做法</li></ul><h3 id="回话管理"><a href="#回话管理" class="headerlink" title="回话管理"></a>回话管理</h3><ul><li><code>__enter__(self)</code> 定义当使用 with 语句的时候会话管理器应该初始块被创建的时候的行为.返回值被 with 语句的目标或者 as 后的名字绑定;</li><li><code>__exit__(self, exception_type, exception_value, traceback)</code> 定义当一个代码块被执行或者终止后会话管理器应该做什么,它可以被用来处理异常,清除工作或者做一些代码块执行完毕之后的日常工作;如果代码块执行成功, exception_type , exception_value , 和 traceback 将会是 None 。否则的话你可以选择处理这个异常或者是直接交给用户处理。如果你想处理这个异常的话,确认 <strong>exit</strong> 在所有结束之后会返回 True 。</li></ul><h3 id="描述器"><a href="#描述器" class="headerlink" title="描述器"></a>描述器</h3><p>描述器对象不能独立存在, 它需要被另一个所有者类所持有。描述器对象可以访问到其拥有者实例的属性,在面向对象编程时,如果一个类的属性有相互依赖的关系时,使用描述器来编写代码可以很巧妙的组织逻辑。<br>如 Django的ORM中, models.Model中的InterField等字段, 就是通过描述器来实现功能的。</p><p>为了构建一个描述器,一个类必须有至少 <strong>get</strong> 或者 <strong>set</strong> 其中一个,并且 <strong>delete</strong> 被实现。</p><ul><li><code>__get__(self, instance, owner)</code> 参数instance是拥有者类的实例。参数owner是拥有者类本身。<strong>get</strong>在其拥有者对其读值的时候调用。</li><li><code>__set__(self, instance, value)</code> 在其拥有者对其进行修改值的时候调用。</li><li><code>__delete__(self, instance)</code> 在其拥有者对其进行删除的时候调用。</li></ul><h2 id="代码实例"><a href="#代码实例" class="headerlink" title="代码实例"></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><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><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</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="keyword">from</span> os.path <span class="keyword">import</span> join</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">FileObject</span><span class="params">(object)</span>:</span></span><br><span class="line"> <span class="string">"""给文件对象进行包装从而确认在删除时文件流关闭"""</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, filepath=<span class="string">''</span>, filename=<span class="string">'sample.txt'</span>)</span>:</span></span><br><span class="line"> <span class="comment"># 读写模式打开一个文件</span></span><br><span class="line"> self.file = open(join(filepath, filename), <span class="string">'r+'</span>)</span><br><span class="line"> print(<span class="string">"打开文件"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__del__</span><span class="params">(self)</span>:</span></span><br><span class="line"> self.file.close()</span><br><span class="line"> <span class="keyword">del</span> self.file</span><br><span class="line"> print(<span class="string">'销毁文件'</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">Word</span><span class="params">(str)</span>:</span></span><br><span class="line"> <span class="string">"""存储单词的类,定义比较单词的几种方法"""</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__new__</span><span class="params">(cls, word)</span>:</span></span><br><span class="line"> <span class="comment"># 注意我们必须要用到__new__方法,因为str是不可变类型</span></span><br><span class="line"> <span class="comment"># 所以我们必须在创建的时候将它初始化</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">' '</span> <span class="keyword">in</span> word:</span><br><span class="line"> print(<span class="string">"Value contains spaces. Truncating to first space."</span>)</span><br><span class="line"> word = word[:word.index(<span class="string">' '</span>)] <span class="comment"># 单词是第一个空格之前的所有字符</span></span><br><span class="line"> <span class="keyword">return</span> str.__new__(cls, word)</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, word)</span>:</span></span><br><span class="line"> self.word = word</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__gt__</span><span class="params">(self, other)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> len(self) > len(other)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__lt__</span><span class="params">(self, other)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> len(self) < len(other)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__ge__</span><span class="params">(self, other)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> len(self) >= len(other)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__le__</span><span class="params">(self, other)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> len(self) <= len(other)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__str__</span><span class="params">(self)</span>:</span></span><br><span class="line"> print(<span class="string">'call str func '</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"Word: %s"</span> % self.word</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"> print(<span class="string">'class repr func'</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"<Word:%s>"</span> % self.word</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__unicode__</span><span class="params">(self)</span>:</span></span><br><span class="line"> print(<span class="string">'call unicode'</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'Word:%s'</span> % self.word</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">MyCounter</span><span class="params">(object)</span>:</span></span><br><span class="line"> <span class="string">"""一个包含计数器的控制权限的类每当值被改变时计数器会加一"""</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, val)</span>:</span></span><br><span class="line"> <span class="comment"># 使用 super 因为不是所有的类都有 __dict__ 属性</span></span><br><span class="line"> super(MyCounter, self).__setattr__(<span class="string">'counter'</span>, <span class="number">0</span>)</span><br><span class="line"> super(MyCounter, self).__setattr__(<span class="string">'value'</span>, val)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__getattr__</span><span class="params">(self, item)</span>:</span></span><br><span class="line"> print(<span class="string">'call getattr '</span>)</span><br><span class="line"> <span class="keyword">return</span> super(MyCounter, self).__getattr__(item)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__getattribute__</span><span class="params">(self, item)</span>:</span></span><br><span class="line"> print(<span class="string">'call getattribute'</span>)</span><br><span class="line"> <span class="keyword">return</span> super(MyCounter, self).__getattribute__(item)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__setattr__</span><span class="params">(self, name, value)</span>:</span></span><br><span class="line"> <span class="comment"># self.key = value # 会隐式的再次调用self.__setattr__ 引起递归</span></span><br><span class="line"> <span class="comment"># self.__dict__[key] = value</span></span><br><span class="line"> print(<span class="string">'call setattr'</span>)</span><br><span class="line"> <span class="keyword">if</span> name == <span class="string">'value'</span>:</span><br><span class="line"> super(MyCounter, self).__setattr__(<span class="string">'counter'</span>, self.counter + <span class="number">1</span>)</span><br><span class="line"> <span class="comment"># 如果你不想让其他属性被访问的话,那么可以抛出 AttributeError(name) 异常</span></span><br><span class="line"> super(MyCounter, self).__setattr__(name, value)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__delattr__</span><span class="params">(self, name)</span>:</span></span><br><span class="line"> print(<span class="string">'call del attr'</span>)</span><br><span class="line"> <span class="keyword">if</span> name == <span class="string">'value'</span>:</span><br><span class="line"> super(MyCounter, self).__setattr__(<span class="string">'counter'</span>, self.counter + <span class="number">1</span>)</span><br><span class="line"> super(MyCounter, self).__delattr__(name)</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">Container</span><span class="params">(object)</span>:</span></span><br><span class="line"> <span class="string">""" 容器基类 """</span></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)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__setitem__</span><span class="params">(self, key, value)</span>:</span></span><br><span class="line"> <span class="keyword">pass</span></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, item)</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">DynamicContainer</span><span class="params">(Container)</span>:</span></span><br><span class="line"> <span class="string">""" 可变容器 """</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__delitem__</span><span class="params">(self, key)</span>:</span></span><br><span class="line"> <span class="keyword">pass</span></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">pass</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__reversed__</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">pass</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)</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">NoDynamicContainer</span><span class="params">(Container)</span>:</span></span><br><span class="line"> <span class="string">""" 不可变容器 """</span></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">pass</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)</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">MyList</span><span class="params">(Container)</span>:</span></span><br><span class="line"> <span class="string">""" 一个封装了一些附加魔术方法比如 head, tail, init, last, drop, 和take的列表类。 """</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, values=None)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> values <span class="keyword">is</span> <span class="keyword">None</span>:</span><br><span class="line"> self.values = []</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> self.values = values</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.values)</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, key)</span>:</span></span><br><span class="line"> <span class="comment"># 如果键的类型或者值无效,列表值将会抛出错误</span></span><br><span class="line"> <span class="keyword">return</span> self.values[key]</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__setitem__</span><span class="params">(self, key, value)</span>:</span></span><br><span class="line"> self.values[key] = value</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__delitem__</span><span class="params">(self, key)</span>:</span></span><br><span class="line"> <span class="keyword">del</span> self.values[key]</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> iter(self.values)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__reversed__</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> reversed(self.values)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">append</span><span class="params">(self, value)</span>:</span></span><br><span class="line"> self.values.append(value)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">head</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> self.values[<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">tail</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> self.values[<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">init</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="comment"># 返回一直到末尾的所有元素</span></span><br><span class="line"> <span class="keyword">return</span> self.values[:<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">last</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="comment"># 返回末尾元素</span></span><br><span class="line"> <span class="keyword">return</span> self.values[<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">drop</span><span class="params">(self, n)</span>:</span></span><br><span class="line"> <span class="comment"># 返回除前n个外的所有元素</span></span><br><span class="line"> <span class="keyword">return</span> self.values[n:]</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">take</span><span class="params">(self, n)</span>:</span></span><br><span class="line"> <span class="comment"># 返回前n个元素</span></span><br><span class="line"> <span class="keyword">return</span> self.values[:n]</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">Entity</span><span class="params">(object)</span>:</span></span><br><span class="line"> <span class="string">"""调用实体来改变实体的位置。"""</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, size, x, y)</span>:</span></span><br><span class="line"> self.x, self.y = x, y</span><br><span class="line"> self.size = size</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, x, y)</span>:</span></span><br><span class="line"> <span class="string">"""改变实体的位置"""</span></span><br><span class="line"> self.x, self.y = x, y</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">Closer</span>:</span></span><br><span class="line"> <span class="string">"""通过with语句和一个close方法来关闭一个对象的会话管理器"""</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, obj)</span>:</span></span><br><span class="line"> self.obj = obj</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__enter__</span><span class="params">(self)</span>:</span></span><br><span class="line"> print(<span class="string">'call enter'</span>)</span><br><span class="line"> <span class="keyword">return</span> self.obj <span class="comment"># bound to target</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__exit__</span><span class="params">(self, exception_type, exception_val, trace)</span>:</span></span><br><span class="line"> print(exception_type, exception_val, trace)</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> self.obj.close()</span><br><span class="line"> <span class="keyword">except</span> AttributeError: <span class="comment"># obj isn't closable</span></span><br><span class="line"> print(<span class="string">'Not closable.'</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">True</span> <span class="comment"># exception handled successfully</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">Meter</span><span class="params">(object)</span>:</span></span><br><span class="line"> <span class="string">"""Descriptor for a meter."""</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, value=<span class="number">0.0</span>)</span>:</span></span><br><span class="line"> self.value = float(value)</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, instance, owner)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> self.value</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__set__</span><span class="params">(self, instance, value)</span>:</span></span><br><span class="line"> self.value = float(value)</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">Foot</span><span class="params">(object)</span>:</span></span><br><span class="line"> <span class="string">"""Descriptor for a foot."""</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, instance, owner)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> instance.meter * <span class="number">3.2808</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__set__</span><span class="params">(self, instance, value)</span>:</span></span><br><span class="line"> instance.meter = float(value) / <span class="number">3.2808</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">Distance</span><span class="params">(object)</span>:</span></span><br><span class="line"> meter = Meter()</span><br><span class="line"> foot = Foot()</span><br><span class="line"></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 class="comment"># fileobj = FileObject()</span></span><br><span class="line"> <span class="comment">#</span></span><br><span class="line"> <span class="comment"># foo = Word('foo')</span></span><br><span class="line"> <span class="comment"># bara = Word('bara')</span></span><br><span class="line"> <span class="comment"># print(foo > bara) # False</span></span><br><span class="line"> <span class="comment"># print(foo < bara) # True</span></span><br><span class="line"> <span class="comment"># print(str('foo') == str('bar')) # False 利用了 string 内置魔法函数 __eq__</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># print(str(foo)) # Word: foo</span></span><br><span class="line"> <span class="comment"># print(repr(foo)) # <Word:foo></span></span><br><span class="line"> <span class="comment"># # print(unicode(foo)) # only in python2</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># inst = MyCounter('Test')</span></span><br><span class="line"> <span class="comment"># inst.value = 'test'</span></span><br><span class="line"> <span class="comment"># print(inst.counter) # 1</span></span><br><span class="line"> <span class="comment"># print(inst.value) # test</span></span><br><span class="line"> <span class="comment"># inst.value = 'test1'</span></span><br><span class="line"> <span class="comment"># print(inst.counter) # 2</span></span><br><span class="line"> <span class="comment"># print(inst.value) # test1 属性存在,只有__getattribute__调用</span></span><br><span class="line"> <span class="comment"># try:</span></span><br><span class="line"> <span class="comment"># inst.value2 # 属性不存在, 先调用__getattribute__, 后调用__getattr__</span></span><br><span class="line"> <span class="comment"># except AttributeError:</span></span><br><span class="line"> <span class="comment"># pass</span></span><br><span class="line"> <span class="comment"># del inst.value # call del attr</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># mylist = MyList([1, 2, 3, 4, 5, 6])</span></span><br><span class="line"> <span class="comment"># print(mylist.head())</span></span><br><span class="line"> <span class="comment"># print(list(mylist.__reversed__()))</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># entiry = Entity(10, 1, 2)</span></span><br><span class="line"> <span class="comment"># entiry(2, 1)</span></span><br><span class="line"> <span class="comment"># print(entiry.x, entiry.y) # 2 1</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># with Closer(int(5)) as i:</span></span><br><span class="line"> <span class="comment"># i += 1</span></span><br><span class="line"> <span class="comment"># print(i)</span></span><br><span class="line"> <span class="comment"># with Closer(open('sample.txt')) as file:</span></span><br><span class="line"> <span class="comment"># print('file>>>>:', file.read())</span></span><br><span class="line"></span><br><span class="line"> d = Distance()</span><br><span class="line"> print(d.meter, d.foot) <span class="comment"># 0.0, 0.0</span></span><br><span class="line"> d.meter = <span class="number">1</span></span><br><span class="line"> print(d.meter, d.foot) <span class="comment"># 1.0 3.2808</span></span><br><span class="line"> d.meter = <span class="number">2</span></span><br><span class="line"> print(d.meter, d.foot) <span class="comment"># 2.0 6.5616</span></span><br></pre></td></tr></table></figure><h2 id="魔术方法列表"><a href="#魔术方法列表" class="headerlink" title="魔术方法列表"></a>魔术方法列表</h2><table><thead><tr><th>魔术方法</th><th>含义</th></tr></thead><tbody><tr><td></td><td>基本的魔法方法</td></tr><tr><td><code>__new__(cls[, ...])</code></td><td>1. <strong>new</strong> 是在一个对象实例化的时候所调用的第一个方法 2. 它的第一个参数是这个类,其他的参数是用来直接传递给 <strong>init</strong> 方法;3. <strong>new</strong> 决定是否要使用该 <strong>init</strong> 方法,因为 <strong>new</strong> 可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果 <strong>new</strong> 没有返回实例对象,则 <strong>init</strong> 不会被调用;4. <strong>new</strong> 主要是用于继承一个不可变的类型比如一个 tuple 或者 string</td></tr><tr><td><code>__init__(self[, ...])</code></td><td>构造器,当一个实例被创建的时候调用的初始化方法</td></tr><tr><td><code>__del__(self)</code></td><td>析构器,当一个实例被销毁的时候调用的方法</td></tr><tr><td><code>__call__(self[, args...])</code></td><td>允许一个类的实例像函数一样被调用:x(a, b) 调用 x.<strong>call</strong>(a, b)</td></tr><tr><td><code>__len__(self)</code></td><td>定义当被 len() 调用时的行为</td></tr><tr><td><code>__repr__(self)</code></td><td>定义当被 repr() 调用时的行为</td></tr><tr><td><code>__str__(self)</code></td><td>定义当被 str() 调用时的行为</td></tr><tr><td><code>__bytes__(self)</code></td><td>定义当被 bytes() 调用时的行为</td></tr><tr><td><code>__hash__(self)</code></td><td>定义当被 hash() 调用时的行为</td></tr><tr><td><code>__bool__(self)</code></td><td>定义当被 bool() 调用时的行为,应该返回 True 或 False</td></tr><tr><td><code>__format__(self, format_spec)</code></td><td>定义当被 format() 调用时的行为</td></tr><tr><td></td><td>有关属性</td></tr><tr><td><code>__getattr__(self, name)</code></td><td>定义当用户试图获取一个不存在的属性时的行为</td></tr><tr><td><code>__getattribute__(self, name)</code></td><td>定义当该类的属性被访问时的行为</td></tr><tr><td><code>__setattr__(self, name, value)</code></td><td>定义当一个属性被设置时的行为</td></tr><tr><td><code>__delattr__(self, name)</code></td><td>定义当一个属性被删除时的行为</td></tr><tr><td><code>__dir__(self)</code></td><td>定义当 dir() 被调用时的行为</td></tr><tr><td><code>__get__(self, instance, owner)</code></td><td>定义当描述符的值被取得时的行为</td></tr><tr><td><code>__set__(self, instance, value)</code></td><td>定义当描述符的值被改变时的行为</td></tr><tr><td><code>__delete__(self, instance)</code></td><td>定义当描述符的值被删除时的行为</td></tr><tr><td></td><td>比较操作符</td></tr><tr><td><code>__lt__(self, other)</code></td><td>定义小于号的行为:x < y 调用 x.<strong>lt</strong>(y)</td></tr><tr><td><code>__le__(self, other)</code></td><td>定义小于等于号的行为:x <= y 调用 x.<strong>le</strong>(y)</td></tr><tr><td><code>__eq__(self, other)</code></td><td>定义等于号的行为:x == y 调用 x.<strong>eq</strong>(y)</td></tr><tr><td><code>__ne__(self, other)</code></td><td>定义不等号的行为:x != y 调用 x.<strong>ne</strong>(y)</td></tr><tr><td><code>__gt__(self, other)</code></td><td>定义大于号的行为:x > y 调用 x.<strong>gt</strong>(y)</td></tr><tr><td><code>__ge__(self, other)</code></td><td>定义大于等于号的行为:x >= y 调用 x.<strong>ge</strong>(y)</td></tr><tr><td></td><td>算数运算符</td></tr><tr><td><code>__add__(self, other)</code></td><td>定义加法的行为:+</td></tr><tr><td><code>__sub__(self, other)</code></td><td>定义减法的行为:-</td></tr><tr><td><code>__mul__(self, other)</code></td><td>定义乘法的行为:*</td></tr><tr><td><code>__truediv__(self, other)</code></td><td>定义真除法的行为:/</td></tr><tr><td><code>__floordiv__(self, other)</code></td><td>定义整数除法的行为://</td></tr><tr><td><code>__mod__(self, other)</code></td><td>定义取模算法的行为:%</td></tr><tr><td><code>__divmod__(self, other)</code></td><td>定义当被 divmod() 调用时的行为</td></tr><tr><td><code>__pow__(self, other[, modulo])</code></td><td>定义当被 power() 调用或 ** 运算时的行为</td></tr><tr><td><code>__lshift__(self, other)</code></td><td>定义按位左移位的行为:<<</td></tr><tr><td><code>__rshift__(self, other)</code></td><td>定义按位右移位的行为:>></td></tr><tr><td><code>__and__(self, other)</code></td><td>定义按位与操作的行为:&</td></tr><tr><td><code>__xor__(self, other)</code></td><td>定义按位异或操作的行为:^</td></tr><tr><td><code>__or__(self, other)</code></td><td>定义按位或操作的行为:</td><td></td></tr><tr><td></td><td>反运算</td></tr><tr><td><code>__radd__(self, other)</code></td><td>(与上方相同,当左操作数不支持相应的操作时被调用)</td></tr><tr><td><code>__rsub__(self, other)</code></td><td>(与上方相同,当左操作数不支持相应的操作时被调用)</td></tr><tr><td><code>__rmul__(self, other)</code></td><td>(与上方相同,当左操作数不支持相应的操作时被调用)</td></tr><tr><td><code>__rtruediv__(self, other)</code></td><td>(与上方相同,当左操作数不支持相应的操作时被调用)</td></tr><tr><td><code>__rfloordiv__(self, other)</code></td><td>(与上方相同,当左操作数不支持相应的操作时被调用)</td></tr><tr><td><code>__rmod__(self, other)</code></td><td>(与上方相同,当左操作数不支持相应的操作时被调用)</td></tr><tr><td><code>__rdivmod__(self, other)</code></td><td>(与上方相同,当左操作数不支持相应的操作时被调用)</td></tr><tr><td><code>__rpow__(self, other)</code></td><td>(与上方相同,当左操作数不支持相应的操作时被调用)</td></tr><tr><td><code>__rlshift__(self, other)</code></td><td>(与上方相同,当左操作数不支持相应的操作时被调用)</td></tr><tr><td><code>__rrshift__(self, other)</code></td><td>(与上方相同,当左操作数不支持相应的操作时被调用)</td></tr><tr><td><code>__rxor__(self, other)</code></td><td>(与上方相同,当左操作数不支持相应的操作时被调用)</td></tr><tr><td><code>__ror__(self, other)</code></td><td>(与上方相同,当左操作数不支持相应的操作时被调用)</td></tr><tr><td></td><td>增量赋值运算</td></tr><tr><td><code>__iadd__(self, other)</code></td><td>定义赋值加法的行为:+=</td></tr><tr><td><code>__isub__(self, other)</code></td><td>定义赋值减法的行为:-=</td></tr><tr><td><code>__imul__(self, other)</code></td><td>定义赋值乘法的行为:*=</td></tr><tr><td><code>__itruediv__(self, other)</code></td><td>定义赋值真除法的行为:/=</td></tr><tr><td><code>__ifloordiv__(self, other)</code></td><td>定义赋值整数除法的行为://=</td></tr><tr><td><code>__imod__(self, other)</code></td><td>定义赋值取模算法的行为:%=</td></tr><tr><td><code>__ipow__(self, other[, modulo])</code></td><td>定义赋值幂运算的行为:**=</td></tr><tr><td><code>__ilshift__(self, other)</code></td><td>定义赋值按位左移位的行为:<<=</td></tr><tr><td><code>__irshift__(self, other)</code></td><td>定义赋值按位右移位的行为:>>=</td></tr><tr><td><code>__iand__(self, other)</code></td><td>定义赋值按位与操作的行为:&=</td></tr><tr><td><code>__ixor__(self, other)</code></td><td>定义赋值按位异或操作的行为:^=</td></tr><tr><td><code>__ior__(self, other)</code></td><td>定义赋值按位或操作的行为:</td><td>=</td></tr><tr><td></td><td>一元操作符</td></tr><tr><td><code>__neg__(self)</code></td><td>定义正号的行为:+x</td></tr><tr><td><code>__pos__(self)</code></td><td>定义负号的行为:-x</td></tr><tr><td><code>__abs__(self)</code></td><td>定义当被 abs() 调用时的行为</td></tr><tr><td><code>__invert__(self)</code></td><td>定义按位求反的行为:~x</td></tr><tr><td></td><td>类型转换</td></tr><tr><td><code>__complex__(self)</code></td><td>定义当被 complex() 调用时的行为(需要返回恰当的值)</td></tr><tr><td><code>__int__(self)</code></td><td>定义当被 int() 调用时的行为(需要返回恰当的值)</td></tr><tr><td><code>__float__(self)</code></td><td>定义当被 float() 调用时的行为(需要返回恰当的值)</td></tr><tr><td><code>__round__(self[, n])</code></td><td>定义当被 round() 调用时的行为(需要返回恰当的值)</td></tr><tr><td><code>__index__(self)</code></td><td>1. 当对象是被应用在切片表达式中时,实现整形强制转换;2. 如果你定义了一个可能在切片时用到的定制的数值型,你应该定义 <strong>index</strong>;3. 如果 <strong>index</strong> 被定义,则 <strong>int</strong> 也需要被定义,且返回相同的值</td></tr><tr><td></td><td>上下文管理(with 语句)</td></tr><tr><td><code>__enter__(self)</code></td><td>1. 定义当使用 with 语句时的初始化行为;2. <strong>enter</strong> 的返回值被 with 语句的目标或者 as 后的名字绑定</td></tr><tr><td><code>__exit__(self, exc_type, exc_value, traceback)</code></td><td>1. 定义当一个代码块被执行或者终止后上下文管理器应该做什么;2. 一般被用来处理异常,清除工作或者做一些代码块执行完毕之后的日常工作</td></tr><tr><td></td><td>容器类型</td></tr><tr><td><code>__len__(self)</code></td><td>定义当被 len() 调用时的行为(返回容器中元素的个数)</td></tr><tr><td><code>__getitem__(self, key)</code></td><td>定义获取容器中指定元素的行为,相当于 self[key]</td></tr><tr><td><code>__setitem__(self, key, value)</code></td><td>定义设置容器中指定元素的行为,相当于 self[key] = value</td></tr><tr><td><code>__delitem__(self, key)</code></td><td>定义删除容器中指定元素的行为,相当于 del self[key]</td></tr><tr><td><code>__iter__(self)</code></td><td>定义当迭代容器中的元素的行为</td></tr><tr><td><code>__reversed__(self)</code></td><td>定义当被 reversed() 调用时的行为</td></tr><tr><td><code>__contains__(self, item)</code></td><td>定义当使用成员测试运算符(in 或 not in)时的行为</td></tr></tbody></table><h2 id="延伸阅读"><a href="#延伸阅读" class="headerlink" title="延伸阅读"></a>延伸阅读</h2><ul><li><a href="http://www.diveintopython3.net/special-method-names.html" target="_blank" rel="noopener">Dive into Python3:Special Method Names</a></li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="http://pycoders-weekly-chinese.readthedocs.io/en/latest/issue6/a-guide-to-pythons-magic-methods.html#id22" target="_blank" rel="noopener">Python 魔术方法指南</a></li></ul>]]></content>
<summary type="html">
<p>本文参考 pycoder’s weekly 的文章,做了下python 魔术方法的使用总结,记录备查。</p>
<h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>魔术方法,英文 Mag
</summary>
<category term="python" scheme="http://pylixm.cc/categories/python/"/>
<category term="python" scheme="http://pylixm.cc/tags/python/"/>
<category term="Magic Method" scheme="http://pylixm.cc/tags/Magic-Method/"/>
</entry>
<entry>
<title>【 Go语言学习笔记 】 - Golang 语法入门</title>
<link href="http://pylixm.cc/posts/2018-01-30-Go-start.html"/>
<id>http://pylixm.cc/posts/2018-01-30-Go-start.html</id>
<published>2018-01-29T16:00:00.000Z</published>
<updated>2018-05-14T08:24:08.282Z</updated>
<content type="html"><![CDATA[<p>本文出处<a href="https://tour.go-zh.org" target="_blank" rel="noopener">Go编程语言</a>,由<a href="https://www.jianshu.com/u/9029357b2874" target="_blank" rel="noopener">Tuberose</a>整理。原文地址:<a href="https://www.jianshu.com/p/bed39de53087" target="_blank" rel="noopener">https://www.jianshu.com/p/bed39de53087</a>。转载到此,方便查阅学习。</p><p>欢迎来到 Go 编程语言指南。本指南涵盖了该语言的大部分重要特性<br>Go 语言的交互式简介,它分为三节。第一节覆盖了基本语法及数据结构,第二节讨论了方法与接口, 第三节则简单介绍了 Go 的并发原语。每节末尾都有几个练习,你可以对自己的所学进行实践。 你可以 <a href="https://tour.go-zh.org" target="_blank" rel="noopener">在线学习</a> 或 安装到本地。</p><h2 id="Go基础语法,方便查阅"><a href="#Go基础语法,方便查阅" class="headerlink" title="Go基础语法,方便查阅"></a>Go基础语法,方便查阅</h2><h3 id="包、变量和函数"><a href="#包、变量和函数" class="headerlink" title="包、变量和函数"></a>包、变量和函数</h3><h4 id="1-包"><a href="#1-包" class="headerlink" title="1.包"></a>1.包</h4><ul><li>每个 Go 程序都是由包组成的。</li><li>程序运行的入口是包 main。</li><li>这个程序使用并导入了包 “fmt” 和 “math/rand” 。</li><li>按照惯例,包名与导入路径的最后一个目录一致。例如,”math/rand” 包由 package rand 语句开始。</li></ul><p><strong>注意:这个程序的运行环境是确定性的,因此 rand.Intn每次都会返回相同的数字。 (为了得到不同的随机数,需要提供一个随机数种子,参阅 rand.Seed。)</strong></p><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"math/rand"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(<span class="string">"My favorite number is"</span>, rand.Intn(<span class="number">10</span>))</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>结果<br><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">My favorite number is 1</span><br></pre></td></tr></table></figure></p><h4 id="2-导入"><a href="#2-导入" class="headerlink" title="2.导入"></a>2.导入</h4><ul><li>这个代码用圆括号组合了导入,这是“打包”导入语句。</li><li><p>同样可以编写多个导入语句,例如:</p><figure class="highlight go"><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> <span class="string">"fmt"</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">"math"</span></span><br></pre></td></tr></table></figure></li><li><p>不过使用打包的导入语句是更好的形式。</p><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"math"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Printf(<span class="string">"Now you have %g problems."</span>, math.Sqrt(<span class="number">7</span>))</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">Now you have 2.6457513110645907 problems.</span><br></pre></td></tr></table></figure></p><h4 id="3-导出名"><a href="#3-导出名" class="headerlink" title="3.导出名"></a>3.导出名</h4><ul><li>在 Go 中,首字母大写的名称是被导出的。</li><li>在导入包之后,你只能访问包所导出的名字,任何未导出的名字是不能被包外的代码访问的。</li><li>Foo 和 FOO 都是被导出的名称。名称 foo是不会被导出的。执行代码,注意编译器报的错误。然后将 math.pi改名为 math.Pi再试着执行一下。<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"math"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(math.pi)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><figure class="highlight plain"><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">tmp/sandbox583763709/main.go:9: cannot refer to unexported name math.pi</span><br><span class="line">tmp/sandbox583763709/main.go:9: undefined: math.pi</span><br></pre></td></tr></table></figure></p><h4 id="4-函数"><a href="#4-函数" class="headerlink" title="4.函数"></a>4.函数</h4><ul><li>函数可以没有参数或接受多个参数。</li><li>在这个例子中, add接受两个 int类型的参数。</li><li>注意类型在变量名 之后 。<br>(参考 <a href="http://blog.go-zh.org/gos-declaration-syntax" target="_blank" rel="noopener">这篇关于 Go 语法定义</a> 的文章了解类型以这种形式出现的原因。)<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">add</span><span class="params">(x <span class="keyword">int</span>, y <span class="keyword">int</span>)</span> <span class="title">int</span></span> {</span><br><span class="line"> <span class="keyword">return</span> x + y</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(add(<span class="number">42</span>, <span class="number">13</span>))</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">55</span><br></pre></td></tr></table></figure></p><h4 id="5-函数(续)"><a href="#5-函数(续)" class="headerlink" title="5.函数(续)"></a>5.函数(续)</h4><ul><li>当两个或多个连续的函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略。</li><li>在这个例子中 ,<code>x int, y int</code> 被缩写为 <code>x, y int</code></li></ul><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">add</span><span class="params">(x, y <span class="keyword">int</span>)</span> <span class="title">int</span></span> {</span><br><span class="line"> <span class="keyword">return</span> x + y</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(add(<span class="number">42</span>, <span class="number">13</span>))</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>结果<br><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">55</span><br></pre></td></tr></table></figure></p><h4 id="6-多值返回"><a href="#6-多值返回" class="headerlink" title="6.多值返回"></a>6.多值返回</h4><ul><li>函数可以返回任意数量的返回值。</li><li>swap函数返回了两个字符串。<figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">swap</span><span class="params">(x, y <span class="keyword">string</span>)</span> <span class="params">(<span class="keyword">string</span>, <span class="keyword">string</span>)</span></span> {</span><br><span class="line"> <span class="keyword">return</span> y, x</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> a, b := swap(<span class="string">"hello"</span>, <span class="string">"world"</span>)</span><br><span class="line"> fmt.Println(a, b)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">world hello</span><br></pre></td></tr></table></figure></p><h4 id="7-命名返回值"><a href="#7-命名返回值" class="headerlink" title="7.命名返回值"></a>7.命名返回值</h4><ul><li>Go 的返回值可以被命名,并且就像在函数体开头声明的变量那样使用。</li><li>返回值的名称应当具有一定的意义,可以作为文档使用。</li><li>没有参数的 return语句返回各个返回变量的当前值。这种用法被称作“裸”返回。</li><li>直接返回语句仅应当用在像下面这样的短函数中。在长的函数中它们会影响代码的可读性。<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">split</span><span class="params">(sum <span class="keyword">int</span>)</span> <span class="params">(x, y <span class="keyword">int</span>)</span></span> {</span><br><span class="line"> x = sum * <span class="number">4</span> / <span class="number">9</span></span><br><span class="line"> y = sum - x</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(split(<span class="number">17</span>))</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">7 10</span><br></pre></td></tr></table></figure></p><h4 id="8-变量"><a href="#8-变量" class="headerlink" title="8.变量"></a>8.变量</h4><ul><li>var 语句定义了一个变量的列表;跟函数的参数列表一样,类型在后面。</li><li>就像在这个例子中看到的一样, var 语句可以定义在包或函数级别。<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> c, python, java <span class="keyword">bool</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> i <span class="keyword">int</span></span><br><span class="line"> fmt.Println(i, c, python, java)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">0 false false false</span><br></pre></td></tr></table></figure></p><h4 id="9-初始化变量"><a href="#9-初始化变量" class="headerlink" title="9.初始化变量"></a>9.初始化变量</h4><ul><li>变量定义可以包含初始值,每个变量对应一个。</li><li>如果初始化是使用表达式,则可以省略类型;变量从初始值中获得类型。<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> i, j <span class="keyword">int</span> = <span class="number">1</span>, <span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> c, python, java = <span class="literal">true</span>, <span class="literal">false</span>, <span class="string">"no!"</span></span><br><span class="line"> fmt.Println(i, j, c, python, java)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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 2 true false no!</span><br></pre></td></tr></table></figure></p><h4 id="10-短声明变量"><a href="#10-短声明变量" class="headerlink" title="10.短声明变量"></a>10.短声明变量</h4><ul><li>在函数中, <code>:=</code> 简洁赋值语句在明确类型的地方,可以用于替代 <code>var</code> 定义。</li><li>函数外的每个语句都必须以关键字开始( <code>var</code>、 <code>func</code>、等等), <code>:=</code> 结构不能使用在函数外<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> i, j <span class="keyword">int</span> = <span class="number">1</span>, <span class="number">2</span></span><br><span class="line"> k := <span class="number">3</span></span><br><span class="line"> c, python, java := <span class="literal">true</span>, <span class="literal">false</span>, <span class="string">"no!"</span></span><br><span class="line"></span><br><span class="line"> fmt.Println(i, j, k, c, python, java)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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 2 3 true false no!</span><br></pre></td></tr></table></figure></p><h4 id="11-基本类型"><a href="#11-基本类型" class="headerlink" title="11.基本类型"></a>11.基本类型</h4><ul><li>Go 的基本类型有Basic types<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></pre></td><td class="code"><pre><span class="line">bool</span><br><span class="line">string</span><br><span class="line">int int8 int16 int32 int64</span><br><span class="line">uint uint8 uint16 uint32 uint64 uintptr</span><br><span class="line">byte // uint8 的别名</span><br><span class="line">rune // int32 的别名</span><br><span class="line">// 代表一个Unicode码</span><br><span class="line">float32 float64</span><br><span class="line">complex64 complex128</span><br></pre></td></tr></table></figure></li></ul><p>这个例子演示了具有不同类型的变量。 同时与导入语句一样,变量的定义“打包”在一个语法块中。<br>int,uint 和 uintptr类型在32位的系统上一般是32位,而在64位系统上是64位。当你需要使用一个整数类型时,你应该首选 int,仅当有特别的理由才使用定长整数类型或者无符号整数类型。<br><figure class="highlight go"><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"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"math/cmplx"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line"> ToBe <span class="keyword">bool</span> = <span class="literal">false</span></span><br><span class="line"> MaxInt <span class="keyword">uint64</span> = <span class="number">1</span><<<span class="number">64</span> - <span class="number">1</span></span><br><span class="line"> z <span class="keyword">complex128</span> = cmplx.Sqrt(<span class="number">-5</span> + <span class="number">12i</span>)</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">const</span> f = <span class="string">"%T(%v)\n"</span></span><br><span class="line"> fmt.Printf(f, ToBe, ToBe)</span><br><span class="line"> fmt.Printf(f, MaxInt, MaxInt)</span><br><span class="line"> fmt.Printf(f, z, z)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><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">bool(false)</span><br><span class="line">uint64(18446744073709551615)</span><br><span class="line">complex128((2+3i))</span><br></pre></td></tr></table></figure></p><h4 id="12-类型转换"><a href="#12-类型转换" class="headerlink" title="12.类型转换"></a>12.类型转换</h4><ul><li>表达式 T(v)将值 v 转换为类型 T 。</li><li>一些关于数值的转换:<figure class="highlight go"><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">var</span> i <span class="keyword">int</span> = <span class="number">42</span></span><br><span class="line"><span class="keyword">var</span> f <span class="keyword">float64</span> = <span class="keyword">float64</span>(i)</span><br><span class="line"><span class="keyword">var</span> u <span class="keyword">uint</span> = <span class="keyword">uint</span>(f)</span><br></pre></td></tr></table></figure></li></ul><p>或者,更加简单的形式:<br><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">i := 42</span><br><span class="line">f := float64(i)</span><br><span class="line">u := uint(f)</span><br></pre></td></tr></table></figure></p><p>与 C 不同的是 Go 的在不同类型之间的项目赋值时需要显式转换。 试着移除例子中 float64 或 int 的转换看看会发生什么。<br><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"math"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> x, y <span class="keyword">int</span> = <span class="number">3</span>, <span class="number">4</span></span><br><span class="line"> <span class="keyword">var</span> f <span class="keyword">float64</span> = math.Sqrt(<span class="keyword">float64</span>(x*x + y*y))</span><br><span class="line"> <span class="keyword">var</span> z <span class="keyword">uint</span> = <span class="keyword">uint</span>(f)</span><br><span class="line"> fmt.Println(x, y, z)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><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">3 4 5</span><br></pre></td></tr></table></figure></p><h4 id="13-零值"><a href="#13-零值" class="headerlink" title="13.零值"></a>13.零值</h4><ul><li>变量在定义时没有明确的初始化时会赋值为 <strong>零值</strong> 。</li><li>零值是:<ul><li>数值类型为 0,</li><li>布尔类型为 false ,</li><li>字符串为 “”(空字符串)。</li></ul></li></ul><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> i <span class="keyword">int</span></span><br><span class="line"> <span class="keyword">var</span> f <span class="keyword">float64</span></span><br><span class="line"> <span class="keyword">var</span> b <span class="keyword">bool</span></span><br><span class="line"> <span class="keyword">var</span> s <span class="keyword">string</span></span><br><span class="line"> fmt.Printf(<span class="string">"%v %v %v %q\n"</span>, i, f, b, s)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>结果<br><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">0 0 false ""</span><br></pre></td></tr></table></figure></p><h4 id="14-类型推导"><a href="#14-类型推导" class="headerlink" title="14.类型推导"></a>14.类型推导</h4><ul><li>在定义一个变量却并不显式指定其类型时(使用 :=语法或者 var =表达式语法), 变量的类型由(等号)右侧的值推导得出。</li><li>当右值定义了类型时,新变量的类型与其相同:<figure class="highlight plain"><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">var i int</span><br><span class="line">j := i // j 也是一个 int</span><br></pre></td></tr></table></figure></li></ul><p>但是当右边包含了未指名类型的数字常量时,新的变量就可能是 int、 float64或 complex128。 这取决于常量的精度:<br><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">i := 42 // int</span><br><span class="line">f := 3.142 // float64</span><br><span class="line">g := 0.867 + 0.5i // complex128</span><br></pre></td></tr></table></figure></p><p>尝试修改演示代码中 v的初始值,并观察这是如何影响其类型的。<br><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> v := <span class="number">42</span> <span class="comment">// change me!</span></span><br><span class="line"> fmt.Printf(<span class="string">"v is of type %T\n"</span>, v)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><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">v is of type int</span><br></pre></td></tr></table></figure></p><h4 id="15-常量"><a href="#15-常量" class="headerlink" title="15.常量"></a>15.常量</h4><ul><li>常量的定义与变量类似,只不过使用 <code>const</code> 关键字。</li><li>常量可以是字符、字符串、布尔或数字类型的值。</li><li>常量不能使用 <code>:=</code> 语法定义。<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Pi = <span class="number">3.14</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">const</span> World = <span class="string">"世界"</span></span><br><span class="line"> fmt.Println(<span class="string">"Hello"</span>, World)</span><br><span class="line"> fmt.Println(<span class="string">"Happy"</span>, Pi, <span class="string">"Day"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> Truth = <span class="literal">true</span></span><br><span class="line"> fmt.Println(<span class="string">"Go rules?"</span>, Truth)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">Hello 世界</span><br><span class="line">Happy 3.14 Day</span><br><span class="line">Go rules? true</span><br></pre></td></tr></table></figure></p><h4 id="16-数值常量"><a href="#16-数值常量" class="headerlink" title="16.数值常量"></a>16.数值常量</h4><ul><li>数值常量是高精度的 值 。</li><li>一个未指定类型的常量由上下文来决定其类型。</li><li>也尝试一下输出needInt(Big)吧。<br>(int可以存放最大64位的整数,根据平台不同有时会更少。)<figure class="highlight go"><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"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> (</span><br><span class="line"> Big = <span class="number">1</span> << <span class="number">100</span></span><br><span class="line"> Small = Big >> <span class="number">99</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">needInt</span><span class="params">(x <span class="keyword">int</span>)</span> <span class="title">int</span></span> { <span class="keyword">return</span> x*<span class="number">10</span> + <span class="number">1</span> }</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">needFloat</span><span class="params">(x <span class="keyword">float64</span>)</span> <span class="title">float64</span></span> {</span><br><span class="line"> <span class="keyword">return</span> x * <span class="number">0.1</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(needInt(Small))</span><br><span class="line"> fmt.Println(needFloat(Small))</span><br><span class="line"> fmt.Println(needFloat(Big))</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">21</span><br><span class="line">0.2</span><br><span class="line">1.2676506002282295e+29</span><br></pre></td></tr></table></figure></p><h3 id="流程控制语句:for、if、else-、switch-和-defer"><a href="#流程控制语句:for、if、else-、switch-和-defer" class="headerlink" title="流程控制语句:for、if、else 、switch 和 defer"></a>流程控制语句:for、if、else 、switch 和 defer</h3><h4 id="1-for"><a href="#1-for" class="headerlink" title="1.for"></a>1.for</h4><ul><li>Go 只有一种循环结构—— for循环。</li><li>基本的 for循环包含三个由分号分开的组成部分:<ul><li>初始化语句:在第一次循环执行前被执行</li><li>循环条件表达式:每轮迭代开始前被求值</li><li>后置语句:每轮迭代后被执行</li></ul></li><li>初始化语句一般是一个短变量声明,这里声明的变量仅在整个 for循环语句可见。</li><li>如果条件表达式的值变为 false,那么迭代将终止。</li></ul><p>注意:不像 C,Java,或者 Javascript 等其他语言,for语句的三个组成部分 并不需要用括号括起来,但循环体必须用 { }括起来。<br><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> sum := <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">10</span>; i++ {</span><br><span class="line"> sum += i</span><br><span class="line"> }</span><br><span class="line"> fmt.Println(sum)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><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">45</span><br></pre></td></tr></table></figure></p><h4 id="2-for(续)"><a href="#2-for(续)" class="headerlink" title="2.for(续)"></a>2.for(续)</h4><ul><li>循环初始化语句和后置语句都是可选的。<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> sum := <span class="number">1</span></span><br><span class="line"> <span class="keyword">for</span> ; sum < <span class="number">1000</span>; {</span><br><span class="line"> sum += sum</span><br><span class="line"> }</span><br><span class="line"> fmt.Println(sum)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">1024</span><br></pre></td></tr></table></figure></p><h4 id="3-for-是-Go-的-“while”"><a href="#3-for-是-Go-的-“while”" class="headerlink" title="3.for 是 Go 的 “while”"></a>3.for 是 Go 的 “while”</h4><ul><li>基于此可以省略分号:C 的 while在 Go 中叫做 for。<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> sum := <span class="number">1</span></span><br><span class="line"> <span class="keyword">for</span> sum < <span class="number">1000</span> {</span><br><span class="line"> sum += sum</span><br><span class="line"> }</span><br><span class="line"> fmt.Println(sum)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">1024</span><br></pre></td></tr></table></figure></p><h4 id="4-无限循环"><a href="#4-无限循环" class="headerlink" title="4.无限循环"></a>4.无限循环</h4><p>如果省略循环条件,该循环就不会结束,因此无限循环可以写得很紧凑。<br><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">for</span> {</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><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">process took too long</span><br></pre></td></tr></table></figure></p><h4 id="5-if"><a href="#5-if" class="headerlink" title="5.if"></a>5.if</h4><p>就像 for循环一样,Go 的 if语句也不要求用 ( )将条件括起来,同时, { }还是必须有的<br><figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"math"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">sqrt</span><span class="params">(x <span class="keyword">float64</span>)</span> <span class="title">string</span></span> {</span><br><span class="line"> <span class="keyword">if</span> x < <span class="number">0</span> {</span><br><span class="line"> <span class="keyword">return</span> sqrt(-x) + <span class="string">"i"</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> fmt.Sprint(math.Sqrt(x))</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(sqrt(<span class="number">2</span>), sqrt(<span class="number">-4</span>))</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><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.4142135623730951 2i</span><br></pre></td></tr></table></figure></p><h4 id="6-if-的便捷语句"><a href="#6-if-的便捷语句" class="headerlink" title="6.if 的便捷语句"></a>6.if 的便捷语句</h4><ul><li>跟 for一样, if语句可以在条件之前执行一个简单语句。</li><li>由这个语句定义的变量的作用域仅在 if范围之内。<br>(在最后的 return语句处使用 v看看。)<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"math"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">pow</span><span class="params">(x, n, lim <span class="keyword">float64</span>)</span> <span class="title">float64</span></span> {</span><br><span class="line"> <span class="keyword">if</span> v := math.Pow(x, n); v < lim {</span><br><span class="line"> <span class="keyword">return</span> v</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> lim</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(</span><br><span class="line"> pow(<span class="number">3</span>, <span class="number">2</span>, <span class="number">10</span>),</span><br><span class="line"> pow(<span class="number">3</span>, <span class="number">3</span>, <span class="number">20</span>),</span><br><span class="line"> )</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">9 20</span><br></pre></td></tr></table></figure></p><h4 id="7-if-和-else"><a href="#7-if-和-else" class="headerlink" title="7.if 和 else"></a>7.if 和 else</h4><ul><li>在 if的便捷语句定义的变量同样可以在任何对应的 else块中使用。<br>(提示:两个 pow调用都在 main调用 fmt.Println前执行完毕了。)<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"math"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">pow</span><span class="params">(x, n, lim <span class="keyword">float64</span>)</span> <span class="title">float64</span></span> {</span><br><span class="line"> <span class="keyword">if</span> v := math.Pow(x, n); v < lim {</span><br><span class="line"> <span class="keyword">return</span> v</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> fmt.Printf(<span class="string">"%g >= %g\n"</span>, v, lim)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 这里开始就不能使用 v 了</span></span><br><span class="line"> <span class="keyword">return</span> lim</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(</span><br><span class="line"> pow(<span class="number">3</span>, <span class="number">2</span>, <span class="number">10</span>),</span><br><span class="line"> pow(<span class="number">3</span>, <span class="number">3</span>, <span class="number">20</span>),</span><br><span class="line"> )</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><figure class="highlight plain"><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">27 >= 20</span><br><span class="line">9 20</span><br></pre></td></tr></table></figure></p><h4 id="8-switch"><a href="#8-switch" class="headerlink" title="8.switch"></a>8.switch</h4><ul><li>你可能已经知道 switch语句会长什么样了。</li><li>除非以 fallthrough 语句结束,否则分支会自动终止</li></ul><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"runtime"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Print(<span class="string">"Go runs on "</span>)</span><br><span class="line"> <span class="keyword">switch</span> os := runtime.GOOS; os {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"darwin"</span>:</span><br><span class="line"> fmt.Println(<span class="string">"OS X."</span>)</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"linux"</span>:</span><br><span class="line"> fmt.Println(<span class="string">"Linux."</span>)</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="comment">// freebsd, openbsd,</span></span><br><span class="line"> <span class="comment">// plan9, windows...</span></span><br><span class="line"> fmt.Printf(<span class="string">"%s."</span>, os)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>结果<br><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">Go runs on nacl.</span><br></pre></td></tr></table></figure></p><h4 id="9-switch-的执行顺序"><a href="#9-switch-的执行顺序" class="headerlink" title="9.switch 的执行顺序"></a>9.switch 的执行顺序</h4><ul><li>switch 的条件从上到下的执行,当匹配成功的时候停止。<br>(例如,<br>switch i {case 0:case f():}<br>当 i==0<br>时不会调用 f<br>。)</li></ul><p>注意:Go playground 中的时间总是从 2009-11-10 23:00:00 UTC 开始, 如何校验这个值作为一个练习留给读者完成。<br><figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(<span class="string">"When's Saturday?"</span>)</span><br><span class="line"> today := time.Now().Weekday()</span><br><span class="line"> <span class="keyword">switch</span> time.Saturday {</span><br><span class="line"> <span class="keyword">case</span> today + <span class="number">0</span>:</span><br><span class="line"> fmt.Println(<span class="string">"Today."</span>)</span><br><span class="line"> <span class="keyword">case</span> today + <span class="number">1</span>:</span><br><span class="line"> fmt.Println(<span class="string">"Tomorrow."</span>)</span><br><span class="line"> <span class="keyword">case</span> today + <span class="number">2</span>:</span><br><span class="line"> fmt.Println(<span class="string">"In two days."</span>)</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> fmt.Println(<span class="string">"Too far away."</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><figure class="highlight plain"><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">When's Saturday?</span><br><span class="line">Too far away.</span><br></pre></td></tr></table></figure></p><h4 id="9-没有条件的-switch"><a href="#9-没有条件的-switch" class="headerlink" title="9.没有条件的 switch"></a>9.没有条件的 switch</h4><ul><li>没有条件的 switch 同 switch true一样。</li><li>这一构造使得可以用更清晰的形式来编写长的 if-then-else 链。<figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> t := time.Now()</span><br><span class="line"> <span class="keyword">switch</span> {</span><br><span class="line"> <span class="keyword">case</span> t.Hour() < <span class="number">12</span>:</span><br><span class="line"> fmt.Println(<span class="string">"Good morning!"</span>)</span><br><span class="line"> <span class="keyword">case</span> t.Hour() < <span class="number">17</span>:</span><br><span class="line"> fmt.Println(<span class="string">"Good afternoon."</span>)</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> fmt.Println(<span class="string">"Good evening."</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">Good evening.</span><br></pre></td></tr></table></figure></p><h4 id="10-defer"><a href="#10-defer" class="headerlink" title="10.defer"></a>10.defer</h4><ul><li>defer 语句会延迟函数的执行直到上层函数返回。</li><li>延迟调用的参数会立刻生成,但是在上层函数返回前函数都不会被调用。<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">defer</span> fmt.Println(<span class="string">"world"</span>)</span><br><span class="line"></span><br><span class="line"> fmt.Println(<span class="string">"hello"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><figure class="highlight plain"><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">hello</span><br><span class="line">world</span><br></pre></td></tr></table></figure></p><h4 id="11-defer-栈"><a href="#11-defer-栈" class="headerlink" title="11.defer 栈"></a>11.defer 栈</h4><ul><li>延迟的函数调用被压入一个栈中。当函数返回时, 会按照后进先出的顺序调用被延迟的函数调用。</li><li>阅读<a href="https://blog.go-zh.org/defer-panic-and-recover" target="_blank" rel="noopener">博文</a>了解更多关于 defer 语句的信息。<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(<span class="string">"counting"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">10</span>; i++ {</span><br><span class="line"> <span class="keyword">defer</span> fmt.Println(i)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> fmt.Println(<span class="string">"done"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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></pre></td><td class="code"><pre><span class="line">counting</span><br><span class="line">done</span><br><span class="line">9</span><br><span class="line">8</span><br><span class="line">7</span><br><span class="line">6</span><br><span class="line">5</span><br><span class="line">4</span><br><span class="line">3</span><br><span class="line">2</span><br><span class="line">1</span><br><span class="line">0</span><br></pre></td></tr></table></figure></p><h3 id="复杂类型:-struct、slice-和-map。"><a href="#复杂类型:-struct、slice-和-map。" class="headerlink" title="复杂类型: struct、slice 和 map。"></a>复杂类型: struct、slice 和 map。</h3><p>学习如何基于已有类型定义新的类型:本课涵盖了结构体、数组、slice 和 map。</p><h4 id="1-指针"><a href="#1-指针" class="headerlink" title="1.指针"></a>1.指针</h4><ul><li>Go 具有指针。 指针保存了变量的内存地址。</li><li>类型 *T是指向类型 T的值的指针。其零值是 nil。<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">var p *int</span><br></pre></td></tr></table></figure></li></ul><p>&符号会生成一个指向其作用对象的指针。<br><figure class="highlight plain"><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">i := 42</span><br><span class="line">p = &i</span><br></pre></td></tr></table></figure></p><p>*符号表示指针指向的底层的值。<br><figure class="highlight plain"><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">fmt.Println(*p) // 通过指针 p 读取 i</span><br><span class="line">*p = 21 // 通过指针 p 设置 i</span><br></pre></td></tr></table></figure></p><p>这也就是通常所说的“间接引用”或“非直接引用”。<br><strong>与 C 不同,Go 没有指针运算。</strong><br><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> i, j := <span class="number">42</span>, <span class="number">2701</span></span><br><span class="line"></span><br><span class="line"> p := &i <span class="comment">// point to i</span></span><br><span class="line"> fmt.Println(*p) <span class="comment">// read i through the pointer</span></span><br><span class="line"> *p = <span class="number">21</span> <span class="comment">// set i through the pointer</span></span><br><span class="line"> fmt.Println(i) <span class="comment">// see the new value of i</span></span><br><span class="line"></span><br><span class="line"> p = &j <span class="comment">// point to j</span></span><br><span class="line"> *p = *p / <span class="number">37</span> <span class="comment">// divide j through the pointer</span></span><br><span class="line"> fmt.Println(j) <span class="comment">// see the new value of j</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><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">42</span><br><span class="line">21</span><br><span class="line">73</span><br></pre></td></tr></table></figure></p><h4 id="2-结构体"><a href="#2-结构体" class="headerlink" title="2.结构体"></a>2.结构体</h4><ul><li>一个结构体( struct)就是一个字段的集合。<br>(而 type的含义跟其字面意思相符。<figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> {</span><br><span class="line"> X <span class="keyword">int</span></span><br><span class="line"> Y <span class="keyword">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(Vertex{<span class="number">1</span>, <span class="number">2</span>})</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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 2}</span><br></pre></td></tr></table></figure></p><h4 id="3-结构体字段"><a href="#3-结构体字段" class="headerlink" title="3.结构体字段"></a>3.结构体字段</h4><ul><li>结构体字段使用点号来访问。<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> {</span><br><span class="line"> X <span class="keyword">int</span></span><br><span class="line"> Y <span class="keyword">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> v := Vertex{<span class="number">1</span>, <span class="number">2</span>}</span><br><span class="line"> v.X = <span class="number">4</span></span><br><span class="line"> fmt.Println(v.X)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">4</span><br></pre></td></tr></table></figure></p><h4 id="4-结构体指针"><a href="#4-结构体指针" class="headerlink" title="4.结构体指针"></a>4.结构体指针</h4><ul><li>结构体字段可以通过结构体指针来访问。</li><li>通过指针间接的访问是透明的。(透明,即指可以看到结构体指针指向的内容。)<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> {</span><br><span class="line"> X <span class="keyword">int</span></span><br><span class="line"> Y <span class="keyword">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> v := Vertex{<span class="number">1</span>, <span class="number">2</span>}</span><br><span class="line"> p := &v</span><br><span class="line"> p.X = <span class="number">1e9</span></span><br><span class="line"> fmt.Println(v)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">{1000000000 2}</span><br></pre></td></tr></table></figure></p><h4 id="5-结构体文法"><a href="#5-结构体文法" class="headerlink" title="5.结构体文法"></a>5.结构体文法</h4><ul><li>结构体文法表示通过结构体字段的值作为列表来新分配一个结构体。</li><li>使用 <code>Name:</code> 语法可以仅列出部分字段。(字段名的顺序无关。)</li><li>特殊的前缀 <code>&</code> 返回一个指向结构体的指针。<figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> {</span><br><span class="line"> X, Y <span class="keyword">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line"> v1 = Vertex{<span class="number">1</span>, <span class="number">2</span>} <span class="comment">// 类型为 Vertex</span></span><br><span class="line"> v2 = Vertex{X: <span class="number">1</span>} <span class="comment">// Y:0 被省略</span></span><br><span class="line"> v3 = Vertex{} <span class="comment">// X:0 和 Y:0</span></span><br><span class="line"> p = &Vertex{<span class="number">1</span>, <span class="number">2</span>} <span class="comment">// 类型为 *Vertex</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(v1, p, v2, v3)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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 2} &{1 2} {1 0} {0 0}</span><br></pre></td></tr></table></figure></p><p>结构体扩展阅读:<a href="https://www.kancloud.cn/kancloud/web-application-with-golang/44153" target="_blank" rel="noopener">Go web编程:struct</a></p><h4 id="6-数组"><a href="#6-数组" class="headerlink" title="6.数组"></a>6.数组</h4><ul><li>类型 [n]T是一个有 n个类型为 T的值的数组。</li><li>表达式<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">var a [10]int</span><br></pre></td></tr></table></figure></li></ul><p>定义变量 a是一个有十个整数的数组。</p><ul><li>数组的长度是其类型的一部分,因此数组不能改变大小。 这看起来是一个制约,但是请不要担心; Go 提供了更加便利的方式来使用数组。<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> a [<span class="number">2</span>]<span class="keyword">string</span></span><br><span class="line"> a[<span class="number">0</span>] = <span class="string">"Hello"</span></span><br><span class="line"> a[<span class="number">1</span>] = <span class="string">"World"</span></span><br><span class="line"> fmt.Println(a[<span class="number">0</span>], a[<span class="number">1</span>])</span><br><span class="line"> fmt.Println(a)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><figure class="highlight plain"><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">Hello World</span><br><span class="line">[Hello World]</span><br></pre></td></tr></table></figure></p><h4 id="7-slice-切片"><a href="#7-slice-切片" class="headerlink" title="7.slice(切片)"></a>7.slice(切片)</h4><ul><li>一个 slice 会指向一个序列的值,并且包含了长度信息。</li><li>[]T是一个元素类型为 T的 slice。</li><li>len(s)返回 slice s 的长度。</li><li>切片的长度就是它所包含的元素个数。</li><li>切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数,。</li><li>切片 s 的长度和容量可通过表达式 len(s) 和 cap(s) 来获取。<figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> s := []<span class="keyword">int</span>{<span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">11</span>, <span class="number">13</span>}</span><br><span class="line"> fmt.Println(<span class="string">"s =="</span>, s)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="built_in">len</span>(s); i++ {</span><br><span class="line"> fmt.Printf(<span class="string">"s[%d] == %d\n"</span>, i, s[i])</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">s == [2 3 5 7 11 13]</span><br><span class="line">s[0] == 2</span><br><span class="line">s[1] == 3</span><br><span class="line">s[2] == 5</span><br><span class="line">s[3] == 7</span><br><span class="line">s[4] == 11</span><br><span class="line">s[5] == 13</span><br></pre></td></tr></table></figure></p><h4 id="8-slice-的-slice"><a href="#8-slice-的-slice" class="headerlink" title="8.slice 的 slice"></a>8.slice 的 slice</h4><ul><li>slice 可以包含任意的类型,包括另一个 slice。<figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"strings"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="comment">// Create a tic-tac-toe board.</span></span><br><span class="line"> game := [][]<span class="keyword">string</span>{</span><br><span class="line"> []<span class="keyword">string</span>{<span class="string">"_"</span>, <span class="string">"_"</span>, <span class="string">"_"</span>},</span><br><span class="line"> []<span class="keyword">string</span>{<span class="string">"_"</span>, <span class="string">"_"</span>, <span class="string">"_"</span>},</span><br><span class="line"> []<span class="keyword">string</span>{<span class="string">"_"</span>, <span class="string">"_"</span>, <span class="string">"_"</span>},</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// The players take turns.</span></span><br><span class="line"> game[<span class="number">0</span>][<span class="number">0</span>] = <span class="string">"X"</span></span><br><span class="line"> game[<span class="number">2</span>][<span class="number">2</span>] = <span class="string">"O"</span></span><br><span class="line"> game[<span class="number">2</span>][<span class="number">0</span>] = <span class="string">"X"</span></span><br><span class="line"> game[<span class="number">1</span>][<span class="number">0</span>] = <span class="string">"O"</span></span><br><span class="line"> game[<span class="number">0</span>][<span class="number">2</span>] = <span class="string">"X"</span></span><br><span class="line"></span><br><span class="line"> printBoard(game)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">printBoard</span><span class="params">(s [][]<span class="keyword">string</span>)</span></span> {</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="built_in">len</span>(s); i++ {</span><br><span class="line"> fmt.Printf(<span class="string">"%s\n"</span>, strings.Join(s[i], <span class="string">" "</span>))</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">X _ X</span><br><span class="line">O _ _</span><br><span class="line">X _ O</span><br></pre></td></tr></table></figure></p><h4 id="9-对-slice-切片"><a href="#9-对-slice-切片" class="headerlink" title="9.对 slice 切片"></a>9.对 slice 切片</h4><ul><li>slice 可以重新切片,创建一个新的 slice 值指向相同的数组。</li><li>表达式 <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">s[lo:hi]</span><br></pre></td></tr></table></figure></li></ul><p>表示从 lo到 hi-1 的 slice 元素,含前端,不包含后端。因此<br><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">s[lo:lo]</span><br></pre></td></tr></table></figure></p><p>是空的,而<br><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">s[lo:lo+1]</span><br></pre></td></tr></table></figure></p><p>有一个元素。<br><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> s := []<span class="keyword">int</span>{<span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">11</span>, <span class="number">13</span>}</span><br><span class="line"> fmt.Println(<span class="string">"s =="</span>, s)</span><br><span class="line"> fmt.Println(<span class="string">"s[1:4] =="</span>, s[<span class="number">1</span>:<span class="number">4</span>])</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 省略下标代表从 0 开始</span></span><br><span class="line"> fmt.Println(<span class="string">"s[:3] =="</span>, s[:<span class="number">3</span>])</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 省略上标代表到 len(s) 结束</span></span><br><span class="line"> fmt.Println(<span class="string">"s[4:] =="</span>, s[<span class="number">4</span>:])</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><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">s == [2 3 5 7 11 13]</span><br><span class="line">s[1:4] == [3 5 7]</span><br><span class="line">s[:3] == [2 3 5]</span><br><span class="line">s[4:] == [11 13]</span><br></pre></td></tr></table></figure></p><h4 id="10-构造-slice"><a href="#10-构造-slice" class="headerlink" title="10.构造 slice"></a>10.构造 slice</h4><ul><li>slice 由函数make创建。这会分配一个全是零值的数组并且返回一个 slice 指向这个数组<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">a := make([]int, 5) // len(a)=5</span><br></pre></td></tr></table></figure></li></ul><p>为了指定容量,可传递第三个参数到 make:<br><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">b := make([]int, 0, 5) // len(b)=0, cap(b)=5</span><br><span class="line">b = b[:cap(b)] // len(b)=5, cap(b)=5</span><br><span class="line">b = b[1:] // len(b)=4, cap(b)=4</span><br></pre></td></tr></table></figure></p><figure class="highlight go"><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"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> a := <span class="built_in">make</span>([]<span class="keyword">int</span>, <span class="number">5</span>)</span><br><span class="line"> printSlice(<span class="string">"a"</span>, a)</span><br><span class="line"> b := <span class="built_in">make</span>([]<span class="keyword">int</span>, <span class="number">0</span>, <span class="number">5</span>)</span><br><span class="line"> printSlice(<span class="string">"b"</span>, b)</span><br><span class="line"> c := b[:<span class="number">2</span>]</span><br><span class="line"> printSlice(<span class="string">"c"</span>, c)</span><br><span class="line"> d := c[<span class="number">2</span>:<span class="number">5</span>]</span><br><span class="line"> printSlice(<span class="string">"d"</span>, d)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">printSlice</span><span class="params">(s <span class="keyword">string</span>, x []<span class="keyword">int</span>)</span></span> {</span><br><span class="line"> fmt.Printf(<span class="string">"%s len=%d cap=%d %v\n"</span>,</span><br><span class="line"> s, <span class="built_in">len</span>(x), <span class="built_in">cap</span>(x), x)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>结果<br><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">a len=5 cap=5 [0 0 0 0 0]</span><br><span class="line">b len=0 cap=5 []</span><br><span class="line">c len=2 cap=5 [0 0]</span><br><span class="line">d len=3 cap=3 [0 0 0]</span><br></pre></td></tr></table></figure></p><h4 id="11-nil-slice"><a href="#11-nil-slice" class="headerlink" title="11.nil slice"></a>11.nil slice</h4><ul><li>slice 的零值是 nil 。</li><li>一个 nil 的 slice 的长度和容量是 0。<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> z []<span class="keyword">int</span></span><br><span class="line"> fmt.Println(z, <span class="built_in">len</span>(z), <span class="built_in">cap</span>(z))</span><br><span class="line"> <span class="keyword">if</span> z == <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(<span class="string">"nil!"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><figure class="highlight plain"><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">[] 0 0</span><br><span class="line">nil!</span><br></pre></td></tr></table></figure></p><h4 id="12-向-slice-添加元素"><a href="#12-向-slice-添加元素" class="headerlink" title="12.向 slice 添加元素"></a>12.向 slice 添加元素</h4><ul><li><p>向 slice 的末尾添加元素是一种常见的操作,因此 Go 提供了一个内建函数 append。 内建函数的文档对 append有详细介绍。</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">func append(s []T, vs ...T) []T</span><br></pre></td></tr></table></figure></li><li><p>append的第一个参数 s是一个元素类型为 T的 slice ,其余类型为 T的值将会附加到该 slice 的末尾。</p></li><li>append的结果是一个包含原 slice 所有元素加上新添加的元素的 slice。</li><li>如果 s的底层数组太小,而不能容纳所有值时,会分配一个更大的数组。 返回的 slice 会指向这个新分配的数组。<br>(了解更多关于 slice 的内容,参阅文章Go <a href="https://blog.go-zh.org/go-slices-usage-and-internals" target="_blank" rel="noopener">切片:用法和本质</a>。)<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> a []<span class="keyword">int</span></span><br><span class="line"> printSlice(<span class="string">"a"</span>, a)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// append works on nil slices.</span></span><br><span class="line"> a = <span class="built_in">append</span>(a, <span class="number">0</span>)</span><br><span class="line"> printSlice(<span class="string">"a"</span>, a)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// the slice grows as needed.</span></span><br><span class="line"> a = <span class="built_in">append</span>(a, <span class="number">1</span>)</span><br><span class="line"> printSlice(<span class="string">"a"</span>, a)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// we can add more than one element at a time.</span></span><br><span class="line"> a = <span class="built_in">append</span>(a, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>)</span><br><span class="line"> printSlice(<span class="string">"a"</span>, a)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">printSlice</span><span class="params">(s <span class="keyword">string</span>, x []<span class="keyword">int</span>)</span></span> {</span><br><span class="line"> fmt.Printf(<span class="string">"%s len=%d cap=%d %v\n"</span>,</span><br><span class="line"> s, <span class="built_in">len</span>(x), <span class="built_in">cap</span>(x), x)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">a len=0 cap=0 []</span><br><span class="line">a len=1 cap=2 [0]</span><br><span class="line">a len=2 cap=2 [0 1]</span><br><span class="line">a len=5 cap=8 [0 1 2 3 4]</span><br></pre></td></tr></table></figure></p><h4 id="13-range"><a href="#13-range" class="headerlink" title="13.range"></a>13.range</h4><ul><li>for循环的 range格式可以对 slice 或者 map 进行迭代循环。</li><li>当使用 for循环遍历一个 slice 时,每次迭代 range将返回两个值。 第一个是当前下标(序号),第二个是该下标所对应元素的一个拷贝。<figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> pow = []<span class="keyword">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">4</span>, <span class="number">8</span>, <span class="number">16</span>, <span class="number">32</span>, <span class="number">64</span>, <span class="number">128</span>}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">for</span> i, v := <span class="keyword">range</span> pow {</span><br><span class="line"> fmt.Printf(<span class="string">"2**%d = %d\n"</span>, i, v)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>结果<br><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">2**0 = 1</span><br><span class="line">2**1 = 2</span><br><span class="line">2**2 = 4</span><br><span class="line">2**3 = 8</span><br><span class="line">2**4 = 16</span><br><span class="line">2**5 = 32</span><br><span class="line">2**6 = 64</span><br><span class="line">2**7 = 128</span><br></pre></td></tr></table></figure></p><h4 id="14-range(续)"><a href="#14-range(续)" class="headerlink" title="14.range(续)"></a>14.range(续)</h4><p>可以通过赋值给 _来忽略序号和值。<br>如果只需要索引值,去掉 “ , value ” 的部分即可。<br><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> pow := <span class="built_in">make</span>([]<span class="keyword">int</span>, <span class="number">10</span>)</span><br><span class="line"> <span class="keyword">for</span> i := <span class="keyword">range</span> pow {</span><br><span class="line"> pow[i] = <span class="number">1</span> << <span class="keyword">uint</span>(i)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> _, value := <span class="keyword">range</span> pow {</span><br><span class="line"> fmt.Printf(<span class="string">"%d\n"</span>, value)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><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">1</span><br><span class="line">2</span><br><span class="line">4</span><br><span class="line">8</span><br><span class="line">16</span><br><span class="line">32</span><br><span class="line">64</span><br><span class="line">128</span><br><span class="line">256</span><br><span class="line">512</span><br></pre></td></tr></table></figure></p><h4 id="15-map"><a href="#15-map" class="headerlink" title="15.map"></a>15.map</h4><p>map 映射键到值。<br>map 在使用之前必须用 make来创建;值为 nil的 map 是空的,并且不能对其赋值。<br><figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> {</span><br><span class="line"> Lat, Long <span class="keyword">float64</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> m <span class="keyword">map</span>[<span class="keyword">string</span>]Vertex</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> m = <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="keyword">string</span>]Vertex)</span><br><span class="line"> m[<span class="string">"Bell Labs"</span>] = Vertex{</span><br><span class="line"> <span class="number">40.68433</span>, <span class="number">-74.39967</span>,</span><br><span class="line"> }</span><br><span class="line"> fmt.Println(m[<span class="string">"Bell Labs"</span>])</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><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">{40.68433 -74.39967}</span><br></pre></td></tr></table></figure></p><h4 id="16-map-的文法"><a href="#16-map-的文法" class="headerlink" title="16.map 的文法"></a>16.map 的文法</h4><p>map 的文法跟结构体文法相似,不过必须有键名<br><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> {</span><br><span class="line"> Lat, Long <span class="keyword">float64</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> m = <span class="keyword">map</span>[<span class="keyword">string</span>]Vertex{</span><br><span class="line"> <span class="string">"Bell Labs"</span>: Vertex{</span><br><span class="line"> <span class="number">40.68433</span>, <span class="number">-74.39967</span>,</span><br><span class="line"> },</span><br><span class="line"> <span class="string">"Google"</span>: Vertex{</span><br><span class="line"> <span class="number">37.42202</span>, <span class="number">-122.08408</span>,</span><br><span class="line"> },</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(m)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><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">map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]</span><br></pre></td></tr></table></figure></p><h4 id="17-map-的文法(续)"><a href="#17-map-的文法(续)" class="headerlink" title="17.map 的文法(续)"></a>17.map 的文法(续)</h4><p>若顶级类型只是一个类型名,你可以在文法的元素中省略它。<br><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> {</span><br><span class="line"> Lat, Long <span class="keyword">float64</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> m = <span class="keyword">map</span>[<span class="keyword">string</span>]Vertex{</span><br><span class="line"> <span class="string">"Bell Labs"</span>: {<span class="number">40.68433</span>, <span class="number">-74.39967</span>},</span><br><span class="line"> <span class="string">"Google"</span>: {<span class="number">37.42202</span>, <span class="number">-122.08408</span>},</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Println(m)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><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">map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]</span><br></pre></td></tr></table></figure></p><h4 id="18-修改-map"><a href="#18-修改-map" class="headerlink" title="18.修改 map"></a>18.修改 map</h4><p>在 map m中插入或修改一个元素:<br><code>m[key] = elem</code><br>获得元素:<br><code>elem = m[key]</code><br>删除元素:<br><code>delete(m, key)</code><br>通过双赋值检测某个键存在:<br><code>elem, ok = m[key]</code><br>如果 key在 m中, ok为 true。否则, ok为 false,并且 elem是 map 的元素类型的零值。<br>同样的,当从 map 中读取某个不存在的键时,结果是 map 的元素类型的零值。<br><figure class="highlight go"><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"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> m := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="keyword">string</span>]<span class="keyword">int</span>)</span><br><span class="line"></span><br><span class="line"> m[<span class="string">"Answer"</span>] = <span class="number">42</span></span><br><span class="line"> fmt.Println(<span class="string">"The value:"</span>, m[<span class="string">"Answer"</span>])</span><br><span class="line"></span><br><span class="line"> m[<span class="string">"Answer"</span>] = <span class="number">48</span></span><br><span class="line"> fmt.Println(<span class="string">"The value:"</span>, m[<span class="string">"Answer"</span>])</span><br><span class="line"></span><br><span class="line"> <span class="built_in">delete</span>(m, <span class="string">"Answer"</span>)</span><br><span class="line"> fmt.Println(<span class="string">"The value:"</span>, m[<span class="string">"Answer"</span>])</span><br><span class="line"></span><br><span class="line"> v, ok := m[<span class="string">"Answer"</span>]</span><br><span class="line"> fmt.Println(<span class="string">"The value:"</span>, v, <span class="string">"Present?"</span>, ok)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><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">The value: 42</span><br><span class="line">The value: 48</span><br><span class="line">The value: 0</span><br><span class="line">The value: 0 Present? false</span><br></pre></td></tr></table></figure></p><p>19.函数值<br>函数也是值。他们可以像其他值一样传递,比如,函数值可以作为函数的参数或者返回值。<br><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"math"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">compute</span><span class="params">(fn <span class="keyword">func</span>(<span class="keyword">float64</span>, <span class="keyword">float64</span>)</span> <span class="title">float64</span>) <span class="title">float64</span></span> {</span><br><span class="line"> <span class="keyword">return</span> fn(<span class="number">3</span>, <span class="number">4</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> hypot := <span class="function"><span class="keyword">func</span><span class="params">(x, y <span class="keyword">float64</span>)</span> <span class="title">float64</span></span> {</span><br><span class="line"> <span class="keyword">return</span> math.Sqrt(x*x + y*y)</span><br><span class="line"> }</span><br><span class="line"> fmt.Println(hypot(<span class="number">5</span>, <span class="number">12</span>))</span><br><span class="line"></span><br><span class="line"> fmt.Println(compute(hypot))</span><br><span class="line"> fmt.Println(compute(math.Pow))</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><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">13</span><br><span class="line">5</span><br><span class="line">81</span><br></pre></td></tr></table></figure></p><h4 id="20-函数的闭包"><a href="#20-函数的闭包" class="headerlink" title="20.函数的闭包"></a>20.函数的闭包</h4><p>Go 函数可以是一个闭包。闭包是一个函数值,它引用了函数体之外的变量。 这个函数可以对这个引用的变量进行访问和赋值;换句话说这个函数被“绑定”在这个变量上。<br>例如,函数 adder返回一个闭包。每个返回的闭包都被绑定到其各自的sum变量上。<br><figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">adder</span><span class="params">()</span> <span class="title">func</span><span class="params">(<span class="keyword">int</span>)</span> <span class="title">int</span></span> {</span><br><span class="line"> sum := <span class="number">0</span></span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">func</span><span class="params">(x <span class="keyword">int</span>)</span> <span class="title">int</span></span> {</span><br><span class="line"> sum += x</span><br><span class="line"> <span class="keyword">return</span> sum</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> pos, neg := adder(), adder()</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">10</span>; i++ {</span><br><span class="line"> fmt.Println(</span><br><span class="line"> pos(i),</span><br><span class="line"> neg(<span class="number">-2</span>*i),</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果<br><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></pre></td><td class="code"><pre><span class="line">1 -2</span><br><span class="line">3 -6</span><br><span class="line">6 -12</span><br><span class="line">10 -20</span><br><span class="line">15 -30</span><br><span class="line">21 -42</span><br><span class="line">28 -56</span><br><span class="line">36 -72</span><br><span class="line">45 -90</span><br></pre></td></tr></table></figure></p><h3 id="方法和接口"><a href="#方法和接口" class="headerlink" title="方法和接口"></a>方法和接口</h3><p>学习如何为类型定义方法;如何定义接口;可以用它们来定义对象和其行为。</p><h4 id="1-方法"><a href="#1-方法" class="headerlink" title="1.方法"></a>1.方法</h4><p>Go 没有类。然而,仍然可以在结构体类型上定义方法。<br>方法接收者 出现在 func关键字和方法名之间的参数中。<br><figure class="highlight go"><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"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"math"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> {</span><br><span class="line"> X, Y <span class="keyword">float64</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(v *Vertex)</span> <span class="title">Abs</span><span class="params">()</span> <span class="title">float64</span></span> {</span><br><span class="line"> <span class="keyword">return</span> math.Sqrt(v.X*v.X + v.Y*v.Y)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> v := &Vertex{<span class="number">3</span>, <span class="number">4</span>}</span><br><span class="line"> fmt.Println(v.Abs())</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><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">5</span><br></pre></td></tr></table></figure></p><h4 id="2-方法(续)"><a href="#2-方法(续)" class="headerlink" title="2.方法(续)"></a>2.方法(续)</h4><p>你可以对包中的 任意 类型定义任意方法,而不仅仅是针对结构体。<br>但是,不能对来自其他包的类型或基础类型定义方法。<br><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"math"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> MyFloat <span class="keyword">float64</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(f MyFloat)</span> <span class="title">Abs</span><span class="params">()</span> <span class="title">float64</span></span> {</span><br><span class="line"> <span class="keyword">if</span> f < <span class="number">0</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">float64</span>(-f)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">float64</span>(f)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> f := MyFloat(-math.Sqrt2)</span><br><span class="line"> fmt.Println(f.Abs())</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><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.4142135623730951</span><br></pre></td></tr></table></figure></p><h4 id="3-接收者为指针的方法"><a href="#3-接收者为指针的方法" class="headerlink" title="3.接收者为指针的方法"></a>3.接收者为指针的方法</h4><p>方法可以与命名类型或命名类型的指针关联。<br>刚刚看到的两个 Abs方法。一个是在<em>Vertex指针类型上,而另一个在 MyFloat值类型上。 有两个原因需要使用指针接收者。首先避免在每个方法调用中拷贝值(如果值类型是大的结构体的话会更有效率)。其次,方法可以修改接收者指向的值。<br>尝试修改 Abs的定义,同时 Scale方法使用 Vertex 代替</em>Vertex作为接收者。<br>当 v是Vertex的时候Scale方法没有任何作用。Scale修改 v。当 v是一个值(非指针),方法看到的是 Vertex的副本,并且无法修改原始值。<br>Abs的工作方式是一样的。只不过,仅仅读取 v。所以读取的是原始值(通过指针)还是那个值的副本并没有关系。<br><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"math"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> {</span><br><span class="line"> X, Y <span class="keyword">float64</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(v *Vertex)</span> <span class="title">Scale</span><span class="params">(f <span class="keyword">float64</span>)</span></span> {</span><br><span class="line"> v.X = v.X * f</span><br><span class="line"> v.Y = v.Y * f</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(v *Vertex)</span> <span class="title">Abs</span><span class="params">()</span> <span class="title">float64</span></span> {</span><br><span class="line"> <span class="keyword">return</span> math.Sqrt(v.X*v.X + v.Y*v.Y)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> v := &Vertex{<span class="number">3</span>, <span class="number">4</span>}</span><br><span class="line"> fmt.Printf(<span class="string">"Before scaling: %+v, Abs: %v\n"</span>, v, v.Abs())</span><br><span class="line"> v.Scale(<span class="number">5</span>)</span><br><span class="line"> fmt.Printf(<span class="string">"After scaling: %+v, Abs: %v\n"</span>, v, v.Abs())</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><figure class="highlight plain"><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">Before scaling: &{X:3 Y:4}, Abs: 5</span><br><span class="line">After scaling: &{X:15 Y:20}, Abs: 25</span><br></pre></td></tr></table></figure></p><p>方法扩展阅读:<a href="https://github.com/pylixm/build-web-application-with-golang/blob/master/zh/02.5.md" target="_blank" rel="noopener">Go web编程:method</a></p><h4 id="4-接口"><a href="#4-接口" class="headerlink" title="4.接口"></a>4.接口</h4><p>接口类型是由一组方法定义的集合。<br>接口类型的值可以存放实现这些方法的任何值。<br>注意: 示例代码的 22 行存在一个错误。 由于 Abs只定义在 *Vertex(指针类型)上, 所以 Vertex(值类型)不满足 Abser。<br><figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"math"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Abser <span class="keyword">interface</span> {</span><br><span class="line"> Abs() <span class="keyword">float64</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> a Abser</span><br><span class="line"> f := MyFloat(-math.Sqrt2)</span><br><span class="line"> v := Vertex{<span class="number">3</span>, <span class="number">4</span>}</span><br><span class="line"></span><br><span class="line"> a = f <span class="comment">// a MyFloat 实现了 Abser</span></span><br><span class="line"> a = &v <span class="comment">// a *Vertex 实现了 Abser</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 下面一行,v 是一个 Vertex(而不是 *Vertex)</span></span><br><span class="line"> <span class="comment">// 所以没有实现 Abser。</span></span><br><span class="line"> a = v</span><br><span class="line"></span><br><span class="line"> fmt.Println(a.Abs())</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> MyFloat <span class="keyword">float64</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(f MyFloat)</span> <span class="title">Abs</span><span class="params">()</span> <span class="title">float64</span></span> {</span><br><span class="line"> <span class="keyword">if</span> f < <span class="number">0</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">float64</span>(-f)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">float64</span>(f)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> {</span><br><span class="line"> X, Y <span class="keyword">float64</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(v *Vertex)</span> <span class="title">Abs</span><span class="params">()</span> <span class="title">float64</span></span> {</span><br><span class="line"> <span class="keyword">return</span> math.Sqrt(v.X*v.X + v.Y*v.Y)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><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">5</span><br></pre></td></tr></table></figure></p><h4 id="5-隐式接口"><a href="#5-隐式接口" class="headerlink" title="5.隐式接口"></a>5.隐式接口</h4><p>类型通过实现那些方法来实现接口。 没有显式声明的必要;所以也就没有关键字“implements“。<br>隐式接口解藕了实现接口的包和定义接口的包:互不依赖。<br>因此,也就无需在每一个实现上增加新的接口名称,这样同时也鼓励了明确的接口定义。<br>包 io 定义了 Reader<br>和 Writer;其实不一定要这么做。<br><figure class="highlight go"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"os"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Reader <span class="keyword">interface</span> {</span><br><span class="line"> Read(b []<span class="keyword">byte</span>) (n <span class="keyword">int</span>, err error)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Writer <span class="keyword">interface</span> {</span><br><span class="line"> Write(b []<span class="keyword">byte</span>) (n <span class="keyword">int</span>, err error)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> ReadWriter <span class="keyword">interface</span> {</span><br><span class="line"> Reader</span><br><span class="line"> Writer</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> w Writer</span><br><span class="line"></span><br><span class="line"> <span class="comment">// os.Stdout 实现了 Writer</span></span><br><span class="line"> w = os.Stdout</span><br><span class="line"></span><br><span class="line"> fmt.Fprintf(w, <span class="string">"hello, writer\n"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><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">hello, writer</span><br></pre></td></tr></table></figure></p><h4 id="6-Stringers"><a href="#6-Stringers" class="headerlink" title="6.Stringers"></a>6.Stringers</h4><p>一个普遍存在的接口是 fmt<br>包中定义的 Stringer<br>。<br><figure class="highlight go"><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">type</span> Stringer <span class="keyword">interface</span> { </span><br><span class="line"> String() <span class="keyword">string</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>Stringer是一个可以用字符串描述自己的类型。fmt包 (还有许多其他包)使用这个来进行输出。<br><figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Person <span class="keyword">struct</span> {</span><br><span class="line"> Name <span class="keyword">string</span></span><br><span class="line"> Age <span class="keyword">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(p Person)</span> <span class="title">String</span><span class="params">()</span> <span class="title">string</span></span> {</span><br><span class="line"> <span class="keyword">return</span> fmt.Sprintf(<span class="string">"%v (%v years)"</span>, p.Name, p.Age)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> a := Person{<span class="string">"Arthur Dent"</span>, <span class="number">42</span>}</span><br><span class="line"> z := Person{<span class="string">"Zaphod Beeblebrox"</span>, <span class="number">9001</span>}</span><br><span class="line"> fmt.Println(a, z)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><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">Arthur Dent (42 years) Zaphod Beeblebrox (9001 years)</span><br></pre></td></tr></table></figure></p><h4 id="7-错误"><a href="#7-错误" class="headerlink" title="7.错误"></a>7.错误</h4><p>Go 程序使用 error值来表示错误状态。<br>与 fmt.Stringer类似, error类型是一个内建接口:<br><figure class="highlight go"><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">type</span> error <span class="keyword">interface</span> { </span><br><span class="line"> Error() <span class="keyword">string</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>(与 fmt.Stringer类似,fmt包在输出时也会试图匹配 error。)<br>通常函数会返回一个 error值,调用的它的代码应当判断这个错误是否等于 nil, 来进行错误处理。<br><figure class="highlight go"><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">i, err := strconv.Atoi(<span class="string">"42"</span>)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> { </span><br><span class="line"> fmt.Printf(<span class="string">"couldn't convert number: %v\n"</span>, err)</span><br><span class="line"> <span class="keyword">return</span>}</span><br><span class="line">fmt.Println(<span class="string">"Converted integer:"</span>, i)</span><br><span class="line"><span class="string">``</span><span class="string">` </span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">error 为 nil 时表示成功;非 nil 的 error表示错误。</span></span><br><span class="line"><span class="string">`</span><span class="string">``</span><span class="keyword">go</span></span><br><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> MyError <span class="keyword">struct</span> {</span><br><span class="line"> When time.Time</span><br><span class="line"> What <span class="keyword">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(e *MyError)</span> <span class="title">Error</span><span class="params">()</span> <span class="title">string</span></span> {</span><br><span class="line"> <span class="keyword">return</span> fmt.Sprintf(<span class="string">"at %v, %s"</span>,</span><br><span class="line"> e.When, e.What)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">run</span><span class="params">()</span> <span class="title">error</span></span> {</span><br><span class="line"> <span class="keyword">return</span> &MyError{</span><br><span class="line"> time.Now(),</span><br><span class="line"> <span class="string">"it didn't work"</span>,</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">if</span> err := run(); err != <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(err)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><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">at 2009-11-10 23:00:00 +0000 UTC, it didn't work</span><br></pre></td></tr></table></figure></p><h4 id="8-Readers"><a href="#8-Readers" class="headerlink" title="8.Readers"></a>8.Readers</h4><p>io包指定了 io.Reader接口, 它表示从数据流结尾读取。<br>Go 标准库包含了这个接口的许多实现, 包括文件、网络连接、压缩、加密等等。<br>io.Reader接口有一个 Read方法:<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(T)</span> <span class="title">Read</span><span class="params">(b []<span class="keyword">byte</span>)</span> <span class="params">(n <span class="keyword">int</span>, err error)</span></span></span><br></pre></td></tr></table></figure></p><p>Read用数据填充指定的字节 slice,并且返回填充的字节数和错误信息。 在遇到数据流结尾时,返回 io.EOF错误。<br>例子代码创建了一个 strings.Reader。 并且以每次 8 字节的速度读取它的输出。<br><figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"io"</span></span><br><span class="line"> <span class="string">"strings"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> r := strings.NewReader(<span class="string">"Hello, Reader!"</span>)</span><br><span class="line"></span><br><span class="line"> b := <span class="built_in">make</span>([]<span class="keyword">byte</span>, <span class="number">8</span>)</span><br><span class="line"> <span class="keyword">for</span> {</span><br><span class="line"> n, err := r.Read(b)</span><br><span class="line"> fmt.Printf(<span class="string">"n = %v err = %v b = %v\n"</span>, n, err, b)</span><br><span class="line"> fmt.Printf(<span class="string">"b[:n] = %q\n"</span>, b[:n])</span><br><span class="line"> <span class="keyword">if</span> err == io.EOF {</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><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">n = 8 err = <nil> b = [72 101 108 108 111 44 32 82]</span><br><span class="line">b[:n] = "Hello, R"</span><br><span class="line">n = 6 err = <nil> b = [101 97 100 101 114 33 32 82]</span><br><span class="line">b[:n] = "eader!"</span><br><span class="line">n = 0 err = EOF b = [101 97 100 101 114 33 32 82]</span><br><span class="line">b[:n] = ""</span><br></pre></td></tr></table></figure></p><h4 id="9-Web-服务器"><a href="#9-Web-服务器" class="headerlink" title="9.Web 服务器"></a>9.Web 服务器</h4><p>包 http 通过任何实现了 http.Handler<br>的值来响应 HTTP 请求:<br><figure class="highlight go"><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">package</span> http</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Handler <span class="keyword">interface</span> { </span><br><span class="line"> ServeHTTP(w ResponseWriter, r *Request)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>在这个例子中,类型 Hello实现了 http.Handler。<br>访问 <a href="http://localhost:4000/" target="_blank" rel="noopener">http://localhost:4000/</a> 会看到来自程序的问候。<br>注意: 这个例子无法在基于 web 的指南用户界面运行。为了尝试编写 web 服务器,可能需要安装 Go。<br><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"log"</span></span><br><span class="line"> <span class="string">"net/http"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Hello <span class="keyword">struct</span>{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(h Hello)</span> <span class="title">ServeHTTP</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params"> w http.ResponseWriter,</span></span></span><br><span class="line"><span class="function"><span class="params"> r *http.Request)</span></span> {</span><br><span class="line"> fmt.Fprint(w, <span class="string">"Hello!"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">var</span> h Hello</span><br><span class="line"> err := http.ListenAndServe(<span class="string">"localhost:4000"</span>, h)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> log.Fatal(err)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><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">2009/11/10 23:00:00 listen tcp: Protocol not available</span><br></pre></td></tr></table></figure></p><h4 id="10-图片"><a href="#10-图片" class="headerlink" title="10.图片"></a>10.图片</h4><p>Package image 定义了 Image<br>接口:<br><figure class="highlight go"><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">package</span> image</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Image <span class="keyword">interface</span> { </span><br><span class="line"> ColorModel() color.Model </span><br><span class="line"> Bounds() Rectangle </span><br><span class="line"> At(x, y <span class="keyword">int</span>) color.Color</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>注意:Bounds方法的 Rectangle返回值实际上是一个 image.Rectangle, 其定义在 image包中。<br>(参阅文档了解全部信息。)<br>color.Color和 color.Model也是接口,但是通常因为直接使用预定义的实现 image.RGBA和 image.RGBAModel而被忽视了。这些接口和类型由image/color包定义。<br><figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"image"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> m := image.NewRGBA(image.Rect(<span class="number">0</span>, <span class="number">0</span>, <span class="number">100</span>, <span class="number">100</span>))</span><br><span class="line"> fmt.Println(m.Bounds())</span><br><span class="line"> fmt.Println(m.At(<span class="number">0</span>, <span class="number">0</span>).RGBA())</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><figure class="highlight plain"><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">(0,0)-(100,100)</span><br><span class="line">0 0 0 0</span><br></pre></td></tr></table></figure></p><h3 id="并发"><a href="#并发" class="headerlink" title="并发"></a>并发</h3><p>作为语言的核心部分,Go 提供了并发的特性。<br>这一部分概览了 goroutine 和 channel,以及如何使用它们来实现不同的并发模式。<br>Go 将并发作为语言的核心构成。</p><h4 id="1-goroutine"><a href="#1-goroutine" class="headerlink" title="1.goroutine"></a>1.goroutine</h4><p>goroutine 是由 Go 运行时环境管理的轻量级线程。<br>go f(x, y, z)<br>开启一个新的 goroutine 执行<br>f(x, y, z)<br>f,x,y和 z是当前 goroutine 中定义的,但是在新的 goroutine 中运行 f。<br>goroutine 在相同的地址空间中运行,因此访问共享内存必须进行同步。sync 提供了这种可能,不过在 Go 中并不经常用到,因为有其他的办法。(在接下来的内容中会涉及到。)</p><figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">say</span><span class="params">(s <span class="keyword">string</span>)</span></span> {</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">5</span>; i++ {</span><br><span class="line"> time.Sleep(<span class="number">100</span> * time.Millisecond)</span><br><span class="line"> fmt.Println(s)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">go</span> say(<span class="string">"world"</span>)</span><br><span class="line"> say(<span class="string">"hello"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>结果:<br><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></pre></td><td class="code"><pre><span class="line">hello</span><br><span class="line">hello</span><br><span class="line">world</span><br><span class="line">world</span><br><span class="line">hello</span><br><span class="line">hello</span><br><span class="line">world</span><br><span class="line">world</span><br><span class="line">hello</span><br><span class="line">``` </span><br><span class="line"></span><br><span class="line">#### 2.channel</span><br><span class="line">channel 是有类型的管道,可以用 channel 操作符 <-对其发送或者接收值。</span><br><span class="line"></span><br><span class="line">```go</span><br><span class="line">ch <- v // 将 v 送入 channel ch。</span><br><span class="line">v := <-ch // 从 ch 接收,并且赋值给 v。</span><br></pre></td></tr></table></figure></p><p>(“箭头”就是数据流的方向。)<br>和 map 与 slice 一样,channel 使用前必须创建:<br><code>ch := make(chan int)</code><br>默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得 goroutine 可以在没有明确的锁或竞态变量的情况下进行同步<br><figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">sum</span><span class="params">(a []<span class="keyword">int</span>, c <span class="keyword">chan</span> <span class="keyword">int</span>)</span></span> {</span><br><span class="line"> sum := <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> _, v := <span class="keyword">range</span> a {</span><br><span class="line"> sum += v</span><br><span class="line"> }</span><br><span class="line"> c <- sum <span class="comment">// 将和送入 c</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> a := []<span class="keyword">int</span>{<span class="number">7</span>, <span class="number">2</span>, <span class="number">8</span>, <span class="number">-9</span>, <span class="number">4</span>, <span class="number">0</span>}</span><br><span class="line"></span><br><span class="line"> c := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line"> <span class="keyword">go</span> sum(a[:<span class="built_in">len</span>(a)/<span class="number">2</span>], c)</span><br><span class="line"> <span class="keyword">go</span> sum(a[<span class="built_in">len</span>(a)/<span class="number">2</span>:], c)</span><br><span class="line"> x, y := <-c, <-c <span class="comment">// 从 c 中获取</span></span><br><span class="line"></span><br><span class="line"> fmt.Println(x, y, x+y)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><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">-5 17 12</span><br></pre></td></tr></table></figure></p><h4 id="3-缓冲-channel"><a href="#3-缓冲-channel" class="headerlink" title="3.缓冲 channel"></a>3.缓冲 channel</h4><p>channel 可以是 带缓冲的。为 make提供第二个参数作为缓冲长度来初始化一个缓冲 channel:<br>ch := make(chan int, 100)<br>向带缓冲的 channel 发送数据的时候,只有在缓冲区满的时候才会阻塞。 而当缓冲区为空的时候接收操作会阻塞。<br>修改例子使得缓冲区被填满,然后看看会发生什么<br><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>, <span class="number">2</span>)</span><br><span class="line"> ch <- <span class="number">1</span></span><br><span class="line"> ch <- <span class="number">2</span></span><br><span class="line"> fmt.Println(<-ch)</span><br><span class="line"> fmt.Println(<-ch)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><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></pre></td><td class="code"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">-----------------</span><br><span class="line">-----------------</span><br><span class="line">fatal error: all goroutines are asleep - deadlock!</span><br><span class="line"></span><br><span class="line">goroutine 1 [chan send]:</span><br><span class="line">main.main() </span><br><span class="line"> /tmp/sandbox156608315/main.go:9 +0x100</span><br></pre></td></tr></table></figure></p><h4 id="4-range-和-close"><a href="#4-range-和-close" class="headerlink" title="4.range 和 close"></a>4.range 和 close</h4><p>发送者可以 close一个 channel 来表示再没有值会被发送了。接收者可以通过赋值语句的第二参数来测试 channel 是否被关闭:当没有值可以接收并且 channel 已经被关闭,那么经过<br>v, ok := <-ch<br>之后 ok会被设置为 false。<br>循环 for i := range c 会不断从 channel 接收值,直到它被关闭。<br>注意: 只有发送者才能关闭 channel,而不是接收者。向一个已经关闭的 channel 发送数据会引起 panic。 还要注意: channel 与文件不同;通常情况下无需关闭它们。只有在需要告诉接收者没有更多的数据的时候才有必要进行关闭,例如中断一个 range。<br><figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">fibonacci</span><span class="params">(n <span class="keyword">int</span>, c <span class="keyword">chan</span> <span class="keyword">int</span>)</span></span> {</span><br><span class="line"> x, y := <span class="number">0</span>, <span class="number">1</span></span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < n; i++ {</span><br><span class="line"> c <- x</span><br><span class="line"> x, y = y, x+y</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">close</span>(c)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> c := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>, <span class="number">10</span>)</span><br><span class="line"> <span class="keyword">go</span> fibonacci(<span class="built_in">cap</span>(c), c)</span><br><span class="line"> <span class="keyword">for</span> i := <span class="keyword">range</span> c {</span><br><span class="line"> fmt.Println(i)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><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">0</span><br><span class="line">1</span><br><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">5</span><br><span class="line">8</span><br><span class="line">13</span><br><span class="line">21</span><br><span class="line">34</span><br></pre></td></tr></table></figure></p><h4 id="5-select"><a href="#5-select" class="headerlink" title="5.select"></a>5.select</h4><p>select语句使得一个 goroutine 在多个通讯操作上等待。<br>select会阻塞,直到条件分支中的某个可以继续执行,这时就会执行那个条件分支。当多个都准备好的时候,会随机选择一个<br><figure class="highlight go"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">fibonacci</span><span class="params">(c, quit <span class="keyword">chan</span> <span class="keyword">int</span>)</span></span> {</span><br><span class="line"> x, y := <span class="number">0</span>, <span class="number">1</span></span><br><span class="line"> <span class="keyword">for</span> {</span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> c <- x:</span><br><span class="line"> x, y = y, x+y</span><br><span class="line"> <span class="keyword">case</span> <-quit:</span><br><span class="line"> fmt.Println(<span class="string">"quit"</span>)</span><br><span class="line"> <span class="keyword">return</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 class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> c := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line"> quit := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">10</span>; i++ {</span><br><span class="line"> fmt.Println(<-c)</span><br><span class="line"> }</span><br><span class="line"> quit <- <span class="number">0</span></span><br><span class="line"> }()</span><br><span class="line"> fibonacci(c, quit)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><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">0</span><br><span class="line">1</span><br><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">5</span><br><span class="line">8</span><br><span class="line">13</span><br><span class="line">21</span><br><span class="line">34</span><br><span class="line">quit</span><br></pre></td></tr></table></figure></p><h4 id="6-默认选择"><a href="#6-默认选择" class="headerlink" title="6.默认选择"></a>6.默认选择</h4><p>当 select中的其他条件分支都没有准备好的时候,default分支会被执行。<br>为了非阻塞的发送或者接收,可使用 default分支:<br><figure class="highlight go"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> {</span><br><span class="line"><span class="keyword">case</span> i := <-c: </span><br><span class="line"> <span class="comment">// 使用 idefault: </span></span><br><span class="line"> <span class="comment">// 从 c 读取会阻塞</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> tick := time.Tick(<span class="number">100</span> * time.Millisecond)</span><br><span class="line"> boom := time.After(<span class="number">500</span> * time.Millisecond)</span><br><span class="line"> <span class="keyword">for</span> {</span><br><span class="line"> <span class="keyword">select</span> {</span><br><span class="line"> <span class="keyword">case</span> <-tick:</span><br><span class="line"> fmt.Println(<span class="string">"tick."</span>)</span><br><span class="line"> <span class="keyword">case</span> <-boom:</span><br><span class="line"> fmt.Println(<span class="string">"BOOM!"</span>)</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> fmt.Println(<span class="string">" ."</span>)</span><br><span class="line"> time.Sleep(<span class="number">50</span> * time.Millisecond)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><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></pre></td><td class="code"><pre><span class="line"> .</span><br><span class="line"> .</span><br><span class="line">tick.</span><br><span class="line"> .</span><br><span class="line"> .</span><br><span class="line">tick.</span><br><span class="line"> .</span><br><span class="line"> .</span><br><span class="line">tick.</span><br><span class="line"> .</span><br><span class="line"> .</span><br><span class="line">tick.</span><br><span class="line"> .</span><br><span class="line"> .</span><br><span class="line">tick.</span><br><span class="line">BOOM!</span><br></pre></td></tr></table></figure></p><h4 id="7-sync-Mutex"><a href="#7-sync-Mutex" class="headerlink" title="7.sync.Mutex"></a>7.sync.Mutex</h4><p>我们已经看到 channel用来在各个 goroutine 间进行通信是非常合适的了。</p><p>但是如果我们并不需要通信呢?比如说,如果我们只是想保证在每个时刻,只有一个 goroutine 能访问一个共享的变量从而避免冲突?</p><p>这里涉及的概念叫做 <code>互斥</code>,通常使用 <code>互斥锁(mutex)_</code> 来提供这个限制。</p><p>Go 标准库中提供了 sync.Mutex 类型及其两个方法:<br><figure class="highlight plain"><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">Lock</span><br><span class="line">Unlock</span><br></pre></td></tr></table></figure></p><p>我们可以通过在代码前调用 Lock方法,在代码后调用 Unlock方法来保证一段代码的互斥执行。 参见 Inc方法。</p><p>我们也可以用 defer语句来保证互斥锁一定会被解锁。参见 Value方法。<br><figure class="highlight go"><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="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"sync"</span></span><br><span class="line"> <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// SafeCounter 的并发使用是安全的。</span></span><br><span class="line"><span class="keyword">type</span> SafeCounter <span class="keyword">struct</span> {</span><br><span class="line"> v <span class="keyword">map</span>[<span class="keyword">string</span>]<span class="keyword">int</span></span><br><span class="line"> mux sync.Mutex</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Inc 增加给定 key 的计数器的值。</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *SafeCounter)</span> <span class="title">Inc</span><span class="params">(key <span class="keyword">string</span>)</span></span> {</span><br><span class="line"> c.mux.Lock()</span><br><span class="line"> <span class="comment">// Lock 之后同一时刻只有一个 goroutine 能访问 c.v</span></span><br><span class="line"> c.v[key]++</span><br><span class="line"> c.mux.Unlock()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Value 返回给定 key 的计数器的当前值。</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *SafeCounter)</span> <span class="title">Value</span><span class="params">(key <span class="keyword">string</span>)</span> <span class="title">int</span></span> {</span><br><span class="line"> c.mux.Lock()</span><br><span class="line"> <span class="comment">// Lock 之后同一时刻只有一个 goroutine 能访问 c.v</span></span><br><span class="line"> <span class="keyword">defer</span> c.mux.Unlock()</span><br><span class="line"> <span class="keyword">return</span> c.v[key]</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> c := SafeCounter{v: <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="keyword">string</span>]<span class="keyword">int</span>)}</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">1000</span>; i++ {</span><br><span class="line"> <span class="keyword">go</span> c.Inc(<span class="string">"somekey"</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> time.Sleep(time.Second)</span><br><span class="line"> fmt.Println(c.Value(<span class="string">"somekey"</span>))</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><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">1000</span><br></pre></td></tr></table></figure></p><h4 id="8-练习:Web-爬虫"><a href="#8-练习:Web-爬虫" class="headerlink" title="8.练习:Web 爬虫"></a>8.练习:Web 爬虫</h4><p>在这个练习中,将会使用 Go 的并发特性来并行执行 web 爬虫。<br>修改 Crawl函数来并行的抓取 URLs,并且保证不重复。<br>提示:你可以用一个 map 来缓存已经获取的 URL,但是需要注意 map 本身并不是并发安全的!</p><figure class="highlight go"><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><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Fetcher <span class="keyword">interface</span> {</span><br><span class="line"> <span class="comment">// Fetch 返回 URL 的 body 内容,并且将在这个页面上找到的 URL 放到一个 slice 中。</span></span><br><span class="line"> Fetch(url <span class="keyword">string</span>) (body <span class="keyword">string</span>, urls []<span class="keyword">string</span>, err error)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Crawl 使用 fetcher 从某个 URL 开始递归的爬取页面,直到达到最大深度。</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Crawl</span><span class="params">(url <span class="keyword">string</span>, depth <span class="keyword">int</span>, fetcher Fetcher)</span></span> {</span><br><span class="line"> <span class="comment">// <span class="doctag">TODO:</span> 并行的抓取 URL。</span></span><br><span class="line"> <span class="comment">// <span class="doctag">TODO:</span> 不重复抓取页面。</span></span><br><span class="line"> <span class="comment">// 下面并没有实现上面两种情况:</span></span><br><span class="line"> <span class="keyword">if</span> depth <= <span class="number">0</span> {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> body, urls, err := fetcher.Fetch(url)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> fmt.Println(err)</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> fmt.Printf(<span class="string">"found: %s %q\n"</span>, url, body)</span><br><span class="line"> <span class="keyword">for</span> _, u := <span class="keyword">range</span> urls {</span><br><span class="line"> Crawl(u, depth<span class="number">-1</span>, fetcher)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> Crawl(<span class="string">"http://golang.org/"</span>, <span class="number">4</span>, fetcher)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// fakeFetcher 是返回若干结果的 Fetcher。</span></span><br><span class="line"><span class="keyword">type</span> fakeFetcher <span class="keyword">map</span>[<span class="keyword">string</span>]*fakeResult</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> fakeResult <span class="keyword">struct</span> {</span><br><span class="line"> body <span class="keyword">string</span></span><br><span class="line"> urls []<span class="keyword">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(f fakeFetcher)</span> <span class="title">Fetch</span><span class="params">(url <span class="keyword">string</span>)</span> <span class="params">(<span class="keyword">string</span>, []<span class="keyword">string</span>, error)</span></span> {</span><br><span class="line"> <span class="keyword">if</span> res, ok := f[url]; ok {</span><br><span class="line"> <span class="keyword">return</span> res.body, res.urls, <span class="literal">nil</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="string">""</span>, <span class="literal">nil</span>, fmt.Errorf(<span class="string">"not found: %s"</span>, url)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// fetcher 是填充后的 fakeFetcher。</span></span><br><span class="line"><span class="keyword">var</span> fetcher = fakeFetcher{</span><br><span class="line"> <span class="string">"http://golang.org/"</span>: &fakeResult{</span><br><span class="line"> <span class="string">"The Go Programming Language"</span>,</span><br><span class="line"> []<span class="keyword">string</span>{</span><br><span class="line"> <span class="string">"http://golang.org/pkg/"</span>,</span><br><span class="line"> <span class="string">"http://golang.org/cmd/"</span>,</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> <span class="string">"http://golang.org/pkg/"</span>: &fakeResult{</span><br><span class="line"> <span class="string">"Packages"</span>,</span><br><span class="line"> []<span class="keyword">string</span>{</span><br><span class="line"> <span class="string">"http://golang.org/"</span>,</span><br><span class="line"> <span class="string">"http://golang.org/cmd/"</span>,</span><br><span class="line"> <span class="string">"http://golang.org/pkg/fmt/"</span>,</span><br><span class="line"> <span class="string">"http://golang.org/pkg/os/"</span>,</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> <span class="string">"http://golang.org/pkg/fmt/"</span>: &fakeResult{</span><br><span class="line"> <span class="string">"Package fmt"</span>,</span><br><span class="line"> []<span class="keyword">string</span>{</span><br><span class="line"> <span class="string">"http://golang.org/"</span>,</span><br><span class="line"> <span class="string">"http://golang.org/pkg/"</span>,</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> <span class="string">"http://golang.org/pkg/os/"</span>: &fakeResult{</span><br><span class="line"> <span class="string">"Package os"</span>,</span><br><span class="line"> []<span class="keyword">string</span>{</span><br><span class="line"> <span class="string">"http://golang.org/"</span>,</span><br><span class="line"> <span class="string">"http://golang.org/pkg/"</span>,</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>结果:<br><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">found: http://golang.org/ "The Go Programming Language"</span><br><span class="line">found: http://golang.org/pkg/ "Packages"</span><br><span class="line">found: http://golang.org/ "The Go Programming Language"</span><br><span class="line">found: http://golang.org/pkg/ "Packages"</span><br><span class="line">not found: http://golang.org/cmd/</span><br><span class="line">not found: http://golang.org/cmd/</span><br><span class="line">found: http://golang.org/pkg/fmt/ "Package fmt"</span><br><span class="line">found: http://golang.org/ "The Go Programming Language"</span><br><span class="line">found: http://golang.org/pkg/ "Packages"</span><br><span class="line">found: http://golang.org/pkg/os/ "Package os"</span><br><span class="line">found: http://golang.org/ "The Go Programming Language"</span><br><span class="line">found: http://golang.org/pkg/ "Packages"</span><br><span class="line">not found: http://golang.org/cmd/</span><br></pre></td></tr></table></figure></p>]]></content>
<summary type="html">
<p>本文出处<a href="https://tour.go-zh.org" target="_blank" rel="noopener">Go编程语言</a>,由<a href="https://www.jianshu.com/u/9029357b2874" target="
</summary>
<category term="golang" scheme="http://pylixm.cc/categories/golang/"/>
<category term="golang" scheme="http://pylixm.cc/tags/golang/"/>
<category term="语言学习" scheme="http://pylixm.cc/tags/%E8%AF%AD%E8%A8%80%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>【 Go语言学习笔记 】 - Golang 命令</title>
<link href="http://pylixm.cc/posts/2018-01-26-Go-commond.html"/>
<id>http://pylixm.cc/posts/2018-01-26-Go-commond.html</id>
<published>2018-01-25T16:00:00.000Z</published>
<updated>2018-05-14T08:24:17.215Z</updated>
<content type="html"><![CDATA[<p>安装好 golang 后,在任意目录运行 <code>go</code> 可看到 Golang 的所有命令,如下:<br><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></pre></td><td class="code"><pre><span class="line">build compile packages and dependencies</span><br><span class="line">clean remove object files</span><br><span class="line">doc show documentation <span class="keyword">for</span> package or symbol</span><br><span class="line">env <span class="built_in">print</span> Go environment information</span><br><span class="line">bug start a bug report</span><br><span class="line">fix run go tool fix on packages</span><br><span class="line">fmt run gofmt on package sources</span><br><span class="line">generate generate Go files by processing <span class="built_in">source</span></span><br><span class="line">get download and install packages and dependencies</span><br><span class="line">install compile and install packages and dependencies</span><br><span class="line">list list packages</span><br><span class="line">run compile and run Go program</span><br><span class="line"><span class="built_in">test</span> <span class="built_in">test</span> packages</span><br><span class="line">tool run specified go tool</span><br><span class="line">version <span class="built_in">print</span> Go version</span><br><span class="line">vet run go tool vet on packages</span><br></pre></td></tr></table></figure></p><p>这里根据郝林老师的课程,做部分命令的总结,记录备查。</p><h2 id="常用-Golang-命令总结"><a href="#常用-Golang-命令总结" class="headerlink" title="常用 Golang 命令总结"></a>常用 Golang 命令总结</h2><h3 id="go-run"><a href="#go-run" class="headerlink" title="go run"></a>go run</h3><p>用于运行命令源文件,只接受一个命令源文件以及若干个库文件作为参数。内部操作步骤为,先编译,将编译结果放到临时文件(可执行文件和归档文件),再运行。</p><p><strong>运行常用标记:</strong></p><ul><li><code>-a</code> 强制编译相关代码,不论他们的编译结果是否已经是最新的。</li><li><code>-n</code> 打印编译过程中所需要运行的命令,但不真正执行他们。</li><li><code>-x</code> 打印编译过程中运行的命令,并执行他们。</li><li><code>-p n</code> 并行执行,n 为并行的数量。</li><li><code>-v</code> 列出被编译的代码包的名称。</li><li><code>-work</code> 显示编译时创建的临时工作目录的路径,并且不删除它。</li></ul><h3 id="go-build"><a href="#go-build" class="headerlink" title="go build"></a>go build</h3><p>用于编译我们指定的源码文件或代码包以及它们的依赖包。编译非命令源码文件是不会产生任何结果文件的。编译命令源码文件会在该命令的执行目录中生成一个可执行文件。</p><ul><li>执行该命令且不追加任何参数,默认会把当前目录作为代码包编译</li><li>执行命令且以代码包导入路径为参数时,该代码包及其依赖代码包会被编译。当加入 -a 时,所有涉及到的代码包会被编译。</li><li>可加若干源码文件作为参数,go 只会编译被列出的源码文件 </li></ul><p><strong>运行常用标记:同 go run</strong></p><h3 id="go-install"><a href="#go-install" class="headerlink" title="go install"></a>go install</h3><p>用于编译并安装代码包或源码文件。安装代码包会在当前工作区的 pkg/<平台相关目录> 下生成归档文件。安装命令源码文件会在当前工作区的 bin 目录 或 $GOBIN 目录下生成可执行文件。</p><ul><li>不加参数时,它试图把当前目录作为代码包安装</li><li>加代码包导入路径作为参数,会安装该代码包及依赖包 </li></ul><h3 id="go-get"><a href="#go-get" class="headerlink" title="go get"></a>go get</h3><p>用于从远程代码仓库上下载并按照代码包。支持版本控制系统, git/hg/svn/Bazaar等。指定的代码包会被下载到 $GOPATH 中包含的第一个工作区的 src 目录中。 </p><p><strong>运行常用标记:</strong></p><ul><li><code>-d</code> 只下载,不安装。</li><li><code>-fix</code> 在下载代码包后,执行修正动作(go不同版本,语法的修正)</li><li><code>-u</code> 更新下已有的代码包及其依赖包。 </li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://github.com/hyper0x/go_command_tutorial/blob/master/SUMMARY.md" target="_blank" rel="noopener">Golang 命令教程</a></li></ul>]]></content>
<summary type="html">
<p>安装好 golang 后,在任意目录运行 <code>go</code> 可看到 Golang 的所有命令,如下:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span clas
</summary>
<category term="golang" scheme="http://pylixm.cc/categories/golang/"/>
<category term="golang" scheme="http://pylixm.cc/tags/golang/"/>
<category term="语言学习" scheme="http://pylixm.cc/tags/%E8%AF%AD%E8%A8%80%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>【 Go语言学习笔记 】 - Golang 环境搭建及相关概念</title>
<link href="http://pylixm.cc/posts/2018-01-25-Go-install.html"/>
<id>http://pylixm.cc/posts/2018-01-25-Go-install.html</id>
<published>2018-01-24T16:00:00.000Z</published>
<updated>2018-05-14T10:17:10.783Z</updated>
<content type="html"><![CDATA[<h2 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h2><p>在环境搭建之前,先来了解几个常用的 Golang 环境变量:</p><ul><li><strong>$GOROOT</strong> 表示 Go 在你的电脑上的安装位置,它的值一般都是 $HOME/go,当然,你也可以安装在别的地方。</li><li><strong>$GOARCH</strong> 表示目标机器的处理器架构,它的值可以是 386、amd64 或 arm。</li><li><strong>$GOOS</strong> 表示目标机器的操作系统,它的值可以是 darwin、freebsd、linux 或 windows。</li><li><strong>$GOBIN</strong> 表示编译器和链接器的安装位置,默认是 $GOROOT/bin,如果你使用的是 Go 1.0.3 及以后的版本,一般情况下你可以将它的值设置为空,Go 将会使用前面提到的默认值。</li><li><strong>$GOPATH</strong> 默认采用和 $GOROOT 一样的值,但从 Go 1.1 版本开始,你必须修改为其它路径。它可以包含多个包含 Go 语言源码文件、包文件和可执行文件的路径,而这些路径下又必须分别包含三个规定的目录:src、pkg 和 bin,这三个目录分别用于存放源码文件、包文件和可执行文件。</li><li><strong>$GOARM</strong> 专门针对基于 arm 架构的处理器,它的值可以是 5 或 6,默认为 6。</li><li><strong>$GOMAXPROCS</strong> 用于设置应用程序可使用的处理器个数与核数。</li><li><strong>$GOHOSTOS</strong> 交叉编译(运行机器和编译机器系统类型不同)时,设置本地机器系统类型,值会和本地机器($GOOS 和 $GOARCH)一样。</li><li><strong>$GOHOSTARCH</strong> 交叉编译时,设置目标机器系统类型,值会和本地机器($GOOS 和 $GOARCH)一样。</li></ul><p>可运行 <code>go env</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><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">$ go env</span><br><span class="line">GOARCH=<span class="string">"amd64"</span></span><br><span class="line">GOBIN=<span class="string">""</span></span><br><span class="line">GOEXE=<span class="string">""</span></span><br><span class="line">GOHOSTARCH=<span class="string">"amd64"</span></span><br><span class="line">GOHOSTOS=<span class="string">"darwin"</span></span><br><span class="line">GOOS=<span class="string">"darwin"</span></span><br><span class="line">GOPATH=<span class="string">"/Users/pylixm/go/"</span></span><br><span class="line">GORACE=<span class="string">""</span></span><br><span class="line">GOROOT=<span class="string">"/usr/local/Cellar/go/1.9.2/libexec"</span></span><br><span class="line">GOTOOLDIR=<span class="string">"/usr/local/Cellar/go/1.9.2/libexec/pkg/tool/darwin_amd64"</span></span><br><span class="line">GCCGO=<span class="string">"gccgo"</span></span><br><span class="line">CC=<span class="string">"clang"</span></span><br><span class="line">GOGCCFLAGS=<span class="string">"-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/hn/nsd6fjdd68z6kblbqvj6t1mm0000gn/T/go-build453502664=/tmp/go-build -gno-record-gcc-switches -fno-common"</span></span><br><span class="line">CXX=<span class="string">"clang++"</span></span><br><span class="line">CGO_ENABLED=<span class="string">"1"</span></span><br><span class="line">CGO_CFLAGS=<span class="string">"-g -O2"</span></span><br><span class="line">CGO_CPPFLAGS=<span class="string">""</span></span><br><span class="line">CGO_CXXFLAGS=<span class="string">"-g -O2"</span></span><br><span class="line">CGO_FFLAGS=<span class="string">"-g -O2"</span></span><br><span class="line">CGO_LDFLAGS=<span class="string">"-g -O2"</span></span><br><span class="line">PKG_CONFIG=<span class="string">"pkg-config"</span></span><br></pre></td></tr></table></figure><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><h4 id="Linux-and-FreeBSD-tarballs"><a href="#Linux-and-FreeBSD-tarballs" class="headerlink" title="Linux, and FreeBSD tarballs"></a>Linux, and FreeBSD tarballs</h4><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 下载源码包</span></span><br><span class="line">wget https://dl.google.com/go/go1.9.3.linux-amd64.tar.gz</span><br><span class="line"></span><br><span class="line"><span class="comment"># 解压到 /usr/local</span></span><br><span class="line">tar -C /usr/<span class="built_in">local</span> -xzf go<span class="variable">$VERSION</span>.<span class="variable">$OS</span>-<span class="variable">$ARCH</span>.tar.gz</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置环境变量 $HOME/.bash_profile</span></span><br><span class="line"><span class="built_in">export</span> PATH=<span class="variable">$PATH</span>:/usr/<span class="built_in">local</span>/go/bin</span><br><span class="line"><span class="built_in">source</span> .bash_profile</span><br></pre></td></tr></table></figure><h4 id="Mac-OS-X"><a href="#Mac-OS-X" class="headerlink" title="Mac OS X"></a>Mac OS X</h4><p>Mac 系统可以使用同 Linux 相同方式的安装,也使用 brew 等包管理工具安装。</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">brew install go</span><br></pre></td></tr></table></figure><h4 id="windows"><a href="#windows" class="headerlink" title="windows"></a>windows</h4><p>可直接在官方下载地址下载 msi 格式安装包安装。 官方下载地址: <a href="https://golang.google.cn/dl/" target="_blank" rel="noopener">https://golang.google.cn/dl/</a></p><p>默认安装位置, c:\Go。</p><p>更多信息,可参考官方安装指导,<a href="https://golang.google.cn/doc/install" target="_blank" rel="noopener">这里</a>。</p><h4 id="测试安装"><a href="#测试安装" class="headerlink" title="测试安装"></a>测试安装</h4><p>在任意位置执行 <code>go version</code> 可看到go 的相关版本信息。</p><figure class="highlight bash"><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="comment"># go version</span></span><br><span class="line">go version go1.9.3 linux/amd64</span><br></pre></td></tr></table></figure><p>在任意目录,创建文件 hello.go 文件,内容如下:<br><figure class="highlight go"><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">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"> fmt.Printf(<span class="string">"hello, world\n"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>执行 <code>go run hello.go</code>,打印如下:<br><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">hello ,world</span><br></pre></td></tr></table></figure></p><p>则说明安装成功。</p><h4 id="自定义安装位置"><a href="#自定义安装位置" class="headerlink" title="自定义安装位置"></a>自定义安装位置</h4><p>Linux 和 Mac 可在解压 Golang 时,将位置变换即可。golang 的安装包为开箱即用的。windows 也可在安装时修改。官方文档说,自定义了安装位置,必须配置 <code>GOROOT</code>。 </p><p>在Linux 测试,更换了安装位置,修改对应 go bin 的系统环境PATH 目录,执行go命令正常执行。通过<code>go env</code> 查看 GOROOT 自动修改为新的位置。</p><h3 id="卸载"><a href="#卸载" class="headerlink" title="卸载"></a>卸载</h3><p>直接删除 golang 目录(Linux/Mac OS: /usr/local/go; Windows: C:\Go )和环境变量即可。</p><h2 id="相关概念"><a href="#相关概念" class="headerlink" title="相关概念"></a>相关概念</h2><h3 id="工作区和GOPATH"><a href="#工作区和GOPATH" class="headerlink" title="工作区和GOPATH"></a>工作区和GOPATH</h3><p>工作区,是放置 Go 源码文件的目录。一般情况下,Go 源码文件都需要放到工作区中,但是对于命令源码文件来说,不是必须的。</p><p>工作区结构如下:<br><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">$ tree -d -L 1</span><br><span class="line">.</span><br><span class="line">├── bin</span><br><span class="line">├── pkg</span><br><span class="line">└── src</span><br></pre></td></tr></table></figure></p><ul><li><strong>src</strong> 用于放置源码文件,以代码包为组织形式。</li><li><strong>pkg</strong> 用于存放归档文件,以 .a 为后缀的文件,存放在相关平台目录下,同样以代码包为组织形式。 目录结构为: <code>$GOPATH/pkg/$GOOS_$GOARCH/<一级代码包>/<二级代码包>/<末级代码包>.a</code></li><li><strong>bin</strong> 用于存放当前工作区的 Go 程序的可执行文件。<ul><li>当环境变量设置 GOBIN 时,改目录变得无意义,所有可执行文件(编译文件)会放到 GOBIN 的目录中;</li><li>当 GOPATH 设置了多个目录时,必须设置 GOBIN 否则无法成功安装 Go 程序的可执行文件。</li></ul></li></ul><p>整体如下:<br><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="/static/imgs/gopath_demo.png" alt="" title=""> </div> <div class="image-caption"></div> </figure></p><h3 id="源码文件类型"><a href="#源码文件类型" class="headerlink" title="源码文件类型"></a>源码文件类型</h3><p>Go 源码文件以 .go 为后缀,左右源码文件都是以包为组织形式的。源码文件分3类:</p><ul><li>命令源码文件:声明自己属于main代码包、包含无参数声明和结果声明的main函数。 go 程序的入口,不建议把所有文件都写在一个文件中。同一个代码包中不建议直接包含多个命令源码文件。安装后,存放在 GOBIN 或 <工作区>/bin 下 。</li><li>库源码文件:不具备命令源码文件两个特征的一般文件。被安装后,存放在 <工作区>/pkg/<相关平台目录> 下。</li><li>测试源码文件: 不具备命令源码文件两个特征的一般文件,以 _test.go 为后缀。其中至少有一个函数的命令以 Test 或 Benchmark 为前缀。</li></ul><h3 id="代码包的相关知识"><a href="#代码包的相关知识" class="headerlink" title="代码包的相关知识"></a>代码包的相关知识</h3><p><strong>代码包的作用</strong></p><ul><li>编译和归档 Go 程序的最基本单位。</li><li>代码划分、集结和依赖的有效组织形式,也是权限控制的辅助手段。</li></ul><p><strong>代码包的规则</strong></p><p>一个代码包实际上就是一个有导入路径代表的目录。导入路径即 <工作区目录>/src 或 <工作区目录>/pkg/<平台相关目录> 之下的某段子路径。</p><p><strong>代码包的声明</strong></p><ul><li>每个源码文件必须声明其所属的代码包。</li><li>同一个代码包中的所有源码文件声明的代码包应该是相同的。</li></ul><p><strong>代码包声明与代码包导入路径的区别</strong></p><p>代码包声明语句中的包名称应该是该代码包的导入路径的最右子路径。如:<br>代码包导入路径: <code>GoPL/tools/</code><br>代码包声明:<code>package tools</code> </p><p><strong>代码包的导入</strong></p><p>代码包导入语句中使用的包名称应该与其导入路径一至,如代码包 <code>fmt</code> 的导入:<br><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">import (</span><br><span class="line"> "fmt"</span><br><span class="line">)</span><br></pre></td></tr></table></figure></p><p><strong>代码包的导入方法</strong></p><ul><li><p>带别名的导入: </p><figure class="highlight go"><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">import</span> str <span class="string">"strings"</span> </span><br><span class="line"></span><br><span class="line">str.HasPrefix(<span class="string">"abc"</span>,<span class="string">"a"</span>)</span><br></pre></td></tr></table></figure></li><li><p>本地化的导入:</p><figure class="highlight go"><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">import</span> . <span class="string">"strings"</span> </span><br><span class="line"></span><br><span class="line">HasPrefix(<span class="string">"abc"</span>,<span class="string">"a"</span>)</span><br></pre></td></tr></table></figure></li><li><p>仅仅初始化</p><figure class="highlight go"><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">import</span> _ <span class="string">"strings"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 仅执行代码包中的初始化函数</span></span><br></pre></td></tr></table></figure></li></ul><p><strong>代码包初始化</strong></p><p>代码包初始化函数即:无参数声明和结果声明的 init 函数。init 函数可以被声明在任何文件中,且可以有多个。</p><p>init 函数执行时机:</p><ul><li>单一代码包:当导入代码包时,对代码包的所有全局变量进行求值,之后执行所有 init 函数。同一代码包中,的 init 函数执行顺序是不确定的。</li><li>不同代码包:先执行被导入的代码包中的 init 函数,再执行本代码包的 init 函数。同一代码包中被导入多个代码包的 init 函数执行顺序不定。</li><li>所有涉及到的代码包:在程序入口,在 main 函数执行之前执行。所有的 init 函数,都只会被执行一次。</li></ul><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>Golang 的安装可谓简单实用,开箱即用。对 Mac 和 Windows 系统都提供了对应的安装包,方便大家安装。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://golang.google.cn/doc/install" target="_blank" rel="noopener">https://golang.google.cn/doc/install</a></li><li><a href="https://www.imooc.com/learn/345" target="_blank" rel="noopener">视频:Go语言第一课 郝林</a></li></ul>]]></content>
<summary type="html">
<h2 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h2><p>在环境搭建之前,先来了解几个常用的 Golang 环境变量:</p>
<ul>
<li><strong>$GOROOT</s
</summary>
<category term="golang" scheme="http://pylixm.cc/categories/golang/"/>
<category term="golang" scheme="http://pylixm.cc/tags/golang/"/>
<category term="语言学习" scheme="http://pylixm.cc/tags/%E8%AF%AD%E8%A8%80%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>【 Go语言学习笔记 】 - Golang 初步了解</title>
<link href="http://pylixm.cc/posts/2018-01-24-Go-introduction.html"/>
<id>http://pylixm.cc/posts/2018-01-24-Go-introduction.html</id>
<published>2018-01-23T16:00:00.000Z</published>
<updated>2018-05-14T10:17:10.774Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>第一次听说 Golang 语言是通过小米开源的 Open-Falcon ,它是用 Go 开发的,速度上是python的好几倍,天生适合并发程序的开发。后来得知大名鼎鼎的 Dorker 也是用 Golang 开发的,便是对这么语言产生了浓厚的兴趣。一直想学习下,拖到现在,有些闲空,便深入的学习下。</p><p>《程序员修炼之道》中有这么一个观点:程序员每年要学习一门语言。这个我也是极力赞同的,只有通过不同语言的对比,你才会加深对语言本身的理解,在业务逻辑中充分发挥语言的特性,不会一味的只关心业务逻辑。 </p><p>当然不同角色的程序员的侧重点不同,”对于大多数的程序员来说,其实我们只需要关注问题域;做底层平台开发的,关注机器模型、通信原理 以及OS原理和实现细节;做算法的,很荣幸,那才是正统的程序设计的核心;前端攻城师则更多关注用户的体验。“(引自<a href="https://studygolang.com/articles/1975" target="_blank" rel="noopener">关于编程语言学习的一些体会</a>)。</p><p>所以说,我们在学习语言的时候也不要过于钻牛角尖,只要学以致用便好。即使不能立即在工作中使用,语言中的思想也会潜移默化的影响我们,当遇到合适的场景时,便可发挥相应语言的长处,解决实际业务中的问题。语言只不过是工具,我们要做到一门精通,其他拿来可用便好。</p><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>Go,又称 golang,是 Google 开发的一种静态强类型,编译型,并发型,并具有垃圾回收功能的开源编程语言。</p><p>Go 语言于2009年11月正式宣布推出,自2012年发布1.0,最新稳定版1.9.3, 国内官方文档地址:<a href="https://golang.google.cn/" target="_blank" rel="noopener">https://golang.google.cn/</a> 。目前,Go的相关工具和生态已逐渐趋于完善,也不乏重量级项目,如 Docker, Kubernetes, Etcd, InfluxDB 等。</p><p>下图为 Go 语言的起源:<br><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="/static/imgs/go_history.png" alt="" title=""> </div> <div class="image-caption"></div> </figure></p><p>其他更多,见这里 <a href="https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/01.1.md" target="_blank" rel="noopener">《The way to go》- Go 语言的起源,发展与普及</a></p><h3 id="Golang-的特点"><a href="#Golang-的特点" class="headerlink" title="Golang 的特点"></a>Golang 的特点</h3><ul><li>静态类型、编译性、开源。</li><li>脚本话语法,支持多种编程范式:函数式、面向对象编程。</li><li>原生支持并发编程,不同于通过函数库支持。</li></ul><h3 id="Golang-设计目标及解决的问题"><a href="#Golang-设计目标及解决的问题" class="headerlink" title="Golang 设计目标及解决的问题"></a>Golang 设计目标及解决的问题</h3><ul><li>将静态语言的安全性和高效性与动态语言的易开发性进行有机结合,达到完美平衡,从而使编程变得更加有乐趣,而不是在艰难抉择中痛苦前行。</li><li>对于网络通信、并发和并行编程的极佳支持,从而更好地利用大量的分布式和多核的计算机。</li><li>高效的构建速度,不必浪费大量的时间在等待程序的构建上。</li><li>通过采用包模型,严格的依赖关系检查机制来加快程序构建的速度。</li><li>实现高效快速的垃圾回收(使用了一个简单的标记-清除算法)</li><li>Go 语言还能够在运行时进行反射相关的操作。</li></ul><p>更多语言特性,请这里:<a href="https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/01.2.md#125-%E8%AF%AD%E8%A8%80%E7%9A%84%E7%89%B9%E6%80%A7" target="_blank" rel="noopener">语言的主要特性与发展的环境和影响因素</a></p><h3 id="Golang-优点与缺点"><a href="#Golang-优点与缺点" class="headerlink" title="Golang 优点与缺点"></a>Golang 优点与缺点</h3><h4 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h4><ul><li>脚本化的语法,新手可以很容易上手。</li><li>静态类型和编译性,保证了运行效率</li><li>原生支持并发编程,可以更容易的编写并发程序,降低了开发和维护成本。</li></ul><h4 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h4><ul><li>语法糖少、第三方库较少。</li><li><p>语言设计上的特性缺陷,见<a href="https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/01.2.md#127-%E5%85%B3%E4%BA%8E%E7%89%B9%E6%80%A7%E7%BC%BA%E5%A4%B1" target="_blank" rel="noopener">这里</a></p></li><li><p>为了避免各种依赖问题,golang将runtime 一块打包进了二进制文件,导致文件体积大,部署比较困难。</p></li><li>golang 语言的错误处理机制。( // TODO 待考究 )</li></ul><h3 id="Golang-与-Python-比较的优点"><a href="#Golang-与-Python-比较的优点" class="headerlink" title="Golang 与 Python 比较的优点"></a>Golang 与 Python 比较的优点</h3><p>这里引用一段<a href="https://www.zhihu.com/question/21409296" target="_blank" rel="noopener">知乎</a>上某大牛的回答,如下:</p><p><strong>1.部署简单。</strong> Go 编译生成的是一个静态可执行文件,除了 glibc 外没有其他外部依赖。这让部署变得异常方便:目标机器上只需要一个基础的系统和必要的管理、监控工具,完全不需要操心应用所需的各种包、库的依赖关系,大大减轻了维护的负担。这和 Python 有着巨大的区别。由于历史的原因,Python 的部署工具生态相当混乱【比如 setuptools, distutils, pip, buildout 的不同适用场合以及兼容性问题】。官方 PyPI 源又经常出问题,需要搭建私有镜像,而维护这个镜像又要花费不少时间和精力。<br><strong>2.并发性好。</strong> Goroutine 和 channel 使得编写高并发的服务端软件变得相当容易,很多情况下完全不需要考虑锁机制以及由此带来的各种问题。单个 Go 应用也能有效的利用多个 CPU 核,并行执行的性能好。这和 Python 也是天壤之比。多线程和多进程的服务端程序编写起来并不简单,而且由于全局锁 GIL 的原因,多线程的 Python 程序并不能有效利用多核,只能用多进程的方式部署;如果用标准库里的 multiprocessing 包又会对监控和管理造成不少的挑战【我们用的 supervisor 管理进程,对 fork 支持不好】。部署 Python 应用的时候通常是每个 CPU 核部署一个应用,这会造成不少资源的浪费,比如假设某个 Python 应用启动后需要占用 100MB 内存,而服务器有 32 个 CPU 核,那么留一个核给系统、运行 31 个应用副本就要浪费 3GB 的内存资源。<br><strong>3.良好的语言设计。</strong> 从学术的角度讲 Go 语言其实非常平庸,不支持许多高级的语言特性;但从工程的角度讲,Go 的设计是非常优秀的:规范足够简单灵活,有其他语言基础的程序员都能迅速上手。更重要的是 Go 自带完善的工具链,大大提高了团队协作的一致性。比如 gofmt 自动排版 Go 代码,很大程度上杜绝了不同人写的代码排版风格不一致的问题。把编辑器配置成在编辑存档的时候自动运行 gofmt,这样在编写代码的时候可以随意摆放位置,存档的时候自动变成正确排版的代码。此外还有 gofix, govet 等非常有用的工具。<br><strong>4.执行性能好。</strong> 虽然不如 C 和 Java,但通常比原生 Python 应用还是高一个数量级的,适合编写一些瓶颈业务。内存占用也非常省。</p><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>通过阅读基本 Golang 入门的书籍,基本了解了 Golang 诞生的时机,及语言的特性、主要解决的问题和专注的方向,总结如下:</p><ul><li><p>诞生时机:人们需要一门 介于 C/C++ 和高级语言之间的新语言,既有C/C++的安全高效,又有高级语言的便捷性。</p></li><li><p>语言特性:高效、代码简洁、格式统一、并发性、垃圾回收。</p></li><li><p>主要解决问题:解决了部分系统语言与高级语言特性结合问题,如:运行性能、开发效率及维护难度。</p></li><li><p>专注方向:后台分布式、高并发中间件系统开发。</p></li></ul><h2 id="引申阅读"><a href="#引申阅读" class="headerlink" title="引申阅读"></a>引申阅读</h2><ul><li><a href="http://outofmemory.cn/golang/golang-beginer-note" target="_blank" rel="noopener">Go的50度灰:Golang新开发者要注意的陷阱和常见错误</a></li><li><a href="http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/" target="_blank" rel="noopener">Go的50度灰:Golang新开发者要注意的陷阱和常见错误- 英文原版</a></li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://startover.github.io/articles/2016/08/15/golang-for-pythonistas/" target="_blank" rel="noopener">Python 程序员的 Golang 学习指南(I): Go 之初体验</a></li><li><a href="https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/01.1.md" target="_blank" rel="noopener">The way to Go: 起源与发展</a></li><li><a href="https://www.imooc.com/learn/345" target="_blank" rel="noopener">视频:Go语言第一课 郝林</a></li></ul>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>第一次听说 Golang 语言是通过小米开源的 Open-Falcon ,它是用 Go 开发的,速度上是python的好几倍,天生适合并发程
</summary>
<category term="golang" scheme="http://pylixm.cc/categories/golang/"/>
<category term="golang" scheme="http://pylixm.cc/tags/golang/"/>
<category term="语言学习" scheme="http://pylixm.cc/tags/%E8%AF%AD%E8%A8%80%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>【 python 基础系列 】 - python 单元测试 unittest 工具使用</title>
<link href="http://pylixm.cc/posts/2018-01-18-python-unittest.html"/>
<id>http://pylixm.cc/posts/2018-01-18-python-unittest.html</id>
<published>2018-01-17T16:00:00.000Z</published>
<updated>2018-05-14T10:17:10.744Z</updated>
<content type="html"><![CDATA[<p>python 的单元测试有许多工具,如unittest、pytest、nosetest 等等,这里主要介绍下unittest 的使用。</p><blockquote><p>以下文字为转载,原文链接:<a href="https://www.cnblogs.com/yufeihlf/p/5707929.html" target="_blank" rel="noopener">https://www.cnblogs.com/yufeihlf/p/5707929.html</a></p></blockquote><p>unittest单元测试框架不仅可以适用于单元测试,还可以适用WEB自动化测试用例的开发与执行,该测试框架可组织执行测试用例,并且提供了丰富的断言方法,判断测试用例是否通过,最终生成测试结果。今天笔者就总结下如何使用unittest单元测试框架来进行WEB自动化测试。</p><h2 id="unittest模块的各个属性说明"><a href="#unittest模块的各个属性说明" class="headerlink" title="unittest模块的各个属性说明"></a>unittest模块的各个属性说明</h2><p>先来聊一聊unittest模块的各个属性,所谓知己知彼方能百战百胜,了解unittest的各个属性,对于后续编写用例有很大的帮助。</p><p><strong>1.unittest的属性如下:</strong></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">[<span class="string">'BaseTestSuite'</span>, <span class="string">'FunctionTestCase'</span>, <span class="string">'SkipTest'</span>, <span class="string">'TestCase'</span>, <span class="string">'TestLoader'</span>, <span class="string">'TestProgram'</span>, <span class="string">'TestResult'</span>, <span class="string">'TestSuite'</span>, <span class="string">'TextTestResult'</span>, <span class="string">'TextTestRunner'</span>, <span class="string">'_TextTestResult'</span>, <span class="string">'__all__'</span>, <span class="string">'__builtins__'</span>, <span class="string">'__doc__'</span>, <span class="string">'__file__'</span>, <span class="string">'__name__'</span>, <span class="string">'__package__'</span>, <span class="string">'__path__'</span>, <span class="string">'__unittest'</span>, <span class="string">'case'</span>, <span class="string">'defaultTestLoader'</span>, <span class="string">'expectedFailure'</span>, <span class="string">'findTestCases'</span>, <span class="string">'getTestCaseNames'</span>, <span class="string">'installHandler'</span>, <span class="string">'loader'</span>, <span class="string">'main'</span>, <span class="string">'makeSuite'</span>, <span class="string">'registerResult'</span>, <span class="string">'removeHandler'</span>, <span class="string">'removeResult'</span>, <span class="string">'result'</span>, <span class="string">'runner'</span>, <span class="string">'signals'</span>, <span class="string">'skip'</span>, <span class="string">'skipIf'</span>, <span class="string">'skipUnless'</span>, <span class="string">'suite'</span>, <span class="string">'util'</span>]</span><br></pre></td></tr></table></figure><p>说明:<br><code>unittest.TestCase</code>: TestCase类,所有测试用例类继承的基本类。</p><p><code>unittest.main()</code>: 使用她可以方便的将一个单元测试模块变为可直接运行的测试脚本,main()方法使用TestLoader类来搜索所有包含在该模块中以“test”命名开头的测试方法,并自动执行他们。执行方法的默认顺序是:根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9,A-Z,a-z。所以以A开头的测试用例方法会优先执行,以a开头会后执行。</p><p><code>unittest.TestSuite()</code>: unittest框架的TestSuite()类是用来创建测试套件的。</p><p><code>unittest.TextTextRunner()</code>: unittest框架的TextTextRunner()类,通过该类下面的run()方法来运行suite所组装的测试用例,入参为suite测试套件。</p><p><code>unittest.defaultTestLoader()</code>: defaultTestLoader()类,通过该类下面的discover()方法可自动根据测试目录start_dir匹配查找测试用例文件(test*.py),并将查找到的测试用例组装到测试套件,因此可以直接通过run()方法执行discover。用法如下:<br><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">discover=unittest.defaultTestLoader.discover(test_dir, pattern=<span class="string">'test_*.py'</span>)</span><br></pre></td></tr></table></figure></p><p><code>unittest.skip()</code>: 装饰器,当运行用例时,有些用例可能不想执行等,可用装饰器暂时屏蔽该条测试用例。一种常见的用法就是比如说想调试某一个测试用例,想先屏蔽其他用例就可以用装饰器屏蔽。</p><p>@unittest.skip(reason): skip(reason)装饰器:无条件跳过装饰的测试,并说明跳过测试的原因。</p><p>@unittest.skipIf(reason): skipIf(condition,reason)装饰器:条件为真时,跳过装饰的测试,并说明跳过测试的原因。</p><p>@unittest.skipUnless(reason): skipUnless(condition,reason)装饰器:条件为假时,跳过装饰的测试,并说明跳过测试的原因。</p><p>@unittest.expectedFailure(): expectedFailure()测试标记为失败。</p><p><strong>2.TestCase类的属性如下:</strong><br><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">[<span class="string">'__call__'</span>, <span class="string">'__class__'</span>, <span class="string">'__delattr__'</span>, <span class="string">'__dict__'</span>, <span class="string">'__doc__'</span>, <span class="string">'__eq__'</span>, <span class="string">'__format__'</span>, <span class="string">'__getattribute__'</span>, <span class="string">'__hash__'</span>, <span class="string">'__init__'</span>, <span class="string">'__module__'</span>, <span class="string">'__ne__'</span>, <span class="string">'__new__'</span>, <span class="string">'__reduce__'</span>, <span class="string">'__reduce_ex__'</span>, <span class="string">'__repr__'</span>, <span class="string">'__setattr__'</span>, <span class="string">'__sizeof__'</span>, <span class="string">'__str__'</span>, <span class="string">'__subclasshook__'</span>, <span class="string">'__weakref__'</span>, <span class="string">'_addSkip'</span>, <span class="string">'_baseAssertEqual'</span>, <span class="string">'_classSetupFailed'</span>, <span class="string">'_deprecate'</span>, <span class="string">'_diffThreshold'</span>, <span class="string">'_formatMessage'</span>, <span class="string">'_getAssertEqualityFunc'</span>, <span class="string">'_truncateMessage'</span>, <span class="string">'addCleanup'</span>, <span class="string">'addTypeEqualityFunc'</span>, <span class="string">'assertAlmostEqual'</span>, <span class="string">'assertAlmostEquals'</span>, <span class="string">'assertDictContainsSubset'</span>, <span class="string">'assertDictEqual'</span>, <span class="string">'assertEqual'</span>, <span class="string">'assertEquals'</span>, <span class="string">'assertFalse'</span>, <span class="string">'assertGreater'</span>, <span class="string">'assertGreaterEqual'</span>, <span class="string">'assertIn'</span>, <span class="string">'assertIs'</span>, <span class="string">'assertIsInstance'</span>, <span class="string">'assertIsNone'</span>, <span class="string">'assertIsNot'</span>, <span class="string">'assertIsNotNone'</span>, <span class="string">'assertItemsEqual'</span>, <span class="string">'assertLess'</span>, <span class="string">'assertLessEqual'</span>, <span class="string">'assertListEqual'</span>, <span class="string">'assertMultiLineEqual'</span>, <span class="string">'assertNotAlmostEqual'</span>, <span class="string">'assertNotAlmostEquals'</span>, <span class="string">'assertNotEqual'</span>, <span class="string">'assertNotEquals'</span>, <span class="string">'assertNotIn'</span>, <span class="string">'assertNotIsInstance'</span>, <span class="string">'assertNotRegexpMatches'</span>, <span class="string">'assertRaises'</span>, <span class="string">'assertRaisesRegexp'</span>, <span class="string">'assertRegexpMatches'</span>, <span class="string">'assertSequenceEqual'</span>, <span class="string">'assertSetEqual'</span>, <span class="string">'assertTrue'</span>, <span class="string">'assertTupleEqual'</span>, <span class="string">'assert_'</span>, <span class="string">'countTestCases'</span>, <span class="string">'debug'</span>, <span class="string">'defaultTestResult'</span>, <span class="string">'doCleanups'</span>, <span class="string">'fail'</span>, <span class="string">'failIf'</span>, <span class="string">'failIfAlmostEqual'</span>, <span class="string">'failIfEqual'</span>, <span class="string">'failUnless'</span>, <span class="string">'failUnlessAlmostEqual'</span>, <span class="string">'failUnlessEqual'</span>, <span class="string">'failUnlessRaises'</span>, <span class="string">'failureException'</span>, <span class="string">'id'</span>, <span class="string">'longMessage'</span>, <span class="string">'maxDiff'</span>, <span class="string">'run'</span>, <span class="string">'setUp'</span>, <span class="string">'setUpClass'</span>, <span class="string">'shortDescription'</span>, <span class="string">'skipTest'</span>, <span class="string">'tearDown'</span>, <span class="string">'tearDownClass'</span>]</span><br></pre></td></tr></table></figure></p><p>说明:</p><p>setUp():setUp()方法用于测试用例执行前的初始化工作。如测试用例中需要访问数据库,可以在setUp中建立数据库连接并进行初始化。如测试用例需要登录web,可以先实例化浏览器。</p><p>tearDown():tearDown()方法用于测试用例执行之后的善后工作。如关闭数据库连接。关闭浏览器。</p><p>assert*():一些断言方法:在执行测试用例的过程中,最终用例是否执行通过,是通过判断测试得到的实际结果和预期结果是否相等决定的。</p><p>assertEqual(a,b,[msg=’测试失败时打印的信息’]):断言a和b是否相等,相等则测试用例通过。</p><p>assertNotEqual(a,b,[msg=’测试失败时打印的信息’]):断言a和b是否相等,不相等则测试用例通过。</p><p>assertTrue(x,[msg=’测试失败时打印的信息’]):断言x是否True,是True则测试用例通过。</p><p>assertFalse(x,[msg=’测试失败时打印的信息’]):断言x是否False,是False则测试用例通过。</p><p>assertIs(a,b,[msg=’测试失败时打印的信息’]):断言a是否是b,是则测试用例通过。</p><p>assertNotIs(a,b,[msg=’测试失败时打印的信息’]):断言a是否是b,不是则测试用例通过。</p><p>assertIsNone(x,[msg=’测试失败时打印的信息’]):断言x是否None,是None则测试用例通过。</p><p>assertIsNotNone(x,[msg=’测试失败时打印的信息’]):断言x是否None,不是None则测试用例通过。</p><p>assertIn(a,b,[msg=’测试失败时打印的信息’]):断言a是否在b中,在b中则测试用例通过。</p><p>assertNotIn(a,b,[msg=’测试失败时打印的信息’]):断言a是否在b中,不在b中则测试用例通过。</p><p>assertIsInstance(a,b,[msg=’测试失败时打印的信息’]):断言a是是b的一个实例,是则测试用例通过。</p><p>assertNotIsInstance(a,b,[msg=’测试失败时打印的信息’]):断言a是是b的一个实例,不是则测试用例通过。</p><p><strong>3.TestSuite类的属性如下:(组织用例时需要用到)</strong></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">[<span class="string">'__call__'</span>, <span class="string">'__class__'</span>, <span class="string">'__delattr__'</span>, <span class="string">'__dict__'</span>, <span class="string">'__doc__'</span>, <span class="string">'__eq__'</span>, <span class="string">'__format__'</span>, <span class="string">'__getattribute__'</span>, <span class="string">'__hash__'</span>, <span class="string">'__init__'</span>, <span class="string">'__iter__'</span>, <span class="string">'__module__'</span>, <span class="string">'__ne__'</span>, <span class="string">'__new__'</span>, <span class="string">'__reduce__'</span>, <span class="string">'__reduce_ex__'</span>, <span class="string">'__repr__'</span>, <span class="string">'__setattr__'</span>, <span class="string">'__sizeof__'</span>, <span class="string">'__str__'</span>, <span class="string">'__subclasshook__'</span>, <span class="string">'__weakref__'</span>, <span class="string">'_addClassOrModuleLevelException'</span>, <span class="string">'_get_previous_module'</span>, <span class="string">'_handleClassSetUp'</span>, <span class="string">'_handleModuleFixture'</span>, <span class="string">'_handleModuleTearDown'</span>, <span class="string">'_tearDownPreviousClass'</span>, <span class="string">'_tests'</span>, <span class="string">'addTest'</span>, <span class="string">'addTests'</span>, <span class="string">'countTestCases'</span>, <span class="string">'debug'</span>, <span class="string">'run'</span>]</span><br></pre></td></tr></table></figure><p>说明:</p><p>addTest(): addTest()方法是将测试用例添加到测试套件中,如下方,是将test_baidu模块下的BaiduTest类下的test_baidu测试用例添加到测试套件。</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">suite = unittest.TestSuite()</span><br><span class="line">suite.addTest(test_baidu.BaiduTest(<span class="string">'test_baidu'</span>))</span><br></pre></td></tr></table></figure><p><strong>4.TextTextRunner的属性如下:(组织用例时需要用到)</strong></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">[<span class="string">'__class__'</span>, <span class="string">'__delattr__'</span>, <span class="string">'__dict__'</span>, <span class="string">'__doc__'</span>, <span class="string">'__format__'</span>, <span class="string">'__getattribute__'</span>, <span class="string">'__hash__'</span>, <span class="string">'__init__'</span>, <span class="string">'__module__'</span>, <span class="string">'__new__'</span>, <span class="string">'__reduce__'</span>, <span class="string">'__reduce_ex__'</span>, <span class="string">'__repr__'</span>, <span class="string">'__setattr__'</span>, <span class="string">'__sizeof__'</span>, <span class="string">'__str__'</span>, <span class="string">'__subclasshook__'</span>, <span class="string">'__weakref__'</span>, <span class="string">'_makeResult'</span>, <span class="string">'buffer'</span>, <span class="string">'descriptions'</span>, <span class="string">'failfast'</span>, <span class="string">'resultclass'</span>, <span class="string">'run'</span>, <span class="string">'stream'</span>, <span class="string">'verbosity'</span>]</span><br></pre></td></tr></table></figure><p>说明:</p><p>run(): run()方法是运行测试套件的测试用例,入参为suite测试套件。</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">runner = unittest.TextTestRunner()</span><br><span class="line">runner.run(suite)</span><br></pre></td></tr></table></figure><h2 id="使用unittest框架编写测试用例思路"><a href="#使用unittest框架编写测试用例思路" class="headerlink" title="使用unittest框架编写测试用例思路"></a>使用unittest框架编写测试用例思路</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><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</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">#1.先设置编码,utf-8可支持中英文,如上,一般放在第一行</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#2.注释:包括记录创建时间,创建人,项目名称。</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Created on 2016-7-27</span></span><br><span class="line"><span class="string">@author: Jennifer</span></span><br><span class="line"><span class="string">Project:使用unittest框架编写测试用例思路</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="comment">#3.导入unittest模块</span></span><br><span class="line"><span class="keyword">import</span> unittest</span><br><span class="line"></span><br><span class="line"><span class="comment">#4.定义测试类,父类为unittest.TestCase。</span></span><br><span class="line"><span class="comment">#可继承unittest.TestCase的方法,如setUp和tearDown方法,不过此方法可以在子类重写,覆盖父类方法。</span></span><br><span class="line"><span class="comment">#可继承unittest.TestCase的各种断言方法。</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test</span><span class="params">(unittest.TestCase)</span>:</span> </span><br><span class="line"> </span><br><span class="line"><span class="comment">#5.定义setUp()方法用于测试用例执行前的初始化工作。</span></span><br><span class="line"><span class="comment">#注意,所有类中方法的入参为self,定义方法的变量也要“self.变量”</span></span><br><span class="line"><span class="comment">#注意,输入的值为字符型的需要转为int型</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">setUp</span><span class="params">(self)</span>:</span></span><br><span class="line"> self.number=raw_input(<span class="string">'Enter a number:'</span>)</span><br><span class="line"> self.number=int(self.number)</span><br><span class="line"></span><br><span class="line"><span class="comment">#6.定义测试用例,以“test_”开头命名的方法</span></span><br><span class="line"><span class="comment">#注意,方法的入参为self</span></span><br><span class="line"><span class="comment">#可使用unittest.TestCase类下面的各种断言方法用于对测试结果的判断</span></span><br><span class="line"><span class="comment">#可定义多个测试用例</span></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">test_case1</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">print</span> self.number</span><br><span class="line"> self.assertEqual(self.number,<span class="number">10</span>,msg=<span class="string">'Your input is not 10'</span>)</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">test_case2</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">print</span> self.number</span><br><span class="line"> self.assertEqual(self.number,<span class="number">20</span>,msg=<span class="string">'Your input is not 20'</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta"> @unittest.skip('暂时跳过用例3的测试')</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">test_case3</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">print</span> self.number</span><br><span class="line"> self.assertEqual(self.number,<span class="number">30</span>,msg=<span class="string">'Your input is not 30'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">#7.定义tearDown()方法用于测试用例执行之后的善后工作。</span></span><br><span class="line"><span class="comment">#注意,方法的入参为self</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">tearDown</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">print</span> <span class="string">'Test over'</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">#8如果直接运行该文件(__name__值为__main__),则执行以下语句,常用于测试脚本是否能够正常运行</span></span><br><span class="line"><span class="keyword">if</span> __name__==<span class="string">'__main__'</span>:</span><br><span class="line"><span class="comment">#8.1执行测试用例方案一如下:</span></span><br><span class="line"><span class="comment">#unittest.main()方法会搜索该模块下所有以test开头的测试用例方法,并自动执行它们。</span></span><br><span class="line"><span class="comment">#执行顺序是命名顺序:先执行test_case1,再执行test_case2</span></span><br><span class="line"> unittest.main()</span><br><span class="line"></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">#8.2执行测试用例方案二如下:</span></span><br><span class="line"><span class="string">#8.2.1先构造测试集</span></span><br><span class="line"><span class="string">#8.2.1.1实例化测试套件</span></span><br><span class="line"><span class="string"> suite=unittest.TestSuite()</span></span><br><span class="line"><span class="string">#8.2.1.2将测试用例加载到测试套件中。</span></span><br><span class="line"><span class="string">#执行顺序是安装加载顺序:先执行test_case2,再执行test_case1</span></span><br><span class="line"><span class="string"> suite.addTest(Test('test_case2'))</span></span><br><span class="line"><span class="string"> suite.addTest(Test('test_case1'))</span></span><br><span class="line"><span class="string">#8.2.2执行测试用例</span></span><br><span class="line"><span class="string">#8.2.2.1实例化TextTestRunner类</span></span><br><span class="line"><span class="string"> runner=unittest.TextTestRunner()</span></span><br><span class="line"><span class="string">#8.2.2.2使用run()方法运行测试套件(即运行测试套件中的所有用例)</span></span><br><span class="line"><span class="string"> runner.run(suite)</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"> </span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">#8.3执行测试用例方案三如下:</span></span><br><span class="line"><span class="string">#8.3.1构造测试集(简化了方案二中先要创建测试套件然后再依次加载测试用例)</span></span><br><span class="line"><span class="string">#执行顺序同方案一:执行顺序是命名顺序:先执行test_case1,再执行test_case2</span></span><br><span class="line"><span class="string"> test_dir = './'</span></span><br><span class="line"><span class="string"> discover = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py')</span></span><br><span class="line"><span class="string">#8.3.2执行测试用例</span></span><br><span class="line"><span class="string">#8.3.2.1实例化TextTestRunner类</span></span><br><span class="line"><span class="string"> runner=unittest.TextTestRunner()</span></span><br><span class="line"><span class="string">#8.3.2.2使用run()方法运行测试套件(即运行测试套件中的所有用例)</span></span><br><span class="line"><span class="string"> runner.run(discover) </span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"></span><br><span class="line"><span class="comment">## 使用unittest框架编写测试用例实例</span></span><br><span class="line"></span><br><span class="line">**百度搜索测试用例Test Case:**</span><br><span class="line">```python</span><br><span class="line"><span class="comment"># coding=utf-8</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Created on 2016-7-22</span></span><br><span class="line"><span class="string">@author: Jennifer</span></span><br><span class="line"><span class="string">Project:登录百度测试用例</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="keyword">from</span> selenium <span class="keyword">import</span> webdriver</span><br><span class="line"><span class="keyword">import</span> unittest, time</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BaiduTest</span><span class="params">(unittest.TestCase)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">setUp</span><span class="params">(self)</span>:</span></span><br><span class="line"> self.driver = webdriver.Firefox()</span><br><span class="line"> self.driver.implicitly_wait(<span class="number">30</span>) <span class="comment">#隐性等待时间为30秒</span></span><br><span class="line"> self.base_url = <span class="string">"https://www.baidu.com"</span></span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">test_baidu</span><span class="params">(self)</span>:</span></span><br><span class="line"> driver = self.driver</span><br><span class="line"> driver.get(self.base_url + <span class="string">"/"</span>)</span><br><span class="line"> driver.find_element_by_id(<span class="string">"kw"</span>).clear()</span><br><span class="line"> driver.find_element_by_id(<span class="string">"kw"</span>).send_keys(<span class="string">"unittest"</span>)</span><br><span class="line"> driver.find_element_by_id(<span class="string">"su"</span>).click()</span><br><span class="line"> time.sleep(<span class="number">3</span>)</span><br><span class="line"> title=driver.title</span><br><span class="line"> self.assertEqual(title, <span class="string">u"unittest_百度搜索"</span>) </span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">tearDown</span><span class="params">(self)</span>:</span></span><br><span class="line"> self.driver.quit()</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"> unittest.main()</span><br></pre></td></tr></table></figure><p><strong>有道翻译测试用例Test Case:</strong><br><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"># coding=utf-8</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Created on 2016-7-22</span></span><br><span class="line"><span class="string">@author: Jennifer</span></span><br><span class="line"><span class="string">Project:使用有道翻译测试用例</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="keyword">from</span> selenium <span class="keyword">import</span> webdriver</span><br><span class="line"><span class="keyword">import</span> unittest, time</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">YoudaoTest</span><span class="params">(unittest.TestCase)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">setUp</span><span class="params">(self)</span>:</span></span><br><span class="line"> self.driver = webdriver.Firefox()</span><br><span class="line"> self.driver.implicitly_wait(<span class="number">30</span>) <span class="comment">#隐性等待时间为30秒</span></span><br><span class="line"> self.base_url = <span class="string">"http://www.youdao.com"</span></span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">test_youdao</span><span class="params">(self)</span>:</span></span><br><span class="line"> driver = self.driver</span><br><span class="line"> driver.get(self.base_url + <span class="string">"/"</span>)</span><br><span class="line"> driver.find_element_by_id(<span class="string">"translateContent"</span>).clear()</span><br><span class="line"> driver.find_element_by_id(<span class="string">"translateContent"</span>).send_keys(<span class="string">u"你好"</span>)</span><br><span class="line"> driver.find_element_by_id(<span class="string">"translateContent"</span>).submit()</span><br><span class="line"> time.sleep(<span class="number">3</span>)</span><br><span class="line"> page_source=driver.page_source</span><br><span class="line"> self.assertIn( <span class="string">"hello"</span>,page_source) </span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">tearDown</span><span class="params">(self)</span>:</span></span><br><span class="line"> self.driver.quit()</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"> unittest.main()</span><br></pre></td></tr></table></figure></p><p><strong>web测试用例:通过测试套件TestSuite来组装多个测试用例。</strong><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># coding=utf-8</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">Created on 2016-7-26</span></span><br><span class="line"><span class="string">@author: Jennifer</span></span><br><span class="line"><span class="string">Project:编写Web测试用例</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="keyword">import</span> unittest</span><br><span class="line"><span class="keyword">from</span> test_case <span class="keyword">import</span> test_baidu</span><br><span class="line"><span class="keyword">from</span> test_case <span class="keyword">import</span> test_youdao</span><br><span class="line"></span><br><span class="line"><span class="comment">#构造测试集</span></span><br><span class="line">suite = unittest.TestSuite()</span><br><span class="line">suite.addTest(test_baidu.BaiduTest(<span class="string">'test_baidu'</span>))</span><br><span class="line">suite.addTest(test_youdao.YoudaoTest(<span class="string">'test_youdao'</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 class="comment">#执行测试</span></span><br><span class="line"> runner = unittest.TextTestRunner()</span><br><span class="line"> runner.run(suite)</span><br></pre></td></tr></table></figure></p><p>测试结果:<br><figure class="image-bubble"> <div class="img-lightbox"> <div class="overlay"></div> <img src="/static/imgs/test_case_ret.jpg" alt="" title=""> </div> <div class="image-caption"></div> </figure></p><p>说明:.代表用例执行通过,两个点表示两个用例执行通过。F表示用例执行不通过。</p>]]></content>
<summary type="html">
<p>python 的单元测试有许多工具,如unittest、pytest、nosetest 等等,这里主要介绍下unittest 的使用。</p>
<blockquote>
<p>以下文字为转载,原文链接:<a href="https://www.cnblogs.com/yuf
</summary>
<category term="python" scheme="http://pylixm.cc/categories/python/"/>
<category term="python" scheme="http://pylixm.cc/tags/python/"/>
<category term="unittest" scheme="http://pylixm.cc/tags/unittest/"/>
</entry>
<entry>
<title>【 python 基础系列 】 - pipenv 试用过程分享</title>
<link href="http://pylixm.cc/posts/2018-01-13-python-pipenv.html"/>
<id>http://pylixm.cc/posts/2018-01-13-python-pipenv.html</id>
<published>2018-01-12T16:00:00.000Z</published>
<updated>2018-05-14T09:23:37.736Z</updated>
<content type="html"><![CDATA[<p>最近常看到<code>pipenv</code>这个管理工具,今天有时间查了下,是 <a href="https://www.kennethreitz.org/values" target="_blank" rel="noopener">Kennethreitz</a> 大神的作品,看了下github的仓库,是2017年1月份创建的,仅仅一年的时间变获得了7k+的收藏,最新一次的提交时间为2天前,可见该仓库活跃程度。自己之前写过一篇文章<a href="http://www.pylixm.cc/posts/2016-06-19-Virtualenv-install.html" target="_blank" rel="noopener">《使用 pyenv + virtualenv 打造多版本python开发环境》</a>,遗留个问题,一直没有找到合理的同时管理python和python依赖包的工具,试用了下 <code>pipenv</code>,可以说完美的解决了python版本及包的管理问题。并且<code>pipebv</code> 还是<code>Python.org</code>正式推荐的python包管理工具。原文如下:</p><blockquote><p>Pipenv — the officially recommended Python packaging tool from Python.org, free (as in freedom).</p></blockquote><p>那么接下来,分享下我的试用过程,供大家参考:</p><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><h3 id="试用环境及相关文档"><a href="#试用环境及相关文档" class="headerlink" title="试用环境及相关文档"></a>试用环境及相关文档</h3><p><strong>环境</strong></p><ul><li>pipenv 9.0.1 </li><li>python3.6</li><li>python2.7</li></ul><p><strong>文档</strong></p><ul><li>github仓库地址:<a href="https://github.com/pypa/pipenv" target="_blank" rel="noopener">pipenv</a> </li><li><a href="https://docs.pipenv.org/" target="_blank" rel="noopener">pipenv 官方文档</a></li></ul><h3 id="pipenv-基本概念理解"><a href="#pipenv-基本概念理解" class="headerlink" title="pipenv 基本概念理解"></a>pipenv 基本概念理解</h3><ol><li>之前我们使用pip + virtualenv 来管理python依赖包,使用 <code>--python=</code>参数来区分python版本(不再使用pyenv,减少包依赖)。而pipenv的思路简单理解便是把pip和virutalenv 2个工具统一起来,使用 <code>pipenv</code> 来代替。</li><li><code>pipenv</code> 使用 Pipfile 来代替 requirement.txt 文件记录python包。</li><li>增加了<code>Pipfile.lock</code> 文件来锁定python软件的包名及版本,以及其依赖关系的列表。</li><li>它参考了其他语言的包管理工具(bundler, composer, npm, cargo, yarn, etc.),旨在将最好的包管理工具带入python世界。</li></ol><h2 id="pipenv-功能试用"><a href="#pipenv-功能试用" class="headerlink" title="pipenv 功能试用"></a>pipenv 功能试用</h2><h3 id="pipenv-安装"><a href="#pipenv-安装" class="headerlink" title="pipenv 安装"></a>pipenv 安装</h3><h4 id="普通安装"><a href="#普通安装" class="headerlink" title="普通安装"></a>普通安装</h4><p><code>pipenv</code> 可使用 pip 直接安装。</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">pip install pipenv</span><br></pre></td></tr></table></figure><p>作者推荐在<code>python3</code>下边安装,会提高与virtualenv的兼容性。</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">The use of Python 3 is highly preferred over Python 2, when installing Pipenv. Compatibility with three virtualenvs is greatly improved when using Python 3 as the installation target.</span><br><span class="line"></span><br><span class="line">—Kenneth Reitz</span><br></pre></td></tr></table></figure><h4 id="用户模式安装"><a href="#用户模式安装" class="headerlink" title="用户模式安装"></a>用户模式安装</h4><p>为防止和系统python库产生影响,可使用此种方案安装。<br><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">pip install --user pipenv</span><br></pre></td></tr></table></figure></p><p>pip 默认安装包路径为<code>/usr/local/lib/python2.7/site-packages</code>。此模式下,pip安装包保存路径为用户库路径,一般为<code>/Users/pylixm/Library/Python/3.6/lib/python/site-packages</code>, 可使用命令<code>python3 -m site --user-site</code> 具体查看。如果在安装后你的shell中pipenv不可用,你需要把用户库的二进制目录<code>/Users/pylixm/Library/Python/3.6/bin</code>添加到你的PATH中。</p><h3 id="pipenv-使用"><a href="#pipenv-使用" class="headerlink" title="pipenv 使用"></a>pipenv 使用</h3><h4 id="初始化虚拟环境"><a href="#初始化虚拟环境" class="headerlink" title="初始化虚拟环境"></a>初始化虚拟环境</h4><p>执行<code>pipenv install</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></pre></td><td class="code"><pre><span class="line">~/laboratory/pip_test_project ⌚ 20:42:10</span><br><span class="line">$ pipenv install</span><br><span class="line">Creating a virtualenv for this project…</span><br><span class="line">⠋New python executable in /Users/pylixm/.local/share/virtualenvs/pip_test_project-MXA0TC90/bin/python2.7</span><br><span class="line">Also creating executable in /Users/pylixm/.local/share/virtualenvs/pip_test_project-MXA0TC90/bin/python</span><br><span class="line">Installing setuptools, pip, wheel...done.</span><br><span class="line"></span><br><span class="line">Virtualenv location: /Users/pylixm/.local/share/virtualenvs/pip_test_project-MXA0TC90</span><br><span class="line">Creating a Pipfile for this project…</span><br><span class="line">Pipfile.lock not found, creating…</span><br><span class="line">Locking [dev-packages] dependencies…</span><br><span class="line">Locking [packages] dependencies…</span><br><span class="line">Updated Pipfile.lock (c23e27)!</span><br><span class="line">Installing dependencies from Pipfile.lock (c23e27)…</span><br><span class="line">▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 0/0 — 00:00:00</span><br><span class="line">To activate this project's virtualenv, run the following:</span><br><span class="line"> $ pipenv shell</span><br></pre></td></tr></table></figure><p>从打印信息可见,它在目录用户目录<code>.local</code>下创建了个和项目同名的虚拟环境(可通过配置环境变量来自定义虚拟环境目录,<code>export WORKON_HOME=~/.venvs</code>),python使用的是默认的python2.7 。<br>可通过参数<code>--two</code> 和<code>--three</code> 来泛指python版本,也可通过<code>--python 3.5</code> 来明确知道python版本,但是这些参数的前提是你系统上有此python版本,否则会报如下错误:<br><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">$ pipenv --python 3.5</span><br><span class="line">Warning: Python 3.5 was not found on your system…</span><br><span class="line">You can specify specific versions of Python with:</span><br><span class="line"> $ pipenv --python path/to/python</span><br></pre></td></tr></table></figure></p><p>有点像 virtualenv 的 <code>--python</code>参数。</p><p>初始化好虚拟环境后,会在项目目录下生成2个文件<code>Pipfile</code>和<code>Pipfile.lock</code>。为pipenv包的配置文件,代替原来的 requirement.txt。项目提交时,可将<code>Pipfile</code> 文件和<code>Pipfile.lock</code>文件受控提交,待其他开发克隆下载,根据此Pipfile 运行命令<code>pipenv install [--dev]</code>生成自己的虚拟环境。</p><p><code>Pipfile.lock</code> 文件是通过hash算法将包的名称和版本,及依赖关系生成哈希值,可以保证包的完整性。</p><h4 id="安装python模块"><a href="#安装python模块" class="headerlink" title="安装python模块"></a>安装python模块</h4><h5 id="正常安装"><a href="#正常安装" class="headerlink" title="正常安装"></a>正常安装</h5><p>安装 <code>requests</code> 模块:<br><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></pre></td><td class="code"><pre><span class="line">$ pipenv install requests</span><br><span class="line">Installing requests…</span><br><span class="line">Collecting requests</span><br><span class="line"> Using cached requests-2.18.4-py2.py3-none-any.whl</span><br><span class="line">Collecting certifi>=2017.4.17 (from requests)</span><br><span class="line"> Using cached certifi-2017.11.5-py2.py3-none-any.whl</span><br><span class="line">Collecting idna<2.7,>=2.5 (from requests)</span><br><span class="line"> Using cached idna-2.6-py2.py3-none-any.whl</span><br><span class="line">Collecting urllib3<1.23,>=1.21.1 (from requests)</span><br><span class="line"> Using cached urllib3-1.22-py2.py3-none-any.whl</span><br><span class="line">Collecting chardet<3.1.0,>=3.0.2 (from requests)</span><br><span class="line"> Using cached chardet-3.0.4-py2.py3-none-any.whl</span><br><span class="line">Installing collected packages: certifi, idna, urllib3, chardet, requests</span><br><span class="line">Successfully installed certifi-2017.11.5 chardet-3.0.4 idna-2.6 requests-2.18.4 urllib3-1.22</span><br><span class="line"></span><br><span class="line">Adding requests to Pipfile's [packages]…</span><br><span class="line"> PS: You have excellent taste!</span><br><span class="line">Locking [dev-packages] dependencies…</span><br><span class="line">Locking [packages] dependencies…</span><br><span class="line">Updated Pipfile.lock (2f8679)!</span><br></pre></td></tr></table></figure></p><p>可通过命令<code>pipenv graph</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></pre></td><td class="code"><pre><span class="line">$ pipenv graph</span><br><span class="line">requests==2.18.4</span><br><span class="line"> - certifi [required: >=2017.4.17, installed: 2017.11.5]</span><br><span class="line"> - chardet [required: <3.1.0,>=3.0.2, installed: 3.0.4]</span><br><span class="line"> - idna [required: >=2.5,<2.7, installed: 2.6]</span><br><span class="line"> - urllib3 [required: >=1.21.1,<1.23, installed: 1.22]</span><br></pre></td></tr></table></figure><h5 id="只安装开发环境"><a href="#只安装开发环境" class="headerlink" title="只安装开发环境"></a>只安装开发环境</h5><p>可通过以下命令,仅安装在开发环境,<br><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">pipenv install --dev requests --three</span><br></pre></td></tr></table></figure></p><p>区别反映在<code>Pipfile</code> 上为:<br><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></pre></td><td class="code"><pre><span class="line">[[source]]</span><br><span class="line"></span><br><span class="line">url = "https://pypi.python.org/simple"</span><br><span class="line">verify_ssl = true</span><br><span class="line">name = "pypi"</span><br><span class="line"></span><br><span class="line">[dev-packages]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">[packages]</span><br><span class="line"></span><br><span class="line">requests = "*"</span><br><span class="line">flask = "==0.10"</span><br><span class="line"></span><br><span class="line">[requires]</span><br><span class="line">python_version = "3.6"</span><br></pre></td></tr></table></figure></p><p>安装包记录是在<code>[dev-packages]</code> 部分,还是<code>[packages]</code> 部分。在安装时,指定<code>--dev</code>参数,则只安装<code>[dev-packages]</code>下的包,若安装时不定指定<code>--dev</code>参数,只会安装<code>[packages]</code> 包下面的模块。</p><p><code>[requires]</code> 下的python在构建新的虚拟环境时,若没有会自动下载安装。</p><h5 id="通过-requirements-txt-安装"><a href="#通过-requirements-txt-安装" class="headerlink" title="通过 requirements.txt 安装"></a>通过 requirements.txt 安装</h5><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">pipenv install -r requirements.txt</span><br></pre></td></tr></table></figure><p>这样我们可以重用之前的requirement.txt 文件来构建我们新的开发环境,可以把我们的项目顺利的迁到pipenv。</p><p>可通过以下命令生成requirements 文件:<br><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">pipenv lock -r [--dev] > requirements.txt</span><br></pre></td></tr></table></figure></p><h4 id="运行虚拟环境"><a href="#运行虚拟环境" class="headerlink" title="运行虚拟环境"></a>运行虚拟环境</h4><p>可使用以下命令来运行项目:<br><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">pipenv run python xxx.py</span><br></pre></td></tr></table></figure></p><p>或者启动虚拟环境的shell环境:<br><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></pre></td><td class="code"><pre><span class="line">~/laboratory/pip_test_project</span><br><span class="line">$ pipenv shell --anyway</span><br><span class="line">Spawning environment shell (/bin/zsh). Use 'exit' to leave.</span><br><span class="line">source /Users/pylixm/.local/share/virtualenvs/pip_test_project-MXA0TC90/bin/activate</span><br><span class="line"></span><br><span class="line">~/laboratory/pip_test_project </span><br><span class="line">$ source /Users/pylixm/.local/share/virtualenvs/pip_test_project-MXA0TC90/bin/activate</span><br><span class="line">(pip_test_project-MXA0TC90)</span><br><span class="line">~/laboratory/pip_test_project</span><br><span class="line">$ exit</span><br><span class="line"></span><br><span class="line">~/laboratory/pip_test_project</span><br><span class="line">$ pipenv shell</span><br><span class="line">Shell for UNKNOWN_VIRTUAL_ENVIRONMENT already activated.</span><br><span class="line">No action taken to avoid nested environments.</span><br></pre></td></tr></table></figure></p><p>直接运行<code>pipenv shell</code> 并不会出现shell命令行,是应为没有配置环境变量。还需要进一步研究,貌似需要配置环境变了,一直没找到。</p><p><code>pipenv</code> 提供了<code>.env</code> 文件,放在项目目录下,提供项目所需的环境变量,运行<code>pipenv shell</code> 时,会自动加载。</p><h4 id="删除虚拟环境及包"><a href="#删除虚拟环境及包" class="headerlink" title="删除虚拟环境及包"></a>删除虚拟环境及包</h4><p>删除包:<br><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">pipenv uninstall reuqests</span><br></pre></td></tr></table></figure></p><p>删除虚拟环境:<br><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">pipenv --rm</span><br></pre></td></tr></table></figure></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li><code>pipenv</code> 完美的解决了python的包和版本的管理。</li><li>并对包之间的依赖关系也管理起来,方便了开发者构建自己的开发运行环境。</li></ul><p>时间有限,以上列举的仅为部分功能,更多的强大功能详见<a href="https://docs.pipenv.org/" target="_blank" rel="noopener">官方文档</a>。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://docs.pipenv.org/" target="_blank" rel="noopener">https://docs.pipenv.org/</a></li></ul>]]></content>
<summary type="html">
<p>最近常看到<code>pipenv</code>这个管理工具,今天有时间查了下,是 <a href="https://www.kennethreitz.org/values" target="_blank" rel="noopener">Kennethreitz</a> 大
</summary>
<category term="python" scheme="http://pylixm.cc/categories/python/"/>
<category term="python" scheme="http://pylixm.cc/tags/python/"/>
<category term="pipenv" scheme="http://pylixm.cc/tags/pipenv/"/>
</entry>
<entry>
<title>2017 年总结,2018 年计划</title>
<link href="http://pylixm.cc/posts/2018-01-04-Summary_2018.html"/>
<id>http://pylixm.cc/posts/2018-01-04-Summary_2018.html</id>
<published>2018-01-03T16:00:00.000Z</published>
<updated>2018-02-09T08:12:37.000Z</updated>
<content type="html"><![CDATA[<p>2017 ,一年下来做了不少事情,遇到了很多挑战,现在看来最算是把它们都克服了。这充分说明了当时我想到的那句话“不管多大的困难,咬咬牙总会过去的“。只要坚持下来了,无论结果如何,都是一种成长。</p><p>2017 年,可以说是自己飞速成长的一年,无论是项目管理的软技能还是编码技术的硬技能都有所提升。下面来总结提炼下,好敦促自己成长。</p><h2 id="2017-总结"><a href="#2017-总结" class="headerlink" title="2017 总结"></a>2017 总结</h2><h3 id="2017-年里程碑"><a href="#2017-年里程碑" class="headerlink" title="2017 年里程碑"></a>2017 年里程碑</h3><ul><li>有了baby;</li><li>买了人生第一栋房子;</li><li>工资水平上一个新台阶;</li><li>对工作,对技术有了新的认知;</li><li>写了几篇阅读量还不错的文章;</li></ul><h3 id="2017年的不足之处"><a href="#2017年的不足之处" class="headerlink" title="2017年的不足之处"></a>2017年的不足之处</h3><ul><li>阅读时间偏少,没有深入完整体系的阅读一本书籍。</li><li>时间利用率低下,空余时间多浪费。</li><li>月季度总结不够。</li><li>学习不够。</li></ul><h3 id="2017-年计划完成情况"><a href="#2017-年计划完成情况" class="headerlink" title="2017 年计划完成情况"></a>2017 年计划完成情况</h3><p><strong>个人管理</strong></p><ul><li>阅读时间管理的书籍,增强自己时间管理的技能。【50%】</li><li>使用eventnote记录每天的工作日志,wunderlist做事件提醒,每天下班前做review,并写下明天的任务。【70%】</li><li>向身边所有的人学习。【70%】</li><li>多参加集体活动,增强自己的自信。【50%】</li><li>抓紧一切时间,补充自己的专业知识。【50%】</li><li>坚持写原创博客,1个月写一篇高质量的的文章。【80%】</li><li>每月,每季度做思考,反思和整理更新自己的计划。【60%】</li></ul><p><strong>读书计划</strong></p><ul><li>《尽管去做》【50%】</li><li>《时间管理》【0%】</li></ul><p><strong>学习计划</strong></p><ul><li>学习go 语言,并实现简单小项目【0%】</li><li>学习 openstack基础知识 【0%】</li><li>学习docker基础及集群管理知识 【80%】</li><li>学习flask 、tornado 框架 【100%】</li><li>学习 vue 框架的使用 【100%】</li><li>多参加各种学习分享会议,听取更多的业内解决方案。【60%】</li><li>整理编写一个自己的前端+后端的脚手架项目 【0%】</li></ul><p><strong>生活计划</strong></p><ul><li>保持健康 【60%, 今年体检增加了好几项不合格指标】</li><li>调整作息 【60%】</li><li>多活动,减肥 【50%】</li></ul><p><strong>总结</strong></p><p>2017年计划完成度整体在60%左右,通过分析计划,自己在整体的时间规划和分配上还是缺乏管理能力,执行力不够,2018年需要加强。</p><p>为加强2018年计划的执行力度,将计划单独一页,没月或季度试试更新。</p><h3 id="2018年计划"><a href="#2018年计划" class="headerlink" title="2018年计划"></a>2018年计划</h3><h4 id="关键字"><a href="#关键字" class="headerlink" title="关键字"></a>关键字</h4><ul><li>执行力</li><li>健康 </li><li>学习</li><li>责任</li></ul><p>详细计划,见 <a href="">这里</a></p>]]></content>
<summary type="html">
<p>2017 ,一年下来做了不少事情,遇到了很多挑战,现在看来最算是把它们都克服了。这充分说明了当时我想到的那句话“不管多大的困难,咬咬牙总会过去的“。只要坚持下来了,无论结果如何,都是一种成长。</p>
<p>2017 年,可以说是自己飞速成长的一年,无论是项目管理的软技能还
</summary>
<category term="summary" scheme="http://pylixm.cc/categories/summary/"/>
<category term="summary" scheme="http://pylixm.cc/tags/summary/"/>
</entry>
<entry>
<title>centos 7 下 postgreSQL 10 安装记录</title>
<link href="http://pylixm.cc/posts/2017-11-05-postgresql-install.html"/>
<id>http://pylixm.cc/posts/2017-11-05-postgresql-install.html</id>
<published>2017-11-04T16:00:00.000Z</published>
<updated>2017-11-05T15:35:49.000Z</updated>
<content type="html"><![CDATA[<p>最近工作中与数据库打交道越来越多,操作数据频繁,感觉到了许多mysql操作的不便利性。想起了几年前使用的 <code>postgreSQL</code>, 重新整理安装下,记录备查。</p><p>关于<code>mysql</code>和 <code>postgreSQL</code> 的对比可见知乎的这个问题<a href="https://www.zhihu.com/question/20010554" target="_blank" rel="noopener">PostgreSQL 与 MySQL 相比,优势何在?</a>。</p><p>看了下postgreSQL的发行版本,除了核心版本外还有针对大数据分析和虚拟化技术的分支版本,发展紧跟时代步伐,详见官方<a href="https://www.postgresql.org/download/" target="_blank" rel="noopener">这里</a>。对于postgreSQL的安装官方提供了许多方法,支持yum 安装、rpm安装、源码编译安装等方式。我这里采用yum 源安装,其他方式可参考<a href="https://www.postgresql.org/download/linux/redhat/" target="_blank" rel="noopener">官方文档</a>。安装步骤如下:</p><h2 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h2><p>检查系统是否安装了postgresSQL。若安装了需要卸载,清理干净,防止造成安装时不必要的问题。<br><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"><span class="comment"># 检查是否安装 </span></span><br><span class="line">rpm -qa | grep postgres <span class="comment"># 检查PostgreSQL 是否已经安装</span></span><br><span class="line">rpm -qal | grep postgres <span class="comment"># 检查PostgreSQL 安装位置</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 卸载 </span></span><br><span class="line">rpm -e postgresql94-contrib-9.4.4-1PGDG.rhel6.x86_64 postgresql94-server-9.4.4-1PGDG.rhel6.x86_64 </span><br><span class="line">rpm -e postgresql94-libs-9.4.4-1PGDG.rhel6.x86_64 postgresql94-9.4.4-1PGDG.rhel6.x86_64</span><br></pre></td></tr></table></figure></p><h2 id="安装yum-源"><a href="#安装yum-源" class="headerlink" title="安装yum 源"></a>安装yum 源</h2><p>在官方文档选择自己系统对应的参数,获取到yum源的正确连接,执行安装。<br><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">yum install https://download.postgresql.org/pub/repos/yum/10/redhat/rhel-7-x86_64/pgdg-centos10-10-1.noarch.rpm</span><br></pre></td></tr></table></figure></p><h2 id="安装客户端和服务端"><a href="#安装客户端和服务端" class="headerlink" title="安装客户端和服务端"></a>安装客户端和服务端</h2><figure class="highlight bash"><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">yum install postgresql10</span><br><span class="line">yum install postgresql10-server</span><br></pre></td></tr></table></figure><h2 id="初始化数据库,启动服务"><a href="#初始化数据库,启动服务" class="headerlink" title="初始化数据库,启动服务"></a>初始化数据库,启动服务</h2><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">/usr/pgsql-10/bin/postgresql-10-setup initdb</span><br><span class="line">systemctl <span class="built_in">enable</span> postgresql-10</span><br><span class="line">systemctl start postgresql-10</span><br></pre></td></tr></table></figure><p><strong>说明:</strong></p><ul><li>1、数据库默认路径:<code>/var/lib/pgsql/10/data</code> ;</li><li>2、修改默认初始化路径,使用<code>postgreSQL</code>自带的初始化命令<code>initdb</code>,如下操作:<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">#mkdir /opt/PostgreSQL</span><br><span class="line">#mkdir /opt/PostgreSQL/data</span><br><span class="line">#chmod 755 /opt/PostgreSQL/data</span><br><span class="line">#chown postgres:postgres /opt/PostgreSQL/data</span><br><span class="line">#su - postgres</span><br><span class="line">#./initdb --encoding=UTF-8 --local=zh_CN.UTF8 --username=postgres --pwprompt --pgdata=/opt/PostgreSQL/data/</span><br></pre></td></tr></table></figure></li></ul><h2 id="连接数据库"><a href="#连接数据库" class="headerlink" title="连接数据库"></a>连接数据库</h2><p>postgresql10 在Linux的安装,默认创建了<code>postgres</code>用户,无需再次创建,直接su 即可。<br><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></pre></td><td class="code"><pre><span class="line">[root@pylixm-web ~]<span class="comment"># su - postgres</span></span><br><span class="line">bash-4.2$ psql</span><br><span class="line">psql (10.0)</span><br><span class="line">Type <span class="string">"help"</span> <span class="keyword">for</span> <span class="built_in">help</span>.</span><br><span class="line"></span><br><span class="line">postgres=<span class="comment"># \l</span></span><br><span class="line"> List of databases</span><br><span class="line"> Name | Owner | Encoding | Collate | Ctype | Access privileges</span><br><span class="line">-----------+----------+----------+-------------+-------------+-----------------------</span><br><span class="line"> postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |</span><br><span class="line"> template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres +</span><br><span class="line"> | | | | | postgres=CTc/postgres</span><br><span class="line"> template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres +</span><br><span class="line"> | | | | | postgres=CTc/postgres</span><br><span class="line">(3 rows)</span><br><span class="line"></span><br><span class="line">postgres=<span class="comment"># \du</span></span><br><span class="line"> List of roles</span><br><span class="line"> Role name | Attributes | Member of</span><br><span class="line">-----------+------------------------------------------------------------+-----------</span><br><span class="line"> postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {}</span><br><span class="line"></span><br><span class="line">postgres=<span class="comment">#</span></span><br></pre></td></tr></table></figure></p><h2 id="数据库登录权限设置"><a href="#数据库登录权限设置" class="headerlink" title="数据库登录权限设置"></a>数据库登录权限设置</h2><p><code>/var/lib/pgsql/10/data/pg_hba.conf</code> 权限相关配置:<br><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></pre></td><td class="code"><pre><span class="line"># TYPE DATABASE USER ADDRESS METHOD</span><br><span class="line"></span><br><span class="line"># "local" is for Unix domain socket connections only </span><br><span class="line">local all all peer 或 trust</span><br><span class="line"># IPv4 local connections:</span><br><span class="line">#host all all 127.0.0.1/32 ident</span><br><span class="line">host all all 0.0.0.0/0 password</span><br><span class="line"># IPv6 local connections:</span><br><span class="line">#host all all ::1/128 ident</span><br><span class="line">host all all ::1/128 password</span><br><span class="line"># Allow replication connections from localhost, by a user with the</span><br><span class="line"># replication privilege.</span><br><span class="line">#local replication all peer</span><br><span class="line">#host replication all 127.0.0.1/32 ident</span><br><span class="line">#host replication all ::1/128 ident</span><br></pre></td></tr></table></figure></p><p><strong>说明:</strong> 设置 trust,本地可以使用psql -U postgres直接登录服务器;设置 peer,本地可以使用psql -h 127.0.0.1 -d postgres -U postgres直接登录服务器; password ,使用用户名密码 登录 ;</p><p><code>/var/lib/pgsql/10/data/postgresql.conf</code> 数据库相关配置:<br><figure class="highlight plain"><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">listen_addresses = '*'</span><br><span class="line">posrt = 5432</span><br></pre></td></tr></table></figure></p><p><strong>说明:</strong> 监听任意IP, 允许任意ip连接数据库。</p><p>更多权限说明,见<a href="https://www.postgresql.org/docs/10/static/auth-methods.html" target="_blank" rel="noopener">官方文档</a>。</p><h2 id="防火墙设置"><a href="#防火墙设置" class="headerlink" title="防火墙设置"></a>防火墙设置</h2><p>此时,数据库可以在本地访问,要想在外部访问,还需要增加防火墙策略或直接关闭防火墙(不建议)。</p><p>centos 7 默认防火墙为 <a href="http://blog.csdn.net/gg_18826075157/article/details/72834694" target="_blank" rel="noopener"><code>firewalld</code></a>, 我们改用熟悉的<code>iptables</code>操作如下:<br>1、关闭firewall:<br>systemctl stop firewalld.service #停止firewall<br>systemctl disable firewalld.service #禁止firewall开机启动<br>firewall-cmd –state #查看默认防火墙状态(关闭后显示notrunning,开启后显示running)</p><p>2、iptables防火墙</p><p><code>yum install iptables iptables-services</code> ## 安装</p><p><code>vim /etc/sysconfig/iptables</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></pre></td><td class="code"><pre><span class="line"># sampleconfiguration for iptables service</span><br><span class="line"># you can edit thismanually or use system-config-firewall</span><br><span class="line"># please do not askus to add additional ports/services to this default configuration</span><br><span class="line">*filter</span><br><span class="line">:INPUT ACCEPT [0:0]</span><br><span class="line">:FORWARD ACCEPT[0:0]</span><br><span class="line">:OUTPUT ACCEPT[0:0]</span><br><span class="line">-A INPUT -m state--state RELATED,ESTABLISHED -j ACCEPT</span><br><span class="line">-A INPUT -p icmp -jACCEPT</span><br><span class="line">-A INPUT -i lo -jACCEPT</span><br><span class="line">-A INPUT -p tcp -mstate --state NEW -m tcp --dport 22 -j ACCEPT</span><br><span class="line">-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -jACCEPT</span><br><span class="line">-A INPUT -p tcp -m state --state NEW -m tcp --dport 5432-j ACCEPT</span><br><span class="line">-A INPUT -j REJECT--reject-with icmp-host-prohibited</span><br><span class="line">-A FORWARD -jREJECT --reject-with icmp-host-prohibited</span><br><span class="line">COMMIT</span><br><span class="line">:wq! #保存退出</span><br></pre></td></tr></table></figure><p><code>systemctl restart iptables.service</code> #最后重启防火墙使配置生效<br><code>systemctl enable iptables.service</code> #设置防火墙开机启动</p><p><strong>注:</strong> 若你使用的服务器为公有云产品,以上防火墙配置可能无限,有些公有云在控制台的安全组设置端口策略,如阿里云产品。</p><p>致此,我们已经安装并配置好数据库了,可以开心的使用了。更多进阶,可参考德哥文章<a href="https://yq.aliyun.com/articles/79330" target="_blank" rel="noopener">PostgreSQL 10.0 解读</a></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="http://www.cnblogs.com/qiyebao/p/4562557.html" target="_blank" rel="noopener">http://www.cnblogs.com/qiyebao/p/4562557.html</a></li><li><a href="http://www.linuxidc.com/Linux/2015-05/117473.htm" target="_blank" rel="noopener">http://www.linuxidc.com/Linux/2015-05/117473.htm</a></li><li><a href="https://my.oschina.net/myaniu/blog/181543" target="_blank" rel="noopener">https://my.oschina.net/myaniu/blog/181543</a></li></ul>]]></content>
<summary type="html">
<p>最近工作中与数据库打交道越来越多,操作数据频繁,感觉到了许多mysql操作的不便利性。想起了几年前使用的 <code>postgreSQL</code>, 重新整理安装下,记录备查。</p>
<p>关于<code>mysql</code>和 <code>postgreSQL
</summary>
<category term="postgreSQL" scheme="http://pylixm.cc/categories/postgreSQL/"/>
<category term="postgreSQL" scheme="http://pylixm.cc/tags/postgreSQL/"/>
<category term="数据库" scheme="http://pylixm.cc/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
</entry>
<entry>
<title>postgreSQL vs MySQL 命令行对比</title>
<link href="http://pylixm.cc/posts/2017-11-05-postgresql-cmd.html"/>
<id>http://pylixm.cc/posts/2017-11-05-postgresql-cmd.html</id>
<published>2017-11-04T16:00:00.000Z</published>
<updated>2017-11-05T13:02:04.000Z</updated>
<content type="html"><![CDATA[<blockquote><p>原文:<a href="http://www.cnblogs.com/qiyebao/p/4749146.html" target="_blank" rel="noopener">http://www.cnblogs.com/qiyebao/p/4749146.html</a></p></blockquote><h2 id="服务启动"><a href="#服务启动" class="headerlink" title="服务启动"></a>服务启动</h2><p><strong>PostgreSQL</strong></p><ul><li>1)#service postgresql start</li><li>2)#/etc/init.d/postgresql start</li><li>3)#su – postgresql</li><li>$pg_ctl start</li></ul><p><strong>MySQL</strong></p><ul><li>1)#service mysqld start</li><li>2)#/etc/init.d/mysqld start</li><li>3)#safe_mysqld&</li></ul><h2 id="第一次进入数据库"><a href="#第一次进入数据库" class="headerlink" title="第一次进入数据库"></a>第一次进入数据库</h2><p><strong>PostgreSQL</strong><br><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 class="comment">#su – postgres</span></span><br><span class="line"><span class="variable">$createdb</span> (建名为postgres的数据库)</span><br><span class="line"><span class="variable">$psql</span></span><br></pre></td></tr></table></figure></p><p><strong>MySQL</strong><br><figure class="highlight bash"><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="comment">#mysql</span></span><br><span class="line">mysql> (出现这个提示符说明成功)</span><br></pre></td></tr></table></figure></p><h2 id="创建用户:-用户Ajian,密码:123"><a href="#创建用户:-用户Ajian,密码:123" class="headerlink" title="创建用户:(用户Ajian,密码:123)"></a>创建用户:(用户Ajian,密码:123)</h2><p><strong>PostgreSQL</strong><br><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 class="comment">#su – postgres</span></span><br><span class="line"><span class="variable">$psql</span></span><br><span class="line">=<span class="comment">#create user ajian with password ‘123’;</span></span><br></pre></td></tr></table></figure></p><p><strong>MySQL</strong><br><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><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">#grant all privileges on *.* to ajian@”%” identified by “123″</span><br><span class="line"><span class="comment">-- (注意:同还可以分配权限,这里是ALL)</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">**PostgreSQL**</span><br><span class="line">```bash </span><br><span class="line">#su – postgres</span><br><span class="line">$psql</span><br><span class="line">=#create database My with owner = ajian template = template1 encoding=’UNICODE’;</span><br></pre></td></tr></table></figure></p><p><strong>MySQL</strong><br><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">1)<span class="comment">#mysql</span></span><br><span class="line">Mysql>create database My;</span><br><span class="line">2)<span class="comment">#mysqladmin create My</span></span><br></pre></td></tr></table></figure></p><h2 id="查看用户和数据库"><a href="#查看用户和数据库" class="headerlink" title="查看用户和数据库"></a>查看用户和数据库</h2><p><strong>PostgreSQL</strong><br><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"><span class="comment">#su – postgres</span></span><br><span class="line"><span class="variable">$psql</span></span><br><span class="line">=<span class="comment">#\l (查看数据库)</span></span><br><span class="line">=<span class="comment">#\du (查看用户)</span></span><br><span class="line">=<span class="comment">#\c 从一个数据库中转到另一个数据库中,如template1=# \c sales 从template1转到sales</span></span><br></pre></td></tr></table></figure></p><p><strong>MySQL</strong><br><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">1)#mysql</span><br><span class="line">Mysql>show databases; (看数据库)</span><br><span class="line">2)#mysqlshow</span><br><span class="line">use dbname;</span><br></pre></td></tr></table></figure></p><h2 id="新建用户登录"><a href="#新建用户登录" class="headerlink" title="新建用户登录"></a>新建用户登录</h2><p><strong>PostgreSQL</strong><br>1、首先修改配置文件</p><h1 id="vi-var-lib-pgsql-data-pg-hba-conf-在最后加"><a href="#vi-var-lib-pgsql-data-pg-hba-conf-在最后加" class="headerlink" title="vi /var/lib/pgsql/data/pg_hba.conf(在最后加)"></a>vi /var/lib/pgsql/data/pg_hba.conf(在最后加)</h1><p>host all all 127.0.0.1 255.255.255.255 md5</p><p>2、再重启服务:</p><p>#service postgresql restart</p><p>3、登录:#psql –h 127.0.0.1 –U ajian My</p><p>Password:<br><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"></span><br><span class="line">**MySQL**</span><br><span class="line">```bash </span><br><span class="line">1)#mysql –u ajian –p (带口令登录)</span><br><span class="line"></span><br><span class="line">2)#mysql</span><br><span class="line"></span><br><span class="line">Mysql>use My;</span><br><span class="line"></span><br><span class="line">(不带口令登录一般用于本机)</span><br></pre></td></tr></table></figure></p><h2 id="创建表-employee"><a href="#创建表-employee" class="headerlink" title="创建表(employee)"></a>创建表(employee)</h2><p><strong>PostgreSQL</strong><br><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">=<span class="comment">#create table employee(</span></span><br><span class="line"></span><br><span class="line">(<span class="comment">#employee_id int primary key,</span></span><br><span class="line"></span><br><span class="line">(<span class="comment">#name char(8),</span></span><br><span class="line"></span><br><span class="line">(<span class="comment">#sex char(2));</span></span><br></pre></td></tr></table></figure></p><p><strong>MySQL</strong><br><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">>create table employee(</span><br><span class="line"></span><br><span class="line">->employee_id int primary key,</span><br><span class="line"></span><br><span class="line">->name char(8),</span><br><span class="line"></span><br><span class="line">->sex char(2));</span><br></pre></td></tr></table></figure></p><h2 id="查看表"><a href="#查看表" class="headerlink" title="查看表"></a>查看表</h2><p><strong>PostgreSQL</strong><br><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">=#\dt</span><br></pre></td></tr></table></figure></p><p><strong>MySQL</strong><br><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">>show tables;</span><br></pre></td></tr></table></figure></p><h2 id="查看表的结构:"><a href="#查看表的结构:" class="headerlink" title="查看表的结构:"></a>查看表的结构:</h2><p><strong>PostgreSQL</strong><br><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">=#\d employee</span><br></pre></td></tr></table></figure></p><p><strong>MySQL</strong><br><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">>sescribe employee;</span><br><span class="line">``` </span><br><span class="line"></span><br><span class="line">## 向表中添加数据:</span><br><span class="line">**PostgreSQL**</span><br></pre></td></tr></table></figure></p><p>=#insert into employee values</p><p>-#(‘1’,’zhang’,’F’);</p><p>-#(‘2’,’chen’,’M’,);<br><figure class="highlight plain"><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">**MySQL**</span><br></pre></td></tr></table></figure></p><blockquote><p>insert into employee values</p></blockquote><p>->(‘1’,’zhang’,’F’);</p><p>->(‘2’,’chen’,’M’,);<br><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"></span><br><span class="line">## 查看表的数据</span><br><span class="line">**PostgreSQL**</span><br></pre></td></tr></table></figure></p><p>=#select * from emlpoyee<br><figure class="highlight plain"><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">**MySQL**</span><br></pre></td></tr></table></figure></p><blockquote><p>select * from emlpoyee;<br><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">## 索引相关</span><br><span class="line"></span><br><span class="line">**PostgreSQL**</span><br><span class="line">```bash </span><br><span class="line">## 创建索引(IN_employee)</span><br><span class="line">=#create index IN_employee on employee(name);</span><br><span class="line">## 查看索引</span><br><span class="line">=#\di</span><br><span class="line">## 删除索引</span><br><span class="line">=#drop index IN_employee on employee;</span><br><span class="line">## 重建索引</span><br><span class="line">=#reindex table employee;(重建employee所有的)</span><br><span class="line"></span><br><span class="line">=#reindex index IN_employee;(重建指定的)</span><br></pre></td></tr></table></figure></p></blockquote><p><strong>MySQL</strong><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">## 创建索引(IN_employee)</span></span><br><span class="line"></span><br><span class="line">1)>create index IN_employee on employee(name);</span><br><span class="line"></span><br><span class="line">2)>alter table employee add index IN_employee(name);</span><br><span class="line"></span><br><span class="line"><span class="comment">## 查看索引:</span></span><br><span class="line"></span><br><span class="line">>show index from employee;</span><br><span class="line"></span><br><span class="line"><span class="comment">## 删除索引:</span></span><br><span class="line"></span><br><span class="line">1)>drop index IN_employee on employee;</span><br><span class="line"></span><br><span class="line">2)>alter table emlpoyee drop index IN_employee;</span><br></pre></td></tr></table></figure></p><h2 id="删除表"><a href="#删除表" class="headerlink" title="删除表"></a>删除表</h2><p><strong>PostgreSQL</strong><br><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="comment">#drop table employee;</span></span><br></pre></td></tr></table></figure></p><p><strong>MySQL</strong><br><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">>drop table employee;</span><br></pre></td></tr></table></figure></p><h2 id="删除数据库:-注意命令前面的标志"><a href="#删除数据库:-注意命令前面的标志" class="headerlink" title="删除数据库:(注意命令前面的标志)"></a>删除数据库:(注意命令前面的标志)</h2><p><strong>PostgreSQL</strong><br><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">1)=#drop database ajian;</span><br><span class="line"></span><br><span class="line">2)$dropdb ajian</span><br></pre></td></tr></table></figure></p><p><strong>MySQL</strong><br><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">1)>drop database ajian;</span><br><span class="line"></span><br><span class="line">2)#mysqladmin drop ajian</span><br></pre></td></tr></table></figure></p>]]></content>
<summary type="html">
<blockquote>
<p>原文:<a href="http://www.cnblogs.com/qiyebao/p/4749146.html" target="_blank" rel="noopener">http://www.cnblogs.com/qiyebao/p/4
</summary>
<category term="postgreSQL" scheme="http://pylixm.cc/categories/postgreSQL/"/>
<category term="mysql" scheme="http://pylixm.cc/tags/mysql/"/>
<category term="postgreSQL" scheme="http://pylixm.cc/tags/postgreSQL/"/>
<category term="数据库" scheme="http://pylixm.cc/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
</entry>
</feed>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。