<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Learn</title>
  
  <subtitle>日有所进，岁有所得</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://lierchao.com/"/>
  <updated>2019-09-29T15:41:17.180Z</updated>
  <id>http://lierchao.com/</id>
  
  <author>
    <name>轩辕御龙</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>《深入理解计算机系统》读书笔记-05</title>
    <link href="http://lierchao.com/2019/09/29/%E3%80%8A%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%B3%BB%E7%BB%9F%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-05/"/>
    <id>http://lierchao.com/2019/09/29/《深入理解计算机系统》读书笔记-05/</id>
    <published>2019-09-29T15:40:33.000Z</published>
    <updated>2019-09-29T15:41:17.180Z</updated>
    
    <content type="html"><![CDATA[<h1 id="《深入理解计算机系统》读书笔记-05"><a href="#《深入理解计算机系统》读书笔记-05" class="headerlink" title="《深入理解计算机系统》读书笔记-05"></a>《深入理解计算机系统》读书笔记-05</h1><h2 id="条件分支的两种实现方法"><a href="#条件分支的两种实现方法" class="headerlink" title="条件分支的两种实现方法"></a>条件分支的两种实现方法</h2><ol><li><p>控制的条件转移（条件控制）</p><p>通过直接改变控制流，来实现条件分支</p></li><li><p>数据的条件转移（条件传送）</p><p>通过对各分支无条件运算，根据条件选取结果，来实现条件分支</p><p>更符合现代处理器的性能特性，充分利用了处理器时间，避免由于空等或分支预测错误的惩罚造成处理器性能的浪费。但使用条件受限，要慎重使用。只有当各分支没有其他副作用时，才可考虑选择条件传送。</p></li></ol><a id="more"></a><h3 id="现代处理器的分支预测"><a href="#现代处理器的分支预测" class="headerlink" title="现代处理器的分支预测"></a>现代处理器的分支预测</h3><p>现代处理器使用分支预测逻辑来尝试预测每个分支可能的走向，这样就可以不用等待求出分支条件的具体结果，从而有效利用现代处理器的高性能。</p><p>预期的正确率是90%，但在复杂情况下，这一概率依旧很低。</p><p>一旦处理器做出预测，就会按照预测的走向继续往下执行指令，如果计算结果发现预测正确，就皆大欢喜；如果发现预测错误，则丢弃从分支入口处到当前时间点所做的全部与该分支有关的工作，从另一个分支入口重新开始计算。</p><p>这个丢弃行为所带来的时间损耗，即称为“<strong>分支预测错误的处罚</strong>”</p><h2 id="while-循环的两种翻译"><a href="#while-循环的两种翻译" class="headerlink" title="while 循环的两种翻译"></a>while 循环的两种翻译</h2><ol><li><p>调转到中间（jump to middle）</p><p>通过一个无条件跳转，直接跳转到测试条件相应的语句块，将<code>while</code>循环转换为<code>do-while</code>循环。</p></li><li><p>guarded-do</p><p>先测试条件，若不成立，直接跳过循环；若成立，则顺序往下执行，循环代码转换为<code>do-while</code>循环。</p></li></ol><h2 id="switch-语句使用跳转表"><a href="#switch-语句使用跳转表" class="headerlink" title="switch 语句使用跳转表"></a>switch 语句使用跳转表</h2><p>使用<code>switch</code>语句可以十分高效地实现跳转，在分支数目较多的情况下，能够节省大量对条件的计算时间，得到较高的运行效率。其中起到关键作用的就是被称为“<strong>跳转表</strong>（jump table）”的数据结构。</p><p>跳转表被实现为一个数组，数组中元素是代码段的地址。GCC 在 C 语言的基础上进行了扩展，可以通过结合使用两个取地址符<code>&amp;&amp;</code>和代码标号，直接获取代码段的地址。这一点需要知悉。</p><p>注意，switch 的开关变量<code>n</code>是<strong>整数</strong>。</p><h2 id="过程"><a href="#过程" class="headerlink" title="过程"></a>过程</h2><p>过程，即 C 语言中的函数（function），在其他语言中也称方法（method）、子例程（subroutine）、处理函数（handler）等。</p><p>过程具有三个属性或者说要素：</p><ol><li>传递控制</li><li>传递数据</li><li>分配和释放内存</li></ol><p>其中“传递控制”主要是指的对程序运行位置，即程序计数器值的压栈和出栈。</p><h3 id="必须要在内存中存放局部数据的常见情况"><a href="#必须要在内存中存放局部数据的常见情况" class="headerlink" title="必须要在内存中存放局部数据的常见情况"></a>必须要在内存中存放局部数据的常见情况</h3><ol><li>局部数据较多，只用寄存器不足以存放</li><li>对局部变量使用了取地址符<code>&amp;</code></li><li>有的局部变量是数组或是结构体，要能够通过数组或结构的访问方式进行访问</li></ol><p>其中第2、3两条说的其实是一个意思。二者都是因为必须要对局部变量产生一个“地址”，只有这样才能够符合 C 语言的语法标准；但是寄存器是不存在“地址”这个概念的，也不可能通过内存地址的方式进行索引，因此处理器必须要将这些存在“地址需求”的变量存储到内存中。</p><p>注意，栈指针<code>%rsp</code>所指为当前栈顶的低位字节，也就是说，在<strong>小端系统</strong>中，假如栈顶存储了一个 8 字节内容，则该内容的最低位字节（从右往左第0~7位）的内存地址就是<code>%rsp</code>的内容，而次低位字节（第8~15位）则存储在<code>1(%rsp)</code>，也就是<code>%rsp+1</code>的地址上，以此类推，最高位字节（第56~63位）才应保存在<code>7(rsp)</code>的地址上。而<code>8(%rsp)</code>所指的又是上一个入栈的元素的低位字节了。</p><h3 id="寄存器中值的保存"><a href="#寄存器中值的保存" class="headerlink" title="寄存器中值的保存"></a>寄存器中值的保存</h3><blockquote><p>寄存器组是唯一被所有过程共享的资源</p></blockquote><h3 id="被调用者保存的寄存器"><a href="#被调用者保存的寄存器" class="headerlink" title="被调用者保存的寄存器"></a>被调用者保存的寄存器</h3><p>16 个通用寄存器中，<code>%rbx</code>、<code>%rbp</code>、<code>%r12</code>、<code>%r13</code>、<code>%r14</code>、<code>%r15</code>共计 6 个寄存器，被划分为“被调用者保存”寄存器。所谓“被调用者保存”，指的是“被调用”地过程有责任维护好调用发生当时，这些中存在的值。</p><p>维护的方式有两种：</p><ol><li>不使用相应寄存器</li><li>在使用相应寄存器之前，将其储存的值入栈</li></ol><p>这两种方式只需择一即可，也就是说，只有需要用到的寄存器才被入栈，如果在整个过程中不需要使用某些寄存器，也就不需要将其入栈。</p><h3 id="调用者保存的寄存器"><a href="#调用者保存的寄存器" class="headerlink" title="调用者保存的寄存器"></a>调用者保存的寄存器</h3><p>除栈指针<code>%rsp</code>外，其他所有通用寄存器均为“调用者保存”寄存器。</p><blockquote><p>此处暂时存疑，不知道是仅限于通用寄存器还是所有寄存器。按逻辑来说，应该是仅限于通用寄存器，但书上表述不太严谨。</p></blockquote><p>这些寄存器包括：<code>%rax</code>、<code>%rcx</code>、<code>%rdx</code>、<code>%rsi</code>、<code>%rdi</code>、<code>%r8</code>、<code>%r9</code>、<code>%r10</code>、<code>%r11</code>，共计 9 个通用寄存器。对应用法见下表。</p><div class="table-container"><table><thead><tr><th style="text-align:center">寄存器</th><th style="text-align:center">用法</th></tr></thead><tbody><tr><td style="text-align:center"><code>%rax</code></td><td style="text-align:center">返回值</td></tr><tr><td style="text-align:center"><code>%rcx</code></td><td style="text-align:center">第 4 个参数</td></tr><tr><td style="text-align:center"><code>%rdx</code></td><td style="text-align:center">第 3 个参数</td></tr><tr><td style="text-align:center"><code>%rsi</code></td><td style="text-align:center">第 2 个参数</td></tr><tr><td style="text-align:center"><code>%rdi</code></td><td style="text-align:center">第 1 个参数</td></tr><tr><td style="text-align:center"><code>%r8</code></td><td style="text-align:center">第 5 个参数</td></tr><tr><td style="text-align:center"><code>%r9</code></td><td style="text-align:center">第 6 个参数</td></tr><tr><td style="text-align:center"><code>%r10</code></td><td style="text-align:center">调用者保存</td></tr><tr><td style="text-align:center"><code>%r11</code></td><td style="text-align:center">调用者保存</td></tr></tbody></table></div><p>所谓“调用者保存”，也就是说被调用的过程对这些寄存器中的值不负任何责任，所有有用值的保存和恢复，即入栈和出栈操作，都是调用者（或者说主调函数）的责任，在调用其他过程之前，就必须自行保存好上述寄存器中的值，以免被所调用的过程覆盖重写。</p><h3 id="递归"><a href="#递归" class="headerlink" title="递归"></a>递归</h3><p>站在机器的视角来看，递归调用和一般的调用没有什么区别。每次调用都产生一个独立的存储空间，用来保存每次调用所需的各种状态信息。</p><p>递归在机器层面是一个很自然的过程。</p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;《深入理解计算机系统》读书笔记-05&quot;&gt;&lt;a href=&quot;#《深入理解计算机系统》读书笔记-05&quot; class=&quot;headerlink&quot; title=&quot;《深入理解计算机系统》读书笔记-05&quot;&gt;&lt;/a&gt;《深入理解计算机系统》读书笔记-05&lt;/h1&gt;&lt;h2 id=&quot;条件分支的两种实现方法&quot;&gt;&lt;a href=&quot;#条件分支的两种实现方法&quot; class=&quot;headerlink&quot; title=&quot;条件分支的两种实现方法&quot;&gt;&lt;/a&gt;条件分支的两种实现方法&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;控制的条件转移（条件控制）&lt;/p&gt;
&lt;p&gt;通过直接改变控制流，来实现条件分支&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;数据的条件转移（条件传送）&lt;/p&gt;
&lt;p&gt;通过对各分支无条件运算，根据条件选取结果，来实现条件分支&lt;/p&gt;
&lt;p&gt;更符合现代处理器的性能特性，充分利用了处理器时间，避免由于空等或分支预测错误的惩罚造成处理器性能的浪费。但使用条件受限，要慎重使用。只有当各分支没有其他副作用时，才可考虑选择条件传送。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
    
    </summary>
    
    
      <category term="计算机" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="基础知识" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"/>
    
      <category term="读书笔记" scheme="http://lierchao.com/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
    
      <category term="读书笔记" scheme="http://lierchao.com/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
      <category term="计算机基础知识" scheme="http://lierchao.com/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"/>
    
  </entry>
  
  <entry>
    <title>Python 之装饰器</title>
    <link href="http://lierchao.com/2019/09/28/Python-%E4%B9%8B%E8%A3%85%E9%A5%B0%E5%99%A8/"/>
    <id>http://lierchao.com/2019/09/28/Python-之装饰器/</id>
    <published>2019-09-28T04:38:37.000Z</published>
    <updated>2019-09-29T00:04:59.181Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Python-之装饰器"><a href="#Python-之装饰器" class="headerlink" title="Python 之装饰器"></a>Python 之装饰器</h1><h2 id="1-概念介绍"><a href="#1-概念介绍" class="headerlink" title="1. 概念介绍"></a>1. 概念介绍</h2><p><strong>装饰器</strong>（decorator），又称“装饰函数”，即一种返回值也是函数的函数，可以称之为“函数的函数”。其目的是在不对现有函数进行修改的情况下，实现额外的功能。最基本的理念来自于一种被称为“装饰模式”的设计模式。</p><a id="more"></a><p>在 Python 中，装饰器属于纯粹的“语法糖”，不使用也没关系，但是使用的话能够大大简化代码，使代码更加易读——当然，是对知道这是怎么回事儿的人而言。</p><p>想必经过一段时间的学习，大概率已经在 Python 代码中见过<code>@</code>这个符号。没错，这个符号正是使用装饰器的标识，也是正经的 Python 语法。</p><blockquote><p><a href="[https://baike.baidu.com/item/%E8%AF%AD%E6%B3%95%E7%B3%96/5247005?fr=aladdin](https://baike.baidu.com/item/语法糖/5247005?fr=aladdin">语法糖</a>)：指计算机语言中添加的某种语法，这种语法对语言的功能并没有影响，但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性，从而减少程序代码出错的机会。</p></blockquote><h2 id="2-运行机制"><a href="#2-运行机制" class="headerlink" title="2. 运行机制"></a>2. 运行机制</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">IAmDecorator</span><span class="params">(foo)</span>:</span></span><br><span class="line">    <span class="string">'''我是一个装饰函数'''</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@IAmDecorator</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">tobeDecorated</span><span class="params">(...)</span>:</span></span><br><span class="line">    <span class="string">'''我是被装饰函数'''</span></span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><p>与：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="function"><span class="keyword">def</span> <span class="title">IAmDecorator</span><span class="params">(foo)</span>:</span></span><br><span class="line">    <span class="string">'''我是一个装饰函数'''</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">tobeDecorated</span><span class="params">(...)</span>:</span></span><br><span class="line">    <span class="string">'''我是被装饰函数'''</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line">tobeDecorated = IAmDecorator(tobeDecorated)</span><br></pre></td></tr></table></figure><p>可以看到，使用装饰器的<code>@</code>语法，就相当于是将具体定义的函数作为参数传入装饰器函数，而装饰器函数则经过一系列操作，返回一个新的函数，然后再将这个新的函数赋值给原先的函数名。</p><p>最终得到的是一个与我们在代码中显式定义的函数<strong>同名</strong>而<strong>异质</strong>的新函数。</p><p>而装饰函数就好像为原来的函数套了一层壳。如图所示，最后得到的组合函数即为应用装饰器产生的新函数：</p><p><img src="https://ws1.sinaimg.cn/large/006cMbyIly1g79vi4kevog30ui0ltadr.gif" alt="2019-09-23-装饰器_03.gif"></p><p>这里要注意一点，上述两段代码在具体执行上还是存在些微的差异。在第二段代码中，函数名<code>tobeDecorated</code>实际上是先指向了原函数，在经过装饰器修饰之后，才指向了新的函数；但第一段代码的执行就没有这个中间过程，直接得到的就是名为<code>tobeDecorated</code>的新函数。</p><p>此外，装饰函数<strong>有且只能有</strong>一个参数，即要被修饰的原函数。</p><h2 id="3-用法"><a href="#3-用法" class="headerlink" title="3. 用法"></a>3. 用法</h2><p>Python 中，装饰器分为两种，分别是“函数装饰器”和“类装饰器”，其中又以“函数装饰器”最为常见，“类装饰器”则用得很少。</p><h3 id="3-1-函数装饰器"><a href="#3-1-函数装饰器" class="headerlink" title="3.1 函数装饰器"></a>3.1 函数装饰器</h3><h4 id="3-1-1-大体结构"><a href="#3-1-1-大体结构" class="headerlink" title="3.1.1 大体结构"></a>3.1.1 大体结构</h4><p>对装饰函数的定义大致可以总结为如图所示的模板，即：</p><p><img src="https://ws1.sinaimg.cn/large/006cMbyIly1g79wrt6bv4j30cf09pq2z.jpg" alt="装饰函数模板示意图.png"></p><p>由于要求装饰函数返回值也为一个函数的缘故，为了在原函数的基础上对功能进行扩充，并且使得扩充的功能能够以函数的形式返回，因此需要在装饰函数的定义中再定义一个内部函数，在这个内部函数中进一步操作。最后<code>return</code>的对象就应该是这个内部函数对象，也只有这样才能够正确地返回一个附加了新功能的函数。</p><p>如图一的动图所示，装饰函数就像一个“包装”，将原函数装在了装饰函数的内部，从而通过在原函数的基础上附加功能实现了扩展，装饰函数再将这个新的整体返回。同时对于原函数本身又不会有影响。这也是“装饰”二字的含义。</p><p><strong>这个地方如果不定义“内部函数”行不行呢？</strong></p><p><strong>答案是“不行”。</strong></p><h4 id="3-1-2-关于结构的解释"><a href="#3-1-2-关于结构的解释" class="headerlink" title="3.1.2 关于结构的解释"></a>3.1.2 关于结构的解释</h4><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></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="function"><span class="keyword">def</span> <span class="title">IAmFakeDecorator</span><span class="params">(fun)</span>:</span></span><br><span class="line"><span class="meta">... </span>    print(<span class="string">"我是一个假的装饰器"</span>)</span><br><span class="line"><span class="meta">... </span>    <span class="keyword">return</span> fun</span><br><span class="line">...</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>@IAmFakeDecorator</span><br><span class="line"><span class="meta">... </span><span class="function"><span class="keyword">def</span> <span class="title">func</span><span class="params">()</span>:</span></span><br><span class="line"><span class="meta">... </span>    print(<span class="string">"我是原函数"</span>)</span><br><span class="line">...</span><br><span class="line">我是一个假的装饰器</span><br></pre></td></tr></table></figure><p>有点奇怪，怎么刚一定义，装饰器扩展的操作就执行了呢？</p><p>再来调用一下新函数：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>func()</span><br><span class="line">我是原函数</span><br></pre></td></tr></table></figure><p>诶呦奇了怪了，扩展功能哪儿去了呀？</p><p>不要着急，我们来分析一下上面的代码。在装饰函数的定义中，我们没有另外定义一个内部函数，扩展操作直接放在装饰函数的函数体中，返回值就是传入的原函数。</p><p>在定义新函数的时候，下面两段代码又是等价的：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>@IAmFakeDecorator</span><br><span class="line"><span class="meta">... </span><span class="function"><span class="keyword">def</span> <span class="title">func</span><span class="params">()</span>:</span></span><br><span class="line"><span class="meta">... </span>    print(<span class="string">"我是原函数"</span>)</span><br><span class="line">...</span><br><span class="line">我是一个假的装饰器</span><br></pre></td></tr></table></figure><p>和</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="function"><span class="keyword">def</span> <span class="title">func</span><span class="params">()</span>:</span></span><br><span class="line"><span class="meta">... </span>    print(<span class="string">"我是原函数"</span>)</span><br><span class="line">...</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>func = IAmFakeDecorator(func)</span><br><span class="line">我是一个假的装饰器</span><br></pre></td></tr></table></figure><p>审视一下后一段代码，我们可以发现，装饰器只在定义新函数的同时调用一次，之后新函数名引用的对象就是装饰器的返回值了，与装饰器没有半毛钱关系。</p><p>换句话说，装饰器本身的函数体中的操作都是<strong>当且仅当</strong>函数定义时，才会执行一次，以后再以新函数名调用函数，执行的只会是内部函数的操作。所以到实际调用新函数的时候，得到的效果跟原函数没有任何区别。</p><p>如果不定义内部函数，单纯返回传入的原函数当然也是可以的，也符合装饰器的要求；但却得不到我们预期的结果，对原函数扩展的功能<strong>无法复用</strong>，只是<strong>一次性</strong>的。因此这样的行为没有任何意义。</p><p>这个在装饰函数内部定义的用于扩展功能的函数可以随意取名，但一般约定俗成命名为<code>wrapper</code>，即“包装”之意。</p><p>正确的装饰器定义应如下所示：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="function"><span class="keyword">def</span> <span class="title">IAmDecorator</span><span class="params">(fun)</span>:</span></span><br><span class="line"><span class="meta">... </span>    <span class="function"><span class="keyword">def</span> <span class="title">wrapper</span><span class="params">(*args, **kw)</span>:</span></span><br><span class="line"><span class="meta">... </span>        print(<span class="string">"我真的是一个装饰器"</span>)</span><br><span class="line"><span class="meta">... </span>        <span class="keyword">return</span> fun(*args, **kw)</span><br><span class="line"><span class="meta">... </span>    <span class="keyword">return</span> wrapper</span><br><span class="line">...</span><br></pre></td></tr></table></figure><h4 id="3-1-3-参数设置的问题"><a href="#3-1-3-参数设置的问题" class="headerlink" title="3.1.3 参数设置的问题"></a>3.1.3 参数设置的问题</h4><p>内部函数参数设置为<code>(*args, **kw)</code>的目的是可以接收<strong>任意参数</strong>，关于如何接收任意参数的内容在前面的<a href="http://www.justdopython.com/2019/09/19/python-function-param-017/" target="_blank" rel="noopener">函数参数</a>部分已经介绍过。</p><p>之所以要让<code>wrapper</code>能够接收任意参数，是因为我们在定义装饰器的时候并不知道会用来装饰什么函数，具体函数的参数又是什么情况；定义为“可以接收任意参数”能够极大增强代码的适应性。</p><p>另外，还要注意给出参数的位置。</p><p>要明确一个概念：除了函数头的位置，其他地方一旦给出了函数参数，表达式的含义就不再是“<strong>一个函数对象</strong>”，而是“<strong>一次函数调用</strong>”。</p><p>因此，我们的装饰器目的是返回一个函数对象，返回语句的对象一定是不带参数的函数名；在内部函数中，我们是需要对原函数进行调用，因此需要带上函数参数，否则，如果内部函数的返回值还是一个函数对象，就还需要再给一组参数才能够调用原函数。Show 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></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="function"><span class="keyword">def</span> <span class="title">IAmDecorator</span><span class="params">(fun)</span>:</span></span><br><span class="line"><span class="meta">... </span>    <span class="function"><span class="keyword">def</span> <span class="title">wrapper</span><span class="params">(*args, **kw)</span>:</span></span><br><span class="line"><span class="meta">... </span>        print(<span class="string">"我真的是一个装饰器"</span>)</span><br><span class="line"><span class="meta">... </span>        <span class="keyword">return</span> fun</span><br><span class="line"><span class="meta">... </span>    <span class="keyword">return</span> wrapper</span><br><span class="line">...</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>@IAmDecorator</span><br><span class="line"><span class="meta">... </span><span class="function"><span class="keyword">def</span> <span class="title">func</span><span class="params">(h)</span>:</span></span><br><span class="line"><span class="meta">... </span>    print(<span class="string">"我是原函数"</span>)</span><br><span class="line">...</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>func()</span><br><span class="line">我真的是一个装饰器</span><br><span class="line">&lt;function func at <span class="number">0x000001FF32E66950</span>&gt;</span><br></pre></td></tr></table></figure><p>原函数没有被成功调用，只是得到了原函数对应的函数对象。只有进一步给出了下一组参数，才能够发生正确的调用（为了演示参数的影响，在函数<code>func</code>的定义中增加了一个参数<code>h</code>）：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>func()(h=<span class="number">1</span>)</span><br><span class="line">我真的是一个装饰器</span><br><span class="line">我是原函数</span><br></pre></td></tr></table></figure><p>只要明白了带参数和不带参数的区别，并且知道你想要的到底是什么效果，就不会在参数上犯错误了。并且也完全不必拘泥上述规则，也许你要的就是一个未经调用的函数对象呢？</p><p>把握住这一点，嵌套的装饰器、嵌套的内部函数这些也就都不是问题了。</p><h4 id="3-1-4-函数属性"><a href="#3-1-4-函数属性" class="headerlink" title="3.1.4 函数属性"></a>3.1.4 函数属性</h4><blockquote><p>本小节内容启发于<a href="https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584" target="_blank" rel="noopener">廖雪峰的官方网站-Python 教程-函数式编程-装饰器</a></p></blockquote><p>还应注意的是，经过装饰器的修饰，原函数的属性也发生了改变。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="function"><span class="keyword">def</span> <span class="title">func</span><span class="params">()</span>:</span></span><br><span class="line"><span class="meta">... </span>    print(<span class="string">"我是原函数"</span>)</span><br><span class="line">...</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>func.__name__</span><br><span class="line"><span class="string">'func'</span></span><br></pre></td></tr></table></figure><p>正常来说，定义一个函数，其函数名称与对应的变量应该是一致的，这样在一些需要以变量名标识、索引函数对象时才能够避免不必要的问题。但是事情并不是那么顺利：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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="meta">&gt;&gt;&gt; </span>@IAmDecorator</span><br><span class="line"><span class="meta">... </span><span class="function"><span class="keyword">def</span> <span class="title">func</span><span class="params">()</span>:</span></span><br><span class="line"><span class="meta">... </span>    print(<span class="string">"我是原函数"</span>)</span><br><span class="line">...</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>func.__name__</span><br><span class="line"><span class="string">'wrapper'</span></span><br></pre></td></tr></table></figure><p>变量名还是那个变量名，原函数还是那个原函数，但是函数名称却变成了装饰器中内部函数的名称。</p><p>在这里我们可以使用 Python 内置模块<code>functools</code>中的<code>wraps</code>工具，实现“在使用装饰器扩展函数功能的同时，保留原函数属性”这一目的。这里<code>functools.wraps</code>本身也是一个装饰器。运行效果如下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">import</span> functools</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="comment"># 定义保留原函数属性的装饰器</span></span><br><span class="line"><span class="meta">... </span><span class="function"><span class="keyword">def</span> <span class="title">IAmDecorator</span><span class="params">(fun)</span>:</span></span><br><span class="line"><span class="meta">... </span>    @functools.wraps(fun)</span><br><span class="line"><span class="meta">... </span>    <span class="function"><span class="keyword">def</span> <span class="title">wrapper</span><span class="params">(*args, **kw)</span>:</span></span><br><span class="line"><span class="meta">... </span>        print(<span class="string">"我真的是一个装饰器"</span>)</span><br><span class="line"><span class="meta">... </span>        <span class="keyword">return</span> fun(*args, **kw)</span><br><span class="line"><span class="meta">... </span>    <span class="keyword">return</span> wrapper</span><br><span class="line">...</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>@IAmDecorator</span><br><span class="line"><span class="meta">... </span><span class="function"><span class="keyword">def</span> <span class="title">func</span><span class="params">()</span>:</span></span><br><span class="line"><span class="meta">... </span>    print(<span class="string">"我是原函数"</span>)</span><br><span class="line">...</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>func.__name__</span><br><span class="line"><span class="string">'func'</span></span><br></pre></td></tr></table></figure><p>大功告成！</p><h3 id="3-2-类装饰器"><a href="#3-2-类装饰器" class="headerlink" title="3.2 类装饰器"></a>3.2 类装饰器</h3><blockquote><p>本节部分参考[<a href="https://docs.python.org/3/reference/compound_stmts.html#class-definitions" target="_blank" rel="noopener">Python3 文档-复合语句-类定义</a>]和[<a href="https://www.jb51.net/article/168276.htm" target="_blank" rel="noopener">python 一篇文章搞懂装饰器所有用法</a>]中类装饰器相关部分</p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@ClassDecorator</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span>:</span></span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><p>等价于</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span>:</span></span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line">Foo = ClassDecorator(Foo)</span><br></pre></td></tr></table></figure><p>在定义类装饰器的时候，要保证类中存在<code>__init__</code>和<code>__call__</code>两种方法。其中<code>__init__</code>方法用以接收原函数或类，<code>__call__</code>方法用以实现装饰逻辑。</p><p>简单来讲，<code>__init__</code>方法负责在初始化类实例的时候，将传入的函数或类绑定到这个实例上；而<code>__call__</code>方法则与一般的函数装饰器差不多，连构造都没什么两样，可以认为<code>__call__</code>方法就是一个函数装饰器，因此不再赘述。</p><h3 id="3-3-多个装饰器的情况"><a href="#3-3-多个装饰器的情况" class="headerlink" title="3.3 多个装饰器的情况"></a>3.3 多个装饰器的情况</h3><p>多个装饰器可以嵌套，具体情况可以理解为从下往上结合的复合函数；或者也可以理解为下一个装饰器的值是前一个装饰器的参数。</p><p>举例来说，下面两段代码是等价的：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@f1(arg)</span></span><br><span class="line"><span class="meta">@f2</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">func</span><span class="params">()</span>:</span> </span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><p>和</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">func</span><span class="params">()</span>:</span> </span><br><span class="line">    <span class="keyword">pass</span></span><br><span class="line">func = f1(arg)(f2(func))</span><br></pre></td></tr></table></figure><p>理解了前面的内容，这种情况也很容易掌握。</p><h2 id="4-总结"><a href="#4-总结" class="headerlink" title="4. 总结"></a>4. 总结</h2><p>本文介绍了 Python 中的装饰器这一特性，详细讲解了装饰器的实际原理和使用方式，能够大大帮助学习者掌握有关装饰器的知识，减小读懂 Python 代码的阻力，写出更加 pythonic 的代码。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>[1] <a href="https://docs.python.org/3/glossary.html#term-decorator" target="_blank" rel="noopener">Python3 术语表-装饰器</a></p><p>[2] <a href="https://docs.python.org/3/reference/compound_stmts.html#function-definitions" target="_blank" rel="noopener">Python3 文档-复合语句-函数定义</a></p><p>[3] <a href="https://docs.python.org/3/reference/compound_stmts.html#class-definitions" target="_blank" rel="noopener">Python3 文档-复合语句-类定义</a></p><p>[4] <a href="https://baike.baidu.com/item/语法糖/5247005?fr=aladdin" target="_blank" rel="noopener">语法糖</a></p><p>[5] <a href="https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584" target="_blank" rel="noopener">廖雪峰的官方网站-Python 教程-函数式编程-装饰器</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;Python-之装饰器&quot;&gt;&lt;a href=&quot;#Python-之装饰器&quot; class=&quot;headerlink&quot; title=&quot;Python 之装饰器&quot;&gt;&lt;/a&gt;Python 之装饰器&lt;/h1&gt;&lt;h2 id=&quot;1-概念介绍&quot;&gt;&lt;a href=&quot;#1-概念介绍&quot; class=&quot;headerlink&quot; title=&quot;1. 概念介绍&quot;&gt;&lt;/a&gt;1. 概念介绍&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;装饰器&lt;/strong&gt;（decorator），又称“装饰函数”，即一种返回值也是函数的函数，可以称之为“函数的函数”。其目的是在不对现有函数进行修改的情况下，实现额外的功能。最基本的理念来自于一种被称为“装饰模式”的设计模式。&lt;/p&gt;
    
    </summary>
    
    
      <category term="计算机" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="编程语言" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
    
      <category term="Python" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/Python/"/>
    
    
      <category term="Python" scheme="http://lierchao.com/tags/Python/"/>
    
      <category term="编程语言" scheme="http://lierchao.com/tags/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
    
  </entry>
  
  <entry>
    <title>Python 之迭代器</title>
    <link href="http://lierchao.com/2019/09/21/Python-%E4%B9%8B%E8%BF%AD%E4%BB%A3%E5%99%A8/"/>
    <id>http://lierchao.com/2019/09/21/Python-之迭代器/</id>
    <published>2019-09-20T16:31:01.000Z</published>
    <updated>2019-09-20T16:32:08.538Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Python-之迭代器"><a href="#Python-之迭代器" class="headerlink" title="Python 之迭代器"></a>Python 之迭代器</h1><h2 id="1-概念引入"><a href="#1-概念引入" class="headerlink" title="1 概念引入"></a>1 概念引入</h2><p>在之前的教程中，我们已经接触过一些典型的<code>for</code>语句<a id="more"></a>，比如：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>list_example = [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">for</span> i <span class="keyword">in</span> list_example:</span><br><span class="line"><span class="meta">... </span> print(i)</span><br><span class="line">...</span><br><span class="line"><span class="number">0</span></span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="number">2</span></span><br><span class="line"><span class="number">3</span></span><br><span class="line"><span class="number">4</span></span><br></pre></td></tr></table></figure><p>通过简单地使用<code>for</code>和<code>in</code>两个关键字，我们可以很轻松地实现在 C 语言中繁琐的遍历操作。相比较而言，C 语言中要实现相同的功能，需要这样写（假设存在整型数组<code>list_example</code>）：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> i;</span><br><span class="line"><span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; list_length; i++)</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"%d\n"</span>, list_example[i]);</span><br></pre></td></tr></table></figure><p>显而易见，在遍历元素的操作上，Python 的表达更加直观优雅，简洁明了；这正是因为 Python 在实现<code>for</code>语句的时候，恰到好处地使用了“迭代器”的概念。</p><p>迭代器在 Python 中随处可见，并且具有统一的标准。通过使用迭代器，Python 能够逐个访问列表<code>list_example</code>中的每个元素。</p><p>下面我们来进一步讨论相关的机制。</p><h2 id="2-定义及原理"><a href="#2-定义及原理" class="headerlink" title="2 定义及原理"></a>2 定义及原理</h2><h3 id="2-1-迭代器的定义"><a href="#2-1-迭代器的定义" class="headerlink" title="2.1 迭代器的定义"></a>2.1 迭代器的定义</h3><blockquote><p>迭代器（iterator）是一种可在容器（container）中遍访的接口，为使用者封装了内部逻辑。</p><p>——<a href="[https://baike.baidu.com/item/%E8%BF%AD%E4%BB%A3%E5%99%A8/3803342?fr=aladdin](https://baike.baidu.com/item/迭代器/3803342?fr=aladdin">百度百科·迭代器</a>) 大意</p></blockquote><p>上面是我们可以查到的、对“迭代器”的一个宽泛的定义。</p><p>而具体到 Python 中，迭代器也属于内置的<strong>标准类</strong>之一，是与我们之前学习过的“序列”同一层次的概念。</p><p>对于迭代器对象本身来说，需要具有<a href="https://docs.python.org/3/library/functions.html#iter" target="_blank" rel="noopener"><code>__iter__()</code></a>和<a href="https://docs.python.org/3/library/stdtypes.html#iterator.__next__" target="_blank" rel="noopener"><code>__next__()</code></a>两种方法，二者合称为“<strong>迭代器协议</strong>”。也就是说，只要同时具有这两种方法，Python 解释器就会认为该对象是一个迭代器；反之，只具有其中一个方法或者二者都不具有，解释器则认为该对象不是一个迭代器。</p><p>上述论断可由下面的代码验证（需要用到内置函数<code>isinstance()</code>，来判断一个对象是否是某个类的实例；该用法启发于[<a href="https://www.liaoxuefeng.com/wiki/1016959663602400/1017323698112640" target="_blank" rel="noopener">廖雪峰的官方网站</a>]）：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">from</span> collections <span class="keyword">import</span> Iterable, Iterator, Container</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="class"><span class="keyword">class</span> <span class="title">bothIterAndNext</span>:</span></span><br><span class="line"><span class="meta">... </span><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="meta">... </span><span class="keyword">pass</span></span><br><span class="line"><span class="meta">... </span><span class="function"><span class="keyword">def</span> <span class="title">__next__</span><span class="params">(self)</span>:</span></span><br><span class="line"><span class="meta">... </span><span class="keyword">pass</span></span><br><span class="line">...</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>isinstance(bothIterAndNext(), Iterable) <span class="comment"># 两种方法都有的对象是可迭代的</span></span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>isinstance(bothIterAndNext(), Iterator) <span class="comment"># 两种方法都有的对象是迭代器</span></span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="class"><span class="keyword">class</span> <span class="title">onlyNext</span>:</span></span><br><span class="line"><span class="meta">... </span><span class="function"><span class="keyword">def</span> <span class="title">__next__</span><span class="params">(self)</span>:</span></span><br><span class="line"><span class="meta">... </span><span class="keyword">pass</span></span><br><span class="line">...</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>isinstance(onlyNext(), Iterable) <span class="comment"># 只有方法 __next__() 是不可迭代的</span></span><br><span class="line"><span class="literal">False</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>isinstance(onlyNext(), Iterator) <span class="comment"># 只有方法 __next__() 不是迭代器</span></span><br><span class="line"><span class="literal">False</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="class"><span class="keyword">class</span> <span class="title">onlyIter</span>:</span></span><br><span class="line"><span class="meta">... </span><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="meta">... </span><span class="keyword">pass</span></span><br><span class="line">...</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>isinstance(onlyIter(), Iterable) <span class="comment"># 只有方法 __iter__() 是可迭代的</span></span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>isinstance(onlyIter(), Iterator) <span class="comment"># 只有方法 __iter__() 不是迭代器</span></span><br><span class="line"><span class="literal">False</span></span><br></pre></td></tr></table></figure><p>由第 8~11 行的代码可知，对于 Python 来说，判断一个对象是否是迭代器的标准仅仅是“是否同时具有<code>__iter__()</code>和<code>__next__()</code>这两个方法”。</p><p>并且从第 17~20 行的代码也可以验证上述推断：只具有方法<code>__next__()</code>既不是可迭代的，也不是一个迭代器。</p><p>有意思的事情发生在代码第 26、27 两行：代码输出结果显示，只有方法<code>__iter__()</code>的对象居然是<strong>可迭代的</strong>！（后文解释）</p><h3 id="2-2-迭代器的实质"><a href="#2-2-迭代器的实质" class="headerlink" title="2.2 迭代器的实质"></a>2.2 迭代器的实质</h3><p>迭代器对象本质上代表的是一个数据流，通过反复调用其方法<code>__next__()</code>或将其作为参数传入<code>next()</code>函数，即可按顺序逐个返回数据流中的每一项；直到流中不再有数据项，从而抛出一个<code>StopIteration</code>异常，终止迭代。</p><p>在 Python 中内置了两个函数：<code>iter()</code>和<code>iter()</code>，分别用于“<strong>将参数对象转换为迭代器对象</strong>”和“<strong>从迭代器中取出下一项</strong>”。</p><p>实际上所有具有方法<code>__iter__()</code>的对象均被视作“可迭代的”。因为方法<code>__iter__()</code>进行的操作其实就是返回一个该对象对应的迭代器，也就是说“可迭代的（iterable）”的真实含义其实是“可以被转换为迭代器（iterator）的”。而内置函数<code>iter()</code>也是调用对象本身具有的<code>__iter__()</code>方法来实现特定对象到迭代器的转换。</p><p>相应地，内置函数<code>next()</code>其实是调用了对象本身的方法<code>__next__()</code>，而该方法执行的操作就是从对象对应的数据流中取出下一项。</p><p>因此直接调用对象的<code>__iter__()</code>和<code>__next__()</code>方法与将对象作为参数传入内置函数<code>iter()</code>和<code>next()</code>是等效的。</p><p>要注意的一点在于，对迭代器调用其本身的<code>__iter__()</code>方法，得到的将会是这个迭代器自身，该迭代器相关的状态都会被保留，包括该迭代器目前的迭代状态。见下述代码：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>li = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>li_iterator = iter(li)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>isinstance(li, Iterator)</span><br><span class="line"><span class="literal">False</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>isinstance(li_iterator, Iterator)</span><br><span class="line"><span class="literal">True</span></span><br></pre></td></tr></table></figure><p>显然，列表<code>li</code>本身并不是一个迭代器，而将其传入内置函数<code>iter()</code>就得到了相应于列表<code>li</code>的迭代器<code>li_iterator</code>。我们调用<code>next()</code>函数来迭代它：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>next(li_iterator)</span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>next(li_iterator)</span><br><span class="line"><span class="number">2</span></span><br></pre></td></tr></table></figure><p>一切都在预料之中。我们再来将其本身作为参数传入内置函数<code>iter()</code>：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>li_iterator = iter(li_iterator)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>next(li_iterator)</span><br><span class="line"><span class="number">3</span></span><br></pre></td></tr></table></figure><p>到这里跟我们希望的就有所出入了。在使用这样一个语句的时候，通常我们的目的都是得到一个新的迭代器，而非跟原先的迭代器一样的对象。</p><p>更进一步地，我们还可以发现，对迭代器调用<code>iter()</code>函数得到的对象不仅与原先的迭代器具有相同的状态，它们其实就是指向<strong>同一个对象</strong>：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(li_iterator)</span><br><span class="line"><span class="number">2195581916440</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>li_iterator = iter(li_iterator)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(li_iterator)</span><br><span class="line"><span class="number">2195581916440</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>li_iterator2 = iter(li_iterator)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(li_iterator2)</span><br><span class="line"><span class="number">2195581916440</span></span><br></pre></td></tr></table></figure><p>也就是说在对象本身就是一个迭代器的情况下，生成的对应迭代器的时候 Python 不会进行另外的操作，就返回这个迭代器本身作为结果。</p><h2 id="3-实现一个迭代器类"><a href="#3-实现一个迭代器类" class="headerlink" title="3 实现一个迭代器类"></a>3 实现一个迭代器类</h2><blockquote><p>本节构建类的代码来自[<a href="https://docs.python.org/3/tutorial/classes.html?highlight=iterator#iterators" target="_blank" rel="noopener">Python3 文档-类-9.8 迭代器</a>]</p></blockquote><p>有了上面的讨论，我们就可以自己实现一个简单的迭代器。只要确保这个简单迭代器具有与迭代器定义相符的行为即可。</p><p>说人话就是：要定义一个数据类型，具有<code>__iter__()</code>方法并且该方法返回一个带有<code>__next__()</code>方法的对象，而当该类已经具有<code>__next__()</code>方法时则返回其本身。示例代码如下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Reverse</span>:</span></span><br><span class="line">    <span class="string">"""反向遍历序列对象的迭代器"""</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, data)</span>:</span></span><br><span class="line">        self.data = data</span><br><span class="line">        self.index = len(data)</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> self</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__next__</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> self.index == <span class="number">0</span>:</span><br><span class="line">            <span class="keyword">raise</span> StopIteration</span><br><span class="line">        self.index = self.index - <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> self.data[self.index]</span><br></pre></td></tr></table></figure><p>验证一下：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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="meta">&gt;&gt;&gt; </span>rev = Reverse(<span class="string">'justdopython.com'</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>next(rev)</span><br><span class="line"><span class="string">'m'</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>next(rev)</span><br><span class="line"><span class="string">'o'</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>next(rev)</span><br><span class="line"><span class="string">'c'</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>next(rev)</span><br><span class="line"><span class="string">'.'</span></span><br></pre></td></tr></table></figure><p>(o゜▽゜)o☆[BINGO!]</p><p>任务完成！</p><h2 id="4-for语句与迭代器"><a href="#4-for语句与迭代器" class="headerlink" title="4 for语句与迭代器"></a>4 <code>for</code>语句与迭代器</h2><p>回到文章开头我们作为引子的<code>for</code>循环示例，实际上在执行<code>for</code>语句的时候，Python 悄悄调用了内置函数<a href="https://docs.python.org/3/library/functions.html#iter" target="_blank" rel="noopener"><code>iter()</code></a>，并将<code>for</code>语句中的容器对象作为参数传入；而函数<a href="https://docs.python.org/3/library/functions.html#iter" target="_blank" rel="noopener"><code>iter()</code></a>返回值则是一个迭代器对象。</p><p>因此，<code>for</code>语句是将容器对象转换为迭代器对象之后，调用<code>__next__()</code>方法，逐个访问原容器中的各个对象，直到遍历完所有元素，抛出一个<code>StopIteration</code>异常，并终止<code>for</code>循环。</p><h2 id="5-总结"><a href="#5-总结" class="headerlink" title="5 总结"></a>5 总结</h2><ul><li>迭代器（iterator）首先要是可迭代的（iterable）；即迭代器一定是可迭代的，但可迭代的不一定是迭代器</li><li>可迭代的对象意味着可以被转换为迭代器</li><li>迭代器需要同时具有方法<code>__iter__()</code>和<code>__next__()</code></li><li>对迭代器调用<code>iter()</code>函数，得到的是这个迭代器本身</li><li><code>for</code>循环实际上使用了迭代器，并且一般情况下将异常<code>StopIteration</code>作为循环终止条件</li></ul><p>本文探究了 Python 中迭代器的相关知识点，深入理解了迭代器的属性和行为，学到了两个重要的方法<code>__iter__()</code>和<code>__next__()</code>。同时搞明白了 Python 实现<code>for</code>循环的内部机制。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>[1] <a href="https://docs.python.org/3/library/stdtypes.html" target="_blank" rel="noopener">Python3 文档-内置类型</a></p><p>[2] <a href="https://www.liaoxuefeng.com/wiki/1016959663602400/1017323698112640" target="_blank" rel="noopener">廖雪峰的官方网站</a></p><p>[3] <a href="https://docs.python.org/3/tutorial/classes.html?highlight=iterator#iterators" target="_blank" rel="noopener">Python3 文档-类-9.8 迭代器</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;Python-之迭代器&quot;&gt;&lt;a href=&quot;#Python-之迭代器&quot; class=&quot;headerlink&quot; title=&quot;Python 之迭代器&quot;&gt;&lt;/a&gt;Python 之迭代器&lt;/h1&gt;&lt;h2 id=&quot;1-概念引入&quot;&gt;&lt;a href=&quot;#1-概念引入&quot; class=&quot;headerlink&quot; title=&quot;1 概念引入&quot;&gt;&lt;/a&gt;1 概念引入&lt;/h2&gt;&lt;p&gt;在之前的教程中，我们已经接触过一些典型的&lt;code&gt;for&lt;/code&gt;语句
    
    </summary>
    
    
      <category term="计算机" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="编程语言" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
    
      <category term="Python" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/Python/"/>
    
    
      <category term="Python" scheme="http://lierchao.com/tags/Python/"/>
    
      <category term="编程语言" scheme="http://lierchao.com/tags/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
    
  </entry>
  
  <entry>
    <title>Python 之引用</title>
    <link href="http://lierchao.com/2019/09/12/Python-%E4%B9%8B%E5%BC%95%E7%94%A8/"/>
    <id>http://lierchao.com/2019/09/12/Python-之引用/</id>
    <published>2019-09-12T12:15:32.000Z</published>
    <updated>2019-09-12T12:17:36.227Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Python-之引用"><a href="#Python-之引用" class="headerlink" title="Python 之引用"></a>Python 之引用</h1><h2 id="1-引用简介与工具引入"><a href="#1-引用简介与工具引入" class="headerlink" title="1. 引用简介与工具引入"></a>1. 引用简介与工具引入</h2><p>Python 中对于变量的处理与 C 语言有着很大的不同，Python 中的变量具有一个特殊的属性：identity，即“身份标识”。这种特殊的属性也在很多地方被称为“引用”。</p><p>为了更加清晰地说明引用相关的问题，我们首先要介绍两个工具：一个Python的内置函数：<code>id()</code>；一个运算符：<code>is</code>；同时还要介绍一个<code>sys</code>模块内的函数：<code>getrefcount()</code>。</p><a id="more"></a><h3 id="1-1-内置函数id"><a href="#1-1-内置函数id" class="headerlink" title="1.1 内置函数id()"></a>1.1 内置函数<code>id()</code></h3><p><strong><code>id(object)</code></strong></p><blockquote><p>Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same <a href="https://docs.python.org/3.7/library/functions.html?highlight=id#id" target="_blank" rel="noopener"><code>id()</code></a> value.</p><p>返回值为传入对象的“标识”。该标识是一个唯一的常数，在传入对象的生命周期内与之一一对应。生命周期没有重合的两个对象可能拥有相同的<code>id()</code>返回值。</p><p><strong>CPython implementation detail:</strong> This is the address of the object in memory.</p><p>CPython 实现细节：“标识”实际上就是对象在内存中的地址。</p></blockquote><p>——引自《<a href="https://docs.python.org/3.7/library/functions.html?highlight=id#id" target="_blank" rel="noopener">Python 3.7.4 文档-内置函数-id()</a>》</p><p>换句话说，不论是否是 CPython 实现，一个对象的<code>id</code>就可以视作是其虚拟的内存地址。</p><h3 id="1-2-运算符is"><a href="#1-2-运算符is" class="headerlink" title="1.2 运算符is"></a>1.2 运算符<code>is</code></h3><div class="table-container"><table><thead><tr><th>运算</th><th>含义</th></tr></thead><tbody><tr><td>is</td><td>object  identity</td></tr></tbody></table></div><blockquote><p>即<code>is</code>的作用是比较对象的标识。</p></blockquote><p>——引自《<a href="https://docs.python.org/3/library/stdtypes.html" target="_blank" rel="noopener">Python 3.7.4 文档-内置类型</a>》</p><h3 id="1-3-sys模块函数getrefcount-函数"><a href="#1-3-sys模块函数getrefcount-函数" class="headerlink" title="1.3 sys模块函数getrefcount()函数"></a>1.3 <code>sys</code>模块函数<code>getrefcount()</code>函数</h3><p><strong><code>sys.getrefcount(object)</code></strong></p><blockquote><p>Return the reference count of the <em>object</em>. The count returned is generally one higher than you might expect, because it includes the (temporary) reference as an argument to <a href="https://docs.python.org/3.7/library/sys.html#sys.getrefcount" target="_blank" rel="noopener"><code>getrefcount()</code></a>.</p><p>返回值是传入对象的引用计数。由于作为参数传入<code>getrefcount()</code>的时候产生了一次临时引用，因此返回的计数值一般要比预期多1。</p><p>——引自《<a href="https://docs.python.org/3.7/library/sys.html#sys.getrefcount" target="_blank" rel="noopener">Python 3.7.4 文档-sys模块——系统相关参数及函数</a>》</p></blockquote><p>此处的“引用计数”，在 <a href="https://docs.python.org/3.7/glossary.html?highlight=getrefcount" target="_blank" rel="noopener">Python 文档</a>中被定义为“对象被引用的次数”。一旦引用计数归零，则对象所在的内存被释放。这是 Python 内部进行自动内存管理的一个机制。</p><h2 id="2-问题示例"><a href="#2-问题示例" class="headerlink" title="2. 问题示例"></a>2. 问题示例</h2><p>C 语言中，变量代表的就是一段固定的内存，而赋给变量的值则是存在这段地址中的数据；但对 Python 来说，变量就不再是一段固定的地址，而只是 Python 中各个对象所附着的标签。理解这一点对于理解 Python 的很多特性十分重要。</p><h3 id="2-1-对同一变量赋值"><a href="#2-1-对同一变量赋值" class="headerlink" title="2.1 对同一变量赋值"></a>2.1 对同一变量赋值</h3><p>举例来说，对于如下的 C 代码：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> a = <span class="number">10000</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"original address: %p\n"</span>, &amp;a); <span class="comment">// original address: 0060FEFC</span></span><br><span class="line">a = <span class="number">12345</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"second address: %p\n"</span>, &amp;a); <span class="comment">// second address: 0060FEFC</span></span><br></pre></td></tr></table></figure><p>对于有 C 语言编程经验的人来说，上述结果是显而易见的：变量<code>a</code>的地址并不会因为赋给它的值有变化而发生变化。对于 C 编译器来说，变量<code>a</code>只是协助它区别各个内存地址的标识，是直接与特定的内存地址绑定的，如图所示：</p><p><img src="https://ws1.sinaimg.cn/large/006cMbyIly1g6wx9mqrqvj30f707o3yf.jpg" alt="C 语言中的变量赋值"></p><p>但 Python 就不一样的。考虑如下代码：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>a = <span class="number">10000</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(a)</span><br><span class="line"><span class="number">1823863879824</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>a = <span class="number">12345</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(a)</span><br><span class="line"><span class="number">1823863880176</span></span><br></pre></td></tr></table></figure><p>这就有点儿意思了，更加神奇的是，即使赋给变量同一个常数，其得到的<code>id</code>也可能不同：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>a = <span class="number">10000</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(a)</span><br><span class="line"><span class="number">1823863880304</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>a = <span class="number">10000</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(a)</span><br><span class="line"><span class="number">1823863879408</span></span><br></pre></td></tr></table></figure><p>假如<code>a</code>对应的数据类型是一个列表，那么：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>a = [<span class="number">1</span>,<span class="number">2</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(a)</span><br><span class="line"><span class="number">2161457994952</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>a = [<span class="number">1</span>,<span class="number">2</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(a)</span><br><span class="line"><span class="number">2161458037448</span></span><br></pre></td></tr></table></figure><p>得到的<code>id</code>值也是不同的。</p><p>正如前文所述，在 Python 中，变量就是一块砖，哪里需要哪里搬。每次将一个新的对象赋值给一个变量，都在内存中<strong>重新创建</strong>了一个对象，这个对象就具有新的引用值。作为一个“标签”，变量也是哪里需要哪里贴，毫无节操可言。</p><p><img src="https://ws1.sinaimg.cn/large/006cMbyIly1g6wxgtfe84j30vh0cjdfx.jpg" alt="Snipaste_2019-09-12_18-50-30.png"></p><blockquote><p>但要注意的是，这里还有一个问题：之所以说“即使赋给变量同一个常数，其得到的<code>id</code>也<strong>可能</strong>不同”，实际上是因为并不是对所有的常数都存在这种情况。以常数<code>1</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></pre></td><td class="code"><pre><span class="line">&gt; &gt;&gt;&gt; a = <span class="number">1</span></span><br><span class="line">&gt; &gt;&gt;&gt; id(a)</span><br><span class="line">&gt; <span class="number">140734357607232</span></span><br><span class="line">&gt; &gt;&gt;&gt; a = <span class="number">1</span></span><br><span class="line">&gt; &gt;&gt;&gt; id(a)</span><br><span class="line">&gt; <span class="number">140734357607232</span></span><br><span class="line">&gt; &gt;&gt;&gt; id(<span class="number">1</span>)</span><br><span class="line">&gt; <span class="number">140734357607232</span></span><br><span class="line">&gt;</span><br></pre></td></tr></table></figure></blockquote><p>&gt;</p><blockquote><p>可以看到，常数<code>1</code>对应的<code>id</code>一直都是相同的，没有发生变化，因此变量<code>a</code>的<code>id</code>也就没有变化。</p><p>这是因为Python在内存中维护了一个特定数量的常量池，对于一定范围内的数值均不再创建新的对象，而直接在这个常量池中进行分配。实际上在我的机器上使用如下代码可以得到这个常量池的范围是 [0, 256] ，而 256 刚好是一个字节的二进制码可以表示的值的个数。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&gt; <span class="keyword">for</span> b <span class="keyword">in</span> range(<span class="number">300</span>):</span><br><span class="line">&gt;  <span class="keyword">if</span> b <span class="keyword">is</span> <span class="keyword">not</span> range(<span class="number">300</span>)[b]:</span><br><span class="line">&gt;      print(<span class="string">"常量池最大值为："</span>, (b - <span class="number">1</span>))</span><br><span class="line">&gt;      <span class="keyword">break</span></span><br><span class="line">&gt; <span class="comment"># 常量池最大值为： 256</span></span><br><span class="line">&gt;</span><br></pre></td></tr></table></figure></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>a = <span class="number">10000</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(a)</span><br><span class="line"><span class="number">2161457772304</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>a = a + <span class="number">1</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>a</span><br><span class="line"><span class="number">10001</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(a)</span><br><span class="line"><span class="number">2161457772880</span></span><br></pre></td></tr></table></figure><p>比较代码块第 3、8行的输出结果，可以看到对数值型变量执行加法并赋值会改变对应变量的引用值。这样的表现应该比较好理解。因为按照 Python 运算符的优先级，<code>a = a + 1</code>实际上就是<code>a = (a + 1)</code>，对变量<code>a</code>对应的数值加1之后得到的是一个新的数值，再将这个新的数值赋给<code>a</code> ，于是<code>a</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></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>a = [<span class="number">1</span>,<span class="number">2</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(a)</span><br><span class="line"><span class="number">2161458326920</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>a = a + [<span class="number">4</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>a</span><br><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">4</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(a)</span><br><span class="line"><span class="number">2161458342792</span></span><br></pre></td></tr></table></figure><h3 id="2-2-不变的情况"><a href="#2-2-不变的情况" class="headerlink" title="2.2 不变的情况"></a>2.2 不变的情况</h3><p>与数值不同，Python 中对列表对象的操作还表现出另一种特性。考虑下面的代码：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>c = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(c)</span><br><span class="line"><span class="number">2161458355400</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>c[<span class="number">2</span>] = <span class="number">5</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>c</span><br><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">5</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(c)</span><br><span class="line"><span class="number">2161458355400</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>c.append(<span class="number">3</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>c</span><br><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">5</span>, <span class="number">3</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(c)</span><br><span class="line"><span class="number">2161458355400</span></span><br></pre></td></tr></table></figure><p>观察代码块第 3、8、13三行，输出相同。也就是说，对于列表而言，可以通过直接操作变量本身，从而在不改变其引用的情况下改变所引用的值。</p><p>更进一步地，如果是两个变量同时引用同一个列表，则对其中一个变量本身直接进行操作，也会影响到另一个变量的值：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>c = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>cc = c</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(c)</span><br><span class="line"><span class="number">1823864610120</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(cc)</span><br><span class="line"><span class="number">1823864610120</span></span><br></pre></td></tr></table></figure><p>显然此时的变量<code>c</code>和<code>cc</code>的<code>id</code>是一致的。现在改变<code>c</code>所引用的列表值：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>c[<span class="number">2</span>] = <span class="number">5</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>cc</span><br><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">5</span>]</span><br></pre></td></tr></table></figure><p>可以看到<code>cc</code>所引用的列表值也随之变化了。再看看相应地<code>id</code>：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(c)</span><br><span class="line"><span class="number">1823864610120</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(cc)</span><br><span class="line"><span class="number">1823864610120</span></span><br></pre></td></tr></table></figure><p>两个变量的<code>id</code>都没有发生变化。再调用<code>append()</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></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>c.append(<span class="number">3</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>c</span><br><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">5</span>, <span class="number">3</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>cc</span><br><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">5</span>, <span class="number">3</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(c)</span><br><span class="line"><span class="number">1823864610120</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(cc)</span><br><span class="line"><span class="number">1823864610120</span></span><br></pre></td></tr></table></figure><p>删除元素：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="meta">&gt;&gt;&gt; </span><span class="keyword">del</span> c[<span class="number">3</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>c</span><br><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">5</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>cc</span><br><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">5</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(c)</span><br><span class="line"><span class="number">1823864610120</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(cc)</span><br><span class="line"><span class="number">1823864610120</span></span><br></pre></td></tr></table></figure><p>在上述所有对列表的操作中，均没有改变相应元素的引用。</p><p>也就是说，对于变量本身进行的操作并不会创建新的对象，而是会直接改变原有对象的值。</p><h3 id="2-3-一个特殊的地方"><a href="#2-3-一个特殊的地方" class="headerlink" title="2.3 一个特殊的地方"></a>2.3 一个特殊的地方</h3><blockquote><p>本小节示例灵感来自[<a href="https://www.cnblogs.com/yuyan/archive/2012/04/21/2461673.html" target="_blank" rel="noopener">关于Python中的引用</a>]</p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>num = <span class="number">10000</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(num)</span><br><span class="line"><span class="number">2161457772336</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>num += <span class="number">1</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(num)</span><br><span class="line"><span class="number">2161457774512</span></span><br></pre></td></tr></table></figure><p>有了前面的铺垫，这样的结果很显得很自然。显然在对变量<code>num</code>进行增1操作的时候，还是计算出新值然后进行赋值操作，因此引用发生了变化。</p><p>但列表却不然。见如下代码：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>li = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(li)</span><br><span class="line"><span class="number">2161458469960</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>li += [<span class="number">4</span>]</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(li)</span><br><span class="line"><span class="number">2161458469960</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>li</span><br><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>]</span><br></pre></td></tr></table></figure><p>注意第 4 行。明明进行的是“相加再赋值”操作，为什么有了跟前面不一样的结果呢？检查变量<code>li</code>的值，发现变量的值也确实发生了改变，但引用却没有变。</p><p>实际上这是因为加法运算符在 Python 中存在重载的情况，对列表对象和数值对象来说，加法运算的底层实现是完全不同的，在简单的加法中，列表的运算还是创建了一个新的列表对象；但在简写的加法运算<code>+=</code>实现中，则并没有创建新的列表对象。这一点要十分注意。</p><h2 id="3-原理解析"><a href="#3-原理解析" class="headerlink" title="3. 原理解析"></a>3. 原理解析</h2><p>前面（<a href="http://www.ityouknow.com/python/2019/08/03/python-003.html" target="_blank" rel="noopener">第3天：Python 变量与数据类型</a>）我们提到过，Python 中的六个标准数据类型实际上分为两大类：<strong>可变数据</strong>和<strong>不可变数据</strong>。其中，列表、字典和集合均为“可变对象”；而数字、字符串和元组均为“不可变对象”。实际上上面演示的数值数据（即数字）和列表之间的差异正是这两种不同的数据类型导致的。</p><p>由于数字是不可变对象，我们不能够对数值本身进行任何可以改变数据值的操作。因此在 Python 中，每出现一个数值都意味着需要另外分配一个新的内存空间（常量池中的数值例外）。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>a = <span class="number">10000</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>a == <span class="number">10000</span></span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>a <span class="keyword">is</span> <span class="number">10000</span></span><br><span class="line"><span class="literal">False</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(a)</span><br><span class="line"><span class="number">2161457773424</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>id(<span class="number">10000</span>)</span><br><span class="line"><span class="number">2161457773136</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span></span><br><span class="line"><span class="keyword">from</span> sys <span class="keyword">import</span> getrefcount</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>getrefcount(a)</span><br><span class="line"><span class="number">2</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>getrefcount(<span class="number">10000</span>)</span><br><span class="line"><span class="number">3</span></span><br></pre></td></tr></table></figure><p>前 9 行的代码容易理解：即使是同样的数值，也可能具有不同的引用值。关键在于这个值是否来自于同一个对象。</p><p>而第 10 行的代码则说明除了<code>getrefcount()</code>函数的引用外，变量<code>a</code>所引用的对象就只有1个引用，也就是变量<code>a</code>。一旦变量<code>a</code>被释放，则相应的对象引用计数归零，也会被释放；并且只有此时，这个对象对应的内存空间才是真正的“被释放”。</p><p>而作为可变对象，列表的值是可以在不新建对象的情况下进行改变的，因此对列表对象本身直接进行操作，是可以达到“改变变量值而不改变引用”的目的的。</p><h2 id="4-总结"><a href="#4-总结" class="headerlink" title="4. 总结"></a>4. 总结</h2><p>对于列表、字典和集合这些“可变对象”，通过对变量所引用对象本身进行操作，可以只改变变量的值而不改变变量的引用；但对于数字、字符串和元组这些“不可变对象”，由于对象本身是不能够进行变值操作的，因此要想改变相应变量的值，就必须要新建对象，再把新建对象赋值给变量。</p><p>通过这样的探究，也能更加生动地理解“万物皆对象”的深刻含义。</p><h2 id="5-参考资料"><a href="#5-参考资料" class="headerlink" title="5. 参考资料"></a>5. 参考资料</h2><p><a href="https://docs.python.org/3.7/library/functions.html?highlight=id#id" target="_blank" rel="noopener">Python 3.7.4 文档-内置函数-id()</a></p><p><a href="https://docs.python.org/3/library/stdtypes.html" target="_blank" rel="noopener">Python 3.7.4 文档-内置类型</a></p><p><a href="https://docs.python.org/3.7/library/sys.html#sys.getrefcount" target="_blank" rel="noopener">Python 3.7.4 文档-sys模块——系统相关参数及函数</a></p><p><a href="https://docs.python.org/3.7/glossary.html?highlight=getrefcount" target="_blank" rel="noopener">Python 3.7.4 文档-术语表</a></p><p><a href="https://www.cnblogs.com/yuyan/archive/2012/04/21/2461673.html" target="_blank" rel="noopener">关于Python中的引用</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;Python-之引用&quot;&gt;&lt;a href=&quot;#Python-之引用&quot; class=&quot;headerlink&quot; title=&quot;Python 之引用&quot;&gt;&lt;/a&gt;Python 之引用&lt;/h1&gt;&lt;h2 id=&quot;1-引用简介与工具引入&quot;&gt;&lt;a href=&quot;#1-引用简介与工具引入&quot; class=&quot;headerlink&quot; title=&quot;1. 引用简介与工具引入&quot;&gt;&lt;/a&gt;1. 引用简介与工具引入&lt;/h2&gt;&lt;p&gt;Python 中对于变量的处理与 C 语言有着很大的不同，Python 中的变量具有一个特殊的属性：identity，即“身份标识”。这种特殊的属性也在很多地方被称为“引用”。&lt;/p&gt;
&lt;p&gt;为了更加清晰地说明引用相关的问题，我们首先要介绍两个工具：一个Python的内置函数：&lt;code&gt;id()&lt;/code&gt;；一个运算符：&lt;code&gt;is&lt;/code&gt;；同时还要介绍一个&lt;code&gt;sys&lt;/code&gt;模块内的函数：&lt;code&gt;getrefcount()&lt;/code&gt;。&lt;/p&gt;
    
    </summary>
    
    
      <category term="计算机" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="编程语言" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
    
      <category term="Python" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/Python/"/>
    
    
      <category term="Python" scheme="http://lierchao.com/tags/Python/"/>
    
      <category term="编程语言" scheme="http://lierchao.com/tags/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/"/>
    
  </entry>
  
  <entry>
    <title>Git 学习</title>
    <link href="http://lierchao.com/2019/09/12/Git-%E5%AD%A6%E4%B9%A0/"/>
    <id>http://lierchao.com/2019/09/12/Git-学习/</id>
    <published>2019-09-12T02:17:15.000Z</published>
    <updated>2019-09-12T12:19:48.127Z</updated>
    
    <content type="html"><![CDATA[<p>@[toc]</p><blockquote><p>2018-12-09，博客园</p></blockquote><h1 id="教程"><a href="#教程" class="headerlink" title="教程"></a>教程</h1><ul><li><p><strong>【强烈推荐】</strong>GitHub项目：<a href="https://github.com/pcottle/learnGitBranching" target="_blank" rel="noopener">learnGitBranching-pcottle</a></p><blockquote><p>网站：<a href="https://learngitbranching.js.org/" target="_blank" rel="noopener">learnGitBranching</a></p></blockquote></li><li><p><a href="https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000" target="_blank" rel="noopener">廖雪峰的官方网站-Git教程</a></p></li></ul><blockquote><p>整理 <strong>Git</strong> 命令如下，按第一个教程顺序整理</p></blockquote><a id="more"></a><h1 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h1><h2 id="初始化命令"><a href="#初始化命令" class="headerlink" title="初始化命令"></a>初始化命令</h2><ul><li><code>git init</code><blockquote><p>在当前文件夹初始化一个本地仓库</p></blockquote></li></ul><h2 id="提交命令"><a href="#提交命令" class="headerlink" title="提交命令"></a>提交命令</h2><ul><li><p><code>git add</code></p><blockquote><p>将改动（文件变化）添加到到暂存区（缓冲区），以待正式提交（commit）</p></blockquote></li><li><p><code>git commit</code>（不建议单独使用）</p><blockquote><p>将暂存区的改动提交到Git，对其中文件进行一次快照，保存为一个节点</p></blockquote></li><li><p><code>git commit -m &lt;对提交的描述信息&gt;</code></p><blockquote><p>在提交变化的同时添加描述信息</p></blockquote></li><li><p><code>git commit --amend</code></p><blockquote><p>好像是将暂存区的修改直接提交到当前所在节点，而不是新建一个节点（提交）<br>有待检验</p></blockquote></li></ul><h2 id="分支命令"><a href="#分支命令" class="headerlink" title="分支命令"></a>分支命令</h2><ul><li><p><code>git branch &lt;新建分支名&gt; [目标节点]</code></p><blockquote><p>在目标节点新建一个分支<br>若不指定目标节点，则默认在当前节点（HEAD）新建分支</p></blockquote></li><li><p><code>git branch -f &lt;指定分支名&gt; &lt;目标节点位置&gt;</code></p><blockquote><p>此命令可以将指定分支强制指向目标节点<br>如<code>git branch -f master HEAD~4</code>命令的作用是使master强制指向HEAD所在节点的前4级节点<br>其中，<code>&lt;目标节点位置&gt;</code>可以是具体的节点哈希值，也可以是相对引用的节点</p></blockquote></li><li><p><code>git branch -u &lt;指定远程分支名&gt; [指定本地分支名]</code></p><blockquote><p>将指定远程分支与指定本地分支关联<br>如果省略<code>[本地分支名]</code>，则默认将当前分支与远程分支关联。[疑问]<sup><a href="#fn_1" id="reffn_1">1</a></sup></p></blockquote></li></ul><h2 id="切换命令"><a href="#切换命令" class="headerlink" title="切换命令"></a>切换命令</h2><ul><li><p><code>git checkout &lt;分支名/标签名/节点哈希值等&gt;</code></p><blockquote><p>切换到对应的分支、标签或节点。（实际上是改变<strong>HEAD</strong>的指向，“HEAD的指向”代表当前所在位置）</p></blockquote></li><li><p><code>git checkout -b &lt;新建分支名&gt;</code></p><blockquote><p>在当前节点新建一个分支，并立即切换到这一新建分支</p></blockquote></li><li><p><code>git checkout -b &lt;新建分支名&gt; &lt;目标远程分支名&gt;</code>[疑问]<sup><a href="#fn_3" id="reffn_3">3</a></sup></p><blockquote><p>在当前节点新建并切换到一个新分支，同时将其与指定的远程分支关联，用来跟踪这一远程分支</p></blockquote></li></ul><h3 id="相对引用"><a href="#相对引用" class="headerlink" title="相对引用"></a>相对引用</h3><ul><li><p><code>git checkout &lt;分支名/HEAD&gt;^</code></p><blockquote><p>将HEAD指向指定分支名指向节点的上一节点，即父节点。<br>或将HEAD指向HEAD的父节点<br>其中，<code>^</code>符号可以一次使用多个。每使用一个代表将HEAD往上一级节点移动一次<br>比如<code>git checkout master^^</code>命令表示<strong>将HEAD指针指向master指向节点的上两级节点</strong>（即上一级的上一级节点）</p></blockquote></li><li><p><code>git checkout &lt;分支名/HEAD&gt;~[数字]</code></p><blockquote><p>作用类比于上一条命令。但通过<code>[数字]</code>可以直接指定将HEAD往前指向的节点级数<br>若不加<code>[数字]</code>则默认上移一级，即<code>git checkout master~</code>与<code>git checkout master^</code>作用相同</p></blockquote></li><li><p><code>git checkout &lt;节点&gt;^[数字]</code></p><blockquote><p>数字指定回溯到哪一条分支<br>若没有数字，则默认回溯到直接分支（即合并操作执行时HEAD所在的分支）</p></blockquote></li><li><p><code>git checkout &lt;节点&gt;~^2~2</code></p><blockquote><p>支持链式操作，将多个切换命令合并为一条命令</p></blockquote></li></ul><h2 id="合并命令"><a href="#合并命令" class="headerlink" title="合并命令"></a>合并命令</h2><ul><li><p><code>git merge &lt;分支名&gt;</code><strong>（合并方法1）</strong></p><blockquote><p>将&lt;分支名&gt;指定的分支合并到<strong>当前</strong>所在分支<br>即以<strong>当前分支</strong>为主，最终合并的结果（最终节点）也是由当前分支来指向【疑问】<sup><a href="#fn_10" id="reffn_10">10</a></sup><br><code>merge</code>的优势在于可以保持<strong>提交历史</strong>的顺序、结构</p></blockquote></li><li><p><code>git rebase &lt;目标分支&gt; [移动分支]</code><strong>（合并方法2）</strong></p><blockquote><p>直译为“以目标分支（分支1）为基础，结合移动分支（分支2）进行梳理整合”<br>将<code>[移动分支]</code>的提交记录整理，复制一个副本合并到<code>[目标分支]</code>，得到一个线性提交记录。原提交记录依然存在。<br>同时将<code>[移动分支]</code>指向rebase得到的分支顶端节点（有待验证）<br>直译过来就是“将<code>[移动分支]</code>整合到<code>[目标分支]</code>”<br>若不指定<code>[移动分支]</code>，则默认移动<strong>当前分支</strong><br>优势在于可以保留干净、线性的提交树</p></blockquote></li></ul><h2 id="标签命令"><a href="#标签命令" class="headerlink" title="标签命令"></a>标签命令</h2><ul><li><code>git tag &lt;标签名&gt; [指定节点]</code><blockquote><p>将标签名标注于指定节点<br>若不指定节点，则加标签于当前节点（HEAD）</p></blockquote></li></ul><h2 id="查看命令"><a href="#查看命令" class="headerlink" title="查看命令"></a>查看命令</h2><ul><li><code>git log</code><blockquote><p>列出提交记录（具体列出全部还是当前分支有待确定）</p></blockquote></li></ul><h2 id="撤销变更命令-疑问-4"><a href="#撤销变更命令-疑问-4" class="headerlink" title="撤销变更命令[疑问]4"></a>撤销变更命令[疑问]<sup><a href="#fn_4" id="reffn_4">4</a></sup></h2><ul><li><p><code>git reset &lt;目标节点&gt;</code>（仅适用于本地分支）</p><blockquote><p>将当前分支指向目标节点<br>即忽略目标节点之后的所有提交<br>但被忽略的节点所作的变更依旧存在，只是处于未加入暂存区状态</p></blockquote></li><li><p><code>git revert &lt;节点&gt;</code>（适用于远程分支）</p><blockquote><p>撤销指定节点的修改，即回到指定节点父节点的状态<br>实现方式不同于<code>git reset &lt;目标节点&gt;</code>命令<br><code>git reset [目标节点]</code>仅仅通过修改指向实现，<br><code>git revert &lt;节点&gt;</code>是对当前节点与其父节点的差异重做而产生一个新的提交节点，得到指定节点的子节点，但是该子节点的状态与指定节点的父节点状态相同<br>此命令没有实际使用过，有待检验</p></blockquote></li></ul><h2 id="远程命令"><a href="#远程命令" class="headerlink" title="远程命令"></a>远程命令</h2><h3 id="克隆到本地"><a href="#克隆到本地" class="headerlink" title="克隆到本地"></a>克隆到本地</h3><ul><li><code>git clone &lt;ssh/https地址&gt;</code><blockquote><p>从远程仓端克隆远程仓库到本地</p></blockquote></li></ul><h3 id="从远程仓库拉取内容"><a href="#从远程仓库拉取内容" class="headerlink" title="从远程仓库拉取内容"></a>从远程仓库拉取内容</h3><ul><li><p><code>git fetch</code></p><blockquote><p>下载远程仓库所有分支及提交记录到本地各个远程分支</p></blockquote></li><li><p><code>git fetch &lt;remote&gt; &lt;place&gt;</code></p><blockquote><p>类比<code>push</code>命令的参数<br>可以理解为仅仅是将远程仓库内容下载下来，但是并没有对本地文件作出改动。但是会更新本地仓库的远程分支指向。<br>区别于<code>git pull</code>命令<br>不需要当前处于远程分支</p></blockquote></li><li><p><code>git fetch &lt;远程仓库名称&gt; &lt;指定的远程分支名&gt;</code>【疑问】<sup><a href="#fn_9" id="reffn_9">9</a></sup></p><blockquote><p>同时会更新本地仓库的对应的远程分支（单独的<code>fetch</code>会更新吗？）</p></blockquote></li><li><p><code>git fetch &lt;远程仓库名称&gt; &lt;源节点&gt;:&lt;本地目标分支&gt;</code>（实际很少用）</p><blockquote><p><code>&lt;源节点&gt;</code>是指远程仓库中的索引位置，<code>&lt;目标分支&gt;</code>才是本地分支了。类比<code>push</code>命令，刚好相反。<br>如果本地仓库不存在指定的<strong>目标分支</strong>，则Git自动在本地仓库（当前节点）创建一个同名分支，并执行操作<br>不更新本地的远程分支（？）</p></blockquote></li><li><p><code>git fetch &lt;远程仓库名称&gt; :&lt;本地目标分支&gt;</code></p><blockquote><p>命令自动在本地（当前节点）<strong>创建</strong>一个本地分支</p></blockquote></li><li><p><code>git pull &lt;&gt;</code></p><blockquote><p>相当于<code>git fetch</code>与<code>git merge</code>相继作用的效果<br>即不光下载远程内容并把本地仓库远程分支指向最新节点，同时将当前所在分支指向这一节点[疑问]<sup><a href="#fn_5" id="reffn_5">5</a></sup>【疑问】<sup><a href="#fn_8" id="reffn_8">8</a></sup></p></blockquote></li></ul><blockquote><p><code>git pull origin foo</code>相当于<code>git pull origin foo; git merge o/foo</code><br><code>git pull origin bar~1:bugFix</code>相当于<code>git fetch origin bar~1:bugFix; git merge bugFix</code><br>只关心提交<strong>最终</strong>合并到哪里</p></blockquote><ul><li><code>git pull --rebase</code><blockquote><p>相当于<code>git fetch</code> + <code>git rebase &lt;远程分支&gt;</code></p></blockquote></li></ul><h3 id="向远程仓库推送内容"><a href="#向远程仓库推送内容" class="headerlink" title="向远程仓库推送内容"></a>向远程仓库推送内容</h3><ul><li><p><code>git push &lt;remote&gt; &lt;place&gt;</code></p><blockquote><p>以上为<code>push</code>命令的标准表述</p></blockquote></li><li><p><code>git push &lt;远程仓库名称&gt; &lt;要推送的本地分支名&gt;</code></p><blockquote><p>可以直译为“将本地分支A推送到远程仓库1”<br>向指定远程仓库推送指定的本地分支的提交记录，并更新本地仓库中与指定本地分支相关联的远程分支[疑问已解决]<sup><a href="#fn_7" id="reffn_7">7</a></sup><br>[疑问]<sup><a href="#fn_2" id="reffn_2">2</a></sup></p></blockquote></li><li><p><code>git push &lt;远程仓库名称&gt; &lt;源节点&gt;:&lt;远程目标分支&gt;</code></p><blockquote><p>如果在远程仓库不存在指定的<strong>目标分支</strong>，则Git会在远程仓库自行创建一个新的分支</p></blockquote></li><li><p><code>git push &lt;远程仓库名称&gt; :&lt;远程目标分支&gt;</code></p><blockquote><p><strong>删除</strong>指定的远程分支</p></blockquote></li></ul><h1 id="进阶命令"><a href="#进阶命令" class="headerlink" title="进阶命令"></a>进阶命令</h1><h2 id="整理提交记录"><a href="#整理提交记录" class="headerlink" title="整理提交记录"></a>整理提交记录</h2><ul><li><code>git cherry-pick &lt;指定节点&gt; [指定节点] ...</code>（cherry-pick意为“精挑细选”）<blockquote><p>可以在一条命令中指定多个节点为执行对象（好像需要是节点的哈希值）可以是提交树上任意位置的节点[疑问]<sup><a href="#fn_6" id="reffn_6">6</a></sup>，但不能是当前节点上游的节点<br>命令的作用顾名思义，是<strong>选中</strong>指定节点，并按顺序将节点副本顺次附加到当前所在节点（HEAD）之后<br>或许这个命令可以直译为“<strong>精选</strong>”命令</p></blockquote></li></ul><h2 id="交互式rebase"><a href="#交互式rebase" class="headerlink" title="交互式rebase"></a>交互式rebase</h2><ul><li><code>git rebase -i &lt;分支名/节点/HEAD&gt;~[数字]</code><blockquote><p>使用参数<code>--interactive</code>（简写为<code>-i</code>）<br>尝试了一下，好像只能用HEAD作为索引？（经验证，不是）<br>数字n代表包括HEAD所在节点向上的n个节点<br>打开一个rebase UI界面</p><ul><li>调整提交记录的顺序（通过鼠标拖放）</li><li>删除不想要的提交</li><li>合并提交</li></ul></blockquote></li></ul><h2 id="描述节点"><a href="#描述节点" class="headerlink" title="描述节点"></a>描述节点</h2><ul><li><code>git describe &lt;索引&gt;</code><blockquote><p>作用是输出关于离<code>[索引]</code>最近的标签的信息<br>“最近”的意思仅限于同一条分支（有待验证）<br><code>&lt;索引&gt;</code>指所有可以指向具体节点的值，官方表述为“任何能被 Git 识别成提交记录的引用”<br>若不指定具体节点，则作用于当前节点（HEAD）</p></blockquote></li></ul><h1 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h1><ul><li><p>节点哈希值基于 SHA-1，共 40 位</p></li><li><p>分支名/HEAD等本质上类似一个指针，指向某个节点</p><blockquote><p>具体讲，分支名指向当前分支的最新节点；而HEAD指向当前状态实际所在的节点<br>同时，<code>git checkout [分支名/标签名/节点哈希值]</code>命令的本质是移动HEAD指针的指向</p></blockquote></li><li><p>本地仓库里的远程分支，或者说远程仓库分支对应的本地分支名（如果存在）不能手动移动。只有在与远程仓库同步时才能够自动修改。</p></li></ul><h1 id="疑问"><a href="#疑问" class="headerlink" title="疑问"></a>疑问</h1><ul><li><p>既然可以用rebase UI直接对节点重排，那各个提交记录到底是记录的“变化”还是文件本身的快照啊？</p><blockquote><p>如果只是变化的话，重拍之后怎么可能还可以正确应用呢？</p></blockquote></li><li><p>尝试了一下，<code>git rebase -i [索引]</code>好像只能用HEAD作为索引？</p><blockquote><p>不必。索引均可。</p></blockquote></li><li><p>对提交进行重排之后，所有后续节点都会产生相应改变？还是说因为记录的都是“变化”，所以改变内容不影响后续？</p></li><li><p>实际工作环境是否存在“本地仓库的远程分支”这一说法？</p><blockquote><p>在<strong>“远程跟踪”</strong>一节提到了，确实存在。<br>原话如下：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">当你克隆时, Git 会为远程仓库中的每个分支在本地仓库中创建一个远程分支（比如 o/master）。然后再创建一个跟踪远程仓库中活动分支的本地分支，默认情况下这个本地分支会被命名为 master。</span><br><span class="line"></span><br><span class="line">克隆完成后，你会得到一个本地分支（如果没有这个本地分支的话，你的目录就是“空白”的），但是可以查看远程仓库中所有的分支（如果你好奇心很强的话）。这样做对于本地仓库和远程仓库来说，都是最佳选择。</span><br></pre></td></tr></table></figure></blockquote></li><li><p>本地仓库有，而远程仓库没有的分支能否推送到远程端并自动创建一个新分支？</p></li></ul><blockquote id="fn_1"><sup>1</sup>. 如果当前没有指向具体分支，而是分离HEAD状态呢？<a href="#reffn_1" title="Jump back to footnote [1] in the text."> &#8617;</a></blockquote><blockquote id="fn_2"><sup>2</sup>. 本地仓库有，而远程仓库没有的分支能否推送到远程端并自动创建一个新分支？<a href="#reffn_2" title="Jump back to footnote [2] in the text."> &#8617;</a></blockquote><blockquote id="fn_3"><sup>3</sup>. 是否能够不指定“远程分支名”，从而默认关联某一远程分支<a href="#reffn_3" title="Jump back to footnote [3] in the text."> &#8617;</a></blockquote><blockquote id="fn_4"><sup>4</sup>. 没懂，看完再看一下。<a href="#reffn_4" title="Jump back to footnote [4] in the text."> &#8617;</a></blockquote><blockquote id="fn_5"><sup>5</sup>. 但是不更新在本地的远程分支？比如<code>o/master</code>，不会改变？<a href="#reffn_5" title="Jump back to footnote [5] in the text."> &#8617;</a></blockquote><blockquote id="fn_6"><sup>6</sup>. <code>git cherry-pick</code>命令的作用对象是否可以是<strong>任意节点</strong>（除当前分支的上游节点外）？<a href="#reffn_6" title="Jump back to footnote [6] in the text."> &#8617;</a></blockquote><blockquote id="fn_7"><sup>7</sup>. 若关联的本地分支与远程分支名称不一致，则在<code>push</code>命令中是以远程分支名为准吗？试一试。（顺便试一试脚注的冒号用中文冒号行不行）<a href="#reffn_7" title="Jump back to footnote [7] in the text."> &#8617;</a></blockquote><blockquote><p>显然，中文冒号不行<br>经试验，<code>push</code>命令中指定的<strong>分支名</strong>以本地分支为准<br>故可以直译为“将本地分支A推送到远程仓库1”</p></blockquote><blockquote id="fn_8"><sup>8</sup>. 是否仅限于当前分支？试一试<a href="#reffn_8" title="Jump back to footnote [8] in the text."> &#8617;</a></blockquote><blockquote id="fn_9"><sup>9</sup>. 应该也是对应于<code>push</code>命令，<code>fetch</code>则以<strong>远程分支名</strong>为准，将指定的远程分支的记录全部拉取到本地与之相关联的分支上。试一试<a href="#reffn_9" title="Jump back to footnote [9] in the text."> &#8617;</a></blockquote><p>单独的<code>fetch</code>会更新本地仓库的远程分支吗？试一试</p><blockquote id="fn_10"><sup>10</sup>. 若当前指向一个具体节点而不是某一个分支名呢？即处在分离HEAD状态，且可以更进一步，HEAD指向的节点没有其他任何分支同时指向，会发生什么？<a href="#reffn_10" title="Jump back to footnote [10] in the text."> &#8617;</a></blockquote><p>单纯移动HEAD？</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;@[toc]&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2018-12-09，博客园&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;教程&quot;&gt;&lt;a href=&quot;#教程&quot; class=&quot;headerlink&quot; title=&quot;教程&quot;&gt;&lt;/a&gt;教程&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;【强烈推荐】&lt;/strong&gt;GitHub项目：&lt;a href=&quot;https://github.com/pcottle/learnGitBranching&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;learnGitBranching-pcottle&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;网站：&lt;a href=&quot;https://learngitbranching.js.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;learnGitBranching&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;廖雪峰的官方网站-Git教程&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;整理 &lt;strong&gt;Git&lt;/strong&gt; 命令如下，按第一个教程顺序整理&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="计算机" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="编程工具" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E7%BC%96%E7%A8%8B%E5%B7%A5%E5%85%B7/"/>
    
    
      <category term="Git" scheme="http://lierchao.com/tags/Git/"/>
    
  </entry>
  
  <entry>
    <title>阅读《深度学习入门：基于Python的理论与实现》的一些问题</title>
    <link href="http://lierchao.com/2019/09/10/%E9%98%85%E8%AF%BB%E3%80%8A%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8%EF%BC%9A%E5%9F%BA%E4%BA%8EPython%E7%9A%84%E7%90%86%E8%AE%BA%E4%B8%8E%E5%AE%9E%E7%8E%B0%E3%80%8B%E7%9A%84%E4%B8%80%E4%BA%9B%E9%97%AE%E9%A2%98/"/>
    <id>http://lierchao.com/2019/09/10/阅读《深度学习入门：基于Python的理论与实现》的一些问题/</id>
    <published>2019-09-10T10:10:37.000Z</published>
    <updated>2019-09-10T12:32:47.169Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一些问题"><a href="#一些问题" class="headerlink" title="一些问题"></a>一些问题</h1><blockquote><p>是 2019-04-03 看书时写下的</p></blockquote><a id="more"></a><h2 id="关于展开为二维数组后转置规律的探究（p208-p221）"><a href="#关于展开为二维数组后转置规律的探究（p208-p221）" class="headerlink" title="关于展开为二维数组后转置规律的探究（p208-p221）"></a>关于展开为二维数组后转置规律的探究（p208-p221）</h2><ul><li><p>对输入数据展开后，其维度情况是$(行数=数据个数\times 单个数据卷积次数, 列数=输入通道数\times 卷积核的宽\times 卷积核的高)$</p><blockquote><p>若把输入数据维度情况记为$(N, C, H, W)$，卷积核维度情况记为$(FN, C, FH, FW)$，输出数据维度情况记为$(ON, OC, OH, OW)$，则有以下关系：</p><script type="math/tex; mode=display">ON=N</script><script type="math/tex; mode=display">OC=FN</script><script type="math/tex; mode=display">OH=\frac{H+2P-FH}{S}+1</script><script type="math/tex; mode=display">OW=\frac{W+2P-FW}{S}+1</script><blockquote><p>$N$个输入数据展开的二维数组形状为$(N\times OH\times OW, C\times FH\times FW)$</p><blockquote><p>其中，行数中$(OW\times OH)$为单个输出数据的维度情况，亦即对单个输入数据需要卷积的次数；而$N$则是输入数据的个数，也是输出数据的个数。完整的行数表示只有一个卷积核时，$N$个输入数据对应的输出元素个数。<br>列数中，$C$为输入数据的通道数；而$(FH\times FW)$则代表卷积核的维度情况，其值是卷积核的元素个数。完整的列数表示对具有$C$个输入通道的输入图像，一次卷积运算需要运算的区域对应的输入元素个数。</p></blockquote><p>$FN$个卷积核展开的二维数组形状为$(C\times FH\times FW, FN)$</p><blockquote><p>其中，行数$(C\times FH\times FW)$的解释如上的列数。<br>列数$FN$则为卷积核的个数，也就是输出数据的通道数。</p></blockquote></blockquote><p>按照数组的排列规则，其排列顺序是优先排最低维度。比如对二维数组(5, 3)，其排完一行的元素个数为 3 ，排完一行再排第二行，也就是说，离首元素最近的元素首先是第一行的其他元素，而不是下一行的首元素。在reshape的时候必须要考虑到元素的顺序问题。<br>数组维度规律是越靠前维度越高，新增的更高维因此也必须加在数组维度的最左边。比如从$Width$维度开始，增加了一个$Height$维度，则数组变为$(Height, Width)$，再增加一个通道维度$Channel$，则数组维度变为$(Channel, Height, Width)$，以此类推。</p></blockquote></li><li><p>输出数据在进行<code>reshape</code>操作前，维度情况是$(行数=数据个数\times 单个数据卷积次数, 列数=卷积核个数=输出通道数)$</p><blockquote><p>输出数据的维度情况用字母表达则为$(N\times OH\times OW, FN)$<br>对应为最终需要的输出，可以表示为$(ON\times OH\times OW, OC)$，经过<code>reshape</code>调整之后维度情况为$(ON, OH, OW, OC)$而需要的输出是$(ON, OC, OH, OW)$，以便作为下一次的输入。<br>所以对<code>reshape</code>的结果，还需要通过<code>transpose</code>更改轴的顺序，把原先的(0,1,2,3)转换为(0,3,1,2)</p></blockquote></li></ul><p>突然想到一个类比：数组的高维、低维就像数制中的高位、低位一样，数先在低位排，排满一个低位之后，则向高位进1，然后继续排列下一个高位的一系列低位。并且数制的进位也是让高位在左，低位在右。十分有趣。数制也可以理解为维度的一种特殊形式，其各维具有相同的元素个数。数组的高维和低维之间也存在一种类似的“进位”关系。</p><h2 id="关于“为什么二维数组的第0轴是列方向”"><a href="#关于“为什么二维数组的第0轴是列方向”" class="headerlink" title="关于“为什么二维数组的第0轴是列方向”"></a>关于“为什么二维数组的第0轴是列方向”</h2><p>从上面的分析可以看出来，多维数组对轴的编号是从左往右的，也就是说，对二维数组(y, x)来说，x方向是第0轴，y方向是第1轴；对三维数组(z, y, x)来说，第0轴则是z轴，第1轴是y轴，第2轴是x轴。</p><h2 id="图书链接"><a href="#图书链接" class="headerlink" title="图书链接"></a>图书链接</h2><p>《<a href="https://www.ituring.com.cn/book/1921" target="_blank" rel="noopener">深度学习入门：基于Python的理论与实现</a>》（图灵社区）</p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;一些问题&quot;&gt;&lt;a href=&quot;#一些问题&quot; class=&quot;headerlink&quot; title=&quot;一些问题&quot;&gt;&lt;/a&gt;一些问题&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;是 2019-04-03 看书时写下的&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="计算机" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="深度学习" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    
    
      <category term="机器学习" scheme="http://lierchao.com/tags/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
    
  </entry>
  
  <entry>
    <title>CSDN上markdown文档图片显示问题</title>
    <link href="http://lierchao.com/2019/09/10/CSDN%E4%B8%8Amarkdown%E6%96%87%E6%A1%A3%E5%9B%BE%E7%89%87%E6%98%BE%E7%A4%BA%E9%97%AE%E9%A2%98/"/>
    <id>http://lierchao.com/2019/09/10/CSDN上markdown文档图片显示问题/</id>
    <published>2019-09-10T10:01:51.000Z</published>
    <updated>2019-09-10T10:05:53.011Z</updated>
    
    <content type="html"><![CDATA[<h1 id="CSDN上markdown文档图片显示问题"><a href="#CSDN上markdown文档图片显示问题" class="headerlink" title="CSDN上markdown文档图片显示问题"></a>CSDN上markdown文档图片显示问题</h1><p>刚刚上传了一篇博客到 CSDN，因为使用的本地编辑器是<code>Typora</code>，本身支持<code>HTML</code>语法，用习惯了之后也喜欢在<code>markdown</code>之外再定制一些显示格式。</p><p>在显示图片失败的文章中我是用的<code>img</code>标签来定制图片的缩放大小，在<code>Typora</code>中显示完全正常，因此也就没有多想直接上传了。但上传之后查看效果才发现三张图片都无法正常显示，于是赶紧排查原因。</p><a id="more"></a><p>看到有的<a href="https://blog.csdn.net/sinat_40692412/article/details/78828940" target="_blank" rel="noopener">博主</a>说是因为 CSDN 要手动上传图片，给我惊了一把。不过再看下时间大概是两年前了，总觉得 CSDN 不能这么坑吧，于是继续排查。发现个人博客使用的 hexo 也无法正确渲染<code>img</code>标签，顿时仿佛明白了什么。于是哼哧哼哧把几张图片的格式都改回了 markdown 的原生格式，终于显示正常了。</p><p>所以结果证明 CSDN 自带的 markdown 编辑器不支持 HTML 语法，以鉴后来。</p><p>是记。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="https://blog.csdn.net/sinat_40692412/article/details/78828940" target="_blank" rel="noopener"><strong>CSDN图片显示不出解决方法！（新手之坑）</strong></a></p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;CSDN上markdown文档图片显示问题&quot;&gt;&lt;a href=&quot;#CSDN上markdown文档图片显示问题&quot; class=&quot;headerlink&quot; title=&quot;CSDN上markdown文档图片显示问题&quot;&gt;&lt;/a&gt;CSDN上markdown文档图片显示问题&lt;/h1&gt;&lt;p&gt;刚刚上传了一篇博客到 CSDN，因为使用的本地编辑器是&lt;code&gt;Typora&lt;/code&gt;，本身支持&lt;code&gt;HTML&lt;/code&gt;语法，用习惯了之后也喜欢在&lt;code&gt;markdown&lt;/code&gt;之外再定制一些显示格式。&lt;/p&gt;
&lt;p&gt;在显示图片失败的文章中我是用的&lt;code&gt;img&lt;/code&gt;标签来定制图片的缩放大小，在&lt;code&gt;Typora&lt;/code&gt;中显示完全正常，因此也就没有多想直接上传了。但上传之后查看效果才发现三张图片都无法正常显示，于是赶紧排查原因。&lt;/p&gt;
    
    </summary>
    
    
      <category term="计算机" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="个人网站" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E4%B8%AA%E4%BA%BA%E7%BD%91%E7%AB%99/"/>
    
    
      <category term="博客" scheme="http://lierchao.com/tags/%E5%8D%9A%E5%AE%A2/"/>
    
  </entry>
  
  <entry>
    <title>HEXO博客使用LeanCloud报错“Counter not initialized! See more at console err msg.”</title>
    <link href="http://lierchao.com/2019/09/10/HEXO%E5%8D%9A%E5%AE%A2%E4%BD%BF%E7%94%A8LeanCloud%E6%8A%A5%E9%94%99%E2%80%9CCounter-not-initialized-See-more-at-console-err-msg-%E2%80%9D/"/>
    <id>http://lierchao.com/2019/09/10/HEXO博客使用LeanCloud报错“Counter-not-initialized-See-more-at-console-err-msg-”/</id>
    <published>2019-09-10T09:11:01.000Z</published>
    <updated>2019-09-11T03:13:40.987Z</updated>
    
    <content type="html"><![CDATA[<h1 id="HEXO博客使用LeanCloud报错“Counter-not-initialized-See-more-at-console-err-msg-”"><a href="#HEXO博客使用LeanCloud报错“Counter-not-initialized-See-more-at-console-err-msg-”" class="headerlink" title="HEXO博客使用LeanCloud报错“Counter not initialized! See more at console err msg.”"></a>HEXO博客使用LeanCloud报错“Counter not initialized! See more at console err msg.”</h1><blockquote><p>CSDN 的 Markdown 编辑器好像不像<code>Typora</code>那样能够正确渲染 <code>.md</code> 文档中夹杂的 HTML 标签，所以导致之前以 HTML 语法插入的图片在 CSDN 上无法正确显示</p><h2 id="具体问题"><a href="#具体问题" class="headerlink" title="具体问题"></a>具体问题</h2></blockquote><p>在根据文档配置好LeanCloud并完成部署之后，刷新博客网页，在主页看到每篇文章的阅读次数都显示为0，而打开每篇文章的相应页面更是发现错误信息：<code>Counter not initialized! More info at console err msg.</code>。<a id="more"></a>具体情况如图所示：</p><p><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly93czEuc2luYWltZy5jbi9sYXJnZS8wMDZjTWJ5SWx5MWc2dWlxN3V3ZDhqMzB0aTA1ZWRnNC5qcGc?x-oss-process=image/format,png" alt="有报错信息的情况"></p><blockquote><p>此外在之前还遇到“阅读次数”不显示或者显示为<code>NaN/NaN</code>的情况，检查发现是在 leancloud 中配置的时候习惯性将自己的自定义域名协议写成了<code>HTTPS</code>，但实际上是用的<code>HTTP</code>协议。因此修改之后就出现了上面的情况。</p><p>如图所示：<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly93czEuc2luYWltZy5jbi9sYXJnZS8wMDZjTWJ5SWx5MWc2dWlvaG14N2VqMzBsZTA0azN5cC5qcGc?x-oss-process=image/format,png" alt="协议错误的显示情况"></p></blockquote><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><p>在搜索过程中发现有人也遇到过这个问题，于是按照《<a href="https://popwalker.github.io/article/d6ed9cc7/" target="_blank" rel="noopener">hexo使用笔记</a>》中的描述，安装了插件<code>hexo-leancloud-counter-security</code>（命令行执行<code>npm install hexo-leancloud-counter-security</code>）</p><p>，并将主题配置文件<code>_config.yml</code>中的对应项<code>security</code>置为<code>false</code>。</p><p>具体位置如下（第 8 行）：</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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="attr">leancloud_visitors:</span></span><br><span class="line"><span class="attr">  enable:</span> <span class="literal">true</span></span><br><span class="line"><span class="attr">  app_id:</span> <span class="comment"># &lt;app_id&gt;</span></span><br><span class="line"><span class="attr">  app_key:</span> <span class="comment"># &lt;app_key&gt;</span></span><br><span class="line">  <span class="comment"># Dependencies: https://github.com/theme-next/hexo-leancloud-counter-security</span></span><br><span class="line">  <span class="comment"># If you don't care about security in leancloud counter and just want to use it directly</span></span><br><span class="line">  <span class="comment"># (without hexo-leancloud-counter-security plugin), set `security` to `false`.</span></span><br><span class="line"><span class="attr">  security:</span> <span class="literal">false</span></span><br><span class="line"><span class="attr">  betterPerformance:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure><p>这样配置之后，就可以看到页面中的数据能够正常显示了。如下图：</p><p><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly93czEuc2luYWltZy5jbi9sYXJnZS8wMDZjTWJ5SWx5MWc2dWoyZGRjMjBqMzBscDA0a3Q4eC5qcGc?x-oss-process=image/format,png" alt="正确配置后的显示效果"></p><h2 id="参考文档"><a href="#参考文档" class="headerlink" title="参考文档"></a>参考文档</h2><p><a href="https://popwalker.github.io/article/d6ed9cc7/" target="_blank" rel="noopener">hexo使用笔记</a></p><blockquote><p>注：原文档中建议的是两种方法：1）使用<code>hexo-leancloud-counter-security</code>插件；2）设置<code>leancloud_visitors.security = false</code>均可。</p><p>但考虑到主题配置文件中关于安全性的提醒，我是按步骤分别完成了这两步。并且只安装<code>hexo-leancloud-counter-security</code>插件依然不能解决报错问题，可能真正生效的是“将<code>security</code>置为<code>false</code>”这一步。</p><p>但出于安全考虑，建议大家也同时安装<code>hexo-leancloud-counter-security</code>插件。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;HEXO博客使用LeanCloud报错“Counter-not-initialized-See-more-at-console-err-msg-”&quot;&gt;&lt;a href=&quot;#HEXO博客使用LeanCloud报错“Counter-not-initialized-See-more-at-console-err-msg-”&quot; class=&quot;headerlink&quot; title=&quot;HEXO博客使用LeanCloud报错“Counter not initialized! See more at console err msg.”&quot;&gt;&lt;/a&gt;HEXO博客使用LeanCloud报错“Counter not initialized! See more at console err msg.”&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;CSDN 的 Markdown 编辑器好像不像&lt;code&gt;Typora&lt;/code&gt;那样能够正确渲染 &lt;code&gt;.md&lt;/code&gt; 文档中夹杂的 HTML 标签，所以导致之前以 HTML 语法插入的图片在 CSDN 上无法正确显示&lt;/p&gt;
&lt;h2 id=&quot;具体问题&quot;&gt;&lt;a href=&quot;#具体问题&quot; class=&quot;headerlink&quot; title=&quot;具体问题&quot;&gt;&lt;/a&gt;具体问题&lt;/h2&gt;&lt;/blockquote&gt;
&lt;p&gt;在根据文档配置好LeanCloud并完成部署之后，刷新博客网页，在主页看到每篇文章的阅读次数都显示为0，而打开每篇文章的相应页面更是发现错误信息：&lt;code&gt;Counter not initialized! More info at console err msg.&lt;/code&gt;。
    
    </summary>
    
    
      <category term="计算机" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="个人网站" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E4%B8%AA%E4%BA%BA%E7%BD%91%E7%AB%99/"/>
    
    
      <category term="博客" scheme="http://lierchao.com/tags/%E5%8D%9A%E5%AE%A2/"/>
    
      <category term="个人网站" scheme="http://lierchao.com/tags/%E4%B8%AA%E4%BA%BA%E7%BD%91%E7%AB%99/"/>
    
  </entry>
  
  <entry>
    <title>CSAPP读书笔记-02</title>
    <link href="http://lierchao.com/2019/09/09/CSAPP%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-02/"/>
    <id>http://lierchao.com/2019/09/09/CSAPP读书笔记-02/</id>
    <published>2019-09-08T16:05:54.000Z</published>
    <updated>2019-09-08T16:06:51.945Z</updated>
    
    <content type="html"><![CDATA[<h1 id="CSAPP读书笔记-02"><a href="#CSAPP读书笔记-02" class="headerlink" title="CSAPP读书笔记-02"></a>CSAPP读书笔记-02</h1><h2 id="浮点数陷阱"><a href="#浮点数陷阱" class="headerlink" title="浮点数陷阱"></a>浮点数陷阱</h2><p>在第二章最初，给出了“浮点运算是不可结合的”这样一个结论，乍一看有点令人费解，但仔细看了看例子，发现其实结合浮点数在内存中的存储方式反而很容易理解，不过就是这种表达很容易让人误会，颇不可取。</p><p>实际上由于浮点数在内存中存储时，计算机存储的是它的尾数，因此在一个数的基础上，给它加上或者减去太小的浮点值，都会导致最终产生截断，表现出来就是“什么也没有发生”的效果。</p><a id="more"></a><p>书中提到的：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(<span class="number">3.14</span> + <span class="number">1e20</span>)<span class="number">-1e20</span></span><br></pre></td></tr></table></figure><p>正是这样一个例子。</p><p>其中的<code>1e20</code>远大于<code>3.14</code>，于是实际发生的事情就是<code>(3.14+1e20)</code>所得的结果为<code>1e20</code>，再减去<code>1e20</code>，于是就得到了看起来很不合理的<code>0</code>。因此在使用浮点数的时候一定要注意精度的问题。</p><p>如果一定要在跨度很大的范围内进行浮点数运算，最好将同一优先级下，数值范围相近的操作数先运算。</p><h2 id="“虚拟内存”的抽象"><a href="#“虚拟内存”的抽象" class="headerlink" title="“虚拟内存”的抽象"></a>“虚拟内存”的抽象</h2><blockquote><p>大多数计算机的最小可寻址内存单位就是“<strong style="color: blue">字节</strong>”。</p><p>机器级程序将内存视为一个非常大的字节数组，称为<strong>虚拟内存</strong>。</p></blockquote><p>这里又复习了一下第一章提到的“抽象”概念，所谓“虚拟内存”，就是对主存和I/O设备的抽象。并且这段话还具体讲了一下“虚拟内存” 抽象的方式，使得这个抽象概念更加明晰。</p><blockquote><p>每台计算机都有一个<strong style="color: blue">字长</strong>（word size），指明指针数据的标称大小。</p></blockquote><p>这里的“字长”可以比较“字节”的概念。“字长”并不等于“字节”，一个“字”可能包含多个字节，并且是取决于机器的位数的。</p><p>所谓“指针数据的标称大小”，我的理解就是在这台机器上存储某个虚拟内存地址的指针变量的位数。更进一步说，应该就是PC寄存器（程序计数器）的大小，因此PC寄存器的大小（也就是“字长”）限制了计算机的最大寻址范围，超出“字长”所能表示的最大值的虚拟内存地址对计算机来说是没有意义的。</p><p>并且还要注意的是，编译结果的二进制程序是32位还是64位并不取决于机器，而是取决于具体使用的编译器和编译方式。</p><h2 id="大端法和小端法"><a href="#大端法和小端法" class="headerlink" title="大端法和小端法"></a>大端法和小端法</h2><p>之前忘记在哪儿看到过关于大端法和小端法的描述，但因为毕竟不是CS专业，了解了一下也没有记住，今天看书的时候倒是认真推敲了一下，发现了一些有趣的东西，也对这两种存储顺序有了更深的理解和记忆。</p><blockquote><p>最低有效位在最前面的（存储）方式，称为<strong>小端法</strong>（little endian）</p><p>最高有效位在最前面的（存储）方式，称为<strong>大端法</strong>（big endian）</p></blockquote><p>其实按人类的正常思维和书写习惯，很显然大端法是一种更加自然的方式，但却很少被采用，连两种主流的移动端操作系统都没落着；而小端法则是一种更加符合逻辑的存储顺序，可能也更加适合在计算机当中表达。</p><p>说是这么说，但实际上转念一想，为什么大端法存储顺序就更贴近人的思维了呢？其实就连在书面上表达的时候，将内存单元按地址增大的顺序从左往右排列也不过是人为规定的而已；对计算机来说，地址的高位、低位并不存在左右之别，反而将低字节放在低地址更便于机器操作。</p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;CSAPP读书笔记-02&quot;&gt;&lt;a href=&quot;#CSAPP读书笔记-02&quot; class=&quot;headerlink&quot; title=&quot;CSAPP读书笔记-02&quot;&gt;&lt;/a&gt;CSAPP读书笔记-02&lt;/h1&gt;&lt;h2 id=&quot;浮点数陷阱&quot;&gt;&lt;a href=&quot;#浮点数陷阱&quot; class=&quot;headerlink&quot; title=&quot;浮点数陷阱&quot;&gt;&lt;/a&gt;浮点数陷阱&lt;/h2&gt;&lt;p&gt;在第二章最初，给出了“浮点运算是不可结合的”这样一个结论，乍一看有点令人费解，但仔细看了看例子，发现其实结合浮点数在内存中的存储方式反而很容易理解，不过就是这种表达很容易让人误会，颇不可取。&lt;/p&gt;
&lt;p&gt;实际上由于浮点数在内存中存储时，计算机存储的是它的尾数，因此在一个数的基础上，给它加上或者减去太小的浮点值，都会导致最终产生截断，表现出来就是“什么也没有发生”的效果。&lt;/p&gt;
    
    </summary>
    
    
      <category term="计算机" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="基础知识" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"/>
    
      <category term="读书笔记" scheme="http://lierchao.com/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
    
      <category term="读书笔记" scheme="http://lierchao.com/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
      <category term="计算机基础知识" scheme="http://lierchao.com/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"/>
    
  </entry>
  
  <entry>
    <title>执行 hexo d 命令时出现的ERROR Deployer not found 错误</title>
    <link href="http://lierchao.com/2019/09/08/%E6%89%A7%E8%A1%8C-hexo-d-%E5%91%BD%E4%BB%A4%E6%97%B6/"/>
    <id>http://lierchao.com/2019/09/08/执行-hexo-d-命令时/</id>
    <published>2019-09-08T15:02:49.000Z</published>
    <updated>2019-09-08T15:08:31.303Z</updated>
    
    <content type="html"><![CDATA[<h1 id="执行hexo-d命令时出现的ERROR-Deployer-not-found-git错误"><a href="#执行hexo-d命令时出现的ERROR-Deployer-not-found-git错误" class="headerlink" title="执行hexo d命令时出现的ERROR Deployer not found: git错误"></a>执行<code>hexo d</code>命令时出现的ERROR Deployer not found: git错误</h1><h2 id="具体问题"><a href="#具体问题" class="headerlink" title="具体问题"></a>具体问题</h2><p>前两天折腾博客时，在静态页面生成完成之后要部署到GitHub上时遇到了<code>ERROR Deployer not found: git</code>的问题。</p><a id="more"></a><p>但是奇怪的是我都是按照文档一步步设置的，其中博客根目录中的配置文件<code>_config.yml</code>中写入了如下内容：</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">deploy:</span></span><br><span class="line"><span class="attr">  type:</span> <span class="string">git</span></span><br><span class="line"><span class="attr">  repo:</span> <span class="attr">https://github.com/lierchao1997/lierchao1997.github.io.git</span> <span class="comment">#https://bitbucket.org/JohnSmith/johnsmith.bitbucket.io</span></span><br><span class="line"><span class="attr">  branch:</span> <span class="string">master</span> <span class="comment">#published</span></span><br></pre></td></tr></table></figure><p>同时也执行相应命令安装了相应的插件：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm install hexo-deployer-git --save</span><br></pre></td></tr></table></figure><p>但在执行<code>hexo d</code>命令的过程中却遇到了上述问题。</p><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><p>最后发现是因为在安装插件时“当前工作目录”不在博客根目录下，所以<code>hexo</code>没有找到这个插件。注意此处的“<strong>博客根目录</strong>”。不是创建博客文件夹时所在的文件夹，而是执行命令<code>hexo init [博客文件夹]</code>时指定的<code>[博客文件夹]</code>。</p><p>要执行<code>cd [博客文件夹]</code>，将命令行当前目录跳转到为博客创建的新文件夹中去，并且保持这个路径不要再进入下一级目录。此时再来执行插件安装命令，即可正确安装相应插件。</p><p>到这里，再执行<code>hexo d</code>命令即可正确部署静态页面。</p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;执行hexo-d命令时出现的ERROR-Deployer-not-found-git错误&quot;&gt;&lt;a href=&quot;#执行hexo-d命令时出现的ERROR-Deployer-not-found-git错误&quot; class=&quot;headerlink&quot; title=&quot;执行hexo d命令时出现的ERROR Deployer not found: git错误&quot;&gt;&lt;/a&gt;执行&lt;code&gt;hexo d&lt;/code&gt;命令时出现的ERROR Deployer not found: git错误&lt;/h1&gt;&lt;h2 id=&quot;具体问题&quot;&gt;&lt;a href=&quot;#具体问题&quot; class=&quot;headerlink&quot; title=&quot;具体问题&quot;&gt;&lt;/a&gt;具体问题&lt;/h2&gt;&lt;p&gt;前两天折腾博客时，在静态页面生成完成之后要部署到GitHub上时遇到了&lt;code&gt;ERROR Deployer not found: git&lt;/code&gt;的问题。&lt;/p&gt;
    
    </summary>
    
    
      <category term="计算机" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="个人网站" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E4%B8%AA%E4%BA%BA%E7%BD%91%E7%AB%99/"/>
    
    
      <category term="博客" scheme="http://lierchao.com/tags/%E5%8D%9A%E5%AE%A2/"/>
    
      <category term="个人网站" scheme="http://lierchao.com/tags/%E4%B8%AA%E4%BA%BA%E7%BD%91%E7%AB%99/"/>
    
  </entry>
  
  <entry>
    <title>Hello World</title>
    <link href="http://lierchao.com/2019/09/06/hello-world/"/>
    <id>http://lierchao.com/2019/09/06/hello-world/</id>
    <published>2019-09-06T13:22:18.899Z</published>
    <updated>2019-09-07T04:33:41.957Z</updated>
    
    <content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/" target="_blank" rel="noopener">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html" target="_blank" rel="noopener">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues" target="_blank" rel="noopener">GitHub</a>.</p><a id="more"></a><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html" target="_blank" rel="noopener">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html" target="_blank" rel="noopener">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html" target="_blank" rel="noopener">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/deployment.html" target="_blank" rel="noopener">Deployment</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Welcome to &lt;a href=&quot;https://hexo.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Hexo&lt;/a&gt;! This is your very first post. Check &lt;a href=&quot;https://hexo.io/docs/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;documentation&lt;/a&gt; for more info. If you get any problems when using Hexo, you can find the answer in &lt;a href=&quot;https://hexo.io/docs/troubleshooting.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;troubleshooting&lt;/a&gt; or you can ask me on &lt;a href=&quot;https://github.com/hexojs/hexo/issues&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
    
    </summary>
    
    
    
  </entry>
  
  <entry>
    <title>CSAPP读书笔记-01</title>
    <link href="http://lierchao.com/2019/09/01/CSAPP%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-01/"/>
    <id>http://lierchao.com/2019/09/01/CSAPP读书笔记-01/</id>
    <published>2019-09-01T04:19:03.000Z</published>
    <updated>2019-09-08T02:43:58.093Z</updated>
    
    <content type="html"><![CDATA[<h1 id="《深入理解计算机系统》读书笔记-01"><a href="#《深入理解计算机系统》读书笔记-01" class="headerlink" title="《深入理解计算机系统》读书笔记-01"></a>《深入理解计算机系统》读书笔记-01</h1><blockquote><p>《深入理解计算机系统（原书第 3 版）》</p><p>英文版简称CS: APP</p><p>2017 年 12 月第 1 版第 6 次印刷</p><p>ISBN 978-7-111-54493-7</p></blockquote><a id="more"></a><h2 id="信息-位-上下文"><a href="#信息-位-上下文" class="headerlink" title="信息 = 位 + 上下文"></a>信息 = 位 + 上下文</h2><h3 id="一切皆比特"><a href="#一切皆比特" class="headerlink" title="一切皆比特"></a>一切皆比特</h3><p>CSAPP 开篇就提到一个概念：信息 = 位 + 上下文</p><p>在计算机内部，“<strong>一切皆比特</strong>”，所有信息都以二进制序列的形式存储、传输、处理和表示，但同样是二进制序列，在不同的情境下就有不同的解释方法。比如同样是四字节二进制序列，可以解释为一个整数，也可以解释为四个ASCII码表示的字符；既可以是某个具体的32位机器指令，又可以是待处理的浮点数……</p><p>所谓“读到这些数据对象时的<strong>上下文</strong>”，实际上就是指的这些二进制序列被呈现时的<strong>具体情境</strong>，是被处理的数值，还是待执行的指令，抑或是要传输的字符，“信息”的意义并不取决于二进制序列本身长什么样，而更多地依赖于它在什么情况下被解释。</p><h3 id="文本文件和二进制文件"><a href="#文本文件和二进制文件" class="headerlink" title="文本文件和二进制文件"></a>文本文件和二进制文件</h3><p>在 CSAPP 第 2 页有一个论断是“只由 ASCII 字符构成的文件称为<strong>文本文件</strong>，所有其他文件都称为<strong>二进制文件</strong>”。这样的说法其实很有意思，理论上来讲，计算机内部所有文件都应该是“二进制文件”，却单单拉出来一个“文本文件”离群索居。</p><p>之前在编程的时候有时会感到奇怪，读取/写入文件时有一个模式是“以二进制格式读取/写入”，为什么就偏是“二进制格式”呢？看到 CSAPP 相关论述的时候突然福至心灵，实际上计算机内部存储的信息大体上被分为两种类型：一种是给人看的，即文本信息；一种是给机器看的，即二进制信息。</p><p>信息要表示给人看，就肯定要以一个个字符的形式呈现，不限于 ASCII 码的字符，GBK、Unicode这些都行，但一定要是字符编码，因为对人而言一串串的二进制序列并没有任何直观的意义，只有这些序列能够被依次解读为一个个连续的字符时才能被人获取到其中的信息。而机器之间的通信则不然，二进制就是机器的语言，就是机器的文字，既不必呈现给人类，也不必再进一步地转换浪费更多时间和空间，高效并且准确。</p><p>与其说计算机内部的文件分为“文本文件”和“二进制文件”，倒不如说是计算机内部文件分为“<strong>给人看的</strong>”和“<strong>给机器看的</strong>”来得贴切。</p><h3 id="关于“上下文”的思考"><a href="#关于“上下文”的思考" class="headerlink" title="关于“上下文”的思考"></a>关于“上下文”的思考</h3><p>不出意外的话，这部分中提到的上下文对应的英文应该是 context，但这里存在一个疑问：“上下文”这个术语在当前的计算机领域内有种被滥用的嫌疑。只要出现了 context 这个单词，不管三七二十一大家都把它译为“上下文”。但实际上作为使用汉语的中国人应该意识到，在很多出现单词 context 的地方，将其译为”情境“、”背景“甚至是”语境“，都比译作”上下文“更好也更准确。</p><p>举例来说，下面这句话：</p><blockquote><p>In the context of AI, the brain is essentially an advanced piece of technology that we must study, reverse engineer, and learn to emulate.</p></blockquote><p>译作：</p><blockquote><p>而在人工智能的学科背景下，大脑本质上就是一种深奥繁复的技术，我们有必要对它进行研究，通过逆向工程来解析它的工作原理和机制，从而模仿它的功能。</p></blockquote><p>显然要比译为：</p><blockquote><p>而在人工智能的上下文中，大脑本质上就是一种深奥繁复的技术，我们有必要对它进行研究，通过逆向工程来解析它的工作原理和机制，从而模仿它的功能。</p></blockquote><p>更加贴切和通顺。</p><p>当然，也有可能此处的“上下文”是后文所称“进程运行所需的所有状态信息”的特指，这当然也能解释得通；但私以为在计算机领域中将 context 译作”上下文“的滥用情况也需要得到重视并及时纠正。</p><h2 id="编译系统"><a href="#编译系统" class="headerlink" title="编译系统"></a>编译系统</h2><blockquote><p>这个部分只是想尝试一下 mermaid 画图功能强行加的</p></blockquote><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">graph LR</span><br><span class="line">A[预处理器]</span><br><span class="line">B[编译器]</span><br><span class="line">C[汇编器]</span><br><span class="line">D[链接器]</span><br><span class="line">a&#123;hello.c&#125;</span><br><span class="line">d&#123;hello&#125;</span><br><span class="line">a--&gt;A</span><br><span class="line">A--&quot;hello.i&quot;--&gt;B</span><br><span class="line">B--&quot;hello.s&quot;--&gt;C</span><br><span class="line">C--&quot;hello.o&quot;--&gt;D</span><br><span class="line">D--&gt;d</span><br></pre></td></tr></table></figure><h2 id="系统的硬件组成"><a href="#系统的硬件组成" class="headerlink" title="系统的硬件组成"></a>系统的硬件组成</h2><h3 id="1-总线"><a href="#1-总线" class="headerlink" title="1. 总线"></a>1. 总线</h3><p>注意区分”字（word）“和”字节（byte）“。</p><p>一个“字”可能包含多个字节，“字长”即一个字中的字节数。字的具体大小应该是与处理器位数相关。</p><h3 id="2-I-O-设备"><a href="#2-I-O-设备" class="headerlink" title="2. I/O 设备"></a>2. I/O 设备</h3><p>硬盘也是 I/O 设备的一种，但主存（即内存）不是。（存疑）</p><p>网络也可以被抽象为 I/O 设备</p><p>I/O 设备又可以被抽象为“文件”</p><p>区别“控制器”和“适配器”</p><blockquote><p>I/O 设备通过控制器<strong>或</strong>适配器与 I/O 总线相连</p></blockquote><ul><li>控制器：是 I/O 设备本身或系统主板上的芯片组</li><li>适配器：一块插在主板插槽上的卡</li></ul><h3 id="3-主存"><a href="#3-主存" class="headerlink" title="3. 主存"></a>3. 主存</h3><p>临时存储设备</p><p>由<strong>动态随机存取存储器芯片</strong>（DRAM，Dynamic Random Access Memory）组成</p><h3 id="4-处理器"><a href="#4-处理器" class="headerlink" title="4. 处理器"></a>4. 处理器</h3><ul><li>指令集架构：描述每条机器代码指令的效果</li><li>微体系结构：处理器实际的实现</li></ul><p>按我的理解，微体系结构指的应该就是处理器具体的半导体结果，也不知对不对。此处存疑。</p><h2 id="存储器层次结构"><a href="#存储器层次结构" class="headerlink" title="存储器层次结构"></a>存储器层次结构</h2><p>高速缓存存储器（cache memory）</p><p>利用<strong>局部性原理</strong>，通过局部加载下一级低速存储器的内容来优化上一级高速存储器的操作速度，最终达到优化计算机整体运行速度的效果。</p><h3 id="要点"><a href="#要点" class="headerlink" title="要点"></a>要点</h3><ul><li>学会利用高速缓存存储器，可以大大提升程序性能</li><li>存储器层次结构逐层抽象，对低速设备而言，与之直接通信的高速设备相当于其对应的高速缓存</li></ul><h2 id="操作系统"><a href="#操作系统" class="headerlink" title="操作系统"></a>操作系统</h2><h3 id="基本功能"><a href="#基本功能" class="headerlink" title="基本功能"></a>基本功能</h3><ol><li>防止硬件被失控的应用程序滥用；</li><li>向应用程序提供简单一致的机制来控制复杂而又通常大不相同的低级硬件设备。</li></ol><h3 id="三个重要抽象"><a href="#三个重要抽象" class="headerlink" title="三个重要抽象"></a>三个重要抽象</h3><ol><li>文件</li><li>虚拟内存</li><li>进程</li></ol><p>此外，还有一个称为“虚拟机”的抽象，针对的是整个系统</p><p>进程是计算机科学中最重要、最成功的概念之一；</p><p>抽象也是。</p><h2 id="Amdahl-定律"><a href="#Amdahl-定律" class="headerlink" title="Amdahl 定律"></a>Amdahl 定律</h2><blockquote><p>要想显著加速整个系统，必须提升全系统中相当大的部分的速度。</p></blockquote><h2 id="并发和并行"><a href="#并发和并行" class="headerlink" title="并发和并行"></a>并发和并行</h2><p>在本书中虽然给出了并发（concurrency）和并行（parallelism）的概念，但二者的区别还是不明显，甚至看完之后比不看更加混乱。</p><h3 id="区别"><a href="#区别" class="headerlink" title="区别"></a>区别</h3><p>按之前的理解，从英文单词来看，并发（concurrency）前缀“con-”用以强调，整个单词是“同时发生”的意思，但并不强调同一时刻，而更在于存续状态。比如同时看《深入理解计算机系统》和《计算机网络：自顶向下方法》两本书，可以今天看前者20页，明天看后者20页；也可以今天上午看前者20页，今天下午看后者20页，无论如何，在我看完两本书之前，我对两本书的状态都是“阅读中”，“看这两本书”的状态是一直存在的，虽然我并没有一直看同一本书，但我一直“在看”这两本书。几种状态同时存在，允许交替，但同一时刻只有一种状态活跃，这就是所谓“并发”。</p><p>而并行（parallelism）的前缀“para-”则是“在旁边”之意，也就是说行为是同时发生的，强调的是<strong>同一时刻</strong>。以看书为例，就不能是我自己一个人同时看两本书了，而应当有另一个人在我旁边跟我一起看书，我看《深入理解计算机系统》，而他看《计算机网络：自顶向下方法》。在这期间任意时刻进行观察，两个人都是在看自己那本书，互不干扰，<strong>并行不悖</strong>。实际上，“并行不悖”正是“并行”概念的题眼，也是其精髓所在。几种状态同时存在，同一时刻保持活跃，这就是所谓“并行”。</p><p>当然，看了 CSAPP 之后感觉自己之前的理解似乎有一些偏颇之处，因此暂时存疑。</p><h3 id="超线程"><a href="#超线程" class="headerlink" title="超线程"></a>超线程</h3><p>前两天在读书群中看见有人讨论超线程的问题，但当时还没有开始看书，因此不是很清楚相关概念，也就没有参与讨论；看完之后对于超线程也有一定的想法，因此写下来以供参考。</p><p>超线程和多线程是不一样的。多核处理的情况下无需赘言，多个处理器相互之间是并行的。在单核的情况下，多线程实际上就是一种并发，通过 CPU 的时间调度来分配各个线程的执行时间，多时间片轮转以此塑造出“多线程并行”的假象。而超线程则是充分利用 CPU 在执行某个线程时多余的资源，在不影响当前线程的情况下再开一个线程，用到的则是空闲的资源；而一旦发生资源竞争，超线程就会终止。也就是说“超线程”在其出现的时候，是真正意义上的“并行”。</p><p>还是举例来说。</p><p>多线程就像是《火影忍者》中学习影分身术的忍者，忍者需要通过在各个事务间高速移动，并且不停接续每项事务的上一个状态，来达到一种“出现分身”的假象。但实际上忍者还是只有一个，因为做每件事都需要忍者投入全部资源，也就无法将资源共享出去。</p><p>而超线程则像是一个人一边看书一边泡脚，确实一个人在看一本书的情况下无法在同一时刻再看另一本书，因为大脑、眼睛都处理不过来；但这并不妨碍他在看书的同时泡脚，因为泡脚并不需要占用大脑和眼睛的资源，而脚这个资源在看书的时候正好是处于空闲状态的。也就是说在进行一项无需投入全部资源的任务的时候，必要资源分配出去后剩余的资源还能够支撑其他任务，并且其他任务也不会对当前任务产生干扰，那么这两项或者是几项任务就可以同时执行，并行不悖。也因此，超线程带来的性能提升远不如多核处理带来的性能提升，因为在超线程中，多个任务的规模并不是对等的，也就不会出现“性能提升两倍”的现象。</p>]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;《深入理解计算机系统》读书笔记-01&quot;&gt;&lt;a href=&quot;#《深入理解计算机系统》读书笔记-01&quot; class=&quot;headerlink&quot; title=&quot;《深入理解计算机系统》读书笔记-01&quot;&gt;&lt;/a&gt;《深入理解计算机系统》读书笔记-01&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;《深入理解计算机系统（原书第 3 版）》&lt;/p&gt;
&lt;p&gt;英文版简称CS: APP&lt;/p&gt;
&lt;p&gt;2017 年 12 月第 1 版第 6 次印刷&lt;/p&gt;
&lt;p&gt;ISBN 978-7-111-54493-7&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="计算机" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/"/>
    
      <category term="基础知识" scheme="http://lierchao.com/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"/>
    
      <category term="读书笔记" scheme="http://lierchao.com/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
    
      <category term="读书笔记" scheme="http://lierchao.com/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
      <category term="计算机基础知识" scheme="http://lierchao.com/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"/>
    
  </entry>
  
  <entry>
    <title>“常数变易法”有效的原理</title>
    <link href="http://lierchao.com/2018/10/14/%E2%80%9C%E5%B8%B8%E6%95%B0%E5%8F%98%E6%98%93%E6%B3%95%E2%80%9D%E6%9C%89%E6%95%88%E7%9A%84%E5%8E%9F%E7%90%86/"/>
    <id>http://lierchao.com/2018/10/14/“常数变易法”有效的原理/</id>
    <published>2018-10-14T11:50:04.000Z</published>
    <updated>2019-09-09T01:54:54.529Z</updated>
    
    <content type="html"><![CDATA[<p>[TOC]</p><blockquote><p>来自博文<a href="https://blog.csdn.net/w573719227/article/details/83050039" target="_blank" rel="noopener">《“常数变易法”有效的原理》</a></p></blockquote><h1 id="常数变易法"><a href="#常数变易法" class="headerlink" title="常数变易法"></a>常数变易法</h1><h2 id="为什么写这篇文章"><a href="#为什么写这篇文章" class="headerlink" title="为什么写这篇文章"></a>为什么写这篇文章</h2><blockquote><p>学过“常数变易法”的同学请直接点击“常数变易法的原理”<br>这里只讲述常数变易法的原理，为什么要用常数变易法请参见参考资料<a href="#ref1">《常数变易法的解释 》</a></p></blockquote><p>在学习高数的过程中，关于为什么在解一阶线性微分方程的时候要使用常数变易法，为什么可以使用常数变易法，常数变易法为什么是有效并且正确的，老师都语焉不详，一笔带过，导致一直不能很好地理解其中的数学思想。自己也只能接受老师的解释，将这个方法强行合理化。</p><p>但是最近再次看到一阶线性微分方程的求解，看到直接给出来的求解公式一头雾水，再去翻书，始终还是感觉隔靴搔痒，雾里看花，始终不自在，所以上网搜索了一下，搜到了一篇相关文章（<a href="https://www.cnblogs.com/lookof/archive/2009/01/06/1370065.html" target="_blank" rel="noopener">常数变易法的解释 </a>），终于明白了其中蕴含的深刻而巧妙的数学思想，喜不自禁。</p><p>所以在此记录下个人的理解，一则梳理自己的思路，二则可供感兴趣的同学参考，倘能有助于大家理解常数变易法的“自然”性，亦是幸甚。</p><a id="more"></a><h2 id="什么是常数变易法？"><a href="#什么是常数变易法？" class="headerlink" title="什么是常数变易法？"></a>什么是常数变易法？</h2><p>有以下一阶线性微分方程：</p><script type="math/tex; mode=display">y' +P(x)y=Q(x) \tag1</script><p>其中，$P(x)\not \equiv 0$ 且 $Q(x)\not \equiv 0$。</p><p>若解其对应的齐次方程：</p><script type="math/tex; mode=display">y' +P(x)y=0\tag2</script><p>则易有：</p><script type="math/tex; mode=display">y=Ce^{-\int P(x)dx}(C\neq 0)</script><p>即为齐次方程的<em>通解</em>。</p><p>这时，我们可以用<strong>常数变易法</strong>来求非齐次方程$(1)$的通解，即将齐次方程$(2)$的通解中的常数$C$换成（变易为）一个关于$x$的未知函数$u(x)$，变易之后，非齐次方程通解表示如下：<script type="math/tex">y=u(x)\cdot e^{-\int P(x)dx} \Big(u(x)\not\equiv 0\Big)\tag3</script>于是将该通解形式代入原方程$(1)$，可以解得：<script type="math/tex">u(x)=\int Q(x)e^{\int P(x)dx}dx+C</script>将上式代入$(3)$式，即可解得：<script type="math/tex">y=e^{-\int P(x)dx}\cdot (\int Q(x)e^{\int P(x)dx}dx+C)</script>这就是所谓<strong>常数变易法</strong>。<br>可以看到，这里把常数 $C$ 直接代换为了函数$u(x)$ ，显得十分生硬不自然，没有什么说服力。然而书上很少会对这个方法的由来作出介绍，所以想必会使很多人感到困惑。</p><h2 id="错误的理解"><a href="#错误的理解" class="headerlink" title="错误的理解"></a>错误的理解</h2><p>对于常数变易法，我以前的理解是：<br>既然 $y=Ce^{-\int P(x)dx}(C\neq 0)$ 可以使齐次方程 $y’ +P(x)y=0$ 成立，那么在其基础上增添一个函数，就应该使得该方程运算结果多出一个与自由项相关的余项$Q(x)$，所以可以使用常数变易法。<br>这样的理解是基于表面形式做出的一个解释，然而还是不能够明确地说明这个方法的正当性与正确性。<br>所以我们需要进一步探究其内在的原理。</p><h2 id="常数变易法的原理"><a href="#常数变易法的原理" class="headerlink" title="常数变易法的原理"></a>常数变易法的原理</h2><h3 id="基本"><a href="#基本" class="headerlink" title="基本"></a>基本</h3><p>容易理解，我们可以把任意函数表示成为两个函数之积，即 <script type="math/tex">y(x)=u(x)\cdot v(x)\tag4</script>对 $y(x)$ 求导，得：$y’(x)=u’(x)v(x)+u(x)v’(x)$</p><h3 id="计算"><a href="#计算" class="headerlink" title="计算"></a>计算</h3><p>将 $y(x)=u(x)\cdot v(x)$，$y’(x)=u’(x)v(x)+u(x)v’(x)$ 代入非齐次方程$(1)$，整理得到：<script type="math/tex">u'(x)v(x)+u(x)\cdot [v'(x)+P(x)v(x)]=Q(x)\tag5</script>由解一阶线性微分方程的常用方法<strong>分离变量法</strong>容易想到，如果没有 $u(x)\cdot [v’(x)+P(x)v(x)]$ 这一项，我们就可以简便地利用分离变量法进行计算。<br>现在单独考察 $u(x)\cdot [v’(x)+P(x)v(x)]$ 这一项。其中 $u(x)$ 不确定，不能用来保持 $u(x)\cdot [v’(x)+P(x)v(x)]\not\equiv0$ ，所以考虑另一个因式 $v’(x)+P(x)v(x)$ 。显然 $v(x)$ 是不确定的，在 $u(x)$ 不确定的情况下，可以任意取值。则假设 $v(x)$ 满足 <script type="math/tex">v'(x)+P(x)v(x)\equiv0\tag6</script> 观察式 $(6)$ ，可以看到其形式与式 $(2)$ 基本一致。<br>求解式 $(6)$，可以得其通解形式：<script type="math/tex">v(x)=C_1\cdot e^{-\int P(x)dx}\tag7</script>将所得通解代入 $(4)$，则<script type="math/tex">y(x)=u(x)\cdot C_1\cdot e^{-\int P(x)dx}\tag8</script>将 $(8)$ 式代入 $(5)$ 式，得到：<script type="math/tex">u'(x)\cdot C_1\cdot e^{-\int P(x)dx}=Q(x)</script>使用<strong>分离变量法</strong>，容易解得：<script type="math/tex">u(x)=\frac1{C_1}\int Q(x)\cdot e^{\int P(x)dx}dx+C_2\tag9</script>将 $(7)$ $(9)$ 同时代入式 $(4)$ ，则<script type="math/tex">y(x)=e^{-\int P(x)dx}(\int Q(x)e^{\int P(x)dx}dx+C_1C_2)</script>令$C=C_1C_2$，则得原一阶线性微分方程的通解为：<script type="math/tex">y(x)=e^{-\int P(x)dx}(\int Q(x)e^{\int P(x)dx}dx+C)</script></p><h3 id="推广"><a href="#推广" class="headerlink" title="推广"></a>推广</h3><blockquote><p>这一部分是在知乎看到了关于“常数变易法”在高阶作用的问题之后增补的</p></blockquote><p>问题链接：<a href="https://www.zhihu.com/question/31329122" target="_blank" rel="noopener">常数变易法思想的来源或本质是什么？</a><br>现在有一般$n$阶线性微分方程<script type="math/tex">P_{n}(x)y^{(n)}+P_{n-1}(x)y^{(n-1)}+P_{n-2}(x)y^{(n-2)}...+P_{1}(x)y'+P_{0}(x)y=Q(x)\tag{10}</script><br>由前述有，$y(x)$可以表示为$y(x)=u(x)v(x)$。<br>现在我们考察两函数乘积的高阶微分形式。<br>比较<strong>二项式展开定理</strong>我们不难发现，对$y=uv$的高阶微分具有类似的形式。<br>比如：<script type="math/tex">(uv)'=u'v+uv'$$$$(uv)''=(u'v+uv')'=u''v+2u'v'+uv''$$$$...</script><br>从原理上来看，展开多项式的每一项都应有$n$阶微分，而这$n$阶微分分别分配在$u、v$上；对于多项式的每一项，相当于任选$k$个微分算子作用于$u$，则另有$(n-k)$个微分算子作用于$v$，与<strong>二项式展开定理</strong>本质相同，所以展开形式也应相同。<br>则有式$(11)$：<script type="math/tex">(uv)^{(n)}=C_n^0u^{(n)}v+C_n^1u^{(n-1)}v^{(1)}+C_n^2u^{(n-2)}v^{(2)}+...+C_n^{n-1}u^{(1)}v^{(n-1)}+C_n^nuv^{(n)}\tag{11}</script><br>将这个一般形式代回式$(10)$，假设将$u$作为主要研究对象（以$v$为主要研究对象亦可，二者地位相同），则按$u$的导数降阶排列多项式：<script type="math/tex">M_{n-1}(x)u^{(n)}+M_{n-2}(x)u^{(n-1)}+...+M_0(x)u'+\bigl(P_n(x)v^{(n)}+P_{n-1}(x)v^{(n-1)}+...+P_{1}(x)v'+P_0(x)v\bigr)u=Q(x)\tag{12}</script><br>其中，$M<em>i(x)$为关于$x$的多项式。<br>按一阶情况下的原理，可以令多项式$\bigl(P_n(x)v^{(n)}+P</em>{n-1}(x)v^{(n-1)}+…+P<em>{1}(x)v’+P_0(x)v\bigr)\equiv0$消去$u$项。解$v$即为解式$10$对应的齐次线性微分方程。<br>则剩下的式子为$$M</em>{n-1}(x)u^{(n)}+M<em>{n-2}(x)u^{(n-1)}+…+M_0(x)u’=Q(x)<script type="math/tex">令$\alpha(x)=u'(x)$，则上式化为</script>M</em>{n-1}(x)\alpha^{(n-1)}+M_{n-2}(x)\alpha^{(n-2)}+…+M_0(x)\alpha=Q(x)\tag{13}$$<br>比较式$(12)、(13)$，可以看到：通过<strong>常数变易法</strong>，成功地把求解一个$n$阶线性微分非齐次方程的问题，为了求解一个对应的$n$阶线性微分齐次方程和一个$(n-1)$阶线性微分非齐次方程的问题。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>很显然我们可以看到，<strong>常数变易法</strong>是蕴含了很深刻的数学思想、具有很强健的数学基础的解题方法，并非无根之萍，更不是突发奇想或是强行合理。<br>但是从其原理上来讲，将其称呼为“常数变易法”是不太妥当的，本质上它并非是单纯地使用一个函数来替代了齐次方程通解的常数。<br><strong>常数变易法</strong>的称呼应该说为了便于日常应用和直观记忆，这里可以不必纠结。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>[1] lookof,<a id="ref1"><a href="https://www.cnblogs.com/lookof/archive/2009/01/06/1370065.html" target="_blank" rel="noopener">常数变易法的解释 </a></a><br>[2] 崔士襄,邯郸农业高等专科学校,<a href="http://kns.cnki.net/KCMS/detail/detail.aspx?dbcode=CJFQ&amp;dbname=CJFD9899&amp;filename=HDLY199801017&amp;uid=WEEvREcwSlJHSldRa1FhdkJkVWEyZnAyYk9seWN1S2dJbGhMcCtxTERzQT0=$9A4hF_YAuvQ5obgVAqNKPCYcEjKensW4IQMovwHtwkF4VYPoHbKxJw!!&amp;v=MDAwNDFyQ1VSTEtlWitkckZpdm1VYnZLTFNuSGQ3S3hGOW5Ncm85RVk0UjhlWDFMdXhZUzdEaDFUM3FUcldNMUY=" target="_blank" rel="noopener">“常数变易法”来历的探讨</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;[TOC]&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;来自博文&lt;a href=&quot;https://blog.csdn.net/w573719227/article/details/83050039&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;《“常数变易法”有效的原理》&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;常数变易法&quot;&gt;&lt;a href=&quot;#常数变易法&quot; class=&quot;headerlink&quot; title=&quot;常数变易法&quot;&gt;&lt;/a&gt;常数变易法&lt;/h1&gt;&lt;h2 id=&quot;为什么写这篇文章&quot;&gt;&lt;a href=&quot;#为什么写这篇文章&quot; class=&quot;headerlink&quot; title=&quot;为什么写这篇文章&quot;&gt;&lt;/a&gt;为什么写这篇文章&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;学过“常数变易法”的同学请直接点击“常数变易法的原理”&lt;br&gt;这里只讲述常数变易法的原理，为什么要用常数变易法请参见参考资料&lt;a href=&quot;#ref1&quot;&gt;《常数变易法的解释 》&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在学习高数的过程中，关于为什么在解一阶线性微分方程的时候要使用常数变易法，为什么可以使用常数变易法，常数变易法为什么是有效并且正确的，老师都语焉不详，一笔带过，导致一直不能很好地理解其中的数学思想。自己也只能接受老师的解释，将这个方法强行合理化。&lt;/p&gt;
&lt;p&gt;但是最近再次看到一阶线性微分方程的求解，看到直接给出来的求解公式一头雾水，再去翻书，始终还是感觉隔靴搔痒，雾里看花，始终不自在，所以上网搜索了一下，搜到了一篇相关文章（&lt;a href=&quot;https://www.cnblogs.com/lookof/archive/2009/01/06/1370065.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;常数变易法的解释 &lt;/a&gt;），终于明白了其中蕴含的深刻而巧妙的数学思想，喜不自禁。&lt;/p&gt;
&lt;p&gt;所以在此记录下个人的理解，一则梳理自己的思路，二则可供感兴趣的同学参考，倘能有助于大家理解常数变易法的“自然”性，亦是幸甚。&lt;/p&gt;
    
    </summary>
    
    
      <category term="数学" scheme="http://lierchao.com/categories/%E6%95%B0%E5%AD%A6/"/>
    
    
      <category term="原理" scheme="http://lierchao.com/tags/%E5%8E%9F%E7%90%86/"/>
    
  </entry>
  
  <entry>
    <title>仿辛弃疾《永遇乐》</title>
    <link href="http://lierchao.com/2012/07/13/%E4%BB%BF%E8%BE%9B%E5%BC%83%E7%96%BE%E3%80%8A%E6%B0%B8%E9%81%87%E4%B9%90%E3%80%8B/"/>
    <id>http://lierchao.com/2012/07/13/仿辛弃疾《永遇乐》/</id>
    <published>2012-07-13T12:19:55.000Z</published>
    <updated>2019-09-07T05:53:55.971Z</updated>
    
    <content type="html"><![CDATA[<p>万人竞渡，互争大路，出头何处？时光飞溯，大器总在，浪沙淘尽后。火树银花，熙熙街头，都道藏龙卧虎。忆华年，壮志雄心，昂首欲踏登天路。</p><p>壬辰一考，方见不足，只余心伤难住。一十五年，学中不忘，风雨思仙途。再堪回首，菩提树下，一阵龙吟天鼓。何人知：我辈年少，可能无数。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;万人竞渡，互争大路，出头何处？时光飞溯，大器总在，浪沙淘尽后。火树银花，熙熙街头，都道藏龙卧虎。忆华年，壮志雄心，昂首欲踏登天路。&lt;/p&gt;
&lt;p&gt;壬辰一考，方见不足，只余心伤难住。一十五年，学中不忘，风雨思仙途。再堪回首，菩提树下，一阵龙吟天鼓。何人知：我辈年少，可能无数。
      
    
    </summary>
    
    
      <category term="轩辕诗集" scheme="http://lierchao.com/categories/%E8%BD%A9%E8%BE%95%E8%AF%97%E9%9B%86/"/>
    
    
      <category term="文学" scheme="http://lierchao.com/tags/%E6%96%87%E5%AD%A6/"/>
    
  </entry>
  
  <entry>
    <title>自习随笔</title>
    <link href="http://lierchao.com/2012/07/13/%E8%87%AA%E4%B9%A0%E9%9A%8F%E7%AC%94/"/>
    <id>http://lierchao.com/2012/07/13/自习随笔/</id>
    <published>2012-07-13T10:08:06.000Z</published>
    <updated>2019-09-07T05:47:47.484Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>公元2010（庚寅）年11月14日夜21：03，晚自习，无故心情大好，故作</p></blockquote><p>窗外冷月洒清辉，</p><p>屋内如昼有灯垂。</p><p>未见后羿射九日，</p><p>却有嫦娥登月回。</p><p>五道天地人神鬼，</p><p>修罗之道不在内。</p><p style="color: red">我自癫狂我逍遥，</p><p style="color: red">哪由他人论白黑！</p><blockquote><p style="color: blue">（2010.12.28日，夜，20：56晚自习，补作）</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;公元2010（庚寅）年11月14日夜21：03，晚自习，无故心情大好，故作&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;窗外冷月洒清辉，&lt;/p&gt;
&lt;p&gt;屋内如昼有灯垂。&lt;/p&gt;
&lt;p&gt;未见后羿射九日，&lt;/p&gt;
&lt;p&gt;却有嫦娥登月回。&lt;/p&gt;
&lt;p&gt;五
      
    
    </summary>
    
    
      <category term="轩辕诗集" scheme="http://lierchao.com/categories/%E8%BD%A9%E8%BE%95%E8%AF%97%E9%9B%86/"/>
    
    
      <category term="文学" scheme="http://lierchao.com/tags/%E6%96%87%E5%AD%A6/"/>
    
  </entry>
  
  <entry>
    <title>赤壁短吟（初一）</title>
    <link href="http://lierchao.com/2012/07/13/%E8%B5%A4%E5%A3%81%E7%9F%AD%E5%90%9F%EF%BC%88%E5%88%9D%E4%B8%80%EF%BC%89/"/>
    <id>http://lierchao.com/2012/07/13/赤壁短吟（初一）/</id>
    <published>2012-07-13T09:46:38.000Z</published>
    <updated>2019-09-07T05:44:26.087Z</updated>
    
    <content type="html"><![CDATA[<center>北来舰船势如虹，</center><center>赤壁之战一场空。</center><center>诸葛筑台借东风，</center><center>周郎之功在此中。</center>]]></content>
    
    <summary type="html">
    
      
      
        &lt;center&gt;北来舰船势如虹，&lt;/center&gt;

&lt;center&gt;赤壁之战一场空。&lt;/center&gt;

&lt;center&gt;诸葛筑台借东风，&lt;/center&gt;

&lt;center&gt;周郎之功在此中。&lt;/center&gt;


      
    
    </summary>
    
    
      <category term="轩辕诗集" scheme="http://lierchao.com/categories/%E8%BD%A9%E8%BE%95%E8%AF%97%E9%9B%86/"/>
    
    
      <category term="文学" scheme="http://lierchao.com/tags/%E6%96%87%E5%AD%A6/"/>
    
  </entry>
  
  <entry>
    <title>男儿谣</title>
    <link href="http://lierchao.com/2012/07/13/%E7%94%B7%E5%84%BF%E8%B0%A3/"/>
    <id>http://lierchao.com/2012/07/13/男儿谣/</id>
    <published>2012-07-13T09:44:37.000Z</published>
    <updated>2019-09-07T05:43:00.390Z</updated>
    
    <content type="html"><![CDATA[<p>天有情，</p><p>地有义，</p><p>人活世间真不易。</p><p>七尺男儿躯，</p><p>顶天又立地，</p><p>空叹太古，</p><p>时光如白驹过隙。</p><p>风云将起，</p><p>祸乱天地，</p><p>雄心壮志至斯不已。</p><p>虎龙相生，</p><p>大鹏同风起。</p><p>梦入神机，</p><p>顶天立地伟丈夫莫逆，</p><p>感天动地真英雄无济</p><p>帷幄神机，</p><p>世间纤毫测算无遗，</p><p>奈何不能明己，</p><p>男儿立志开天辟地！</p><p>天有情，</p><p>地有义，</p><p>人活世间真不易。</p><p>七尺男儿躯，</p><p>顶天又立地！</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;天有情，&lt;/p&gt;
&lt;p&gt;地有义，&lt;/p&gt;
&lt;p&gt;人活世间真不易。&lt;/p&gt;
&lt;p&gt;七尺男儿躯，&lt;/p&gt;
&lt;p&gt;顶天又立地，&lt;/p&gt;
&lt;p&gt;空叹太古，&lt;/p&gt;
&lt;p&gt;时光如白驹过隙。&lt;/p&gt;
&lt;p&gt;风云将起，&lt;/p&gt;
&lt;p&gt;祸乱天地，&lt;/p&gt;
&lt;p&gt;雄心壮志至斯不已。&lt;/p&gt;

      
    
    </summary>
    
    
      <category term="轩辕诗集" scheme="http://lierchao.com/categories/%E8%BD%A9%E8%BE%95%E8%AF%97%E9%9B%86/"/>
    
    
      <category term="文学" scheme="http://lierchao.com/tags/%E6%96%87%E5%AD%A6/"/>
    
  </entry>
  
  <entry>
    <title>随感</title>
    <link href="http://lierchao.com/2011/09/11/%E9%9A%8F%E6%84%9F/"/>
    <id>http://lierchao.com/2011/09/11/随感/</id>
    <published>2011-09-11T04:27:40.000Z</published>
    <updated>2019-09-07T05:42:03.741Z</updated>
    
    <content type="html"><![CDATA[<p>天地苍茫，日月同光。</p><p>晓风残照，落红为妆。</p><p>不识大道，此心永殇。</p><p>纵横宇宙，睥睨八荒。</p><p>飞扬跋扈，虚梦一桩。</p><p>王侯将相，谁人久长？</p><p>风云变幻，前路迷茫。</p><p>我欲改命，谁可扶相？</p><p>悲悯天下，泣血炎黄。</p><p>匹夫一怒，覆灭玄黄！</p><p>——————————————————————————————————————</p><p>天道长享，既寿永昌！！！</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;天地苍茫，日月同光。&lt;/p&gt;
&lt;p&gt;晓风残照，落红为妆。&lt;/p&gt;
&lt;p&gt;不识大道，此心永殇。&lt;/p&gt;
&lt;p&gt;纵横宇宙，睥睨八荒。&lt;/p&gt;
&lt;p&gt;飞扬跋扈，虚梦一桩。&lt;/p&gt;
&lt;p&gt;王侯将相，谁人久长？&lt;/p&gt;
&lt;p&gt;风云变幻，前路迷茫。&lt;/p&gt;
&lt;p&gt;我欲改命，谁可扶相？&lt;
      
    
    </summary>
    
    
      <category term="轩辕诗集" scheme="http://lierchao.com/categories/%E8%BD%A9%E8%BE%95%E8%AF%97%E9%9B%86/"/>
    
    
      <category term="文学" scheme="http://lierchao.com/tags/%E6%96%87%E5%AD%A6/"/>
    
  </entry>
  
  <entry>
    <title>漫想</title>
    <link href="http://lierchao.com/2011/09/11/%E6%BC%AB%E6%83%B3/"/>
    <id>http://lierchao.com/2011/09/11/漫想/</id>
    <published>2011-09-11T03:30:44.000Z</published>
    <updated>2019-09-07T05:40:55.064Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>兴之所至，提笔为诗，茫茫然不知所云</p><p>凑字数故带冥王星</p></blockquote><p>域外苍茫星耀光，</p><p>夜夜吾梦入洪荒。</p><p>回望火星苍苍凉，</p><p>警钟长鸣铛铛响。</p><p>天海冥王三星降，</p><p>日似龙头四面汤。</p><p>中土千古一梦想，</p><p>放眼宇内心激荡。</p><p>但望地球非绝唱，</p><p>代代前人总惆怅。</p><p>九州风云总要让，</p><p>宇内生灵何与抗？</p><p>伦理道德万年长，</p><p>星途遥远独泪殇。</p><p>生灵源于星斗傍，</p><p>星斗源于宙缩张。</p><p>缩缩张张不见光，</p><p>黑白二洞太极样。</p><p>黑者阴也司灭亡，</p><p>白者阳也主生长。</p><p>吞吐之间衍道行，</p><p>宙际来去洞连相。</p><p>天马神驹铁蹄扬，</p><p>飞碟无声择日降。</p><p>相坐勿怒叙旧伤，</p><p>目望不到二洞长。</p><p>一叙久别有无恙，</p><p>何以令尓弃故乡？</p><p>二叙仙神在何方，</p><p>通灵显圣多猖狂。</p><p>三叙何处育炎黄，</p><p>神龙皇者自主张。</p><p>言叙久时多孟浪，</p><p>待入席时把话讲。</p><p>张灯结彩心昂扬，</p><p>非是俗事使我忙。</p><p>此乃域外天客到，</p><p>糊糊涂涂把神装。</p><p>心慕仙法志如钢，</p><p>世世生生随梦张。</p><p>大道五十衍四九，</p><p>独余其一驻天上。</p><p>噫唏！</p><p>三千大道，</p><p>凡人各取其一，</p><p>吾独取三千而皇！</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;兴之所至，提笔为诗，茫茫然不知所云&lt;/p&gt;
&lt;p&gt;凑字数故带冥王星&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;域外苍茫星耀光，&lt;/p&gt;
&lt;p&gt;夜夜吾梦入洪荒。&lt;/p&gt;
&lt;p&gt;回望火星苍苍凉，&lt;/p&gt;
&lt;p&gt;警钟长鸣铛铛响。&lt;/p&gt;
&lt;p&gt;天海冥王三
      
    
    </summary>
    
    
      <category term="轩辕诗集" scheme="http://lierchao.com/categories/%E8%BD%A9%E8%BE%95%E8%AF%97%E9%9B%86/"/>
    
    
      <category term="文学" scheme="http://lierchao.com/tags/%E6%96%87%E5%AD%A6/"/>
    
  </entry>
  
  <entry>
    <title>无题</title>
    <link href="http://lierchao.com/2011/07/01/%E6%97%A0%E9%A2%98/"/>
    <id>http://lierchao.com/2011/07/01/无题/</id>
    <published>2011-07-01T15:06:56.000Z</published>
    <updated>2019-09-07T05:36:48.314Z</updated>
    
    <content type="html"><![CDATA[<p>抬眼望，逆乱间，匆匆亘古，不为红颜。<br>古风长存在世间。<br>腾龙出海，一世万年，轮回百世洪荒前。<br>逝者恩怨幻如烟。<br>虎落平阳，龙游浅滩，一日声震宇寰。<br>月明无恨月常圆。<br>帝睨天下，一往无前，枪林弹雨风拂面！<br>哪怕身死亦无怨，但求心安。<br>修战剑，问荒蛮，英雄热血洒九天！<br>开声吐气，倒挂成星汉：<br>铁拳在手，一意破敌咽！<br>身作军人生不悔，但求无恨是好汉！<br>国事当前，奋勇争先，谁人在乎血汗？<br>一枪破敌一千万，忠义人生万万年！！！</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;抬眼望，逆乱间，匆匆亘古，不为红颜。&lt;br&gt;古风长存在世间。&lt;br&gt;腾龙出海，一世万年，轮回百世洪荒前。&lt;br&gt;逝者恩怨幻如烟。&lt;br&gt;虎落平阳，龙游浅滩，一日声震宇寰。&lt;br&gt;月明无恨月常圆。&lt;br&gt;帝睨天下，一往无前，枪林弹雨风拂面！&lt;br&gt;哪怕身死亦无怨，但求心安。&lt;
      
    
    </summary>
    
    
      <category term="轩辕诗集" scheme="http://lierchao.com/categories/%E8%BD%A9%E8%BE%95%E8%AF%97%E9%9B%86/"/>
    
    
      <category term="文学" scheme="http://lierchao.com/tags/%E6%96%87%E5%AD%A6/"/>
    
  </entry>
  
</feed>
