<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:webfeeds="http://webfeeds.org/rss/1.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>iswbm</title>
    <link>https://iswbm.com/en/</link>
    <description>Recent content on iswbm</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Sun, 30 Jun 2024 22:19:26 +0800</lastBuildDate>
    
    <atom:link href="https://iswbm.com/en/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>Search</title>
      <link>https://iswbm.com/en/search/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>https://iswbm.com/en/search/</guid>
      <description>Currently this site utilizes Pagefind to search with relatively high degree of accuracy in English. To search in Chinese, it is recommended to split the keywords into groups of words(up to two words) and include them in the symbol &amp;quot;. For example, if you want to search for 区块链 or 智能合约, you can use &amp;quot;区块 链&amp;</description>
      <content:encoded><![CDATA[<blockquote>
<p>Currently this site utilizes <a href="https://pagefind.app/">Pagefind</a> to search with relatively high degree of accuracy in English. To search in Chinese, it is recommended to split the keywords into groups of words(up to two words) and include them in the symbol <code>&quot;</code>. For example, if you want to search for <code>区块链</code> or <code>智能合约</code>, you can use <code>&quot;区块 链&quot;</code> and <code>&quot;智能 合约&quot;</code> and you may get better results.</p>
</blockquote>
<blockquote>
<p>If you are not satisfied with the search results or would like to know more, please feel free to share with me by leaving a comment on any of the articles, or email me at <code>wongbingming@163.com</code>.</p>
</blockquote>
]]></content:encoded>
    </item>
    
    <item>
      <title>About</title>
      <link>https://iswbm.com/en/about/</link>
      <pubDate>Sun, 20 Aug 2017 21:38:52 +0800</pubDate>
      
      <guid>https://iswbm.com/en/about/</guid>
      <description>Hugo is a static site engine written in Go.
It makes use of a variety of open source projects including:
Cobra Viper J Walter Weatherman Cast Learn more and contribute on GitHub.</description>
      <content:encoded><![CDATA[<p>Hugo is a static site engine written in Go.</p>
<p>It makes use of a variety of open source projects including:</p>
<ul>
<li><a href="https://github.com/spf13/cobra">Cobra</a></li>
<li><a href="https://github.com/spf13/viper">Viper</a></li>
<li><a href="https://github.com/spf13/jWalterWeatherman">J Walter Weatherman</a></li>
<li><a href="https://github.com/spf13/cast">Cast</a></li>
</ul>
<p>Learn more and contribute on <a href="https://github.com/gohugoio">GitHub</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>如何使用Hugo搭建个人博客</title>
      <link>https://iswbm.com/en/2024/06/16/how-to-deploy-blog-with-hugo/</link>
      <pubDate>Sun, 16 Jun 2024 21:49:38 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/16/how-to-deploy-blog-with-hugo/</guid>
      <description>从业 7 年来，搭建过不下 10 个网站，就个人博客而言，就已经搭建过 4 次，中间换了 4 个框架，从最早的 Hexo，再到 Django，再到 Sphinx，再</description>
      <content:encoded><![CDATA[<p>从业 7 年来，搭建过不下 10 个网站，就个人博客而言，就已经搭建过 4 次，中间换了 4 个框架，从最早的 Hexo，再到 Django，再到 Sphinx，再到 Wordpress。</p>
<p>而最后一个 WordPress 比较重，需要消耗不少服务器资源，访问人数多了后，也比较卡，弃用的念头已经非常强烈，刚好前两天服务器到期，就打算重新换个轻量的静态框架。</p>
<p>经过一番调研，出于颜值和性能的要求，最终选择了 Hugo ！不可否认，选择 Hugo 也有一部分原因是我当前以 Go 开发为主，就跟当初写 Python 代码时，选择用 Sphinx 搭建同理。</p>
<p>以下操作仅以 macOS 为例，Windows 请自行尝试，除了工具安装方式有区别外，其他区别不大，可放心食用。</p>
<h2 id="1-安装-hugo">1. 安装 Hugo</h2>
<p>首先在电脑上安装 hugo 二进制</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">brew install hugo
</span></span></code></pre></div><p>初始化站点</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">hugo new site iswbm.com
</span></span></code></pre></div><p>初始化 git 仓库</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git init
</span></span><span class="line"><span class="cl">git add -A
</span></span><span class="line"><span class="cl">git commit -m <span class="s2">&#34;first commit&#34;</span>
</span></span><span class="line"><span class="cl">git branch -M main
</span></span><span class="line"><span class="cl">git remote add origin git@github.com:iswbm/iswbm.com.git
</span></span></code></pre></div><h2 id="2-引入主题">2. 引入主题</h2>
<p>让我决定选择使用 hugo，其实就是无意中浏览了 pseudoyu 的个人站点（https://www.pseudoyu.com/），非常钟意这个主题。</p>
<p>pseudoyu 使用的是 hugo-theme-den 主题，并基于此主题做了一些个性的定制。</p>
<p>而 pseudoyu 关于此部分的定制貌似没有在 github 上更新，于是我只能自己研究将搜索植入主题中。</p>
<p>要使用我改过的主题，可以按照我的步骤来操作</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># 添加子模块</span>
</span></span><span class="line"><span class="cl">git submodule add https://github.com/iswbm/hugo-theme-den.git themes/hugo-theme-den
</span></span></code></pre></div><p>后续如果我的主题有更新，你都可以直接使用如下命令来更新</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git submodule update --remote
</span></span></code></pre></div><p><code>hugo-theme-den</code>主题会提供了实例配置与初始页面，开始使用主题时可以将其 <code>exampleSite/</code> 目录下的文件复制到站点目录下，在此基础上进行调整配置。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">cp -rf themes/hugo-theme-den/exampleSite/* ./
</span></span></code></pre></div><p>初始化主题基础配置后，我们可以在 <code>config.toml</code> 文件中进行站点细节配置，具体配置项参考各主题说明文档。</p>
<p>将拷贝过来的文件，加入到自己的仓库中</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git add -A
</span></span><span class="line"><span class="cl">git commit -m <span class="s2">&#34;use hugo-theme-den&#34;</span>
</span></span></code></pre></div><h2 id="3-发布新文章">3. 发布新文章</h2>
<p>通过 <code>hugo new</code> 命令可以在 content/zh 目录下新建文章</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">hugo new posts/first-post.md
</span></span></code></pre></div><p>模板的内容，可以自行修改 <code>archetypes/default.md</code> ，我的模板是</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">title: <span class="s1">&#39;{{ replace .File.ContentBaseName &#34;-&#34; &#34; &#34; | title }}&#39;</span>
</span></span><span class="line"><span class="cl">date: <span class="o">{{</span> .Date <span class="o">}}</span>
</span></span><span class="line"><span class="cl">draft: <span class="nb">false</span>
</span></span><span class="line"><span class="cl">lastmod: <span class="o">{{</span> .Date <span class="o">}}</span>
</span></span><span class="line"><span class="cl">tags: <span class="o">[</span><span class="s2">&#34;python&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">categories: <span class="o">[</span><span class="s2">&#34;Programming&#34;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">authors:
</span></span><span class="line"><span class="cl">- <span class="s2">&#34;iswbm&#34;</span>
</span></span><span class="line"><span class="cl">---
</span></span></code></pre></div><p>新建文章后，用 vscode 或 typora 等 markdown 编辑器可以写一些内容。</p>
<p>完成内容的补充后，可以用如下命令直接运行 http 的服务器（若你的模板里指定draft=true，这里就直接执行 hugo server 即可）</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">hugo server --buildDrafts --ignoreCache
</span></span></code></pre></div><p>然后访问 <code>http://localhost:1313</code></p>
<p><img src="https://image.iswbm.com/202406101009205.png"></p>
<p>‍</p>
<h2 id="4-pagefind-搜索">4. Pagefind 搜索</h2>
<p>在个人博客支持文章搜索，是一个非常刚需的需求。</p>
<p>正常的网站都是在后端检索再返回给前端，但 hugo 生成的是静态的 HTML 文件，想要在这样的网站上支持搜索，就得提前将网站的文章进行分析，生成静态的索引文件。</p>
<p>用 Pagefind 可预生成索引文件，后续用户的搜索全部在客户端完成，可以快速加载并减少服务器负载，是一种非常轻量的解决个人博客搜索的方案。</p>
<p>Pagefind 是开源项目</p>
<ul>
<li>仓库地址：https://github.com/CloudCannon/pagefind</li>
<li>二进制下载：https://github.com/CloudCannon/pagefind/releases</li>
</ul>
<p>使用时只需要下载二进制文件即可，以本人是 macOS 的为例（根据自己电脑选择合适的二进制版本）</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">wget https://github.com/CloudCannon/pagefind/releases/download/v1.1.0/pagefind-v1.1.0-x86_64-apple-darwin.tar.gz
</span></span><span class="line"><span class="cl">tar -xvf pagefind-v1.1.0-x86_64-apple-darwin.tar.gz
</span></span><span class="line"><span class="cl">mv pagefind /Users/iswbm/.local/bin/
</span></span></code></pre></div><p>若不创建任何的配置文件，可以直接将关键配置以参数形式指定</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="l">pagefind --source public --bundle-dir pagefind</span><span class="w">
</span></span></span></code></pre></div><p>也可以将配置写入配置文件中，在项目根目录创建 pagefind.yml</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># 生成的 pagefind 的目录</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">site</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;public&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">output-path</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;pagefind&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># 指定要索引的源目录，通常是 Hugo 生成的静态文件目录</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c">#contentDir: &#34;public&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># 指定输出索引文件的目录</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c">#indexDir: &#34;static/pagefind&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># 指定静态文件的 URL 前缀，这通常与你的 Hugo 网站的 URL 结构相匹配</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c">#baseURL: &#34;https://example.com&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># 指定要索引的文件类型，例如 .html</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">indexDocs</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;.html&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># 指定要排除索引的文件或目录的 glob 模式</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">excludeFiles</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;**/404.html&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;*/noindex/*&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># 指定要包含的字段，这些字段将被索引用于搜索</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">fields</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;title&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;description&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;content&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># 指定用于生成摘要的字段</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">excerptField</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;excerpt&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># 指定用于搜索结果标题的字段</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">titleField</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;title&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># 指定用于搜索结果 URL 的字段</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c">#urlField: &#34;link&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># 指定用于搜索结果内容的字段</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c">#contentField: &#34;content&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># 指定是否将 HTML 内容转换为纯文本</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c">#htmlAsText: true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># 指定是否启用对中文语言的支持</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">chinese</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># 指定是否在控制台中显示详细的日志信息</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">verbose</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># 指定是否跟踪堆内存使用情况</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">traceHeap</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># 指定是否启用调试模式</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">debug</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span></code></pre></div><p>以后只需要直接执行 <code>pagefind</code> ，就会在 <code>public/zh</code> 目录下生成 pagefind 目录（即索引文件）</p>
<p><img src="https://image.iswbm.com/202406102222590.png"></p>
<p>有了索引后，还需要有一个搜索的页面。</p>
<p>在 <code>content/zh</code> 和 <code>content/en</code> 下各新增一个 <code>search.md</code> 文件，中文的版本如下</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">title: &#34;搜索&#34;
</span></span><span class="line"><span class="cl">layout: search
</span></span><span class="line"><span class="cl">menu: &#34;main&#34;
</span></span><span class="line"><span class="cl">weight: 20
</span></span><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&gt;目前本网站应用 [<span class="nt">Pagefind</span>](<span class="na">https://pagefind.app/</span>) 进行搜索，英文准确度较高，若要检索中文，建议把关键词拆分成最多两个字一组，并包含在符号 <span class="sb">`&#34;`</span> 之中。例如若要搜索 <span class="sb">`区块链`</span> 或 <span class="sb">`智能合约`</span>，可以使用 <span class="sb">`&#34;区块 链&#34;`</span> 和 <span class="sb">`&#34;智能 合约&#34;`</span>，可能会有更好的搜索结果。
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&gt;如果你对搜索结果不太满意或想了解更多，欢迎通过任意文章留言与我分享，或发送邮件至 <span class="sb">`wongbingming@163.com`</span>，告诉我你的想法。
</span></span></code></pre></div><p>由于指定了 <code>menu:&quot;main&quot;</code> ，因此会直接新增一个菜单，效果如下</p>
<p><img src="https://image.iswbm.com/202406102145238.png"></p>
<p>搜索的效果如下</p>
<p><img src="https://image.iswbm.com/202406102225417.png"></p>
<h2 id="5-部署上线">5. 部署上线</h2>
<p>一切调度完成后，需要将网站部署到服务器上才可能公网访问。</p>
<p>如果自己没有服务器的，可以自行购买一个，618 将近，各大厂商也开始打折促销，可以点我下面的链接领个卷再去购买比较划算</p>
<ul>
<li>阿里云：https://iswbm.com/alicloud</li>
<li>腾讯云：https://iswbm.com/txcloud</li>
<li>Ucloud：https://iswbm.com/ucloud</li>
</ul>
<p>有了服务器后，需要在服务器上安装 nginx</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">yum install nginx
</span></span></code></pre></div><p>然后我们在本地电脑的项目根目录下直接执行 hugo 即可编译最终的 html 文件到 public 中（记得清空此前的 public 目录，以免有缓存残留）</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">hugo
</span></span></code></pre></div><p>然后将 public 目录直接打包上传到服务器的某个目录下，这个目录自行定义即可，比如我的目录是 /home/rtd-docs/iswbm.com/public，然后直接用 nginx 进行代理即可，nginx 的配置如下</p>
<p>需要注意的是，在如下配置中的需要自签证书，没有搞过的请自行翻阅我之前的文章，有比较系统的讲解</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">server<span class="o">{</span>
</span></span><span class="line"><span class="cl">        listen 80<span class="p">;</span>
</span></span><span class="line"><span class="cl">        server_name  iswbm.com www.iswbm.com<span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="m">301</span> https://iswbm.com<span class="nv">$request_uri</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">server <span class="o">{</span>
</span></span><span class="line"><span class="cl">        listen <span class="m">443</span> ssl<span class="p">;</span>
</span></span><span class="line"><span class="cl">        server_name iswbm.com<span class="p">;</span>
</span></span><span class="line"><span class="cl">        index index.html<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">       ssl_certificate /path/to/fullchain.pem<span class="p">;</span>
</span></span><span class="line"><span class="cl">       ssl_certificate_key /path/to/privkey.pem<span class="p">;</span>
</span></span><span class="line"><span class="cl">       ssl_trusted_certificate /path/to/cert.pem<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">       root /home/rtd-docs/iswbm.com/public<span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>配置完后，直接重启 nginx，访问你的域名即可，以我的网站为例效果见下，干净简洁而且轻量，目前只整理了早期极少部分的文章，后续再花个时间慢慢搬过来</p>
<p><img src="https://image.iswbm.com/202406161832625.png"></p>
<p>以上大概就是搭建的过程，对于大多数人来说参照本篇文章都能快速搭建，如果你在搭建过程中受阻，也可联系我v: stromwbm 交流～</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="https://gohugo.io/">Hugo 官方网站</a></li>
<li><a href="https://sspai.com/post/73512">Hugo + GitHub Action，搭建你的博客自动发布系统</a></li>
<li><a href="https://github.com/shaform/hugo-theme-den">hugo-theme-den 主题仓库</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>如何使用Hexo搭建个人博客</title>
      <link>https://iswbm.com/en/2024/06/16/how-to-deploy-blog-with-hexo/</link>
      <pubDate>Sun, 16 Jun 2024 21:18:42 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/16/how-to-deploy-blog-with-hexo/</guid>
      <description>&lt;p&gt;现在越来越多的人喜欢利用Github搭建静态网站，原因不外乎简单省钱。本人也利用hexo+github搭建了本博客，用于分享一些心得。在此过程中，折腾博客的各种配置以及功能占具了我一部分时间，在此详细记录下我是如何利用hexo+github搭建静态博客以及一些配置相关问题，以免过后遗忘，且当备份之用。&lt;/p&gt;
&lt;h2 id=&#34;1创建博客项目&#34;&gt;1、创建博客项目&lt;/h2&gt;
&lt;h3 id=&#34;11-准备工作&#34;&gt;1.1 准备工作&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;下载&lt;a href=&#34;http://nodejs.cn/download/&#34;&gt;node.js&lt;/a&gt;并安装（官网下载安装），默认会安装npm。&lt;/li&gt;
&lt;li&gt;下载安装git（官网下载安装）&lt;/li&gt;
&lt;li&gt;下载安装hexo。方法：打开终端 运行&lt;code&gt;npm install -g hexo&lt;/code&gt;（要翻墙）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;http://image.iswbm.com/image-20200321163152876.png&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;12-初始化项目&#34;&gt;1.2 初始化项目&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;新创建一个目录，比如叫做 &lt;code&gt;iswbm-blog&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;使用终端进入该文件夹内，执行&lt;code&gt;hexo init&lt;/code&gt; （初始化项目）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;http://image.iswbm.com/image-20200321163746032.png&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;运行你的博客项目（用于调试），查看生成的文章是否符合自己的预期。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ hexo g  &lt;span class=&#34;c1&#34;&gt;# 生成静态文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ hexo s  &lt;span class=&#34;c1&#34;&gt;# 运行本地web服务器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;运行完 &lt;code&gt;hexo s&lt;/code&gt; 会有一个本地web地址（&lt;code&gt;localhost:4000&lt;/code&gt;）出现在你的屏幕，打开它即可看到。&lt;/p&gt;
&lt;h3 id=&#34;13-部署上线&#34;&gt;1.3 部署上线&lt;/h3&gt;
&lt;p&gt;要让我们在 &lt;code&gt;localhost:4000&lt;/code&gt;  看到的那些内容能够在公网能够访问。我们需要将其部署上线。&lt;/p&gt;
&lt;p&gt;按照下面的步骤来&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在Github上创建名字为XXX.github.io的项目，XXX为自己的github用户名。然后 clone 到你的本地电脑上。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://image.iswbm.com/image-20200321165634287.png&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;打开该文件夹内的&lt;code&gt;_config.yml&lt;/code&gt;配置文件，将其中的type设置为git，其他配置如下&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yml&#34; data-lang=&#34;yml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Deployment&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c&#34;&gt;## Docs: https://hexo.io/docs/deployment.html&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;deploy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;git&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;repository&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;https://github.com/iswbm/iswbm.github.io.git&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;branch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;master&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;安装Git部署插件&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ npm install hexo-deployer-git --save
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;重新生成静态文件，部署上线。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ hexo clean 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ hexo g 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ hexo d
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在第一次部署后，请到 github 后台，补充你的 网站信息，否则无法访问。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://image.iswbm.com/image-20200321171008622.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;一切完成之后，你就可以通过上面的网址来访问我的博客了。&lt;/p&gt;
&lt;h3 id=&#34;14-绑定域名&#34;&gt;1.4 绑定域名&lt;/h3&gt;
&lt;p&gt;做为一个个人博客，使用 github的域名，明显不够个性化。&lt;/p&gt;
&lt;p&gt;我们可以自己去阿里云购买一个域名（我的是 iswbm.com），然后将其 CNAME 到你的博客地址上。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;去阿里云 域名云解析&lt;br&gt;
&lt;img src=&#34;http://image.iswbm.com/image-20200321171939919.png&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;然后到对应的GitHub仓库，绑定我的个性域名&lt;br&gt;
&lt;img src=&#34;http://image.iswbm.com/image-20200321171821683.png&#34;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;然后再在你本地的项目里的 &lt;code&gt;source&lt;/code&gt; 目录下新建 &lt;code&gt;CNAME&lt;/code&gt;文件，内容就是我的域名&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;iswbm.com
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;然后重新部署，使配置生效。这样就可以使用域名访问。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ hexo clean 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ hexo g 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ hexo d
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;2丰富博客页面&#34;&gt;2、丰富博客页面&lt;/h2&gt;
&lt;hr&gt;
&lt;h3 id=&#34;21-标签页&#34;&gt;2.1 标签页&lt;/h3&gt;
&lt;p&gt;具体请参考下官方教程：&lt;a href=&#34;https://github.com/iissnan/hexo-theme-next/wiki/%E5%88%9B%E5%BB%BA%E6%A0%87%E7%AD%BE%E4%BA%91%E9%A1%B5%E9%9D%A2&#34;&gt;创建标签云页面&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;22-分类页面&#34;&gt;2.2 分类页面&lt;/h3&gt;
&lt;p&gt;具体请参考下官方教程：&lt;a href=&#34;https://github.com/iissnan/hexo-theme-next/wiki/%E5%88%9B%E5%BB%BA%E5%88%86%E7%B1%BB%E9%A1%B5%E9%9D%A2&#34;&gt;创建分类页面&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;23-关于页面&#34;&gt;2.3 关于页面&lt;/h3&gt;
&lt;p&gt;具体请参考下官方教程：&lt;a href=&#34;https://github.com/iissnan/hexo-theme-next/wiki/%E5%88%9B%E5%BB%BA-%22%E5%85%B3%E4%BA%8E%E6%88%91%22-%E9%A1%B5%E9%9D%A2&#34;&gt;创建关于页面&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;3博文书写技巧&#34;&gt;3、博文书写技巧&lt;/h2&gt;
&lt;hr&gt;
&lt;h3 id=&#34;31-引用块&#34;&gt;3.1 引用块&lt;/h3&gt;
&lt;p&gt;更多设置，查看&lt;a href=&#34;https://hexo.io/zh-cn/docs/tag-plugins.html&#34;&gt;官方文档&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;{% blockquote David Levithan, Wide Awake %}
Do not just seek happiness for yourself. Seek happiness for all. Through kindness. Through mercy.
{% endblockquote %}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;效果如下&lt;br&gt;
&lt;img src=&#34;http://image.iswbm.com/17-9-10/85269241.jpg&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;32-一键生成md头格式&#34;&gt;3.2 一键生成md头格式&lt;/h3&gt;
&lt;p&gt;首先在&lt;code&gt;/scaffolds/post.md&lt;/code&gt;文件中添加：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;---
title: {{ title }}
date: {{ date }}
tags:
categories: 
copyright: true
permalink: 01
top: 0
password:
---
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;然后使用 &lt;code&gt;hexo new&lt;/code&gt;就可以一键生成新文章的头格式了，不用手动去搬运或者书写。相当方便。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;image-20200321201555321&#34; src=&#34;http://image.iswbm.com/image-20200321201555321.png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;4美化博客&#34;&gt;4、美化博客&lt;/h2&gt;
&lt;hr&gt;
&lt;h3 id=&#34;41-更换主题&#34;&gt;4.1 更换主题&lt;/h3&gt;
&lt;p&gt;在 hexo 部署目录下，使用如下命令下载主题&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git clone https://github.com/iissnan/hexo-theme-next themes/next
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;打开站点配置文件（部署代码根目录下的 &lt;code&gt;_config.yml&lt;/code&gt;）选择刚刚下载的next主题&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# Extensions
## Plugins: https://hexo.io/plugins/
## Themes: https://hexo.io/themes/
theme: next
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;然后再编辑 &lt;code&gt;themes/next/_config.yml&lt;/code&gt; 选择主题样式&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;scheme: Pisces
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;42-ico缩略图&#34;&gt;4.2 ico缩略图&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;1. 制作icon图标，最好是32x32，可以在这里制作：https://tool.lu/favicon/

2. 将制作的ico文件，放到next主题source/images目录下

3. 配置ico文件路径。配置文件在 themes/next/_config.yml
favicon:
  small: /images/favicon-16x16-next.ico
  medium: /images/favicon-32x32-next.ico
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;43-菜单栏和图标&#34;&gt;4.3 菜单栏和图标&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;menu:
  home: / || home
  about: /about/ || user
  tags: /tags/ || tags
  categories: /categories/ || th
  archives: /archives/ || archive
  schedule: /schedule/ || calendar
  # sitemap: /sitemap.xml || sitemap
  # commonweal: /404/ || heartbeat

# Enable/Disable menu icons.
menu_icons:
  enable: true
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;44-社交网络和图标&#34;&gt;4.4 社交网络和图标&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;social:
  GitHub: https://github.com/iswbm || github
  E-Mail: mailto:wongbingming@163.com || envelope-o
  微博: http://weibo.com/942663728 || weibo
  WeChat: http://image.iswbm.com/17-9-9/58657236.jpg || weixin
  知乎: https://www.zhihu.com/people/wongbingming/activities || chain-broken
  CnBlog: http://www.cnblogs.com/wongbingming/ || file-text-o
  
social_icons:
  enable: true
  icons_only: false
  transition: false
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;45-背景动画&#34;&gt;4.5 背景动画&lt;/h3&gt;
&lt;p&gt;在&lt;code&gt;next&lt;/code&gt;下的&lt;code&gt;_config.yml&lt;/code&gt;找到&lt;code&gt;canvas_nest&lt;/code&gt;设置为&lt;code&gt;True&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&#34;46-添加热度&#34;&gt;4.6 添加热度&lt;/h3&gt;
&lt;p&gt;next主题集成leanCloud，我只需稍微配置下(在主题配置文件)&lt;br&gt;
其中的id和key要去&lt;code&gt;LeanCloud&lt;/code&gt;注册登录然后创建应用后，新建Class，名字一定要是&lt;code&gt;Counter&lt;/code&gt;，然后查看id和key填入&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;post_meta:
  item_text: true

leancloud_visitors:
  enable: true
  app_id: 你的id
  app_key: 你的key
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;打开&lt;code&gt;themes/next/layout/_macro/post.swig&lt;/code&gt;&lt;br&gt;
在&lt;code&gt;”leancloud-visitors-count”&amp;gt;&lt;/code&gt;标签后面添加℃。&lt;br&gt;
然后打开，&lt;code&gt;themes/next/languages/zh-Hans.yml&lt;/code&gt;，将visitors内容改为热度即可。&lt;/p&gt;
&lt;p&gt;为什么不直接用不蒜子，因为首页的时候，无法显示。&lt;br&gt;
如果也开了不蒜子的计数功能的话，可以直接把下面代码删掉&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;{% if not is_index and theme.busuanzi_count.enable and theme.busuanzi_count.page_pv %}
            &amp;lt;span class=&amp;#34;post-meta-divider&amp;#34;&amp;gt;|&amp;lt;/span&amp;gt;
            &amp;lt;span class=&amp;#34;page-pv&amp;#34;&amp;gt;{{ theme.busuanzi_count.page_pv_header }}
            &amp;lt;span class=&amp;#34;busuanzi-value&amp;#34; id=&amp;#34;busuanzi_value_page_pv&amp;#34; &amp;gt;&amp;lt;/span&amp;gt;{{ theme.busuanzi_count.page_pv_footer }}
            &amp;lt;/span&amp;gt;
{% endif %}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果你在前端看到了这个错误&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Code 504: The app is archived, please restore in console before use.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;很好解决，前往 &lt;a href=&#34;https://leancloud.cn/&#34;&gt;LeanCloud&lt;/a&gt; 重新激活应用即可。&lt;/p&gt;
&lt;p&gt;如果在前端又出现了这个错误&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Code 403: 访问被api域名白名单拒绝，请检查你的安全域名设置.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;也很好解决，前往 &lt;a href=&#34;https://leancloud.cn/&#34;&gt;LeanCloud&lt;/a&gt; 绑定你的域名即可。不过要注意的是这个域名，你得备案，否则会绑定失败&lt;/p&gt;
&lt;h3 id=&#34;47-分享插件jiathis&#34;&gt;4.7 分享插件JiaThis&lt;/h3&gt;
&lt;p&gt;默认有好多分享平台，可以在jiathis.swig里删除不需要的&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;jiathis:
  uid: 2135144 #Get this uid from http://www.jiathis.com/
# add_this_id:
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;隐藏页脚的hexo强力驱动，在&lt;code&gt;footer.swig&lt;/code&gt;里注释这段代码&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;!--
{% if theme.copyright %}
  &amp;lt;div class=&amp;#34;powered-by&amp;#34;&amp;gt;{#
  #}{{ __(&amp;#39;footer.powered&amp;#39;, &amp;#39;&amp;lt;a class=&amp;#34;theme-link&amp;#34; href=&amp;#34;https://hexo.io&amp;#34;&amp;gt;Hexo&amp;lt;/a&amp;gt;&amp;#39;) }}{#
#}&amp;lt;/div&amp;gt;

  &amp;lt;span class=&amp;#34;post-meta-divider&amp;#34;&amp;gt;|&amp;lt;/span&amp;gt;
  &amp;lt;div class=&amp;#34;theme-info&amp;#34;&amp;gt;{#
  #}{{ __(&amp;#39;footer.theme&amp;#39;) }} &amp;amp;mdash; {#
  #}&amp;lt;a class=&amp;#34;theme-link&amp;#34; href=&amp;#34;https://github.com/iissnan/hexo-theme-next&amp;#34;&amp;gt;{#
    #}NexT.{{ theme.scheme }}{#
  #}&amp;lt;/a&amp;gt; v{{ theme.version }}{#
#}&amp;lt;/div&amp;gt;
{% endif %}
--&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;48-fork-me-on-github&#34;&gt;4.8 Fork me on Github&lt;/h3&gt;
&lt;p&gt;点击&lt;a href=&#34;https://github.com/blog/273-github-ribbons&#34;&gt;这里&lt;/a&gt;挑选自己喜欢的样式，并复制代码&lt;br&gt;
然后粘贴刚才复制的代码到&lt;code&gt;themes/next/layout/_layout.swig&lt;/code&gt;文件中(放在&lt;code&gt;&amp;lt;div class=&amp;quot;headband&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;的下面)，并把&lt;code&gt;href&lt;/code&gt;改为你的&lt;code&gt;github&lt;/code&gt;地址&lt;/p&gt;
&lt;h3 id=&#34;49-设置阅读全文&#34;&gt;4.9 设置阅读全文&lt;/h3&gt;
&lt;p&gt;在md博文里添加&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>现在越来越多的人喜欢利用Github搭建静态网站，原因不外乎简单省钱。本人也利用hexo+github搭建了本博客，用于分享一些心得。在此过程中，折腾博客的各种配置以及功能占具了我一部分时间，在此详细记录下我是如何利用hexo+github搭建静态博客以及一些配置相关问题，以免过后遗忘，且当备份之用。</p>
<h2 id="1创建博客项目">1、创建博客项目</h2>
<h3 id="11-准备工作">1.1 准备工作</h3>
<ul>
<li>下载<a href="http://nodejs.cn/download/">node.js</a>并安装（官网下载安装），默认会安装npm。</li>
<li>下载安装git（官网下载安装）</li>
<li>下载安装hexo。方法：打开终端 运行<code>npm install -g hexo</code>（要翻墙）</li>
</ul>
<p><img src="http://image.iswbm.com/image-20200321163152876.png"></p>
<h3 id="12-初始化项目">1.2 初始化项目</h3>
<ul>
<li>新创建一个目录，比如叫做 <code>iswbm-blog</code></li>
<li>使用终端进入该文件夹内，执行<code>hexo init</code> （初始化项目）</li>
</ul>
<p><img src="http://image.iswbm.com/image-20200321163746032.png"></p>
<ul>
<li>运行你的博客项目（用于调试），查看生成的文章是否符合自己的预期。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ hexo g  <span class="c1"># 生成静态文件</span>
</span></span><span class="line"><span class="cl">$ hexo s  <span class="c1"># 运行本地web服务器</span>
</span></span></code></pre></div><p>运行完 <code>hexo s</code> 会有一个本地web地址（<code>localhost:4000</code>）出现在你的屏幕，打开它即可看到。</p>
<h3 id="13-部署上线">1.3 部署上线</h3>
<p>要让我们在 <code>localhost:4000</code>  看到的那些内容能够在公网能够访问。我们需要将其部署上线。</p>
<p>按照下面的步骤来</p>
<ul>
<li>
<p>在Github上创建名字为XXX.github.io的项目，XXX为自己的github用户名。然后 clone 到你的本地电脑上。</p>
<p><img src="http://image.iswbm.com/image-20200321165634287.png"></p>
</li>
<li>
<p>打开该文件夹内的<code>_config.yml</code>配置文件，将其中的type设置为git，其他配置如下</p>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="c"># Deployment</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c">## Docs: https://hexo.io/docs/deployment.html</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">deploy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">repository</span><span class="p">:</span><span class="w"> </span><span class="l">https://github.com/iswbm/iswbm.github.io.git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">branch</span><span class="p">:</span><span class="w"> </span><span class="l">master</span><span class="w">
</span></span></span></code></pre></div><ul>
<li>安装Git部署插件</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ npm install hexo-deployer-git --save
</span></span></code></pre></div><ul>
<li>重新生成静态文件，部署上线。</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ hexo clean 
</span></span><span class="line"><span class="cl">$ hexo g 
</span></span><span class="line"><span class="cl">$ hexo d
</span></span></code></pre></div><p>在第一次部署后，请到 github 后台，补充你的 网站信息，否则无法访问。</p>
<p><img src="http://image.iswbm.com/image-20200321171008622.png"></p>
<p>一切完成之后，你就可以通过上面的网址来访问我的博客了。</p>
<h3 id="14-绑定域名">1.4 绑定域名</h3>
<p>做为一个个人博客，使用 github的域名，明显不够个性化。</p>
<p>我们可以自己去阿里云购买一个域名（我的是 iswbm.com），然后将其 CNAME 到你的博客地址上。</p>
<ul>
<li>
<p>去阿里云 域名云解析<br>
<img src="http://image.iswbm.com/image-20200321171939919.png"></p>
</li>
<li>
<p>然后到对应的GitHub仓库，绑定我的个性域名<br>
<img src="http://image.iswbm.com/image-20200321171821683.png"></p>
</li>
<li>
<p>然后再在你本地的项目里的 <code>source</code> 目录下新建 <code>CNAME</code>文件，内容就是我的域名</p>
<pre tabindex="0"><code>iswbm.com
</code></pre></li>
<li>
<p>然后重新部署，使配置生效。这样就可以使用域名访问。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ hexo clean 
</span></span><span class="line"><span class="cl">$ hexo g 
</span></span><span class="line"><span class="cl">$ hexo d
</span></span></code></pre></div></li>
</ul>
<h2 id="2丰富博客页面">2、丰富博客页面</h2>
<hr>
<h3 id="21-标签页">2.1 标签页</h3>
<p>具体请参考下官方教程：<a href="https://github.com/iissnan/hexo-theme-next/wiki/%E5%88%9B%E5%BB%BA%E6%A0%87%E7%AD%BE%E4%BA%91%E9%A1%B5%E9%9D%A2">创建标签云页面</a></p>
<h3 id="22-分类页面">2.2 分类页面</h3>
<p>具体请参考下官方教程：<a href="https://github.com/iissnan/hexo-theme-next/wiki/%E5%88%9B%E5%BB%BA%E5%88%86%E7%B1%BB%E9%A1%B5%E9%9D%A2">创建分类页面</a></p>
<h3 id="23-关于页面">2.3 关于页面</h3>
<p>具体请参考下官方教程：<a href="https://github.com/iissnan/hexo-theme-next/wiki/%E5%88%9B%E5%BB%BA-%22%E5%85%B3%E4%BA%8E%E6%88%91%22-%E9%A1%B5%E9%9D%A2">创建关于页面</a></p>
<h2 id="3博文书写技巧">3、博文书写技巧</h2>
<hr>
<h3 id="31-引用块">3.1 引用块</h3>
<p>更多设置，查看<a href="https://hexo.io/zh-cn/docs/tag-plugins.html">官方文档</a></p>
<pre tabindex="0"><code>{% blockquote David Levithan, Wide Awake %}
Do not just seek happiness for yourself. Seek happiness for all. Through kindness. Through mercy.
{% endblockquote %}
</code></pre><p>效果如下<br>
<img src="http://image.iswbm.com/17-9-10/85269241.jpg"></p>
<h3 id="32-一键生成md头格式">3.2 一键生成md头格式</h3>
<p>首先在<code>/scaffolds/post.md</code>文件中添加：</p>
<pre tabindex="0"><code>---
title: {{ title }}
date: {{ date }}
tags:
categories: 
copyright: true
permalink: 01
top: 0
password:
---
</code></pre><p>然后使用 <code>hexo new</code>就可以一键生成新文章的头格式了，不用手动去搬运或者书写。相当方便。</p>
<p><img alt="image-20200321201555321" src="http://image.iswbm.com/image-20200321201555321.png"></p>
<h2 id="4美化博客">4、美化博客</h2>
<hr>
<h3 id="41-更换主题">4.1 更换主题</h3>
<p>在 hexo 部署目录下，使用如下命令下载主题</p>
<pre tabindex="0"><code>git clone https://github.com/iissnan/hexo-theme-next themes/next
</code></pre><p>打开站点配置文件（部署代码根目录下的 <code>_config.yml</code>）选择刚刚下载的next主题</p>
<pre tabindex="0"><code># Extensions
## Plugins: https://hexo.io/plugins/
## Themes: https://hexo.io/themes/
theme: next
</code></pre><p>然后再编辑 <code>themes/next/_config.yml</code> 选择主题样式</p>
<pre tabindex="0"><code>scheme: Pisces
</code></pre><h3 id="42-ico缩略图">4.2 ico缩略图</h3>
<pre tabindex="0"><code>1. 制作icon图标，最好是32x32，可以在这里制作：https://tool.lu/favicon/

2. 将制作的ico文件，放到next主题source/images目录下

3. 配置ico文件路径。配置文件在 themes/next/_config.yml
favicon:
  small: /images/favicon-16x16-next.ico
  medium: /images/favicon-32x32-next.ico
</code></pre><h3 id="43-菜单栏和图标">4.3 菜单栏和图标</h3>
<pre tabindex="0"><code>menu:
  home: / || home
  about: /about/ || user
  tags: /tags/ || tags
  categories: /categories/ || th
  archives: /archives/ || archive
  schedule: /schedule/ || calendar
  # sitemap: /sitemap.xml || sitemap
  # commonweal: /404/ || heartbeat

# Enable/Disable menu icons.
menu_icons:
  enable: true
</code></pre><h3 id="44-社交网络和图标">4.4 社交网络和图标</h3>
<pre tabindex="0"><code>social:
  GitHub: https://github.com/iswbm || github
  E-Mail: mailto:wongbingming@163.com || envelope-o
  微博: http://weibo.com/942663728 || weibo
  WeChat: http://image.iswbm.com/17-9-9/58657236.jpg || weixin
  知乎: https://www.zhihu.com/people/wongbingming/activities || chain-broken
  CnBlog: http://www.cnblogs.com/wongbingming/ || file-text-o
  
social_icons:
  enable: true
  icons_only: false
  transition: false
</code></pre><h3 id="45-背景动画">4.5 背景动画</h3>
<p>在<code>next</code>下的<code>_config.yml</code>找到<code>canvas_nest</code>设置为<code>True</code></p>
<h3 id="46-添加热度">4.6 添加热度</h3>
<p>next主题集成leanCloud，我只需稍微配置下(在主题配置文件)<br>
其中的id和key要去<code>LeanCloud</code>注册登录然后创建应用后，新建Class，名字一定要是<code>Counter</code>，然后查看id和key填入</p>
<pre tabindex="0"><code>post_meta:
  item_text: true

leancloud_visitors:
  enable: true
  app_id: 你的id
  app_key: 你的key
</code></pre><p>打开<code>themes/next/layout/_macro/post.swig</code><br>
在<code>”leancloud-visitors-count”&gt;</code>标签后面添加℃。<br>
然后打开，<code>themes/next/languages/zh-Hans.yml</code>，将visitors内容改为热度即可。</p>
<p>为什么不直接用不蒜子，因为首页的时候，无法显示。<br>
如果也开了不蒜子的计数功能的话，可以直接把下面代码删掉</p>
<pre tabindex="0"><code>{% if not is_index and theme.busuanzi_count.enable and theme.busuanzi_count.page_pv %}
            &lt;span class=&#34;post-meta-divider&#34;&gt;|&lt;/span&gt;
            &lt;span class=&#34;page-pv&#34;&gt;{{ theme.busuanzi_count.page_pv_header }}
            &lt;span class=&#34;busuanzi-value&#34; id=&#34;busuanzi_value_page_pv&#34; &gt;&lt;/span&gt;{{ theme.busuanzi_count.page_pv_footer }}
            &lt;/span&gt;
{% endif %}
</code></pre><p>如果你在前端看到了这个错误</p>
<pre tabindex="0"><code>Code 504: The app is archived, please restore in console before use.
</code></pre><p>很好解决，前往 <a href="https://leancloud.cn/">LeanCloud</a> 重新激活应用即可。</p>
<p>如果在前端又出现了这个错误</p>
<pre tabindex="0"><code>Code 403: 访问被api域名白名单拒绝，请检查你的安全域名设置.
</code></pre><p>也很好解决，前往 <a href="https://leancloud.cn/">LeanCloud</a> 绑定你的域名即可。不过要注意的是这个域名，你得备案，否则会绑定失败</p>
<h3 id="47-分享插件jiathis">4.7 分享插件JiaThis</h3>
<p>默认有好多分享平台，可以在jiathis.swig里删除不需要的</p>
<pre tabindex="0"><code>jiathis:
  uid: 2135144 #Get this uid from http://www.jiathis.com/
# add_this_id:
</code></pre><p>隐藏页脚的hexo强力驱动，在<code>footer.swig</code>里注释这段代码</p>
<pre tabindex="0"><code>&lt;!--
{% if theme.copyright %}
  &lt;div class=&#34;powered-by&#34;&gt;{#
  #}{{ __(&#39;footer.powered&#39;, &#39;&lt;a class=&#34;theme-link&#34; href=&#34;https://hexo.io&#34;&gt;Hexo&lt;/a&gt;&#39;) }}{#
#}&lt;/div&gt;

  &lt;span class=&#34;post-meta-divider&#34;&gt;|&lt;/span&gt;
  &lt;div class=&#34;theme-info&#34;&gt;{#
  #}{{ __(&#39;footer.theme&#39;) }} &amp;mdash; {#
  #}&lt;a class=&#34;theme-link&#34; href=&#34;https://github.com/iissnan/hexo-theme-next&#34;&gt;{#
    #}NexT.{{ theme.scheme }}{#
  #}&lt;/a&gt; v{{ theme.version }}{#
#}&lt;/div&gt;
{% endif %}
--&gt;
</code></pre><h3 id="48-fork-me-on-github">4.8 Fork me on Github</h3>
<p>点击<a href="https://github.com/blog/273-github-ribbons">这里</a>挑选自己喜欢的样式，并复制代码<br>
然后粘贴刚才复制的代码到<code>themes/next/layout/_layout.swig</code>文件中(放在<code>&lt;div class=&quot;headband&quot;&gt;&lt;/div&gt;</code>的下面)，并把<code>href</code>改为你的<code>github</code>地址</p>
<h3 id="49-设置阅读全文">4.9 设置阅读全文</h3>
<p>在md博文里添加</p>
e><h3 id="410-添加版权">4.10 添加版权</h3>
<p>修改<code>themes/next/layout/_macro/post-copyright.swig</code>如下</p>
<pre tabindex="0"><code>&lt;ul class=&#34;post-copyright&#34;&gt;
  &lt;li class=&#34;my_post-copyright-author&#34;&gt;
    &lt;strong&gt;{{ __(&#39;post.copyright.author&#39;) + __(&#39;symbol.colon&#39;) }}&lt;/strong&gt;
    {{ config.author }}
  &lt;/li&gt;
  &lt;li class=&#34;my_post-copyright-link&#34;&gt;
    &lt;strong&gt;本文链接：&lt;/strong&gt;
    &lt;a href=&#34;{{ post.permalink }}&#34; title=&#34;{{ post.title }}&#34;&gt;{{ post.permalink }}&lt;/a&gt;
  &lt;/li&gt;
  &lt;li class=&#34;my_post-copyright-link&#34;&gt;
    &lt;strong&gt;发布时间：&lt;/strong&gt;{{ page.date.format(&#34;YYYY年MM月DD日 - HH:MM&#34;) }}
  &lt;/li&gt;
  &lt;li class=&#34;my_post-copyright-link&#34;&gt;
    &lt;strong&gt;最后更新：&lt;/strong&gt;{{ page.updated.format(&#34;YYYY年MM月DD日 - HH:MM&#34;) }}
  &lt;/li&gt;

  &lt;li class=&#34;my_post-copyright-license&#34;&gt;
    &lt;strong&gt;{{ __(&#39;post.copyright.license_title&#39;) + __(&#39;symbol.colon&#39;) }} &lt;/strong&gt;
    {{ __(&#39;post.copyright.license_content&#39;, theme.post_copyright.license_url, theme.post_copyright.license) }}
  &lt;/li&gt;
&lt;/ul&gt;
</code></pre><p>到主题配置文件，改<code>enable</code>为<code>True</code></p>
<pre tabindex="0"><code>post_copyright:
  enable: true
  license: CC BY-NC-SA 3.0
  license_url: https://creativecommons.org/licenses/by-nc-sa/3.0/
</code></pre><h3 id="411-文章结尾页眉">4.11 文章结尾页眉</h3>
<p>在路径<code>themes/next/layout/_macro</code>中新建 <code>passage-end-tag.swig</code> 文件,并添加以下内容：</p>
<pre tabindex="0"><code>&lt;div&gt;
    {% if not is_index %}
        &lt;div style=&#34;text-align:center;color: #ccc;font-size:14px;&#34;&gt;-------------Page&#39;s over&lt;i class=&#34;fa fa-paw&#34;&gt;&lt;/i&gt;Thanks for reading-------------&lt;/div&gt;
    {% endif %}
&lt;/div&gt;
</code></pre><p>接着打开<code>themes/next/layout/_macro/post.swig</code>文件，添加如下下代码，注意位置<br>
<img src="http://image.iswbm.com/17-9-9/63041495.jpg"><br>
代码如下：</p>
<pre tabindex="0"><code>&lt;div&gt;
  {% if not is_index %}
    {% include &#39;passage-end-tag.swig&#39; %}
  {% endif %}
&lt;/div&gt;
</code></pre><p>然后再主题文件增加如下配置，以便可以方便开关这个功能</p>
<pre tabindex="0"><code># 文章末尾添加“本文结束”标记
passage_end_tag:
  enabled: true
</code></pre><h3 id="412-更改标签的图标">4.12 更改标签的图标</h3>
<p>修改模板<code>/themes/next/layout/_macro/post.swig</code>，搜索 <code>rel=”tag”&gt;#</code>，将 # 换成</p>
<pre tabindex="0"><code>&lt;i class=&#34;fa fa-tag&#34;&gt;&lt;/i&gt;
</code></pre><h3 id="413-添加访问量和访客数">4.13 添加访问量和访客数</h3>
<p><code>\themes\next\layout_partials\footer.swig</code>最前面添加如下代码</p>
<pre tabindex="0"><code>&lt;script async src=&#34;https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js&#34;&gt;&lt;/script&gt;
</code></pre><h3 id="414-更改容器宽度">4.14 更改容器宽度</h3>
<p>更改NexT容器宽度可以参考这个<a href="http://theme-next.iissnan.com/faqs.html">常见问题</a><br>
Pisces Scheme比较特殊。<br>
在<code>themes/next/source/css/_schemes/Pisces/_layout.styl</code>最后面增加如下样式</p>
<pre tabindex="0"><code>.header{
    width: 80%;
    +tablet() {
        width: 100%;
    }
    +mobile() {
        width: 100%;
    }
}
.container .main-inner {
    width: 80%;
    +tablet() {
        width: 100%;
    }
    +mobile() {
        width: 100%;
    }
}
.content-wrap {
    width: calc(100% - 260px);
    +tablet() {
        width: 100%;
    }
    +mobile() {
        width: 100%;
    }
}
</code></pre><h3 id="415-自定义css">4.15 自定义CSS</h3>
<p>默认的博客文章，是按照你选定的主题来显示。其中可能有些并不那么尽如你意。</p>
<p>但是没有关系，你可以自己定义自己满意的css样式，以下是我自己定义的。在这里做个记录。</p>
<pre tabindex="0"><code># 设置图片不居中
themes/next/source/css/_common/components/post/post-expand.styl

.post-gallery-row .fancybox img { margin: 0 auto !important;}


# 标题格式
themes/next/source/css/_common/scaffolding/base.styl

h1 {
    font-size: 27px;
    position: relative;
    padding: 15px;
    margin-bottom: 20px;
    border: 1px solid #eee;
    border-radius: 3px;
    border-left-color: red;
    border-left-width: 5px;
    background-color: #406CA4;
    color: #ffffff;
    font-family: cursive;
    border-radius: 15px 15px 15px 15px !important;
}
</code></pre><h3 id="416-添加头像">4.16 添加头像</h3>
<p>将你的头像放置到 <code>themes/next/source/images/avatar.png</code></p>
<p>在 <code>themes/next/_config.yml</code> 设置路径</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">avatar</span><span class="p">:</span><span class="w"> </span><span class="l">/images/avatar.png</span><span class="w">
</span></span></span></code></pre></div><h3 id="417-设置中文">4.17 设置中文</h3>
<p>在根目录的 <code>_config.yml</code> 里 把 <code>language</code> 改成 <code>zh-Hans</code>，这个值是要和 <code>themes/next/languages</code> 目录下的文件名保持一致。</p>
<h3 id="418-不渲染-readme">4.18 不渲染 README</h3>
<p>在根目录创建 README.md 文件，内容由你决定。</p>
<p>然后在 <code>_config.yml</code> 修改配置</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">skip_render</span><span class="p">:</span><span class="w"> </span><span class="l">README.md</span><span class="w">
</span></span></span></code></pre></div><h2 id="5其他实用功能">5、其他实用功能</h2>
<h3 id="51-给文章加密">5.1 给文章加密</h3>
<p>打开themes-&gt;next-&gt;layout-&gt;_partials-&gt;head.swig文件,搜索<code>pace</code>，在这个代码块后面插入这样一段代码：</p>
<pre tabindex="0"><code>&lt;script&gt;
    (function(){
        if(&#39;{{ page.password }}&#39;){
            if (prompt(&#39;请输入文章密码&#39;) !== &#39;{{ page.password }}&#39;){
                alert(&#39;密码错误！&#39;);
                history.back();
            }
        }
    })();
&lt;/script&gt;
</code></pre><p>然后文章中设置</p>
<pre tabindex="0"><code>password: 你的密码
</code></pre><h3 id="52-博文压缩">5.2 博文压缩</h3>
<p>在站点的根目录下执行以下命令：</p>
<pre tabindex="0"><code>$ npm install gulp -g
$ npm install gulp-minify-css gulp-uglify gulp-htmlmin gulp-htmlclean gulp --save
</code></pre><p>在根目录下新建<code>gulpfile.js</code></p>
<pre tabindex="0"><code>var gulp = require(&#39;gulp&#39;);
var minifycss = require(&#39;gulp-minify-css&#39;);
var uglify = require(&#39;gulp-uglify&#39;);
var htmlmin = require(&#39;gulp-htmlmin&#39;);
var htmlclean = require(&#39;gulp-htmlclean&#39;);
// 压缩 public 目录 css
gulp.task(&#39;minify-css&#39;, function() {
    return gulp.src(&#39;./public/**/*.css&#39;)
        .pipe(minifycss())
        .pipe(gulp.dest(&#39;./public&#39;));
});
// 压缩 public 目录 html
gulp.task(&#39;minify-html&#39;, function() {
  return gulp.src(&#39;./public/**/*.html&#39;)
    .pipe(htmlclean())
    .pipe(htmlmin({
         removeComments: true,
         minifyJS: true,
         minifyCSS: true,
         minifyURLs: true,
    }))
    .pipe(gulp.dest(&#39;./public&#39;))
});
// 压缩 public/js 目录 js
gulp.task(&#39;minify-js&#39;, function() {
    return gulp.src(&#39;./public/**/*.js&#39;)
        .pipe(uglify())
        .pipe(gulp.dest(&#39;./public&#39;));
});
// 执行 gulp 命令时执行的任务
gulp.task(&#39;default&#39;, [
    &#39;minify-html&#39;,&#39;minify-css&#39;,&#39;minify-js&#39;
]);
</code></pre><p>生成博文是执行<code>hexo g &amp;&amp; gulp</code>就会根据<code>gulpfile.js</code>中的配置，对<code>public</code>目录中的静态资源文件进行压缩。</p>
<h3 id="53-设定置顶顺序">5.3 设定置顶/顺序</h3>
<p><code>F:\MyBlog\node_modules\hexo-generator-index\lib\generator.js</code>改成下面</p>
<pre tabindex="0"><code>&#39;use strict&#39;;
var pagination = require(&#39;hexo-pagination&#39;);
module.exports = function(locals){
  var config = this.config;
  var posts = locals.posts;
    posts.data = posts.data.sort(function(a, b) {
        if(a.top &amp;amp;&amp;amp; b.top) { // 两篇文章top都有定义
            if(a.top == b.top) return b.date - a.date; // 若top值一样则按照文章日期降序排
            else return b.top - a.top; // 否则按照top值降序排
        }
        else if(a.top &amp;amp;&amp;amp; !b.top) { // 以下是只有一篇文章top有定义，那么将有top的排在前面（这里用异或操作居然不行233）
            return -1;
        }
        else if(!a.top &amp;amp;&amp;amp; b.top) {
            return 1;
        }
        else return b.date - a.date; // 都没定义按照文章日期降序排
    });
  var paginationDir = config.pagination_dir || &#39;page&#39;;
  return pagination(&#39;&#39;, posts, {
    perPage: config.index_generator.per_page,
    layout: [&#39;index&#39;, &#39;archive&#39;],
    format: paginationDir + &#39;/%d/&#39;,
    data: {
      __index: true
    }
  });
};
</code></pre><p>在文章中添加 top 值，数值越大文章越靠前（默认是0，按时间排序），如</p>
<pre tabindex="0"><code>---
title: 解决Charles乱码问题
date: 2017-05-22 22:45:48
tags: 技巧
categories: 技巧
copyright: true
top: 100
---
</code></pre><h3 id="55-搜索功能">5.5 搜索功能</h3>
<p>参照<a href="https://superbsco.github.io/2017/01/13/new-article/">这篇博客</a>，搜索<code>搜索功能</code></p>
<h3 id="57-文章内链接文本样式">5.7 文章内链接文本样式</h3>
<p>在<code>F:\MyBlog\themes\next\source\css\_common\components\post\post.styl</code></p>
<pre tabindex="0"><code>// 文章内链接文本样式
.post-body p a{
  color: #0593d3;
  border-bottom: none;
  border-bottom: 1px solid #0593d3;
  &amp;amp;:hover {
    color: #fc6423;
    border-bottom: none;
    border-bottom: 1px solid #fc6423;
  }
}
</code></pre><p>其中选择.post-body 是为了不影响标题，选择 p 是为了不影响首页“阅读全文”的显示样式,颜色可以自己定义。</p>
<h3 id="58-归档设置分页数量">5.8 归档设置分页数量</h3>
<p>在根目录的 <code>_config.yml</code></p>
<pre tabindex="0"><code>index_generator:
  per_page: 5

archive_generator:
  per_page: 20
  yearly: true
  monthly: true

tag_generator:
  per_page: 10
</code></pre><h3 id="59-设置永久链接">5.9 设置永久链接</h3>
<p>在根目录的 <code>_config.yml</code></p>
<pre tabindex="0"><code>permalink: :year/:i_month/:i_day/:title.html
</code></pre><p>博文里设置一下，举个例子</p>
<pre tabindex="0"><code>permalink: Database-MySQL-Basic_usage
</code></pre><h2 id="6多台电脑协同更新博客">6、多台电脑协同更新博客</h2>
<hr>
<p>上班后，工作都会给配置了一台电脑，而自己家里也有电脑，有时候我想使用家里电脑更新博客，而有时候我也想使用公司电脑更新博客。</p>
<p>这就很蛋疼了，因为Markdown的原文只有一份，如何将两台电脑的原文保持一致呢。当然，容易想到的是代管在Github上。</p>
<h3 id="61-github上操作">6.1 Github上操作</h3>
<p>将博客项目分成两个分支</p>
<ul>
<li><code>master</code>为博客界面前端文件</li>
<li><code>hexo</code> 为博文markdown原文</li>
</ul>
<p>在web界面新建分支，命名为<code>hexo</code></p>
<p>在web界面设置 <code>hexo</code> 为默认分支，因为我们只会在这个分支上进行操作。<br>
<img src="http://image.iswbm.com/image-20200321193444320.png"></p>
<h3 id="62-本地pc操作">6.2 本地PC操作</h3>
<p>clone项目到本地：</p>
<pre tabindex="0"><code>git clone git@github.com:iswbm/iswbm.github.io.git
</code></pre><p>使用 <code>git branch</code> 确认此时的分支是否为hexo，如果不是则上面设置默认分支有误，要重新设置。</p>
<h3 id="63-处理hexo分支">6.3 处理hexo分支</h3>
<p>第一步我们创建hexo分支时，hexo的内容和master的内容是完全一致的，存放的是博客前端文件，这并不是我们想要的，所以我们要先将其清空，再放入我们的文件。</p>
<p>清空hexo分支</p>
<pre tabindex="0"><code>git checkout --orphan hexo
git rm -rf .
git commit -m &#34;clear hexo branch&#34;
git push origin hexo
</code></pre><p>将博客原始文件添加进来，但是有一些是没必要放的，具体要放哪些文件，看图片。<br>
<img src="https://i.loli.net/2018/04/15/5ad31888232e9.png"></p>
<pre tabindex="0"><code>git add -A
git commit -m &#34;add blog markdown raw files&#34;
</code></pre><p>添加node_modules/文件夹<br>
本来这个文件夹，不需要拷过来的，但是怕安装 <code>hexo-deployer-git</code> 失败，所以之前要拷贝的。</p>
<p>可以从下面这个链接里获取：</p>
<blockquote>
<p>下载链接；<a href="https://pan.baidu.com/s/1-DHgTuxb0mCj_7wdaKqAwQ">https://pan.baidu.com/s/1-DHgTuxb0mCj_7wdaKqAwQ</a><br>
获取密码：pckc</p>
</blockquote>
<p>然后检查是否正常渲染</p>
<pre tabindex="0"><code>hexo g
hexo s
</code></pre><p>如果一切正常，那么再确认下 <code>_config.yml</code> 文件的deploy参数的分支是master，我们可不能将其发布到 hexo 分支。</p>
<p>确认过后，我们就可以发布博客了。</p>
<pre tabindex="0"><code>hexo d
</code></pre><p>如果发布出现问题，可能是 <code>hexo-deployer-git</code> 这个插件安装有误，就要想办法装上了。这里就不讲了。</p>
<h2 id="7更换电脑后如何迁移博客">7、更换电脑后如何迁移博客</h2>
<p>在真实场景里，我们会有可能会换电脑或者将系统进行重装操作，而这个时候如何将之前的电脑上项目（主要是 Markdown原文）迁移过来呢？</p>
<p>在<code>七、多台电脑协同更新博客</code> 这一节里，我们已经讲过，如何将md原文进行管理。</p>
<p>那就好办了呀。只要将之前的项目 clone 下来就可以了呀。</p>
<p>首先肯定是要在新电脑上安装<code>Git</code>，<code>Node.Js</code>，<code>Hexo</code></p>
<h3 id="71-工具安装">7.1 工具安装</h3>
<p><code>安装Git</code>：百度自行下载</p>
<p><code>安装npm</code>：到 <a href="https://nodejs.org/en/">官网</a> 下载exe文件安装</p>
<p><code>安装hexo</code>：执行以下命令安装（注意需要新开一个git bash窗口，不然会提示找不到npm命令）</p>
<pre tabindex="0"><code>npm install -g hexo
</code></pre><h3 id="72-clone最新分支">7.2 clone最新分支</h3>
<pre tabindex="0"><code>git clone git@github.com:iswbm/iswbm.github.io.git
</code></pre><p>添加node_modules/文件夹
本来这个文件夹，不需要拷过来的，但是怕安装 <code>hexo-deployer-git</code> 失败，所以这里我直接备份一份，可以拷贝覆盖上即可。</p>
<blockquote>
<p>下载链接；<a href="https://pan.baidu.com/s/1-DHgTuxb0mCj_7wdaKqAwQ">https://pan.baidu.com/s/1-DHgTuxb0mCj_7wdaKqAwQ</a>
获取密码：pckc</p>
</blockquote>
<p>完成之后，应该就可以正常更新博客了。</p>
<p>在写完文章后，一定要注意操作顺序。</p>
<ol>
<li>先将hexo分支push要远端</li>
<li>再执行部署到master分支。</li>
</ol>
<p>不然哪天这个电脑又出现故障，那就麻烦了。</p>
<h2 id="参考资料">参考资料</h2>
<hr>
<ul>
<li><a href="https://hexo.io/zh-cn/docs/index.html">Hexo  官方使用文档 - 中文</a></li>
<li><a href="http://theme-next.iissnan.com/getting-started.html">Hexo Next 主题 官方使用文档 - 中文</a></li>
<li><a href="https://www.jianshu.com/p/f054333ac9e6">Hexo的next主题个性化教程：打造炫酷网站</a></li>
<li><a href="https://www.zhihu.com/question/21193762">使用hexo：如果换了电脑怎么更新博客？</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/26625249">GitHub+Hexo 搭建个人网站详细教程</a></li>
<li><a href="https://github.com/gitalk/gitalk/blob/master/readme-cn.md">Gitalk：基于 Github Issue 和 Preact 开发的评论插件</a></li>
<li><a href="https://thief.one/2017/03/03/Hexo%E6%90%AD%E5%BB%BA%E5%8D%9A%E5%AE%A2%E6%95%99%E7%A8%8B/">Hexo搭建博客教程</a></li>
</ul>]]></content:encoded>
    </item>
    
    <item>
      <title>如何使用letscertbot部署证书</title>
      <link>https://iswbm.com/en/2024/06/16/how-to-deploy-https-with-letscertbot/</link>
      <pubDate>Sun, 16 Jun 2024 21:14:39 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/16/how-to-deploy-https-with-letscertbot/</guid>
      <description>1、安装 certbot 以前安装 certbot 有一个叫做 certbot-auto 的脚本工具，但这个工具由于是用 python2 编写的，pythoh2 不维护后，官方也放弃了那些默认使用 python2 为默认 Python 的操作系统</description>
      <content:encoded><![CDATA[<h2 id="1安装-certbot">1、安装 certbot</h2>
<p>以前安装 certbot 有一个叫做 certbot-auto 的脚本工具，但这个工具由于是用 python2 编写的，pythoh2 不维护后，官方也放弃了那些默认使用 python2 为默认 Python 的操作系统。</p>
<p>没有了那个certbot-auto，虽然会麻烦一些，但好在官方给的文档非常全，一般也不会有什么问题，具体可以去​<a href="https://certbot.eff.org/">官方</a>查看。</p>
<p>下面是我在 CentOS 7.2 上的安装 方法，供你参考。</p>
<p><strong>第一步</strong>：安装 snapd，并启动它。</p>
<p>使用 snap 对内核有要求，太老的内核版本无法使用，我的内核版本是 <a href="https://buildlogs.centos.org/c7.2003.00.x86_64/kernel/20200331233310/3.10.0-1127.el7.x86_64/kernel-3.10.0-1127.el7.x86_64.rpm">3.10.0-1127.el7.x86_64</a> 供你参考</p>
<pre tabindex="0"><code># https://snapcraft.io/docs/installing-snap-on-red-hat
sudo yum -y install snapd
sudo systemctl enable --now snapd.socket
sudo systemctl start --now snapd.socket
sudo ln -s /var/lib/snapd/snap /snap
sudo snap install core; sudo snap refresh core
</code></pre><p><strong>第二步</strong>：使用 snap 安装 certbot（如果你在此之前有使用系统自带的包管理器安装过 certbot，那么请你先卸载它）</p>
<pre tabindex="0"><code>sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
</code></pre><h2 id="2使用-certbot">2、使用 certbot</h2>
<p>有了 certbot 就可以用来申请证书、续费证书、部署证书了。</p>
<p><strong>1、申请证书</strong></p>
<p>使用如下 certbot 命令就可以开始证书的申请。以下自动完成申请，需要替换邮箱及自己的域名：</p>
<pre tabindex="0"><code>certbot --nginx --agree-tos --redirect \
        -m email@youremail.com --eff-email \
        --preferred-challenges http-01 \
        --cert-name url.com \
        -d url.com,xyz.url.com,www.url.com  -q
</code></pre><p>前提你得提前安装 nginx （这个自然不用说），安装过了但路径不对的话，需要你建立一个软链接</p>
<pre tabindex="0"><code>ln -s /usr/local/nginx/conf/ /etc/nginx
</code></pre><p><strong>2、续期证书</strong></p>
<p>certbot 的证书有效期是 3个月（准确的说应该是 90 天），这意味着你 3个月就要来处理一下续期。</p>
<p>续期的命令就比较简单了。</p>
<pre tabindex="0"><code>certbot renew
</code></pre><h2 id="3难用的-certboot">3、难用的 certboot</h2>
<p>使用 cerbot 申请证书的时候，有许多交互界面需要你填入各种参数，允许各种协议，体验非常的不好。</p>
<p>并且最主要的是，在证书的申请和续期的过程中，有一步需要验证你对该域名的所有权，因此需要你人工登陆你域名提供商的控制台去添加类型为 txt 的 DNS 记录。</p>
<p>这导致了整个过程非常的繁琐，需要全程人工的界入。</p>
<p>而这繁琐的过程，不仅在你申请证书的时候要来一次，以后续费每次都要重复操作。</p>
<p>虽然各大国内域名提供商提供了 API 接口，可以让开发者调用去管理域名的 DNS 解析。</p>
<p>但 certbot 本身并未实现这个功能，这非常的遗憾。</p>
<h2 id="4好用的-letscertbot">4、好用的 letscertbot</h2>
<p>但庆幸的是，certbot 的流程中保留了不少的 hook，开发者可以自行实现 DNS 的验证逻辑。</p>
<p>于是乎，就出现了一些有心的开发者，花时间写一些脚本，结合 certbot 来实现更多智能的工作。</p>
<p>在早期，有一个叫做 <code>certbot-letencrypt-wildcardcertificates-alydns-au</code> 在 github 项目，是基于 certbot-auto 的脚本。</p>
<p>之前不知道 certbot-auto 已经废弃了，让我试了半天，一直提示失败。</p>
<p>结果去 issue 上看，很多 issue 都没有处理，作者也已经一年没有在 github 上活跃过了。也许该项目是已经和 certbot-auto 的废弃而告终了。</p>
<p>于是我又开始漫天寻找新的解决方案。</p>
<p>终于让我找到一个叫 <code>letscertbot</code> 的项目。</p>
<p><img src="http://image.iswbm.com/20210710170159.png"></p>
<h2 id="5使用-letscertbot">5、使用 letscertbot</h2>
<p>使用letscertbot 的方法有两种</p>
<ol>
<li>使用容器运行</li>
<li>使用脚本运行</li>
</ol>
<p>我这里使用脚本运行演示。</p>
<p>在开始使用之前，你需要拷贝模板配置文件，并进行配置</p>
<pre tabindex="0"><code>cp config.json.example config.json
</code></pre><p>主要配置的项有：</p>
<ul>
<li><code>base.email</code>：填写你的邮箱，以便接收通知</li>
<li><code>dns.aliyun</code>：填写你的阿里云(如果你用的是阿里云域名的话)的授权id和密钥</li>
</ul>
<p>阿里云的授权id怎么获取呢？</p>
<p>可前往阿里云的帮助文档：​<a href="https://help.aliyun.com/knowledge_detail/38738.html">https://help.aliyun.com/knowledge_detail/38738.html</a></p>
<p>接下来我们就可以：</p>
<p><strong>1、申请证书</strong></p>
<p>申请的证书全部在 <code>/etc/letsencrypt/live/</code> 目录下</p>
<pre tabindex="0"><code>sudo python ./bin/obtain.py -d your.example.com *.your.example.com
</code></pre><p><strong>2、续费证书</strong></p>
<pre tabindex="0"><code>sudo python ./bin/renewal.py
</code></pre><p>然后你可以将该命令部署到 cron 定时任务中，这个计划任务将每七天天执行 renewal 脚本，但是七天间隔太久，最好加上 <code>--force</code> 参数，强制续期。</p>
<pre tabindex="0"><code>0 1 * * * $your_letscertbot_home/bin/renewal.py --force &gt;&gt; /var/log/letscertbot-renewal.log 2&gt;&amp;amp;1
</code></pre><p>**如果你只想续期某个域名，可以加上  **<code>--certs</code> 参数:</p>
<pre tabindex="0"><code>sudo python ./bin/renewal.py --certs xny.example.com --force
</code></pre><p><strong>3、部署证书</strong></p>
<p>如果你将 <code>deploy.server.enable</code> 设置为 true, Certbot 将执行 deployment 脚本 (<code>deploy.py</code>) 在 deploy 钩子上。这个脚本接收到已经续期的证书并将它推送到配置好的服务器中。</p>
<p>Let&rsquo;s Certbot 通过 SSH 为远程服务器部署证书，这意味着你执行 Certbot 的机器须通过 SSH 连接上远程服务器。为了使连接成功，你需要上传公钥到远程服务器或者提供 <code>deploy.server.password</code> 给 <code>sshpass</code> 工具。</p>
<p>此外，为了将证书部署到 <code>deploy.server.deploy_to</code> 或重启 nginx, Let&rsquo;s Certbot 要求 <code>deploy.server.user</code> 有执行对应操作的权限。</p>
<p>你可以通过执行下面命令获取 deployment 脚本:</p>
<pre tabindex="0"><code>sudo python ./bin/deploy.py --check
</code></pre><p>如果需要推送证书到配置中的服务器，可以执行:</p>
<pre tabindex="0"><code>sudo python ./bin/deploy.py --push --cert $certificate_name --server $server_host
</code></pre>]]></content:encoded>
    </item>
    
    <item>
      <title>如何将http重定向到https</title>
      <link>https://iswbm.com/en/2024/06/16/how-to-redirect-http-to-https/</link>
      <pubDate>Sun, 16 Jun 2024 20:58:11 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/16/how-to-redirect-http-to-https/</guid>
      <description>1. 设置 WP 主页域名 进入网站后台，设置站点地址为 https://iswbm.com 2. 设置两个 A 记录 到你的域名提供商控制台里设置两条 DNS 解析记录 主机记录 记录类型 记录值 @ A ${host-ip} www A ${host-ip} 3.</description>
      <content:encoded><![CDATA[<h2 id="1-设置-wp-主页域名">1. 设置 WP 主页域名</h2>
<p>进入网站后台，设置站点地址为 <code>https://iswbm.com</code></p>
<p><img src="http://image.iswbm.com/20210706232903.png"></p>
<h2 id="2-设置两个-a-记录">2. 设置两个 A 记录</h2>
<p>到你的域名提供商控制台里设置两条 DNS 解析记录</p>
<table>
<thead>
<tr>
<th style="text-align:center">主机记录</th>
<th style="text-align:center">记录类型</th>
<th style="text-align:center">记录值</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">@</td>
<td style="text-align:center">A</td>
<td style="text-align:center">${host-ip}</td>
</tr>
<tr>
<td style="text-align:center">www</td>
<td style="text-align:center">A</td>
<td style="text-align:center">${host-ip}</td>
</tr>
</tbody>
</table>
<h2 id="3-修改nginx配置">3. 修改nginx配置</h2>
<p>编辑 <code>/usr/local/nginx/conf/nginx.conf</code> 修改如下两个服务器配置</p>
<ul>
<li><strong>第一个配置</strong>：将 http 重定向到 https（不管带 www 还是不带 www）</li>
<li><strong>第二个配置</strong>：接收所有的 https 请求（不管带 www 还是不带 www）</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">server</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="err">listen</span> <span class="err">80;</span>
</span></span><span class="line"><span class="cl">        <span class="err">server_name</span> <span class="err">iswbm.com</span> <span class="err">www.iswbm.com;</span>
</span></span><span class="line"><span class="cl">        <span class="err">return</span> <span class="err">301</span> <span class="err">https:</span><span class="c1">//iswbm.com$request_uri;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">server</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="err">listen</span> <span class="err">443</span> <span class="err">ssl;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="err">ssl_certificate</span> <span class="err">1_iswbm.com_bundle.pem;</span>
</span></span><span class="line"><span class="cl">        <span class="err">ssl_certificate_key</span> <span class="err">0_iswbm.com.key;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="err">server_name</span> <span class="err">_;</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">        <span class="err">#</span> <span class="err">剩下的其他配置</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span></code></pre></div><p>记得重启 nginx 使之生效。</p>
<p>当你在使用 <code>https://www.iswbm.com</code> 有可能访问不成功，因为有 301 缓存啥的，使用 F5 强制刷新就可以了。</p>
<h2 id="4-查看重定向情况">4. 查看重定向情况</h2>
<p>使用 <code>curl -I url</code> 就可以看到地址的重定向情况</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ curl -I http://iswbm.com
</span></span><span class="line"><span class="cl">HTTP/1.1 <span class="m">301</span> Moved Permanently
</span></span><span class="line"><span class="cl">Server: nginx
</span></span><span class="line"><span class="cl">Date: Tue, <span class="m">06</span> Jul <span class="m">2021</span> 15:34:44 GMT
</span></span><span class="line"><span class="cl">Content-Type: text/html
</span></span><span class="line"><span class="cl">Content-Length: <span class="m">162</span>
</span></span><span class="line"><span class="cl">Connection: keep-alive
</span></span><span class="line"><span class="cl">Location: https://iswbm.com/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ curl -I https://iswbm.com
</span></span><span class="line"><span class="cl">HTTP/1.1 <span class="m">200</span> OK
</span></span><span class="line"><span class="cl">Server: nginx
</span></span><span class="line"><span class="cl">Date: Tue, <span class="m">06</span> Jul <span class="m">2021</span> 15:34:48 GMT
</span></span><span class="line"><span class="cl">Content-Type: text/html<span class="p">;</span> <span class="nv">charset</span><span class="o">=</span>UTF-8
</span></span><span class="line"><span class="cl">Connection: keep-alive
</span></span><span class="line"><span class="cl">Vary: Accept-Encoding
</span></span><span class="line"><span class="cl">X-Powered-By: PHP/7.4.9
</span></span><span class="line"><span class="cl">Set-Cookie: <span class="nv">baiduseo_data_category</span><span class="o">=</span>%7B%22book%22%3A%22baiduseo_category%22%7D<span class="p">;</span> <span class="nv">expires</span><span class="o">=</span>Thu, 05-Aug-2021 15:34:47 GMT<span class="p">;</span> Max-Age<span class="o">=</span><span class="m">2592000</span>
</span></span><span class="line"><span class="cl">Set-Cookie: <span class="nv">baiduseo_data_paymoney</span><span class="o">=</span>%7B%22msg%22%3A1%2C%22url%22%3A%22033c2eda0b95dfc04b9c9ef1e7bb631e%22%2C%22status%22%3A1%7D<span class="p">;</span> <span class="nv">expires</span><span class="o">=</span>Wed, 07-Jul-2021 15:34:47 GMT<span class="p">;</span> Max-Age<span class="o">=</span><span class="m">86400</span>
</span></span><span class="line"><span class="cl">Set-Cookie: wp-editormd-lang<span class="o">=</span>zh-CN<span class="p">;</span> <span class="nv">path</span><span class="o">=</span>/
</span></span><span class="line"><span class="cl">Set-Cookie: <span class="nv">PHPSESSID</span><span class="o">=</span>fden169bmmjbopn4rnb89c95rp<span class="p">;</span> <span class="nv">path</span><span class="o">=</span>/
</span></span><span class="line"><span class="cl">Expires: Thu, <span class="m">19</span> Nov <span class="m">1981</span> 08:52:00 GMT
</span></span><span class="line"><span class="cl">Cache-Control: no-store, no-cache, must-revalidate
</span></span><span class="line"><span class="cl">Pragma: no-cache
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ curl -I http://www.iswbm.com
</span></span><span class="line"><span class="cl">HTTP/1.1 <span class="m">301</span> Moved Permanently
</span></span><span class="line"><span class="cl">Server: nginx
</span></span><span class="line"><span class="cl">Date: Tue, <span class="m">06</span> Jul <span class="m">2021</span> 15:35:03 GMT
</span></span><span class="line"><span class="cl">Content-Type: text/html
</span></span><span class="line"><span class="cl">Content-Length: <span class="m">162</span>
</span></span><span class="line"><span class="cl">Connection: keep-alive
</span></span><span class="line"><span class="cl">Location: https://iswbm.com/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ curl -I https://www.iswbm.com
</span></span><span class="line"><span class="cl">HTTP/1.1 <span class="m">301</span> Moved Permanently
</span></span><span class="line"><span class="cl">Server: nginx
</span></span><span class="line"><span class="cl">Date: Tue, <span class="m">06</span> Jul <span class="m">2021</span> 15:35:08 GMT
</span></span><span class="line"><span class="cl">Content-Type: text/html<span class="p">;</span> <span class="nv">charset</span><span class="o">=</span>UTF-8
</span></span><span class="line"><span class="cl">Connection: keep-alive
</span></span><span class="line"><span class="cl">X-Powered-By: PHP/7.4.9
</span></span><span class="line"><span class="cl">Set-Cookie: <span class="nv">baiduseo_data_category</span><span class="o">=</span>%7B%22book%22%3A%22baiduseo_category%22%7D<span class="p">;</span> <span class="nv">expires</span><span class="o">=</span>Thu, 05-Aug-2021 15:35:07 GMT<span class="p">;</span> Max-Age<span class="o">=</span><span class="m">2592000</span>
</span></span><span class="line"><span class="cl">Set-Cookie: <span class="nv">baiduseo_data_paymoney</span><span class="o">=</span>%7B%22msg%22%3A1%2C%22url%22%3A%22033c2eda0b95dfc04b9c9ef1e7bb631e%22%2C%22status%22%3A1%7D<span class="p">;</span> <span class="nv">expires</span><span class="o">=</span>Wed, 07-Jul-2021 15:35:07 GMT<span class="p">;</span> Max-Age<span class="o">=</span><span class="m">86400</span>
</span></span><span class="line"><span class="cl">Set-Cookie: wp-editormd-lang<span class="o">=</span>zh-CN<span class="p">;</span> <span class="nv">path</span><span class="o">=</span>/
</span></span><span class="line"><span class="cl">Set-Cookie: <span class="nv">PHPSESSID</span><span class="o">=</span>0smke620aseeucca5dlbh31hhs<span class="p">;</span> <span class="nv">path</span><span class="o">=</span>/
</span></span><span class="line"><span class="cl">Expires: Thu, <span class="m">19</span> Nov <span class="m">1981</span> 08:52:00 GMT
</span></span><span class="line"><span class="cl">Cache-Control: no-store, no-cache, must-revalidate
</span></span><span class="line"><span class="cl">Pragma: no-cache
</span></span><span class="line"><span class="cl">X-Redirect-By: WordPress
</span></span><span class="line"><span class="cl">Location: https://iswbm.com/
</span></span></code></pre></div><h2 id="5-一个小插曲">5. 一个小插曲</h2>
<p>在最开始的时候，网站后台的站点地址不小心设置错了，导致网站 都访问不了，想把地址修改回来都没办法。</p>
<p>真的很慌，害怕网站 就这么挂了。</p>
<p>不过冷静下来后，我就找到了解决方法。</p>
<p>方法分两步：</p>
<p><strong>第一步</strong>：进入 WP 安装目录，替换目录下所有文件的域名信息</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">cd</span> /home/wwwroot/wordpress/
</span></span><span class="line"><span class="cl">sed -i <span class="s2">&#34;s/www.iswbm.com/iswbm.com/g&#34;</span>  <span class="sb">`</span>grep -r www.iswbm.com ./ -l<span class="sb">`</span>
</span></span></code></pre></div><p><strong>第二步</strong>：进入数据库，修改数据站点地址</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">mysql&gt; <span class="k">select</span> * from wp_options limit 5<span class="p">;</span>
</span></span><span class="line"><span class="cl">+-----------+--------------------+--------------------------------------------------------------+----------+
</span></span><span class="line"><span class="cl"><span class="p">|</span> option_id <span class="p">|</span> option_name        <span class="p">|</span> option_value                                                 <span class="p">|</span> autoload <span class="p">|</span>
</span></span><span class="line"><span class="cl">+-----------+--------------------+--------------------------------------------------------------+----------+
</span></span><span class="line"><span class="cl"><span class="p">|</span>         <span class="m">1</span> <span class="p">|</span> siteurl            <span class="p">|</span> https://www.iswbm.com                                        <span class="p">|</span> yes      <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>         <span class="m">2</span> <span class="p">|</span> home               <span class="p">|</span> https://www.iswbm.com                                        <span class="p">|</span> yes      <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>         <span class="m">3</span> <span class="p">|</span> blogname           <span class="p">|</span> 明哥教程                                                     <span class="p">|</span> yes      <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>         <span class="m">4</span> <span class="p">|</span> blogdescription    <span class="p">|</span> 专注于 Python Golang Linux 云计算相关的技术分享              <span class="p">|</span> yes      <span class="p">|</span>
</span></span><span class="line"><span class="cl"><span class="p">|</span>         <span class="m">5</span> <span class="p">|</span> users_can_register <span class="p">|</span> <span class="m">1</span>                                                            <span class="p">|</span> yes      <span class="p">|</span>
</span></span><span class="line"><span class="cl">+-----------+--------------------+--------------------------------------------------------------+----------+
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">mysql&gt; update wp_options <span class="nb">set</span> <span class="nv">option_value</span><span class="o">=</span><span class="s2">&#34;https://iswbm.com&#34;</span> where option_id in <span class="o">(</span>1,2<span class="o">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">Query OK, <span class="m">2</span> rows affected <span class="o">(</span>0.00 sec<span class="o">)</span>
</span></span><span class="line"><span class="cl">Rows matched: <span class="m">2</span>  Changed: <span class="m">2</span>  Warnings: <span class="m">0</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>如何为你的网站部署https</title>
      <link>https://iswbm.com/en/2024/06/16/how-to-deploy-https-for-your-web-site/</link>
      <pubDate>Sun, 16 Jun 2024 20:55:18 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/16/how-to-deploy-https-for-your-web-site/</guid>
      <description>要想使用 HTTPS ，要做的事情还是有点多的： 准备一个域名和服务器（自行准备） 并将该域名解析到你的服务器ip上 生成 CSR 证书请求文件 拿着 CSR 文件去申请证书 把</description>
      <content:encoded><![CDATA[<p>要想使用 HTTPS ，要做的事情还是有点多的：</p>
<ol>
<li>准备一个域名和服务器（自行准备）</li>
<li>并将该域名解析到你的服务器ip上</li>
<li>生成 CSR 证书请求文件</li>
<li>拿着 CSR 文件去申请证书</li>
<li>把申请到的证书部署到你的服务器上</li>
</ol>
<h2 id="1-设置-dns-解析">1. 设置 DNS 解析</h2>
<p>我有一个域名 <code>iswbm.com</code>，其下有好多的子域名，比如写 Python 的在线博客 <code>python.iswbm.com</code>，还有写 Golang 的在线博客 <code>golang.iswbm.com</code>，还有我的图床 <code>image.iswbm.com</code> 等等。</p>
<p>这里新建了个子域名 <code>demo.iswbm.com</code> ，并设置其解析到 我的服务器上，如下图（服务器 ip 已码掉）。</p>
<p><img src="http://image.iswbm.com/20200728233602.png"></p>
<h2 id="2-搭建-http-站点">2. 搭建 HTTP 站点</h2>
<p>为了方便，我这里就使用 Apache 放了一个 HTTP 的静态网页，方法很简单，大家百度即可。</p>
<p><img src="http://image.iswbm.com/20200729230813.png"></p>
<h2 id="3-申请-ssl-证书">3. 申请 SSL 证书</h2>
<p>SSL 数字证书怎么来的呢？</p>
<p>你可以自己制作，也可以向CA权威机构申请。</p>
<p>二者的区别是：</p>
<ol>
<li>自己颁发的证书，需要客户端验证通过，也就是需要用户手动安装证书，并将其设置为受信任的根证书。但即使如此，浏览器上（ chrome, firefox）仍不认可这种自签名证书，会在地址栏前面提示连接不安全，手动安装证书后，也会提示该证书无效。若想要继续访问，并忽略该提示，可以选择继续访问。</li>
<li>向权威的数字证书认证机构申请，由于这些机构在网民的电脑里都有相应的根证书，且这些机构是绝对可信任的。因此你在访问网站时，不会提示连接不安全。</li>
</ol>
<p>下面，我将分别向你展示这两种方法，都是如何操作的。</p>
<h3 id="第一种向权威ca机构申请">第一种：向权威CA机构申请</h3>
<p>在阿里云和腾讯云都可以 进行 SSL 证书的申请，证书的申请有付费的（价格也不便宜），也有免费的，看了一圈，只有腾讯云有免费的 SSL 证书的申请渠道（阿里云听说以前也有，后来关闭了）。</p>
<p>本篇文章，仅以演示教学之用，所以只用腾讯云的免费证书的就足够啦。</p>
<p>登陆腾讯云，可以看到SSL 证书有分很多种，企业型的，企业型专业版的，增强型，增强型专业版的，还有域名型免费版。</p>
<p><img src="http://image.iswbm.com/image-20200718102622663.png"></p>
<p>点击选择 <code>域名型免费版</code></p>
<p><img src="http://image.iswbm.com/image-20200718101358755.png"></p>
<p>点击 <code>免费快速申请</code>后，填写域名和你的个人邮箱</p>
<p><img src="http://image.iswbm.com/20200729232432.png"></p>
<p>再点击下一步，会需要你验证域名所有权，验证方式有如下三种</p>
<ol>
<li>自动DNS验证</li>
<li>手动DNS验证</li>
<li>文件验证</li>
</ol>
<p>但由于我的域名不是腾讯云平台解析的，因此没有 自动DNS验证的选项，只有其他两种</p>
<p><img src="http://image.iswbm.com/image-20200718101652899.png"></p>
<p>点击 <code>确认申请</code> 后，会提示你进入域名验证所有权的流程，这里我选择 手动DNS验证。</p>
<p><img src="http://image.iswbm.com/20200729004207.png"></p>
<p>审核通过后，3s 内就会给你颁发证书，你可以从控制台点击证书下载。</p>
<p><img src="http://image.iswbm.com/20200729004307.png"></p>
<p>下载下来的会是一个 zip 包。</p>
<p>解压一下，会有不同的服务器类型（有 Apache、IIS、Nginx、Tomcat）的文件夹。</p>
<p><img src="http://image.iswbm.com/20200729004456.png"></p>
<p>我使用的是 Apache ，在这个文件夹下面有三个文件：</p>
<ol>
<li><code>1_root_bundle.crt</code>：根证书</li>
<li><code>2_demo.iswbm.com.crt</code>：域名证书</li>
<li><code>3_demo.iswbm.com.key</code>：私钥文件</li>
</ol>
<p>这三个文件，下一步会部署于我的服务器上。</p>
<p>接下来讲第二种 SSL 证书申请方式。</p>
<h3 id="第二种自签名的-ssl-证书">第二种：自签名的 SSL 证书</h3>
<p>没有权威的第三方 CA 机构给自己颁发证书，那就自己给自己颁发咯。</p>
<p>自签名 SSL 的证书制作过程，可以分为两步：</p>
<ol>
<li>自己要当 CA 机构，那 CA 有的 CA 根证书、私钥 一样都不能少，因此第一步：生成 CA 的 crt 证书 和 CA 的私钥。</li>
<li>要申请证书，首先服务器自己要有一个密钥对（公钥和私钥）</li>
<li>拿着上面生成的公钥，制作一个 CSR 证书申请文件</li>
<li>用第一步的 CA 私钥，给这个 CSR 签名，生成咱所需要的 SSL 数字证书文件。</li>
</ol>
<p>步骤很多，命令很多，命令所带的参数更多，对于只想学习证书签发流程的你，把这些命令都背下来，并不是一个好的选择，毕竟这种事可能也干不了几次。</p>
<p>因此，我把这些步骤、命令，都整合成一个 shell 脚本，你只要执行这个脚本就行了。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ bash create_self-signed-cert.sh --ssl-domain<span class="o">=</span>demo.iswbm.com --ssl-trusted-domain<span class="o">=</span>demo.iswbm.com --ssl-size<span class="o">=</span><span class="m">2048</span> --ssl-date<span class="o">=</span><span class="m">3650</span>
</span></span></code></pre></div><p>对应的参数的解释，在脚本中都有解释</p>
<p><img src="http://image.iswbm.com/20200729235153.png"></p>
<p>这个脚本过长，不好直接贴上来，我将它放在我的公众号（<strong>Python编程时光</strong>）后台，你可以直接回复『<strong>证书签名</strong>』直接获取下载。</p>
<p>执行完成后，会在当前目录下生成好多个文件。</p>
<p>其中，只有两个文件对我们有用</p>
<p><img src="http://image.iswbm.com/20200730000142.png"></p>
<h2 id="4-部署-ssl-证书">4. 部署 SSL 证书</h2>
<p>根据服务器的类型不同，部署安装的方式有有所区别，腾讯云的操作文档已经非常详细了，你可以通过这个链接访问到如下的文档：https://cloud.tencent.com/document/product/400/4143</p>
<p><img src="http://image.iswbm.com/20200718105347.png"></p>
<p>这里我将以 CentOS 7.2  + Apache 为例，演示如何部署 SSL 证书。</p>
<p>先安装一下 mod_ssl</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ yum install -y mod_ssl
</span></span></code></pre></div><p>安装完后，在 /etc/httpd/conf.d/ 目录下 会有个 ssl.conf 文件。</p>
<p>编辑修改这个文件，以下是我的配置供你参考</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">&lt;VirtualHost 0.0.0.0:443&gt;
</span></span><span class="line"><span class="cl">     DocumentRoot <span class="s2">&#34;/var/www/html&#34;</span> 
</span></span><span class="line"><span class="cl">     <span class="c1">#填写证书名称</span>
</span></span><span class="line"><span class="cl">     ServerName demo.iswbm.com
</span></span><span class="line"><span class="cl">     <span class="c1">#启用 SSL 功能</span>
</span></span><span class="line"><span class="cl">     SSLEngine on 
</span></span><span class="line"><span class="cl">     <span class="c1">#证书文件的路径</span>
</span></span><span class="line"><span class="cl">     SSLCertificateFile /etc/pki/tls/certs/demo.iswbm.com.crt
</span></span><span class="line"><span class="cl">     <span class="c1">#私钥文件的路径</span>
</span></span><span class="line"><span class="cl">     SSLCertificateKeyFile /etc/pki/tls/private/demo.iswbm.com.key
</span></span><span class="line"><span class="cl">     <span class="c1">#根证书文件的路径</span>
</span></span><span class="line"><span class="cl">     SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt
</span></span><span class="line"><span class="cl">&lt;/VirtualHost&gt;
</span></span></code></pre></div><p><strong>如果你的证书是从权威 CA 机构上申请来的。</strong></p>
<p>比如我上面从腾讯云上申请来的，那么这三个文件就是从已经从腾讯云的控制台上下载下来的那三个文件。</p>
<p>在修改完后，务必记得把下载的这三个文件，放到相应的目录中去。</p>
<p><img src="http://image.iswbm.com/20200730214826.png"></p>
<p>配置完 ssl.conf，可能还需要你 check 一下 <code>/etc/httpd/conf/httpd.conf</code> 的一些配置，这些配置一般用默认的就可以，但是以防万一，还是写一下吧</p>
<pre tabindex="0"><code>Include conf.modules.d/*.conf
</code></pre><p>写这一行的目的，就是为了 httpd 去加载 mod_ssl 这个模块</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ cat /etc/httpd/conf.modules.d/00-ssl.conf 
</span></span><span class="line"><span class="cl">LoadModule ssl_module modules/mod_ssl.so
</span></span></code></pre></div><p>一切配置完成后，记得重启一下 httpd 服务</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ systemctl restart httpd
</span></span></code></pre></div><p>然后使用 chrome 访问一下 <code>https//demo.iswbm.com</code> 看看，大功告成。</p>
<p><img src="http://image.iswbm.com/20200730215613.png"></p>
<p><strong>而如果你的证书是自签名的。</strong></p>
<p>ssl.conf 配置文件下的应该改成这样</p>
<pre tabindex="0"><code>&lt;VirtualHost 0.0.0.0:443&gt;
     DocumentRoot &#34;/var/www/html&#34; 
     #填写证书名称
     ServerName demo.iswbm.com
     #启用 SSL 功能
     SSLEngine on 
     #证书文件的路径
     SSLCertificateFile /etc/pki/tls/certs/tls.crt
     #私钥文件的路径
     SSLCertificateKeyFile /etc/pki/tls/private/tls.key
&lt;/VirtualHost&gt;
</code></pre><p>同时记得把这两个文件也拷贝到相应的目录下</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ cp tls.crt /etc/pki/tls/certs/
</span></span><span class="line"><span class="cl">$ cp tls.key /etc/pki/tls/private/
</span></span></code></pre></div><p>最后还是不要忘了重启 httpd</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ systemctl restart httpd
</span></span></code></pre></div><p>试着用 chrome 访问一下，可以看到 chrome 提示该连接不安全</p>
<p><img src="http://image.iswbm.com/20200730220835.png"></p>
<p>如果执意要访问，可以点击左下方的 <code>继续前往</code>，这样以后再访问的时候，就不会再出现这个警告页面了。</p>
<p><img src="http://image.iswbm.com/20200730221745.png"></p>
<p><code>不安全</code> 三个字，让人很没有安全感，那有没有办法去掉呢？</p>
<p>答案是，没有，只要是自签名的证书，在 chrome ，firefox 等主流浏览器看来都是不安全的。</p>
<p>即使你把这个根证书添加到你的受信任的证书列表中，也是徒然。</p>
<p>下面就试着来安装一下这个根证书。</p>
<p>按照下图指示，拖动证书到本地磁盘上。</p>
<p><img src="http://image.iswbm.com/20200728234740.png"></p>
<p>打开 Mac 上的 <code>钥匙串访问</code></p>
<p><img src="http://image.iswbm.com/20200730222441.png"></p>
<p>点击 <code>登陆</code>，然后再拖动这个证书到窗口中进行安装</p>
<p><img src="http://image.iswbm.com/20200728235331.png"></p>
<p>右键该证书，点击 <code>显示简介</code>，跳出下面的界面后，再点击 <code>信任</code>，把 IP 安全选择选为 <code>始终信任</code>。</p>
<p><img src="http://image.iswbm.com/20200730222700.png"></p>
<p>设置完后，再访问下 <code>demo.iswbm.com</code> ，仍然显示连接不安全，并且证书是无效的</p>
<p><img src="http://image.iswbm.com/20200730222827.png"></p>
<p>点击证书，显示证书，该证书确实已经放入信任列表中了。</p>
<p><img src="http://image.iswbm.com/20200730222928.png"></p>
<h2 id="5-宝塔申请证书">5. 宝塔申请证书</h2>
<p>注册并登陆宝塔（https://bt.cn），然后进入控制面板。</p>
<p><img src="http://image.iswbm.com/image-20200929123223096.png"></p>
<p>点击申请证书</p>
<p><img src="http://image.iswbm.com/image-20200929123355037.png"></p>
<p>选择免费的就好</p>
<p><img src="http://image.iswbm.com/image-20200929123418789.png"></p>
<p>填写你的域名后，支付订单（其实不要钱）。</p>
<p><img src="http://image.iswbm.com/image-20200929123459545.png"></p>
<p>然后登陆我的阿里去域名解析，根据如下提示去添加 DNS解析规则</p>
<p><img src="http://image.iswbm.com/image-20200929123956070.png"></p>
<p>然后静待一段时间验证成功了，就可以点击如下按钮进行下载  <img src="http://image.iswbm.com/image-20200929194100905.png"></p>
<p>下载到本地后，你会得到一个 zip 包，解压一下，就可以看到证书文件及私钥。</p>
<p><img src="http://image.iswbm.com/image-20200929201114297.png"></p>
<p>因为我的博客使用的是 Nginx，因此我该 Nginx 下的两个文件上传到我的服务器上的 nginx 目录下.</p>
<p>具体怎么上传呢？你可以使用远程拷贝软件，例如 WinSCP，也可以使用 <code>lrzsz</code> （推荐使用）。</p>
<p>传到哪个目录下呢？</p>
<p>先使用 find 命令查找一下你的 nginx.conf 路径</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ find / -name nginx.conf
</span></span><span class="line"><span class="cl">/usr/local/nginx/conf/nginx.conf
</span></span></code></pre></div><p>你的证书文件可以和 nginx.conf 放在同一目录下</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">/usr/local/nginx/conf
</span></span></code></pre></div><p>接下来使用 vim 编辑该文件，找到 server，添加如下行（ server 原本的内容 我使用 <code>...</code> 表示，意思是不需要去动。 ）</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">server
</span></span><span class="line"><span class="cl">    <span class="o">{</span>
</span></span><span class="line"><span class="cl">        listen <span class="m">443</span> ssl<span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># 注释掉该行</span>
</span></span><span class="line"><span class="cl">				<span class="c1"># listen 80 default_server reuseport;</span>
</span></span><span class="line"><span class="cl">			
</span></span><span class="line"><span class="cl">        <span class="c1">#证书文件名称</span>
</span></span><span class="line"><span class="cl">        ssl_certificate 1_iswbm.com_bundle.pem<span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="c1">#私钥文件名称</span>
</span></span><span class="line"><span class="cl">        ssl_certificate_key 0_iswbm.com.key<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">				...
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span></code></pre></div><p>尝试着登陆一下</p>
<h2 id="6-七牛去图床开启https">6. 七牛去图床开启https</h2>
<p>登陆七牛云</p>
<p>然后进入 <a href="https://portal.qiniu.com/certificate/ssl%20">证书管理</a> 上传证书。</p>
<p><img src="http://image.iswbm.com/image-20200929210645477.png"></p>
<p>进入对象存储 -&gt; 域名管理，找到 HTTPS 配置的位置，点击 <code>修改配置</code>。</p>
<p><img src="http://image.iswbm.com/image-20200929205400898.png"></p>
<p>将按钮置为开启状态，选择我们刚刚上传的证书。</p>
<p><img src="http://image.iswbm.com/image-20200929205611442.png"></p>
<p>设置完后，并不能立马使用</p>
<p><img src="http://image.iswbm.com/image-20200929210431735.png"></p>
<p>域名的升级需要一定时间，等待即可。</p>
<p><img src="http://image.iswbm.com/image-20200929210900562.png"></p>
<h2 id="参考文档">参考文档</h2>
<ul>
<li><a href="https://cloud.tencent.com/document/product/400/35243">Apache 服务器证书安装</a></li>
<li><a href="https://docs.rancher.cn/rancher2x/install-prepare/self-signed-ssl.html#_2-3-%E6%89%A9%E5%B1%95%E5%90%8D">自签名 SSL 证书</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>如何使用WordPress搭建个人博客</title>
      <link>https://iswbm.com/en/2024/06/16/how-to-deploy-blog-with-wordpress/</link>
      <pubDate>Sun, 16 Jun 2024 20:53:21 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/16/how-to-deploy-blog-with-wordpress/</guid>
      <description>你现在所见到的这个网站，是我用 WordPress 搭建的，整个过程非常的顺利，体验非常地好，于是我就整个过程、以及其中的一些搭建心得，记录下来。 如果你也正好有</description>
      <content:encoded><![CDATA[<p>你现在所见到的这个网站，是我用 WordPress 搭建的，整个过程非常的顺利，体验非常地好，于是我就整个过程、以及其中的一些搭建心得，记录下来。</p>
<p><img src="http://image.iswbm.com/20201015003006.png"></p>
<p>如果你也正好有搭建个人网站的想法，那么本文会给你一个参考，也许看了这篇文章你就可以不用再百度、甚至谷歌了，因为我会写得 <strong>足够的细致而系统</strong>。</p>
<h2 id="1-写在前面">1. 写在前面</h2>
<p>注意：文章为早期写的，iswbm.com 的站点已从 wordpress 转到当前的 hugo，下图为 wordpress 的效果</p>
<p><img src="http://image.iswbm.com/image-20201012232521149.png"></p>
<h2 id="2-准备工作">2. 准备工作</h2>
<h3 id="一台-linux-服务器">一台 Linux 服务器</h3>
<p>在开始搭建网站之前，当然第一步是购买一个属于自己的 『服务器』。</p>
<p>你可以选择如下渠道进行购买：</p>
<ul>
<li>阿里云</li>
<li>腾讯云</li>
<li>Ucloud</li>
</ul>
<p>等的大厂服务器，不要贪图便宜去买国外的服务器厂商，因为速度很慢很多，很影响体验。</p>
<p>我的就是在 <a href="https://www.aliyun.com/minisite/goods?source=5176.11533457&userCode=x1rep02u">阿里云</a> 上购买的，刚开始买个最低配置的服务器就行，以后不够用了，可以慢慢升级，扩容。</p>
<p>可以先领个券，再购买更实惠：<a href="https://www.aliyun.com/minisite/goods?source=5176.11533457&userCode=x1rep02u">点击领券</a></p>
<p><strong>今年的双11，我被阿里云邀请成为推广大使，他们给了我 100 台的免费服务器，需要的可以联系我（微信： hello-wbm），闲着也是闲着，我可以免费送给大家。</strong></p>
<h3 id="一个专属的域名">一个专属的域名</h3>
<p>买了服务器后，你就会拥有一个公网ip，如果网站搭建起来了，你完全可以使用这个 ip 去访问，但仅供开发、测试使用。</p>
<p>如果要真正运营起来，想要有流量，还得搞一个域名，方便你推广。</p>
<p>域名的购买建议和上面服务器使用同一个厂商，可以省去一些麻烦。比如阿里云购买的域名要备案是需要你在阿里云下有一台服务器的。</p>
<h3 id="一个远程登陆软件">一个远程登陆软件</h3>
<p>由于后面我使用的是手工部署的方式，所以要登陆服务器进行操作。</p>
<p>登陆的方法有两种：</p>
<ol>
<li>
<p>厂商提供的控制台界面登陆：Workbench 和 VNC</p>
<p><img src="http://image.iswbm.com/image-20201007102912673.png"></p>
<p>这种方法对于不经常登陆服务器运维的人来说，还是挺香的。</p>
<p><img src="http://image.iswbm.com/image-20201007103330062.png"></p>
</li>
<li>
<p>自己下载专业的远程登陆软件：Xshell 或者 CRTSecure</p>
<p><img src="http://image.iswbm.com/20201015000746.png"></p>
</li>
</ol>
<p>在使用这些远程登陆软件时，你需要在服务器厂商控制台上面先获取到三个信息</p>
<ul>
<li>服务器公网IP</li>
<li>服务器SSH端口</li>
<li>服务器远程连接密码</li>
</ul>
<p>关于 Xshell 如何使用的，可以自行百度搜索，教程非常多。</p>
<p>可以到阿里云上去购买，选最低配置就足够啦，后续访问量起来了再扩容升级。</p>
<h2 id="2-部署方式选择">2. 部署方式选择</h2>
<p>部署方式，可分为两种</p>
<ol>
<li>使用服务器管理软件，实现自动化部署，最著名的就是  <a href="https://www.bt.cn/">宝塔面板</a> 。</li>
<li>手动登陆远程服务器，实现脚本化部署。</li>
</ol>
<p><strong>那么如何选择呢？</strong></p>
<p>使用宝塔部署，门槛低，只要会界面点一点即可。</p>
<p>而使用脚本自己手工部署，需要你学会</p>
<ul>
<li>远程登陆服务器：使用 Xshell 或者 直接使用厂商提供的在线SSH窗口</li>
<li>一些 Linux 的基本操作：比如 Vi/Vim 的使用，目录及文件的基本操作等</li>
</ul>
<p>在这里建议大家跟着我使用第二种方法，也就是手工使用脚本进行部署。</p>
<p>原因有二：</p>
<ol>
<li>第一次接触，更精细的部署步骤会让你对 WordPress 的运作方式有更深的理解，比如使用了哪些软件，装了哪些包？</li>
<li>自己搭建了网站，难免以后会碰到各种各样的服务器问题，尽早的接触 Linux，熟悉各项配置，对以后的运维工作会有很有帮助。</li>
</ol>
<h2 id="3-部署-lnmp">3. 部署 LNMP</h2>
<h3 id="31-什么是-lnmp">3.1 什么是 LNMP</h3>
<p>LNMP 是 Linux + Nginx + MySQL + PHP 组合的简写。</p>
<p>类似的组合还有：</p>
<ul>
<li>LAMP 的全称是 Linux + Apache + MySQL + PHP</li>
<li>LNAMP 的全称是 Linux + Nginx + Apache + MySQL + PHP</li>
</ul>
<p>其中：</p>
<ul>
<li>Linux 是类 Unix 计算机操作系统的统称，是目前最流行的免费操作系统。代表版本有：debian、centos、ubuntu、fedora、gentoo 等。</li>
<li>Nginx 是一个高性能的 HTTP 和反向代理服务器，也是一个 IMAP/POP3/SMTP 代理服务器。</li>
<li>Apache 是世界使用排名第一的Web服务器软件。 它可以运行在几乎所有广泛使用的计算机平台上，由于其跨平台和安全性被广泛使用，是最流行的Web服务器端软件之一。</li>
<li>PHP 是一种在服务器端执行的嵌入 HTML 文档的脚本语言。</li>
<li>MySQL 是一个关系型数据库管理系统。</li>
</ul>
<p>这些软件一个一个安装比较费力，特别是数据库。</p>
<p>因此有人把这些软件的安装部署过程集成为一个 Shell 脚本，而你只要下载并执行它就可以了。简直不要太方便。</p>
<h3 id="32-安装-lnmp">3.2 安装 LNMP</h3>
<p>下载 LNMP 安装脚本（目前最新版本是 1.7 ，我安装的是 1.5，更多版本可查看: <a href="https://lnmp.org/download.html">https://lnmp.org/download.html</a>）</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ wget http://soft.vpser.net/lnmp/lnmp1.5.tar.gz -cO lnmp1.5.tar.gz
</span></span></code></pre></div><p>解压并执行它</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ tar zxf lnmp1.5.tar.gz <span class="p">&amp;</span>amp<span class="p">;&amp;</span>amp<span class="p">;</span> <span class="nb">cd</span> lnmp1.5 <span class="p">&amp;</span>amp<span class="p">;&amp;</span>amp<span class="p">;</span> ./install.sh lnmp
</span></span></code></pre></div><p>接下来会出现大量的选项，如果你不是很懂各个选项间的区别，按默认就行啦</p>
<p>运行脚本后，首先会让你选择数据库的版本：</p>
<p><img src="http://image.iswbm.com/20201007104606.png"></p>
<p>没有特殊需要，建议使用默认配置，直接回车或输入序号再回车。</p>
<p>选好数据库，会让你设置数据库 root 用户的密码，此时如果你直接回车，会默认设置为 <code>lnmp.org#随机数字</code>，</p>
<p><img src="http://image.iswbm.com/20201007104643.png"></p>
<p>在输入密码的时候，对于新手有一点注意：如果输入有错误需要删除，需要按住Ctrl再按Backspace键进行删除。</p>
<p>密码输好后，回车进入下一步</p>
<p><img src="http://image.iswbm.com/20201007104714.png"></p>
<p>询问是否需要启用 MySQL InnoDB，InnoDB引擎默认为开启，一般建议开启，直接回车或输入 y 。</p>
<p>如果确定确实不需要该引擎可以输入 n，(MySQL 5.7+版本无法关闭InnoDB),输入完成，回车进入下一步：选择 PHP 版本，建议安装 PHP 7+的版本</p>
<p><img src="http://image.iswbm.com/20201007104727.png"></p>
<p>回车进入下一步，选择是否安装内存优化：</p>
<p><img src="http://image.iswbm.com/20201007105120.png"></p>
<p>可以选择不安装、Jemalloc或TCmalloc，输入对应序号回车，直接回车为默认为不安装。</p>
<p>如果是LNMPA或LAMP的话还会提示设置邮箱和选择Apache：</p>
<p><img src="http://image.iswbm.com/20201007105137.png"></p>
<p>“Please enter Administrator Email Address:”，需要设置管理员邮箱，该邮箱会在报错时显示在错误页面上。再选择Apache版本：</p>
<p><img src="http://image.iswbm.com/20201007105150.png"></p>
<p>按提示输入对应版本前面的数字序号，回车。</p>
<p>提示&quot;Press any key to install&hellip;or Press Ctrl+c to cancel&quot;后，按回车键确认开始安装。 LNMP脚本就会自动安装编译Nginx、MySQL、PHP、phpMyAdmin等软件及相关的组件。</p>
<p>安装时间可能会几十分钟到几个小时不等，主要是机器的配置网速等原因会造成影响。</p>
<p>如果显示Nginx: OK，MySQL: OK，PHP: OK</p>
<p><img src="http://image.iswbm.com/20201007105021.png"></p>
<p>表明安装成功。</p>
<p>最后几行的输出</p>
<ul>
<li>3306 端口是 MySQL 监听的</li>
<li>80 是 HTTP 端口</li>
<li>22 是 SSH 端口</li>
</ul>
<h3 id="33-配置-nginx">3.3 配置 Nginx</h3>
<p>安装好后，使用如下指令查看nginx配置文件（如果你的服务器上找不到该文件，那请使用 <code>find / -name nginx.conf</code> 搜索一下）</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ cat /usr/local/nginx/conf/nginx.conf
</span></span></code></pre></div><p>你会看到如下内容：</p>
<p><img src="http://image.iswbm.com/20201007105202.png"></p>
<p>上图表明，安装好的nginx将网站的根目录设置为/home/wwwroot/default，这个可以根据自己的喜好进行修改。</p>
<p>用浏览器打开<code>http://ip</code>，可以看到如下内容：</p>
<p><img src="http://image.iswbm.com/20201007104831.png"></p>
<p>其实这就是一个简单的网页demo了，自己随便修改index.html，就会有不同的内容。</p>
<h2 id="4-安装-wordpress">4. 安装 WordPress</h2>
<p><a href="https://iswbm.com/">WordPress</a>是使用PHP语言（这也是我们上面为什么要安装 PHP 的原因）开发的博客平台，也就是一个博客框架。</p>
<p>上一步安装的 LNMP，只是保证了 WordPress 的基本运行环境。</p>
<p>想要把你的个人网站跑起来，咱还需要安装 WordPress 。</p>
<p>方法也很简单，下面跟着操作就行。</p>
<p>第一步：安装 wordpress 安装包并解压到 <code>/home/wwwroot</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ wget https://cn.wordpress.org/latest-zh_CN.zip <span class="p">&amp;</span>amp<span class="p">;&amp;</span>amp<span class="p">;</span> unzip latest-zh_CN.zip -d /home/wwwroot
</span></span></code></pre></div><p>第二步：登录MySQL（密码在前面 部署LNMP 时你设置过），创建wordpress表，创建完后输入exit退出。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ mysql -u root -p
</span></span><span class="line"><span class="cl">Enter password: 
</span></span><span class="line"><span class="cl">MySQL &gt; create database wordpress<span class="p">;</span>
</span></span></code></pre></div><p>第三步：使用 vim 修改 nginx 配置文件（不会vim的自行百度）</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ vim /usr/local/nginx/conf/nginx.conf
</span></span></code></pre></div><p>找到网站根目录位置，如下图所示：</p>
<p><img src="http://image.iswbm.com/20201007110554.png"></p>
<p>修改成如下内容：</p>
<p><img src="http://image.iswbm.com/20201007110611.png"></p>
<p>随后，使用wq保存配置退出即可。</p>
<p>验证nginx是否有配置错误：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ nginx -t
</span></span></code></pre></div><p>如果出现ok，successful字样，说明没有错误。没有错误，重新加载nginx：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ nginx -s reload
</span></span></code></pre></div><p>修改wordpress目录权限：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ <span class="nb">cd</span> /home/wwwroot <span class="p">&amp;</span>amp<span class="p">;&amp;</span>amp<span class="p">;</span> chown -R www wordpress/ <span class="p">&amp;</span>amp<span class="p">;&amp;</span>amp<span class="p">;</span> chgrp -R www wordpress/
</span></span></code></pre></div><p>用浏览器打开http://ip/wp-admin/setup-config.php，你可以看到如下内容：</p>
<p><img src="http://image.iswbm.com/20201007110628.png"></p>
<p>现在就开始，进行安装。填写信息如下：</p>
<p><img src="http://image.iswbm.com/20201007110649.png"></p>
<p>其中，*** 改为你设置的MySQL数据库密码。点击提交，出现如下内容：</p>
<p><img src="http://image.iswbm.com/20201007110709.png"></p>
<p>在/home/wwwroot/wordpress下，创建并编写wp-config.php文件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ vim wp-config.php
</span></span></code></pre></div><p>将上图内容复制进去并wq保存退出，然后点击现在安装。最后就是配置用户信息了，这个是你登录wordpress用户后台的，要记住：</p>
<p><img src="http://image.iswbm.com/20201007110841.png"></p>
<p>最后点击安装WordPress，安装成功会出现如下界面：</p>
<p><img src="http://image.iswbm.com/20201007110859.png"></p>
<p>点击登录，输入账户密码，就可以登录自己的 wordpress 后台了。</p>
<p><img src="http://image.iswbm.com/image-20201014205746752.png"></p>
<p>这时候你使用浏览器去访问 http://ip ，就可以看到你搭建的第一个网站了。</p>
<p>此时你看到的这个网站，是 wordpress 默认为你安装的免费主题（应该有三个），可能并不是那么的好看，但没关系，后面我会教大家如何挑选一个自己合适的主题，把你的网站装扮得有模有样。</p>
<h2 id="5-装扮你的网站">5. 装扮你的网站</h2>
<p>到这里，你的网站部署已经全部完成，但是还差最后一步，也是使用 WordPress 最为核心的一步：让你的网站看起来更加专业，更加成熟。</p>
<p>为此我们需要做两件事：</p>
<ol>
<li>安装主题：让网站变得好看</li>
<li>安装插件：丰富网站的功能</li>
</ol>
<h3 id="51-安装主题">5.1 安装主题</h3>
<p>同样都使用的 WordPress 的网站，外观上却天差地别。有的很酷炫，有点很简约，这是因为选了不同的主题（可以理解为皮肤）。</p>
<p>在你安装完 WordPress 后，本身就自带了几个免费的主题。</p>
<p>几乎没人会使用它们，因为实在没什么亮点（就是丑）。</p>
<p>在 WordPress 的自带了主题商店，里面有大量的免费主题，各式各样的都有，可能有你喜欢的。</p>
<p><img src="http://image.iswbm.com/image-20201014210919205.png"></p>
<p>在这里要说明一点，我的主题不是在商店里安装的，而是我通过搜索引擎找到了一个比较清爽的主题，很符合我的审美，我装扮过后的效果如下。</p>
<p><img src="http://image.iswbm.com/image-20201007112159757.png"></p>
<p>如果你想和我用一样的主题，我已经将安装包打包好了（需要的添加我微信领取: stromwbm），直接下载后，按下图的方式上传到你的 WordPress 后台安装就行。</p>
<p><img src="http://image.iswbm.com/image-20201007112442478.png"></p>
<h3 id="52-安装插件">5.2 安装插件</h3>
<p>目前我安装的插件比较少，但是基本够用啦，下面列举一下，如果你有什么好插件介绍，可以评论区推荐一下：</p>
<p><strong>1. WP User Avatar</strong></p>
<p>原生 WordPress 默认使用 Gravatar头像，用户（包括访客评论）的头像调取都是根据所留邮箱匹配的 Gravatar 头像。</p>
<p>没有 Gravatar 怎么办？</p>
<p>只要装上 <code>WP User Avatar</code> 这个插件就能可以使用 WordPress 媒体库中的图片作为默认头像了。</p>
<p><strong>2. WP-PostViews</strong></p>
<p>安装了 WP-PostViews，就可以统计你文章的浏览次数。</p>
<p>需要两步配置</p>
<p>第一步：设置显示的文字</p>
<p><img src="http://image.iswbm.com/image-20201014220218693.png"></p>
<p>第二步：在你要显示的位置写入如下代码</p>
<p><img src="http://image.iswbm.com/image-20201014220510843.png"></p>
<p>效果如下</p>
<p><img src="http://image.iswbm.com/image-20201014220652829.png"></p>
<p><strong>3. Post Views Counter</strong></p>
<p>咦，上面已经安装了一个统计阅读量的插件，怎么这边又推荐一个。</p>
<p>上面那个统计阅读量的插件，其实做的比较粗糙。</p>
<p>这是什么意思。</p>
<p>比如同一个 ip 连续连刷10次，WP-PostViews 的阅读量会 <code>+10</code>，但如果你使用 Post Views Counter，这个规则可以由你来定，可以只显示一次。</p>
<p><img src="http://image.iswbm.com/image-20201014214127315.png"></p>
<p>安装完这个插件后，同样需要进行一些配置</p>
<p><img src="http://image.iswbm.com/image-20201014214110056.png"></p>
<p>为什么这里选择手动呢？</p>
<p><img src="http://image.iswbm.com/image-20201014214217099.png"></p>
<p>因为我发现不管在内容之前，还是在内容之后，显示的位置都比较尴尬（它会换行）。</p>
<p>如果你和我用一样的主题，想和我有一样的效果，那么你选择<strong>手动</strong>之后，还要按下图指示修改下代码，新增如下一段代码。</p>
<p><img src="http://image.iswbm.com/image-20201014214436969.png"></p>
<p>效果如下</p>
<p><img src="http://image.iswbm.com/image-20201014213834821.png"></p>
<p><strong>4. WP Editor.md</strong></p>
<p>或许这是一个WordPress中最好，最完美的Markdown编辑器。</p>
<p>可以像 md2all 和 mdnice 那样，即时显示 Markdown 的渲染效果，喜欢在后台写文章的可以安装（其实我是先本地 Typora 写好了再复制上去的）。</p>
<p><img src="http://image.iswbm.com/image-20201014221844359.png"></p>
<p><strong>5. Simple Custom CSS</strong></p>
<p>在修改 WordPress主题时，CSS修改是最经常用到的方法，比如调整字体、调整颜色、边距之类的都需要用到自定义的CSS代码。</p>
<p>虽然说 WordPress 本身提供了CSS修改的功能，不过使用起来有很多的弊端，其中最麻烦的一点就是每次更换或者更新主题之前的修改都会消失，需要重复的添加。</p>
<p>使用 <code>Simple Custom CSS</code> 这个插件可以避免这种尴尬，安装后他会在 <code>外观</code> 下新增一个 <code>自定义CSS</code> 的选项。</p>
<p><img src="http://image.iswbm.com/image-20201014213322238.png"></p>
<p><strong>6. LuckyWP Table of Contents</strong></p>
<p>使用我这个主题，在文章页面是没有目录索引的，对于读者来说，其实挺不方便的。</p>
<p>因此我安装了 LuckyWP Table of Contents，可以在我的文章中生成一个目录。目录默认是隐藏的（当然你也可以设置默认展示），需要的话再点击展开。</p>
<p><img src="http://image.iswbm.com/image-20201014214735971.png"></p>
<p>点击目录可以直接跳转到对应位置。</p>
<p>但可能是我这个主题的原因，我的网站无法跳转，如果有人知道原因，还请留言区评论一下。</p>
<p><strong>7. 百度搜索推送管理</strong></p>
<p>百度搜索推送管理插件是一款针对WP开发的功能非常强大的百度和Bing搜索引擎收录辅助插件。</p>
<p>利用该插件，站长可以快速实现百度搜索资源平台和Bing站长平台URL数据推送及网站百度收录数据查询等。</p>
<p>目的在于进一步提升网站的百度和Bing搜索引擎收录效率，提升网站SEO优化效果；及帮助站长通过该插件了解网站百度收录数据情况，基于数据统计参考进一步对网站内容进行调整与优化。</p>
<p>具体使用方法比较复杂，自己百度一下吧</p>
<p><strong>8. Smart SEO Tool</strong></p>
<p>Smart SEO Tool是一款专门针对WordPress开发的智能SEO优化插件，与众多WordPress的SEO插件不一样的是，Smart SEO Tool更加简单易用，帮助站长快速完成WordPress博客/网站的SEO基础优化。</p>
<p>我在生成 sitemap 时，报这个错误</p>
<p><img src="http://image.iswbm.com/20210112140017.png"></p>
<p>Google 了半天，网上说的解决方法有很多，但是好像对我没啥用。</p>
<p>最后我无意中发现在你的主题目录下的 <code>functions.php</code> 文件最后多了很多空格，于是就把这些空格给清理了下，没想到还真的好了。<br>
<img src="http://image.iswbm.com/20210112140423.png"></p>
<h2 id="6-优化你的网站">6. 优化你的网站</h2>
<h3 id="61-使用固定链接">6.1 使用固定链接</h3>
<p>先在后台按如下图示进行设置</p>
<p><img src="http://image.iswbm.com/image-20201014222135425.png"></p>
<p>但是这样不够，要使用固定链接，服务器还需要开启 rewrite 的功能。</p>
<p>如果你和我使用的是 nginx ，那么只要登陆服务器，在</p>
<p><code>/usr/local/nginx/conf/nginx.conf</code> 里的 server 里加上这一段（我放在倒数第二段）</p>
<pre tabindex="0"><code>location / {
		if (!-e $request_filename) {
				rewrite (.*) /index.php;
		}
}
</code></pre><p>然后重启一下 nginx</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ systemctl restart nginx
</span></span></code></pre></div><h3 id="62-网站添加备案号">6.2 网站添加备案号</h3>
<p>如果你的域名已经备案了，最好将你的备案号放到底部（好像会有检查）。</p>
<p><img src="http://image.iswbm.com/image-20201014224157459.png"></p>
<h3 id="63-美化文章的排版">6.3 美化文章的排版</h3>
<p>一个好的排版，能够极大提升阅读体验，因此定制一个好的排版非常有必要。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">#</span><span class="nn">write</span> <span class="nt">a</span><span class="p">.</span><span class="nc">md-toc-inner</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">line-height</span><span class="p">:</span> <span class="mf">1.6</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">white-space</span><span class="p">:</span> <span class="kc">pre-line</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-bottom</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">#</span><span class="nn">write</span> <span class="nt">a</span><span class="p">:</span><span class="nd">hover</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-bottom</span><span class="p">:</span> <span class="mi">2</span><span class="kt">px</span> <span class="kc">solid</span> <span class="mh">#f22f27</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#cc1616</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">h1</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h2</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h3</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h4</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h5</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h6</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">position</span><span class="p">:</span> <span class="kc">relative</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-top</span><span class="p">:</span> <span class="mi">1</span><span class="kt">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-bottom</span><span class="p">:</span> <span class="mi">1</span><span class="kt">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-weight</span><span class="p">:</span> <span class="kc">bold</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">line-height</span><span class="p">:</span> <span class="mf">1.4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">cursor</span><span class="p">:</span> <span class="kc">text</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">h1</span><span class="p">:</span><span class="nd">hover</span> <span class="nt">a</span><span class="p">.</span><span class="nc">anchor</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h2</span><span class="p">:</span><span class="nd">hover</span> <span class="nt">a</span><span class="p">.</span><span class="nc">anchor</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h3</span><span class="p">:</span><span class="nd">hover</span> <span class="nt">a</span><span class="p">.</span><span class="nc">anchor</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h4</span><span class="p">:</span><span class="nd">hover</span> <span class="nt">a</span><span class="p">.</span><span class="nc">anchor</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h5</span><span class="p">:</span><span class="nd">hover</span> <span class="nt">a</span><span class="p">.</span><span class="nc">anchor</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h6</span><span class="p">:</span><span class="nd">hover</span> <span class="nt">a</span><span class="p">.</span><span class="nc">anchor</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">text-decoration</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">h1</span> <span class="nt">tt</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h1</span> <span class="nt">code</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="kc">inherit</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">h2</span> <span class="nt">tt</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h2</span> <span class="nt">code</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="kc">inherit</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">h2</span><span class="p">.</span><span class="nc">uptop</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">border-bottom</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">green</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">uptop</span><span class="p">::</span><span class="nd">before</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">content</span><span class="p">:</span> <span class="s1">&#39; &#39;</span><span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#f22f27</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">h3</span> <span class="nt">tt</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h3</span> <span class="nt">code</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="kc">inherit</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">h4</span> <span class="nt">tt</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h4</span> <span class="nt">code</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="kc">inherit</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">h5</span> <span class="nt">tt</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h5</span> <span class="nt">code</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="kc">inherit</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">h6</span> <span class="nt">tt</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h6</span> <span class="nt">code</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="kc">inherit</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">h2</span> <span class="nt">a</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">h3</span> <span class="nt">a</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#34495e</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">h2</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">2</span><span class="kt">em</span> <span class="kc">auto</span> <span class="mf">1.4</span><span class="kt">em</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c">/* padding-left: 10px; */</span>
</span></span><span class="line"><span class="cl">    <span class="c">/* display:inline-block; */</span>
</span></span><span class="line"><span class="cl">    <span class="k">line-height</span><span class="p">:</span> <span class="mf">1.4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mf">1.8</span><span class="kt">em</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c">/* border-left: 9px solid var(--main-6); */</span>
</span></span><span class="line"><span class="cl">    <span class="c">/* border-bottom: 1px solid #ddd; */</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-bottom</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="mh">#f22f27</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">h2</span><span class="p">::</span><span class="nd">before</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">content</span><span class="p">:</span> <span class="s1">&#39;# &#39;</span><span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#f22f27</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">h3</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mf">1.4</span><span class="kt">em</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">line-height</span><span class="p">:</span> <span class="mf">1.43</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mf">1.6</span><span class="kt">em</span> <span class="kc">auto</span> <span class="mf">1.2</span><span class="kt">em</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">padding-left</span><span class="p">:</span> <span class="mi">9</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-left</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span> <span class="kc">solid</span> <span class="mh">#f22f27</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 三级四级标题点击后左边的提示图标 */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">#</span><span class="nn">write</span><span class="o">&gt;</span><span class="nt">h3</span><span class="p">.</span><span class="nc">md-focus</span><span class="p">:</span><span class="nd">before</span><span class="o">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 引用文字 */</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">view-content</span> <span class="nt">blockquote</span> <span class="nt">p</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#353535</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">16</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">0</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">display</span><span class="p">:</span> <span class="kc">block</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">view-content</span> <span class="p">.</span><span class="nc">blockquote</span><span class="p">:</span><span class="nd">after</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">content</span><span class="p">:</span> <span class="s2">&#34;”&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">float</span><span class="p">:</span> <span class="kc">right</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">display</span><span class="p">:</span> <span class="kc">block</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">2</span><span class="kt">em</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="nb">rgb</span><span class="p">(</span><span class="mi">248</span><span class="p">,</span> <span class="mi">57</span><span class="p">,</span> <span class="mi">41</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-family</span><span class="p">:</span> <span class="n">Arial</span><span class="p">,</span> <span class="kc">serif</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">line-height</span><span class="p">:</span> <span class="mi">1</span><span class="kt">em</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-weight</span><span class="p">:</span> <span class="mi">700</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 二维码显示 */</span>
</span></span><span class="line"><span class="cl"><span class="nt">header</span> <span class="p">.</span><span class="nc">gongzhonghao</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#fff</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">14</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">position</span><span class="p">:</span> <span class="kc">absolute</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">bottom</span><span class="p">:</span> <span class="mi">30</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">header</span> <span class="p">.</span><span class="nc">weixinhao</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#fff</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">14</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">position</span><span class="p">:</span> <span class="kc">absolute</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">bottom</span><span class="p">:</span> <span class="mi">180</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 表格样式 */</span>
</span></span><span class="line"><span class="cl"><span class="nt">table</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">padding</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">word-break</span><span class="p">:</span> <span class="kc">initial</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">table</span> <span class="nt">tr</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-top</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="mh">#f22f27</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">padding</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">table</span> <span class="nt">tr</span><span class="p">:</span><span class="nd">nth-child</span><span class="o">(</span><span class="nt">2n</span><span class="o">),</span>
</span></span><span class="line"><span class="cl"><span class="nt">thead</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">background-color</span><span class="p">:</span> <span class="mh">#fafafa</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">table</span> <span class="nt">tr</span> <span class="nt">th</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-weight</span><span class="p">:</span> <span class="kc">bold</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="mh">#f22f27</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-bottom</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">text-align</span><span class="p">:</span> <span class="kc">left</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">padding</span><span class="p">:</span> <span class="mi">6</span><span class="kt">px</span> <span class="mi">13</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">table</span> <span class="nt">tr</span> <span class="nt">td</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">border</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="mh">#f22f27</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">text-align</span><span class="p">:</span> <span class="kc">left</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">padding</span><span class="p">:</span> <span class="mi">6</span><span class="kt">px</span> <span class="mi">13</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">table</span> <span class="nt">tr</span> <span class="nt">th</span><span class="p">:</span><span class="nd">first-child</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">table</span> <span class="nt">tr</span> <span class="nt">td</span><span class="p">:</span><span class="nd">first-child</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-top</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">table</span> <span class="nt">tr</span> <span class="nt">th</span><span class="p">:</span><span class="nd">last-child</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"><span class="nt">table</span> <span class="nt">tr</span> <span class="nt">td</span><span class="p">:</span><span class="nd">last-child</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-bottom</span><span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">/* 代码框 */</span>
</span></span><span class="line"><span class="cl"><span class="p">#</span><span class="nn">write</span> <span class="p">.</span><span class="nc">md-fences</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">border</span><span class="p">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="kc">solid</span> <span class="mh">#7a7a7a</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kp">-webkit-</span><span class="n">font-smoothing</span><span class="p">:</span> <span class="kc">initial</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">2</span><span class="kt">rem</span> <span class="mi">0</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c">/* padding: 0.3rem 0 !important; */</span>
</span></span><span class="line"><span class="cl">    <span class="k">padding</span><span class="p">:</span> <span class="mi">3</span><span class="kt">px</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">line-height</span><span class="p">:</span> <span class="mf">1.55</span><span class="kt">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-radius</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-family</span><span class="p">:</span> <span class="s1">&#39;Roboto Mono&#39;</span><span class="p">,</span> <span class="s1">&#39;Source Sans Pro&#39;</span><span class="p">,</span> <span class="s1">&#39;Microsoft YaHei&#39;</span><span class="p">,</span> <span class="s1">&#39;微软雅黑&#39;</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mf">0.9</span><span class="kt">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">word-wrap</span><span class="p">:</span> <span class="kc">normal</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">#</span><span class="nn">write</span> <span class="o">[</span><span class="nt">mdtype</span><span class="o">=</span><span class="s2">&#34;math_block&#34;</span><span class="o">]</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mf">1.2</span><span class="kt">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">#</span><span class="nn">write</span> <span class="p">.</span><span class="nc">CodeMirror-wrap</span> <span class="p">.</span><span class="nc">CodeMirror-code</span> <span class="nt">pre</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">padding-left</span><span class="p">:</span> <span class="mi">12</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">line-height</span><span class="p">:</span> <span class="mf">1.55</span><span class="kt">rem</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">cm-s-inner</span> <span class="p">.</span><span class="nc">CodeMirror-linenumber</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">width</span><span class="p">:</span> <span class="mi">2</span><span class="kt">ch</span> <span class="cp">!important</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="nb">rgba</span><span class="p">(</span><span class="mi">128</span><span class="p">,</span> <span class="mi">128</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mf">0.8</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">#</span><span class="nn">write</span> <span class="p">.</span><span class="nc">CodeMirror-cursors</span> <span class="p">.</span><span class="nc">CodeMirror-cursor</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-left</span><span class="p">:</span> <span class="mi">2</span><span class="kt">px</span> <span class="kc">solid</span> <span class="mh">#ff887a</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>进入 WP Editor.md 插件配置文章的 markdown 渲染：</p>
<p><strong>第一：开启目录</strong></p>
<p><img src="http://image.iswbm.com/20210618085435.png"></p>
<p>安装完 Table of Contents Plus 插件后，可以设置目录的显示位置</p>
<p><img src="http://image.iswbm.com/20210618085803.png"></p>
<p><strong>第二：设置代码主题</strong></p>
<p><img src="http://image.iswbm.com/20210618085544.png"></p>
<h3 id="64-部署-https">6.4 部署 HTTPS</h3>
<p>关于如何部署 HTTPS ，我在之前的文章里讲过一种。</p>
<p>今天再介绍另外一种：使用宝塔。</p>
<p>注册并登陆宝塔（https://bt.cn），然后进入控制面板，进行实名认证。</p>
<p><img src="http://image.iswbm.com/image-20200929123223096.png"></p>
<p>点击申请证书</p>
<p><img src="http://image.iswbm.com/image-20200929123355037.png"></p>
<p>选择免费的就好</p>
<p><img src="http://image.iswbm.com/image-20200929123418789.png"></p>
<p>填写你的域名后，支付订单（其实不要钱）。</p>
<p><img src="http://image.iswbm.com/image-20200929123459545.png"></p>
<p>然后点击详情，需要验证该域名是归你所有。</p>
<p>方法它会告诉你，登陆我的阿里云域名解析，根据如下提示去添加 DNS解析规则</p>
<p><img src="http://image.iswbm.com/image-20200929123956070.png"></p>
<p>然后静待一段时间验证成功了，就可以点击如下按钮，下载数据证书。  <img src="http://image.iswbm.com/image-20200929194100905.png"></p>
<p>下载到本地后，你会得到一个 zip 包，解压一下，就可以看到证书文件及私钥。</p>
<p><img src="http://image.iswbm.com/image-20200929201114297.png"></p>
<p>因为我的博客使用的是 Nginx，因此我该 Nginx 下的两个文件上传到我的服务器上的 nginx 目录下.</p>
<p>具体怎么上传呢？你可以使用远程拷贝软件，例如 WinSCP，也可以使用 <code>lrzsz</code> （推荐使用）。</p>
<p>传到哪个目录下呢？</p>
<p>先使用 find 命令查找一下你的 nginx.conf 路径</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ find / -name nginx.conf
</span></span><span class="line"><span class="cl">/usr/local/nginx/conf/nginx.conf
</span></span></code></pre></div><p>你的证书文件可以和 nginx.conf 放在同一目录下</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">/usr/local/nginx/conf
</span></span></code></pre></div><p>接下来使用 vim 编辑该文件，找到 server，添加如下行（ server 原本的内容 我使用 <code>...</code> 表示，意思是不需要去动。 ）</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">server
</span></span><span class="line"><span class="cl">    <span class="o">{</span>
</span></span><span class="line"><span class="cl">        listen <span class="m">443</span> ssl<span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># 注释掉该行</span>
</span></span><span class="line"><span class="cl">				<span class="c1"># listen 80 default_server reuseport;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">#证书文件名称</span>
</span></span><span class="line"><span class="cl">        ssl_certificate 1_iswbm.com_bundle.pem<span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="c1">#私钥文件名称</span>
</span></span><span class="line"><span class="cl">        ssl_certificate_key 0_iswbm.com.key<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">				...
</span></span><span class="line"><span class="cl">    <span class="o">}</span>
</span></span></code></pre></div><p>最后重启 nginx</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ systemctl restart nginx
</span></span></code></pre></div><p>尝试用 https 访问一下我的网站 <code>https://iswbm.com</code>，非常顺利。</p>
<p><img src="http://image.iswbm.com/image-20201014223409827.png"></p>
<p>到这里，事情其实还没有结束，你还需要做两件事情：</p>
<p><strong>第一件事</strong>：在后台进行一些配置，不然从网站跳转的时候还是会使用 http</p>
<p><img src="http://image.iswbm.com/image-20201014223547198.png"></p>
<p><strong>第二件事</strong>：将 http 重定向到你 https，不然有人在浏览器输入 iswbm.com 时，访问的还是 http。</p>
<p>使用 vim 编辑 /usr/local/nginx/conf/nginx.conf，在原本 server 的前面加入下面这么一段代码</p>
<pre tabindex="0"><code>server {
        listen 80;
        server_name iswbm.com;
        rewrite ^(.*) https://$server_name$1 permanent;
        }
</code></pre><p>然后再重启 nginx</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ systemctl restart nginx
</span></span></code></pre></div><h3 id="65-图床开启https">6.5 图床开启HTTPS</h3>
<p>由于我之前的 markdown 文章，使用的图床都是 七牛云，当时并没有开启 https，现在网站开启了 https 后，自然图片也要开启，不然浏览器会显示有部分不安全的资源。</p>
<p>登陆七牛云，然后进入 <a href="https://portal.qiniu.com/certificate/ssl%20">证书管理</a> 上传证书（注意这个证书得另外申请，不能使用先前申请的 iswbm.com 证书）。</p>
<p><img src="http://image.iswbm.com/image-20200929210645477.png"></p>
<p>进入对象存储 -&gt; 域名管理，找到 HTTPS 配置的位置，点击 <code>修改配置</code>。</p>
<p><img src="http://image.iswbm.com/image-20200929205400898.png"></p>
<p>将按钮置为开启状态，选择我们刚刚上传的证书。</p>
<p><img src="http://image.iswbm.com/image-20200929205611442.png"></p>
<p>设置完后，并不能立马使用</p>
<p><img src="http://image.iswbm.com/image-20200929210431735.png"></p>
<p>域名的升级需要一定时间，等待即可。</p>
<p><img src="http://image.iswbm.com/image-20200929210900562.png"></p>
<p>以上就是我搭建网站的全部总结，写了两个晚上，直到昨晚才完成。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>如何通过utterance实现Sphinx文档的评论功能</title>
      <link>https://iswbm.com/en/2024/06/16/how-to-support-comment-in-sphinx/</link>
      <pubDate>Sun, 16 Jun 2024 20:49:10 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/16/how-to-support-comment-in-sphinx/</guid>
      <description>1. 评论系统调研 关注我时间长点的读者，都知道我除了维护一个个人技术博客之外，还有写过很多实用的在线文档。 这些文档，都有一个痛病，他们都是静态网</description>
      <content:encoded><![CDATA[<h2 id="1-评论系统调研">1. 评论系统调研</h2>
<p>关注我时间长点的读者，都知道我除了维护一个个人技术博客之外，还有写过很多实用的在线文档。</p>
<p><img src="http://image.iswbm.com/20220219203945.png"></p>
<p>这些文档，都有一个痛病，他们都是静态网站，因此没有评论区。</p>
<p>而对于博客来说，评论区是增强技术交流的一个重要工具，文档若有事实错误和描述性问题，读者就没法方便地向我反馈。</p>
<p>对于动态网站来说，可以动手设计一个评论管理系统，虽然可以做，但也并不是一件易事，要做用户的管理（包括注册、登陆），库表的设计、多级回复等等</p>
<p>而静态网站没有数据库，若想提供评论功能，通常的做法是直接使用现成的评论插件，界面美观还易用。</p>
<p>我了解过的评论系统有：</p>
<ul>
<li>国内：有言（官网已经挂了）、畅言（搜狐出品）</li>
<li>国外：Disqus（被墙）、gitment、gitalk、来比力（速度很慢）</li>
</ul>
<p>其中 gitment 和 gitalk 的原理类似，都是将评论内容存储在 github 仓库的 issue 中。</p>
<p>我挑了知名度较高的 gitalk 部署了下，当我访问文档时，才发现这货居然，要我手动为每篇文章仓库一个 issue？</p>
<p><img src="http://image.iswbm.com/image-20220219130216439.png"></p>
<p>这对于一个还没有多少文章的新站点来说，可能还不是什么问题，可是我的 5、6 年的文章，有上千篇，一个一个去手动创建，这是不可能的。</p>
<p>于是我就开始找自动化脚本，其中有 ruby 的，python 的，方案特别多，看得我眼花缭乱的。</p>
<p>最终在某个 Python 脚本作者的方案中，看到了这么一句话</p>
<p><img src="http://image.iswbm.com/image-20220219151027696.png"></p>
<p>好家伙，刚部署完一个，又让我部署另一个。</p>
<p>不过 utterances 不用自动为每篇文章创建 issue，这个正是我需要的。</p>
<p>因此本篇文章是基于 utterances 插件的评论系统接入 Sphinx 文档的教程</p>
<p>若你之前有跟着我的教程搭建过 Sphinx 在线技术文档，可以跟着操作一下，因为我搜索了下，本篇应该是第一篇有公开的 Sphinx 接入 utterances评论系统的教程（也许有人使用过，但没人分享过文章）。</p>
<h2 id="2-安装-utterances">2. 安装 utterances</h2>
<p>访问 <a href="https://github.com/apps/utterances">utterances 应用程序</a> ，然后点击 Install 按钮进行安装</p>
<p><img src="http://image.iswbm.com/image-20220219132215678.png"></p>
<p>在安装时，可以立即选择是否限制其访问的仓库，若安装时忘记设置了，也可以在安装后再次选择。</p>
<p>只要再次访问 <a href="https://github.com/apps/utterances">utterances 应用程序</a>，就会显示配置的界面</p>
<p><img src="http://image.iswbm.com/image-20220219132255419.png"></p>
<p>点进去就能更新配置，选择完直接 Save 。</p>
<p><img src="http://image.iswbm.com/image-20220219132425611.png"></p>
<h2 id="3-定义评论模板">3. 定义评论模板</h2>
<p>我的文档使用的是 Sphinx + rtd 主题，因此找到 sphinx_rtd_theme 所在的目录，比如我的目录是 /usr/local/lib/python3.6/site-packages/sphinx_rtd_theme</p>
<p>在该目录下新建 comments.html 文件，内容如下</p>
<pre tabindex="0"><code>&lt;comments&gt;
  &lt;script src=&#34;https://utteranc.es/client.js&#34;
    repo=&#34;iswbm/magic-python&#34;
    issue-term=&#34;pathname&#34;
    theme=&#34;github-light&#34;
    crossorigin=&#34;anonymous&#34;
    async&gt;
  &lt;/script&gt;
&lt;/comments&gt;
</code></pre><p>其中你需要根据自己的情况进行调整的字段只有 repo，填写你评论要存放在哪个 github 仓库。</p>
<p>定义了 comments 的模板文件后，要在 layout.html 中引用它</p>
<p>但由于原先 layout.html 已经预留了 comments 的内容，先将其删除</p>
<p><img src="http://image.iswbm.com/image-20220219142947203.png"></p>
<p>替换成</p>
<pre tabindex="0"><code>         &lt;div class=&#34;articleComments&#34;&gt;
               {% include &#34;comments.html&#34; %}
           &lt;/div&gt;
</code></pre><h2 id="4-编译查看效果">4. 编译查看效果</h2>
<p>执行 <code>make html</code> 重新编译文档的静态 html 文件，再次查看网页，就会发现在文档的尾部出现了期待的评论区</p>
<p><img src="http://image.iswbm.com/image-20220219141145507.png"></p>
<p>想要在文章下评论，就得先登陆 Github 授权一下</p>
<p><img src="http://image.iswbm.com/image-20220219141201659.png"></p>
<p>登陆并授权之后，你的 Github 头像出现了，是可以评论的状态</p>
<p><img src="http://image.iswbm.com/image-20220219141255145.png"></p>
<p>utterances 的评论是放在 issue 里的，因此评论这边也是要支持 markdown</p>
<p><img src="http://image.iswbm.com/image-20220219141456684.png"></p>
<p>写完之后，点下预览可以看下效果</p>
<p><img src="http://image.iswbm.com/image-20220219141507596.png"></p>
<p>没什么问题的话，可以点击 comment 发送评论</p>
<p><img src="http://image.iswbm.com/image-20220219141616499.png"></p>
<p>评论就会出现在上方，这个布局有点迷呆，怎么不是在下方？</p>
<p><img src="http://image.iswbm.com/image-20220219141807370.png"></p>
<p>去 Github 仓库的 issue 上看，确实是多了一条信息</p>
<p><img src="http://image.iswbm.com/image-20220219141752533.png"></p>
<h2 id="5-总结一下">5. 总结一下</h2>
<p>utterances 与 gitalk 对比一下，缺点不少，比如：</p>
<ul>
<li>gitalk 可以通过 如下容器定义评论区的位置，而 utterances 则不行（反正我没有找到对应方法），需要你有动手能力去定义模板。</li>
<li>utterances 不支持在评论区直接引用他人的评论进行多级回复</li>
<li>当有评论时 utterances 的评论框是在所有的评论的下方，不太符合用户逻辑</li>
</ul>
<p>即使 utterances 有如此之多的缺点，但 utterances 不需要手动去创建 issue ，仅凭这一点，在我看来，就可以秒杀 gitalk ，希望 gitalk 早日改进支持吧。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>如何为Sphinx部署网站分析插件</title>
      <link>https://iswbm.com/en/2024/06/16/how-to-deploy-analytical-plugin-for-your-sphinx-document-site/</link>
      <pubDate>Sun, 16 Jun 2024 20:45:29 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/16/how-to-deploy-analytical-plugin-for-your-sphinx-document-site/</guid>
      <description>对于静态网站来说，若想分析我们网站的访问数据，通常是引用一些第三方平台提供的 js，我使用的是百度家的产品 - 百度统计 。 它可以帮我们收集网站访问</description>
      <content:encoded><![CDATA[<p>对于静态网站来说，若想分析我们网站的访问数据，通常是引用一些第三方平台提供的 js，我使用的是百度家的产品 - <code>百度统计</code> 。</p>
<p>它可以帮我们收集网站访问数据，提供流量趋势、来源分析、转化跟踪、页面热力图、访问流等多种统计分析服务。</p>
<p>首先使用你的百度帐号登陆 <code>百度统计</code>。</p>
<p>然后在网站列表新增一个你的网站，我的信息如下：</p>
<p><img src="https://image.iswbm.com/202406162018580.png"></p>
<p>填写完成，就可以获取一段属于你的网站的专属 js 代码（下面第一步）。</p>
<p><img src="https://image.iswbm.com/202406162020046.png"></p>
<p>第二步内容，是教你如何安装这段 js 代码。</p>
<p>将这段代码内容写入一个单独的 js 文件：<code>baidutongji.js</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">_hmt</span> <span class="o">=</span> <span class="nx">_hmt</span> <span class="o">||</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">hm</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">&#34;script&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">hm</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="s2">&#34;https://hm.baidu.com/hm.js?xxxxxxxx&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kd">var</span> <span class="nx">s</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByTagName</span><span class="p">(</span><span class="s2">&#34;script&#34;</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span> 
</span></span><span class="line"><span class="cl">  <span class="nx">s</span><span class="p">.</span><span class="nx">parentNode</span><span class="p">.</span><span class="nx">insertBefore</span><span class="p">(</span><span class="nx">hm</span><span class="p">,</span> <span class="nx">s</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">})();</span>
</span></span></code></pre></div><p>并修改 conf.py 后，提交至你的 Github。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">html_js_files</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;js/readmore.js&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;js/baidutongji.js&#39;</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span>
</span></span></code></pre></div><p>一切完成后，就可以去 readthedocs 重建构建。构建完成后，去执行第三步，代码安装检查。像我下面这样，就是安装完成了。</p>
<p><img src="https://image.iswbm.com/202406162041544.png"></p>
<p>这个插件安装完成后，如果你的网站有流量，可以过个一个小时，点击一下查看报告查看你网站的详细访问数据。</p>
<p><img src="http://image.iswbm.com/20191016211012.png"></p>
<p>数据真的非常全面，你可以知道，访客都是从哪里访问（直接访问，Google等），每篇文章的点击量（你就知道哪篇是爆款？），每天有多少老访客，多少新访客等等，更多维度的数据你可以自己去查看下。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>如何使用让Sphnix文档支持多版本构建</title>
      <link>https://iswbm.com/en/2024/06/16/how-to-support-multi-version-in-sphinx-document/</link>
      <pubDate>Sun, 16 Jun 2024 20:06:14 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/16/how-to-support-multi-version-in-sphinx-document/</guid>
      <description>若你参照我之前写过的 Sphinx 教程自己搭建过个人博客或者在线文档站点的话，你会发现通过 RTD 托管后，在左下角会有多版本切换的边栏。 但在本地使用 make html 构建生</description>
      <content:encoded><![CDATA[<p>若你参照我之前写过的 Sphinx 教程自己搭建过个人博客或者在线文档站点的话，你会发现通过 RTD 托管后，在左下角会有多版本切换的边栏。</p>
<p><img src="http://image.iswbm.com/20210709123357.png"></p>
<p>但在本地使用 <code>make html</code> 构建生成的 html ，是没有这个的。由于我一直使用 RTD 进行托管，对于这个差异也没有必要去关心了。</p>
<p>可是最近我在搞 SEO 往 Google 上提交站点的 sitemap 时，发现往 Sphinx 项目中加入 sphinx-sitemap 插件后，再使用 RTD 的时候生成的 sitemap 是有问题的，主站的 sitemap 仅有几个版本分支的 url，文章的 url 一个都没有。</p>
<p>但是在本地使用 <code>make html</code> 又是没有问题的，这让我非常的困惑，但又没有办法，毕竟我无法对 RTD 的构建过程进行调试。</p>
<p>于是我只能放弃 RTD 的托管，而改用本地构建，并把构建好的 HTML 放到自己的服务器上，使用 NGINX 进行代理，对外提供访问服务。</p>
<p>但是使用本地构建，我又得放弃 RTD 提供的多版本切换边栏，这也让我不能接受。</p>
<p>原因是在我的 《PyCharm中文指南》在线文档中，有分 windows 版本和 mac 版本的文档，对于不同的人需要看不同版本的。</p>
<p>如果在界面上提供这个入口的话，那么有很多人就不知道怎么切换了。</p>
<p>于是乎，我就在网络上，找了各种 Sphinx 支持本地构建插入 RTD Theme 的 multi-version sidebar。</p>
<p>在网上能找到的插件有两个：</p>
<ol>
<li>sphinxcontrib-versioning</li>
<li>sphinx-multiversion</li>
</ol>
<p>其中 sphinxcontrib-versioning 是 N 多年前的项目了，我在本地使用报各种报错，去 github 上一看，发现这个仓库如今已经没人维护了，在 github 一堆 issue 没人解决。</p>
<p>最后只能把希望寄托在 sphinx-multiversion 上面。在验证 sphinx-multiversion 的过程中，实际上也遇到了不少问题，花了不少时间，直到昨天晚上才把流程跑通。</p>
<p>下面来分享下经过整理精简过的教程，如果你刚好有需要，可以参考下。</p>
<h2 id="1-安装插件">1. 安装插件</h2>
<p>使用 pip 安装 <code>sphinx-multiversion</code> 插件</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ pip3 install sphinx-multiversion
</span></span></code></pre></div><p>并把该依赖库写入到 requirements.txt 文件中</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;sphinx-multiversion==0.2.4&#34;</span> &gt;&gt;requirements.txt
</span></span></code></pre></div><p>并且该插件追加到 Sphinx 的配置文件中的 extensions 中</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nv">extensions</span> <span class="o">=</span> <span class="o">[</span><span class="s1">&#39;chinese_search&#39;</span>,<span class="s1">&#39;sphinx.ext.mathjax&#39;</span>,<span class="s1">&#39;sphinx_sitemap&#39;</span>, <span class="s1">&#39;sphinx_multiversion&#39;</span><span class="o">]</span>
</span></span></code></pre></div><h2 id="2-使用插件">2. 使用插件</h2>
<p>使用 <code>sphinx-multiversion</code> 帮你做的其实就是把你设定好的 html 模板渲染好写入每一个生成的 html 的文件中。</p>
<p>因此 ，在用它之前，你首先得写好你的模板，在这里由于我们要使用 RTD Theme 的样式，所以你得找到这样的现成的样式，在官方文档上刚好有 share，我直接搬过来了。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">{%- if current_version %}
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;rst-versions&#34;</span> <span class="na">data-toggle</span><span class="o">=</span><span class="s">&#34;rst-versions&#34;</span> <span class="na">role</span><span class="o">=</span><span class="s">&#34;note&#34;</span> <span class="na">aria-label</span><span class="o">=</span><span class="s">&#34;versions&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;rst-current-version&#34;</span> <span class="na">data-toggle</span><span class="o">=</span><span class="s">&#34;rst-current-version&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;fa fa-book&#34;</span><span class="p">&gt;</span> Other Versions<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    v: {{ current_version.name }}
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;fa fa-caret-down&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;rst-other-versions&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    {%- if versions.tags %}
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">dl</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">dt</span><span class="p">&gt;</span>Tags<span class="p">&lt;/</span><span class="nt">dt</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      {%- for item in versions.tags %}
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">dd</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ item.url }}&#34;</span><span class="p">&gt;</span>{{ item.name }}<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">dd</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      {%- endfor %}
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">dl</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    {%- endif %}
</span></span><span class="line"><span class="cl">    {%- if versions.branches %}
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">dl</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">dt</span><span class="p">&gt;</span>Branches<span class="p">&lt;/</span><span class="nt">dt</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      {%- for item in versions.branches %}
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">dd</span><span class="p">&gt;&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ item.url }}&#34;</span><span class="p">&gt;</span>{{ item.name }}<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;&lt;/</span><span class="nt">dd</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      {%- endfor %}
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">dl</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    {%- endif %}
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">{%- endif %}
</span></span></code></pre></div><p>这个文件可以放在与 conf.py 同级目录下的 <code>_templates</code> 目录，文件名为 <code>versions.html</code>，如果你先前没有该目录，需要你手动创建它。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ <span class="nb">cd</span> <span class="nb">source</span>
</span></span><span class="line"><span class="cl">$ mkdir _templates
</span></span></code></pre></div><p>接下来，你得在 conf.py 中写几个配置，告诉 Sphinx 去哪里找到这个 <code>versions.html</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">templates_path</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;_templates&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">html_sidebars</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;**&#39;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;versioning.html&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 指定哪个分支为 lastest 版本</span>
</span></span><span class="line"><span class="cl"><span class="n">smv_latest_version</span> <span class="o">=</span> <span class="s1">&#39;master&#39;</span>
</span></span></code></pre></div><p>接下来就可以使用 <code>sphinx-multiversion</code> 提供的 cli 命令去构建多版本的 html 了。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ sphinx-multiversion <span class="nb">source</span> build/html
</span></span></code></pre></div><p>其中两个参数的意义是：</p>
<ul>
<li><code>source</code>：表示 md 或 rst 文档所在的目录，一般是 conf.py 所在目录</li>
<li><code>build/html</code>：表示你要将构建好的 html 放在哪个目录下</li>
</ul>
<p>执行后，会在 <code>build/html</code> 目录下生成多个目录，每一个目录是一个版本。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"> $ ls -l build/html/
</span></span><span class="line"><span class="cl">drwxr-xr-x <span class="m">19</span> root root   <span class="m">4096</span> Jul  <span class="m">9</span> 01:31 master
</span></span><span class="line"><span class="cl">drwxr-xr-x <span class="m">19</span> root root   <span class="m">4096</span> Jul  <span class="m">9</span> 00:39 v1.0
</span></span><span class="line"><span class="cl">drwxr-xr-x <span class="m">19</span> root root   <span class="m">4096</span> Jul  <span class="m">9</span> 00:39 v2.0
</span></span><span class="line"><span class="cl">drwxr-xr-x <span class="m">19</span> root root   <span class="m">4096</span> Jul  <span class="m">9</span> 00:39 v3.0
</span></span></code></pre></div><p>最后一步，就配置 nginx 代理，并重启 nginx</p>
<pre tabindex="0"><code>server {
        listen 80;
        server_name magic.iswbm.com;
        root /home/wwwroot/magic-python/build/html/;
        }
</code></pre><p>如此一来，以后访问在线文档的三个版本就得使用如下三个 url：</p>
<ul>
<li><a href="http://magic.iswbm.com/master">http://magic.iswbm.com/master</a></li>
<li><a href="http://magic.iswbm.com/v1.0">http://magic.iswbm.com/v1.0</a></li>
<li><a href="http://magic.iswbm.com/v2.0">http://magic.iswbm.com/v2.0</a></li>
</ul>
<p>是不是我还不错呢？</p>
<p>不过这还不够，访问最新版本的时候，不应该还使用 <code>http://magic.iswbm.com/master</code>，而应该还是根目录 <code>http://magic.iswbm.com</code></p>
<p>那怎么办呢？</p>
<p>我想到的办法是把 master 目录里的文件及文件夹，全部拷贝一份到 与 master 同级目录下（<strong>如果你有更好的办法，请一定留言告诉我</strong>）。</p>
<p>因此最后的构建命令变成了这样</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">rm -rf build/ <span class="p">&amp;</span>amp<span class="p">;&amp;</span>amp<span class="p">;</span> sphinx-multiversion <span class="nb">source</span> build/html <span class="p">&amp;</span>amp<span class="p">;&amp;</span>amp<span class="p">;</span> cp -rf build/html/master/* build/html/
</span></span></code></pre></div><p>为了方便，我为这条命令定义了别名 <code>rebuild</code>，这样以后要构建只要执行 <code>rebuild</code> 即可</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ <span class="nb">echo</span> <span class="s2">&#34;alias rebuild=&#39;rm -rf build/ &amp;amp;&amp;amp; sphinx-multiversion source build/html &amp;amp;&amp;amp; cp -rf build/html/master/* build/html/&#39;&#34;</span> &gt;&gt; /root/.bash_profile
</span></span><span class="line"><span class="cl">$ <span class="nb">source</span> /root/.bash_profile
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>如何使用Sphinx搭建个人技术文档</title>
      <link>https://iswbm.com/en/2024/06/16/how-to-build-technical-doc-with-sphinx/</link>
      <pubDate>Sun, 16 Jun 2024 19:58:42 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/16/how-to-build-technical-doc-with-sphinx/</guid>
      <description>10个优秀的程序员里，有9个人都有写博客的习惯。这是非常好的习惯，值得每个程序员，投入时间和精力去坚持做下去。 写博客的平台有很多，CSDN，</description>
      <content:encoded><![CDATA[<p>10个优秀的程序员里，有9个人都有写博客的习惯。这是非常好的习惯，值得每个程序员，投入时间和精力去坚持做下去。</p>
<p>写博客的平台有很多，CSDN，博客园，51CTO，还有人会使用Hexo+GitHub，WordPress，比较会折腾的人还会自己使用Java，Python搭建，我就干过这样的事，不过每年还要支付域名和服务器，比较麻烦而且浪费钱。</p>
<p>以上博客我都有注册使用过，不过最终还是放弃。博客文章，比较零散，无法形成一个系统性的知识体系，不便索引。</p>
<p>为了使自己的文章能有一个比较完整的体系。经过一番探索之后，能满足我的基本要求的有如下两种：</p>
<ul>
<li>GitHub Wiki，适合做知识整理，但排版一般，不方便查看。</li>
<li>GitBook，样式不好看，访问速度慢。</li>
</ul>
<p>由于以上两种都有各自的不足的地方，所以我最后选择了一个完美的解决方案：Markdown+Pandoc+Sphinx+GitHub+ReadtheDocs<br>
来管理我的文章。</p>
<ul>
<li>Markdown：书写文档</li>
<li>Pandoc：格式转化</li>
<li>Sphinx：生成网页</li>
<li>GitHub：托管项目</li>
<li>ReadtheDocs：发布网页</li>
</ul>
<h2 id="1-成品展示">1. 成品展示</h2>
<p>以我的博客(<code>python.iswbm.com</code>)为例，先给大家展示一下。</p>
<p>这是首页。显示了你所有的文章索引。<br>
<img src="http://image.iswbm.com/20190511160523.png"></p>
<p>这是我的导航栏。是不是结构很清晰，很方便索引。<br>
<img src="http://image.iswbm.com/20190511161056.png"></p>
<p>点击文章后，还可以很方便查看标题，跳转。<br>
<img src="http://image.iswbm.com/20190511161130.png"></p>
<p>体验下搜索功能，速度很快。</p>
<p><img src="http://image.iswbm.com/20190511161147.png"></p>
<p>看完这些你是不是也很想拥有这样一个博客呢？</p>
<p>只要你认真往下看，30分钟搭建这样一个博客不在话下。</p>
<h2 id="2-安装sphinx">2. 安装Sphinx</h2>
<p>安装之前，请确认下Python版本。我这里使用的是Python 2.7.14，其他版本请自行尝试噢，Python3.6好像有些坑，你需要踩一下。</p>
<p>安装Python工具包</p>
<pre tabindex="0"><code>$ pip install sphinx sphinx-autobuild sphinx_rtd_theme -i https://pypi.douban.com/simple/
</code></pre><p>初始化</p>
<pre tabindex="0"><code># 先创建一个工程目录:F:\mkdocs
$ cd F:\mkdocs

$ sphinx-quickstart
</code></pre><p>执行这个命令sphinx-quickstart的时候，会让你输入配置。除了这几个个性化配置，其他的都可以按照默认的来。</p>
<pre tabindex="0"><code>&gt; Project name: MING&#39;s BLOG
&gt; Author name(s): MING
&gt; Project release []: 1.0
&gt; Project language [en]: zh_CN
</code></pre><p>完了后，就可以看见创建的工程文件。</p>
<pre tabindex="0"><code>F:\mkdocs
(mkdocs) λ ls -l
total 5
-rw-r--r-- 1 wangbm 1049089 610 Jun 23 16:57 Makefile
drwxr-xr-x 1 wangbm 1049089   0 Jun 23 16:57 build/
-rw-r--r-- 1 wangbm 1049089 817 Jun 23 16:57 make.bat
drwxr-xr-x 1 wangbm 1049089   0 Jun 23 16:57 source/

F:\mkdocs
(mkdocs) λ tree
卷 文档 的文件夹 PATH 列表
卷序列号为 0002-B4B9
F:.
├─build
└─source
    ├─_static
    └─_templates
</code></pre><p>解释下这些文件/夹：</p>
<ul>
<li>build：文件夹，当你执行make html的时候，生成的html静态文件都存放在这里。</li>
<li>source：文件夹：你的文档源文件全部应全部放在source根目录下。</li>
<li>Makefile：编译文件。完全不用管。</li>
<li>make.bat：bat脚本。你也不用管。</li>
</ul>
<h2 id="3-配置及扩展">3. 配置及扩展</h2>
<p>Sphinx 的配置文件是 source\conifg.py</p>
<p>由于修改的内容比较多而杂，为了使这个搭建过程，更加顺畅。</p>
<p>小明已经给你精心准备了一份配置文件。你只要关注我的公众号「<code>Python编程时光</code>」，后台直接回复 「Sphinx」即可获取。</p>
<p>关于配置文件，我做了哪些事：</p>
<ul>
<li>配置主题</li>
<li>支持LaTeX</li>
<li>支持中文检索</li>
</ul>
<p>以上配置文件，需要搭配扩展模块才能使用。扩展模块同样我也给你准备好了，在你回复「Sphinx」后，获取压缩包后，里面有个 exts 文件夹。你只要将这个文件夹原封不动的放置在与source的同级目录下即可。</p>
<p>由于扩展模块会用到一些第三方依赖包，需要你去包装它。requirements.txt 同样我也给你准备好了，在压缩包里有。</p>
<p>你只要执行这个命令，即可安装。</p>
<pre tabindex="0"><code>pip install -r requirements.txt -i https://pypi.douban.com/simple/
</code></pre><h2 id="4-撰写文章">4. 撰写文章</h2>
<p>万事俱备，接下来要写文档了。</p>
<p>在source目录下，新增文件 how_to_be_a_rich_man.rst（至于什么是rst格式呢，请自行搜索引擎噢）</p>
<p>文件内容如下</p>
<pre tabindex="0"><code>第一章 如何成为有钱人
======================

1.1 财富继承法
---------------------

有个有钱的老爸。


1.2 财富共享法
---------------------

有个有钱的老婆。
</code></pre><p>写好文档后，千万记得要把这个文档写进，目录排版里面。</p>
<p>排版配置文件是 source\index.rst，千万要注意中间的空行不可忽略。</p>
<pre tabindex="0"><code>.. toctree::
   :maxdepth: 2
   :caption: Contents:

   how_to_be_a_rich_man
</code></pre><p>然后删除这几行吧，没啥用。</p>
<pre tabindex="0"><code>Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
</code></pre><p>然后执行make html 生成html静态文件。</p>
<pre tabindex="0"><code>F:\mkdocs
(mkdocs) λ make html
Running Sphinx v1.7.4
loading translations [zh_CN]... done
loading pickled environment... done
building [mo]: targets for 0 po files that are out of date
building [html]: targets for 2 source files that are out of date
updating environment: [extensions changed] 2 added, 0 changed, 0 removed
reading sources... [100%] index
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
writing output... [100%] index
generating indices... genindex
writing additional pages... search
copying static files... done
copying extra files... done
dumping search index in English (code: en) ... done
dumping object inventory... done
build succeeded.

The HTML pages are in build\html.
</code></pre><p>执行完了后，你可以发现原先的build，不再是空文件夹了。</p>
<p>我们点进去 build\html\ 目录，使用浏览器打开index.html文件。
<img src="http://image.iswbm.com/20190511161212.png"></p>
<p>真棒，已经完成了一半了。点击 我们刚写的 暴富指南。<br>
<img src="http://image.iswbm.com/20190511161240.png"></p>
<h2 id="5-托管项目">5. 托管项目</h2>
<p>看到网页的那一刻是不是相当激动。</p>
<p>不过别激动，这只是本地的，我们需要将其发布在线上。</p>
<p>这里我将工程文件，托管在GitHub上，然后由Read the Docs发布。</p>
<p>在托管之前呢，我们需要准备工作。在mkdocs根目录下，添加文件.gitignore（聪明的你，肯定知道这是什么），内容如下</p>
<pre tabindex="0"><code>build/
.idea/
*.pyc
</code></pre><p>接下来，在你的GitHub上新建一个仓库。然后把mkdocs这个目录下的所有文件都提交上去。步骤很简单，这里就不细讲了。</p>
<h2 id="6-发布上线">6. 发布上线</h2>
<p>托管完成后，我们要发布它，让别人也可以使用公网访问。</p>
<p>你需要先去 Read the Docs 注册下帐号。</p>
<p>关联一下GitHub<br>
<img src="http://image.iswbm.com/20190511161255.png"></p>
<p><img src="http://image.iswbm.com/20190511161311.png"></p>
<p>导入代码库。填好与你对应的信息。<br>
<img src="http://image.iswbm.com/20190511161334.png"></p>
<p><img src="http://image.iswbm.com/20190511161414.png"></p>
<p>构建网页后。右下方，你可以看见你的在线地址。</p>
<p><img src="http://image.iswbm.com/20190511161426.png"></p>
<p>这里要提醒一下的是，Sphinx的文档格式，默认是 rst 格式，如果你习惯了使用Markdown来写文章，可以使用 Pandoc 这个神器转换一下。</p>
<p>这里给出格式转换命令。</p>
<pre tabindex="0"><code>pandoc -V mainfont=&#34;SimSun&#34; -f markdown -t rst hello.md -o hello.rst
</code></pre><p>或者你也可以在 Sphinx 上添加支持 Markdown 渲染的扩展模块。这需要你自己去折腾了。</p>
<p>到这里，属于你的个人博客就搭建好了，快去试一下吧。</p>
<h2 id="附录参考文档">附录：参考文档</h2>
<ul>
<li><a href="http://www.sphinx-doc.org/en/master/usage/markdown.html">Sphinx配置MarkDown解析</a></li>
<li><a href="http://www.pythondoc.com/sphinx/contents.html">Sphinx使用手册(部分汉化)</a></li>
<li><a href="https://www.xncoding.com/2017/01/22/fullstack/readthedoc.html">搭建参考文章</a></li>
<li><a href="https://www.sphinx-doc.org/zh_CN/master/usage/configuration.html">Sphinx conf.py 配置详解</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>一个帮你快速编写装饰器神器 -- decorator</title>
      <link>https://iswbm.com/en/2024/06/01/a-tool-to-help-you-write-decorators-quickly/</link>
      <pubDate>Sat, 01 Jun 2024 20:26:48 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/01/a-tool-to-help-you-write-decorators-quickly/</guid>
      <description>今天介绍的是一个已经存在十年，但是依旧不红的库 decorator，好像很少有人知道他的存在一样。 这个库可以帮你做什么呢 ？ 其实很简单，就是可以</description>
      <content:encoded><![CDATA[<p>今天介绍的是一个已经存在十年，但是依旧不红的库 decorator，好像很少有人知道他的存在一样。</p>
<p>这个库可以帮你做什么呢 ？</p>
<p>其实很简单，就是可以帮你更方便地写python装饰器代码，更重要的是，它让 Python 中被装饰器装饰后的方法长得更像装饰前的方法。</p>
<p>本篇文章不会过多的向你介绍装饰器的基本知识，我会默认你知道什么是装饰器，并且懂得如何写一个简单的装饰器。</p>
<p>不了解装饰器的可以先去阅读我之前写的文章，非常全且详细的介绍了装饰器的各种实现方法。</p>
<h2 id="常规的装饰器">常规的装饰器</h2>
<p>下面这是一个最简单的装饰器示例，在运行 <code>myfunc</code> 函数的前后都会打印一条日志。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">deco</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Ready to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Successful to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">wrapper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@deco</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">myfunc</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Running the task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">myfunc</span><span class="p">()</span>
</span></span></code></pre></div><p>装饰器使用起来，似乎有些高端和魔幻，对于一些重复性的功能，往往我们会封装成一个装饰器函数。</p>
<p>在定义一个装饰器的时候，我们都需要像上面一样机械性的写一个嵌套的函数，对装饰器原理理解不深的初学者，往往过段时间就会忘记如何定义装饰器。</p>
<p>有一些比较聪明的同学，会利用 PyCharm 来自动生成装饰器模板</p>
<p><img src="http://image.iswbm.com/image-20210420211718252.png"></p>
<p>然后要使用的时候，直接敲入 <code>deco</code> 就会生成一个简单的生成器代码，提高编码的准备效率</p>
<p><img src="http://image.iswbm.com/deco.gif"></p>
<h2 id="使用神库">使用神库</h2>
<p>使用 PyCharm 的 Live Template ，虽然能降低编写装饰器的难度，但却要依赖 PyCharm 这一专业的代码编辑器。</p>
<p>这里，明哥要教你一个更加简单的方法，使用这个方法呢，你需要先安装一个库 ： <code>decorator</code>，使用 pip 可以很轻易地去安装它</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ python3 -m pip install decorator
</span></span></code></pre></div><p>从库的名称不难看出，这是一个专门用来解决装饰器问题的第三方库。</p>
<p>有了它之后，你会惊奇的发现，以后自己定义的装饰器，就再也不需要写嵌套的函数了</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">decorator</span> <span class="kn">import</span> <span class="n">decorator</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@decorator</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">deco</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Ready to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Successful to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@deco</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">myfunc</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Running the task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">myfunc</span><span class="p">()</span>
</span></span></code></pre></div><p>deco 作为装饰函数，第一个参数是固定的，都是指被装饰函数，而后面的参数都固定使用 可变参数 <code>*args</code> 和 <code>**kw</code> 的写法，代码被装饰函数的原参数。</p>
<p>这种写法，不得不说，更加符合直觉，代码的逻辑也更容易理解。</p>
<h2 id="带参数的装饰器">带参数的装饰器</h2>
<p>装饰器根据有没有携带参数，可以分为两种</p>
<p><strong>第一种</strong>：不带参数，最简单的示例，上面已经举例</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">wrapper</span>
</span></span></code></pre></div><p><strong>第二种</strong>：带参数，这就相对复杂了，理解起来了也不是那么容易。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">def</span> <span class="nf">deco</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">deco</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">wrapper</span>
</span></span></code></pre></div><p>那么对于需要带参数的装饰器，<code>decorator</code> 是否也一样能很好的支持呢？</p>
<p>下面是一个官方的示例</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">decorator</span> <span class="kn">import</span> <span class="n">decorator</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@decorator</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">warn_slow</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="n">timelimit</span><span class="o">=</span><span class="mi">60</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">t0</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">dt</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">t0</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">dt</span> <span class="o">&gt;</span> <span class="n">timelimit</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">logging</span><span class="o">.</span><span class="n">warn</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1"> took </span><span class="si">%d</span><span class="s1"> seconds&#39;</span><span class="p">,</span> <span class="n">func</span><span class="o">.</span><span class="vm">__name__</span><span class="p">,</span> <span class="n">dt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1"> took </span><span class="si">%d</span><span class="s1"> seconds&#39;</span><span class="p">,</span> <span class="n">func</span><span class="o">.</span><span class="vm">__name__</span><span class="p">,</span> <span class="n">dt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">result</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl"><span class="nd">@warn_slow</span><span class="p">(</span><span class="n">timelimit</span><span class="o">=</span><span class="mi">600</span><span class="p">)</span>  <span class="c1"># warn if it takes more than 10 minutes</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">run_calculation</span><span class="p">(</span><span class="n">tempdir</span><span class="p">,</span> <span class="n">outdir</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span></code></pre></div><p>可以看到</p>
<ul>
<li>装饰函数的第一个参数，还是被装饰器 func ，这个跟之前一样</li>
<li>而第二个参数 timelimit 写成了位置参数的写法，并且有默认值</li>
<li>再往后，就还是跟原来一样使用了可变参数的写法</li>
</ul>
<p>不难推断，只要你在装饰函数中第二个参数开始，使用了非可变参数的写法，这些参数就可以做为装饰器调用时的参数。</p>
<h2 id="签名问题有解决">签名问题有解决？</h2>
<p>我们在自己写装饰器的时候，通常都会顺手加上一个叫 <code>functools.wraps</code> 的装饰器，我想你应该也经常见过，那他有啥用呢？</p>
<p>先来看一个例子</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">inner_function</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">pass</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">inner_function</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@wrapper</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapped</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">wrapped</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#inner_function</span>
</span></span></code></pre></div><p>为什么会这样子？不是应该返回 <code>func </code> 吗？</p>
<p>这也不难理解，因为上边执行<code>func</code> 和下边 <code>decorator(func)</code>  是等价的，所以上面 <code>func.__name__</code> 是等价于下面<code>decorator(func).__name__</code> 的，那当然名字是 <code>inner_function</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">inner_function</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">pass</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">inner_function</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapped</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">wrapper</span><span class="p">(</span><span class="n">wrapped</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#inner_function</span>
</span></span></code></pre></div><p>目前，我们可以看到当一个函数被装饰器装饰过后，它的签名信息会发生变化（譬如上面看到的函数名）</p>
<p>那如何避免这种情况的产生？</p>
<p><strong>解决方案就是使用我们前面所说的 functools .wraps 装饰器。</strong></p>
<p>它的作用就是将 被修饰的函数(wrapped) 的一些属性值赋值给 修饰器函数(wrapper) ，最终让属性的显示更符合我们的直觉。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">inner_function</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">pass</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">inner_function</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@wrapper</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapped</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">wrapped</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># wrapped</span>
</span></span></code></pre></div><p>那么问题就来了，我们使用了 decorator 之后，是否还会存在这种签名的问题呢？</p>
<p>写个例子来验证一下就知道啦</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">decorator</span> <span class="kn">import</span> <span class="n">decorator</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@decorator</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">deco</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Ready to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Successful to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@deco</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">myfunc</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Running the task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">myfunc</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span>
</span></span></code></pre></div><p>输出的结果是 <code>myfunc</code>，说明 <code>decorator</code> 已经默认帮我们处理了一切可预见的问题。</p>
<h2 id="总结一下">总结一下</h2>
<p><code>decorator</code> 是一个提高装饰器编码效率的第三方库，它适用于对装饰器原理感到困惑的新手，可以让你很轻易的写出更符合人类直觉的代码。对于带参数装饰器的定义，是非常复杂的，它需要要写多层的嵌套函数，并且需要你熟悉各个参数的传递路径，才能保证你写出来的装饰器可以正常使用。这时候，只要用上 <code>decorator</code> 这个库，你就可以很轻松的写出一个带参数的装饰器。同时你也不用担心他会出现签名问题，这些它都为你妥善的处理好了。</p>
<p>这么棒的一个库，推荐你使用起来。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>一个帮你快速编写装饰器神器 -- decorator</title>
      <link>https://iswbm.com/en/2024/06/01/a-tool-to-help-you-write-decorators-quickly/</link>
      <pubDate>Sat, 01 Jun 2024 20:26:48 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/01/a-tool-to-help-you-write-decorators-quickly/</guid>
      <description>今天介绍的是一个已经存在十年，但是依旧不红的库 decorator，好像很少有人知道他的存在一样。 这个库可以帮你做什么呢 ？ 其实很简单，就是可以</description>
      <content:encoded><![CDATA[<p>今天介绍的是一个已经存在十年，但是依旧不红的库 decorator，好像很少有人知道他的存在一样。</p>
<p>这个库可以帮你做什么呢 ？</p>
<p>其实很简单，就是可以帮你更方便地写python装饰器代码，更重要的是，它让 Python 中被装饰器装饰后的方法长得更像装饰前的方法。</p>
<p>本篇文章不会过多的向你介绍装饰器的基本知识，我会默认你知道什么是装饰器，并且懂得如何写一个简单的装饰器。</p>
<p>不了解装饰器的可以先去阅读我之前写的文章，非常全且详细的介绍了装饰器的各种实现方法。</p>
<h2 id="常规的装饰器">常规的装饰器</h2>
<p>下面这是一个最简单的装饰器示例，在运行 <code>myfunc</code> 函数的前后都会打印一条日志。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">deco</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Ready to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Successful to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">wrapper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@deco</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">myfunc</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Running the task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">myfunc</span><span class="p">()</span>
</span></span></code></pre></div><p>装饰器使用起来，似乎有些高端和魔幻，对于一些重复性的功能，往往我们会封装成一个装饰器函数。</p>
<p>在定义一个装饰器的时候，我们都需要像上面一样机械性的写一个嵌套的函数，对装饰器原理理解不深的初学者，往往过段时间就会忘记如何定义装饰器。</p>
<p>有一些比较聪明的同学，会利用 PyCharm 来自动生成装饰器模板</p>
<p><img src="http://image.iswbm.com/image-20210420211718252.png"></p>
<p>然后要使用的时候，直接敲入 <code>deco</code> 就会生成一个简单的生成器代码，提高编码的准备效率</p>
<p><img src="http://image.iswbm.com/deco.gif"></p>
<h2 id="使用神库">使用神库</h2>
<p>使用 PyCharm 的 Live Template ，虽然能降低编写装饰器的难度，但却要依赖 PyCharm 这一专业的代码编辑器。</p>
<p>这里，明哥要教你一个更加简单的方法，使用这个方法呢，你需要先安装一个库 ： <code>decorator</code>，使用 pip 可以很轻易地去安装它</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ python3 -m pip install decorator
</span></span></code></pre></div><p>从库的名称不难看出，这是一个专门用来解决装饰器问题的第三方库。</p>
<p>有了它之后，你会惊奇的发现，以后自己定义的装饰器，就再也不需要写嵌套的函数了</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">decorator</span> <span class="kn">import</span> <span class="n">decorator</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@decorator</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">deco</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Ready to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Successful to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@deco</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">myfunc</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Running the task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">myfunc</span><span class="p">()</span>
</span></span></code></pre></div><p>deco 作为装饰函数，第一个参数是固定的，都是指被装饰函数，而后面的参数都固定使用 可变参数 <code>*args</code> 和 <code>**kw</code> 的写法，代码被装饰函数的原参数。</p>
<p>这种写法，不得不说，更加符合直觉，代码的逻辑也更容易理解。</p>
<h2 id="带参数的装饰器">带参数的装饰器</h2>
<p>装饰器根据有没有携带参数，可以分为两种</p>
<p><strong>第一种</strong>：不带参数，最简单的示例，上面已经举例</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">wrapper</span>
</span></span></code></pre></div><p><strong>第二种</strong>：带参数，这就相对复杂了，理解起来了也不是那么容易。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">def</span> <span class="nf">deco</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">deco</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">wrapper</span>
</span></span></code></pre></div><p>那么对于需要带参数的装饰器，<code>decorator</code> 是否也一样能很好的支持呢？</p>
<p>下面是一个官方的示例</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">decorator</span> <span class="kn">import</span> <span class="n">decorator</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@decorator</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">warn_slow</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="n">timelimit</span><span class="o">=</span><span class="mi">60</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">t0</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">dt</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">t0</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">dt</span> <span class="o">&gt;</span> <span class="n">timelimit</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">logging</span><span class="o">.</span><span class="n">warn</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1"> took </span><span class="si">%d</span><span class="s1"> seconds&#39;</span><span class="p">,</span> <span class="n">func</span><span class="o">.</span><span class="vm">__name__</span><span class="p">,</span> <span class="n">dt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1"> took </span><span class="si">%d</span><span class="s1"> seconds&#39;</span><span class="p">,</span> <span class="n">func</span><span class="o">.</span><span class="vm">__name__</span><span class="p">,</span> <span class="n">dt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">result</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl"><span class="nd">@warn_slow</span><span class="p">(</span><span class="n">timelimit</span><span class="o">=</span><span class="mi">600</span><span class="p">)</span>  <span class="c1"># warn if it takes more than 10 minutes</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">run_calculation</span><span class="p">(</span><span class="n">tempdir</span><span class="p">,</span> <span class="n">outdir</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span></code></pre></div><p>可以看到</p>
<ul>
<li>装饰函数的第一个参数，还是被装饰器 func ，这个跟之前一样</li>
<li>而第二个参数 timelimit 写成了位置参数的写法，并且有默认值</li>
<li>再往后，就还是跟原来一样使用了可变参数的写法</li>
</ul>
<p>不难推断，只要你在装饰函数中第二个参数开始，使用了非可变参数的写法，这些参数就可以做为装饰器调用时的参数。</p>
<h2 id="签名问题有解决">签名问题有解决？</h2>
<p>我们在自己写装饰器的时候，通常都会顺手加上一个叫 <code>functools.wraps</code> 的装饰器，我想你应该也经常见过，那他有啥用呢？</p>
<p>先来看一个例子</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">inner_function</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">pass</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">inner_function</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@wrapper</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapped</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">wrapped</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#inner_function</span>
</span></span></code></pre></div><p>为什么会这样子？不是应该返回 <code>func </code> 吗？</p>
<p>这也不难理解，因为上边执行<code>func</code> 和下边 <code>decorator(func)</code>  是等价的，所以上面 <code>func.__name__</code> 是等价于下面<code>decorator(func).__name__</code> 的，那当然名字是 <code>inner_function</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">inner_function</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">pass</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">inner_function</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapped</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">wrapper</span><span class="p">(</span><span class="n">wrapped</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#inner_function</span>
</span></span></code></pre></div><p>目前，我们可以看到当一个函数被装饰器装饰过后，它的签名信息会发生变化（譬如上面看到的函数名）</p>
<p>那如何避免这种情况的产生？</p>
<p><strong>解决方案就是使用我们前面所说的 functools .wraps 装饰器。</strong></p>
<p>它的作用就是将 被修饰的函数(wrapped) 的一些属性值赋值给 修饰器函数(wrapper) ，最终让属性的显示更符合我们的直觉。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">inner_function</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">pass</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">inner_function</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@wrapper</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapped</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">wrapped</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># wrapped</span>
</span></span></code></pre></div><p>那么问题就来了，我们使用了 decorator 之后，是否还会存在这种签名的问题呢？</p>
<p>写个例子来验证一下就知道啦</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">decorator</span> <span class="kn">import</span> <span class="n">decorator</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@decorator</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">deco</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Ready to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Successful to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@deco</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">myfunc</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Running the task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">myfunc</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span>
</span></span></code></pre></div><p>输出的结果是 <code>myfunc</code>，说明 <code>decorator</code> 已经默认帮我们处理了一切可预见的问题。</p>
<h2 id="总结一下">总结一下</h2>
<p><code>decorator</code> 是一个提高装饰器编码效率的第三方库，它适用于对装饰器原理感到困惑的新手，可以让你很轻易的写出更符合人类直觉的代码。对于带参数装饰器的定义，是非常复杂的，它需要要写多层的嵌套函数，并且需要你熟悉各个参数的传递路径，才能保证你写出来的装饰器可以正常使用。这时候，只要用上 <code>decorator</code> 这个库，你就可以很轻松的写出一个带参数的装饰器。同时你也不用担心他会出现签名问题，这些它都为你妥善的处理好了。</p>
<p>这么棒的一个库，推荐你使用起来。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>一个帮你快速编写装饰器神器 -- decorator</title>
      <link>https://iswbm.com/en/2024/06/01/a-tool-to-help-you-write-decorators-quickly/</link>
      <pubDate>Sat, 01 Jun 2024 20:26:48 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/01/a-tool-to-help-you-write-decorators-quickly/</guid>
      <description>今天介绍的是一个已经存在十年，但是依旧不红的库 decorator，好像很少有人知道他的存在一样。 这个库可以帮你做什么呢 ？ 其实很简单，就是可以</description>
      <content:encoded><![CDATA[<p>今天介绍的是一个已经存在十年，但是依旧不红的库 decorator，好像很少有人知道他的存在一样。</p>
<p>这个库可以帮你做什么呢 ？</p>
<p>其实很简单，就是可以帮你更方便地写python装饰器代码，更重要的是，它让 Python 中被装饰器装饰后的方法长得更像装饰前的方法。</p>
<p>本篇文章不会过多的向你介绍装饰器的基本知识，我会默认你知道什么是装饰器，并且懂得如何写一个简单的装饰器。</p>
<p>不了解装饰器的可以先去阅读我之前写的文章，非常全且详细的介绍了装饰器的各种实现方法。</p>
<h2 id="常规的装饰器">常规的装饰器</h2>
<p>下面这是一个最简单的装饰器示例，在运行 <code>myfunc</code> 函数的前后都会打印一条日志。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">deco</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Ready to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Successful to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">wrapper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@deco</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">myfunc</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Running the task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">myfunc</span><span class="p">()</span>
</span></span></code></pre></div><p>装饰器使用起来，似乎有些高端和魔幻，对于一些重复性的功能，往往我们会封装成一个装饰器函数。</p>
<p>在定义一个装饰器的时候，我们都需要像上面一样机械性的写一个嵌套的函数，对装饰器原理理解不深的初学者，往往过段时间就会忘记如何定义装饰器。</p>
<p>有一些比较聪明的同学，会利用 PyCharm 来自动生成装饰器模板</p>
<p><img src="http://image.iswbm.com/image-20210420211718252.png"></p>
<p>然后要使用的时候，直接敲入 <code>deco</code> 就会生成一个简单的生成器代码，提高编码的准备效率</p>
<p><img src="http://image.iswbm.com/deco.gif"></p>
<h2 id="使用神库">使用神库</h2>
<p>使用 PyCharm 的 Live Template ，虽然能降低编写装饰器的难度，但却要依赖 PyCharm 这一专业的代码编辑器。</p>
<p>这里，明哥要教你一个更加简单的方法，使用这个方法呢，你需要先安装一个库 ： <code>decorator</code>，使用 pip 可以很轻易地去安装它</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">$ python3 -m pip install decorator
</span></span></code></pre></div><p>从库的名称不难看出，这是一个专门用来解决装饰器问题的第三方库。</p>
<p>有了它之后，你会惊奇的发现，以后自己定义的装饰器，就再也不需要写嵌套的函数了</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">decorator</span> <span class="kn">import</span> <span class="n">decorator</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@decorator</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">deco</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Ready to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Successful to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@deco</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">myfunc</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Running the task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">myfunc</span><span class="p">()</span>
</span></span></code></pre></div><p>deco 作为装饰函数，第一个参数是固定的，都是指被装饰函数，而后面的参数都固定使用 可变参数 <code>*args</code> 和 <code>**kw</code> 的写法，代码被装饰函数的原参数。</p>
<p>这种写法，不得不说，更加符合直觉，代码的逻辑也更容易理解。</p>
<h2 id="带参数的装饰器">带参数的装饰器</h2>
<p>装饰器根据有没有携带参数，可以分为两种</p>
<p><strong>第一种</strong>：不带参数，最简单的示例，上面已经举例</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">wrapper</span>
</span></span></code></pre></div><p><strong>第二种</strong>：带参数，这就相对复杂了，理解起来了也不是那么容易。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">arg1</span><span class="p">,</span> <span class="n">arg2</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">def</span> <span class="nf">deco</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">deco</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">wrapper</span>
</span></span></code></pre></div><p>那么对于需要带参数的装饰器，<code>decorator</code> 是否也一样能很好的支持呢？</p>
<p>下面是一个官方的示例</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">decorator</span> <span class="kn">import</span> <span class="n">decorator</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@decorator</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">warn_slow</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="n">timelimit</span><span class="o">=</span><span class="mi">60</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">t0</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">dt</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">t0</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">dt</span> <span class="o">&gt;</span> <span class="n">timelimit</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">logging</span><span class="o">.</span><span class="n">warn</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1"> took </span><span class="si">%d</span><span class="s1"> seconds&#39;</span><span class="p">,</span> <span class="n">func</span><span class="o">.</span><span class="vm">__name__</span><span class="p">,</span> <span class="n">dt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">logging</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">&#39;</span><span class="si">%s</span><span class="s1"> took </span><span class="si">%d</span><span class="s1"> seconds&#39;</span><span class="p">,</span> <span class="n">func</span><span class="o">.</span><span class="vm">__name__</span><span class="p">,</span> <span class="n">dt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">result</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl"><span class="nd">@warn_slow</span><span class="p">(</span><span class="n">timelimit</span><span class="o">=</span><span class="mi">600</span><span class="p">)</span>  <span class="c1"># warn if it takes more than 10 minutes</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">run_calculation</span><span class="p">(</span><span class="n">tempdir</span><span class="p">,</span> <span class="n">outdir</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span></code></pre></div><p>可以看到</p>
<ul>
<li>装饰函数的第一个参数，还是被装饰器 func ，这个跟之前一样</li>
<li>而第二个参数 timelimit 写成了位置参数的写法，并且有默认值</li>
<li>再往后，就还是跟原来一样使用了可变参数的写法</li>
</ul>
<p>不难推断，只要你在装饰函数中第二个参数开始，使用了非可变参数的写法，这些参数就可以做为装饰器调用时的参数。</p>
<h2 id="签名问题有解决">签名问题有解决？</h2>
<p>我们在自己写装饰器的时候，通常都会顺手加上一个叫 <code>functools.wraps</code> 的装饰器，我想你应该也经常见过，那他有啥用呢？</p>
<p>先来看一个例子</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">inner_function</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">pass</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">inner_function</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@wrapper</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapped</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">wrapped</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#inner_function</span>
</span></span></code></pre></div><p>为什么会这样子？不是应该返回 <code>func </code> 吗？</p>
<p>这也不难理解，因为上边执行<code>func</code> 和下边 <code>decorator(func)</code>  是等价的，所以上面 <code>func.__name__</code> 是等价于下面<code>decorator(func).__name__</code> 的，那当然名字是 <code>inner_function</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">inner_function</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">pass</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">inner_function</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapped</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">wrapper</span><span class="p">(</span><span class="n">wrapped</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">#inner_function</span>
</span></span></code></pre></div><p>目前，我们可以看到当一个函数被装饰器装饰过后，它的签名信息会发生变化（譬如上面看到的函数名）</p>
<p>那如何避免这种情况的产生？</p>
<p><strong>解决方案就是使用我们前面所说的 functools .wraps 装饰器。</strong></p>
<p>它的作用就是将 被修饰的函数(wrapped) 的一些属性值赋值给 修饰器函数(wrapper) ，最终让属性的显示更符合我们的直觉。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">inner_function</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">pass</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">inner_function</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@wrapper</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wrapped</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">wrapped</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># wrapped</span>
</span></span></code></pre></div><p>那么问题就来了，我们使用了 decorator 之后，是否还会存在这种签名的问题呢？</p>
<p>写个例子来验证一下就知道啦</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">decorator</span> <span class="kn">import</span> <span class="n">decorator</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@decorator</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">deco</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Ready to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Successful to run task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@deco</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">myfunc</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="s2">&#34;Running the task&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">myfunc</span><span class="o">.</span><span class="vm">__name__</span><span class="p">)</span>
</span></span></code></pre></div><p>输出的结果是 <code>myfunc</code>，说明 <code>decorator</code> 已经默认帮我们处理了一切可预见的问题。</p>
<h2 id="总结一下">总结一下</h2>
<p><code>decorator</code> 是一个提高装饰器编码效率的第三方库，它适用于对装饰器原理感到困惑的新手，可以让你很轻易的写出更符合人类直觉的代码。对于带参数装饰器的定义，是非常复杂的，它需要要写多层的嵌套函数，并且需要你熟悉各个参数的传递路径，才能保证你写出来的装饰器可以正常使用。这时候，只要用上 <code>decorator</code> 这个库，你就可以很轻松的写出一个带参数的装饰器。同时你也不用担心他会出现签名问题，这些它都为你妥善的处理好了。</p>
<p>这么棒的一个库，推荐你使用起来。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>一些有关Python 包与模块的知识点</title>
      <link>https://iswbm.com/en/2024/06/01/some-things-to-know-about-python-packages-and-modules/</link>
      <pubDate>Sat, 01 Jun 2024 20:26:48 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/01/some-things-to-know-about-python-packages-and-modules/</guid>
      <description>1. 使用 all 控制可被导入的变量 使用 from module import * 默认情况下会导入 module 里的所有变量，若你只想从模块中导入其中几个变量，可以在 module 中使用 __all__ 来控制想要被其他模块</description>
      <content:encoded><![CDATA[<h2 id="1-使用-__all__--控制可被导入的变量">1. 使用 <strong>all</strong>  控制可被导入的变量</h2>
<p>使用 <code>from module import *</code>  默认情况下会导入 module 里的所有变量，若你只想从模块中导入其中几个变量，可以在 module 中使用 <code>__all__</code> 来控制想要被其他模块导入的变量。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># profile.py</span>
</span></span><span class="line"><span class="cl"><span class="n">name</span><span class="o">=</span><span class="s1">&#39;wangbm&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">age</span><span class="o">=</span><span class="mi">27</span>
</span></span><span class="line"><span class="cl"><span class="n">gender</span><span class="o">=</span><span class="s1">&#39;male&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">__all__</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;name&#39;</span><span class="p">]</span>
</span></span></code></pre></div><p>打开 python console 验证一下</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">profile</span> <span class="kn">import</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">wangbm</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">age</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="n">File</span> <span class="s2">&#34;&lt;stdin&gt;&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="ne">NameError</span><span class="p">:</span> <span class="n">name</span> <span class="s1">&#39;age&#39;</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">defined</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">gender</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="n">File</span> <span class="s2">&#34;&lt;stdin&gt;&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="ne">NameError</span><span class="p">:</span> <span class="n">name</span> <span class="s1">&#39;gender&#39;</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">defined</span>
</span></span></code></pre></div><p><code>__all__</code> 仅对于使用<code>from module import *</code>  这种情况适用。</p>
<p>它经常在一个包的 <code>__init__.py</code> 中出现。</p>
<h2 id="2-命名空间包的神奇之处">2. 命名空间包的神奇之处</h2>
<p>命名空间包，一个陌生的名字。</p>
<p>与我们熟悉的常规包不同的是，它没有 <code>__init__.py</code> 文件。</p>
<p>更为特殊的是，它可以跨空间地将两个不相邻的子包，合并成一个虚拟机的包，我们将其称之为 <code>命名空间包</code>。</p>
<p>例如，一个项目的部分代码布局如下</p>
<pre tabindex="0"><code>foo-package/
    spam/
        blah.py

bar-package/
    spam/
        grok.py
</code></pre><p>在这2个目录里，都有着共同的命名空间spam。在任何一个目录里都没有__init__.py文件。</p>
<p>让我们看看，如果将foo-package和bar-package都加到python模块路径并尝试导入会发生什么？</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="s1">&#39;foo-package&#39;</span><span class="p">,</span> <span class="s1">&#39;bar-package&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">spam.blah</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">spam.grok</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span>
</span></span></code></pre></div><p>当一个包为命名空间包时，他就不再和常规包一样具有 <code>__file_</code> 属性，取而代之的是 <code>__path__</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="s1">&#39;foo-package&#39;</span><span class="p">,</span> <span class="s1">&#39;bar-package&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">spam.blah</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">spam.grok</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">spam</span><span class="o">.</span><span class="n">__path__</span>
</span></span><span class="line"><span class="cl"><span class="n">_NamespacePath</span><span class="p">([</span><span class="s1">&#39;foo-package/spam&#39;</span><span class="p">,</span> <span class="s1">&#39;bar-package/spam&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">spam</span><span class="o">.</span><span class="vm">__file__</span>
</span></span><span class="line"><span class="cl"><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">File</span> <span class="s2">&#34;&lt;stdin&gt;&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="ne">AttributeError</span><span class="p">:</span> <span class="s1">&#39;module&#39;</span> <span class="nb">object</span> <span class="n">has</span> <span class="n">no</span> <span class="n">attribute</span> <span class="s1">&#39;__file__&#39;</span>
</span></span></code></pre></div><h2 id="3-包重载就是一个坑">3. 包重载就是一个坑</h2>
<p>第一种方法</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">spam</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">imp</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">imp</span><span class="o">.</span><span class="n">reload</span><span class="p">(</span><span class="n">spam</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="n">module</span> <span class="s1">&#39;spam&#39;</span> <span class="kn">from</span> <span class="s1">&#39;./spam.py&#39;</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span>
</span></span></code></pre></div><p>由于这种重载方法，只对 <code>import module</code> 有效，而使用 <code>from module import arg</code> 导入的 arg 并不会刷新。</p>
<p>因此，在生产环境中可能需要避免重新加载模块。而在调试模式中，它会提供一定的便利，但你要知道这个重载的弊端，以免掉入坑里。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>一些有关Python 包与模块的知识点</title>
      <link>https://iswbm.com/en/2024/06/01/some-things-to-know-about-python-packages-and-modules/</link>
      <pubDate>Sat, 01 Jun 2024 20:26:48 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/01/some-things-to-know-about-python-packages-and-modules/</guid>
      <description>1. 使用 all 控制可被导入的变量 使用 from module import * 默认情况下会导入 module 里的所有变量，若你只想从模块中导入其中几个变量，可以在 module 中使用 __all__ 来控制想要被其他模块</description>
      <content:encoded><![CDATA[<h2 id="1-使用-__all__--控制可被导入的变量">1. 使用 <strong>all</strong>  控制可被导入的变量</h2>
<p>使用 <code>from module import *</code>  默认情况下会导入 module 里的所有变量，若你只想从模块中导入其中几个变量，可以在 module 中使用 <code>__all__</code> 来控制想要被其他模块导入的变量。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># profile.py</span>
</span></span><span class="line"><span class="cl"><span class="n">name</span><span class="o">=</span><span class="s1">&#39;wangbm&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">age</span><span class="o">=</span><span class="mi">27</span>
</span></span><span class="line"><span class="cl"><span class="n">gender</span><span class="o">=</span><span class="s1">&#39;male&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">__all__</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;name&#39;</span><span class="p">]</span>
</span></span></code></pre></div><p>打开 python console 验证一下</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">profile</span> <span class="kn">import</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">wangbm</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">age</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="n">File</span> <span class="s2">&#34;&lt;stdin&gt;&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="ne">NameError</span><span class="p">:</span> <span class="n">name</span> <span class="s1">&#39;age&#39;</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">defined</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">gender</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="n">File</span> <span class="s2">&#34;&lt;stdin&gt;&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="ne">NameError</span><span class="p">:</span> <span class="n">name</span> <span class="s1">&#39;gender&#39;</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">defined</span>
</span></span></code></pre></div><p><code>__all__</code> 仅对于使用<code>from module import *</code>  这种情况适用。</p>
<p>它经常在一个包的 <code>__init__.py</code> 中出现。</p>
<h2 id="2-命名空间包的神奇之处">2. 命名空间包的神奇之处</h2>
<p>命名空间包，一个陌生的名字。</p>
<p>与我们熟悉的常规包不同的是，它没有 <code>__init__.py</code> 文件。</p>
<p>更为特殊的是，它可以跨空间地将两个不相邻的子包，合并成一个虚拟机的包，我们将其称之为 <code>命名空间包</code>。</p>
<p>例如，一个项目的部分代码布局如下</p>
<pre tabindex="0"><code>foo-package/
    spam/
        blah.py

bar-package/
    spam/
        grok.py
</code></pre><p>在这2个目录里，都有着共同的命名空间spam。在任何一个目录里都没有__init__.py文件。</p>
<p>让我们看看，如果将foo-package和bar-package都加到python模块路径并尝试导入会发生什么？</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="s1">&#39;foo-package&#39;</span><span class="p">,</span> <span class="s1">&#39;bar-package&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">spam.blah</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">spam.grok</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span>
</span></span></code></pre></div><p>当一个包为命名空间包时，他就不再和常规包一样具有 <code>__file_</code> 属性，取而代之的是 <code>__path__</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="s1">&#39;foo-package&#39;</span><span class="p">,</span> <span class="s1">&#39;bar-package&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">spam.blah</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">spam.grok</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">spam</span><span class="o">.</span><span class="n">__path__</span>
</span></span><span class="line"><span class="cl"><span class="n">_NamespacePath</span><span class="p">([</span><span class="s1">&#39;foo-package/spam&#39;</span><span class="p">,</span> <span class="s1">&#39;bar-package/spam&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">spam</span><span class="o">.</span><span class="vm">__file__</span>
</span></span><span class="line"><span class="cl"><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">File</span> <span class="s2">&#34;&lt;stdin&gt;&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="ne">AttributeError</span><span class="p">:</span> <span class="s1">&#39;module&#39;</span> <span class="nb">object</span> <span class="n">has</span> <span class="n">no</span> <span class="n">attribute</span> <span class="s1">&#39;__file__&#39;</span>
</span></span></code></pre></div><h2 id="3-包重载就是一个坑">3. 包重载就是一个坑</h2>
<p>第一种方法</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">spam</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">imp</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">imp</span><span class="o">.</span><span class="n">reload</span><span class="p">(</span><span class="n">spam</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="n">module</span> <span class="s1">&#39;spam&#39;</span> <span class="kn">from</span> <span class="s1">&#39;./spam.py&#39;</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span>
</span></span></code></pre></div><p>由于这种重载方法，只对 <code>import module</code> 有效，而使用 <code>from module import arg</code> 导入的 arg 并不会刷新。</p>
<p>因此，在生产环境中可能需要避免重新加载模块。而在调试模式中，它会提供一定的便利，但你要知道这个重载的弊端，以免掉入坑里。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>一些有关Python 包与模块的知识点</title>
      <link>https://iswbm.com/en/2024/06/01/some-things-to-know-about-python-packages-and-modules/</link>
      <pubDate>Sat, 01 Jun 2024 20:26:48 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/01/some-things-to-know-about-python-packages-and-modules/</guid>
      <description>1. 使用 all 控制可被导入的变量 使用 from module import * 默认情况下会导入 module 里的所有变量，若你只想从模块中导入其中几个变量，可以在 module 中使用 __all__ 来控制想要被其他模块</description>
      <content:encoded><![CDATA[<h2 id="1-使用-__all__--控制可被导入的变量">1. 使用 <strong>all</strong>  控制可被导入的变量</h2>
<p>使用 <code>from module import *</code>  默认情况下会导入 module 里的所有变量，若你只想从模块中导入其中几个变量，可以在 module 中使用 <code>__all__</code> 来控制想要被其他模块导入的变量。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># profile.py</span>
</span></span><span class="line"><span class="cl"><span class="n">name</span><span class="o">=</span><span class="s1">&#39;wangbm&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">age</span><span class="o">=</span><span class="mi">27</span>
</span></span><span class="line"><span class="cl"><span class="n">gender</span><span class="o">=</span><span class="s1">&#39;male&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">__all__</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;name&#39;</span><span class="p">]</span>
</span></span></code></pre></div><p>打开 python console 验证一下</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">profile</span> <span class="kn">import</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">wangbm</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">age</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="n">File</span> <span class="s2">&#34;&lt;stdin&gt;&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="ne">NameError</span><span class="p">:</span> <span class="n">name</span> <span class="s1">&#39;age&#39;</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">defined</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">gender</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="n">File</span> <span class="s2">&#34;&lt;stdin&gt;&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="ne">NameError</span><span class="p">:</span> <span class="n">name</span> <span class="s1">&#39;gender&#39;</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">defined</span>
</span></span></code></pre></div><p><code>__all__</code> 仅对于使用<code>from module import *</code>  这种情况适用。</p>
<p>它经常在一个包的 <code>__init__.py</code> 中出现。</p>
<h2 id="2-命名空间包的神奇之处">2. 命名空间包的神奇之处</h2>
<p>命名空间包，一个陌生的名字。</p>
<p>与我们熟悉的常规包不同的是，它没有 <code>__init__.py</code> 文件。</p>
<p>更为特殊的是，它可以跨空间地将两个不相邻的子包，合并成一个虚拟机的包，我们将其称之为 <code>命名空间包</code>。</p>
<p>例如，一个项目的部分代码布局如下</p>
<pre tabindex="0"><code>foo-package/
    spam/
        blah.py

bar-package/
    spam/
        grok.py
</code></pre><p>在这2个目录里，都有着共同的命名空间spam。在任何一个目录里都没有__init__.py文件。</p>
<p>让我们看看，如果将foo-package和bar-package都加到python模块路径并尝试导入会发生什么？</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="s1">&#39;foo-package&#39;</span><span class="p">,</span> <span class="s1">&#39;bar-package&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">spam.blah</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">spam.grok</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span>
</span></span></code></pre></div><p>当一个包为命名空间包时，他就不再和常规包一样具有 <code>__file_</code> 属性，取而代之的是 <code>__path__</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="s1">&#39;foo-package&#39;</span><span class="p">,</span> <span class="s1">&#39;bar-package&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">spam.blah</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">spam.grok</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">spam</span><span class="o">.</span><span class="n">__path__</span>
</span></span><span class="line"><span class="cl"><span class="n">_NamespacePath</span><span class="p">([</span><span class="s1">&#39;foo-package/spam&#39;</span><span class="p">,</span> <span class="s1">&#39;bar-package/spam&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">spam</span><span class="o">.</span><span class="vm">__file__</span>
</span></span><span class="line"><span class="cl"><span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">File</span> <span class="s2">&#34;&lt;stdin&gt;&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="ne">AttributeError</span><span class="p">:</span> <span class="s1">&#39;module&#39;</span> <span class="nb">object</span> <span class="n">has</span> <span class="n">no</span> <span class="n">attribute</span> <span class="s1">&#39;__file__&#39;</span>
</span></span></code></pre></div><h2 id="3-包重载就是一个坑">3. 包重载就是一个坑</h2>
<p>第一种方法</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">spam</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">imp</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">imp</span><span class="o">.</span><span class="n">reload</span><span class="p">(</span><span class="n">spam</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="n">module</span> <span class="s1">&#39;spam&#39;</span> <span class="kn">from</span> <span class="s1">&#39;./spam.py&#39;</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span>
</span></span></code></pre></div><p>由于这种重载方法，只对 <code>import module</code> 有效，而使用 <code>from module import arg</code> 导入的 arg 并不会刷新。</p>
<p>因此，在生产环境中可能需要避免重新加载模块。而在调试模式中，它会提供一定的便利，但你要知道这个重载的弊端，以免掉入坑里。</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>在 Python 中理解与使用单例模式</title>
      <link>https://iswbm.com/en/2024/06/01/understanding-and-using-the-singleton-pattern-in-python/</link>
      <pubDate>Sat, 01 Jun 2024 20:26:48 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/01/understanding-and-using-the-singleton-pattern-in-python/</guid>
      <description>单例模式一种常见的设计模式，并且也是最基础的设计模式，需要每个开发人员都熟练掌握。 当你希望在整个系统中，某个类只能出现一个实例时，就需要学会</description>
      <content:encoded><![CDATA[<p>单例模式一种常见的设计模式，并且也是最基础的设计模式，需要每个开发人员都熟练掌握。</p>
<p>当你希望在整个系统中，某个类只能出现一个实例时，就需要学会使用单例模式。</p>
<p>比较常见的场景是：某个项目的配置信息存放在一个配置文件中，通过一个 <code>Config</code>​ 的类来读取配置文件的信息。如果在程序运行期间，有很多地方都需要使用配置文件的内容，也就是说，很多地方都需要创建 <code>Config</code>​ 对象的实例，这就导致系统中存在多个 <code>Config</code>​ 的实例对象，而这样会严重浪费内存资源，尤其是在配置文件内容很多的情况下。事实上，类似 <code>Config</code>​ 这样的类，我们希望在程序运行期间只存在一个实例对象。</p>
<p>在 Python 中，可以用多种方法实现单例模式，常见的有：</p>
<ul>
<li>通过使用模块</li>
<li>通过装饰器</li>
<li>通过__new__方法</li>
<li>通过元类</li>
</ul>
<p><strong>不同的方法，有不同的坑，对 Python 机制认识不够的同学，很容易踩到坑，本文用实例来详细说明一下这几种方法的区别，以及那些不容易被察觉的坑点，给出最佳的选择方案。</strong></p>
<p>下面就详细说明以下这几种实现方法：</p>
<h2 id="1-通过使用模块">1. 通过使用模块</h2>
<p><strong>在Python中，Python 的模块就是天然的单例模式。</strong></p>
<p>因为模块在第一次导入时，会生成 <code>.pyc</code>​ 文件，当第二次导入时，就会直接加载 <code>.pyc</code>​ 文件，而不会再次执行模块代码。因此，我们只需把相关的函数和数据定义在一个模块中，就可以获得一个单例对象了。</p>
<p>看如下示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># 例如在config.py文件中定义Config类和实例</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">LOG_LEVEL</span> <span class="o">=</span> <span class="s1">&#39;INFO&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 然后在其他文件中，通过import导入此实例</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">config</span> <span class="kn">import</span> <span class="n">config</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">LOG_LEVEL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 输出结果：INFO</span>
</span></span></code></pre></div><p>如上所示，通过在文件中先生成一个类实例，然后在别的文件中直接导入这个类实例，就实现了单例模式。</p>
<h2 id="2-通过装饰器">2. 通过装饰器</h2>
<p>另外一种典型的用法是使用类装饰器，由于逻辑比较易懂，被广泛被开发人员使用。</p>
<p>如下是一个例子，在每次对 Config 实例化的时候，都会进入 Singleton 类装饰器，该装饰器维护一个 dict，key 为 cls，value 为 instance，每次实例化都检查该 cls 是否已经在 dict 中存在，若存在则直接将之前实例化的对象返回，如此来保证单例。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">Singleton</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">instance</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@wraps</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="bp">cls</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">instance</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">instance</span><span class="p">[</span><span class="bp">cls</span><span class="p">]</span> <span class="o">=</span> <span class="bp">cls</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">instance</span><span class="p">[</span><span class="bp">cls</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">wrapper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@Singleton</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">cfg1</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">cfg2</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">cfg1</span> <span class="o">==</span> <span class="n">cfg2</span><span class="p">)</span>
</span></span></code></pre></div><p>运行后，输出结果 <code>True</code>​</p>
<p><strong>不过，这种方法虽然逻辑非常清晰易懂，但却有一个非常大的问题：经过 Singleton 装饰的后，其返回的是函数，因为无法被继承。</strong></p>
<p>装饰器由于本身机制的限制，在实例单例的同时也带来了一定的 “副作用”。</p>
<p>仔细一想，用装饰器实现单例的思路，不就是拦截实例对象的创建，来保证类只有唯一的实例嘛！</p>
<p>那我们只要换种方法，来拦截实例对象的创建不就行了。</p>
<p>通常的方法，有两种：</p>
<ul>
<li><strong>new</strong> 方法</li>
<li>元类</li>
</ul>
<h2 id="3-通过__new__方法">3. 通过__new__方法</h2>
<p>先说一下 <strong>new</strong> 方法，它是 Python 中的魔法方法之一。</p>
<p>很多人可能对他不是很熟悉，不过完全没有关系，你只要知道，当你在实例化的时候，是先执行类的 <strong>new</strong> 方法，再执行类的 <strong>init</strong> 即可。</p>
<p>因此，我们可以在 <strong>new</strong> 上做一些事情，使得类的实例只能存在一个。</p>
<p>如下是一个示例，在第一次实例的时候，会在 cls 上添加一个属性 <code>_instance</code>​ 来保存第一个实例，后面再实例化时，就会返回第一次创建的时候，</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s1">&#39;_instance&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="bp">cls</span><span class="o">.</span><span class="n">_instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_instance</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">cfg1</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">cfg2</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">cfg1</span> <span class="o">==</span> <span class="n">cfg2</span><span class="p">)</span>
</span></span></code></pre></div><p>运行后，输出结果 <code>True</code>​</p>
<p>细心的朋友，想必已经发现，如果有其他类继承了 Config 这个类，那么 <code>_instance</code>​ 也同样会被子类继承过去，这会导致只要 Config 及其子类只能有一个实例，只要 Config 实例过了，其子类就不能再实例化了，但这显然不是我们所期望的。</p>
<p>有的朋友，可能会想到用双下划线的 <code>__instance</code>​，这样就不会被继承了。</p>
<p>很遗憾的是，双下划线的属性，虽然不会被继承，但却有一个问题，就是属性名会被 Python 修改掉，变成 <code>_Config__instance</code>​，这样一样，我们在编写 <strong>new</strong> 时就无法使用 hasattr 来判断。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s1">&#39;__instance&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">cfg1</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="nb">hasattr</span><span class="p">(</span><span class="n">Config</span><span class="p">,</span> <span class="s2">&#34;__instance&#34;</span><span class="p">))</span>          <span class="c1"># 输出：False</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="nb">hasattr</span><span class="p">(</span><span class="n">Config</span><span class="p">,</span> <span class="s2">&#34;_Config__instance&#34;</span><span class="p">))</span>   <span class="c1"># 输出：True</span>
</span></span></code></pre></div><p>最好的做法是：在所有的子类将 <code>_instance</code>​ 重置为 None，并且重写 <code>__new__</code>​ 方法，最重要的是不要去调用 <code>supper.__new__()</code>​</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s1">&#39;_instance&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="bp">cls</span><span class="o">.</span><span class="n">_instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_instance</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ConfigExt</span><span class="p">(</span><span class="n">Config</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">_instance</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s1">&#39;_instance&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="bp">cls</span><span class="o">.</span><span class="n">_instance</span> <span class="o">=</span> <span class="nb">object</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_instance</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">cfg1</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">cfg2</span> <span class="o">=</span> <span class="n">ConfigExt</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">cfg3</span> <span class="o">=</span> <span class="n">ConfigExt</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">cfg1</span> <span class="o">==</span> <span class="n">cfg2</span><span class="p">)</span>  <span class="c1"># False</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">cfg2</span> <span class="o">==</span> <span class="n">cfg3</span><span class="p">)</span>  <span class="c1"># True</span>
</span></span></code></pre></div><p>可以看到，通过修改 <code>__new__</code>​ 方法，已经可以解决装饰器不能被继承的问题，但在有可能被继承的类里，却存在安全隐患。</p>
<h2 id="4-通过-metaclass">4. 通过 metaclass</h2>
<p>metaclass 是什么呢？ 用一句话说明</p>
<ul>
<li>类，是用来创建实例对象的「模板」。</li>
<li>而元类，是创建类的「模板」。</li>
</ul>
<p>因此我们可以通过修改元类，来使得创建此类时，就直接是一个单例类。</p>
<p>学习元类的作用过程，可以用普通类来做对比学习：</p>
<ul>
<li>在创建一个实例时，会走类的 <strong>new</strong> 方法</li>
<li>同样地逻辑，在创建一个普通类时，也会走元类的 <strong>new</strong></li>
</ul>
<p>与此同时：</p>
<ul>
<li>在类里实现了 <strong>call</strong> 可以让这个类的实例，变成可调用对象</li>
<li>对普通类进行实例化时，实际是对一个元类的实例（也就是普通类）进行直接调用，因此会走元类的 <strong>call</strong> 方法</li>
</ul>
<p>如下是一个示例，可以发现，父类和子类都可以完美实现单例，而不需要有额外的约定，也不需要团队里的人有统一的技术积累，就像平时一样，不会有任何突兀的感觉。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Singleton</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__call__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">Singleton</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ConfigExt</span><span class="p">(</span><span class="n">Config</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">cfg1</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">cfg2</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">cfg3</span> <span class="o">=</span> <span class="n">ConfigExt</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">cfg4</span> <span class="o">=</span> <span class="n">ConfigExt</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">cfg1</span> <span class="o">==</span> <span class="n">cfg2</span><span class="p">)</span>  <span class="c1"># True</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">cfg2</span> <span class="o">==</span> <span class="n">cfg3</span><span class="p">)</span>  <span class="c1"># False</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">cfg3</span> <span class="o">==</span> <span class="n">cfg4</span><span class="p">)</span>  <span class="c1"># True</span>
</span></span></code></pre></div><p>另外，该方法在多线程场景下并发创建实例对象时，由于初始化时，是需要一点时间，那么就会导致数据不同步的问题，导致出现多个实例。</p>
<p>为了方便复现，我在 <code>__call__</code>​ 里加了 time.sleep(1) 来实现延长实例化的时间</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">threading</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Singleton</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__call__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">Singleton</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">task</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">cfg</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="nb">id</span><span class="p">(</span><span class="n">cfg</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">task</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span></code></pre></div><p>运行结果如下，说明单例模式出现了问题</p>
<pre tabindex="0"><code>4336068992
4373051424
4335324896
4336069088
4335324848
4336069088
4335324896
4336069088
4335324848
4336069088
</code></pre><p>但这个问题可以通过线程锁来解决，先定义一个装饰器 <code>synchronized</code>​ ，然后在元类里的 <code>__call__</code>​ 加上这个装饰器。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">threading</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">synchronized</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">func</span><span class="o">.</span><span class="n">__lock__</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Lock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">lock_func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="n">func</span><span class="o">.</span><span class="n">__lock__</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">lock_func</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Singleton</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@synchronized</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__call__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">Singleton</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">task</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">cfg</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="nb">id</span><span class="p">(</span><span class="n">cfg</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">task</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span></code></pre></div><p>运行的结果就正常了</p>
<pre tabindex="0"><code>4381387136
4381387136
4381387136
4381387136
4381387136
4381387136
4381387136
4381387136
4381387136
4381387136
</code></pre><h2 id="5-总结一下">5. 总结一下</h2>
<p>在 Python 中有很多种方法实现单例的效果，但不同的方法，却有不同的局限性：</p>
<ul>
<li>使用模块：最简单直接且安全，推荐使用</li>
<li>使用装饰器：不能被继承，不推荐使用</li>
<li>使用 new 方法：需要开发成员对Python有足够的认识，不然代码会有BUG。</li>
<li>使用 metaclass：完美的单例实践，也推荐使用，但要注意加锁</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>在 Python 中理解与使用单例模式</title>
      <link>https://iswbm.com/en/2024/06/01/understanding-and-using-the-singleton-pattern-in-python/</link>
      <pubDate>Sat, 01 Jun 2024 20:26:48 +0800</pubDate>
      
      <guid>https://iswbm.com/en/2024/06/01/understanding-and-using-the-singleton-pattern-in-python/</guid>
      <description>单例模式一种常见的设计模式，并且也是最基础的设计模式，需要每个开发人员都熟练掌握。 当你希望在整个系统中，某个类只能出现一个实例时，就需要学会</description>
      <content:encoded><![CDATA[<p>单例模式一种常见的设计模式，并且也是最基础的设计模式，需要每个开发人员都熟练掌握。</p>
<p>当你希望在整个系统中，某个类只能出现一个实例时，就需要学会使用单例模式。</p>
<p>比较常见的场景是：某个项目的配置信息存放在一个配置文件中，通过一个 <code>Config</code>​ 的类来读取配置文件的信息。如果在程序运行期间，有很多地方都需要使用配置文件的内容，也就是说，很多地方都需要创建 <code>Config</code>​ 对象的实例，这就导致系统中存在多个 <code>Config</code>​ 的实例对象，而这样会严重浪费内存资源，尤其是在配置文件内容很多的情况下。事实上，类似 <code>Config</code>​ 这样的类，我们希望在程序运行期间只存在一个实例对象。</p>
<p>在 Python 中，可以用多种方法实现单例模式，常见的有：</p>
<ul>
<li>通过使用模块</li>
<li>通过装饰器</li>
<li>通过__new__方法</li>
<li>通过元类</li>
</ul>
<p><strong>不同的方法，有不同的坑，对 Python 机制认识不够的同学，很容易踩到坑，本文用实例来详细说明一下这几种方法的区别，以及那些不容易被察觉的坑点，给出最佳的选择方案。</strong></p>
<p>下面就详细说明以下这几种实现方法：</p>
<h2 id="1-通过使用模块">1. 通过使用模块</h2>
<p><strong>在Python中，Python 的模块就是天然的单例模式。</strong></p>
<p>因为模块在第一次导入时，会生成 <code>.pyc</code>​ 文件，当第二次导入时，就会直接加载 <code>.pyc</code>​ 文件，而不会再次执行模块代码。因此，我们只需把相关的函数和数据定义在一个模块中，就可以获得一个单例对象了。</p>
<p>看如下示例：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># 例如在config.py文件中定义Config类和实例</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">LOG_LEVEL</span> <span class="o">=</span> <span class="s1">&#39;INFO&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 然后在其他文件中，通过import导入此实例</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">config</span> <span class="kn">import</span> <span class="n">config</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">LOG_LEVEL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 输出结果：INFO</span>
</span></span></code></pre></div><p>如上所示，通过在文件中先生成一个类实例，然后在别的文件中直接导入这个类实例，就实现了单例模式。</p>
<h2 id="2-通过装饰器">2. 通过装饰器</h2>
<p>另外一种典型的用法是使用类装饰器，由于逻辑比较易懂，被广泛被开发人员使用。</p>
<p>如下是一个例子，在每次对 Config 实例化的时候，都会进入 Singleton 类装饰器，该装饰器维护一个 dict，key 为 cls，value 为 instance，每次实例化都检查该 cls 是否已经在 dict 中存在，若存在则直接将之前实例化的对象返回，如此来保证单例。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">Singleton</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">instance</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@wraps</span><span class="p">(</span><span class="bp">cls</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="bp">cls</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">instance</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">instance</span><span class="p">[</span><span class="bp">cls</span><span class="p">]</span> <span class="o">=</span> <span class="bp">cls</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">instance</span><span class="p">[</span><span class="bp">cls</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">wrapper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nd">@Singleton</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">cfg1</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">cfg2</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">cfg1</span> <span class="o">==</span> <span class="n">cfg2</span><span class="p">)</span>
</span></span></code></pre></div><p>运行后，输出结果 <code>True</code>​</p>
<p><strong>不过，这种方法虽然逻辑非常清晰易懂，但却有一个非常大的问题：经过 Singleton 装饰的后，其返回的是函数，因为无法被继承。</strong></p>
<p>装饰器由于本身机制的限制，在实例单例的同时也带来了一定的 “副作用”。</p>
<p>仔细一想，用装饰器实现单例的思路，不就是拦截实例对象的创建，来保证类只有唯一的实例嘛！</p>
<p>那我们只要换种方法，来拦截实例对象的创建不就行了。</p>
<p>通常的方法，有两种：</p>
<ul>
<li><strong>new</strong> 方法</li>
<li>元类</li>
</ul>
<h2 id="3-通过__new__方法">3. 通过__new__方法</h2>
<p>先说一下 <strong>new</strong> 方法，它是 Python 中的魔法方法之一。</p>
<p>很多人可能对他不是很熟悉，不过完全没有关系，你只要知道，当你在实例化的时候，是先执行类的 <strong>new</strong> 方法，再执行类的 <strong>init</strong> 即可。</p>
<p>因此，我们可以在 <strong>new</strong> 上做一些事情，使得类的实例只能存在一个。</p>
<p>如下是一个示例，在第一次实例的时候，会在 cls 上添加一个属性 <code>_instance</code>​ 来保存第一个实例，后面再实例化时，就会返回第一次创建的时候，</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s1">&#39;_instance&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="bp">cls</span><span class="o">.</span><span class="n">_instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_instance</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">cfg1</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">cfg2</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">cfg1</span> <span class="o">==</span> <span class="n">cfg2</span><span class="p">)</span>
</span></span></code></pre></div><p>运行后，输出结果 <code>True</code>​</p>
<p>细心的朋友，想必已经发现，如果有其他类继承了 Config 这个类，那么 <code>_instance</code>​ 也同样会被子类继承过去，这会导致只要 Config 及其子类只能有一个实例，只要 Config 实例过了，其子类就不能再实例化了，但这显然不是我们所期望的。</p>
<p>有的朋友，可能会想到用双下划线的 <code>__instance</code>​，这样就不会被继承了。</p>
<p>很遗憾的是，双下划线的属性，虽然不会被继承，但却有一个问题，就是属性名会被 Python 修改掉，变成 <code>_Config__instance</code>​，这样一样，我们在编写 <strong>new</strong> 时就无法使用 hasattr 来判断。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s1">&#39;__instance&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">cfg1</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="nb">hasattr</span><span class="p">(</span><span class="n">Config</span><span class="p">,</span> <span class="s2">&#34;__instance&#34;</span><span class="p">))</span>          <span class="c1"># 输出：False</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="nb">hasattr</span><span class="p">(</span><span class="n">Config</span><span class="p">,</span> <span class="s2">&#34;_Config__instance&#34;</span><span class="p">))</span>   <span class="c1"># 输出：True</span>
</span></span></code></pre></div><p>最好的做法是：在所有的子类将 <code>_instance</code>​ 重置为 None，并且重写 <code>__new__</code>​ 方法，最重要的是不要去调用 <code>supper.__new__()</code>​</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s1">&#39;_instance&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="bp">cls</span><span class="o">.</span><span class="n">_instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_instance</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ConfigExt</span><span class="p">(</span><span class="n">Config</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">_instance</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="s1">&#39;_instance&#39;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="bp">cls</span><span class="o">.</span><span class="n">_instance</span> <span class="o">=</span> <span class="nb">object</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_instance</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">cfg1</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">cfg2</span> <span class="o">=</span> <span class="n">ConfigExt</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">cfg3</span> <span class="o">=</span> <span class="n">ConfigExt</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">cfg1</span> <span class="o">==</span> <span class="n">cfg2</span><span class="p">)</span>  <span class="c1"># False</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">cfg2</span> <span class="o">==</span> <span class="n">cfg3</span><span class="p">)</span>  <span class="c1"># True</span>
</span></span></code></pre></div><p>可以看到，通过修改 <code>__new__</code>​ 方法，已经可以解决装饰器不能被继承的问题，但在有可能被继承的类里，却存在安全隐患。</p>
<h2 id="4-通过-metaclass">4. 通过 metaclass</h2>
<p>metaclass 是什么呢？ 用一句话说明</p>
<ul>
<li>类，是用来创建实例对象的「模板」。</li>
<li>而元类，是创建类的「模板」。</li>
</ul>
<p>因此我们可以通过修改元类，来使得创建此类时，就直接是一个单例类。</p>
<p>学习元类的作用过程，可以用普通类来做对比学习：</p>
<ul>
<li>在创建一个实例时，会走类的 <strong>new</strong> 方法</li>
<li>同样地逻辑，在创建一个普通类时，也会走元类的 <strong>new</strong></li>
</ul>
<p>与此同时：</p>
<ul>
<li>在类里实现了 <strong>call</strong> 可以让这个类的实例，变成可调用对象</li>
<li>对普通类进行实例化时，实际是对一个元类的实例（也就是普通类）进行直接调用，因此会走元类的 <strong>call</strong> 方法</li>
</ul>
<p>如下是一个示例，可以发现，父类和子类都可以完美实现单例，而不需要有额外的约定，也不需要团队里的人有统一的技术积累，就像平时一样，不会有任何突兀的感觉。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Singleton</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__call__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">Singleton</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ConfigExt</span><span class="p">(</span><span class="n">Config</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">cfg1</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">cfg2</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">cfg3</span> <span class="o">=</span> <span class="n">ConfigExt</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">cfg4</span> <span class="o">=</span> <span class="n">ConfigExt</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">cfg1</span> <span class="o">==</span> <span class="n">cfg2</span><span class="p">)</span>  <span class="c1"># True</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">cfg2</span> <span class="o">==</span> <span class="n">cfg3</span><span class="p">)</span>  <span class="c1"># False</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">cfg3</span> <span class="o">==</span> <span class="n">cfg4</span><span class="p">)</span>  <span class="c1"># True</span>
</span></span></code></pre></div><p>另外，该方法在多线程场景下并发创建实例对象时，由于初始化时，是需要一点时间，那么就会导致数据不同步的问题，导致出现多个实例。</p>
<p>为了方便复现，我在 <code>__call__</code>​ 里加了 time.sleep(1) 来实现延长实例化的时间</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">threading</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Singleton</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__call__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">Singleton</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">task</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">cfg</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="nb">id</span><span class="p">(</span><span class="n">cfg</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">task</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span></code></pre></div><p>运行结果如下，说明单例模式出现了问题</p>
<pre tabindex="0"><code>4336068992
4373051424
4335324896
4336069088
4335324848
4336069088
4335324896
4336069088
4335324848
4336069088
</code></pre><p>但这个问题可以通过线程锁来解决，先定义一个装饰器 <code>synchronized</code>​ ，然后在元类里的 <code>__call__</code>​ 加上这个装饰器。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">threading</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">synchronized</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">func</span><span class="o">.</span><span class="n">__lock__</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Lock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">lock_func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="n">func</span><span class="o">.</span><span class="n">__lock__</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">lock_func</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Singleton</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">        <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@synchronized</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__call__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">cls</span><span class="o">.</span><span class="n">__instance</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Config</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">Singleton</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">task</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">cfg</span> <span class="o">=</span> <span class="n">Config</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="nb">id</span><span class="p">(</span><span class="n">cfg</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">task</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span></code></pre></div><p>运行的结果就正常了</p>
<pre tabindex="0"><code>4381387136
4381387136
4381387136
4381387136
4381387136
4381387136
4381387136
4381387136
4381387136
4381387136
</code></pre><h2 id="5-总结一下">5. 总结一下</h2>
<p>在 Python 中有很多种方法实现单例的效果，但不同的方法，却有不同的局限性：</p>
<ul>
<li>使用模块：最简单直接且安全，推荐使用</li>
<li>使用装饰器：不能被继承，不推荐使用</li>
<li>使用 new 方法：需要开发成员对Python有足够的认识，不然代码会有BUG。</li>
<li>使用 metaclass：完美的单例实践，也推荐使用，但要注意加锁</li>
</ul>
]]></content:encoded>
    </item>
    
  </channel>
</rss>