<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Posts on WKLKEN THINKING</title>
		<link>https://wklken.me/posts.html</link>
		<description>Recent content in Posts on WKLKEN THINKING</description>
		<generator>Hugo -- gohugo.io</generator>
		<language>en-us</language>
		<copyright>This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.</copyright>
		<lastBuildDate>Wed, 27 May 2026 23:00:00 +0800</lastBuildDate>
		<atom:link href="https://wklken.me/posts/index.xml" rel="self" type="application/rss+xml" />
		
		<item>
			<title>AI 重构的那些 bad case</title>
			<link>https://wklken.me/posts/2026/05/27/bad-cases-of-ai-refactor.html</link>
			<pubDate>Wed, 27 May 2026 23:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2026/05/27/bad-cases-of-ai-refactor.html</guid>
			<description>以下 case 使用模型均为 gpt5.5 xhigh TLDR 当前最厉害的模型之一 做事靠谱吗？ 大部分靠谱 会有幻觉吗？ 会 会偷懒取巧吗？ 会 会为了做而做吗？会 会生成屎山代码吗？ 会 会使得</description>
			<content type="html"><![CDATA[<p>以下 case 使用模型均为 gpt5.5 xhigh</p>
<h2 id="tldr">TLDR</h2>
<p>当前最厉害的模型之一</p>
<ul>
<li>做事靠谱吗？ 大部分靠谱</li>
<li>会有幻觉吗？ 会</li>
<li>会偷懒取巧吗？ 会</li>
<li>会为了做而做吗？会</li>
<li>会生成屎山代码吗？ 会</li>
<li>会使得整体项目更复杂还是更简单？ 看你怎么用</li>
</ul>
<p>所以</p>
<ul>
<li>我们要 review 生成的代码吗？ 要</li>
<li>当前能做到交给 Agent 一个任务，之后看都不用看直接合并吗？ 暂时不能，大概率翻车</li>
<li>要为代码负责吗？ 废话，你的 PR，你的代码，你的锅</li>
</ul>
<p>建议：</p>
<ul>
<li>复杂任务尽量用最好的模型</li>
<li>Agent 说 job done 之后，用另一个 模型 交叉 review 【重点：交叉验证，不同模型能力、关注点不一样】</li>
<li>提交 PR 前后，人工 review 【重点：为自己的产出负责】</li>
<li>对于 review comment（不管是来自于 Agent，还是人）， 先阅读，反向读代码，理解后做判断 【重点：不要让 Agent 直接去修 comments，修多了或修少了都有可能，没有自己的理解和判断，永远积累不到经验】</li>
</ul>
<h2 id="过度防御">过度防御</h2>
<p>某些模型（gpt) 或者某些场景下， Agent 总是过度防御</p>
<p>例子： 命名 view 层有限制大于零，调用链上每一层都防御 【大概率是没读到，没理解上下文】</p>
<p>代码能用是能用，可读性非常差</p>
<h2 id="无意义到包装或转发">无意义到包装或转发</h2>
<p><code>Proxy</code> function or methods</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nd">@staticmethod</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">wait_release_done</span><span class="p">(</span><span class="n">release_history_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">timeout</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">DEFAULT_WAIT_RELEASE_TIMEOUT</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">wait_release_done</span><span class="p">(</span><span class="n">release_history_id</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="n">timeout</span><span class="p">)</span>
</span></span></code></pre></div><p>这个更过分， 先封装成对象 - 在 proxy method 中再转成id</p>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-1.png" alt="image-20260602232443128"></p>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-2.png" alt="image-20260602232336527"></p>
<p><code>Proxy</code> constants</p>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-3.png" alt="image-20260527232050538"></p>
<p>甚至，模块 export 代理</p>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-4.png" alt="image-20260602234716305"></p>
<h2 id="不会删掉没有调用的dead-code">不会删掉没有调用的dead code</h2>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-5.png" alt="image-20260602234055431"></p>
<h2 id="只要能解决问题什么方法都行">只要能解决问题，什么方法都行</h2>
<p>lazy import to avoid cycle import</p>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-6.png" alt="image-20260528234918701"></p>
<p>其实应该 break the cycle</p>
<h2 id="丢失掉所有有用的注释">丢失掉所有有用的注释</h2>
<blockquote>
<p>怎么写着写着，注释越来越少，干净又整洁</p>
</blockquote>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-7.png" alt="image-20260602232155194"></p>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-8.png" alt="image-20260602225050389"></p>
<h2 id="超级复杂的实现-vs-看似取巧的变通">超级复杂的实现 vs 看似取巧的变通</h2>
<p>你告诉它这个字段会导致一个注入，然后，它就去解决这个注入</p>
<blockquote>
<p>增加一个非常复杂的 escape 方法 vs 舍弃掉这个字段</p>
</blockquote>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-9.png" alt="image-20260602225333542"></p>
<p>你告诉它这个 version 有个注入， 它专心去解决注入，但是实际上这只是一个版本号，合法就不会出现注入问题</p>
<blockquote>
<p>解决注入 vs 校验合法性</p>
</blockquote>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-10.png" alt="image-20260602225833153"></p>
<h2 id="相对保守不改变函数的协议带来的复杂度">相对保守不改变函数的<code>协议</code>带来的复杂度</h2>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-11.png" alt="image-20260602233101283"></p>
<h2 id="半吊子的封装">半吊子的封装</h2>
<p>嗯，复杂的逻辑封装成函数了， 但是，这两个 <code>except</code> 是怎么回事？</p>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-12.png" alt="image-20260602231108403"></p>
<p><code>LockTimeout 和 ReleaseError</code> 实际上也属于 release_to_stages 的上下文</p>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-13.png" alt="image-20260602231312144"></p>
<p>看一个更典型的， 你或许不会犯第二次错误，但是 Agent 会</p>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-14.png" alt="image-20260602231455870"></p>
<h2 id="小心-noqa">小心 noqa</h2>
<p>如果我的变更命中的你的规则，那么一定是你的规则有问题，改一下绕过去好了</p>
<p>规则存在的目的并不是为了被破坏</p>
<blockquote>
<p>注：这是个proxy method， 最终删掉了</p>
</blockquote>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-15.png" alt="image-20260602233315414"></p>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-16.png" alt="image-20260602234351439"></p>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-17.png" alt="image-20260602235238977"></p>
<p>下面这个就厉害了，为了解 import 顺序导致的循环引用， 偷偷塞了一个 （其实我第一轮都没注意到，review 其他 noqa 的时候搜到的）</p>
<blockquote>
<p>应该去解决循环引用，而不是通过 import 顺序绕过循环引用， 然后通过 noqa 绕过import order 检查</p>
</blockquote>
<p><img src="/imgs/ai/bad-cases-of-ai-refactor-18.png" alt="image-20260602234453500"></p>
]]></content>
		</item>
		
		<item>
			<title>ai 编程实践</title>
			<link>https://wklken.me/posts/2026/05/11/ai-programming-practice.html</link>
			<pubDate>Mon, 11 May 2026 23:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2026/05/11/ai-programming-practice.html</guid>
			<description>很久没有动笔写 blog 了，想不到再更新已经到了 AGENTS 时代了。 去年开始，经历过了疯狂vibe-coding 的时期（一堆玩具，都是没用过的技术栈），也养过</description>
			<content type="html"><![CDATA[<p>很久没有动笔写 blog 了，想不到再更新已经到了 AGENTS 时代了。</p>
<p>去年开始，经历过了疯狂vibe-coding 的时期（一堆玩具，都是没用过的技术栈），也养过小龙虾（现在还火吗？），然后研究过 MCP（我们的产品上加了这个能力）， 然后是现在的 cli + skills。</p>
<p>从 github copilot 第一批用户，到 cursor 自费订阅（tab-tab)， 再到 sonnet 出来之后大跃进，经历了一个月左右的 AI 额度自由（想念opus），再到后面不自由自费订阅了 codex pro（稳重的 gpt 5.4/5.5）。</p>
<p>也焦虑过一段时期，疯狂刷推，看各种项目、文章，让小龙虾帮忙做各种汇总。</p>
<p>然后，到现在，形成了相对稳定的工作流和生产力。</p>
<p>5 天左右的 SDD，做完了一个生产级别的 cli，放原先手工时代至少半个月。 3 天左右的大规模重构，完成了之前接近一个月的重构工作量。这是以前想都不敢想的生产力提升。</p>
<p>虽然个人生产力提升很多，但是整体项目运作的摩擦还是很大的，毕竟沟通、基建等还有待改进。</p>
<p>下面就简单介绍下一些确定的实践经验。</p>
<hr>
<h2 id="1-深度使用一个-agent">1. 深度使用一个 agent</h2>
<p>虽然我们最好不要绑死在某个 Agent、某个模型或某个工具上，但是提升生产力的大前提是，磨刀。</p>
<p>而最好的刀，一般是<code>御三家</code>几个头部。</p>
<p>推荐 claude 及 codex； claude 更爽，codex 更稳，个人偏好 codex。</p>
<p>建议走官方订阅。（不建议走中转，太黑盒）</p>
<p>目前主要是用 VSCode Codex 插件 + Codex app. 然后少量用 Cursor(开发体验最好，就是太贵)， 一些简单执行类用的 claude+自定义模型；</p>
<p>不差钱的话， cursor 的体验胜过 VSCode Codex 插件（每次切回去用一段时间都得回来骂两句）</p>
<p>这里用好它，指的是深度阅读官方文档，阅读其他人的实践文章，了解各种配置，获取自己需要的配置，定制自己的配置。（让我想起了当初折腾vim）</p>
<h2 id="2-创建一个自己的-agents-仓库">2. 创建一个自己的 .agents 仓库</h2>
<p>创建一个仓库，私有的，维护到 git 的，然后 clone 到你的所有机器</p>
<p>我们的目标</p>
<ol>
<li>所有机器使用相同的配置，</li>
<li>并且通过<code>makefile</code>将仓库中差异化配置通过软链到各个 agent 目录，从而抹平所有 Agent 的使用体验。</li>
<li>配置是可以持续迭代的，某个机器上优化更新后，可以同步到其他所有的机器。</li>
</ol>
<p>例如，<code>.agents/skills</code>目录，codex 是认的，但是claude 不认，那就做一个软链。个性化的 AGENTS.md， 软链到 <code>.codex</code>下，以及软链成<code>CLAUDE.md</code></p>
<p>目前市面上vibe-coding 出来各种<code>管理工具</code>，我觉得是没必要的，最原始的git，让 Agent 帮你去处理，是定制化最高的方式。</p>
<h2 id="3-创建自己的-agentsmd">3. 创建自己的 AGENTS.md</h2>
<p>如果不知道写什么内容，参考 <a href="https://github.com/forrestchang/andrej-karpathy-skills">https://github.com/forrestchang/andrej-karpathy-skills</a> 这个项目，直接拷贝放进去。</p>
<p>如果使用 codex， 软链到<code>~/.codex/AGENTS.md</code></p>
<p>然后，你可以在使用过程中，持续优化这个 AGENTS.md， 应该怎么沟通，用什么语气，翻过的错误等等</p>
<h2 id="4-选一个基础工作流">4. 选一个基础工作流</h2>
<blockquote>
<p>最底层原子skill</p>
</blockquote>
<p>目前有很多工作流，我们现在需要安装上一批适合自己的、原子的 SKILL。</p>
<p>注意，不要装 SDD 这种核武器，要装瑞士军刀这种轻量并且相对全的</p>
<ul>
<li><a href="https://github.com/obra/superpowers">https://github.com/obra/superpowers</a> 【推荐】</li>
<li><a href="https://github.com/gsd-build/get-shit-done">https://github.com/gsd-build/get-shit-done</a></li>
<li><a href="https://github.com/addyosmani/agent-skills">https://github.com/addyosmani/agent-skills</a></li>
</ul>
<p>加入方式： git submodule 加进来，软链到skills 【这样能定时更新】</p>
<p>目前重度使用 superpowers， 如果你觉得某个工作流触发比较烦，那么可以在 AGENTS.md 中优化它。（触发规则）</p>
<p>例如： 如果是小的修改、bugfix，请不要调用 tdd</p>
<h2 id="5-挑选一批适合自己的skill">5. 挑选一批适合自己的skill</h2>
<blockquote>
<p>最底层原子 skill</p>
</blockquote>
<p>市面上skills 非常多，例如code-review 你估计能找到几十上百个。</p>
<p>但是，符合自己工作方式、风格的 SKILL，才是最合适的。</p>
<p>目前比较火的集合</p>
<ul>
<li><a href="https://github.com/anthropics/skills">https://github.com/anthropics/skills</a></li>
<li><a href="https://github.com/openai/skills">https://github.com/openai/skills</a></li>
<li><a href="https://github.com/mattpocock/skills">https://github.com/mattpocock/skills</a>  【个人很推荐】</li>
</ul>
<p>加入方式： git submodule 加进来，仅挑选想要的skill，软链到skills 【这样能定时更新】</p>
<h2 id="6-创建自己的skill">6. 创建自己的skill</h2>
<blockquote>
<p>上层skill</p>
</blockquote>
<p>使用 <code>$skill-creator</code> 可以创建自己的 SKILL</p>
<ol>
<li>可以在基础工作流之上，定制自己的工作流（自己的 SKILL，依赖底层原子 SKILL）
<ol>
<li>例如，<code>small-steps-refactor</code>可以依赖 superpowers 里面的<strong>writing-plans</strong>和<strong>executing-plans</strong> （我用这个 SKILL 重构了近三万行代码，单测+可 review 的小步重构使得巨大的重构变得可控）</li>
<li>例如，<code>fix-bug</code>可以依赖 superpowers 里面的<strong>systematic-debugging</strong> 以及自己的<code>git-commit</code></li>
</ol>
</li>
<li>也可以是全新的 SKILL</li>
</ol>
<p>可以导出自己所有同 AGENTS 的对话记录，让 Agent 分析那些重复的，反复出现的，形成模式的对话，然后创建成 SKILL。</p>
<p>你还可以考虑把那些繁琐的日常行为，转换成 SKILL。</p>
<p>特别是那些被动触发的 SKILL，效果非常好，例如<code>auto-load-env</code> 在 Agent 找到不到环境的时候自动触发，加载正确环境，那么意味着未来的所有遇到这种场景，Agent 都能自动解决，无需询问、打断。</p>
<h2 id="7-安装-cli--skill">7. 安装 cli + skill</h2>
<p>个人偏好 cli + SKILL，厌恶MCP</p>
<p>那么，此时你可以通过各种各样的 CLI， 扩展你的 Agent 能力</p>
<p>例如 playwright-cli</p>
<h2 id="8-持续迭代它">8. 持续迭代它</h2>
<ul>
<li>Agent 不好用，去优化 agent 配置</li>
<li>对话或各种交互、服从性不好，去优化 AGENT 配置</li>
<li>不断添加、优化、淘汰 SKILL</li>
<li>不断通过 cli 扩展 Agent 的能力，配合 SKILL 做更为复杂的事</li>
</ul>
<p>慢慢的，我们的工作流变得非常高效，与 Agent 配合非常默契，甚至大部分时候能一波流完成目标。</p>
<p>那么，你有了一份适合自己的，可持续迭代的配置。</p>
<p>或者说，你有了一份 <code>数字的你</code>,  当你把他放入openclaw 或 hermes 的时候，他就能用你的行事风格以及标准，做你能做到的事情</p>
<p>（至于会不会被 ai 替代掉，我们后面再讨论）</p>
<h2 id="9-啰嗦两句保证交付质量">9. 啰嗦两句：保证交付质量</h2>
<p>不管是 Agent 还是我们自己写的，我们需要保证交付的质量</p>
<p>AI 带来的质量方面的好处：</p>
<ol>
<li>写单测不再是负担，并且可以达到非常恐怖的覆盖率，给重构予信心</li>
<li>集成测试、自动化测试，都可以轻易地验证</li>
<li>debug 非常之快</li>
</ol>
<p>但是：</p>
<ol>
<li>AI 是有幻觉的（屎山也能给你写 100% 覆盖率单测，所以不能轻信）</li>
<li>模型差异是个的代码质量差异非常大</li>
<li>AI 无法一步到位解决存量项目中的复杂度</li>
</ol>
<p>你需要</p>
<ul>
<li>选好模型，写好prompt，把控好处理流程</li>
<li>review 自己的代码【不要超出你的掌控】</li>
<li>做好自测</li>
</ul>
<hr>
<p>写在最后</p>
<p>其实，由于这个浪潮来的太快，信息过载，进而导致人非常焦虑，漫天的工具、配置、最佳实践，一个又一个的热点，导致整个人非常浮躁。巨大的不确定性的情况下，我们需要去寻找确定性。</p>
<p>而以上，就能给你一份确定性，而且是越用越聪明，越用越有信心的积累。</p>
<p>或许未来有一天 Agent 强大到这些也变得没有意义，但是在当下，这个能给我们信心。</p>
]]></content>
		</item>
		
		<item>
			<title>apisix 中的 lrucache</title>
			<link>https://wklken.me/posts/2024/09/28/apisix-lrucache.html</link>
			<pubDate>Sat, 28 Sep 2024 00:00:00 +2000</pubDate>
			
			<guid>https://wklken.me/posts/2024/09/28/apisix-lrucache.html</guid>
			<description>基于 3.10.0 版本 apisix 的 lrucache 封装了 resty-lrucache 及 resty-lock 机制 代码不多，逐行分析 先看下新建一个 lrucache时支持的选项 opts type 类型， 如果是插件是 plugin count 容量， 如果没有设置， 类</description>
			<content type="html"><![CDATA[<blockquote>
<p>基于 3.10.0 版本</p>
</blockquote>
<p>apisix 的 <a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/core/lrucache.lua#L22">lrucache</a> 封装了 <a href="https://github.com/openresty/lua-resty-lrucache">resty-lrucache</a> 及 <a href="https://github.com/openresty/lua-resty-lock">resty-lock</a></p>
<h2 id="机制">机制</h2>
<p>代码不多，逐行分析</p>
<p>先看下新建一个 lrucache时支持的选项 opts</p>
<pre tabindex="0"><code>type  类型， 如果是插件是 plugin
count 容量， 如果没有设置， 类型为 plugin 的默认 count=8，其他类型默认 count=1024
ttl   过期时间，如果没有设置，类型为 plugin 默认 ttl=5min， 其他类型默认 ttl=60min

release  缓存命中，但是val.version不是预期的，会调用hook函数, 做一些额外的行为
invalid_stale    是否允许获取已过期的值， key 对应value过期，但是val.version是一致的，是的话，会重新将这个已过期值设置到 lrucache中 （即，会命中缓存，不会调用 create_obj_fun
serial_creating  确保缓存对象是串行创建的，即创建流程中会上锁避免竟态场景下创建同一个对象
</code></pre><p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/core/lrucache.lua#L72">new_lru_fun</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">new_lru_fun</span><span class="p">(</span><span class="n">opts</span><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="kd">local</span> <span class="n">item_count</span><span class="p">,</span> <span class="n">item_ttl</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">opts</span> <span class="ow">and</span> <span class="n">opts.type</span> <span class="o">==</span> <span class="s1">&#39;plugin&#39;</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">item_count</span> <span class="o">=</span> <span class="n">opts.count</span> <span class="ow">or</span> <span class="n">PLUGIN_ITEMS_COUNT</span>
</span></span><span class="line"><span class="cl">        <span class="n">item_ttl</span> <span class="o">=</span> <span class="n">opts.ttl</span> <span class="ow">or</span> <span class="n">PLUGIN_TTL</span>
</span></span><span class="line"><span class="cl">    <span class="kr">else</span>
</span></span><span class="line"><span class="cl">        <span class="n">item_count</span> <span class="o">=</span> <span class="n">opts</span> <span class="ow">and</span> <span class="n">opts.count</span> <span class="ow">or</span> <span class="n">GLOBAL_ITEMS_COUNT</span>
</span></span><span class="line"><span class="cl">        <span class="n">item_ttl</span> <span class="o">=</span> <span class="n">opts</span> <span class="ow">and</span> <span class="n">opts.ttl</span> <span class="ow">or</span> <span class="n">GLOBAL_TTL</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">item_release</span> <span class="o">=</span> <span class="n">opts</span> <span class="ow">and</span> <span class="n">opts.release</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">invalid_stale</span> <span class="o">=</span> <span class="n">opts</span> <span class="ow">and</span> <span class="n">opts.invalid_stale</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">serial_creating</span> <span class="o">=</span> <span class="n">opts</span> <span class="ow">and</span> <span class="n">opts.serial_creating</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">    <span class="c1">-- 创建一个 lrucache 对象， 大小为 item_count</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">lru_obj</span> <span class="o">=</span> <span class="n">lru_new</span><span class="p">(</span><span class="n">item_count</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">-- 返回一个fun， 其参数为 key, version, create_obj_fun, 以及 create_obj_fun 的参数</span>
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="kr">function</span> <span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">version</span><span class="p">,</span> <span class="n">create_obj_fun</span><span class="p">,</span> <span class="p">...)</span>
</span></span><span class="line"><span class="cl">        <span class="c1">-- 非 serial_creating ，或者 phase 不是 ssl_session_fetch、ssl_session_store、rewrite、access、content、timer 之一</span>
</span></span><span class="line"><span class="cl">        <span class="c1">-- nginx.get_phase: https://github.com/openresty/lua-nginx-module#ngxget_phase</span>
</span></span><span class="line"><span class="cl">        <span class="kr">if</span> <span class="ow">not</span> <span class="n">serial_creating</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">can_yield_phases</span><span class="p">[</span><span class="n">get_phase</span><span class="p">()]</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="c1">-- 获取缓存</span>
</span></span><span class="line"><span class="cl">            <span class="kd">local</span> <span class="n">cache_obj</span> <span class="o">=</span> <span class="n">fetch_valid_cache</span><span class="p">(</span><span class="n">lru_obj</span><span class="p">,</span> <span class="n">invalid_stale</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="n">item_ttl</span><span class="p">,</span> <span class="n">item_release</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">version</span><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="kr">if</span> <span class="n">cache_obj</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                <span class="kr">return</span> <span class="n">cache_obj.val</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">      
</span></span><span class="line"><span class="cl">            <span class="c1">-- 主意，这里没有上锁； 重复调用 create_obj_fun 以及 lru_obj:set 是允许的</span>
</span></span><span class="line"><span class="cl">            <span class="c1">-- 没上锁的好处：1. 不必要的复杂度 2. 性能</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">            <span class="kd">local</span> <span class="n">obj</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">create_obj_fun</span><span class="p">(...)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="n">obj</span> <span class="o">~=</span> <span class="kc">nil</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                <span class="c1">-- 注意这里 set 的时候，值 = 对象+version</span>
</span></span><span class="line"><span class="cl">                <span class="n">lru_obj</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="p">{</span><span class="n">val</span> <span class="o">=</span> <span class="n">obj</span><span class="p">,</span> <span class="n">ver</span> <span class="o">=</span> <span class="n">version</span><span class="p">},</span> <span class="n">item_ttl</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="kr">return</span> <span class="n">obj</span><span class="p">,</span> <span class="n">err</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</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">        <span class="kd">local</span> <span class="n">cache_obj</span> <span class="o">=</span> <span class="n">fetch_valid_cache</span><span class="p">(</span><span class="n">lru_obj</span><span class="p">,</span> <span class="n">invalid_stale</span><span class="p">,</span> <span class="n">item_ttl</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="n">item_release</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">version</span><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="kr">if</span> <span class="n">cache_obj</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="kr">return</span> <span class="n">cache_obj.val</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</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">        <span class="c1">-- 1. 获取 lock</span>
</span></span><span class="line"><span class="cl">        <span class="kd">local</span> <span class="n">lock</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">resty_lock</span><span class="p">:</span><span class="n">new</span><span class="p">(</span><span class="n">lock_shdict_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">if</span> <span class="ow">not</span> <span class="n">lock</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="kr">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="s2">&#34;failed to create lock: &#34;</span> <span class="o">..</span> <span class="n">err</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">-- 2. 上锁</span>
</span></span><span class="line"><span class="cl">        <span class="kd">local</span> <span class="n">key_s</span> <span class="o">=</span> <span class="n">tostring</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">log.info</span><span class="p">(</span><span class="s2">&#34;try to lock with key &#34;</span><span class="p">,</span> <span class="n">key_s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">local</span> <span class="n">elapsed</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">lock</span><span class="p">:</span><span class="n">lock</span><span class="p">(</span><span class="n">key_s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">if</span> <span class="ow">not</span> <span class="n">elapsed</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="kr">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="s2">&#34;failed to acquire the lock: &#34;</span> <span class="o">..</span> <span class="n">err</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">-- 3. 再次获取缓存： 避免从 获取缓存失败 到 上锁成功 这段时间内 其他地方已经获取缓存并成功</span>
</span></span><span class="line"><span class="cl">        <span class="n">cache_obj</span> <span class="o">=</span> <span class="n">fetch_valid_cache</span><span class="p">(</span><span class="n">lru_obj</span><span class="p">,</span> <span class="n">invalid_stale</span><span class="p">,</span> <span class="n">item_ttl</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="kc">nil</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">version</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1">--- 3.1 命中则解锁，返回</span>
</span></span><span class="line"><span class="cl">        <span class="kr">if</span> <span class="n">cache_obj</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="n">lock</span><span class="p">:</span><span class="n">unlock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="n">log.info</span><span class="p">(</span><span class="s2">&#34;unlock with key &#34;</span><span class="p">,</span> <span class="n">key_s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">return</span> <span class="n">cache_obj.val</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">       
</span></span><span class="line"><span class="cl">        <span class="c1">-- 4. 调用 create_obj_fun， 获取数据</span>
</span></span><span class="line"><span class="cl">        <span class="kd">local</span> <span class="n">obj</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">create_obj_fun</span><span class="p">(...)</span>
</span></span><span class="line"><span class="cl">        <span class="c1">--- 4.1 获取成功，设置</span>
</span></span><span class="line"><span class="cl">        <span class="kr">if</span> <span class="n">obj</span> <span class="o">~=</span> <span class="kc">nil</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="n">lru_obj</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="p">{</span><span class="n">val</span> <span class="o">=</span> <span class="n">obj</span><span class="p">,</span> <span class="n">ver</span> <span class="o">=</span> <span class="n">version</span><span class="p">},</span> <span class="n">item_ttl</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">        <span class="c1">-- 如果获取成功， obj正确, err=nil； 如果获取失败，obj nil, err!=nil</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">        <span class="c1">--- 5. 解锁</span>
</span></span><span class="line"><span class="cl">        <span class="n">lock</span><span class="p">:</span><span class="n">unlock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">log.info</span><span class="p">(</span><span class="s2">&#34;unlock with key &#34;</span><span class="p">,</span> <span class="n">key_s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">-- 6. 返回</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="n">obj</span><span class="p">,</span> <span class="n">err</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><p>说明：</p>
<ol>
<li>如果  opts.serial_creating = true， 在lrucache流程中会上锁，如果  opts.serial_creating = false或者没有设置，则不会上锁</li>
<li>如果当前的 phase <a href="https://github.com/openresty/lua-nginx-module#ngxget_phase">nginx.get_phase()</a>  不是 <code>ssl_session_fetch、ssl_session_store、rewrite、access、content、timer</code> 之一（can_yield_phases， 即属于不能 yield的 phase）, 例如 rewrite 阶段、log 阶段等，那么 lrucache流程也不会上锁；
<ol>
<li>即，如果配置了 <code>serial_creating = true</code>, 当前 phase 也必须是 <code>can_yield_phases: ssl_session_fetch、ssl_session_store、rewrite、access、content、timer</code>，才会是附带上锁的逻辑</li>
<li>如果phase被yield，那么必须上锁，不上锁的话，执行一半被yeild， 当恢复执行时，获取的缓存对象可能不是最新，使用过程中会发现缓存被设置回旧的值。</li>
</ol>
</li>
</ol>
<p>大部分的使用场景，都只设置了 <code>count/ttl/type</code>， 所以使用时几乎都没有上锁</p>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/core/lrucache.lua#L52">fetch_valid_cache</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">fetch_valid_cache</span><span class="p">(</span><span class="n">lru_obj</span><span class="p">,</span> <span class="n">invalid_stale</span><span class="p">,</span> <span class="n">item_ttl</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                 <span class="n">item_release</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">version</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">-- 从 lru 中获取 key 对应值</span>
</span></span><span class="line"><span class="cl">    <span class="c1">--- 如果存在且未过期， obj非空是， 否则obj = nil</span>
</span></span><span class="line"><span class="cl">    <span class="c1">--- 如果存在但过期了， obj=nil， stale_obj != nil</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">obj</span><span class="p">,</span> <span class="n">stale_obj</span> <span class="o">=</span> <span class="n">lru_obj</span><span class="p">:</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">-- 1. 命中，并且 version 一致，直接返回</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">obj</span> <span class="ow">and</span> <span class="n">obj.ver</span> <span class="o">==</span> <span class="n">version</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="n">obj</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">-- 2. 没有命中（如果有已过期的值， stale_obj非空），或者 version 不一致</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">    <span class="c1">--- 2.1 如果配置允许使用陈旧的值， 并且缓存中存在过期的值，且 version 一致</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">invalid_stale</span> <span class="ow">and</span> <span class="n">stale_obj</span> <span class="ow">and</span> <span class="n">stale_obj.ver</span> <span class="o">==</span> <span class="n">version</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="c1">-- 重新设置为陈旧的值，并且返回</span>
</span></span><span class="line"><span class="cl">        <span class="n">lru_obj</span><span class="p">:</span><span class="n">set</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">stale_obj</span><span class="p">,</span> <span class="n">item_ttl</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="n">stale_obj</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</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">    <span class="c1">--- a. key 对应 value 不存在, 此时 obj = nil</span>
</span></span><span class="line"><span class="cl">    <span class="c1">--- b. key 对应 value 存在，但是已过期，没有配置 invalid_stale = true， 此时 obj = nil</span>
</span></span><span class="line"><span class="cl">    <span class="c1">--- c. key 对应的 value 存在，但是 version不一致, 此时 obj != nil</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">    <span class="c1">--- 2.2 如果配置了 item_release， 并且 obj 非空(value存在但是version不一致)，调用 item_release（是一个 hoook?)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">---- 目的是，发现 version不一致，主动调用缓存主动刷新接口？</span>
</span></span><span class="line"><span class="cl">    <span class="c1">---- 目前项目中没有调用点</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">item_release</span> <span class="ow">and</span> <span class="n">obj</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">item_release</span><span class="p">(</span><span class="n">obj.val</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">--- 2.3 返回 nil</span>
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><h2 id="接口">接口</h2>
<p>lrucache中提供了一个快捷创建插件 lrucache的函数</p>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/core/lrucache.lua#L173">plugin_ctx</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="c1">---</span>
</span></span><span class="line"><span class="cl"><span class="c1">--  Cache some objects for plugins to avoid duplicate resources creation.</span>
</span></span><span class="line"><span class="cl"><span class="c1">--</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- @function core.lrucache.plugin_ctx</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- @tparam table lrucache LRUCache object instance.</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- @tparam table api_ctx The request context.</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- @tparam string extra_key Additional parameters for generating the lrucache identification key.</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- @tparam function create_obj_func Functions for creating cache objects.</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- If the object does not exist in the lrucache, this function is</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- called to create it and cache it in the lrucache.</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- @treturn table The object cached in lrucache.</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- @usage</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- local function create_obj() {</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- --   create the object</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- --   return the object</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- }</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- local obj, err = core.lrucache.plugin_ctx(lrucache, ctx, nil, create_obj)</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- -- obj is the object cached in lrucache</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">plugin_ctx</span><span class="p">(</span><span class="n">lrucache</span><span class="p">,</span> <span class="n">api_ctx</span><span class="p">,</span> <span class="n">extra_key</span><span class="p">,</span> <span class="n">create_obj_func</span><span class="p">,</span> <span class="p">...)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">key</span><span class="p">,</span> <span class="n">ver</span> <span class="o">=</span> <span class="n">plugin_ctx_key_and_ver</span><span class="p">(</span><span class="n">api_ctx</span><span class="p">,</span> <span class="n">extra_key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="n">lrucache</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">ver</span><span class="p">,</span> <span class="n">create_obj_func</span><span class="p">,</span> <span class="p">...)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 生成 key 和 version</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- key = api_ctx.conf_type .. &#34;#&#34; .. api_ctx.conf_id = route#123  每个路由唯一</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- version = api_ctx.conf_version  =  = route.modifiedIndex, 只要 route 配置不变，这个version 不变</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">plugin_ctx_key_and_ver</span><span class="p">(</span><span class="n">api_ctx</span><span class="p">,</span> <span class="n">extra_key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">key</span> <span class="o">=</span> <span class="n">api_ctx.conf_type</span> <span class="o">..</span> <span class="s2">&#34;#&#34;</span> <span class="o">..</span> <span class="n">api_ctx.conf_id</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">extra_key</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">key</span> <span class="o">=</span> <span class="n">key</span> <span class="o">..</span> <span class="s2">&#34;#&#34;</span> <span class="o">..</span> <span class="n">extra_key</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="n">key</span><span class="p">,</span> <span class="n">api_ctx.conf_version</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><p>使用示例</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">lrucache</span> <span class="o">=</span> <span class="n">core.lrucache</span><span class="p">.</span><span class="n">new</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="n">type</span> <span class="o">=</span> <span class="s2">&#34;plugin&#34;</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="c1">-- 这里创建一个携带 limit object 的 lrucache</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- 其中 </span>
</span></span><span class="line"><span class="cl"><span class="c1">-- 1. lrucache</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- 2. 没有设置 extra_key</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- 3. ctx, conf 为 apisix plugin 机制中的 ctx, conf</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- 4. create_limit_obj 为创建函数</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">lim</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">core.lrucache</span><span class="p">.</span><span class="n">plugin_ctx</span><span class="p">(</span><span class="n">lrucache</span><span class="p">,</span> <span class="n">ctx</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="n">create_limit_obj</span><span class="p">,</span> <span class="n">conf</span><span class="p">)</span>
</span></span></code></pre></div><p>注意，这里会存在一个非常容易导致 bug 的点：假设我在公共模块配置了一个lrucache，然后在多个插件中调用，那么你会发现不同插件 lrucache 获取到的对象<code>串</code>了， <code>api_ctx.conf_type/api_ctx.conf_id</code> 对于同一个资源是一致的， 所以同一个资源如果没有指定 <code>extra_key</code>，那么多个插件调用生成的key是一样地！</p>
<p>解决：公共模块中如果使用 plugin_ctx， 需要额外指定 <code>extra_key = plugin_name</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="c1">-- ratelimit/init.lua</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">lrucache</span> <span class="o">=</span> <span class="n">core.lrucache</span><span class="p">.</span><span class="n">new</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="n">type</span> <span class="o">=</span> <span class="s2">&#34;plugin&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">serial_creating</span> <span class="o">=</span> <span class="kc">true</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="o">#</span> <span class="n">bug</span> <span class="n">here</span>
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">rate_limit</span><span class="p">(</span><span class="n">conf</span><span class="p">,</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">time_window</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="kd">local</span> <span class="n">lim</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">core.lrucache</span><span class="p">.</span><span class="n">plugin_ctx</span><span class="p">(</span><span class="n">lrucache</span><span class="p">,</span> <span class="n">ctx</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="n">create_limit_obj</span><span class="p">,</span> <span class="n">plugin_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">#</span> <span class="n">ok</span> <span class="n">here</span>
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">rate_limit</span><span class="p">(</span><span class="n">conf</span><span class="p">,</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">plugin_name</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">count</span><span class="p">,</span> <span class="n">time_window</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="kd">local</span> <span class="n">lim</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">core.lrucache</span><span class="p">.</span><span class="n">plugin_ctx</span><span class="p">(</span><span class="n">lrucache</span><span class="p">,</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">plugin_name</span><span class="p">,</span> <span class="n">create_limit_obj</span><span class="p">,</span> <span class="n">plugin_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><h2 id="其他同类库">其他同类库</h2>
<blockquote>
<p>Fast and automated layered caching for OpenResty.</p>
</blockquote>
<p><a href="https://github.com/thibaultcha/lua-resty-mlcache">GitHub - thibaultcha/lua-resty-mlcache: Layered caching library for OpenResty</a> 两级缓存库</p>
<p>默认lrucache是 worker 维度的， 这个库实现了两集缓存，二级缓存走的 lua_shared_dict, 进程维度，多个worker 共享</p>
]]></content>
		</item>
		
		<item>
			<title>apisix 中的服务发现机制</title>
			<link>https://wklken.me/posts/2024/09/21/apisix-service-discovery.html</link>
			<pubDate>Sat, 21 Sep 2024 00:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2024/09/21/apisix-service-discovery.html</guid>
			<description>基于 3.10.0 版本 机制 0. 入口 在 apisix 的ngx_tpl.lua中 init_worker_by_lua_block { apisix.http_init_worker() } apisix/init.lua local router = require(&amp;#34;apisix.router&amp;#34;) function _M.http_init_worker() ..... local discovery = require(&amp;#34;apisix.discovery.init&amp;#34;).discovery if discovery and discovery.init_worker then discovery.init_worker() end ..... end 1. discovery.init_worker apisix/discovery/init.lua local discovery_type = local_conf.discovery local discovery = {} if discovery_type then for discovery_name, _ in pairs(discovery_type) do</description>
			<content type="html"><![CDATA[<blockquote>
<p>基于 3.10.0 版本</p>
</blockquote>
<h2 id="机制">机制</h2>
<h3 id="0-入口">0. 入口</h3>
<p><img src="https://cloud.githubusercontent.com/assets/2137369/15272097/77d1c09e-1a37-11e6-97ef-d9767035fc3e.png" alt=""></p>
<p>在 apisix 的ngx_tpl.lua中</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nginx" data-lang="nginx"><span class="line"><span class="cl">    <span class="k">init_worker_by_lua_block</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kn">apisix.http_init_worker()</span>
</span></span><span class="line"><span class="cl">    <span class="err">}</span>
</span></span></code></pre></div><p>apisix/init.lua</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">router</span>          <span class="o">=</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;apisix.router&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">http_init_worker</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span><span class="o">..</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">discovery</span> <span class="o">=</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;apisix.discovery.init&#34;</span><span class="p">).</span><span class="n">discovery</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">discovery</span> <span class="ow">and</span> <span class="n">discovery.init_worker</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">discovery.init_worker</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span><span class="o">..</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><h3 id="1--discoveryinit_worker">1.  discovery.init_worker</h3>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/discovery/init.lua#L32">apisix/discovery/init.lua</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">discovery_type</span> <span class="o">=</span> <span class="n">local_conf.discovery</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">discovery</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="kr">if</span> <span class="n">discovery_type</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">    <span class="kr">for</span> <span class="n">discovery_name</span><span class="p">,</span> <span class="n">_</span> <span class="kr">in</span> <span class="n">pairs</span><span class="p">(</span><span class="n">discovery_type</span><span class="p">)</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">        <span class="n">log.info</span><span class="p">(</span><span class="s2">&#34;use discovery: &#34;</span><span class="p">,</span> <span class="n">discovery_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">discovery</span><span class="p">[</span><span class="n">discovery_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;apisix.discovery.&#34;</span> <span class="o">..</span> <span class="n">discovery_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">discovery</span><span class="p">.</span><span class="nf">init_worker</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">discovery_type</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="kr">for</span> <span class="n">discovery_name</span><span class="p">,</span> <span class="n">_</span> <span class="kr">in</span> <span class="n">pairs</span><span class="p">(</span><span class="n">discovery_type</span><span class="p">)</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">            <span class="n">discovery</span><span class="p">[</span><span class="n">discovery_name</span><span class="p">].</span><span class="n">init_worker</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><p>根据配置文件中配置的服务发现类型，加载对应的模块，在 <code>init_worker</code>中调用对应模块的<code>init_worker</code></p>
<p>假设配置文件 <code>config.yaml</code> 中我们配置的是  dns (最简单的实现，其他的实现原理上一一样的)</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">discovery</span><span class="p">:</span><span class="w">                      </span><span class="c"># Service Discovery</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">dns</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">servers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;127.0.0.1:8600&#34;</span><span class="w">         </span><span class="c"># Replace with the address of your DNS server.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">resolv_conf</span><span class="p">:</span><span class="w"> </span><span class="l">/etc/resolv.conf</span><span class="w"> </span><span class="c"># Replace with the path to the local DNS resolv config. Configure either &#34;servers&#34; or &#34;resolv_conf&#34;.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">order</span><span class="p">:</span><span class="w">                       </span><span class="c"># Resolve DNS records this order.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">last                    </span><span class="w"> </span><span class="c"># Try the latest successful type for a hostname.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">SRV</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">A</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">AAAA</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">CNAME</span><span class="w">
</span></span></span></code></pre></div><h3 id="2-apisixdiscoverydns">2. apisix.discovery.dns</h3>
<p><a href="https://github.com/apache/apisix/blob/release/3.10/apisix/discovery/dns/init.lua">apisix/discovery/dns/init.lua</a></p>
<p>一共两个方法 （实现其他类型服务发现同理需要实现这两个方法）</p>
<ol>
<li><code>init_worker()</code> 初始化，根据配置，构建一个 <code>client</code></li>
<li><code>nodes(service_name)</code> 根据 service_name， 使用client做服务发现，返回 service_name 对应可用的 nodes</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">init_worker</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="kd">local</span> <span class="n">client</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">core.dns_client</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="n">opts</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">client</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">error</span><span class="p">(</span><span class="s2">&#34;failed to init the dns client: &#34;</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">dns_client</span> <span class="o">=</span> <span class="n">client</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">unction</span> <span class="n">_M.nodes</span><span class="p">(</span><span class="n">service_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">host</span><span class="p">,</span> <span class="n">port</span> <span class="o">=</span> <span class="n">core.utils</span><span class="p">.</span><span class="n">parse_addr</span><span class="p">(</span><span class="n">service_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">core.log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&#34;discovery dns with host &#34;</span><span class="p">,</span> <span class="n">host</span><span class="p">,</span> <span class="s2">&#34;, port &#34;</span><span class="p">,</span> <span class="n">port</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">records</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">dns_client</span><span class="p">:</span><span class="n">resolve</span><span class="p">(</span><span class="n">host</span><span class="p">,</span> <span class="n">core.dns_client</span><span class="p">.</span><span class="n">RETURN_ALL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">records</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="n">err</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">nodes</span> <span class="o">=</span> <span class="n">core.table</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="o">#</span><span class="n">records</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">index</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="kr">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">r</span> <span class="kr">in</span> <span class="n">ipairs</span><span class="p">(</span><span class="n">records</span><span class="p">)</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">        <span class="p">......</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="n">nodes</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><p>nodes示例</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><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 class="s2">&#34;host&#34;</span><span class="p">:</span> <span class="s2">&#34;192.168.1.100&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;port&#34;</span><span class="p">:</span> <span class="mi">8761</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;weight&#34;</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;metadata&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;management.port&#34;</span><span class="p">:</span> <span class="s2">&#34;8761&#34;</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 class="p">]</span>
</span></span></code></pre></div><h3 id="3-调用点在哪里">3. 调用点在哪里？</h3>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/init.lua#L40">apisix/init.lua</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">set_upstream</span>    <span class="o">=</span> <span class="n">apisix_upstream.set_by_route</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- from access phase</span>
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">http_access_phase</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="n">_M.handle_upstream</span><span class="p">(</span><span class="n">api_ctx</span><span class="p">,</span> <span class="n">route</span><span class="p">,</span> <span class="n">enable_websocket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">handle_upstream</span><span class="p">(</span><span class="n">api_ctx</span><span class="p">,</span> <span class="n">route</span><span class="p">,</span> <span class="n">enable_websocket</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="kd">local</span> <span class="n">code</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">set_upstream</span><span class="p">(</span><span class="n">route</span><span class="p">,</span> <span class="n">api_ctx</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="kr">end</span>
</span></span></code></pre></div><p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/upstream.lua#L255">apisix/upstream.lua </a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">set_by_route</span><span class="p">(</span><span class="n">route</span><span class="p">,</span> <span class="n">api_ctx</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="c1">-- 如果 upstream 中有配置 service_name， 则需要服务发现</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">up_conf.service_name</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <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="kd">local</span> <span class="n">dis</span> <span class="o">=</span> <span class="n">discovery</span><span class="p">[</span><span class="n">up_conf.discovery_type</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="c1">-- 根据service_name 获取 nodes</span>
</span></span><span class="line"><span class="cl">        <span class="kd">local</span> <span class="n">new_nodes</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">dis.nodes</span><span class="p">(</span><span class="n">up_conf.service_name</span><span class="p">,</span> <span class="n">up_conf.discovery_args</span><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="kd">local</span> <span class="n">same</span> <span class="o">=</span> <span class="n">upstream_util.compare_upstream_node</span><span class="p">(</span><span class="n">up_conf</span><span class="p">,</span> <span class="n">new_nodes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">if</span> <span class="ow">not</span> <span class="n">same</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <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="n">up_conf.nodes</span> <span class="o">=</span> <span class="n">new_nodes</span>
</span></span><span class="line"><span class="cl">            <span class="n">up_conf.original_nodes</span> <span class="o">=</span> <span class="n">up_conf.nodes</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="kd">local</span> <span class="n">new_up_conf</span> <span class="o">=</span> <span class="n">core.table</span><span class="p">.</span><span class="n">clone</span><span class="p">(</span><span class="n">up_conf</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="kd">local</span> <span class="n">parent</span> <span class="o">=</span> <span class="n">up_conf.parent</span>
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="n">parent.value</span><span class="p">.</span><span class="n">upstream</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                <span class="c1">-- the up_conf comes from route or service</span>
</span></span><span class="line"><span class="cl">                <span class="n">parent.value</span><span class="p">.</span><span class="n">upstream</span> <span class="o">=</span> <span class="n">new_up_conf</span>
</span></span><span class="line"><span class="cl">            <span class="kr">else</span>
</span></span><span class="line"><span class="cl">                <span class="n">parent.value</span> <span class="o">=</span> <span class="n">new_up_conf</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">            <span class="n">up_conf</span> <span class="o">=</span> <span class="n">new_up_conf</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">id</span> <span class="o">=</span> <span class="n">up_conf.parent</span><span class="p">.</span><span class="n">value.id</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">conf_version</span> <span class="o">=</span> <span class="n">up_conf.parent</span><span class="p">.</span><span class="n">modifiedIndex</span>
</span></span><span class="line"><span class="cl">    <span class="c1">-- include the upstream object as part of the version, because the upstream will be changed</span>
</span></span><span class="line"><span class="cl">    <span class="c1">-- by service discovery or dns resolver.</span>
</span></span><span class="line"><span class="cl">    <span class="n">set_directly</span><span class="p">(</span><span class="n">api_ctx</span><span class="p">,</span> <span class="n">id</span><span class="p">,</span> <span class="n">conf_version</span> <span class="o">..</span> <span class="s2">&#34;#&#34;</span> <span class="o">..</span> <span class="n">tostring</span><span class="p">(</span><span class="n">up_conf</span><span class="p">),</span> <span class="n">up_conf</span><span class="p">)</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">set_directly</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">ver</span><span class="p">,</span> <span class="n">conf</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="c1">-- 注意这里，更新了 api_ctx 的这几个字段</span>
</span></span><span class="line"><span class="cl">    <span class="n">ctx.upstream_conf</span> <span class="o">=</span> <span class="n">conf</span>
</span></span><span class="line"><span class="cl">    <span class="n">ctx.upstream_version</span> <span class="o">=</span> <span class="n">ver</span>
</span></span><span class="line"><span class="cl">    <span class="n">ctx.upstream_key</span> <span class="o">=</span> <span class="n">key</span>
</span></span><span class="line"><span class="cl">    <span class="kr">return</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/balancer.lua#L194">apisix/balancer.lua</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="c1">-- pick_server will be called:</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- 1. in the access phase so that we can set headers according to the picked server</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- 2. each time we need to retry upstream</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">pick_server</span><span class="p">(</span><span class="n">route</span><span class="p">,</span> <span class="n">ctx</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="kd">local</span> <span class="n">version</span> <span class="o">=</span> <span class="n">ctx.upstream_version</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">key</span> <span class="o">=</span> <span class="n">ctx.upstream_key</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">checker</span> <span class="o">=</span> <span class="n">ctx.up_checker</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">-- the same picker will be used in the whole request, especially during the retry</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">server_picker</span> <span class="o">=</span> <span class="n">ctx.server_picker</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">server_picker</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">server_picker</span> <span class="o">=</span> <span class="n">lrucache_server_picker</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">version</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                               <span class="n">create_server_picker</span><span class="p">,</span> <span class="n">up_conf</span><span class="p">,</span> <span class="n">checker</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</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="kd">local</span> <span class="n">server</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">server_picker.get</span><span class="p">(</span><span class="n">ctx</span><span class="p">)</span>
</span></span></code></pre></div><p>这里，如果服务发现导致 <code>upstream_key/upstream_version</code> 变化，那么意味着 server_picker 对应的 lrucache 会失效，进入 <code>create_server_picker</code>的逻辑</p>
<h2 id="数据面服务发现的问题">数据面服务发现的问题</h2>
<p>如果服务发现获取上游的 node 数据变更非常频繁</p>
<ol>
<li>会被对比出来，有差异</li>
<li>会多一些赋值</li>
<li>负载均衡lrucache会失效重建，重建时，如果配置有主动健康检查，还会触发主动健康检查获取健康的节点列表</li>
</ol>
<p>这样会导致服务出现抖动。</p>
<h2 id="基于控制面的服务发现">基于控制面的服务发现</h2>
<p>官方有一个基于控制面的服务发现项目 <a href="https://github.com/api7/apisix-seed">api7/apisix-seed: Do service discovery on the CP side</a></p>
<p>通过订阅 service_name的变更， 获取服务发现的变更，写入到etcd</p>
<p><img src="https://raw.githubusercontent.com/apache/apisix/release/3.10/docs/assets/images/control-plane-service-discovery.png" alt=""></p>
<h2 id="相关文档">相关文档</h2>
<ul>
<li><a href="https://apisix.apache.org/zh/docs/apisix/discovery/">集成服务发现注册中心 | Apache APISIX® &ndash; Cloud-Native API Gateway</a></li>
<li><a href="https://apisix.apache.org/zh/docs/apisix/discovery/control-plane-service-discovery/">控制面服务发现 | Apache APISIX® &ndash; Cloud-Native API Gateway</a></li>
<li><a href="https://apisix.apache.org/zh/blog/2022/11/10/what-is-service-in-microservice-discovery/">微服务中的服务发现是什么 | Apache APISIX® &ndash; Cloud-Native API Gateway</a></li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>apisix 中的负载均衡</title>
			<link>https://wklken.me/posts/2024/09/21/apisix-load-balance.html</link>
			<pubDate>Sat, 21 Sep 2024 00:00:00 +2000</pubDate>
			
			<guid>https://wklken.me/posts/2024/09/21/apisix-load-balance.html</guid>
			<description>基于 3.10.0 版本 机制 0. 入口 在 apisix 的ngx_tpl.lua中 -- 初始化 init_worker_by_lua_block { apisix.http_init_worker() } -- balance balancer_by_lua_block { apisix.http_balancer_phase() } 1. 初始化 apisix/init.lua local router = require(&amp;#34;apisix.router&amp;#34;) local load_balancer function _M.http_init_worker() ..... -- 这个什么都没做 require(&amp;#34;apisix.balancer&amp;#34;).init_worker() -- 这个这个是核心</description>
			<content type="html"><![CDATA[<blockquote>
<p>基于 3.10.0 版本</p>
</blockquote>
<h2 id="机制">机制</h2>
<h3 id="0-入口">0. 入口</h3>
<p><img src="https://cloud.githubusercontent.com/assets/2137369/15272097/77d1c09e-1a37-11e6-97ef-d9767035fc3e.png" alt=""></p>
<p>在 apisix 的ngx_tpl.lua中</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nginx" data-lang="nginx"><span class="line"><span class="cl">    <span class="k">--</span> <span class="s">初始化</span>
</span></span><span class="line"><span class="cl">    <span class="s">init_worker_by_lua_block</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kn">apisix.http_init_worker()</span>
</span></span><span class="line"><span class="cl">    <span class="err">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="s">--</span> <span class="s">balance</span>
</span></span><span class="line"><span class="cl">        <span class="s">balancer_by_lua_block</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kn">apisix.http_balancer_phase()</span>
</span></span><span class="line"><span class="cl">        <span class="err">}</span>
</span></span></code></pre></div><h3 id="1-初始化">1. 初始化</h3>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/init.lua#L123">apisix/init.lua</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">router</span>          <span class="o">=</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;apisix.router&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">load_balancer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">http_init_worker</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span><span class="o">..</span>
</span></span><span class="line"><span class="cl">    <span class="c1">-- 这个什么都没做</span>
</span></span><span class="line"><span class="cl">    <span class="n">require</span><span class="p">(</span><span class="s2">&#34;apisix.balancer&#34;</span><span class="p">).</span><span class="n">init_worker</span><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="n">load_balancer</span> <span class="o">=</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;apisix.balancer&#34;</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="p">...</span><span class="o">..</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><h3 id="2-执行入口">2. 执行入口</h3>
<p>在 balance 阶段，进入<code>http_balancer_phase</code></p>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/init.lua#L889">apisix/init.lua</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">http_balancer_phase</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="n">load_balancer.run</span><span class="p">(</span><span class="n">api_ctx.matched_route</span><span class="p">,</span> <span class="n">api_ctx</span><span class="p">,</span> <span class="n">common_phase</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><p>进入</p>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/balancer.lua#L342">apisix/balancer.lua</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">run</span><span class="p">(</span><span class="n">route</span><span class="p">,</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">plugin_funcs</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">server</span><span class="p">,</span> <span class="n">err</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">ctx.picked_server</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="c1">-- use the server picked in the access phase</span>
</span></span><span class="line"><span class="cl">        <span class="n">server</span> <span class="o">=</span> <span class="n">ctx.picked_server</span>
</span></span><span class="line"><span class="cl">        <span class="n">ctx.picked_server</span> <span class="o">=</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">set_balancer_opts</span><span class="p">(</span><span class="n">route</span><span class="p">,</span> <span class="n">ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">else</span>
</span></span><span class="line"><span class="cl">        <span class="c1">-- retry 相关，暂时忽略</span>
</span></span><span class="line"><span class="cl">        <span class="p">......</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">ok</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">set_current_peer</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="n">ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">ok</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="n">core.response</span><span class="p">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">502</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">ctx.proxy_passed</span> <span class="o">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><h3 id="3--api_ctxpicked_server">3.  api_ctx.picked_server</h3>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/init.lua#L551">apisix/init.lua</a></p>
<p>实际上，在 <code>http_access_phase</code> 就已经做了 <code>pick_server</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">http_access_phase</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="n">_M.handle_upstream</span><span class="p">(</span><span class="n">api_ctx</span><span class="p">,</span> <span class="n">route</span><span class="p">,</span> <span class="n">enable_websocket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">handle_upstream</span><span class="p">(</span><span class="n">api_ctx</span><span class="p">,</span> <span class="n">route</span><span class="p">,</span> <span class="n">enable_websocket</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="kd">local</span> <span class="n">server</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">load_balancer.pick_server</span><span class="p">(</span><span class="n">route</span><span class="p">,</span> <span class="n">api_ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">server</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">core.log</span><span class="p">.</span><span class="n">error</span><span class="p">(</span><span class="s2">&#34;failed to pick server: &#34;</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="n">core.response</span><span class="p">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">502</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">api_ctx.picked_server</span> <span class="o">=</span> <span class="n">server</span>
</span></span><span class="line"><span class="cl">    <span class="p">......</span>
</span></span><span class="line"><span class="cl">  
</span></span></code></pre></div><h3 id="4-load_balancerpick_server">4. load_balancer.pick_server</h3>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/balancer.lua#L194">apisix/balancer.lua</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="c1">-- pick_server will be called:</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- 1. in the access phase so that we can set headers according to the picked server</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- 2. each time we need to retry upstream</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">pick_server</span><span class="p">(</span><span class="n">route</span><span class="p">,</span> <span class="n">ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">up_conf</span> <span class="o">=</span> <span class="n">ctx.upstream_conf</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">nodes_count</span> <span class="o">=</span> <span class="o">#</span><span class="n">up_conf.nodes</span>
</span></span><span class="line"><span class="cl">    <span class="c1">-- 如果只有一个 node, 直接返回</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">nodes_count</span> <span class="o">==</span> <span class="mi">1</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="kd">local</span> <span class="n">node</span> <span class="o">=</span> <span class="n">up_conf.nodes</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="n">ctx.balancer_ip</span> <span class="o">=</span> <span class="n">node.host</span>
</span></span><span class="line"><span class="cl">        <span class="n">ctx.balancer_port</span> <span class="o">=</span> <span class="n">node.port</span>
</span></span><span class="line"><span class="cl">        <span class="n">node.upstream_host</span> <span class="o">=</span> <span class="n">parse_server_for_upstream_host</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">ctx.upstream_scheme</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="n">node</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">-- the same picker will be used in the whole request, especially during the retry</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">server_picker</span> <span class="o">=</span> <span class="n">ctx.server_picker</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">server_picker</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="c1">-- 从lrucache中获取，没有的话创建一个 create_server_picker</span>
</span></span><span class="line"><span class="cl">        <span class="n">server_picker</span> <span class="o">=</span> <span class="n">lrucache_server_picker</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">version</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                               <span class="n">create_server_picker</span><span class="p">,</span> <span class="n">up_conf</span><span class="p">,</span> <span class="n">checker</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">server_picker</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="s2">&#34;failed to fetch server picker&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</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">    <span class="kd">local</span> <span class="n">server</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">server_picker.get</span><span class="p">(</span><span class="n">ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">server</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">err</span> <span class="o">=</span> <span class="n">err</span> <span class="ow">or</span> <span class="s2">&#34;no valid upstream node&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="s2">&#34;failed to find valid upstream server, &#34;</span> <span class="o">..</span> <span class="n">err</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">ctx.balancer_server</span> <span class="o">=</span> <span class="n">server</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">create_server_picker</span><span class="p">(</span><span class="n">upstream</span><span class="p">,</span> <span class="n">checker</span><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="kd">local</span> <span class="n">picker</span> <span class="o">=</span> <span class="n">pickers</span><span class="p">[</span><span class="n">upstream.type</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">picker</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">pickers</span><span class="p">[</span><span class="n">upstream.type</span><span class="p">]</span> <span class="o">=</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;apisix.balancer.&#34;</span> <span class="o">..</span> <span class="n">upstream.type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">picker</span> <span class="o">=</span> <span class="n">pickers</span><span class="p">[</span><span class="n">upstream.type</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">picker</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="p">......</span>
</span></span><span class="line"><span class="cl">        <span class="kd">local</span> <span class="n">server_picker</span> <span class="o">=</span> <span class="n">picker.new</span><span class="p">(</span><span class="n">up_nodes</span><span class="p">[</span><span class="n">up_nodes._priority_index</span><span class="p">[</span><span class="mi">1</span><span class="p">]],</span> <span class="n">upstream</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="n">server_picker</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>  
</span></span><span class="line"><span class="cl">  
</span></span></code></pre></div><p>the balance algorithm chash/ewma/least_conn/priority/roundrobin</p>
<p>以 roundrobin 为例  <a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/balancer/roundrobin.lua#L38">apisix/balancer/roundrobin.lua</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">up_nodes</span><span class="p">,</span> <span class="n">upstream</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">safe_limit</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="kr">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">weight</span> <span class="kr">in</span> <span class="n">pairs</span><span class="p">(</span><span class="n">up_nodes</span><span class="p">)</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">        <span class="c1">-- the weight can be zero</span>
</span></span><span class="line"><span class="cl">        <span class="n">safe_limit</span> <span class="o">=</span> <span class="n">safe_limit</span> <span class="o">+</span> <span class="n">weight</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">picker</span> <span class="o">=</span> <span class="n">roundrobin</span><span class="p">:</span><span class="n">new</span><span class="p">(</span><span class="n">up_nodes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">nodes_count</span> <span class="o">=</span> <span class="n">nkeys</span><span class="p">(</span><span class="n">up_nodes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">upstream</span> <span class="o">=</span> <span class="n">upstream</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">get</span> <span class="o">=</span> <span class="kr">function</span> <span class="p">(</span><span class="n">ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="n">ctx.balancer_tried_servers</span> <span class="ow">and</span> <span class="n">ctx.balancer_tried_servers_count</span> <span class="o">==</span> <span class="n">nodes_count</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                <span class="kr">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="s2">&#34;all upstream servers tried&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="kd">local</span> <span class="n">server</span><span class="p">,</span> <span class="n">err</span>
</span></span><span class="line"><span class="cl">            <span class="kr">for</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">safe_limit</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">                <span class="n">server</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">picker</span><span class="p">:</span><span class="n">find</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                <span class="kr">if</span> <span class="ow">not</span> <span class="n">server</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                    <span class="kr">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="n">err</span>
</span></span><span class="line"><span class="cl">                <span class="kr">end</span>
</span></span><span class="line"><span class="cl">                <span class="kr">if</span> <span class="n">ctx.balancer_tried_servers</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                    <span class="kr">if</span> <span class="ow">not</span> <span class="n">ctx.balancer_tried_servers</span><span class="p">[</span><span class="n">server</span><span class="p">]</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                        <span class="kr">break</span>
</span></span><span class="line"><span class="cl">                    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">                <span class="kr">else</span>
</span></span><span class="line"><span class="cl">                    <span class="kr">break</span>
</span></span><span class="line"><span class="cl">                <span class="kr">end</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="kr">return</span> <span class="n">server</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">after_balance</span> <span class="o">=</span> <span class="kr">function</span> <span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">before_retry</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="ow">not</span> <span class="n">before_retry</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                <span class="kr">if</span> <span class="n">ctx.balancer_tried_servers</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                    <span class="n">core.tablepool</span><span class="p">.</span><span class="n">release</span><span class="p">(</span><span class="s2">&#34;balancer_tried_servers&#34;</span><span class="p">,</span> <span class="n">ctx.balancer_tried_servers</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="n">ctx.balancer_tried_servers</span> <span class="o">=</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">                <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="kr">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="ow">not</span> <span class="n">ctx.balancer_tried_servers</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                <span class="n">ctx.balancer_tried_servers</span> <span class="o">=</span> <span class="n">core.tablepool</span><span class="p">.</span><span class="n">fetch</span><span class="p">(</span><span class="s2">&#34;balancer_tried_servers&#34;</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">            <span class="c1">-- 标记 及 计数</span>
</span></span><span class="line"><span class="cl">            <span class="n">ctx.balancer_tried_servers</span><span class="p">[</span><span class="n">ctx.balancer_server</span><span class="p">]</span> <span class="o">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">            <span class="n">ctx.balancer_tried_servers_count</span> <span class="o">=</span> <span class="p">(</span><span class="n">ctx.balancer_tried_servers_count</span> <span class="ow">or</span> <span class="mi">0</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">before_retry_next_priority</span> <span class="o">=</span> <span class="kr">function</span> <span class="p">(</span><span class="n">ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="n">ctx.balancer_tried_servers</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                <span class="n">core.tablepool</span><span class="p">.</span><span class="n">release</span><span class="p">(</span><span class="s2">&#34;balancer_tried_servers&#34;</span><span class="p">,</span> <span class="n">ctx.balancer_tried_servers</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">ctx.balancer_tried_servers</span> <span class="o">=</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="n">ctx.balancer_tried_servers_count</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</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="kr">end</span>
</span></span></code></pre></div><p>这几个函数在哪里被调用的?</p>
<ul>
<li><code>get</code>: 在<code>pick_server(route, ctx)</code> new 得到一个实例后, 立马<code>get</code>, <code>local server, err = server_picker.get(ctx)</code>并且执行赋值<code>ctx.balancer_server = server</code></li>
<li><code>after_balance</code>: 在 http_log_phase 阶段, 请求结束后, 调用的<code>api_ctx.server_picker.after_balance(api_ctx, false)</code>; 另外, <code>pick_server</code>的retry过程中, 也会<code>ctx.server_picker.after_balance(ctx, true)</code></li>
<li><code>before_retry_next_priority</code>: balancer/priority.lua 中调用的, <code>priority.get(ctx) -&gt; picker.before_retry_next_priority;</code> 在逐一确认priority的过程中, 调用这个函数</li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>apisix etcd机制</title>
			<link>https://wklken.me/posts/2024/09/17/apisix-etcd.html</link>
			<pubDate>Tue, 17 Sep 2024 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2024/09/17/apisix-etcd.html</guid>
			<description>基于 3.10.0 版本 机制 0. 入口 在 apisix 的ngx_tpl.lua中 init_worker_by_lua_block { apisix.http_init_worker() } apisix/init.lua local router = require(&amp;#34;apisix.router&amp;#34;) function _M.http_init_worker() ..... router.http_init_worker() ..... end 1. http_init_worker apisix/router.lua function _M.http_init_worker() local conf = core.config.local_conf() local router_http_name = &amp;#34;radixtree_uri&amp;#34; if conf and conf.apisix and conf.apisix.router then router_http_name = conf.apisix.router.http or router_http_name ...... end local router_http =</description>
			<content type="html"><![CDATA[<blockquote>
<p>基于 3.10.0 版本</p>
</blockquote>
<h2 id="机制">机制</h2>
<h3 id="0-入口">0. 入口</h3>
<p><img src="https://cloud.githubusercontent.com/assets/2137369/15272097/77d1c09e-1a37-11e6-97ef-d9767035fc3e.png" alt=""></p>
<p>在 apisix 的ngx_tpl.lua中</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nginx" data-lang="nginx"><span class="line"><span class="cl">    <span class="k">init_worker_by_lua_block</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kn">apisix.http_init_worker()</span>
</span></span><span class="line"><span class="cl">    <span class="err">}</span>
</span></span></code></pre></div><p>apisix/init.lua</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">router</span>          <span class="o">=</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;apisix.router&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">http_init_worker</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span><span class="o">..</span>
</span></span><span class="line"><span class="cl">    <span class="n">router.http_init_worker</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span><span class="o">..</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><h3 id="1-http_init_worker">1. http_init_worker</h3>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/router.lua#L71">apisix/router.lua</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">http_init_worker</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">conf</span> <span class="o">=</span> <span class="n">core.config</span><span class="p">.</span><span class="n">local_conf</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">router_http_name</span> <span class="o">=</span> <span class="s2">&#34;radixtree_uri&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">conf</span> <span class="ow">and</span> <span class="n">conf.apisix</span> <span class="ow">and</span> <span class="n">conf.apisix</span><span class="p">.</span><span class="n">router</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">router_http_name</span> <span class="o">=</span> <span class="n">conf.apisix</span><span class="p">.</span><span class="n">router.http</span> <span class="ow">or</span> <span class="n">router_http_name</span>
</span></span><span class="line"><span class="cl">        <span class="p">......</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">router_http</span> <span class="o">=</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;apisix.http.router.&#34;</span> <span class="o">..</span> <span class="n">router_http_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">attach_http_router_common_methods</span><span class="p">(</span><span class="n">router_http</span><span class="p">)</span> <span class="c1">-- 这里 attach 两个方法给到模块</span>
</span></span><span class="line"><span class="cl">    <span class="n">router_http.init_worker</span><span class="p">(</span><span class="n">filter</span><span class="p">)</span> <span class="c1">-- 这里调用 init_worker 完成初始化</span>
</span></span><span class="line"><span class="cl">    <span class="n">_M.router_http</span> <span class="o">=</span> <span class="n">router_http</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">......</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><p>其中 <code>attach_http_router_common_methods</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">attach_http_router_common_methods</span><span class="p">(</span><span class="n">http_router</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">http_router.routes</span> <span class="o">==</span> <span class="kc">nil</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">http_router.routes</span> <span class="o">=</span> <span class="kr">function</span> <span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="ow">not</span> <span class="n">http_router.user_routes</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                <span class="kr">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">            <span class="c1">-- 返回模块中的 user_routes 的值值和版本</span>
</span></span><span class="line"><span class="cl">            <span class="kd">local</span> <span class="n">user_routes</span> <span class="o">=</span> <span class="n">http_router.user_routes</span>
</span></span><span class="line"><span class="cl">            <span class="kr">return</span> <span class="n">user_routes.values</span><span class="p">,</span> <span class="n">user_routes.conf_version</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">http_router.init_worker</span> <span class="o">==</span> <span class="kc">nil</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">http_router.init_worker</span> <span class="o">=</span> <span class="kr">function</span> <span class="p">(</span><span class="n">filter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">http_router.user_routes</span> <span class="o">=</span> <span class="n">http_route.init_worker</span><span class="p">(</span><span class="n">filter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="c1">-- 这里引用 local http_route = require(&#34;apisix.http.route&#34;) 的 init_worker</span>
</span></span><span class="line"><span class="cl">            <span class="c1">-- 获取 routes， 赋值给模块中的 user_routes</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/http/route.lua#L138">apisix/http/route.lua</a></p>
<p><code>http_route.init_worker</code> 从 <code>core.config.new(&quot;/routes&quot;, {})</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">init_worker</span><span class="p">(</span><span class="n">filter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">user_routes</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">core.config</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;/routes&#34;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">automatic</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">item_schema</span> <span class="o">=</span> <span class="n">core.schema</span><span class="p">.</span><span class="n">route</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">checker</span> <span class="o">=</span> <span class="n">check_route</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">filter</span> <span class="o">=</span> <span class="n">filter</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="kr">if</span> <span class="ow">not</span> <span class="n">user_routes</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">error</span><span class="p">(</span><span class="s2">&#34;failed to create etcd instance for fetching /routes : &#34;</span> <span class="o">..</span> <span class="n">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="n">user_routes</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><h3 id="2-具体某个routerlua">2. 具体某个router.lua</h3>
<pre tabindex="0"><code>apisix/http/router
├── radixtree_host_uri.lua
├── radixtree_uri.lua
└── radixtree_uri_with_parameter.lua
</code></pre><p>即，如果配置文件中有配置 <code>apisix.router.http</code>， 那么将会使用配置文件中的，否则使用 <code>radixtree_uri</code></p>
<p>假设我们配置了 <code>apisix.router.http=radixtree_uri_with_parameter</code></p>
<pre tabindex="0"><code>local router_http = require(&#34;apisix.http.router.radixtree_uri_with_parameter&#34;)
</code></pre><p>对应代码</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">require</span> <span class="o">=</span> <span class="n">require</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">core</span> <span class="o">=</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;apisix.core&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">base_router</span> <span class="o">=</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;apisix.http.route&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">get_services</span> <span class="o">=</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;apisix.http.service&#34;</span><span class="p">).</span><span class="n">services</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">cached_router_version</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">cached_service_version</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">_M</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="kd">local</span> <span class="n">uri_routes</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">uri_router</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">match</span><span class="p">(</span><span class="n">api_ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">user_routes</span> <span class="o">=</span> <span class="n">_M.user_routes</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">_</span><span class="p">,</span> <span class="n">service_version</span> <span class="o">=</span> <span class="n">get_services</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">cached_router_version</span> <span class="ow">or</span> <span class="n">cached_router_version</span> <span class="o">~=</span> <span class="n">user_routes.conf_version</span>
</span></span><span class="line"><span class="cl">        <span class="ow">or</span> <span class="ow">not</span> <span class="n">cached_service_version</span> <span class="ow">or</span> <span class="n">cached_service_version</span> <span class="o">~=</span> <span class="n">service_version</span>
</span></span><span class="line"><span class="cl">    <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">uri_router</span> <span class="o">=</span> <span class="n">base_router.create_radixtree_uri_router</span><span class="p">(</span><span class="n">user_routes.values</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                                             <span class="n">uri_routes</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">cached_router_version</span> <span class="o">=</span> <span class="n">user_routes.conf_version</span>
</span></span><span class="line"><span class="cl">        <span class="n">cached_service_version</span> <span class="o">=</span> <span class="n">service_version</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">uri_router</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">core.log</span><span class="p">.</span><span class="n">error</span><span class="p">(</span><span class="s2">&#34;failed to fetch valid `uri_with_parameter` router: &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="n">_M.matching</span><span class="p">(</span><span class="n">api_ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</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="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">matching</span><span class="p">(</span><span class="n">api_ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">core.log</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&#34;route match mode: radixtree_uri_with_parameter&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="n">base_router.match_uri</span><span class="p">(</span><span class="n">uri_router</span><span class="p">,</span> <span class="n">api_ctx</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</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="kr">return</span> <span class="n">_M</span>
</span></span></code></pre></div><p>说明：</p>
<ol>
<li>声明了 <code>match(api_ctx)</code> 和 <code>matching(api_ctx)</code>， 并且前者调用后者</li>
<li>声明了模块变量 <code>user_routes</code>，在启动时，通过 <code>http_route.init_worker(filter)</code> 被初始化</li>
<li>声明了模块变量 <code>cached_router_version/cached_service_version</code>, 用于判断当前 router 是否最新，如果 etcd watch到变更，那么会导致这里判断版本不一致，进而调用 <code>base_router.create_radixtree_uri_router(user_routes.values, uri_routes, true)</code>重建radixtree</li>
<li>这个过程是在 <code>match</code>中，意味着，数据面在处理请求的时候，执行<code>match</code>，发现service或route有更新，此时会发生radixtree重建</li>
</ol>
<p>这个可能会导致问题，见下面【特性跟踪-实时更新】</p>
<p>我们回到原点，关注 user_routes 的初始化</p>
<h3 id="3-user_routes">3. user_routes</h3>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/http/route.lua#L138">apisix/http/route.lua</a></p>
<p>引用 <code>local http_route = require(&quot;apisix.http.route&quot;)</code> 的 <code>init_worker</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">init_worker</span><span class="p">(</span><span class="n">filter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">user_routes</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">core.config</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;/routes&#34;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">automatic</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>  <span class="c1">-- 注意这个标志</span>
</span></span><span class="line"><span class="cl">            <span class="n">item_schema</span> <span class="o">=</span> <span class="n">core.schema</span><span class="p">.</span><span class="n">route</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">checker</span> <span class="o">=</span> <span class="n">check_route</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">filter</span> <span class="o">=</span> <span class="n">filter</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="kr">if</span> <span class="ow">not</span> <span class="n">user_routes</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">error</span><span class="p">(</span><span class="s2">&#34;failed to create etcd instance for fetching /routes : &#34;</span> <span class="o">..</span> <span class="n">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="n">user_routes</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/core.lua#L27">apisix/core.lua</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">config_provider</span> <span class="o">=</span> <span class="n">local_conf.deployment</span> <span class="ow">and</span> <span class="n">local_conf.deployment</span><span class="p">.</span><span class="n">config_provider</span>
</span></span><span class="line"><span class="cl">                      <span class="ow">or</span> <span class="s2">&#34;etcd&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">log.info</span><span class="p">(</span><span class="s2">&#34;use config_provider: &#34;</span><span class="p">,</span> <span class="n">config_provider</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">config</span> <span class="o">=</span> <span class="n">require</span><span class="p">(</span><span class="s2">&#34;apisix.core.config_&#34;</span> <span class="o">..</span> <span class="n">config_provider</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">config.type</span> <span class="o">=</span> <span class="n">config_provider</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">return</span> <span class="p">{</span>
</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 class="p">}</span>
</span></span></code></pre></div><p>配置的<code>config_provider=etcd</code>,  则</p>
<pre tabindex="0"><code>local config = require(&#34;apisix.core.config_etcd&#34;)
</code></pre><p>则调用的new方法是 <a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/core/config_etcd.lua#L932">apisix/core/config_etcd.lua</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="c1">---</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- Create a new connection to communicate with the control plane.</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- This function should be used in the `init_worker_by_lua` phase.</span>
</span></span><span class="line"><span class="cl"><span class="c1">--</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">opts</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="c1">-- etcd 相关配置</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">etcd_conf</span> <span class="o">=</span> <span class="n">local_conf.etcd</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">prefix</span> <span class="o">=</span> <span class="n">etcd_conf.prefix</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">resync_delay</span> <span class="o">=</span> <span class="n">etcd_conf.resync_delay</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">resync_delay</span> <span class="ow">or</span> <span class="n">resync_delay</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">resync_delay</span> <span class="o">=</span> <span class="mi">5</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">health_check_timeout</span> <span class="o">=</span> <span class="n">etcd_conf.health_check_timeout</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">health_check_timeout</span> <span class="ow">or</span> <span class="n">health_check_timeout</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">health_check_timeout</span> <span class="o">=</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">automatic</span> <span class="o">=</span> <span class="n">opts</span> <span class="ow">and</span> <span class="n">opts.automatic</span>
</span></span><span class="line"><span class="cl">    <span class="c1">-- 初始化对象</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">obj</span> <span class="o">=</span> <span class="n">setmetatable</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">        <span class="n">etcd_cli</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">key</span> <span class="o">=</span> <span class="n">key</span> <span class="ow">and</span> <span class="n">prefix</span> <span class="o">..</span> <span class="n">key</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">automatic</span> <span class="o">=</span> <span class="n">automatic</span><span class="p">,</span>  <span class="c1">-- true</span>
</span></span><span class="line"><span class="cl">        <span class="n">item_schema</span> <span class="o">=</span> <span class="n">item_schema</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">checker</span> <span class="o">=</span> <span class="n">checker</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">sync_times</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>   <span class="c1">-- 同步次数</span>
</span></span><span class="line"><span class="cl">        <span class="n">running</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">conf_version</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>  <span class="c1">-- 当前数据版本号，可以通过这个判断是否有更新</span>
</span></span><span class="line"><span class="cl">        <span class="n">values</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span>      <span class="c1">-- 所有同步过来的数据</span>
</span></span><span class="line"><span class="cl">        <span class="n">need_reload</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>  <span class="c1">-- 注意这里，首次 new 的时候，need_reload 是 true</span>
</span></span><span class="line"><span class="cl">        <span class="n">watching_stream</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">routes_hash</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">prev_index</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>     <span class="c1">-- 上一次执行同步etcd 返回的 modifiedIndex</span>
</span></span><span class="line"><span class="cl">        <span class="n">last_err</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">last_err_time</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">resync_delay</span> <span class="o">=</span> <span class="n">resync_delay</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">health_check_timeout</span> <span class="o">=</span> <span class="n">health_check_timeout</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">timeout</span> <span class="o">=</span> <span class="n">timeout</span><span class="p">,</span>  <span class="c1">-- 注意，这里没有设置timeout, 那么在连接的时候，http_waitdir 将使用代码中的默认值 ` local ok, err = self.watch_sema:wait(timeout or 60)`</span>
</span></span><span class="line"><span class="cl">        <span class="n">single_item</span> <span class="o">=</span> <span class="n">single_item</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">filter</span> <span class="o">=</span> <span class="n">filter_fun</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span> <span class="n">mt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">automatic</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <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="kr">if</span> <span class="n">loaded_configuration</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="kd">local</span> <span class="n">res</span> <span class="o">=</span> <span class="n">loaded_configuration</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">            <span class="n">loaded_configuration</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="kc">nil</span> <span class="c1">-- tried to load</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="n">log.notice</span><span class="p">(</span><span class="s2">&#34;use loaded configuration &#34;</span><span class="p">,</span> <span class="n">key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="kd">local</span> <span class="n">dir_res</span><span class="p">,</span> <span class="n">headers</span> <span class="o">=</span> <span class="n">res.body</span><span class="p">,</span> <span class="n">res.headers</span>
</span></span><span class="line"><span class="cl">            <span class="n">load_full_data</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">dir_res</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">   
</span></span><span class="line"><span class="cl">        <span class="c1">-- 启动一个定时器， local ngx_timer_at = ngx.timer.at</span>
</span></span><span class="line"><span class="cl">        <span class="n">ngx_timer_at</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">_automatic_fetch</span><span class="p">,</span> <span class="n">obj</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">else</span>
</span></span><span class="line"><span class="cl">        <span class="c1">-- 初始化 etcd_cli</span>
</span></span><span class="line"><span class="cl">        <span class="kd">local</span> <span class="n">etcd_cli</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">get_etcd</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="kr">if</span> <span class="ow">not</span> <span class="n">etcd_cli</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="kr">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="s2">&#34;failed to start an etcd instance: &#34;</span> <span class="o">..</span> <span class="n">err</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">        <span class="n">obj.etcd_cli</span> <span class="o">=</span> <span class="n">etcd_cli</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">-- 将 `/routes` 放入 created_obj</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">key</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">created_obj</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">obj</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="n">obj</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">    
</span></span></code></pre></div><p>在 <code>_automatic_fetch</code> 中, 执行结束前判断如果还在运行时，启动下一个定时器， 从而达到持续同步的目的，<code>ngx.timer.at</code>更多资料可以阅读 <a href="https://moonbingbing.gitbooks.io/openresty-best-practices/content/ngx_lua/timer.html">OpenResty最佳实践： 定时任务</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">_automatic_fetch</span><span class="p">(</span><span class="n">premature</span><span class="p">,</span> <span class="n">self</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="kd">local</span> <span class="n">ok</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">sync_data</span><span class="p">(</span><span class="n">self</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="kr">if</span> <span class="ow">not</span> <span class="n">exiting</span><span class="p">()</span> <span class="ow">and</span> <span class="n">self.running</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">ngx_timer_at</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">_automatic_fetch</span><span class="p">,</span> <span class="n">self</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><p>然后 <a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/core/config_etcd.lua#L594">sync_data</a> 进行数据同步, 注意<code>core.config.new(&quot;/routes, {})&quot;</code>的时候，首次调用 <code>sync_data</code>时， <code>need_reload=true</code></p>
<p>所以这里会涉及两个关键函数</p>
<ul>
<li>如果 <code>need_reload=true</code>， 调用 <code>readdir(self.etcd_cli, self.key)</code>， 全量拉取数据， 最终调用 <code>load_full_data(self, dir_res, headers)</code></li>
<li>否则， 调用 <code> waitdir(self)</code> watch增量变更数据, 更新 <code>self.values</code> 以及更新版本 <code> self.prev_index = new_ver</code>
<ul>
<li>如果<code>self.sync_times&gt;100</code>， 会重建 <code>self.values</code>和<code>self.values_hash</code></li>
<li><code>self.conf_version = self.conf_version + 1</code> (外部可以通过这个判断是否存在更新）</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">sync_data</span><span class="p">(</span><span class="n">self</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">self.key</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="s2">&#34;missing &#39;key&#39; arguments&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">init_watch_ctx</span><span class="p">(</span><span class="n">self.key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">self.need_reload</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="kd">local</span> <span class="n">res</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">readdir</span><span class="p">(</span><span class="n">self.etcd_cli</span><span class="p">,</span> <span class="n">self.key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">if</span> <span class="ow">not</span> <span class="n">res</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="kr">return</span> <span class="kc">false</span><span class="p">,</span> <span class="n">err</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">local</span> <span class="n">dir_res</span><span class="p">,</span> <span class="n">headers</span> <span class="o">=</span> <span class="n">res.body</span><span class="p">.</span><span class="n">list</span> <span class="ow">or</span> <span class="n">res.body</span><span class="p">.</span><span class="n">node</span> <span class="ow">or</span> <span class="p">{},</span> <span class="n">res.headers</span>
</span></span><span class="line"><span class="cl">        <span class="n">log.debug</span><span class="p">(</span><span class="s2">&#34;readdir key: &#34;</span><span class="p">,</span> <span class="n">self.key</span><span class="p">,</span> <span class="s2">&#34; res: &#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                  <span class="n">json.delay_encode</span><span class="p">(</span><span class="n">dir_res</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kr">if</span> <span class="n">self.values</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="kr">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">val</span> <span class="kr">in</span> <span class="n">ipairs</span><span class="p">(</span><span class="n">self.values</span><span class="p">)</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">                <span class="n">config_util.fire_all_clean_handlers</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="n">self.values</span> <span class="o">=</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">            <span class="n">self.values_hash</span> <span class="o">=</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">load_full_data</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">dir_res</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">dir_res</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">waitdir</span><span class="p">(</span><span class="n">self</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">dir_res</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="kr">if</span> <span class="n">err</span> <span class="o">==</span> <span class="s2">&#34;compacted&#34;</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="n">self.need_reload</span> <span class="o">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">            <span class="n">log.error</span><span class="p">(</span><span class="s2">&#34;waitdir [&#34;</span><span class="p">,</span> <span class="n">self.key</span><span class="p">,</span> <span class="s2">&#34;] err: &#34;</span><span class="p">,</span> <span class="n">err</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                     <span class="s2">&#34;, will read the configuration again via readdir&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">return</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="kc">false</span><span class="p">,</span> <span class="n">err</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span></code></pre></div><p>另外，如果 <code>waitdir</code>的时候，发现 etcd 发生了 <code>compacted</code>， 将会设置 <code>self.need_reload = true</code>， 触发全量同步, 可能会带来问题，见下面【问题】</p>
<h2 id="问题">问题</h2>
<h3 id="etcd-compacted">etcd compacted</h3>
<p>apisix 同步 etcd, 如果 etcd 发生了 compact, 此时apisix watch 的revision 小于 etcd compact revision, apisix watch 到事件变更会直接触发全量同步, 具体机制参考<a href="https://github.com/apache/apisix/issues/10500#issuecomment-1818315794">文档</a></p>
<p>带来的问题:</p>
<ul>
<li>
<p>apisix本身的性能抖动(全量同步后, 会重建radixtree)</p>
<ul>
<li>会导致请求 <code>499</code>(相当于请求进来，在等待radixtree重建，客户端等不及或配置了timeout， 主动关闭掉了连接)</li>
</ul>
</li>
<li>
<p>etcd 读 IO 波峰 / 内存波峰(所有连接的 worker 都进行了全量拉取)</p>
</li>
</ul>
<p>解决方案:
默认etcd的配置 <code>--auto-compaction-mode=periodic --auto-compaction-retention=5m</code>, 如果apisix实例很多, 无论怎么配置, apisix 全量同步的概率还是很大;</p>
<p>建议将配置改成 <code>--auto-compaction-mode=revision --auto-compaction-retention=1000</code>, 默认会保留 1000 个有效的<code>revision</code></p>
<h3 id="实时更新的问题">实时更新的问题</h3>
<p>压测可以看到，如果持续变更，那么所有请求在同一个时间 rebuild radixtree</p>
<pre tabindex="0"><code>2023/10/09 12:10:26 [info] 38#38: *231868 [lua] radixtree.lua:355: pre_insert_route(): 
2023/10/09 12:10:26 [info] 40#40: *231883 [lua] radixtree.lua:355: pre_insert_route(): 
2023/10/09 12:10:26 [info] 37#37: *231820 [lua] radixtree.lua:355: pre_insert_route(): 
2023/10/09 12:10:26 [info] 39#39: *231882 [lua] radixtree.lua:355: pre_insert_route(): 
</code></pre><p>如果路由比较多，并且存在路由频繁更新，那么可能带来性能抖动。</p>
<p>因为 watch 变更后，下一次请求对比版本的时候发现不一致，会重建 radixtree， 而请求需要等到重建之后才继续执行；</p>
<p>我提过一个issue <a href="https://github.com/apache/apisix/issues/10268">help request: Is the radix tree rebuilt every time any route is updated?</a> 讨论这个事情。</p>
<p>最终，我们暂时是通过将重建打散到一个时间范围内，确保所有线上实例不会在同一时间rebuild radixtree， 有需要可以参考 <a href="https://github.com/TencentBlueKing/blueking-apigateway-apisix/blob/master/src/build/patches/004_radixtree_uri_with_parameter_rebuild_with_interval.patch">patch</a></p>
<h2 id="特性追踪">特性追踪</h2>
<h3 id="etcd-http">etcd http</h3>
<p>在 <a href="https://github.com/apache/apisix/issues/6473">APISIX&rsquo;s V3 (2022) Roadmap</a> 中提到 <code>Connect to etcd via gRPC and reduce the number of etcd connection</code></p>
<p>最早使用 http api 连接 etcd， 意味着每个worker中的每一种资源类型都会存在连接（实例数 * worker 数 * 资源类型数）。</p>
<p>在apisix 3.2.2 合入了一个 PR<a href="https://github.com/apache/apisix/pull/9456">feat(config_etcd): use a single long http connection to watch all resources</a>, 使用一个 http connection 来watch 所有资源类型，这样能有效降低连接数（实例数*worker 数， 当然还是可能很大）。但是，这个 PR 引入了一个bug <a href="https://github.com/apache/apisix/issues/9951">bug: route 404 after upgrade to 3.2.2</a>, 当 etcd prefix 中带了<code>-</code>会 watch 不到，这个在 <strong>3.5.0</strong>及以上的版本中被修复 <a href="https://github.com/apache/apisix/pull/9967">fix: can&rsquo;t sync etcd data if key has special character </a></p>
<p><img src="https://private-user-images.githubusercontent.com/4401042/238114137-d3642daf-898d-4f4e-8ad0-446cf46e5694.svg?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjY1MjY2MTMsIm5iZiI6MTcyNjUyNjMxMywicGF0aCI6Ii80NDAxMDQyLzIzODExNDEzNy1kMzY0MmRhZi04OThkLTRmNGUtOGFkMC00NDZjZjQ2ZTU2OTQuc3ZnP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDkxNiUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA5MTZUMjIzODMzWiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NjIzZGFiNmRlMGQxYjExZTY2OGJhYzA3ZTE3N2Y3MTg2ODhkYmZiM2E1NmU3Y2ZiODAzZjY5ZTQ1NzIzMzlmMCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.v3ppz7HwUUDyU_ed4gBDXjoBSnR9tOFLZu3CTq6sKJk" alt=""></p>
<h3 id="etcd-grpc">etcd grpc</h3>
<p>原先在配置 etcd 连接的时候是支持配置 <code>use_grpc: true</code> 通过 grpc 连接 etcd 的</p>
<p>后来 <a href="https://lists.apache.org/thread/b69vjkbdszdtk9y30k45c2tvg4f3hqwt">[DISCUSS] Proposal: APISIX: remove etcd grpc and conf server</a> 的讨论中， <strong>this module has too many bugs</strong> ， 最终在 <a href="https://github.com/apache/apisix/pull/10015">PR:fix: remove etcd.use_grpc</a>中移除了。</p>
<h3 id="增量更新">增量更新</h3>
<p>社区曾经有个 pr <a href="https://github.com/apache/apisix/pull/9692">feat: increment route update for radixtree host uri, radixtree uri and radi…</a> ，但是最终被close 了（个人认为这个 PR 还是很重要的）。</p>
<p>PS：近期 apisix和 apisix-ingress-controller github 仓库的 issue 陆续被无差别close(机器人根据时间、活跃自动化执行的)， 但是其中有一些是比较重要的 issue。并且比较影响用户的积极性和活跃度，再持续下去可能后续就没多少 issue 了。</p>
<h2 id="依赖库">依赖库</h2>
<blockquote>
<p><a href="https://github.com/api7/lua-resty-etcd">lua-resty-etcd</a>  Nonblocking Lua etcd driver library for OpenResty, this module supports etcd API v3.</p>
</blockquote>
<p>涉及函数</p>
<h3 id="readdirhttpsgithubcomapi7lua-resty-etcdblobmasterapi_v3mdreaddir"><a href="https://github.com/api7/lua-resty-etcd/blob/master/api_v3.md#readdir">readdir</a>:</h3>
<ul>
<li>syntax: <code>res, err = cli:readdir(dir:string [, opts:table])</code></li>
<li>opts
<ul>
<li>timeout: (int) request timeout seconds. Set to 0 would use <code>lua_socket_connect_timeout</code> as timeout. (5 seconds)</li>
<li>revision: (int) revision is the point-in-time of the key-value store to use for the range. If revision is less than or equal to zero, the range is over the newest key-value store. <strong>If the revision has been compacted, ErrCompacted is returned as a response.</strong></li>
<li>limit: (int) limit is a limit on the number of keys returned for the request. When limit is set to 0, it is treated as no limit.</li>
<li>sort_order: (int [SortNone:0, SortAscend:1, SortDescend:2]) sort_order is the order for returned sorted results.</li>
<li>sort_target: (int [SortByKey:0, SortByVersion:1, SortByCreateRevision:2, SortByModRevision:3, SortByValue:4]) sort_target is the key-value field to use for sorting.</li>
<li>keys_only: (bool) keys_only when set returns only the keys and not the values.</li>
<li>count_only: (bool) count_only when set returns only the count of the keys in the range.</li>
</ul>
</li>
</ul>
<p>特别注意，当revision被<code>compacted</code>，将会返回异常 <code>ErrCompacted</code></p>
<h3 id="watchdirhttpsgithubcomapi7lua-resty-etcdblobmasterapi_v3mdwatchdir"><a href="https://github.com/api7/lua-resty-etcd/blob/master/api_v3.md#watchdir">watchdir</a></h3>
<ul>
<li>syntax: <code>res, err = cli:watchdir(dir:string [, opts:table])</code></li>
<li>opts: optional options.
<ul>
<li>timeout: (int) request timeout seconds. Set to 0 would use <code>lua_socket_connect_timeout</code> as timeout. (5 seconds)</li>
<li>start_revision: (int) start_revision is an optional revision to watch from (inclusive). No start_revision is &ldquo;now&rdquo;.</li>
<li>progress_notify: (bool) progress_notify is set so that the etcd server will periodically send a WatchResponse with no events to the new watcher if there are no recent events.</li>
<li>filters: (slice of [enum FilterType {NOPUT = 0;NODELETE = 1;}]) filters filter the events at server side before it sends back to the watcher.</li>
<li>prev_kv: (bool) If prev_kv is set, created watcher gets the previous KV before the event happens. If the previous KV is already compacted, nothing will be returned.</li>
<li>watch_id: (int) If watch_id is provided and non-zero, it will be assigned to this watcher. Since creating a watcher in etcd is not a synchronous operation, this can be used to ensure that ordering is correct when creating multiple watchers on the same stream. Creating a watcher with an ID already in use on the stream will cause an error to be returned.</li>
<li>fragment: (bool) fragment enables splitting large revisions into multiple watch responses.</li>
</ul>
</li>
</ul>
<p>注意， watchdir 默认如果超过 <code>5s</code>没有结束，会直接timeout异常</p>
]]></content>
		</item>
		
		<item>
			<title>apisix 中的 DNS 解析</title>
			<link>https://wklken.me/posts/2024/09/16/apisix-dns-resolve.html</link>
			<pubDate>Mon, 16 Sep 2024 00:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2024/09/16/apisix-dns-resolve.html</guid>
			<description>基于 3.10.0 版本 入口 在 apisix 的 ngx_tpl.lua 中相关的配置 access_by_lua_block { apisix.http_access_phase() } 即，请求在 access phase， 会走到 http_access_phase 函数 在 apisix/init.lua 的 http_access_phase 中确定匹配到的 route之后， 调用了 _M.handle_upstream(api_ctx, route, enable_websocket) 主入口： handle_upstream _M.handle_upstream</description>
			<content type="html"><![CDATA[<blockquote>
<p>基于 3.10.0 版本</p>
</blockquote>
<h2 id="入口">入口</h2>
<p><img src="https://cloud.githubusercontent.com/assets/2137369/15272097/77d1c09e-1a37-11e6-97ef-d9767035fc3e.png" alt=""></p>
<p>在 apisix 的 ngx_tpl.lua 中相关的配置</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nginx" data-lang="nginx"><span class="line"><span class="cl">            <span class="k">access_by_lua_block</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="kn">apisix.http_access_phase()</span>
</span></span><span class="line"><span class="cl">            <span class="err">}</span>
</span></span></code></pre></div><p>即，请求在 access phase， 会走到 <code>http_access_phase</code> 函数</p>
<p>在 apisix/init.lua 的 <code>http_access_phase</code> 中确定匹配到的 <code>route</code>之后， 调用了 <code>_M.handle_upstream(api_ctx, route, enable_websocket)</code></p>
<h2 id="主入口-handle_upstream">主入口： handle_upstream</h2>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/init.lua#L463">_M.handle_upstream</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">handle_upstream</span><span class="p">(</span><span class="n">api_ctx</span><span class="p">,</span> <span class="n">route</span><span class="p">,</span> <span class="n">enable_websocket</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">up_id</span> <span class="o">=</span> <span class="n">route.value</span><span class="p">.</span><span class="n">upstream_id</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">up_id</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="kd">local</span> <span class="n">upstream</span> <span class="o">=</span> <span class="n">apisix_upstream.get_by_id</span><span class="p">(</span><span class="n">up_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">if</span> <span class="ow">not</span> <span class="n">upstream</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="n">is_http</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                <span class="kr">return</span> <span class="n">core.response</span><span class="p">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">502</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="kr">return</span> <span class="n">ngx_exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">api_ctx.matched_upstream</span> <span class="o">=</span> <span class="n">upstream</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">else</span>
</span></span><span class="line"><span class="cl">        <span class="kr">if</span> <span class="n">route.has_domain</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="kd">local</span> <span class="n">err</span>
</span></span><span class="line"><span class="cl">            <span class="n">route</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">parse_domain_in_route</span><span class="p">(</span><span class="n">route</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="n">err</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                <span class="n">core.log</span><span class="p">.</span><span class="n">error</span><span class="p">(</span><span class="s2">&#34;failed to get resolved route: &#34;</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="kr">return</span> <span class="n">core.response</span><span class="p">.</span><span class="n">exit</span><span class="p">(</span><span class="mi">500</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</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="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="kd">local</span> <span class="n">route_val</span> <span class="o">=</span> <span class="n">route.value</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">api_ctx.matched_upstream</span> <span class="o">=</span> <span class="p">(</span><span class="n">route.dns_value</span> <span class="ow">and</span>
</span></span><span class="line"><span class="cl">                                    <span class="n">route.dns_value</span><span class="p">.</span><span class="n">upstream</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                                   <span class="ow">or</span> <span class="n">route_val.upstream</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span></code></pre></div><p>这里，如果有 upstream， 走upstream， 否则，走route； route包含域名，则 <code>parse_domain_in_route(route)</code></p>
<p>调用链路:</p>
<pre tabindex="0"><code>if up_id:

  apisix/init.lua: apisix_upstream.get_by_id(up_id)
  -&gt;
  apisix/upstream.lua: get_by_id(up_id) 
                       -&gt; upstream_util.parse_domain_in_up(upstream)
  -&gt;
  apisix/utils/upstream.lua: parse_domain_in_up(up) 
                           -&gt; parse_domain_for_nodes(nodes) 
                           -&gt; core.resolver.parse_domain(host)
                           
else:
  if route.has_domain:
  apisix/init.lua: parse_domain_in_route(route) 
                   -&gt; upstream_util.parse_domain_for_nodes(nodes)
  -&gt;
  apisix/utils/upstream.lua: parse_domain_for_nodes(nodes) 
                           -&gt; core.resolver.parse_domain(host)

公共部分:

-&gt;
apisix/core/resolver.lua: parse_domain(host) -&gt; utils.dns_parse(host)
-&gt; 
apisix/core/utils.lua: dns_client.new(opts) -&gt;  current_dns_client:resolve(domain, selector)
-&gt;
lib: resty.dns.client
</code></pre><h3 id="1-parse_domain_for_nodes">1. parse_domain_for_nodes</h3>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/utils/upstream.lua#L64">parse_domain_for_nodes</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">parse_domain_for_nodes</span><span class="p">(</span><span class="n">nodes</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">new_nodes</span> <span class="o">=</span> <span class="n">core.table</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="o">#</span><span class="n">nodes</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">node</span> <span class="kr">in</span> <span class="n">ipairs</span><span class="p">(</span><span class="n">nodes</span><span class="p">)</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">        <span class="kd">local</span> <span class="n">host</span> <span class="o">=</span> <span class="n">node.host</span>
</span></span><span class="line"><span class="cl">        <span class="kr">if</span> <span class="ow">not</span> <span class="n">ipmatcher.parse_ipv4</span><span class="p">(</span><span class="n">host</span><span class="p">)</span> <span class="ow">and</span>
</span></span><span class="line"><span class="cl">                <span class="ow">not</span> <span class="n">ipmatcher.parse_ipv6</span><span class="p">(</span><span class="n">host</span><span class="p">)</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">            <span class="kd">local</span> <span class="n">ip</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">core.resolver</span><span class="p">.</span><span class="n">parse_domain</span><span class="p">(</span><span class="n">host</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="n">ip</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                <span class="kd">local</span> <span class="n">new_node</span> <span class="o">=</span> <span class="n">core.table</span><span class="p">.</span><span class="n">clone</span><span class="p">(</span><span class="n">node</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="n">new_node.host</span> <span class="o">=</span> <span class="n">ip</span>
</span></span><span class="line"><span class="cl">                <span class="n">new_node.domain</span> <span class="o">=</span> <span class="n">host</span>
</span></span><span class="line"><span class="cl">                <span class="n">core.table</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">new_nodes</span><span class="p">,</span> <span class="n">new_node</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="n">err</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                <span class="n">core.log</span><span class="p">.</span><span class="n">error</span><span class="p">(</span><span class="s2">&#34;dns resolver domain: &#34;</span><span class="p">,</span> <span class="n">host</span><span class="p">,</span> <span class="s2">&#34; error: &#34;</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">        <span class="kr">else</span>
</span></span><span class="line"><span class="cl">            <span class="n">core.table</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">new_nodes</span><span class="p">,</span> <span class="n">node</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="n">new_nodes</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl"><span class="n">_M.parse_domain_for_nodes</span> <span class="o">=</span> <span class="n">parse_domain_for_nodes</span>
</span></span></code></pre></div><p>注意， 这里没有检测最终结果是否是空的，导致 new_nodes为空 table 返回，  <code>local new_nodes, err = parse_domain_for_nodes(nodes)</code></p>
<p>会带来一个问题，当dns server短时间不可用之后又恢复了，此时apisix中的dns解析并没有恢复，还是会持续失败， 报</p>
<p>dns server 异常时</p>
<pre tabindex="0"><code>[lua] upstream.lua:79: parse_domain_for_nodes(): dns resolver domain: test.com error: failed to query the DNS server: dns client error: 101 empty record received
[lua] resolver.lua:80: parse_domain(): failed to parse domain: test.com, error: failed to query the DNS server: dns client error: 101 empty record received
</code></pre><p>并且， 在dns  server恢复后</p>
<pre tabindex="0"><code>[lua] init.lua:486: handle_upstream(): failed to set upstream: no valid upstream node
</code></pre><p>直到重启apisix， 或者执行<code>apisix reload</code></p>
<p>之前提过一个issue <a href="https://github.com/apache/apisix/issues/10093">bug: dns resolution did not resume immediately after the dns server resume</a>, 后来由于开发没有复现，该单据已经被关闭（实际上是可以复现的）；目前的修复方式是patch， 在 <code>return new_nodes</code>前面加上判断， 具体可以参考 <a href="https://github.com/TencentBlueKing/blueking-apigateway-apisix/pull/52/files">fix(build/patches): add patch for parse_domain_for_nodes in apisix/utils/upstream.lua</a></p>
<h3 id="2-parse_domainhost">2. parse_domain(host)</h3>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/core/resolver.lua#L61">parse_domain</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">parse_domain</span><span class="p">(</span><span class="n">host</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">ip_info</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">utils.dns_parse</span><span class="p">(</span><span class="n">host</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">ip_info</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">log.error</span><span class="p">(</span><span class="s2">&#34;failed to parse domain: &#34;</span><span class="p">,</span> <span class="n">host</span><span class="p">,</span> <span class="s2">&#34;, error: &#34;</span><span class="p">,</span><span class="n">err</span><span class="p">)</span> <span class="c1">-- line 80</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="n">err</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span></code></pre></div><h3 id="3-dns_parsedomain-selector">3. dns_parse(domain, selector)</h3>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/core/utils.lua#L95">dns_parse</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">dns_parse</span><span class="p">(</span><span class="n">domain</span><span class="p">,</span> <span class="n">selector</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="kr">return</span> <span class="n">current_dns_client</span><span class="p">:</span><span class="n">resolve</span><span class="p">(</span><span class="n">domain</span><span class="p">,</span> <span class="n">selector</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="4-resolvedomain">4. resolve(domain)</h3>
<p><a href="https://github.com/apache/apisix/blob/5433dce25de6c3aabb14919f899d9e8aa6a16c4b/apisix/core/dns/client.lua#L95">resolve</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">_M</span><span class="p">.</span><span class="nf">resolve</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">domain</span><span class="p">,</span> <span class="n">selector</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">client</span> <span class="o">=</span> <span class="n">self.client</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">-- this function will dereference the CNAME records</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">answers</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">client.resolve</span><span class="p">(</span><span class="n">domain</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="ow">not</span> <span class="n">answers</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="s2">&#34;failed to query the DNS server: &#34;</span> <span class="o">..</span> <span class="n">err</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">answers.errcode</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="s2">&#34;server returned error code: &#34;</span> <span class="o">..</span> <span class="n">answers.errcode</span>
</span></span><span class="line"><span class="cl">                    <span class="o">..</span> <span class="s2">&#34;: &#34;</span> <span class="o">..</span> <span class="n">answers.errstr</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">selector</span> <span class="o">==</span> <span class="n">_M.RETURN_ALL</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">log.info</span><span class="p">(</span><span class="s2">&#34;dns resolve &#34;</span><span class="p">,</span> <span class="n">domain</span><span class="p">,</span> <span class="s2">&#34;, result: &#34;</span><span class="p">,</span> <span class="n">json.delay_encode</span><span class="p">(</span><span class="n">answers</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="kr">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">answer</span> <span class="kr">in</span> <span class="n">ipairs</span><span class="p">(</span><span class="n">answers</span><span class="p">)</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">            <span class="kr">if</span> <span class="n">answer.type</span> <span class="o">==</span> <span class="n">client.TYPE_SRV</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">                <span class="kr">return</span> <span class="n">resolve_srv</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">answers</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="kr">end</span>
</span></span><span class="line"><span class="cl">        <span class="kr">end</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="n">table.deepcopy</span><span class="p">(</span><span class="n">answers</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">idx</span> <span class="o">=</span> <span class="n">math_random</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">#</span><span class="n">answers</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">answer</span> <span class="o">=</span> <span class="n">answers</span><span class="p">[</span><span class="n">idx</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">local</span> <span class="n">dns_type</span> <span class="o">=</span> <span class="n">answer.type</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">dns_type</span> <span class="o">==</span> <span class="n">client.TYPE_A</span> <span class="ow">or</span> <span class="n">dns_type</span> <span class="o">==</span> <span class="n">client.TYPE_AAAA</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">        <span class="n">log.info</span><span class="p">(</span><span class="s2">&#34;dns resolve &#34;</span><span class="p">,</span> <span class="n">domain</span><span class="p">,</span> <span class="s2">&#34;, result: &#34;</span><span class="p">,</span> <span class="n">json.delay_encode</span><span class="p">(</span><span class="n">answer</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="kr">return</span> <span class="n">table.deepcopy</span><span class="p">(</span><span class="n">answer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="s2">&#34;unsupported DNS answer&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></div><h2 id="依赖库">依赖库</h2>
<p><a href="https://github.com/api7/lua-resty-dns-client">api7/lua-resty-dns-client</a></p>
<p>从 <a href="https://github.com/Kong/lua-resty-dns-client">https://github.com/Kong/lua-resty-dns-client</a>  fork 过来，改了什么：</p>
<ul>
<li>feat: support run in stream subsystem <a href="https://github.com/api7/lua-resty-dns-client/pull/1">https://github.com/api7/lua-resty-dns-client/pull/1</a></li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>如何减少打断提升效率</title>
			<link>https://wklken.me/posts/2024/08/25/how-to-reduce-interruptions-and-improve-efficiency.html</link>
			<pubDate>Sun, 25 Aug 2024 18:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2024/08/25/how-to-reduce-interruptions-and-improve-efficiency.html</guid>
			<description>去年接手一个上线小十年的系统，由于这个系统是出于上下游的中间环节（意味着会跟几乎所有系统打交道），并且存在着大量的历史包袱（意味着很多黑盒，</description>
			<content type="html"><![CDATA[<p>去年接手一个上线小十年的系统，由于这个系统是出于上下游的中间环节（意味着会跟几乎所有系统打交道），并且存在着大量的历史包袱（意味着很多黑盒，一些奇怪的用法以及机制），导致这个系统的<strong>日常</strong>工作最为繁重。</p>
<p>我们面向的用户是所有的系统开发者，问题涉及各类调用问题，白名单，权限审批，机制，线上问题排查等等，其中很大一部分需要有实时反馈，这就意味着日常工作中的打断非常多，进而导致团队的效率下降，以及整体产出变少。</p>
<p>对此，经过近一年的优化和改进，我们已经成功地将打断减少了 90% 以上，并且确保<strong>服务</strong>质量。</p>
<p>以下分享一些过程中的经验</p>
<h2 id="让每个人深切感受到打断">让每个人深切感受到<code>打断</code></h2>
<blockquote>
<p>所有人都需要真切感受到被打断、拉群</p>
</blockquote>
<ul>
<li>轮值作为接口人，不能让某个人或某几个人单独作为这种打断的受害者，脏活累活得平摊开</li>
<li>项目的负责人不能例外</li>
<li>产品经理也不能例外，直接在一些接收用户的反馈</li>
</ul>
<p>跟吃自己的狗粮类似，所有人参与到轮值中，就能覆盖到各类咨询问题的方方面面。</p>
<h2 id="记录记录记录">记录，记录，记录</h2>
<p>轮值的助手需要记录：</p>
<ol>
<li>用户咨询的问题</li>
<li>原因及解决方案
<ol>
<li>如果是操作类，做了哪些操作，操作路径是什么</li>
<li>如果是产品问题，应该怎么处理更好</li>
</ol>
</li>
<li>耗时</li>
</ol>
<p>有记录，才有统计，有统计，才能更高效直接地解决问题</p>
<p>我们的项目轮值经过 2 个月左右，基本已经清楚地知道所有打断的来源</p>
<h2 id="统计和分类">统计和分类</h2>
<p>分析所有的记录，将相同的问题汇聚计数，得到一张表格</p>
<p>分类：</p>
<ul>
<li>咨询类：哪些问题，出现频次，如何解决</li>
<li>操作类：什么操作，操作的链路， 出现频次</li>
</ul>
<p>二八定律，先处理 20% 的核心问题，解决掉 80% 的打断</p>
<h2 id="处理">处理</h2>
<h3 id="1-文档体系">1. 文档体系</h3>
<p>我们发现，虽然这个系统已经存在了小十年，但是并没有太多的文档，并且文档非最新、含糊不清。</p>
<p>大量的咨询原因是没有文档，或者文档看不懂， 所以我们决定重新构建文档体系，替代掉原先的文档站点。</p>
<p>注意：</p>
<ul>
<li>文档的目的是让所有人可达，所以优先选择所有人员可见的方式</li>
<li>文档的编辑成本需要足够低，用户反馈-修改-生效，最好选择所见即所得的方案，例如wiki(而不是需要仓库管理-编辑-review-合并-发布才生效)，低成本，高频率地持续维护</li>
<li>文档不要啰嗦，不要歧义，信息量足</li>
<li>有目录，有锚点，方便分享</li>
<li>重要，容易误操作的地方一定要重点强调</li>
</ul>
<p>之前有参考一些最佳实践，文档体系由 4 个部分组成</p>
<ul>
<li>Tutorials 教程，学习相关，例如quick start/demo等</li>
<li>How-To Guides， 问题相关，如何解决一个具体的文昌塔</li>
<li>Explanation， 概念相关，用于深入理解某些概念</li>
<li>Reference， 引用相关，用于提供更多的参考信息， 例如 API 文档</li>
</ul>
<p>而具体划分，可能还会涉及</p>
<ol>
<li>产品介绍（能做什么，最新的动态，升级日志等等）</li>
<li>使用指南（产品页面功能相关的step-by-step使用指引，保姆式教学）</li>
<li>概念说明（概念维度的组织方式）</li>
<li>常见问题 FAQ （<strong>重点</strong>，所有常见问题的解答，包含一些列说明和相关文档的引用）</li>
<li>问题反馈</li>
</ol>
<p>这样，文档就成了一张网，用户不管从什么页面进去，都能够找到相关的概念、背景、用法、DEMO、FAQ， 足够清晰就将用户可能的问题全部在这个阶段解决掉。</p>
<p>并且，在面对咨询的时候，可以通过直接扔文档链接的方式解决，无须废话。</p>
<p>目前大模型已经足够聪明，能够辅助解决问题，所以我们将整个文档站点喂给了大模型，问题咨询的时候直接拉机器人进群，用户发问题的时候机器人协助解答，也拦截了很大一部分问题。</p>
<h3 id="2-操作的合理性必要性">2. 操作的合理性、必要性</h3>
<p>对于操作类，我们首先评估合理性，是否有存在的必要？</p>
<p>例如，有一个加调用方 IP 白名单的操作类，每一个调用方都需要找过来加白，而原先的老系统也做了一个快速加白的入口，用户找过来申请加白-打开系统-完成加白，这似乎没毛病，也存在了很多年一直是这么做的。</p>
<p>但是这样做有意义吗？ 无脑加白=没有白名单限制，后来才发现，是 N 年前的安全团队扫描推动加的，但是其实已经不需要了，形同虚设。后来直接给内网网段加白，废弃掉这个流程</p>
<p>同样，还有一些接口权限申请，最早可能有用，但是随着时间流逝，有些权限审批变得不再必要，所以系统上改变，非敏感的不需要审批直接通过，敏感的走自动审批流审批，而不是集中在系统开发这里进行人工审批</p>
<h3 id="3-自动化及分派">3. 自动化及分派</h3>
<blockquote>
<p>能自动化的绝不手工做，能别人做的绝不自己做</p>
</blockquote>
<p>有一些操作是有其存在的合理性的，但是这类操作的操作链路特别长，涉及多套系统。</p>
<p>我们其实是有职能类的人工助手的，但是在这个阶段他们没法做任何协助。</p>
<p>后来我们做了一套系统（3 人、一周），将高频的十个操作整合在一起，配合权限控制、审计，然后将这类操作直接分派给了职能化助手。</p>
<p>这套系统直接在前置步骤拦截了几乎所有的操作类问题， 3 人一周的成本非常划算</p>
<h3 id="4-其他系统带来的成本">4. 其他系统带来的成本</h3>
<p>我们发现有几个高频操作是其他系统带来的，他们在文档上直接指引使用者，找到我们系统的开发做一些操作、配置。</p>
<p>实际上这样给我们带来了非常大的工作量，以及困扰。</p>
<p>所以我们最终：</p>
<ol>
<li>写了相关明确的指引文档</li>
<li>在产品做了相应的快捷按钮</li>
</ol>
<p>然后反向推这些系统的维护者改文档说明和入口，直接让用户自助解决问题。</p>
<h3 id="5-faq">5. FAQ</h3>
<p>咨询中很大一部分是接口调用后的返回问题</p>
<p>我们直接在文档中，将所有报错返回进行枚举，并将每种报错的可能原因，排查方式明确，直接让用户通过关键字搜索就能自行排查问题</p>
<p>另外咨询中一部分是关于一些系统的限制配置，我们直接将代码中各类限制放到了文档中。</p>
<h3 id="6-热点问题置顶">6. 热点问题置顶</h3>
<p>我们发现，总是有一个问题：用户无法识别是自己的问题还是我们系统的问题，所以总是发起咨询。</p>
<p>后来我们将这个做成一个专题，并且置顶，放入用户排查问题的必经链路中。</p>
]]></content>
		</item>
		
		<item>
			<title>控糖革命</title>
			<link>https://wklken.me/posts/2024/06/23/glucose-revolution.html</link>
			<pubDate>Sun, 23 Jun 2024 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2024/06/23/glucose-revolution.html</guid>
			<description>上半年去了医院，空腹血糖偏高，虽然还没有超出阈值，但是医生建议减重+低糖饮食。 然后，开始有目的性地减少糖，例如早餐减少碳水摄入，不再喝可乐等</description>
			<content type="html"><![CDATA[<p>上半年去了医院，空腹血糖偏高，虽然还没有超出阈值，但是医生建议减重+低糖饮食。</p>
<p>然后，开始有目的性地减少糖，例如早餐减少碳水摄入，不再喝可乐等含糖饮料，晚上戒掉夜宵等，三个月顺利减重 15斤，但是目前到了平台期，体重还有接近 10 斤要减，但是 app 提醒是基础代谢太低导致的，可能缺乏运动。</p>
<p>期间读了这本书，有些观点可以作为参考。</p>
<p>以下仅为简单的摘录总结</p>
<p><img src="/imgs/books/glucose-revolution.jpg" alt="封面"></p>
<hr>
<p>全书分为三部分，为什么要控糖，出现葡糖糖峰值的 10 个危害，轻松控糖的 10 个窍门。其中前两者可以快速过下，最后一部分可以看看，有些做法可以参考，例如先吃蔬菜和蛋白质，最后吃碳水。</p>
<p>引用了很多<code>事例</code>，但是缺乏一些支撑。</p>
<h2 id="第一部分-为什么要控糖">第一部分 为什么要控糖</h2>
<ul>
<li>我们的身体需要葡萄糖，但是平稳的血糖曲线非常重要（果糖，胰岛素水平很难连续被监测，但是血糖可以）；</li>
<li>葡萄糖以不同的形式存在：淀粉，纤维，果糖和蔗糖；</li>
<li>碳水化合物=淀粉+纤维+糖类（葡萄糖、果糖、蔗糖）；</li>
<li>大自然希望我们能通过植物来摄入葡萄糖，但是食品加工过程中通常都会去除纤维，然后将淀粉和糖进行浓缩；</li>
<li>吃甜食（含有蔗糖，是一种双糖，由葡萄糖和果糖缩合脱水形成）会导致葡萄糖和果糖双峰值；</li>
</ul>
<h2 id="第二部分-出现葡萄糖峰值有哪些危害">第二部分 出现葡萄糖峰值有哪些危害</h2>
<p>个人体会：突然摄入大量糖分，例如一块西瓜，一罐可乐，一杯奶茶，会真切感受到血糖上升，脑子有点懵懵的，做不了重脑力活；控糖之后就基本再也没有出现过了。</p>
<blockquote>
<p>降低葡萄糖峰值可以预防炎症的产生，继而降低患炎症类疾病的风险。</p>
</blockquote>
<ul>
<li>葡萄糖的存储方式： 糖元（在肝脏或者肌肉中），脂肪（在脂肪细胞中）；果糖的存储方式： 脂肪（在脂肪细胞中）</li>
<li>当过多的葡萄糖进入，我们身体会尽最大努力将其储存起来。当葡萄糖水平升高，胰腺分泌胰岛素，胰岛素唯一的作用就是将多余的葡萄糖存储在身体的存储单元中，使其脱离身体的循环，保护我们不被伤害。</li>
<li>脂肪会导致我们的身体出现一些不好的情况；建议选择不含果糖的咸香美味的食物，而不是含有果糖的甜食；</li>
<li>葡萄糖峰值是如何让我们生病的？
<ul>
<li>短期影响 1：持续的饥饿感。原因：胰岛素水平较高</li>
<li>短期影响 2：食欲旺盛。</li>
<li>短期影响 3：慢性疲劳。太多的葡萄糖会使线粒体停止工作，然后能量的生产就会受到影响，结果就是我们会感到疲惫不堪。</li>
<li>短期影响 4：糟糕的睡眠。</li>
<li>短期影响 5：感冒和冠状病毒并发症。经历一次葡萄糖峰值后，免疫系统会出现短暂故障，更容易遭到病毒感染。</li>
<li>短期影响 6：更棘手的妊娠期糖尿病</li>
<li>短期影响 7：潮热和盗汗</li>
<li>短期影响 8：偏头痛</li>
<li>短期影响 9：记忆和认知功能问题。大脑变得迟钝。</li>
<li>短期影响 10：更难管理的 1 型糖尿病</li>
<li>长期危害 1：痤疮和其他皮肤问题</li>
<li>长期危害 2：衰老与关节炎。糖化、自由基过多以及随之而来的炎症会使我们的细胞功能缓慢退化，我们称之为衰老。保持血糖平稳，辅以锻炼和减少压力控制，才是抗衰老的有效方法。</li>
<li>长期危害 3：阿尔茨海默病与痴呆</li>
<li>长期危害 4：患癌症的风险加大</li>
<li>长期危害 5：精神障碍</li>
<li>长期危害 6：肠道问题</li>
<li>长期危害 7：心脏病</li>
<li>长期危害 8：不孕症和多囊卵巢综合症</li>
<li>长期危害 9：胰岛素抵抗综合症与 2 型糖尿病。如果胰岛素水平长期处于高位时，细胞就开始对胰岛素产生抵抗。胰岛素抵抗是 2 型糖尿病的根本原因。降低血糖水平的饮食方式有助于逆转 2 型糖尿病。</li>
<li>长期危害 10：非酒精性脂肪性肝病。原因：果糖，降低果糖水平，降低果糖峰值</li>
<li>长期危害 11：皱纹和白内障</li>
</ul>
</li>
</ul>
<h2 id="第三部分轻松控糖的10个小窍门">第三部分：轻松控糖的10个小窍门</h2>
<h3 id="窍门-1正确的饮食顺序">窍门 1：正确的饮食顺序</h3>
<blockquote>
<p>按照科学的顺序进食，不节食，不放弃自己喜欢的食物，可将葡萄糖峰值降低 73%、胰岛素降低 48%。</p>
</blockquote>
<p>正确的顺序：先吃纤维，然后吃蛋白质和脂肪，最后吃淀粉和糖类。</p>
<p>PS: 亲测有点用，先吃蔬菜，再次肉，最后吃米饭。但是有个问题，中国人的饮食结构中，淀粉占比过大，蛋白质占比小，像面食这类几乎无解。所以选择吃什么很重要，然后是吃的顺序。应该提升蔬菜和蛋白质的占比，减少碳水，或者使用粗粮替代原先的精细米面。</p>
<h3 id="窍门-2-在每餐前增加一到绿色开胃菜">窍门 2： 在每餐前增加一到绿色开胃菜</h3>
<p>在吃其他食物之前，先吃纤维。</p>
<h3 id="窍门-3停止计算热量">窍门 3：停止计算热量</h3>
<h3 id="窍门-4平稳早餐后的血糖曲线">窍门 4：平稳早餐后的血糖曲线</h3>
<p>早餐最不适合吃糖类和淀粉类食物，但是，大多数人早餐只吃糖类和淀粉类食物。</p>
<p>如果我们准备吃一些糖，一个完整的水果是最好的选择（而不是打碎，果汁，NFC 之类的）。</p>
<blockquote>
<p>果汁本身并不健康，只是商家让你认为健康而已。</p>
</blockquote>
<p>开启咸香美味早餐</p>
<ul>
<li>只做自己的咸香美味早餐</li>
<li>确保自己的早餐含有蛋白质</li>
<li>添加脂肪</li>
<li>添加额外的纤维</li>
<li>选择性地添加一些淀粉或者完整的水果（也可以不加）</li>
</ul>
<p>水果：最好的选择是讲过，柑橘类水果或者是又小又酸的苹果。不建议选择芒果、菠萝和其他热带水果</p>
<p>咖啡：要小心加糖的咖啡。</p>
<h3 id="窍门-5吃自己喜欢的糖因为所有糖都一样">窍门 5：吃自己喜欢的糖，因为所有糖都一样</h3>
<p>蜂蜜含有葡萄糖和果糖，和食用糖一样。</p>
<p>天然糖并没有更好，因为所有糖都一样。</p>
<p>水果干看似健康，其实不然。确实含有一些纤维，但是其大多数成分和食用糖一样</p>
<ul>
<li>选择自己喜欢的糖</li>
<li>尽量选择水果作为甜品</li>
<li>人造甜味剂
<ul>
<li>对葡萄糖水平和胰岛素水平没有副作用的最佳甜味剂：阿洛酮糖、罗汉果、甜叶菊。赤藓糖醇</li>
<li>避免食用，会导致胰岛素水平升高：阿斯巴甜、麦芽糖醇、三氯蔗糖、木糖醇、安赛蜜</li>
</ul>
</li>
</ul>
<h3 id="窍门-6选择餐后甜点而不是甜甜的零食">窍门 6：选择餐后甜点而不是甜甜的零食</h3>
<p>不管是水果、奶昔、糖果棒还是饼干，如果你想吃的话，一定要在饭后吃。</p>
<p>PS：减少峰值出现的次数；另外饭后非空腹，会减缓吸收，避免掉必然出现的峰值。</p>
<h3 id="窍门-7吃饭之前喝点醋">窍门 7：吃饭之前喝点醋</h3>
<p>一旦醋酸进入血液，会渗透到我们的肌肉中，刺激肌肉比原来更快的制造糖元，是葡萄糖以更高的效率被吸收。</p>
<h3 id="窍门-8饭后动起来">窍门 8：饭后动起来</h3>
<p>饭后散步。</p>
<p>餐后运动会让血糖曲线变得平稳，但是胰岛素水平不会增高。</p>
<h3 id="窍门-9如果一定要吃零食就吃咸香风味的零食">窍门 9：如果一定要吃零食，就吃咸香风味的零食</h3>
<h3 id="窍门-10为你摄入的碳水化合物穿上外衣">窍门 10：为你摄入的碳水化合物“穿上外衣”</h3>
<p>享用碳水化合物的时候，加点纤维、蛋白质或脂肪，并将这种做法变成习惯。</p>
<p>好的脂肪以饱和脂肪酸（来自动物、黄油、酥油和椰子油）或单不饱和脂肪酸（来自水果和坚果，如牛油果、夏威夷果和橄榄果）为主要成份。</p>
<p>做饭是，最好是用饱和脂肪酸含量高的有。（常温下通常呈固态）。</p>
<p>不好的脂肪，以多不饱和脂肪酸或反式脂肪酸为主要成份，存在于大豆油、玉米油、菜籽油、米糠油、油炸食品和快餐中。有一种稍微好一点的种子油是菜籽油。</p>
]]></content>
		</item>
		
		<item>
			<title>聊聊项目架构调整</title>
			<link>https://wklken.me/posts/2024/06/16/about-architecture-adjustment.html</link>
			<pubDate>Sun, 16 Jun 2024 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2024/06/16/about-architecture-adjustment.html</guid>
			<description>过度设计的反面是务实 去年接手了一套系统，花了近三个月做了一些调整才正式外发。 当时这套系统是为了满足一个时间点快速迭代发布的，接手后觉得其中有</description>
			<content type="html"><![CDATA[<blockquote>
<p>过度设计的反面是<code>务实</code></p>
</blockquote>
<p>去年接手了一套系统，花了近三个月做了一些调整才正式外发。</p>
<p>当时这套系统是为了满足一个时间点快速迭代发布的，接手后觉得其中有一些问题，聊聊其中做的一些决策。</p>
<h2 id="模块存在的合理性">模块存在的合理性</h2>
<h3 id="case1">case1</h3>
<p>A 项目和 B 项目都是 Django+DRF 提供的服务。</p>
<p>仔细阅读代码后发现</p>
<ol>
<li>B 项目逻辑并不复杂，但是麻雀虽小五脏俱全，DB、缓存、配置、helm-charts，出包流水线等都得来一套</li>
<li>存在一些共同的逻辑，抽象出一个 SDK 来实现逻辑复用</li>
<li>A 项目需要调用 B 项目，此时封装一层独立的调用逻辑（B 项目加一个接口，需要同步改这里）</li>
<li>为同一套前端提供的服务（通过k8s svc将请求转发到不同项目）</li>
</ol>
<p><strong>同构的技术栈，强耦合的逻辑，却是两个独立的项目。</strong></p>
<p>能否合并成一个项目？答案是可以。</p>
<p>花了几个小时，将 B 项目的差异化代码直接拷贝到 A 项目，去掉 SDK，独立调用层改成直接函数调用，去掉对应的helm-charts，出包流水线等等。</p>
<p>结果：</p>
<ul>
<li>项目 -1</li>
<li>SDK -1</li>
<li>流水线 -1</li>
<li>helm-chart -1</li>
<li>A 项目中调用逻辑 -1</li>
</ul>
<p>整体的维护成本，复杂度等都降低了。</p>
<p>一个服务是否拆分成多个，以及多个服务是否应该合并成一个，取决于很多因素，不一定说拆分一定不好，但是拆分带来的维护成本往往是大于单体的。</p>
<h3 id="case2">case2</h3>
<p>A 项目启动时，会从一个静态文件服务器 B 拉取一些运行时文件，B 是可以动态更新这些文件的。这样做的好处，是为了方便用户侧动态更新一些文件，直接 rolling update A 就能使得线上接口刷新成新的。</p>
<p>当然，这样做时候代价的，需要维护服务 B，同时 A 需要嵌入拉取逻辑。</p>
<p><strong>问，这个机制目前有使用场景吗？没有，但是可能有。</strong></p>
<p>这个就有点臆测未来用户侧的使用场景了，这种更应该有真实用户反馈过来再去加上，而不是一开始就加上。</p>
<p>所以，选择了去掉 静态文件服务器 B, 直接在 A 项目打镜像时将运行时依赖文件打进去，相当于强耦合。我们甚至留了口子，如果用户侧需要更新，基于 A 镜像再打一个镜像，问题是上线一年多一次也没遇到。</p>
<p>结果：</p>
<ul>
<li>项目 -1</li>
<li>A 项目中的拉取更新逻辑 -1</li>
</ul>
<h3 id="case3">case3</h3>
<p>重构时，A 项目使用了最新的实现方案，去替代 B 项目，二者语言和技术栈完全不一样。</p>
<p>B 项目中有个频率控制逻辑，使用的漏桶算法，而 A 的生态中频率控制算法只有fixed window，由于觉得漏桶算法效果更好，并且时间很赶并没有多余的精力在 A 中实现漏桶算法。所以将 B 中的漏桶算法直接挪出来独立成一个模块 C，然后 A 项目实现对应逻辑依赖模块 C。</p>
<p>相当于异构的实现，通过 RPC 协议交互。</p>
<p>但是这样引入了额外的模块和复杂度。</p>
<p>那么， fixed window频率控制算法在实际用户使用中真的会有问题吗？是的，会有问题，但是最终的频率控制效果用户能接受吗？</p>
<p>捞了线上正在运行的 B 项目的频率控制触发的日志，发现实际上用户侧配置的值一般都比较高，并且很少有触发的频率控制的调用方。</p>
<p>最终，我们决定去掉这个模块 C，直接使用 A 生态中基于fixed window的频率控制。</p>
<p>上线后只出现了一两例触发极端情况的场景，但是这个也给我们在 A 项目中实现漏桶算法提供了充足的缓冲时间。</p>
<p><strong>如果效果差不多，或者稍微差一点但是能接受，那么不一定需要引入一个新模块来解决问题。</strong></p>
<h2 id="模块实现的合理性">模块实现的合理性</h2>
<h3 id="case1-1">case1</h3>
<p>A 项目需要用到外部鉴权，依赖于外部的权限数据。此时引入一个模块 B，B 使用 A 生态的多语言框架实现（不同的语言），内部通过 RPC 交互。B 通过 HTTP 调用Web做全量以及增量的权限数据同步。</p>
<p>相当于： <code>A =&gt; RPC =&gt; B =&gt; HTTP =&gt; Web Server</code></p>
<p>由于这个全量、增量同步做得很复杂，并且做了一层cache，导致线上出现权限问题时并不好排查。并且 <code>Web Server</code> 是一个产品项目，发布频率高并且稳定性低于 A。</p>
<p>那么为什么使用多语言框架呢？可能刚好官方支持吧（坑比较多），这时候有点像是拿着锤子满世界都是钉子，往里面放也行。</p>
<p>感觉掉坑里了，能否去掉 B 模块呢？</p>
<p>最终经过评审，我们的实现非常简单<code>A =&gt; HTTP =&gt; Core Server =&gt; DB</code>，不存在全量、增量数据同步，直接依赖 DB，加了一层缓存，整体复杂度非常低。</p>
<p>有时候生态里面一些亮点、强力的 feature，并不一定是好事，平平无奇的机制能实现的话，不一定要用。</p>
<h3 id="case2-1">case2</h3>
<p>A 项目生态中有一个白名单， 历史 B 项目中也有个白名单逻辑，然后在从 B 项目升级到 A 项目的过程中， 竟然在 A 项目也实现了 B 项目的白名单逻辑，即同时存在两个白名单逻辑。</p>
<p>两个白名单逻辑本质上是一致的。</p>
<p>决策：升级过程中做简单的数据迁移，只留一个白名单逻辑。</p>
<h2 id="不合理的依赖">不合理的依赖</h2>
<h3 id="case1-反向依赖">case1 反向依赖</h3>
<p>上面模块合理性的case， 底层对可用性要求更高的模块，依赖了上层可用性不那么高的<code>Web Server</code>模块，直接拉低了整体的可用性。</p>
<p>通过新起一个更底层的服务，只依赖于 DB，独立于<code>Web Server</code>，将之前的依赖链路抹掉，变成可靠性更高的链路。</p>
<h3 id="case2-循环依赖">case2 循环依赖</h3>
<p>A 项目是一个业务网关，依赖了一个认证项目 B，实施的时候，发现这个 B 的地址竟然是 A 项目的子路径。</p>
<p>原来是因为这个认证逻辑本身没有流水日志用于问题排查，所以将 B 项目接入了 A 项目网管。</p>
<p>此时即循环依赖 A 的认证逻辑依赖了 A 自己的网关地址；</p>
<p>后来决策，依赖地址直接改成B 服务，流水日志 B 项目自行解决。</p>
<h2 id="其他相关的">其他相关的</h2>
<h3 id="过于灵活的配置">过于灵活的配置</h3>
<p>项目的配置文件生成：原始helm chart中的template有一个configmap， 并且有一批环境变量，在启动的shell脚本中，读取环境变量，并且用 envset、sed 等命令，将配置文件和镜像中的配置文件合并生成一个新的文件。 相当于，配置有 3 个来源，configmap/镜像中的yaml/环境变量中的配置。</p>
<p>这样看起来非常灵活，一系列判断逻辑可以根据一些开关、环境变量，生成差异化的配置，然后启动；</p>
<p>但是带来的问题是，在 shell 脚本中维护这个逻辑可维护性无疑是非常差的，并且认知负担非常重。</p>
<p>后来做的决策是，去掉启动脚本中的逻辑，在configmap中由 helm 的语法、模板负责生成一个完备的配置，即<code>所见即所得</code>。</p>
<p>这样所有的差异化在模板阶段就处理掉了，而不是在运行时处理。</p>
<p>有时候，灵活的配置，强大的脚本，看起来很厉害，但是并不一定是好事，特别是来源多，条件复杂这种场景。这时候，唯一来源，并且看起来有些<code>笨</code>的方式，反而是最好维护的。</p>
<h3 id="过于冗长的流水线">过于冗长的流水线</h3>
<blockquote>
<p>存在不一定合理</p>
</blockquote>
<p>由于之前处于快速迭代期，交付期间出现过各个环节的问题，导致出包的流水线非常长，几乎要二三十分钟才能出一个包，一旦有问题重出又是二三十分钟。</p>
<p>流水线中很多环节，是为了 cover 掉之前快速迭代容易遗漏的一些点，例如依赖包遗漏不熟检查，例如打包后执行一次helm install部署测试。 这些环节耗时都比较长，并且是同步的。</p>
<p>但是，当过来快速迭代期之后，依赖变更已经不频繁，并且出现了其他环节能够cover掉出包过程中的早期测试，那么原先有效的环节可能变得非常<code>鸡肋</code>。</p>
<p>不删只是慢了点，存在即合理，会导致很多时候不敢去改流水线，去掉这些鸡肋。</p>
<p>后来复盘，统一删除，将时间压缩到 5 分钟以内。</p>
<h3 id="过多的测试用例测试环境及流程">过多的测试用例、测试环境及流程</h3>
<p>使用 A 项目替换掉之前的 B 项目，但是，没有重新去梳理 case，而是在 B 项目的 case 基础上又加了一批 case；并且也没有重新做差异化的梳理，导致出现了 4 套测试环境+300多个case；</p>
<p>每个环境的 case 都是提前造好的，接手后，正式发布前需要把 4 个环境升级上来，并且执行完所有 case，有失败的需要逐一去排查原因。</p>
<p>这样带来的问题是，出包后的测试成本也非常高，并且 4 套环境的维护，case 的更新代价也很高。</p>
<p>你不跑，怕有问题没覆盖到，去跑，又得更新四套环境逐一验证，没过的 case 还得逐一确认修复。</p>
<p>后来，我们决定进行测试左移，到开发阶段而不是出包后，重新梳理 case，变成原先三分之一，并且能够在任意环境，自动构建环境，执行覆盖所有case。</p>
]]></content>
		</item>
		
		<item>
			<title>聊聊框架</title>
			<link>https://wklken.me/posts/2024/06/08/about-framework.html</link>
			<pubDate>Sat, 08 Jun 2024 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2024/06/08/about-framework.html</guid>
			<description>很久没写了，感觉是要提高下更新频率了。 今天聊聊框架，线索是我的工作经历。 刚毕业那会 大学的时候，因为学的是软件工程，所以高级编程语言当时教的是</description>
			<content type="html"><![CDATA[<p>很久没写了，感觉是要提高下更新频率了。</p>
<p>今天聊聊框架，线索是我的工作经历。</p>
<h2 id="刚毕业那会">刚毕业那会</h2>
<p>大学的时候，因为学的是软件工程，所以高级编程语言当时教的是 Java，当时所有教材都是英文版的大部头。</p>
<p>那时候选修课，学了 XML，学了设计模式，当然只是学，一知半解。</p>
<p>然后疯狂地用 Java 写东西，做项目，调试，学 Servlet/JSP（最近去了图书馆，发现2024了尽然还有JSP的新书，真是。。。。）所以毕业前，我一直认为自己未来会加入 Java 大军（想想那时候真的如日中天，想不到自己后来用的语言越来越小众）。</p>
<p>无脑学习 SSH，包括大四实习那会，只有我一个进入了 Java 组，当时花了很多时间折腾实习公司的自研框架，徒手搭建。</p>
<p>那时候对框架是无脑地崇拜，这里配配那里配配就 work 了，很神奇，但是如果一个 xml typo 就可能让你 debug 半天，这可能也是后来自己对框架的偏好会倾向于：不喜欢<code>约定</code> 过多的框架。</p>
<h2 id="毕业了">毕业了</h2>
<p>我并没有如愿以偿加入 Java 大军，如果当时选了那条路，可能至今还在北漂。</p>
<p>后来面了阿里的 SDET，选择了杭州，然后南下了。</p>
<p>那时候工作，测试多余开发，常常加班，常常周末去公司，</p>
<p>工作的关系，接触了非常多的语言，Java/C++/Perl（现在应该很少人用了吧）/Python/Bash 等等，然后用 Python 实现了一些提升效率的测试工具。算是在繁忙的测试工作中寻找到一点开发的乐趣。</p>
<p>后来决定走，原因是因为想做回开发。算是遵从自己的内心吧。</p>
<p>那时候自己啃 Python 那本《Python 学习手册》，都快翻烂了。</p>
<p>然后网上投了简历，拿了个 Offer 之后，南下深圳。</p>
<h2 id="第二份工作">第二份工作</h2>
<p>测试转开发，并且是 Python 开发，准确来说是 Web 开发。</p>
<p>那时候对框架没啥概念，因为做测试工具的时候并没有机会接触太多，只懂一些皮毛。</p>
<p>那时候小组内重度使用 Tornado 以及 Flask。</p>
<p>目前 Tornado 应该很少人使用了吧？当时的感觉是写起来有点别扭，不过也还行。然后另一个就是 Flask。因为当时是 CS 架构，所以主要工作就是写 API。</p>
<p>对 Flask 也算情有独钟吧，轻量，简单直接，好用，产出效率杠杠的，是我喜欢的类型。</p>
<p>当时也自学了下 Django，第一印象，<code>约定</code> 有点多，有点重，额，不喜欢。</p>
<h2 id="第三份工作">第三份工作</h2>
<p>后来我司因为不可抗力，额，没了。</p>
<p>当时找工作，那时候是 2014 年左右，万众创业，满地的创业公司。根本没有找大厂，直接去了一家小创业公司，CEO 是国外回来的，用的 Python，后端开发加我一共三个。</p>
<p>那时候技术栈也是 Flask，原因是第一个招进来懂 Python 哥们（很厉害）的个人偏好。</p>
<p>做得还行吧，但是小公司管理后台的需求比较多，后来竟然需要一个专职开发做管理后台（Flask+Sqlalchemy+Flask-admin)。</p>
<h2 id="第四份工作至今">第四份工作，至今</h2>
<p>后来不到十个月，额，又没了。</p>
<p>Gap 了 100 天，找工作，面试进了大厂。</p>
<p>一二十个 Python 开发 的一个中心（现在应该翻了几倍），做 Django。</p>
<p>从抗拒 Django，理解 Django，到选择 Django，也算是经历了完整历程。</p>
<p>后来上 DRF，也是抗拒，还行（但是还谈不上喜欢）。</p>
<p>内部也形成了比较好的分层/规范等。</p>
<p>后来技术栈加入 Golang, 一开始 Gin + GORM(v1) 感觉有点香，但是后来主导两个大项目的选型，一个用的 Chi + Sqlx（网关），另一个用的 Gin + Sqlx（权限中心）。</p>
<p>但是实际使用很多第三方<code>框架</code>，或者配合做一些开发，由于主导权不在自己手里，可以看到很多不是特别合理的配置。
例如：巨无霸开发框架（应该用 Core+Contrib 的模式更为合理），为了微服务而微服务的框架（加个字段加 N 层 N 个项目），手搓的 ORM，手搓的 XXX。</p>
<h2 id="那么框架怎么选">那么框架怎么选？</h2>
<p>用不用框架？特别是做 Golang 的时候，直接用标准库，也不是不行。</p>
<p>如果是简单或者小项目，用什么其实无所谓。但是，如果是多人协作/需求复杂这类项目，用个框架显然能事半功倍。因为不用框架，可能很多很细碎的业务无关的功能，都需要手搓，然后慢慢慢慢，你的项目中的<code>框架</code>其实跟社区的框架差不多。</p>
<p>框架对于生产力有一个非常大的提升，原因是，社区红利。一个好的框架，其生态非常完善，你遇到的 90% 以上的问题，都有现成的解法，即使没有现成的，也有可以参考的。</p>
<p>Go 当时的 Web 框架非常之多，各有特色，但是现在显然 Gin 胜出了。</p>
<p>Python 的同上，Django 目前应该算第一 (FastAPI 是后起之秀，用起来很强大，但是，我不喜欢<code>约定</code>过多的框架)</p>
<p>原则就是：除非某个框架的 feature 特别适用于你的业务场景 (或者排第一的框架不能满足某个硬性条件)，否则选社区最流行的，这样能少踩很多坑。</p>
<p>并且，不要因为协作开发中某个人特别熟悉某个相对小众的框架而选择它，最好还是客观评估。有时候宁愿选择大家都不怎么熟悉但是简单的，而不是某个人精通的（你擦几次这种项目的屁股就知道有多难受了）。</p>
<p>选择框架的标准，我觉得应该是：一个初级开发没有接触过，能在一周内通过阅读文档，搭建环境，写 Demo 的方式熟悉大体的框架，并且能在第二周开始修简单的 bug。</p>
<h2 id="有哪些需要注意的">有哪些需要注意的？</h2>
<p>尽量避免手搓轮子！尽量避免手搓轮子！尽量避免手搓轮子！（强调三遍，受过太多苦）</p>
<p>大部份通用的功能，框架或开源的实现中都有，应该先看框架中本身是否自带，如果没有，寻求第三方库，如果还没有，再考虑自己实现。</p>
<p>尽量避免：</p>
<ol>
<li>框架一般已经做到极简，堆业务逻辑出现一些代码上的重复是正常的，不要为了少写几行代码，在框架之上又封装了一层，相当于在约定之上，又做了一层自定义约定（其他维护者会骂娘的，没有<code>共识</code>，而是在框架的认知负担之上，又要学习这一层<code>约定</code>，复杂度太高，百害无一利）&ndash; 写代码的人爽了，维护代码的人非常不爽（要是一个人的项目，当我没说）</li>
<li>不要炫技。写一个 DSL 和平铺几个实现，我更倾向于后者。</li>
<li>不要一开始就上可扩展性，大量的抽象加一两个具体实现。</li>
</ol>
<p>记住，可维护性更重要。</p>
<h2 id="框架该怎么用">框架该怎么用？</h2>
<p>像 Django 和 DRF 这类框架非常强大，不同人可以写出不同风格的代码，例如我们最早的 FBV 和 CBV，使用 DRF 可以继承 N 种 View/Viewset 实现。</p>
<p>但是这样的代码是不利于多人协作维护的。</p>
<p>应该形成项目内，或者组内的统一规范（这种规范静态代码检查大部份是扫不出来的）。</p>
<p>例如：</p>
<ul>
<li>纯 Django 项目应该使用 CBV，禁止用 FBV</li>
<li>使用 DRF 时，禁止直接继承 <code>*ViewSet</code>，只允许继承 <code>*APIView</code></li>
<li>SLZ 只允许到 View，禁止下传，禁止在其他模块用到</li>
</ul>
<p>相当于，需要去学习框架，了解框架，用起来，知道各种用法/风格的优缺点，然后，在组织内形成<code>共识</code>，并落地为规范，这个规范新人在接触项目的第一天就能看到，新人看到的代码不会出现意外的惊喜。</p>
<h2 id="那么代码是不是太无趣了">那么，代码是不是太无趣了？</h2>
<p>这时候写代码，是不是有点无聊？</p>
<p>因为框架限制了你，或者内部的规范限制了你？</p>
<p>是的，可能或者大概率会有点无聊，但是，上面的选择，是多人协作下比较稳的一种选择，或者说效率比较高的选择。</p>
<p>你是否经历过，google 一个小众框架报错只有几条结果甚至没有结果，然后花几天时间一点点 debug 解决（很有成就感是吧？）；实现一个通用需求别的框架只需要一行配置而你用的这个需要自己手搓两天（可能搓出来也很有成就感是吧？）；接手一个项目发现里面有七八种用法，甚至没有分层，在各个地方访问数据库（骂娘）；接手一个项目发现用法跟标准框架不一样，而是多了一层封装，得去学习自定义的约定，问题是封装的不咋地老是得去擦屁股，然后查问题一查查一天（每天都来几个问题遇到过没？我遇到过）</p>
<p>自己写的爽，但是坑了未来的自己或者其他接手的开发，还是说，稍微不那么爽，效率高点少加班。</p>
<p>其实还是有一些有趣的。</p>
<p>你可以考虑分层，模块之间的依赖关系，数据的流转，做一些扩展，或者 dig 到更深的地方看看是怎么实现的。</p>
<h2 id="其他">其他</h2>
<p>如果是 <strong>个人项目</strong>, 要用啥都行，手搓都行，开心就好。</p>
<p>如果是 <strong>一个人的项目</strong>，谨记客观选型不炫技，未来维护成本低，别人接手不骂。</p>
<p>如果是 <strong>多人协作的项目</strong>，复杂度越低越好，可维护性越高越好。</p>
]]></content>
		</item>
		
		<item>
			<title>关于 k8s 的 zero downtime deployment 一些建议</title>
			<link>https://wklken.me/posts/2023/12/17/some-tips-for-zero-downtime-deployment.html</link>
			<pubDate>Sun, 17 Dec 2023 22:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2023/12/17/some-tips-for-zero-downtime-deployment.html</guid>
			<description>问题 后端服务对接了 API 网关, 服务正常, 但是在滚动更新的时候, 会出现 502 滚动更新时, k8s会起新的pod, 同时删除老的pod 可能原因: 新的pod启</description>
			<content type="html"><![CDATA[<h2 id="问题">问题</h2>
<p>后端服务对接了 API 网关, 服务正常, 但是在滚动更新的时候, 会出现 502</p>
<p>滚动更新时, k8s会起新的pod, 同时删除老的pod</p>
<p>可能原因:</p>
<ol>
<li>新的pod启动后, 标记为ready但实际服务并没有准备好</li>
<li>老的pod在删除时, 已经在处理的请求没有处理完就退出, 或者退出时还有流量进来</li>
</ol>
<h2 id="解决">解决</h2>
<p>需要通过一系列配置, 做到 zero downtime rolling update (可以google相关的关键字了解更多), 以下给到一些建议</p>
<h3 id="1-配置-livenessrediness">1. 配置 liveness/rediness</h3>
<ul>
<li><code>liveness</code> 判断一个pod是否正常启动, 进入<code>Running</code>状态</li>
<li><code>rediness</code> 判断一个pod是否完成了所有必要的初始化动作, 可以开始提供服务, 进入<code>Ready</code>状态</li>
</ul>
<p>pod 进入 <code>Ready</code> 状态意味着此时会有流量进来, 此时如果进程无法提供正常服务, 会导致 502</p>
<p>所以, 需要确保 <code>rediness</code> 的正确性, 使得进程可以提供服务之后, pod 才进入 <code>Ready</code> 状态</p>
<h3 id="2-配置-terminationgraceperiodseconds">2. 配置 terminationGracePeriodSeconds</h3>
<p><code>terminationGracePeriodSeconds</code>, 最长的宽限期，是允许 Pod 在收到终止信号后优雅关闭的最大时间。如果 Pod 在这个时间结束之前已经自主成功退出，那么 Kubernetes 就会立即进行后续的清理回收步骤, 默认是 <code>30s</code></p>
<p>如果pod被删除时, 后端服务有正在处理的请求, 并且请求需要超过 30s 才能处理完, 此时服务无法正常 graceful shutdown, 会被强杀, 导致 502.</p>
<p>所以这个值需要设置为 <code>terminationGracePeriodSeconds &gt; 服务承诺的最大的请求耗时时间 + 进程graceful shutdown耗时</code>, 例如你的服务设置的处理请求最大超时时间是 60s, 那么 <code>terminationGracePeriodSeconds</code> 需要大于 60s</p>
<h3 id="3-程序要支持-graceful-shutdown">3. 程序要支持 graceful shutdown</h3>
<p>确保正在处理的请求能被全部处理完之后再结束, 需要监听 <code>SIGTERM</code> 信号并且处理</p>
<p>例如 golang 1.18中支持 <a href="https://pkg.go.dev/net/http#Server.Shutdown">Server.Shutdown</a></p>
<blockquote>
<p>Shutdown gracefully shuts down the server without interrupting any active connections. Shutdown works by first closing all open listeners, then closing all idle connections, and then waiting indefinitely for connections to return to idle and then shut down. If the provided context expires before the shutdown is complete, Shutdown returns the context&rsquo;s error, otherwise it returns any error returned from closing the Server&rsquo;s underlying Listener(s).</p>
</blockquote>
<p>如何验证: 发送一个请求, 请求正在处理时, 发送<code>SIGTERM</code>信号给进程, 这个请求能被正常处理完</p>
<h3 id="4-主进程在容器中的-pid-必须为-1">4. 主进程在容器中的 pid 必须为 1</h3>
<p>这样才能正常接收到信号</p>
<p>某些应用编写了自定义脚本来启动服务, 但是pid=1的进程是这个脚本而不是服务, 这样会导致服务没法接收到信号并做graceful shutdown</p>
<p>例如:</p>
<ul>
<li><code>/bin/bash -c app</code>, 信号是shell收到的, app收不到</li>
<li>用<code>exec</code></li>
</ul>
<h3 id="5-配置-prestop">5. 配置 <code>preStop</code></h3>
<p>建议配置 <code>preStop</code></p>
<p>配置后执行的顺序 <code>preStop -&gt; SIGTERM -&gt; SIGKILL</code></p>
<ul>
<li>先执行<code>preStop</code>脚本</li>
<li>之后给进程发送信号<code>SIGTERM</code></li>
</ul>
<p>另外一个502可能原因, 从service中摘除 pod endpoint和删除pod是同步进行的, 但是谁先谁后是不确定的(watch到变更, 独立的事件), 此时如果删除pod先执行并且程序很快退出, 此时可能endpoint还没有被摘除, 那么会出现 502</p>
<p>因为 <code>preStop</code> 之后 k8s会发送 <code>SIGTERM</code>, 所以如果进程pid=1并且处理了<code>SIGTERM</code>信号能很好地进行graceful shutdown, 那么只需要加一个sleep确保没有流量进来</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">        </span><span class="nt">lifecycle</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">preStop</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">exec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">command</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span>- <span class="l">sleep</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span>- <span class="m">30</span><span class="w">
</span></span></span></code></pre></div><p>注意: 这个sleep的 30s 只是一个示例, 各个后端服务要自行压测确定, 例如如果集群使用ipvs+nodeport暴露服务到外部, 那么经验值是 sleep 120s, 此时注意terminationGracePeriodSeconds 需要配置并且大于 sleep时间+进程graceful shutdown时间</p>
<p><code>preStop</code>也可以执行一些等待/资源回收/主动触发hook之类的操作, 但是需要注意:</p>
<ul>
<li>如果<code>preStop</code>执行时间+进程graceful shutdown在 <code>terminationGracePeriodSeconds</code> 时间之内执行结束, 容器正常删除;</li>
<li>如果超过了<code>terminationGracePeriodSeconds</code>, 容器会被kill;</li>
<li>如果 <code>preStop</code> 阻塞住了, 直到超过<code>terminationGracePeriodSeconds</code>, 容器会被kill</li>
<li>如果 <code>preStop</code> 异常了, 容器会被kill</li>
</ul>
<h3 id="6-maxunavailablemaxsurge">6. maxUnavailable/maxSurge</h3>
<p>这两个跟 zero downtime 无关, 但是能降低影响范围</p>
<ul>
<li><code>.spec.strategy.rollingUpdate.maxUnavailable</code>, 滚动更新过程中，允许 “不可用” 的最大 Pod 数量; 默认 25%, 可以是绝对值或百分比
<ul>
<li>确保在更新期间始终有足够多的 Pod 保持可用, 避免因为可用实例过少带来的问题(健康的pod负载过高导致不健康)</li>
</ul>
</li>
<li><code>.spec.strategy.rollingUpdate.maxSurge</code>, 滚动更新过程中，允许 “超过” Desired Replicas 数的最大 Pod 数量; 默认 25%;
<ul>
<li>控制在更新过程中可能会暂时承受的额外负载</li>
</ul>
</li>
</ul>
<p>需要根据自身集群的资源/每个服务的负载等确定</p>
<h2 id="怎么验证">怎么验证</h2>
<ul>
<li>部署 deployment</li>
<li>启动压测</li>
<li>滚动pod <code>kubectl rollout restart deployment xxxx</code></li>
<li>如果压测结果中没有状态码为 502 的, 那么代表生效了</li>
</ul>
<h2 id="其他">其他</h2>
<h3 id="websocket-这类服务能做到zero-downtime么">websocket 这类服务能做到zero downtime么?</h3>
<p>通过rolling update的方式无法做到, 需要其他方式, 例如部署两套后通过接入层做切换</p>
<h2 id="参考文档">参考文档</h2>
<ul>
<li><a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/">k8s pod-lifecycle</a></li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>apisix 遇到的一些问题</title>
			<link>https://wklken.me/posts/2023/12/16/apisix-2023.html</link>
			<pubDate>Sat, 16 Dec 2023 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2023/12/16/apisix-2023.html</guid>
			<description>背景 大约十年前，部门内部有一套 ESB, 一套网关服务，当时网关服务用的 Python (Django 框架), 处理了一些基本的认证/流控逻辑，但是无法支持高并发并且经过网关的性</description>
			<content type="html"><![CDATA[<h2 id="背景">背景</h2>
<p>大约十年前，部门内部有一套 ESB, 一套网关服务，当时网关服务用的 Python (Django 框架), 处理了一些基本的认证/流控逻辑，但是无法支持高并发并且经过网关的性能损耗很大</p>
<p>在 2019 年，我们使用 Golang 重写了网关服务，性能大约是原来的 34 倍</p>
<p>至于选择 Golang 的原因，主要是因为：</p>
<ul>
<li>团队内部有 Golang 的开发经验，当时基于 Golang 的开源网关比较多，例如 janus, KrakenD, tyk 等等</li>
<li>相反，团队内部没有 OpenResty 的开发和运维经验 (当时 OpenResty 及 Kong 比较火，并且当时 Apisix 并不火)</li>
</ul>
<p>(我们的网关上线一年多之后，Apisix 团队有到我们内部做过分享，当时切到其他项目救火了并没有去听)</p>
<p>当时技术选型没有使用 gin, 而是 chi (轻量，100% compatible with net/http ) + std reverse proxy + dbless 模型 (借鉴 Kong 的概念)</p>
<p>做了两个大版本：</p>
<ul>
<li>第一个版本，底层数据不变，完全实现了原来的功能，发布上线 (此时新老版本都可以运行，灰度过去)</li>
<li>第二个版本，重新设计了底层数据结构，重做了管理端功能，数据迁移发布切换到新版本</li>
</ul>
<p>(这个版本的一直非常稳定，并且性能比 apisix 版本还好。得益于 goroutines / channels 以及无处不在的缓存)</p>
<p>然后在 2022 年，我们又遇到下一个问题：扩展性，我们有越来越多的需求</p>
<p>但是</p>
<ul>
<li>Golang 要实现插件的成本太高了，并且缺乏生态，很多很常见的特性都需要自己实现</li>
<li>无法支持自定义插件的场景</li>
<li>无法支持热更新 (在内存中全量重建 radixtree 之后替换掉旧的)</li>
</ul>
<p>在 2019 我切换去做权限系统，搞完之后又去接用户系统，到了 2023 四月份，由于某些原因，切回来接手新版的基于 apisix 的网关</p>
<p>接近八个月时间，很大部分时间在做架构调整以及重构，并且花费了很长一段时间验证新版本的稳定性，遇到了蛮多问题</p>
<p>最近在整理工作文档，汇总一下</p>
<p>简单梳理总结一下</p>
<h2 id="架构调整和代码重构">架构调整和代码重构</h2>
<p>原先的项目拆分的非常细碎，通过 SDK 共享代码，通过 API 做数据交互</p>
<ul>
<li>合并管理端的项目到一个单体项目，去掉 SDK 及交互 API</li>
<li>删除过度设计的模块，使用更直接/务实的方式实现</li>
<li>去除 基于 <a href="https://github.com/apache/apisix-go-plugin-runner">apisix-go-plugin-runner</a> 实现的权限数据同步/鉴权模块，使用 HTTP + lrucache 方式实现 (简单，不用考虑数据一致性)</li>
<li>去除 基于 <a href="https://github.com/apache/apisix-go-plugin-runner">apisix-go-plugin-runner</a> 实现的基于漏桶算法的频率控制，使用原生插件实现 (fixed-windows, 务实，虽然效果不如漏桶)</li>
<li>去反向依赖</li>
<li>对代码进行重构，模块及分层，提升可读性; 同时增加单元测试，引入 test-nginx</li>
<li>集成测试左移</li>
<li>对 dockerfile, helm-charts, 各种运维脚本进行优化，优化出包流水线</li>
<li>增加多套环境用于不同场景的测试</li>
<li>&hellip;&hellip;</li>
</ul>
<p>(这块花了很多时间，才做到 production ready, 主要是又得在开飞机的时候换引擎)</p>
<h2 id="apisix-相关的问题">Apisix 相关的问题</h2>
<h3 id="升级215-升级到-321">升级：2.15 升级到 3.2.1</h3>
<p>基本按照 <a href="https://apisix.apache.org/docs/apisix/upgrade-guide-from-2.15.x-to-3.0.0/">Upgrade Guide</a> 阅读，评估后直接升级到 3.2.1</p>
<p>主要的变更还是配置文件，顺带还把原先配置文件的生成方式重写，直接 helm 生成最终用的文件，抹掉了在容器启动时各种 sed/subst 之类的操作</p>
<p>其实后来还尝试升级到 3.2.2, 当时以为是一个小版本，结果合入了一个 pr <a href="https://github.com/apache/apisix/pull/9456">#9456 feat(config_etcd): use a single long http connection to watch all resources</a>, 导致了 <a href="https://github.com/apache/apisix/issues/9951">bug: route 404 after upgrade to 3.2.2</a>, 原因是<code>etcd prefix</code>中带了<code>-</code>, 在这个 <a href="https://github.com/apache/apisix/pull/9967">fix: can&rsquo;t sync etcd data if key has special character</a> 中修复了</p>
<p>带了 9456 这个特性的版本都需要关注下这个 PR 后面带的一批 bugfix (应该有四五个了，并且都比较重要，所以建议用最新版)</p>
<h3 id="prometheus-的问题">prometheus 的问题</h3>
<ul>
<li>
<p>apisix 3.2.1 已经合入 <a href="https://github.com/apache/apisix/pull/8434">#8434 feat(prometheus): support collect metrics works in the priviledged agent</a>, 所以理论上由于 prometheus 带来的性能问题应该已经解决了。(相关文章 <a href="https://apisix.apache.org/zh/blog/2023/03/06/the-mystery-of-prometheus-plugins-and-long-tail-requests/">从 1 秒到 10 毫秒！在 APISIX 中减少 Prometheus 请求阻塞</a>)</p>
</li>
<li>
<p>踩坑 1: 最早我们网关的 prometheus 自定义指标中，使用了 <code>path</code> 而不是 <code>matched_uri</code>作为 label, 这样会导致<code>/metrics</code>中记录数爆炸式增长，带来性能问题</p>
</li>
<li>
<p>踩坑 2: 由于我们的 route 数量很多，官方的 <code>local DEFAULT_BUCKETS = {1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 30000, 60000}</code> 是写死的，非常大导致记录数很大。所以我们提了个 issue <a href="https://github.com/apache/apisix/issues/9653">feat: The Prometheus plugin can now allow users to configure the DEFAULT_BUCKETS</a>, 后来在 <a href="https://github.com/apache/apisix/pull/9673">#9673</a> 实现</p>
<ul>
<li>解决：我们在构建镜像时自己 patch 了这个修复</li>
</ul>
</li>
<li>
<p>踩坑 3: 某一天线上某个环境的 apisix 被 KILL 了，影响到业务，后来定位到是 CPU 超过了 limits 被 k8s 杀掉; 而这个环境的请求量并不大，通过分析我们发现是 prometheus 特权进程在处理<code>/healthz</code>时把 CPU 给打满了，<a href="https://github.com/apache/apisix/issues/10000">bug: high cpu usage after prometheus.lua report &rsquo;no memory&rsquo;</a>, 原因还是 官方的 metrics 记录数过多</p>
<ul>
<li>解决：patch 一堆开关，支持从配置文件中关闭掉所有的官方 metrics, 只留下自定义 metrics</li>
</ul>
</li>
<li>
<p>踩坑 4: 目前我们还遇到一个疑似内存泄露问题，看着也是跟 prometheus 插件有关系 <a href="https://github.com/apache/apisix/issues/10618">help request: 3.2.1 memory leak</a>, 暂时没有解决，生产环境分配比较大的内存 (涨到一定大小之后就趋于稳定了)</p>
</li>
</ul>
<h3 id="radixtree-最长路径匹配问题">radixtree 最长路径匹配问题</h3>
<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="nt">apisix</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">router</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">http</span><span class="p">:</span><span class="w"> </span><span class="l">radixtree_uri_with_parameter</span><span class="w">
</span></span></span></code></pre></div><p>在这种类型的 radixtree 中，最长路径匹配实际上并不是确定的，跟路径被加入树中的顺序有关，<a href="https://github.com/apache/apisix/issues/9366">bug: Adding routes to a Radix Tree in a different order can lead to the same URL matching the first added route instead of the longest path match</a></p>
<p>这个问题目前官方还没定位修复，我们目前是通过写入路由配置时，计算 priority，使得其看起来像最长路径匹配 <a href="https://github.com/TencentBlueKing/blueking-apigateway-operator/blob/60f0cd4c1ea59b169ba47e55b23a6af8d2dabf8c/pkg/commiter/conversion/resource.go#L61C6-L61C40">通过 priority 解决最长路径匹配失败：calculateMatchSubPathRoutePriority</a></p>
<h3 id="dns-服务出问题恢复后apisix-无法从解析失败中恢复">DNS 服务出问题，恢复后，apisix 无法从解析失败中恢复</h3>
<p>DNS 解析失败之后，有报错日志; 然后 DNS 服务恢复了，apisix 也一直无法将请求进行转发</p>
<p>报错：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plaintext" data-lang="plaintext"><span class="line"><span class="cl">2023/04/20 09:08:02 [error] 68#68: *151026556 [lua] resolver.lua:47: parse_domain(): failed to parse domain: aaa.com, error: failed to query the DNS serve
</span></span><span class="line"><span class="cl">r: dns server error: 2 server failure, client: 1.1.1.1, server: _, request: &#34;POST /api/v1/xxxxx HTTP/1.0&#34;, host: &#34;aaa.com&#34;
</span></span><span class="line"><span class="cl">2023/04/20 09:08:02 [error] 68#68: *151026556 [lua] upstream.lua:79: parse_domain_for_nodes(): dns resolver domain: aaa.com error: failed to query the DNS
</span></span><span class="line"><span class="cl"> server: dns server error: 2 server failure, client: 1.1.1.1, server: _, request: &#34;POST /api/v1/xxxxx  HTTP/1.0&#34;, host: &#34;aaa.com&#34;
</span></span><span class="line"><span class="cl">2023/04/20 09:08:02 [error] 68#68: *151026556 [lua] init.lua:540: http_access_phase(): failed to set upstream: no valid upstream node, client: 1.1.1.1, server: _, req
</span></span><span class="line"><span class="cl">uest: &#34;POST /api/v1/xxxxx HTTP/1.0&#34;, host: &#34;aaa.com&#34;
</span></span></code></pre></div><p>触发条件：route 有 upstream(upstream 中是域名), 并且 router 关联到了 service, 并且 service 中有 upstream</p>
<p>相关 issue: <a href="https://github.com/apache/apisix/issues/10093">bug: dns resolution did not resume immediately after the dns server resume</a></p>
<p>这个问题官方暂时没有定位修复，解决方法：自行 patch <a href="https://github.com/apache/apisix/issues/10093#issuecomment-1738381865">parse_domain_for_nodes</a></p>
<p>并且切换到更为稳定的 DNS 服务</p>
<h3 id="管理端频繁变更带来的性能抖动">管理端频繁变更带来的性能抖动</h3>
<p>我们有几万个 route, 变更非常频繁，我们发现在管理端变更时，会导致 apisix 的性能抖动 <a href="https://github.com/apache/apisix/issues/10268">help request: Is the radix tree rebuilt every time any route is updated?</a></p>
<p>后来通过阅读源码发现，每次 watch 到变更，都会导致 radixtree 重建，并且这个重建是同步的，会导致性能抖动</p>
<p>官方后来有个 <a href="https://github.com/apache/apisix/pull/9692">feat: increment route update for radixtree host uri, radixtree uri and radi&hellip;</a> 做增量更新优化</p>
<p>我们暂时的解决办法：如果有变更，定期重建 radixtree(而不是实时), 并且打散到一定范围的时间内，避免整体服务性能抖动，具体参考 <a href="https://github.com/TencentBlueKing/blueking-apigateway-apisix/blob/master/src/build/patches/004_radixtree_uri_with_parameter_rebuild_with_interval.patch">radixtree_uri_with_parameter_rebuild_with_interval</a></p>
<h3 id="url-路径参数中带中文会导致-404">url 路径参数中带中文会导致 404</h3>
<p>相关 issue <a href="https://github.com/apache/apisix/issues/10555">help request: when use Chinese word as uri parameter got 404</a></p>
<p>apisix 上下文中使用的路径是<code>decode</code>过的，但是下层 radixtree 使用的是<code>encode</code>过的，会导致这类路径匹配 404</p>
<p>目前我们暂时用 <a href="https://github.com/apache/apisix/pull/10561">10561</a> 这种方式修复，但是这样做看着比较低效 (我们压测完发现差别不大，线上直接先 patch 了)</p>
<h3 id="zero-downtime-deployment">zero downtime deployment</h3>
<p>发布时，正在处理的请求会受到影响 <a href="https://github.com/apache/apisix/issues/10437#issuecomment-1793473840">Is the apisix support preStop hook? for graceful shutdown</a></p>
<p>除了这个，还遇到 ipvs+linux 内核版本会导致滚动更新时，还会有持续的流量进入到正在 terminate 的 pod 中</p>
<h3 id="etcd-compacted-导致-apisix-全量拉取">etcd compacted 导致 apisix 全量拉取</h3>
<p>生产环境上线后，etcd 会定期出现内存使用量告警，突然飙升，伴随着大量的 read 行为</p>
<p>查看 etcd 配置以及阅读 apisix 源码发现，etcd 当前配置 5 分钟定时 compacted 一次，存在发布变更的话，apisix watch 到 event 发现 <code>err=compacted</code>, 会导致 apisix 全量拉取数据，此时会导致大量的 read 行为，导致 etcd 内存飙升</p>
<p>所以建议 apisix 依赖的 etcd 配置 compaction mode 改成 <code>revision</code>,  <a href="https://github.com/etcd-io/etcd/blob/a9cf27b169b1fc89bc0189ffaea7d21c52856b24/CHANGELOG/CHANGELOG-3.3.md#improved-5">相关文档</a></p>
<p>变更 etcd 配置：</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">auto-compaction-mode</span><span class="p">:</span><span class="w"> </span><span class="l">revision</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">auto-compaction-retention</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;1000&#34;</span><span class="w">
</span></span></span></code></pre></div><h3 id="file-logger-的性能问题">file-logger 的性能问题</h3>
<p>自定义插件，通过增加 batch-processor 解决 (本质上就是将 json encode 转移到了 nginx timer 里)</p>
<h3 id="querystring-中使用分号作为分隔符">queryString 中使用分号作为分隔符</h3>
<p>灰度过程中，有用户反馈拼接的参数没有生效，而在原来 Golang 版本网关中是正常的</p>
<p>排查发现，我们使用的是 Golang 1.6, 这个版本中，支持同时使用<code>&amp;</code>和<code>;</code>作为 QueryString 的分隔符，然后这个特性在 Golang 1.7 中被去除了 <a href="https://go.dev/doc/go1.17">Go 1.17 Release Notes: URL query parsing</a></p>
<p>所以，存量的请求中，原先有一些请求使用的是<code>;</code>作为分隔符，灰度到 Apisix 后就失效了</p>
<p>最后我们通过一个自定义插件，向前兼容了这批路由，原理很简单，把 <code>;</code> 换成 <code>&amp;</code> 再放回去解析</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">new_args</span> <span class="o">=</span> <span class="n">string_replace</span><span class="p">(</span><span class="n">ctx.var</span><span class="p">.</span><span class="n">args</span><span class="p">,</span> <span class="s2">&#34;;&#34;</span><span class="p">,</span> <span class="s2">&#34;&amp;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">core.request</span><span class="p">.</span><span class="n">set_uri_args</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">new_args</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="偶发-502">偶发 502</h3>
<p>有应用反应原先使用 nginx 作为反向代理的时候正常，接入 apisix 之后偶发 502</p>
<p>nginx 默认的配置 <a href="https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_http_version">proxy_http_version</a> 是 <code>HTTP/1.0</code>;</p>
<p>即没有配置的情况下，默认是 <code>HTTP/1.0</code></p>
<p>但是 apisix 的默认配置</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nginx" data-lang="nginx"><span class="line"><span class="cl"><span class="k">proxy_http_version</span> <span class="mi">1</span><span class="s">.1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">keepalive_timeout</span> <span class="s">60s</span><span class="p">;</span>
</span></span></code></pre></div><p>这就意味着，如果后端服务开启了 keepalive, 但是配置的 timeout 小于 60s, 那么就会出现偶发 502</p>
<p>实际验证中，对于 gunicorn 以 gevent/gthread 方式启动的服务，将默认的<code>2s</code>改成<code>65s</code>也不能规避这个问题，只能通过 <code>--keep-alive 0</code> 关闭 keepalive; <a href="https://docs.gunicorn.org/en/stable/settings.html#keepalive">相关文档</a></p>
]]></content>
		</item>
		
		<item>
			<title>关于在除夕前一天换了一个洗衣机的故事</title>
			<link>https://wklken.me/posts/2023/01/23/about-buy-a-new-washing-machine-before-chinese-new-year.html</link>
			<pubDate>Mon, 23 Jan 2023 00:15:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2023/01/23/about-buy-a-new-washing-machine-before-chinese-new-year.html</guid>
			<description>先讲讲这件事情的经过: 六年前入住的时候, 买了倍科洗烘套装, 没有买洗烘一体以及为啥买了一个比较小众的品牌是源于 SMZDM 上一个评测(小众算是一个坑点)</description>
			<content type="html"><![CDATA[<p>先讲讲这件事情的经过:</p>
<p>六年前入住的时候, 买了倍科洗烘套装, 没有买洗烘一体以及为啥买了一个比较小众的品牌是源于 SMZDM 上一个评测(小众算是一个坑点), 洗衣机价值 2500 左右, 然后只有一个地漏所以买了个三通(第二个坑点)</p>
<p>去年洗衣机坏过一次, 不排水导致衣服没法洗, 无法甩干, 然后电话客服, 已经过了质保, 只有电机是保十年的. 然后安排了一个不知道什么第三方的维修师傅过来.</p>
<p>师傅捣鼓半天, 结论是排水泵坏了, 换个新的, 410 CNY, 加上上门费用快 500 了, 修完也的确好了(坑, 第三方, 无任何价目表, 换的也非原装不知道什么牌子的)</p>
<p>然后, 现在快除夕了, 又出现了衣服无法甩干, 总是在最后剩一分钟洗几个小时, 怀疑是排水泵又又又坏了.</p>
<p>考虑到大过年的估计师傅没法及时过来修, 并且按照上一次经验, 肯定给换个排水泵, 又是四五百, 急用的话, 换一个.</p>
<p>并且考虑到未来的售后, 买的小天鹅, 京东下单, 第二天送到, 顺带旧的洗衣机回收了 150,  自己安装(过年师傅没法上门), 然后第一次桶自洁洗就报故障码了, 看说明书, 确认是排水管堵塞, 把三通拆开确定是里面堵死了. 这说明上一台洗衣机是好的, 甚至是上上次维修其实也是没必要的(估计当时师傅清理了三通然后给换了排水泵). 心里 wtf.</p>
<p>老婆说你原先不是做过测试吗? 为啥没排查出来&gt;_&lt;</p>
<hr>
<p>我想了想, 一部分在产品, 一部分在个人</p>
<h2 id="1-故障码">1. 故障码</h2>
<p>如果新买的洗衣机也没有故障码, 估计就得继续叫售后(这跟我上次空调杂音美的的师傅上门几次没修好退货买格力安装之后师傅发现是楼上空调滴水导致的, 这是另一个故事了)</p>
<p>旧的洗衣机没有任何故障码, 然后出错了也一直洗下去, 让你难以确认问题, 到底是哪坏了, 主板有没有问题(可能是比较老? 还是中国的洗衣机更懂中国?)</p>
<p>所以:</p>
<ol>
<li>一报错旧停止, 不要无视报错继续执行后面的事情, 或者假装无事发生(吞掉异常)</li>
<li>要有错误码/错误信息, 产品给出提示, 说明书给出解决方案(小天鹅的故障码列表直接贴操作面板上了)</li>
</ol>
<h2 id="2-你是否检查了所有地方">2. 你是否检查了所有地方?</h2>
<p>因为洗衣机维修那次全程围观了修理师傅的修理手段, 跟debug很像, 无非是切换各种模式, 验证整个洗衣的过程环节是否都正常(当然, 我没有注意到他清理了三通&hellip;.)</p>
<p>所以我一样地通过切换模式, 验证各个环节, 最后的结论是: 排水泵又坏了</p>
<p>但是, 无法洗衣甩干是现象, 排水泵坏了只是其中的原因, 应该还有其他几种可能(因为有过一次维修的记录相当然了)</p>
<p>是否确认了所有可能的原因? (如果我当时检查了过滤口/三通, 应该能省去一笔)</p>
<h2 id="3-经验">3. 经验</h2>
<p>我基于上次维修的现象和结论, 对当前的现象和结论形成了定向的思维. 这 100% 是排水泵的问题</p>
<p>只是基于过于小众/时效性, 决定换一台更符合当前的迫切需求.</p>
<p>那么, 原先的经验真的是有帮助的吗?</p>
<p>我们都知道, bug都会相对集中在某几个地方, 那么当现象和上次一样的时候, 凭借过往的经验, 我们很容易下结论(很笃定的那种), 这个过程中相当于全新去排查一个问题, 会少了很多步骤, 以至于无法判断是否是其他问题导致的.</p>
<p>另一方面, 我觉得那个维修洗衣机的师傅的经验对他肯定是有帮助的, 虽然我现在我很大程度上确定当时排水泵是没问题的, 但是当时那个师傅是不是已经确认是三通的问题然后多换掉一个本来完好的东西呢?</p>
<h2 id="4-小众与大众">4. 小众与大众</h2>
<p>买家电, 空调/冰箱/洗衣机这种大家电, 售后很重要, 毕竟维修和更换的成本太大, 所以建议选 top2, 并且我的选择原则是噱头越少越好, 买热销的型号, 功能少但是非常实用的这种(可靠性更高). 售后很重要, 又让我想起了六年前倍科洗衣机安装师傅过来安装, 连运输固定滚筒的泡沫都没取出来(这就很离谱了), 维修师傅上门穿的是第三方品牌的衣服.</p>
<p>买小家电, 微波炉/空气炸锅之类的, 成本比较低, 坏了也不心疼, 建议也选top的, 某些产品可以尝试小众的.</p>
<p>那么我们平时在做技术选型, 同样会基于这些因素去考虑.</p>
<p>例如第三方库, star数/最近的版本/最近的issue/bug比例/个人项目还是某个组织的项目等等, 都需要纳入考虑</p>
<h2 id="5-说明书">5. 说明书</h2>
<p>新洗衣机的安装, 安装师傅电话说过节了没法来, 让我自己装装看.</p>
<p>也很简单, 有工具就行, 安装说明书写得很清楚.(虽然我看半天没看懂隔音条应该装在哪个位置/边缘还是内侧横杠)</p>
<p>当然, 宜家的说明书我觉得体验会更好一些, 从图中很多细节可以确认正反/方向之类的.</p>
<p>所以, 文档很重要, 好的文档可以节省双方很多时间.</p>
<hr>
<p>后记: 对新买的洗衣机比较满意, 差了 6 年整体体验还是差异比较大的, 自动投放功能大大减轻洗衣负担, 容量比之前多了 2kg 但是更安静, 洗的速度也更快, 最重要的是, 价格比之前便宜:)</p>
]]></content>
		</item>
		
		<item>
			<title>Django DRF 性能优化</title>
			<link>https://wklken.me/posts/2022/10/19/django-drf-performance.html</link>
			<pubDate>Wed, 19 Oct 2022 01:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2022/10/19/django-drf-performance.html</guid>
			<description>1. DB 查询分析 步骤: 通过日志/监控等, 统计top 10的接口(URL及请求参数) 通过 APM 等, 统计慢接口top 10, 以及得到慢查询 SQL 开发环境, 开启SQL</description>
			<content type="html"><![CDATA[<h2 id="1-db-查询分析">1. DB 查询分析</h2>
<p>步骤:</p>
<ol>
<li>通过日志/监控等, 统计top 10的接口(URL及请求参数)</li>
<li>通过 APM 等, 统计慢接口top 10, 以及得到慢查询 SQL</li>
<li>开发环境, 开启SQL打印<code>django.db.backends</code>, 可以参考<a href="https://www.neilwithdata.com/django-sql-logging">Logging raw SQL to the console in Django</a></li>
<li>构造 <code>1/2</code>步中收集的请求, 在开发环境复现</li>
<li>获取执行的 SQL</li>
<li>分析</li>
</ol>
<p>分析:</p>
<ol>
<li>SQL 的查询条件是否命中索引, 是否有全表扫描(<code>mysql explain</code>)</li>
<li>SQL 中查询返回字段是否可以减少</li>
<li>是否某些 SQL 不应该执行, 但是执行了(误用)</li>
<li>是否某些 SQL 可以合并成一次查询</li>
<li>是否可以不执行/只执行一次, 例如通过加入缓存</li>
</ol>
<p><strong>重点</strong>: 高频查询, 增加索引能解决很大一部分问题; 尽量避免全表扫描的存在.</p>
<h2 id="2-serializer-优化">2. serializer 优化</h2>
<h3 id="21-property-or-serializermethodfield">2.1 property or SerializerMethodField</h3>
<blockquote>
<p>N+1 selects problem</p>
</blockquote>
<p>这两种, 在里面做了一些特殊操作, 例如执行一次db查询</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">Foo</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@property</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">last_check_time</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># property 放大</span>
</span></span><span class="line"><span class="cl">        <span class="n">cr</span> <span class="o">=</span> <span class="n">CheckRecords</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">is_success</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="o">.</span><span class="n">latest</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">cr</span><span class="o">.</span><span class="n">check_time</span> <span class="k">if</span> <span class="n">cr</span> <span class="ow">or</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">class</span> <span class="nc">FooOutputSLZ</span><span class="p">(</span><span class="n">serializers</span><span class="o">.</span><span class="n">ModelSerializer</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">last_check_time</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">DateTimeField</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">bar</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">SerializerMethodField</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">get_bar</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">Bar</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">foo_type</span><span class="o">=</span><span class="n">obj</span><span class="o">.</span><span class="n">type</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="p">()</span>
</span></span></code></pre></div><p>此时, 列表查询, page_size=100, 会产生 1+100+100=201 次 db 查询</p>
<p>优化:</p>
<ol>
<li>不返回</li>
<li>加缓存, 例如增加外部一个方法<code>get_bar_from_cache(foo_type: str)</code></li>
<li>通过<code>serializer context</code>在上层提前查好, 适合所有items公共的数据, 例如用户职位名称</li>
</ol>
<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">UserListApi</span><span class="p">(</span><span class="n">generics</span><span class="o">.</span><span class="n">ListAPIView</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_serializer_context</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># set into context, for slz to_representation</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">{</span><span class="s2">&#34;position_name_map&#34;</span><span class="p">:</span> <span class="n">get_position_name_map_from_db</span><span class="p">()}</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">UserOutputSLZ</span><span class="p">(</span><span class="n">serializers</span><span class="o">.</span><span class="n">ModelSerializer</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">position_name</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">SerializerMethodField</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_position_name</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">m</span> <span class="o">=</span>  <span class="bp">self</span><span class="o">.</span><span class="n">context</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;position_name_map&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">m</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">obj</span><span class="o">.</span><span class="n">position_id</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="22-serializer本身的性能问题">2.2 Serializer本身的性能问题</h3>
<p>可以参考: <a href="https://hakibenita.com/django-rest-framework-slow">Improve Serialization Performance in Django Rest Framework: How we reduced serialization time by 99%!</a></p>
<p>小改:</p>
<ol>
<li><code>read_only=True</code></li>
<li>自己构建数据, 不使用serializer</li>
</ol>
<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">UserOutputSLZ</span><span class="p">(</span><span class="n">serializers</span><span class="o">.</span><span class="n">ModelSerializer</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">position</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">read_only</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">data</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">u</span> <span class="ow">in</span> <span class="n">users</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span><span class="o">.</span><span class="n">append</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;id&#34;</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;name&#34;</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">name</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="k">return</span> <span class="n">Response</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">data</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">status</span><span class="o">.</span><span class="n">HTTP_200_OK</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="3-queryset-优化">3. queryset 优化</h2>
<h3 id="31-defer--only-等-只查询想要的字段">3.1 defer / only 等, 只查询想要的字段</h3>
<p>如果模型中存在一些本次逻辑用不上的字段, 那么不要查询出来, 或者声明只查询想要的字段</p>
<ul>
<li>例如<code>create_time/update_time</code></li>
<li><code>description</code>/<code>content</code>/<code>extras</code>等 TextField or JsonField</li>
</ul>
<p>使用 <a href="https://docs.djangoproject.com/en/4.1/ref/models/querysets/#only">django queryset only()</a> 只获取指定字段</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">Department</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">only</span><span class="p">(</span><span class="s2">&#34;id&#34;</span><span class="p">,</span> <span class="s2">&#34;name&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>使用 <a href="https://docs.djangoproject.com/en/4.1/ref/models/querysets/#defer">django queryset defer()</a> 延迟获取某些字段</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">Department</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">defer</span><span class="p">(</span><span class="s2">&#34;description&#34;</span><span class="p">,</span> <span class="s2">&#34;extras&#34;</span><span class="p">,</span> <span class="s2">&#34;create_time&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>可以在打印的 SQL 中看到 SELECT 字段列表的变化.</p>
<h3 id="32-valuesvalues_list">3.2 values()/values_list()</h3>
<p>以dict/tuple的形式获取结果列表, 避免对象创建</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"># got List[Dict]</span>
</span></span><span class="line"><span class="cl"><span class="n">Department</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">values</span><span class="p">(</span><span class="s2">&#34;id&#34;</span><span class="p">,</span> <span class="s2">&#34;name&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># got List[Tuple]</span>
</span></span><span class="line"><span class="cl"><span class="n">Department</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">values_list</span><span class="p">(</span><span class="s2">&#34;id&#34;</span><span class="p">,</span> <span class="s2">&#34;name&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p><code>values_list()</code>如果只获取一个字段, 那么可以加上<code>flat=True</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">Department</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">values_list</span><span class="p">(</span><span class="s1">&#39;id&#39;</span><span class="p">,</span> <span class="n">flat</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="33-prefetch_related--selected_related">3.3 prefetch_related / selected_related</h3>
<blockquote>
<p>N+1 selects problem</p>
</blockquote>
<p>这两个的目的都是将<code>可能的多次查询</code>合并, 减少 DB 查询次数, 可以参考这篇文章 <a href="https://segmentfault.com/a/1190000016149027">Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化</a></p>
<ul>
<li><a href="https://docs.djangoproject.com/en/4.1/ref/models/querysets/#select-related">select_related()</a>: “follow” foreign-key relationships, selecting additional related-object data when it executes its query. (SQL JOIN)</li>
<li><a href="https://docs.djangoproject.com/en/4.1/ref/models/querysets/#prefetch-related">prefetch_related()</a>: automatically retrieve, in a single batch, related objects for each of the specified lookups.(ANOTHER SQL)</li>
</ul>
<p>注意, <code>select_related</code>/<code>prefetch_related</code> 的表如果数据量特别大(百万), 或者单页page_size特别大(几千/上万), 可能导致查询直接卡死</p>
<h3 id="34-其他">3.4 其他</h3>
<p>use <code>count()</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"># bad</span>
</span></span><span class="line"><span class="cl"><span class="nb">len</span><span class="p">(</span><span class="n">User</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</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"># good</span>
</span></span><span class="line"><span class="cl"><span class="n">User</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">count</span><span class="p">()</span>
</span></span></code></pre></div><p>use <code>exists()</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"># bad</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">User</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">username</span><span class="o">=</span><span class="n">x</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">dosomething</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># good</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span>  <span class="n">User</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">username</span><span class="o">=</span><span class="n">x</span><span class="p">)</span><span class="o">.</span><span class="n">exists</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">dosomething</span>
</span></span></code></pre></div><p>use <code>bulk_create()</code>/<code>bulk_update()</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">Entry</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">bulk_create</span><span class="p">(</span><span class="n">objs</span><span class="p">,</span> <span class="mi">1000</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">Entry</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">bulk_update</span><span class="p">(</span><span class="n">objs</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;headline&#39;</span><span class="p">],</span> <span class="mi">1000</span><span class="p">)</span>
</span></span></code></pre></div><p>specific the updated fields</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">product</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="s1">&#39;Name changed again&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">product</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="n">update_fields</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>use <code>F</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">Reporter</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">stories_filed</span><span class="o">=</span><span class="n">F</span><span class="p">(</span><span class="s1">&#39;stories_filed&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="4-缓存-local-memory-or-redis">4. 缓存: local memory or redis</h2>
<blockquote>
<p>没有加一层缓存解决不了的问题, 如果有, 加两层&hellip;&hellip;</p>
</blockquote>
<p>有些数据库查询, 每次请求都会触发; 有些请求会触发 N 次相同的重复查询;</p>
<p>这些不一定能通过上面列举的方法优化</p>
<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">django.core.cache</span> <span class="kn">import</span> <span class="n">caches</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">get_default_department_id_from_cache</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">cache</span> <span class="o">=</span> <span class="n">caches</span><span class="p">[</span><span class="s2">&#34;locmem&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="nb">id</span> <span class="o">=</span> <span class="n">cache</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">DEFAULT_DEPT_ID</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">id</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">id</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nb">id</span> <span class="o">=</span> <span class="n">Department</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">get_default</span><span class="p">()</span><span class="o">.</span><span class="n">id</span>
</span></span><span class="line"><span class="cl">    <span class="n">cache</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">DEFAULT_DEPT_ID</span><span class="p">,</span> <span class="n">category_id</span><span class="p">,</span> <span class="mi">1</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">id</span>
</span></span></code></pre></div><p>粗粒度: <a href="https://docs.djangoproject.com/en/4.1/topics/cache/#the-per-view-cache">cache_page: The per-view cache</a></p>
]]></content>
		</item>
		
		<item>
			<title>DRF 的一些实践 Part1: Serializer</title>
			<link>https://wklken.me/posts/2022/10/09/django-drf-serializer.html</link>
			<pubDate>Sun, 09 Oct 2022 01:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2022/10/09/django-drf-serializer.html</guid>
			<description>SLZ 只做输入/输出相关的逻辑, 尽量避免处理具体的业务逻辑 命名建议 写全 xxSerializers 也可以用缩写xxSLZ(注意全大写, 不要用xxSlz) 两种风格 1. 全部se</description>
			<content type="html"><![CDATA[<blockquote>
<p>SLZ 只做输入/输出相关的逻辑, 尽量避免处理具体的业务逻辑</p>
</blockquote>
<h2 id="命名建议">命名建议</h2>
<ol>
<li>写全 <code>xxSerializers</code></li>
<li>也可以用缩写<code>xxSLZ</code>(注意全大写, 不要用<code>xxSlz</code>)</li>
</ol>
<h2 id="两种风格">两种风格</h2>
<h3 id="1-全部serializer放在一起">1. 全部serializer放在一起</h3>
<p>同一个application的全部serializer放在一起</p>
<pre tabindex="0"><code>application
    |- serializers.py
</code></pre><p>如果都放在一起, 建议区分 <code>input</code> 和 <code>output</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">LoginLogInputSLZ</span><span class="p">(</span><span class="n">serializers</span><span class="o">.</span><span class="n">Serializer</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="k">class</span> <span class="nc">LoginLogOutputSLZ</span><span class="p">(</span><span class="n">serializers</span><span class="o">.</span><span class="n">Serializer</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="k">class</span> <span class="nc">LoginLogListApi</span><span class="p">(</span><span class="n">generics</span><span class="o">.</span><span class="n">ListAPIView</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">LoginLogOutputSLZ</span>
</span></span></code></pre></div><h3 id="2-直接放在view中">2. 直接放在view中</h3>
<p>放在api定义中(<code>Serializer should be nested in the API and be named either InputSerializer or OutputSerializer</code>) <a href="https://github.com/HackSoftware/Django-Styleguide#apis--serializers"></a></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">UserListApi</span><span class="p">(</span><span class="n">APIView</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nc">OutputSerializer</span><span class="p">(</span><span class="n">serializers</span><span class="o">.</span><span class="n">Serializer</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="k">class</span> <span class="nc">InputSerializer</span><span class="p">(</span><span class="n">serializers</span><span class="o">.</span><span class="n">Serializer</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="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">users</span> <span class="o">=</span> <span class="n">user_list</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">OutputSerializer</span><span class="p">(</span><span class="n">users</span><span class="p">,</span> <span class="n">many</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="o">.</span><span class="n">data</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">Response</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="inputslz">InputSLZ</h2>
<blockquote>
<p>只做校验相关的逻辑, 避免放入业务逻辑</p>
</blockquote>
<h3 id="1-method-validate_field">1. method: validate_{field}</h3>
<p>校验某个字段: <code>validate_{field}</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">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">serializers</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">BlogPostSerializer</span><span class="p">(</span><span class="n">serializers</span><span class="o">.</span><span class="n">Serializer</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">title</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">content</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">CharField</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">validate_title</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">        Check that the blog post is about Django.
</span></span></span><span class="line"><span class="cl"><span class="s2">        &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="s1">&#39;django&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">value</span><span class="o">.</span><span class="n">lower</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="n">serializers</span><span class="o">.</span><span class="n">ValidationError</span><span class="p">(</span><span class="s2">&#34;Blog post is not about Django&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">value</span>
</span></span></code></pre></div><h3 id="2-method-validate">2. method: validate</h3>
<p>整体校验(对象级别): <code>validate</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">from</span> <span class="nn">rest_framework</span> <span class="kn">import</span> <span class="n">serializers</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">EventSerializer</span><span class="p">(</span><span class="n">serializers</span><span class="o">.</span><span class="n">Serializer</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">description</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">start</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">DateTimeField</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">finish</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">DateTimeField</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">validate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">        Check that the start is before the stop.
</span></span></span><span class="line"><span class="cl"><span class="s2">        &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">data</span><span class="p">[</span><span class="s1">&#39;start&#39;</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">data</span><span class="p">[</span><span class="s1">&#39;finish&#39;</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="n">serializers</span><span class="o">.</span><span class="n">ValidationError</span><span class="p">(</span><span class="s2">&#34;finish must occur after start&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">data</span>
</span></span></code></pre></div><h3 id="3-validators">3. validators</h3>
<p><a href="https://www.django-rest-framework.org/api-guide/validators/">官方文档: Validators</a></p>
<p>复用 validate 方法</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">multiple_of_ten</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">value</span> <span class="o">%</span> <span class="mi">10</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">serializers</span><span class="o">.</span><span class="n">ValidationError</span><span class="p">(</span><span class="s1">&#39;Not a multiple of ten&#39;</span><span class="p">)</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">GameRecord</span><span class="p">(</span><span class="n">serializers</span><span class="o">.</span><span class="n">Serializer</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">score</span> <span class="o">=</span> <span class="n">IntegerField</span><span class="p">(</span><span class="n">validators</span><span class="o">=</span><span class="p">[</span><span class="n">multiple_of_ten</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span></code></pre></div><h3 id="4-to_internal_value">4. to_internal_value</h3>
<p><a href="https://www.django-rest-framework.org/api-guide/serializers/#overriding-serialization-and-deserialization-behavior">官方文档: Overriding serialization and deserialization behavior</a></p>
<p>如果输入的表单和业务模型数据结构差异很大, 需要做全局转换, 可以使用 <code>to_internal_value</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">DynamicFieldUpdateInputSLZ</span><span class="p">(</span><span class="n">serializers</span><span class="o">.</span><span class="n">Serializer</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">to_internal_value</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">field_a</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;field_a&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># do something with field_a</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;x&#34;</span><span class="p">:</span> <span class="n">field_a</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span></code></pre></div><h2 id="outputslz">OutputSLZ</h2>
<blockquote>
<p>只做展示拼装相关的逻辑</p>
</blockquote>
<h3 id="1-serializermethodfield">1. SerializerMethodField</h3>
<p>使用 <code>SerializerMethodField</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">LoginLogOutputSLZ</span><span class="p">(</span><span class="n">serializers</span><span class="o">.</span><span class="n">Serializer</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">datetime</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">SerializerMethodField</span><span class="p">(</span><span class="n">help_text</span><span class="o">=</span><span class="n">_</span><span class="p">(</span><span class="s2">&#34;登录时间&#34;</span><span class="p">),</span> <span class="n">required</span><span class="o">=</span><span class="kc">False</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">get_datetime</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">obj</span><span class="o">.</span><span class="n">create_time</span>
</span></span></code></pre></div><p>注意: 尽量避免在 <code>SerializerMethodField</code>或者<code>model</code>的<code>@property</code>中做一些独立的查询工作, 这样会带来查询放大, 一次请求查 1000 条数据, 可能会有 1001 次数据库查询</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">Profile</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@property</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">last_login_time</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Optional</span><span class="p">[</span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;&#34;获取用户最近一次登录时间&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">latest_login</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">login_set</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">is_success</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="o">.</span><span class="n">latest</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">latest_login</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="n">latest_login</span><span class="o">.</span><span class="n">create_time</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">None</span>
</span></span></code></pre></div><p>如果在多个SLZ中反复出现同一个<code>SerializerMethodField</code></p>
<ul>
<li>处理逻辑跟<code>展示层</code>关系不大, 可下沉到<code>model</code>的<code>@property</code></li>
<li>处理逻辑跟<code>展示层</code>有关系, 可以抽象成<code>mixin</code></li>
</ul>
<h3 id="2-to_representation">2. to_representation</h3>
<p><a href="https://www.django-rest-framework.org/api-guide/serializers/#overriding-serialization-and-deserialization-behavior">官方文档: Overriding serialization and deserialization behavior</a></p>
<p>如果需要做较大的转换, 例如得到的模型数据相对返回的数据结构差异很大, 需要做大量的转换/映射/内嵌/判断等, 此时可以直接定义<code>to_representation(self, obj)</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">GeneralLogOutputSLZ</span><span class="p">(</span><span class="n">serializers</span><span class="o">.</span><span class="n">Serializer</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">id</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">IntegerField</span><span class="p">(</span><span class="n">help_text</span><span class="o">=</span><span class="n">_</span><span class="p">(</span><span class="s2">&#34;ID&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">extra_value</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">JSONField</span><span class="p">(</span><span class="n">help_text</span><span class="o">=</span><span class="n">_</span><span class="p">(</span><span class="s2">&#34;额外信息&#34;</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">to_representation</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">to_representation</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">extra_value</span> <span class="o">=</span> <span class="n">instance</span><span class="p">[</span><span class="s2">&#34;extra_value&#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">field_a</span> <span class="o">=</span> <span class="n">extra_value</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;field_a&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;field_a&#34;</span><span class="p">:</span> <span class="n">field_a</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;filed_b&#34;</span><span class="p">:</span> <span class="n">field_b</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span></code></pre></div><h3 id="3-serializer_context">3. serializer_context</h3>
<p>通过<code>get_serializer_context</code>设置context变量, 之后在渲染时可以通过<code>self.context.get(&quot;{KEY}&quot;)</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">LoginLogListApi</span><span class="p">(</span><span class="n">generics</span><span class="o">.</span><span class="n">ListAPIView</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">serializer_class</span> <span class="o">=</span> <span class="n">LoginLogOutputSLZ</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">get_serializer_context</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># request/format/view</span>
</span></span><span class="line"><span class="cl">        <span class="n">ctx</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">get_serializer_context</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">ctx</span><span class="p">[</span><span class="s2">&#34;category_name_map&#34;</span><span class="p">]</span> <span class="o">=</span>  <span class="n">get_category_display_name_map</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">ctx</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">LoginLogOutputSLZ</span><span class="p">(</span><span class="n">serializers</span><span class="o">.</span><span class="n">Serializer</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">category_display_name</span> <span class="o">=</span> <span class="n">serializers</span><span class="o">.</span><span class="n">SerializerMethodField</span><span class="p">(</span><span class="n">help_text</span><span class="o">=</span><span class="n">_</span><span class="p">(</span><span class="s2">&#34;所属目录&#34;</span><span class="p">),</span> <span class="n">required</span><span class="o">=</span><span class="kc">False</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">get_category_display_name</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;&#34;get category display name from log extra value&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">category_id</span> <span class="o">=</span> <span class="n">obj</span><span class="o">.</span><span class="n">profile</span><span class="o">.</span><span class="n">id</span>
</span></span><span class="line"><span class="cl">        <span class="n">category_name_map</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">context</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;category_name_map&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">category_display_name</span> <span class="o">=</span> <span class="n">category_name_map</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">category_id</span><span class="p">,</span> <span class="n">PLACE_HOLDER</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">category_display_name</span>
</span></span></code></pre></div><p>坑: 如果自己实例化slz, 需要显式传递context, 否则在slz中是拿不到的</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">context</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_serializer_context</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">xxOutputSLZ</span><span class="p">(</span><span class="n">results</span><span class="p">,</span> <span class="n">many</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">context</span><span class="o">=</span><span class="n">context</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="slz-传递的参数">SLZ 传递的参数</h2>
<ul>
<li>input slz 可能传递一些查询相关的参数, 例如 <code>order_by=create_time</code></li>
<li>output slz可能传递一些返回结果相关的参数, 例如<code>fields=a,b,c</code></li>
</ul>
<p>逻辑上是需要支持的, 但是, <strong>要严格控制字段白名单</strong></p>
<p>例如, <code>order_by</code>只允许传递 主键或有建立相关索引的字段名, 而不是任意字段, 避免 <code>order_by=enabled</code>这类查询无法命中索引带来的慢查询</p>
<p>同样, <code>fileds</code>应该避免允许传递<code>重</code>的property字段(存在额外查询). 如果调用方不传递, 应该默认使用<code>最小化</code>集合, 而不是<code>全部</code>;</p>
<h2 id="modelserializer-尽量使用最小化集合">ModelSerializer 尽量使用最小化集合</h2>
<p>如果使用了<code>serializers.ModelSerializer</code>, <code>Meta.fields</code>尽量使用枚举, 或者配置<code>exclude</code>, 而不是<code>fields = &quot;__all__&quot;</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">CategorySettingOutputSLZ</span><span class="p">(</span><span class="n">serializers</span><span class="o">.</span><span class="n">ModelSerializer</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">model</span> <span class="o">=</span> <span class="n">Setting</span>
</span></span><span class="line"><span class="cl">        <span class="n">fields</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;key&#34;</span><span class="p">,</span> <span class="s2">&#34;namespace&#34;</span><span class="p">,</span> <span class="s2">&#34;region&#34;</span><span class="p">,</span> <span class="s2">&#34;value&#34;</span><span class="p">,</span> <span class="s2">&#34;enabled&#34;</span><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"># fields = &#34;__all__&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># 可以用这个, 但是后续加新字段可能会忘记加exclude</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># exclude = (&#34;create_time&#34;, &#34;update_time&#34;)</span>
</span></span></code></pre></div><p>避免:</p>
<ol>
<li>暴露不必要的字段出去, (场景: 未来字段变更想去掉, 如果原来没有暴露, 那么直接去, 否则需要找调用方逐一确认)</li>
<li>不小心暴露不能更新的字段出去, 例如<code>create_time</code>/<code>update_time</code></li>
</ol>
<h2 id="单一职责">单一职责</h2>
<p><strong>单一职责原则</strong>: 一个slz只给一个view使用, 尽量避免多个view共用一个slz</p>
<h2 id="不一定需要继承-复用-vs-不复用">不一定需要继承: 复用 vs 不复用</h2>
<p>极端处理: 每个 slz 都是独立的, 及时存在公共字段, 也不抽父类出来;</p>
<p>带来的后果: 字段的变更/校验逻辑变更等, 需要改动涉及的所有 slz</p>
<p>使用继承: 变更只需要改一个地方, 但是存在风险, 改一个地方可能动到意想不到的其他接口校验/返回</p>
<p>倾向的做法:</p>
<ol>
<li>刚开始开发, 完全不考虑继承, 完全独立</li>
<li>主体逻辑和接口开发完之后, 可以根据需要加入部分继承, 例如公共的<code>create_time/update_time</code>等字段</li>
<li>如果使用继承, 尽量减少继承的层级(不大于 3 层), 并且需要考虑<code>隔离</code>: 输入/输出的 slz 父类进行隔离, 或者核心模块slz和非核心模块slz隔离, 即使它们有相同的字段;(缩小未来变更的影响范围)</li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>DRF继承关系图</title>
			<link>https://wklken.me/posts/2022/10/07/django-drf-inherit.html</link>
			<pubDate>Fri, 07 Oct 2022 22:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2022/10/07/django-drf-inherit.html</guid>
			<description>最佳实践: 业务逻辑类, 尽量继承于右下角的这批基类, 以保持简单(所有开发有共同的框架背景认识) 尽量不要在业务逻辑类与 DRF 基类之间增加一层 公共基类</description>
			<content type="html"><![CDATA[<p><img src="/imgs/python-practices/drf_inherit.drawio.png" alt=""></p>
<p>最佳实践:</p>
<ul>
<li>业务逻辑类, 尽量继承于<code>右下角</code>的这批基类, 以保持简单(所有开发有共同的框架背景认识)</li>
<li>尽量不要在业务逻辑类与 DRF 基类之间增加一层 <code>公共基类</code>(避免引入意大利面式的调用链, 阅读困难, 带来认知负担)</li>
<li>只实现最小的扩展, 做到<code>望名知意</code>, 切忌实现大而全的扩展</li>
<li>使用 DRF 的大多数情况下, 代码越多越好维护</li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>Better Code: 关于接口的灵活性</title>
			<link>https://wklken.me/posts/2022/07/08/better-code-5-flexible-api.html</link>
			<pubDate>Fri, 08 Jul 2022 23:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2022/07/08/better-code-5-flexible-api.html</guid>
			<description>在实际需求开发中，我们会面对复杂多变的需求。所以在给外部提供API的时候，经常会面临接口协议的变更：例如增加一个查询字段，支持按照某个字段升</description>
			<content type="html"><![CDATA[<p>在实际需求开发中，我们会面对复杂多变的需求。所以在给外部提供API的时候，经常会面临接口协议的变更：例如增加一个查询字段，支持按照某个字段升降序，或者多返回某个字段。</p>
<p>此时你也许会想，不如直接支持graphql或者类似的语法，将整个模型暴露出来，想要什么字段，根据什么排序，查询多少数据量，按照什么升降序自己决定好了。这样能做到一次开发，多次使用，一劳永逸。</p>
<p>但是，这往往是<strong>过于理想化</strong>的考虑，特别是对于大部分项目，或者被非常多外部系统依赖的服务（这也是为什么需求变更比较多的原因）</p>
<hr>
<p>在实际开发中，我们都知道，一个接口一旦上线生产，那么协议变更就变得非常难，至少要非常谨慎。因为有众多的依赖方。并且下线一个接口也尤为困难，因为势必要推动所有系统切换新接口。</p>
<p>所以，如果暴露整个模型由调用方自行选择，那么结果就是，将<strong>主动权</strong>交给了<strong>调用方</strong>，把自己变得被动。因为你很难感知到调用方是如何组合那些条件的，用了哪些参数，是否合理。模型层面的变更将变得困难，字段的增删，类型变更，格式变更等等都需要考虑诸多调用方。</p>
<p>相当于拿一时的便利，换取了未来维护的<strong>不确定性</strong>。但是，确定的是，维护成本是成倍地上升。</p>
<hr>
<p>假设模型数据变得庞大，从几千增长为十万级，百万级，那么原先很多无关紧要条件变得棘手，例如按照一个不确定的字段排序，或者一个不在索引中的字段排序。此时原先的查询变得很慢，数据库压力变大，机器负载上升。</p>
<p>那么，这时候要保证系统正常，就会付出更大的代价。</p>
<p>这个可不是提供一个<code>/api/v2</code>那么简单，大部分系统一旦使用，除非特别的原因，否则是不会考虑迁移新接口的，此时，可能由于某几个很小的系统调用吃掉系统绝大部份的资源。举步维艰。</p>
<p>所以，在接口协议设计和提供的时候，需要花更多的时间去思考，始终保持<strong>谨慎</strong>，<strong>克制</strong>。</p>
<p>比如，需要一个字段，绝不提供两个。协议中可选的内容，都是已确认可控的内容。消除不确定性。</p>
<p>这或许看起来接口不够灵活，不够强大。但是，消除了不确定性。</p>
<hr>
<p>应该在内部实现保持灵活，但暴露出去的保持克制。例如某些地方可以变成可配置的，需求变更只需要改配置而不是代码。</p>
<p>即，灵活性始终把握在自己手中。应对需求变更的时候，付出少量的时间。相对原先绝对灵活的方式会多出一些确认及开发的时间，但是对于未来的维护时间，这已经是非常划算的了。</p>
<blockquote>
<p>内部支持，谨慎提供。
make everything under control</p>
</blockquote>
<p>如果调用方可控，例如前后分离自己的前端或者同一个系统内的上层服务，那么用graphql 或者类似灵活的方式提供接口问题不大，毕竟变更或升级的成本不高，比较可控。</p>
<p>否则，提供接口不要那么奔放。</p>
<hr>
<p>另外，最近遇到一个问题得到一个小的tip，如果一个接口需要提供两种或两种以上有差异的返回（协议有差别，但是不是特别大），建议提供多个接口而不是在一个接口通过参数动态确定返回。即使差异非常小。差异小只在当下，随着时间推移需求变化，耦合在一起的代码将会维护困难，改一个逻辑可能动到不相关的返回。宁可多写一些代码，尽量保持逻辑独立性。</p>
]]></content>
		</item>
		
		<item>
			<title>新的仓库: wklken/naming</title>
			<link>https://wklken.me/posts/2022/07/03/the-project-naming.html</link>
			<pubDate>Sun, 03 Jul 2022 11:30:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2022/07/03/the-project-naming.html</guid>
			<description>上一次开新项目, 还是 wklken/httptest, 还有很多功能没有完善 后来, 又fork了gorequest来了一回大修 wklken/gorequest 这次本来在写一片关于命名的文章, 然后花了几个小时</description>
			<content type="html"><![CDATA[<p>上一次开新项目, 还是 <a href="https://github.com/wklken/httptest">wklken/httptest</a>, 还有很多功能没有完善</p>
<p>后来, 又fork了gorequest来了一回大修 <a href="https://github.com/wklken/gorequest">wklken/gorequest</a></p>
<p>这次本来在写一片关于命名的文章, 然后花了几个小时通过bash+python+nltk, 写了一个项目</p>
<p><a href="https://github.com/wklken/naming">https://github.com/wklken/naming</a></p>
<p>目前完成了基本的结构, 分析了k8s整个仓库的命名</p>
<p>后续会花时间继续完善:</p>
<ul>
<li>增加各种语言top10项目的分析</li>
<li>加入github action自动化</li>
<li>做一个主页, 关键字搜索关联的词/分类等</li>
<li>中文支持</li>
</ul>
<p>希望这个项目, 能够在给接口/类/函数命名时, 给到提示, 节省时间, 提高效率.</p>
]]></content>
		</item>
		
		<item>
			<title>缓存使用的一些经验</title>
			<link>https://wklken.me/posts/2022/06/26/summary-01-cache.html</link>
			<pubDate>Sun, 26 Jun 2022 10:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2022/06/26/summary-01-cache.html</guid>
			<description>在一个大的项目中, 使用了全缓存模型, 即, 所有数据都会经过cache. 简单分层: 应用-&amp;gt;内存缓存-&amp;gt;redis缓存-&amp;gt;数据库</description>
			<content type="html"><![CDATA[<p>在一个大的项目中, 使用了全缓存模型, 即, 所有数据都会经过cache.</p>
<p>简单分层: <code>应用-&gt;内存缓存-&gt;redis缓存-&gt;数据库</code></p>
<p>是一个典型的<code>多读写少</code>的场景, 并且数据量, 请求量非常大.</p>
<p>总结了一些使用经验, 供参考</p>
<h2 id="1-更新缓存的design-pattern-使用cache-aside">1. 更新缓存的Design Pattern: 使用Cache aside</h2>
<blockquote>
<p>简洁优雅</p>
</blockquote>
<p>关于缓存更新, 可以阅读这篇文章: <a href="https://coolshell.cn/articles/17416.html">CoolShell: 缓存更新的套路</a></p>
<p>为什么选择<code>Cache Aside Pattern</code>, 因为这个模式足够简单, 出现不一致的概率非常低, 对于大多数项目来说够用了.</p>
<p>而其他几种模式, 复杂度会高很多.</p>
<h2 id="2-并发很高时-需要防缓存击穿">2. 并发很高时, 需要防缓存击穿</h2>
<p>当并发很高的时候, 一个热点<code>key</code>失效, 会触发回数据库重查的逻辑, 此时会有大量请求落到数据库</p>
<p>需要做防缓存击穿的处理.</p>
<p>一般各种语言的库, 都有考虑到这一点, 例如 <a href="https://github.com/go-redis/cache/blob/v8/cache.go#L116">go-redis/cache</a></p>
<p>如果是golang并且自定义了cache, 可以使用 <a href="https://pkg.go.dev/golang.org/x/sync/singleflight">singleflight</a>, 其他语言也可以找类似机制的库.</p>
<p>这个库很轻量</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="err">#</span> <span class="nx">define</span>
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Cache</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">name</span>              <span class="kt">string</span>
</span></span><span class="line"><span class="cl">	<span class="nx">keyPrefix</span>         <span class="kt">string</span>
</span></span><span class="line"><span class="cl">	<span class="nx">codec</span>             <span class="o">*</span><span class="nx">cache</span><span class="p">.</span><span class="nx">Cache</span>
</span></span><span class="line"><span class="cl">	<span class="nx">cli</span>               <span class="o">*</span><span class="nx">redis</span><span class="p">.</span><span class="nx">Client</span>
</span></span><span class="line"><span class="cl">	<span class="nx">defaultExpiration</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Duration</span>
</span></span><span class="line"><span class="cl">	<span class="nx">G</span>                 <span class="nx">singleflight</span><span class="p">.</span><span class="nx">Group</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="err">#</span> <span class="nx">usage</span>
</span></span><span class="line"><span class="cl"><span class="c1">// if missing, call retrieveFunc
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">data</span><span class="p">,</span> <span class="nx">err</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">G</span><span class="p">.</span><span class="nf">Do</span><span class="p">(</span><span class="nx">key</span><span class="p">.</span><span class="nf">Key</span><span class="p">(),</span> <span class="kd">func</span><span class="p">()</span> <span class="p">(</span><span class="kd">interface</span><span class="p">{},</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nf">retrieveFunc</span><span class="p">(</span><span class="nx">key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="p">})</span>
</span></span></code></pre></div><h2 id="3-缓存空值-需要防缓存穿透">3. 缓存空值, 需要防缓存穿透</h2>
<p>如果一个key不存在, 在缓存中查不到, 在数据库中也查不到, 那么这个key的请求每次都会穿透到数据库</p>
<p>此时, 可以引入 <code>bloomfilter</code> 或者 <code>cuckoofilter</code>;</p>
<p>但是, 更简单的做法是, 缓存空值; 当成一个普通的key处理(缓存失效/数据一致性处理等)</p>
<h2 id="4-总是设置过期时间-并且带随机数避免缓存雪崩">4. 总是设置过期时间, 并且带随机数避免缓存雪崩</h2>
<p>大部分场景下, 给每一个缓存<code>key</code>设置 <code>TTL</code>是一个很好的习惯. 可以避免无用数据占用资源, 及时淘汰掉使用较少的数据.</p>
<p>但是, 设置<code>TTL</code>的时候, 建议加上一个范围内容的随机数, 避免缓存在同一时间失效, 造成缓存雪崩.</p>
<pre tabindex="0"><code>TTL = 900s + randint(0,10)
</code></pre><h2 id="5-key-中使用namespaceversion前缀">5. key 中使用namespace+version前缀</h2>
<blockquote>
<p>key = {namespace}:{version}:{type}:{uniqueKey}</p>
</blockquote>
<p>在实际应用部署中, 由于可能跟其他应用共用一套缓存, 所以建议缓存的<code>key</code>加入前缀, 防止冲突(如果冲突, 非常难以debug)</p>
<p>另外, 需要加入一个<code>version</code>, 在版本发布必要时变更, 以弃用缓存中已有的数据</p>
<ol>
<li>由于不断迭代开发, 同一个key对应的value可能会变更, 例如value对应的数据结构新增了一个字段, 那么此时缓存中存量的缓存数据是没有这个字段的, 可能会造成一些bug.</li>
<li>还有另外一个需要特别小心的是, 升级缓存第三方库的时候, 某些版本可能是breaking change, 例如改变了压缩算法, 此时存量数据将无法正确被获取.<a href="https://github.com/go-redis/cache/issues/52">一个例子: Can&rsquo;t upgrade from v7 to v8 directly?</a></li>
</ol>
<h2 id="6-缓存结构体-使用msgpack替代json">6. 缓存结构体, 使用msgpack替代json</h2>
<p><a href="https://msgpack.org/">MessagePack: It&rsquo;s like JSON.but fast and small</a></p>
<p>优点:</p>
<ul>
<li>快</li>
<li>小</li>
</ul>
<p>缺点:</p>
<ul>
<li>在redis等服务端debug获取时不是明文, 不是很利于调试</li>
</ul>
<p>所以, 缓存数据量比较大, 并且对性能有要求的, 可以使用msgpack</p>
<h2 id="7-value比较大-可以考虑启用压缩">7. value比较大, 可以考虑启用压缩</h2>
<p>如果<code>value</code>比较大, 那么在放入缓存前, 可以进行一次压缩, 获取后再解压</p>
<p>当然, 这个会产生额外的资源消耗(CPU), 以及会多一些耗时.</p>
<p>但是, 这个有利于减少网络传输中的包大小. 如果<code>读取</code>是非常高频的话, 那么代价还是值得的.</p>
<p>可以参考 <a href="https://github.com/go-redis/cache/blob/v8/cache.go#L349">go-redis/cache</a>, 当值超过一定大小时使用 <a href="https://github.com/klauspost/compress/tree/master/s2">s2 compression</a> 进行压缩</p>
<h2 id="8-批量操作-使用pipeline">8. 批量操作, 使用pipeline</h2>
<p>以redis为例, 批量操作</p>
<ol>
<li>代码for循环, 一个个获取</li>
<li>可以考虑使用<code>mget/mhget</code>之类的多个key</li>
<li>使用<code>pipeline</code></li>
</ol>
<p>可以根据<code>key-value</code>特征, 批量<code>key</code>的数量等, 简单压测下性能, 决定使用哪种方式. 正常情况下, <code>key</code>数量较大的时候, <code>pipeline</code>性能最好.</p>
<p>甚至, 代码实现可以根据<code>key</code>的数量, 自行决定使用<code>mget</code>还是<code>pipeline</code></p>
<h2 id="9-内存缓存-vs-redis">9. 内存缓存 vs Redis</h2>
<p>大部分情况, 项目中会混用两种缓存.</p>
<p>如果对数据一致性要求比较高, 可以全部使用 Redis.</p>
<p>但是, 其实每一次 Redis 操作代价大于内存操作</p>
<p>某些数据, 例如模型, 主键之类的, 一旦确定, 是不会变更的.</p>
<p>此时, 可以考虑使用<code>内存缓存</code>替代.</p>
<p>如果是golang, 推荐使用 <a href="https://github.com/patrickmn/go-cache">go-cache</a>. 没有其他实现那么强大, 但是胜在不需要序列化/反序列化.</p>
<h2 id="10-多级缓存-and-client-side-cache">10. 多级缓存 and client-side-cache</h2>
<p>如果使用的 Redis6, 并且程序的driver支持, 那么可以直接利用 <a href="https://redis.io/docs/manual/client-side-caching/">client-side-caching</a> 特性获取最大的性能. 这个对程序透明, 无需在额外的逻辑处理.</p>
<p>但是, 当前(2022)有很多时候, 部署基建还是老版本Redis, 很多语言的driver也还没有支持, 可能复用不了</p>
<p>那么, 此时如果使用了<code>内存-&gt;redis</code>两级缓存, 如何确保数据一致性.</p>
<p>可以做的额外操作:</p>
<ol>
<li>实现类似redis6 client-side-cache机制, 通过发布订阅等方式实现</li>
<li>可以使用一个<code>sorted-set</code>存储<code>5</code>分钟内变更的<code>key</code>, 内存缓存TTL设置<code>5</code>分钟; 每次先获取变更<code>key</code>列表, 本地缓存进行时间戳对比(这个方案对于批量key操作性能提升很大, 相当于把 N 次redis操作, 变成 本地缓存+ 1 次 changedkeylist获取+M次redis操作)</li>
</ol>
<h2 id="11-配置建议-同时支持standalone和sentinel配置">11. 配置建议: 同时支持standalone和sentinel配置</h2>
<p>让运维根据实际应用场景, 自行切换使用.</p>
<p>成本不高的话, 也可以支持下<code>redis-cluster</code>配置</p>
<p>注意, 开启<code>pool</code>以获取更好的性能</p>
<p>另外, 也需要关注下如何开启prometheus/otel相关的配置, 以便某些情况下, 监测相关的指标</p>
<h2 id="12-设置开关-支持withcachewithoutcache调试">12. 设置开关, 支持withCache/withoutCache调试</h2>
<p>引入缓存后, 在进行问题调试的时候非常不变.</p>
<p>建议加入相关的调试标志, 例如<code>?force=true</code></p>
<ul>
<li>加上, 全链路数据获取走数据库</li>
<li>没加, 全链路走缓存</li>
</ul>
<p>此时, 可以通过对比两次请求的差异, 确定是否是缓存问题</p>
<p>甚至, 可以加入<code>?debug=true</code>以获取各个环节的上下文信息, 快速调试</p>
]]></content>
		</item>
		
		<item>
			<title>Better Code: 抽象: 可扩展性与可维护性的抉择</title>
			<link>https://wklken.me/posts/2022/06/21/better-code-4-abstraction-scalability-maintainability.html</link>
			<pubDate>Tue, 21 Jun 2022 23:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2022/06/21/better-code-4-abstraction-scalability-maintainability.html</guid>
			<description>抽象程度 抽象程度高, 往往意味着: 灵活, 可扩展性高 但是, 这也同时意味着: 复杂, 可维护性低 而可维护性在一个项目的生命周期中又是非常重要的, 一次开</description>
			<content type="html"><![CDATA[<h2 id="抽象程度">抽象程度</h2>
<p>抽象程度高, 往往意味着: 灵活, 可扩展性高
但是, 这也同时意味着: 复杂, 可维护性低</p>
<p>而可维护性在一个项目的生命周期中又是非常重要的, 一次开发编写的代码, 可能有几十倍的阅读/变更/debug等等</p>
<p>所以, 需要取得平衡, 但是这个度应该如何平衡?</p>
<h2 id="至下而上-够用保留一定可扩展">至下而上: 够用+保留一定可扩展</h2>
<p>务实的做法:</p>
<ol>
<li>实现功能, 简单/粗暴/丑陋, but it works!</li>
<li>明确<code>分层</code>/<code>已知的概念</code>, 然后进行抽象, 第一次重构; (分层/模块切分/类切分/方法挪动)</li>
<li>明确什么是<code>当前的需求</code>, 以及<code>可见范围内的需求</code>(要长远, 但是不能过远, 正常, 2-3年的量, 支持当前十倍作用的上层业务考虑)</li>
<li>明确<code>变更点</code>, 开闭原则, 变更点意味着未来的可扩展性; 抽出概念, 适当使用相关的设计模式, 进行第二次重构; 此时, 可能会多几个概念, 多一些模式.(但是不会多很多)</li>
<li>重新组织代码, 测试功能, 加单测等, 交付</li>
<li>每一次新需求或需求变更, 再来重新审视设计, 概念以及层的合并/拆分, 然后重构代码, 加新需求逻辑</li>
</ol>
<p>特征:
代码中, 具体需求逻辑代码占比很大, 只包含少量必要的抽象/分层等</p>
<h2 id="至上而下-可能会过度设计">至上而下: 可能会过度设计</h2>
<blockquote>
<p>没有必要为不变的点抽出太多的概念/太多层, 没有必要为了未来几年都不会扩展的点增加某个设计模式;</p>
</blockquote>
<p>注意: 是<code>可能</code></p>
<p>容易过度设计的做法:</p>
<ol>
<li>想要一步到位, 或者完美主义, 或者看得很远, 所以基于需求直接拆分出一堆层次和概念</li>
<li>基于概念, 通过组合/继承或者设计模式, 完成一堆父模块/基类/调用框架(此时没有具体代码)</li>
<li>实现一两个子类, 完成需求, 测试功能, 加单测, 交付</li>
</ol>
<p>特征:
代码中, 抽象的代码占比很大, 大量的概念/抽象等, 只在几个具体实现类中包含实现代码</p>
<h2 id="问题-概念抽象真的有那么重要吗">问题: 概念抽象真的有那么重要吗?</h2>
<p>看场景</p>
<ol>
<li>大型项目, 很重要</li>
<li>需求多变/变更频繁, 很重要</li>
<li>生命周期很长的项目, 很重要</li>
<li>其他, 不是那么重要</li>
</ol>
<p>如果是一次性的任务, 那么quick and dirty搞完即可, 很少或根本没有抽象</p>
<p>如果是一个小型项目, 或者项目本身需求明确变更不频繁, 或者需求实现之后不怎么变半年一年一种实现.
那么, <strong>概念抽象</strong>以及<strong>可扩展性</strong>并没有想象中那么<strong>重要</strong>, 而应该把<strong>可读性</strong>/<strong>可维护性</strong>放在前面, 保持适当的<strong>可扩展性</strong>, 某些极端场景, 甚至不考虑<strong>可扩展性</strong></p>
<p>如果是中大型项目, 生命周期长, 需求复杂且多, 那么此时概念抽象就很重要了</p>
<h2 id="怎么抉择">怎么抉择?</h2>
<p>看到这里, 并不是说, 一定要<code>至下而上</code>, 或者一定不能<code>至上而下</code></p>
<p>而是需要根据很多因素决策: 看项目, 看需求, 看场景&hellip;..</p>
<ol>
<li>项目大小</li>
<li>项目的生命周期</li>
<li>具体需求的复杂度</li>
<li>需求变更是否频繁? 新需求加入是否频繁? 是否有可见的需求?</li>
<li>&hellip;&hellip;</li>
</ol>
<h2 id="例子-一个需求及实现">例子: 一个需求及实现</h2>
<p>需求: 从两个不同的服务, 同步数据到同一个表; 这两个服务的协议不一样, 获取的数据字段映射到数据库表字段也不一样, 并且不同字段有不同的处理规则</p>
<p>几种实现:</p>
<ol>
<li>写两个脚本或者celery任务, 执行同步
<ul>
<li>加字段/字段变更需要两边都改</li>
</ul>
</li>
<li>写一个dataclass+两个client, client负责转换为统一格式
<ul>
<li>加字段/变更字段, 改两个client即可</li>
</ul>
</li>
<li>写一个dataclass+两个client+两个mapper, mapper 负责数据转换
<ul>
<li>加字段/变更字段, 改两个mapper即可, 甚至mapper变更纯配置</li>
</ul>
</li>
<li>抽象概念: syncer/fetcher/client/mapper/hook/helper/loader</li>
</ol>
<ul>
<li>一次性的任务, 选 1(quick and dirty), 如果时间空余, 做到 2</li>
<li>偶尔有字段变更, 选 2</li>
<li>频繁字段变更, 选 3, 最好变成配置</li>
<li>大型项目, N 个服务, N 种协议, N 种映射转换, 且不断有新逻辑, 初期还是最多做到 3, 逐步抽象, 最终可能演变成 4; 但是, 并不是一开始就那么多概念(至上而下), 而是不断迭代中演化出来的</li>
</ul>
<p>所以, 以<code>务实</code>的方式来设计方案, 逐步随着项目迭代演化出最终的形态.</p>
<h2 id="务实">务实</h2>
<p>从零开始学架构, 提到了三个原则: 合适/简单/演化</p>
<p>以务实的角度去思考</p>
<ul>
<li>合适, 但也不能太简陋/落后</li>
<li>简单, 但是要能满足现在及未来可见范围内的需求</li>
<li>演化, 但是底子要好, 避免无法演化只能重写</li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>Better Code: 异常时, 该提示用户哪些信息?</title>
			<link>https://wklken.me/posts/2022/05/24/better-code-3-better-error-message.html</link>
			<pubDate>Tue, 24 May 2022 23:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2022/05/24/better-code-3-better-error-message.html</guid>
			<description>接前一篇 Better Code: 更好的异常日志打印 做 toB 一个非常高的成本是, 用户的环境/网络/数据等, 可能跟你预期的差异很大, 再加上沟通相对困难(涉及三方/四方,</description>
			<content type="html"><![CDATA[<p>接前一篇 <a href="https://wklken.me/posts/2022/01/16/better-code-2-logging.html">Better Code: 更好的异常日志打印</a></p>
<p>做 toB 一个非常高的成本是, 用户的环境/网络/数据等, 可能跟你预期的差异很大, 再加上沟通相对困难(涉及三方/四方, 无法便捷地登机器排查问题, 无法便捷地获取日志, 接口人业务或技术水平参差不齐).</p>
<p>而这时候, 你会面临一个问题, 如何在<code>信息不足</code>/<code>沟通不畅</code>的场景下, 尽量提升效率, 降低成本(<strong>大部分情况下, 逢单必结, 你必须解决问题, 没有选择的余地</strong>)</p>
<p>TLDR:</p>
<ol>
<li>如果有工具, 尽量用工具解决问题, sentry/otel等等</li>
<li>日志, 该打的地方, 一定要打, 并且尽量包含上下文; 并且能通过日志级别配置动态调整打印明细.</li>
<li>页面报错提示, 尽量详尽, 用户不一定能看懂, 但是配合文档或者截图给你, 你能<code>秒懂</code>(如果不方便展示给用户, 可以通过<code>request_id</code>或者隐藏在页面等方式)</li>
<li>尽量减少排查链路(例如, 尽量汇总日志, 尽量在一个地方能看到, 或者通过一个<code>request_id</code>能汇总相关日志等等)</li>
</ol>
<h2 id="场景-1-代码逻辑报错">场景 1: 代码逻辑报错</h2>
<blockquote>
<p>有个代码初始化数据到数据库的逻辑报错了, 不管是什么原因引起的, 你需要解决!</p>
</blockquote>
<h3 id="实现v1">实现v1</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">Setting</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">create</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">key</span><span class="o">=</span><span class="n">k</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="n">v</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="n">x</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></div><p>此时, 新装环境没问题, 升级环境会报错,</p>
<p>通过异常信息知道创建由于<code>唯一性限制</code>报错了, 是由于用户手动插入了数据导致报错</p>
<p>此时, 用户问你, 数据有几百条, 哪条数据引起的? 我该怎么修复(成本: 小时级)</p>
<h3 id="实现v2">实现v2</h3>
<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">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">Setting</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">create</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">key</span><span class="o">=</span><span class="n">k</span><span class="p">,</span> <span class="n">value</span><span class="o">=</span><span class="n">v</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">x</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="o">.</span><span class="n">exception</span><span class="p">(</span><span class="s2">&#34;create setting fail, k=</span><span class="si">%s</span><span class="s2">, v=</span><span class="si">%s</span><span class="s2">, default=</span><span class="si">%s</span><span class="s2">&#34;</span><span class="p">,</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">,</span> <span class="n">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="n">e</span>
</span></span></code></pre></div><p>此时, 通过异常信息上下文可以看到哪条数据有问题!(成本: 分钟级)</p>
<h3 id="实现v3">实现v3</h3>
<p>代码考虑<code>兼容性</code>, 已发的版本/存量的系统, <code>最好</code>能顺滑升级!</p>
<p>例如, 这里的<code>create</code>, 可以考虑用<code>get_or_create</code>替代(当然, 前提是符合需求)</p>
<h2 id="场景-2-用户输入报错了">场景 2: 用户输入报错了</h2>
<blockquote>
<p>有个功能是支持用户配置ldap相关地址, 拉取用户数据, 用户点击: <code>测试连接</code>.</p>
</blockquote>
<h3 id="实现v1-1">实现v1</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">try</span><span class="p">:</span> 
</span></span><span class="line"><span class="cl">   <span class="n">test_connection</span><span class="p">(</span><span class="o">**</span><span class="n">params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">   <span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s2">&#34;测试连接失败, 请检查配置&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>此时, 用户坚信自己的配置是对的, 你如何帮他定位呢? (成本: 几个小时甚至一两天)</p>
<h3 id="实现v2-1">实现v2</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">try</span><span class="p">:</span> 
</span></span><span class="line"><span class="cl">   <span class="n">test_connection</span><span class="p">(</span><span class="o">**</span><span class="n">params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">   <span class="c1"># 注意, 这里一定要logger.exception, 用error意义不大!</span>
</span></span><span class="line"><span class="cl">   <span class="n">logger</span><span class="o">.</span><span class="n">exception</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">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s2">&#34;测试连接失败, 请检查配置&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>这时候的调试方式, 用户找你, 你告诉用户去哪里看日志!(成本: 分钟到几个小时)</p>
<h3 id="实现v3-1">实现v3</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">try</span><span class="p">:</span> 
</span></span><span class="line"><span class="cl">   <span class="n">test_connection</span><span class="p">(</span><span class="o">**</span><span class="n">params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">   <span class="c1"># 注意, 这里一定要logger.exception, 用error意义不大!</span>
</span></span><span class="line"><span class="cl">   <span class="n">logger</span><span class="o">.</span><span class="n">exception</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="n">error_detail</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34; (</span><span class="si">{</span><span class="nb">type</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="o">.</span><span class="vm">__module__</span><span class="si">}</span><span class="s2">.</span><span class="si">{</span><span class="nb">type</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="si">}</span><span class="s2">)&#34;</span>
</span></span><span class="line"><span class="cl">   <span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s2">&#34;测试连接失败, 请检查配置&#34;</span> <span class="o">+</span> <span class="n">error_detail</span><span class="p">)</span>
</span></span></code></pre></div><p>这时候, 在页面上, 用户就能看到 <code>报错的类型及错误信息</code></p>
<ul>
<li>报错类型: 能定位到是第三方库还是自己程序哪个模块报的, 如果出现标准库<code>KeyError/ValueError</code>等, 大概率是自己哪里代码逻辑处理不正确.</li>
<li>报错信息: 例如 <code>invalid server address</code>, <code>socket connection error while opening: [Errno 111] Connection refused</code>之类的;</li>
</ul>
<p>此时, 用户绝大多数可以在前端提示中看到, 无需支持!</p>
<h3 id="实现v4">实现v4</h3>
<p>有必要的话</p>
<ul>
<li>如果你能准确根据 Exception 或message区分详情, 可以进一步处理, 返回更精确的提示. 例如<code>invalid server address</code>, 可以翻译成<code>不合法的地址, 请检查xxxx字段, 必须是一个合法的域名</code>, 建议同时带上一个<code>error code</code>方便快速确定问题/文档中检索关键字</li>
<li>如果有必要, 可以在错误中带上<code>上下文</code>, 但是需要注意<code>脱敏</code></li>
</ul>
<h2 id="场景-3-用户输入-报错了-但是不能直接提示给对方">场景 3: 用户输入, 报错了, 但是不能直接提示给对方</h2>
<blockquote>
<p>用户登录, 输入的用户名/密码有问题或者账号本身有问题时;由于安全原因, 不能明确告诉对方具体是哪里有问题. 在用户看来, 无论怎么尝试, 报错总是一样的.</p>
</blockquote>
<p>那么, 如果用户<code>坚持</code>自己输入是对的, 账号是正常的, 如何帮助其排查问题?</p>
<h3 id="实现v1-2">实现v1</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">user</span> <span class="ow">not</span> <span class="n">exists</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="n">PasswordWrongException</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">status</span> <span class="ow">in</span> <span class="p">[</span><span class="n">DISABLED</span><span class="p">,</span> <span class="n">LOCKED</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="n">PasswordWrongException</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">user</span> <span class="n">login</span> <span class="n">hit</span> <span class="n">secure</span> <span class="n">policies</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="n">PasswordWrongException</span><span class="p">()</span>
</span></span></code></pre></div><p>这种实现, 你只能看代码, 逐个同用户沟通检查(成本: 小时/天)</p>
<h3 id="实现v2-2">实现v2</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">user</span> <span class="ow">not</span> <span class="n">exists</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="s2">&#34;login fail, user&lt;</span><span class="si">%s</span><span class="s2">&gt; not exists&#34;</span><span class="p">,</span> <span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="n">PasswordWrongException</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">status</span> <span class="ow">in</span> <span class="p">[</span><span class="n">DISABLED</span><span class="p">,</span> <span class="n">LOCKED</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="s2">&#34;login fail, user&lt;</span><span class="si">%s</span><span class="s2">&gt; status is </span><span class="si">%s</span><span class="s2">&#34;</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">user</span><span class="o">.</span><span class="n">status</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="n">PasswordWrongException</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">user</span> <span class="n">login</span> <span class="n">hit</span> <span class="n">secure</span> <span class="n">policies</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 这里有必要的话, 甚至需要明确到具体哪个policy</span>
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="s2">&#34;login fail, user&lt;</span><span class="si">%s</span><span class="s2">&gt; hit xxx secure policies&#34;</span><span class="p">,</span> <span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="n">PasswordWrongException</span><span class="p">()</span>
</span></span></code></pre></div><p>这种实现, 让用户捞下日志就能明确具体问题(成本: 分钟)</p>
<h2 id="场景-4强依赖于一个第三方库-如何-debug">场景 4:强依赖于一个第三方库, 如何 debug?</h2>
<p>如果应用本身依赖于一个第三方库, 且第三方库默认是有logger的.</p>
<p>此时, 调用第三方库报错, 可能返回的异常信息不足以帮助用户定位问题;</p>
<p>例如依赖<code>ldap3</code>同步用户数据</p>
<p>此时数据链路: <code>应用 -&gt; ldap3 -&gt; 网络 -&gt; ldap服务器</code></p>
<ol>
<li>由于<code>网络</code>/<code>ldap服务器</code>类型及配置差异较大, 导致非常容易出现<code>异常</code></li>
<li><code>ldap服务器</code>往往由另一个团队维护, 具体的服务端配置对于配置用户并不透明</li>
<li><code>ldap3</code>库本身不同版本也存在bug</li>
</ol>
<p>此时, 用户配置了ldap相关参数, 但是点击报错, 并且, 用户坚信自己的配置是对的; 如何帮助用户调试?</p>
<p>我们是否可以支持, 开启<code>ldap3</code>的debug模式?</p>
<ol>
<li>首先, 需要确认库是否支持logger配置, 常用的第三方库一般都支持, 例如<code>requests</code>, <code>ldap3</code>是支持的, <a href="https://ldap3.readthedocs.io/en/latest/logging.html">ldap3 logging</a></li>
<li>其次, 通过配置/环境变量, 支持用户可以开启</li>
</ol>
<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">ldap3.utils</span> <span class="kn">import</span> <span class="n">log</span> <span class="k">as</span> <span class="n">ldap3log</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">settings</span><span class="o">.</span><span class="n">ENABLE_LDAP3_DEBUG</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">ldap3log</span><span class="o">.</span><span class="n">set_library_log_detail_level</span><span class="p">(</span><span class="n">ldap3log</span><span class="o">.</span><span class="n">EXTENDED</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">ldap3log</span><span class="o">.</span><span class="n">set_library_log_activation_level</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">)</span>
</span></span></code></pre></div><p>那么, 在用户遇到问题无法解决的时候, 可以通过配置开启, 进入debug模式; 在日志中可以看到详细的 <code>ldap3 -&gt; 网络 -&gt; ldap服务器</code> 的数据细节.</p>
<h2 id="其他">其他</h2>
<h3 id="tip-如果有可能-接入sentryotel">tip: 如果有可能, 接入sentry/otel</h3>
<blockquote>
<p>如果基建有otel, 建议接入; 如果基建有日志采集汇聚, 建议接入!</p>
</blockquote>
<p>能极大提升问题排查效率</p>
<p>所以, 尽量整合相应的SDK, 并且提供可插拔的配置方式, 让有条件的使用方开启.</p>
<h3 id="tip-日志链路需要串起来">tip: 日志链路需要串起来</h3>
<p>如果:</p>
<ol>
<li>调用链路长, <code>用户 -&gt; A -&gt; B -&gt; C -&gt; D</code></li>
<li>服务请求量非常大</li>
</ol>
<p>此时, 某个用户一个请求报错(前端或 API), 如何定位问题?</p>
<p>首先明确一个原则, <code>A -&gt; B</code> 如果出现调用失败, 一定要记录日志, 包括<code>url/request detail(header/body/credentials)/response detail(status/header/body)</code>等等(如果什么都不记, 让用户或运维去 <code>B</code> 看日志, 这是不合理的!)</p>
<p>此时, 如果请求量很大, 调用链路长, 其实很难逐层定位每一层的处理/报错, 光去捞日志就已经非常费劲了(大部分时候, url或请求中并没有明显的特征)</p>
<p>如果整个调用链路接入了 OTEL, 那么问题相对简单; 但是如果没有接入, 如何处理?</p>
<p>我们可以通过 HTTP HEADER中带一个<code>request_id</code>来解决!</p>
<ol>
<li>约定一个统一的header, 例如<code>x-request-id</code></li>
<li>在 <code>网关</code>/<code>ESB</code>/<code>nginx</code>等地方, 需要显示配置<code>透传</code></li>
<li>程序处理逻辑, 需要在接收请求时, 获取<code>request_id</code>, 如果有向下一级调用的请求, 需要传递该<code>request_id</code></li>
<li>日志,异常上报等场景, 使用统一的<code>request_id</code></li>
</ol>
<h3 id="tip-当开启debug的时候-确保能看到足够多的信息">tip: 当开启debug的时候, 确保能看到足够多的信息</h3>
<p><code>logging.DEBUG</code>主要目的是, 开启后, 可以准确定位问题!</p>
<p>所以, 在一些关键的逻辑中</p>
<ol>
<li>加上<code>debug</code>日志, 覆盖所有关键的处理逻辑和返回</li>
<li><code>debug</code>需要带上足够多的<code>上下文</code></li>
</ol>
<p>例如, 当关键一个函数, 中间有超过 5 个<code>return</code>, 开启<code>debug</code>之后, 需要能明确知道, 在哪里<code>return</code>的, 为什么会被<code>return</code></p>
<h3 id="tip-输入尽量做好防御">tip: 输入尽量做好<code>防御</code></h3>
<p>不管是用户的输入, 还是从某些 API 拉取到的返回值, 尽量在获取到数据的<code>入口</code>处做好防御和检查, 例如, <code>类型/格式/非空</code>等等</p>
<p>而不是, 层层传递后, 在某个地方异常报错, 这个时候排查的代价会大很多.</p>
<h3 id="tip-当问题排查陷入僵局">tip: 当问题排查陷入僵局</h3>
<h4 id="1-如果是第三方库-可以考虑升级试试">1. 如果是第三方库, 可以考虑升级试试?</h4>
<p>场景: 依赖于ldap3, 能确认用户的配置是正确的, 网络是联通的, 此时无论如何都连不上, 开debug模式也没看出问题</p>
<p>解决: 确定库版本, 2.6.1, 在官方github issue的bug report中查找相关报错信息, 然后升级了一个版本就成功了</p>
<p>常见的, 例如requests/加密相关库/ssl相关库等等</p>
<h4 id="2-重启-本机重新部署或在另一台机器部署-也是可以尝试的方式">2. 重启, 本机重新部署或在另一台机器部署, 也是可以尝试的方式</h4>
<p>场景: 在一台机器部署, 连接另外一个服务始终连不上, 运维确定网络是没问题的; 后来在另一台机器部署后正常.</p>
<p>场景: 有一台机器<code>reuqests</code>库一直会报错, 换一台机器部署正常.(openssl相关)</p>
<h4 id="3-重新检查配置负载io网络依赖库依赖包版本等-重新排查调用链路-追踪请求流转过程-重新获取日志">3. 重新检查配置/负载/io/网络/依赖库/依赖包版本等, 重新排查调用链路, 追踪请求流转过程, 重新获取日志</h4>
<p>场景: 对方上了网络策略, 存量机器没有处理导致原先正常的环境突然有问题了</p>
<p>很多时候, 你被拉去排查问题, 并不是因为你负责的服务有问题, 而是更底层的环境问题, 只是因为环境出问题你的服务刚好打了异常, 对方看到了而已.</p>
]]></content>
		</item>
		
		<item>
			<title>Better Code: 更好的异常日志打印</title>
			<link>https://wklken.me/posts/2022/01/16/better-code-2-logging.html</link>
			<pubDate>Sun, 16 Jan 2022 00:04:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2022/01/16/better-code-2-logging.html</guid>
			<description>维护一个 N 年前的Python项目, 协助排查问题时发现, 原先的日志打印真真一言难尽 导致了一个问题, 同负责的运维 A 沟通, A 与其对接的甲方沟通&amp;h</description>
			<content type="html"><![CDATA[<p>维护一个 N 年前的Python项目, 协助排查问题时发现, 原先的日志打印真真一言难尽</p>
<p>导致了一个问题, 同负责的运维 A 沟通, A 与其对接的甲方沟通&hellip;&hellip;日志的缺失导致本来简单的问题变得复杂</p>
<p>如果日志打得更完备一点, 排查问题的难度不是一个的级别的</p>
<h2 id="bad-case">bad case</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># got a data here</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># a lot of codes here</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># x = data[&#34;key&#34;]</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># call_another_func(id, type)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># call_another_func2(id, type, name)</span>
</span></span><span class="line"><span class="cl"><span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="s2">&#34;error while do something&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>这时候, 异常触发的时候, 日志只有</p>
<pre tabindex="0"><code>error while do something
</code></pre><p>相对于没打日志,只好了一点点, 知道有异常出现了, 问题是, 具体错误是什么?</p>
<h2 id="第一次改进">第一次改进</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># got a data here</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># a lot of codes here</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># x = data[&#34;key&#34;]</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># call_another_func(id, type)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># call_another_func2(id, type, name)</span>
</span></span><span class="line"><span class="cl"><span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">error</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="o">.</span><span class="n">error</span><span class="p">(</span><span class="s2">&#34;error while do something, error=</span><span class="si">%s</span><span class="s2">&#34;</span><span class="p">,</span> <span class="n">error</span><span class="p">)</span>
</span></span></code></pre></div><p>此时报错时, 会打印</p>
<pre tabindex="0"><code>error while do something, error=App matching query does not exist
</code></pre><p>但是, 这里相对原始版本只好了一点点, 多了error message, 但是这行message并没有包含多少<code>有利于问题排查</code>的信息, 甚至连类型都没有, 看到了一脸懵</p>
<h2 id="再次改进1">再次改进1</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># got a data here</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># a lot of codes here</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># x = data[&#34;key&#34;]</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># call_another_func(id, type)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># call_another_func2(id, type, name)</span>
</span></span><span class="line"><span class="cl"><span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">error</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="o">.</span><span class="n">exception</span><span class="p">(</span><span class="s2">&#34;error while do something&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p>将<code>logger.error</code>改成<code>logger.exception</code>, 此时错误日志</p>
<pre tabindex="0"><code>error while do something
Traceback (most recent call last) :
  File &#34;a.py&#34;, line 674 in call_another_func
    app = get_app(id)
  File &#34;b.py&#34;, line 18 in get_app
    app = App.objects.get(id)
DoesNotExist: App matching query does not exist.
</code></pre><p>这时候, 有详细的错误堆栈, 可以用于追溯错误原因.</p>
<p>但是, 某些场景下, 看到错误堆栈并不一定能确定问题, 根据堆栈信息回去看代码, <code>这代码不可能有问题</code>&hellip;&hellip;</p>
<p>此时, 可能是数据问题(各种原因的脏数据), 或者数据被人工改动(例如直接改表数据).</p>
<p>如果只有以上信息, 排查问题依旧要花很多时间.</p>
<h2 id="再次改进2">再次改进2</h2>
<p>增加<code>数据</code>/<code>业务</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">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># got a data here</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># a lot of codes here</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># x = data[&#34;key&#34;]</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># call_another_func(id, type)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># call_another_func2(id, type, name)</span>
</span></span><span class="line"><span class="cl"><span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">error</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">logger</span><span class="o">.</span><span class="n">exception</span><span class="p">(</span><span class="s2">&#34;error while do something [id=</span><span class="si">%s</span><span class="s2">, type=</span><span class="si">%s</span><span class="s2">, name=</span><span class="si">%s</span><span class="s2">]&#34;</span><span class="p">,</span> <span class="nb">id</span><span class="p">,</span> <span class="nb">type</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
</span></span></code></pre></div><p>错误信息</p>
<pre tabindex="0"><code>error while do something[id=1, type=test, name=abc]
Traceback (most recent call last) :
  File &#34;a.py&#34;, line 674 in call_another_func
    app = get_app(id)
  File &#34;b.py&#34;, line 18 in get_app
    app = App.objects.get(id)
DoesNotExist: App matching query does not exist.
</code></pre><h2 id="更好的做法">更好的做法</h2>
<p>使用自定义异常, 带<code>code/message</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">BaseException</span><span class="p">(</span><span class="ne">Exception</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">self</span><span class="p">,</span> <span class="n">code</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">code</span> <span class="o">=</span> <span class="n">code</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">message</span> <span class="o">=</span> <span class="n">message</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="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">code</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">message</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="sa">f</span><span class="s2">&#34;&lt;ErrorCode </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">code</span><span class="si">}</span><span class="s2">:(</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">message</span><span class="si">}</span><span class="s2">)&gt;&#34;</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">XXException</span><span class="p">(</span><span class="ne">BaseException</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="o">.....</span>
</span></span></code></pre></div><ul>
<li>使用<code>raise Error/CustomException</code>替代<code>try-except</code></li>
<li>在统一的地方处理错误, 例如<code>middleware</code>或统一调用入口, <code>try-except</code>依次捕获所有已知的<code>Error</code>和<code>CustomException</code>, 最后捕获通用的<code>Exception</code>(未知异常)</li>
</ul>
<p>好处:</p>
<ol>
<li>底层代码逻辑更简单, 少一层嵌套, 可读性更好</li>
<li>统一的错误声明及处理, 对于开发者及使用者都更为友好, 例如可以将错误码作为协议的一部分, 使用者根据错误码可以快速定位问题</li>
</ol>
<h2 id="golang的error-wrap">Golang的error wrap</h2>
<p>Go 的error wrap更为优雅, <a href="https://go.dev/blog/go1.13-errors">Working with Errors in Go 1.13</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">dosomething</span><span class="p">(</span><span class="kd">type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;dosomething fail, type=`%s`, err=%w&#34;</span><span class="p">,</span> <span class="kd">type</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span></code></pre></div><p>也可以看下之前 go 项目的实践: <a href="https://wklken.me/posts/2021/01/26/golang-error-wrap.html">Go: 一种error wrap调用链处理方式</a></p>
<h2 id="其他">其他</h2>
<ul>
<li>不要用<code>print</code>替代logging</li>
<li>打很多日志等于没打</li>
<li>谨慎确定当前要打日志的<code>level</code></li>
<li>尽量只在必要的地方打日志, 信息是<code>有用的</code>, 也是<code>完备的</code>, 有利于<code>定位问题的</code></li>
<li>一定不要<code>吞</code>掉异常</li>
<li>如果使用<code>try-except</code>, Exception尽量精确, 打印日志尽量完备</li>
<li>如果使用<code>try-except</code>, 捕获的代码块尽量小, 不会出现异常的语句不要放进去. <strong>反例</strong>: 整个函数的所有代码都在一个<code>try-except</code>中</li>
<li><code>logger</code> 本身尽量按功能/模块/业务等切分多个, <strong>反例</strong>: 全打标准输出</li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>Go: some libs</title>
			<link>https://wklken.me/posts/2021/08/04/golang-some-libs.html</link>
			<pubDate>Wed, 04 Aug 2021 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2021/08/04/golang-some-libs.html</guid>
			<description>I have finished 3 web projects in the past 16 months, about 100000 lines.
The libs below are my recommended.
Last Updated: 2021-08-04
framework recommend gin chi layout golang-standards/project-layout
database sqlx gorm cache memory: go-cache redis: go-redis/redis cache go-redis/cache distribute lock redislock ratelimit: go-redis/redis_rate http client gorequest test assert testify BDD onsi/ginkgo steinfletcher/apitest mock DATA-DOG/go-sqlmock gomonkey miniredis logger recommend uber-go/zap sirupsen/logrus lumberjack doc swaggo/swag gin swaggo/gin-swaggo error uber-go/multierr config &amp;amp; cli viper cobra json json-iterator/go observability sentry-go client_golang validate go-playground/validator worker pool panjf2000/ants others dgrijalva/jwt-go elasticsearch msgpack </description>
			<content type="html"><![CDATA[<p>I have finished 3 web projects in the past 16 months, about 100000 lines.</p>
<p>The libs below are my recommended.</p>
<p>Last Updated: 2021-08-04</p>
<h2 id="framework">framework</h2>
<ul>
<li>recommend <a href="github.com/gin-gonic/gin">gin</a></li>
<li><a href="https://github.com/go-chi/chi">chi</a></li>
</ul>
<h2 id="layout">layout</h2>
<p><a href="https://github.com/golang-standards/project-layout">golang-standards/project-layout</a></p>
<h2 id="database">database</h2>
<ul>
<li><a href="github.com/jmoiron/sqlx">sqlx</a></li>
<li><a href="https://github.com/go-gorm/gorm">gorm</a></li>
</ul>
<h2 id="cache">cache</h2>
<ul>
<li>memory: <a href="https://github.com/patrickmn/go-cache">go-cache</a></li>
<li>redis: <a href="https://github.com/go-redis/redis">go-redis/redis</a>
<ul>
<li>cache <a href="https://github.com/go-redis/cache">go-redis/cache</a></li>
<li>distribute lock <a href="https://github.com/bsm/redislock">redislock</a></li>
<li>ratelimit: <a href="https://github.com/go-redis/redis_rate">go-redis/redis_rate</a></li>
</ul>
</li>
</ul>
<h2 id="http-client">http client</h2>
<ul>
<li><a href="github.com/parnurzeal/gorequest">gorequest</a></li>
</ul>
<h2 id="test">test</h2>
<ul>
<li>assert <a href="https://github.com/stretchr/testify">testify</a></li>
<li>BDD <a href="https://github.com/onsi/ginkgo">onsi/ginkgo</a></li>
<li><a href="https://github.com/steinfletcher/apitest">steinfletcher/apitest</a></li>
<li>mock
<ul>
<li><a href="https://github.com/DATA-DOG/go-sqlmock">DATA-DOG/go-sqlmock</a></li>
<li><a href="https://github.com/agiledragon/gomonkey">gomonkey</a></li>
<li><a href="github.com/alicebob/miniredis">miniredis</a></li>
</ul>
</li>
</ul>
<h2 id="logger">logger</h2>
<ul>
<li>recommend <a href="https://github.com/uber-go/zap">uber-go/zap</a></li>
<li><a href="https://github.com/sirupsen/logrus">sirupsen/logrus</a></li>
<li><a href="https://github.com/natefinch/lumberjack">lumberjack</a></li>
</ul>
<h2 id="doc">doc</h2>
<ul>
<li><a href="https://github.com/swaggo/swag">swaggo/swag</a></li>
<li>gin <a href="https://github.com/swaggo/gin-swagger">swaggo/gin-swaggo</a></li>
</ul>
<h2 id="error">error</h2>
<ul>
<li><a href="https://github.com/uber-go/multierr">uber-go/multierr</a></li>
</ul>
<h2 id="config--cli">config &amp; cli</h2>
<ul>
<li><a href="https://github.com/spf13/viper">viper</a></li>
<li><a href="https://github.com/spf13/cobra">cobra</a></li>
</ul>
<h2 id="json">json</h2>
<ul>
<li><a href="https://github.com/json-iterator/go">json-iterator/go</a></li>
</ul>
<h2 id="observability">observability</h2>
<ul>
<li><a href="github.com/getsentry/sentry-go">sentry-go</a></li>
<li><a href="github.com/prometheus/client_golang">client_golang</a></li>
</ul>
<h2 id="validate">validate</h2>
<ul>
<li><a href="https://github.com/go-playground/validator">go-playground/validator</a></li>
</ul>
<h2 id="worker-pool">worker pool</h2>
<ul>
<li><a href="https://github.com/panjf2000/ants">panjf2000/ants</a></li>
</ul>
<h2 id="others">others</h2>
<ul>
<li><a href="github.com/dgrijalva/jwt-go">dgrijalva/jwt-go</a></li>
<li><a href="github.com/elastic/go-elasticsearch">elasticsearch</a></li>
<li><a href="https://github.com/vmihailenco/msgpack">msgpack</a></li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>Go: go-redis/cache升级的坑</title>
			<link>https://wklken.me/posts/2021/03/05/golang-upgrade-go-redis-cache.html</link>
			<pubDate>Fri, 05 Mar 2021 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2021/03/05/golang-upgrade-go-redis-cache.html</guid>
			<description>这是在做 Golang 项目中的一些实践 在做一个项目, 使用了 go-redis/redis 及 go-redis/cache 某个节点, 做了所有依赖包的升级. 在做缓存依赖包的升级时, 必须十分注意, 新版本的第三方包做</description>
			<content type="html"><![CDATA[<blockquote>
<p>这是在做 Golang 项目中的一些实践</p>
</blockquote>
<p>在做一个项目, 使用了 <a href="https://github.com/go-redis/redis">go-redis/redis</a> 及 <a href="https://github.com/go-redis/cache">go-redis/cache</a></p>
<p>某个节点, 做了所有依赖包的升级.</p>
<p>在做缓存依赖包的升级时, 必须十分注意, 新版本的第三方包做了哪些变更, 阅读CHANGE LOG和相关的源码;</p>
<p>主要风险:</p>
<ul>
<li>breaking changes</li>
<li>对象/方法/参数等协议变更</li>
<li>存储格式(是否向前兼容)</li>
</ul>
<p>即, 一旦做了升级, 原先<code>正常</code>的代码, 可能会有<code>问题</code></p>
<p>所以需要确认:</p>
<ol>
<li>新老版本存储格式是否一致?</li>
<li>代码中涉及<code>读</code>和<code>写</code>的地方是否使用同样的操作?</li>
</ol>
<h2 id="1-v7升级v8">1. v7升级v8</h2>
<p>issue: <a href="https://github.com/go-redis/cache/issues/52">Can&rsquo;t upgrade from v7 to v8 directly?</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;context&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;fmt&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">cv7</span> <span class="s">&#34;github.com/go-redis/cache/v7&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="nx">cv8</span> <span class="s">&#34;github.com/go-redis/cache/v8&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="nx">rdsv7</span> <span class="s">&#34;github.com/go-redis/redis/v7&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="nx">rdsv8</span> <span class="s">&#34;github.com/go-redis/redis/v8&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="nx">msgpackv4</span> <span class="s">&#34;github.com/vmihailenco/msgpack/v4&#34;</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="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// TODO: 测试放入redis用另一个取
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">d</span> <span class="o">:=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}{</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;hello&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;world&#34;</span><span class="p">:</span> <span class="s">&#34;ok&#34;</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="c1">// 使用两个版本的cli进行测试, v7/v8
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// 1. 使用v7, marshal 放入db
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// 2. use v8, 获取
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">	<span class="nx">v7</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">cv7</span><span class="p">.</span><span class="nx">Codec</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">Redis</span><span class="p">:</span>     <span class="nx">rdsv7</span><span class="p">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">rdsv7</span><span class="p">.</span><span class="nx">Options</span><span class="p">{}),</span>
</span></span><span class="line"><span class="cl">		<span class="nx">Marshal</span><span class="p">:</span>   <span class="nx">msgpackv4</span><span class="p">.</span><span class="nx">Marshal</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="nx">Unmarshal</span><span class="p">:</span> <span class="nx">msgpackv4</span><span class="p">.</span><span class="nx">Unmarshal</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="nx">err</span> <span class="o">:=</span> <span class="nx">v7</span><span class="p">.</span><span class="nf">Set</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">cv7</span><span class="p">.</span><span class="nx">Item</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">Key</span><span class="p">:</span>    <span class="s">&#34;aaa&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="nx">Object</span><span class="p">:</span> <span class="nx">d</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="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;set err:&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">v8</span> <span class="o">:=</span> <span class="nx">cv8</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">cv8</span><span class="p">.</span><span class="nx">Options</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">Redis</span><span class="p">:</span> <span class="nx">rdsv8</span><span class="p">.</span><span class="nf">NewClient</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">rdsv8</span><span class="p">.</span><span class="nx">Options</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="kd">var</span> <span class="nx">result</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">	<span class="nx">err</span> <span class="p">=</span> <span class="nx">v8</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">TODO</span><span class="p">(),</span> <span class="s">&#34;aaa&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">result</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;get err:&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</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"><span class="nb">set</span> err: &lt;nil&gt;
</span></span><span class="line"><span class="cl">get err: unknown compression method: 6b
</span></span><span class="line"><span class="cl">map<span class="o">[]</span>
</span></span></code></pre></div><p>原因, go-redis/cache v8引入的压缩 <a href="https://github.com/go-redis/cache/blob/v8/cache.go#L353">v8/cache.go</a>, 没有做向前兼容的逻辑</p>
<p>解决方案: 在基础类中, 更换掉key的namespace, 增加版本号; 例如 <code>project:hello</code> 变成 <code>project:v8:hello</code></p>
<h2 id="2-升级v8后的msgpack-与-pipeline的处理">2. 升级v8后的msgpack 与 pipeline的处理</h2>
<p>由于 go-redis/cache 主要用于单个对象的 set/get</p>
<p>某些业务场景下, 我们使用pipeline进行批量操作</p>
<p>此时, pipeline拿回回来的是 <code>raw string</code>, 原先v7由于set算法用的<code>msgpack.Marshal</code>, 自定义pipeline处理结果直接用<code>msgpack.Unmarshal</code>没有问题.</p>
<p>但是, 升级到v8后, 由于compession逻辑的存在, 此时使用原生的<code>msgpack.Unmarshal</code>将会报错(无法识别被压缩的字符串)</p>
<p>解决: 需要切换为 <code>go-redis/cache</code> 对象的<code>Unmarshal</code></p>
]]></content>
		</item>
		
		<item>
			<title>Go: logrus性能提升</title>
			<link>https://wklken.me/posts/2021/02/09/golang-high-performance-logrus.html</link>
			<pubDate>Tue, 09 Feb 2021 20:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2021/02/09/golang-high-performance-logrus.html</guid>
			<description>在Go项目中, logrus是一个相对完备的第三方日志库 用起来非常顺手, 特别是WithField/WithFields/WithError 我们</description>
			<content type="html"><![CDATA[<p>在Go项目中, logrus是一个相对完备的第三方日志库</p>
<p>用起来非常顺手, 特别是<code>WithField/WithFields/WithError</code></p>
<p>我们开发一些对性能要求非常高的应用, 例如API网关/权限服务等, 需要记录流水日志, 此时日志库的性能直接会影响整体接口性能.</p>
<p>所以针对性地做了一些优化</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">log</span><span class="p">.</span><span class="nf">WithFields</span><span class="p">(</span><span class="nx">fields</span><span class="p">).</span><span class="nf">Info</span><span class="p">(</span><span class="s">&#34;-&#34;</span><span class="p">)</span>
</span></span></code></pre></div><p><img src="/imgs/golang/logrus1.png" alt=""></p>
<h2 id="json-formatter">json formatter</h2>
<p>logrus的默认json formatter <a href="https://github.com/sirupsen/logrus/blob/master/json_formatter.go">json_formatter</a> 使用的是标准库</p>
<p>众所周知, 性能比第三方库差很多</p>
<p>我们可以自定义一个json formatter, 使用例如 <a href="https://github.com/json-iterator/go">json-iterator/go</a>来提升序列化性能</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl">	<span class="c1">// ! change here: use jsnoiter to do marshal. but ignore the entry.Buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">	<span class="c1">// var b *bytes.Buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// if entry.Buffer != nil {
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// 	b = entry.Buffer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// } else {
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// 	b = &amp;bytes.Buffer{}
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">	<span class="c1">// encoder := json.NewEncoder(b)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// if f.PrettyPrint {
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// 	encoder.SetIndent(&#34;&#34;, &#34;  &#34;)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// if err := encoder.Encode(data); err != nil {
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// 	return nil, fmt.Errorf(&#34;failed to marshal fields to JSON, %v&#34;, err)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// }
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// return b.Bytes(), nil
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">	<span class="nx">buf</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">jsoniter</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;failed to marshal fields to JSON, %v&#34;</span><span class="p">,</span> <span class="nx">err</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="nx">buf</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">buf</span><span class="p">,</span> <span class="sc">&#39;\n&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">buf</span><span class="p">,</span> <span class="kc">nil</span>
</span></span></code></pre></div><h2 id="null-formatter-和-hooks">null formatter 和 hooks</h2>
<p>如果不指定, 默认的formatter是<code>TextFormatter</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">New</span><span class="p">()</span> <span class="o">*</span><span class="nx">Logger</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="o">&amp;</span><span class="nx">Logger</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">Out</span><span class="p">:</span>          <span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="nx">Formatter</span><span class="p">:</span>    <span class="nb">new</span><span class="p">(</span><span class="nx">TextFormatter</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">		<span class="nx">Hooks</span><span class="p">:</span>        <span class="nb">make</span><span class="p">(</span><span class="nx">LevelHooks</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">		<span class="nx">Level</span><span class="p">:</span>        <span class="nx">InfoLevel</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="nx">ExitFunc</span><span class="p">:</span>     <span class="nx">os</span><span class="p">.</span><span class="nx">Exit</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="nx">ReportCaller</span><span class="p">:</span> <span class="kc">false</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></code></pre></div><p>如果我们使用了 hooks (<a href="https://github.com/sirupsen/logrus/blob/master/hooks.go">hooks.go</a>)将日志转到文件/redis或其他渠道; 此时, 默认的Output和Formatter还是会被调用到;</p>
<p>其实我们配置了hooks, 那么原entry理论上是不需要的</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">entry</span> <span class="nx">Entry</span><span class="p">)</span> <span class="nf">log</span><span class="p">(</span><span class="nx">level</span> <span class="nx">Level</span><span class="p">,</span> <span class="nx">msg</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">entry</span><span class="p">.</span><span class="nf">fireHooks</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">    <span class="nx">entry</span><span class="p">.</span><span class="nf">write</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="kd">func</span> <span class="p">(</span><span class="nx">entry</span> <span class="o">*</span><span class="nx">Entry</span><span class="p">)</span> <span class="nf">write</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">entry</span><span class="p">.</span><span class="nx">Logger</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="k">defer</span> <span class="nx">entry</span><span class="p">.</span><span class="nx">Logger</span><span class="p">.</span><span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="nx">serialized</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">entry</span><span class="p">.</span><span class="nx">Logger</span><span class="p">.</span><span class="nx">Formatter</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="nx">entry</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintf</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">&#34;Failed to obtain reader, %v\n&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">entry</span><span class="p">.</span><span class="nx">Logger</span><span class="p">.</span><span class="nx">Out</span><span class="p">.</span><span class="nf">Write</span><span class="p">(</span><span class="nx">serialized</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintf</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">&#34;Failed to write to log, %v\n&#34;</span><span class="p">,</span> <span class="nx">err</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></code></pre></div><p>所以, 此时可以配置一个 <code>NullFormatter</code>, 将默认的链路关闭(do nothing), 日志只会走hooks</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">formatter</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;github.com/sirupsen/logrus&#34;</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">// NullFormatter formats logs into text
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">NullFormatter</span> <span class="kd">struct</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="c1">// Format renders a single log entry
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">f</span> <span class="o">*</span><span class="nx">NullFormatter</span><span class="p">)</span> <span class="nf">Format</span><span class="p">(</span><span class="nx">entry</span> <span class="o">*</span><span class="nx">logrus</span><span class="p">.</span><span class="nx">Entry</span><span class="p">)</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">),</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>重新设置log的配置</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">logger</span><span class="p">.</span><span class="nf">SetOutput</span><span class="p">(</span><span class="nx">ioutil</span><span class="p">.</span><span class="nx">Discard</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">logger</span><span class="p">.</span><span class="nf">SetFormatter</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">formatter</span><span class="p">.</span><span class="nx">NullFormatter</span><span class="p">{})</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">logger</span><span class="p">.</span><span class="nf">AddHook</span><span class="p">(</span><span class="nf">NewRedisHook</span><span class="p">())</span>
</span></span></code></pre></div><h2 id="log-entry-pool">log entry pool</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">logging</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;sync&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">log</span> <span class="s">&#34;github.com/sirupsen/logrus&#34;</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="kd">type</span> <span class="nx">logEntryPool</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">pool</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">Pool</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="kd">func</span> <span class="nf">newLogEntryPool</span><span class="p">()</span> <span class="o">*</span><span class="nx">logEntryPool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="o">&amp;</span><span class="nx">logEntryPool</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">pool</span><span class="p">:</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">Pool</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">New</span><span class="p">:</span> <span class="kd">func</span><span class="p">()</span> <span class="kd">interface</span><span class="p">{}</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">return</span> <span class="o">&amp;</span><span class="nx">log</span><span class="p">.</span><span class="nx">Entry</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">					<span class="c1">// Logger: logger,
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>					<span class="c1">// Default is three fields, plus one optional.  Give a little extra room.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>					<span class="nx">Data</span><span class="p">:</span> <span class="nb">make</span><span class="p">(</span><span class="nx">log</span><span class="p">.</span><span class="nx">Fields</span><span class="p">,</span> <span class="mi">6</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 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="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">logEntryPool</span><span class="p">)</span> <span class="nf">Get</span><span class="p">(</span><span class="nx">logger</span> <span class="o">*</span><span class="nx">log</span><span class="p">.</span><span class="nx">Logger</span><span class="p">)</span> <span class="o">*</span><span class="nx">log</span><span class="p">.</span><span class="nx">Entry</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">entry</span> <span class="o">:=</span> <span class="nx">p</span><span class="p">.</span><span class="nx">pool</span><span class="p">.</span><span class="nf">Get</span><span class="p">().(</span><span class="o">*</span><span class="nx">log</span><span class="p">.</span><span class="nx">Entry</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">entry</span><span class="p">.</span><span class="nx">Logger</span> <span class="p">=</span> <span class="nx">logger</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">entry</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="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">logEntryPool</span><span class="p">)</span> <span class="nf">Put</span><span class="p">(</span><span class="nx">e</span> <span class="o">*</span><span class="nx">log</span><span class="p">.</span><span class="nx">Entry</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// TODO: clean, should make?
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// reference: https://github.com/sirupsen/logrus/pull/796/files
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// e.Data = make(log.Fields, 6)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">e</span><span class="p">.</span><span class="nx">Data</span> <span class="p">=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">p</span><span class="p">.</span><span class="nx">pool</span><span class="p">.</span><span class="nf">Put</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>使用</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">entry</span> <span class="o">:=</span> <span class="nx">logging</span><span class="p">.</span><span class="nx">LogEntryPool</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="nx">logger</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">defer</span> <span class="nx">logging</span><span class="p">.</span><span class="nx">LogEntryPool</span><span class="p">.</span><span class="nf">Put</span><span class="p">(</span><span class="nx">entry</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="异步日志">异步日志</h2>
<p><img src="/imgs/golang/logrus2.png" alt=""></p>
<p><img src="/imgs/golang/logrus3.png" alt=""></p>
<p>使用一个专门处理日志的goroutine</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">f</span> <span class="o">*</span><span class="nx">FileLogHook</span><span class="p">)</span> <span class="nf">makeAsync</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">f</span><span class="p">.</span><span class="nx">fireChannel</span> <span class="p">=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="o">*</span><span class="nx">logrus</span><span class="p">.</span><span class="nx">Entry</span><span class="p">,</span> <span class="nx">f</span><span class="p">.</span><span class="nx">asyncBufferSize</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;file hook will use a async buffer with size %d\n&#34;</span><span class="p">,</span> <span class="nx">f</span><span class="p">.</span><span class="nx">asyncBufferSize</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">for</span> <span class="nx">entry</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">f</span><span class="p">.</span><span class="nx">fireChannel</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">f</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="nx">entry</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Error during sending message to file:&#34;</span><span class="p">,</span> <span class="nx">err</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 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">// Fire is called when a log event is fired.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">f</span> <span class="o">*</span><span class="nx">FileLogHook</span><span class="p">)</span> <span class="nf">Fire</span><span class="p">(</span><span class="nx">entry</span> <span class="o">*</span><span class="nx">logrus</span><span class="p">.</span><span class="nx">Entry</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">f</span><span class="p">.</span><span class="nx">fireChannel</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> <span class="c1">// Async mode.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>		<span class="k">select</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">case</span> <span class="nx">f</span><span class="p">.</span><span class="nx">fireChannel</span> <span class="o">&lt;-</span> <span class="nx">entry</span><span class="p">:</span> <span class="c1">// try and put into chan, if fail will to default
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>		<span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="nx">f</span><span class="p">.</span><span class="nx">asyncBlock</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;the log buffered chan is full! will block&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">				<span class="nx">f</span><span class="p">.</span><span class="nx">fireChannel</span> <span class="o">&lt;-</span> <span class="nx">entry</span> <span class="c1">// Blocks the goroutine because buffer is full.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>				<span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">			<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;the log buffered chan is full! will drop&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="c1">// Drop message by default.
</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 class="k">return</span> <span class="kc">nil</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">// Sync mode.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="k">return</span> <span class="nx">f</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="nx">entry</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="kd">func</span> <span class="p">(</span><span class="nx">f</span> <span class="o">*</span><span class="nx">FileLogHook</span><span class="p">)</span> <span class="nf">send</span><span class="p">(</span><span class="nx">entry</span> <span class="o">*</span><span class="nx">logrus</span><span class="p">.</span><span class="nx">Entry</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">f</span><span class="p">.</span><span class="nx">loghook</span><span class="p">.</span><span class="nf">Fire</span><span class="p">(</span><span class="nx">entry</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">Async write to redis 
</span></span><span class="line"><span class="cl">with <span class="nv">buffersize</span><span class="o">=</span><span class="m">10000</span>
</span></span><span class="line"><span class="cl"><span class="o">(</span>block<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="m">7700</span> -&gt; <span class="m">18000</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Async write to redis
</span></span><span class="line"><span class="cl">With <span class="nv">buffersize</span><span class="o">=</span><span class="m">10000</span>
</span></span><span class="line"><span class="cl"><span class="o">(</span>none block, drop <span class="k">if</span> full<span class="o">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">60000+
</span></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>Go: gin validation</title>
			<link>https://wklken.me/posts/2021/02/04/golang-gin-validation.html</link>
			<pubDate>Thu, 04 Feb 2021 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2021/02/04/golang-gin-validation.html</guid>
			<description>这是在做 Golang 项目中的一些实践 传统的校验方式 需要在获取数据后, 写很多if判断语句, 无法复用且非常罗嗦 if a != &amp;#34;&amp;#34; { } if len(a) &amp;lt; 10 { } gin 的参数校验 gin 使用了</description>
			<content type="html"><![CDATA[<blockquote>
<p>这是在做 Golang 项目中的一些实践</p>
</blockquote>
<h2 id="传统的校验方式">传统的校验方式</h2>
<p>需要在获取数据后, 写很多<code>if</code>判断语句, 无法复用且非常罗嗦</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">a</span> <span class="o">!=</span> <span class="s">&#34;&#34;</span> <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></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="p">&lt;</span> <span class="mi">10</span> <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></span></code></pre></div><h2 id="gin-的参数校验">gin 的参数校验</h2>
<p>gin 使用了 <a href="https://github.com/go-playground/validator">go-playground/validator</a>库, 使用tag声明的方式, 支持http请求request中的各类校验</p>
<ul>
<li><a href="https://github.com/gin-gonic/gin#model-binding-and-validation">gin: Model binding and validation</a></li>
<li><a href="https://pkg.go.dev/github.com/go-playground/validator">Package validator docs</a></li>
<li><a href="https://github.com/gin-gonic/gin#custom-validators">gin: Custom Validators</a></li>
</ul>
<h2 id="几种常用参数校验">几种常用参数校验</h2>
<h3 id="1-get-参数">1. get 参数</h3>
<p><a href="https://github.com/gin-gonic/gin#only-bind-query-string">gin: Only Bind Query String</a></p>
<blockquote>
<p>curl -X GET &ldquo;localhost:8085/testing?name=eason&amp;address=xyz&rdquo;</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Person</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Name</span>    <span class="kt">string</span> <span class="s">`form:&#34;name&#34;`</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Address</span> <span class="kt">string</span> <span class="s">`form:&#34;address&#34;`</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="kd">var</span> <span class="nx">person</span> <span class="nx">Person</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBindQuery</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">person</span><span class="p">)</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">log</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;====== Only Bind By Query String ======&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="nx">log</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">person</span><span class="p">.</span><span class="nx">Name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="nx">log</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">person</span><span class="p">.</span><span class="nx">Address</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="2-路径参数">2. 路径参数</h3>
<p><a href="https://github.com/gin-gonic/gin#bind-uri">gin: Bind Uri</a></p>
<blockquote>
<p>curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;github.com/gin-gonic/gin&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Person</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">ID</span> <span class="kt">string</span> <span class="s">`uri:&#34;id&#34; binding:&#34;required,uuid&#34;`</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Name</span> <span class="kt">string</span> <span class="s">`uri:&#34;name&#34; binding:&#34;required&#34;`</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="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">route</span> <span class="o">:=</span> <span class="nx">gin</span><span class="p">.</span><span class="nf">Default</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="nx">route</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/:name/:id&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</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">person</span> <span class="nx">Person</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBindUri</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">person</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">400</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span><span class="s">&#34;msg&#34;</span><span class="p">:</span> <span class="nx">err</span><span class="p">})</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">		<span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span><span class="s">&#34;name&#34;</span><span class="p">:</span> <span class="nx">person</span><span class="p">.</span><span class="nx">Name</span><span class="p">,</span> <span class="s">&#34;uuid&#34;</span><span class="p">:</span> <span class="nx">person</span><span class="p">.</span><span class="nx">ID</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="nx">route</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="s">&#34;:8088&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="3-json-body">3. json body</h3>
<ul>
<li><a href="https://github.com/gin-gonic/gin#model-binding-and-validation">gin: Model binding and validation</a></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Login</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">User</span>     <span class="kt">string</span> <span class="s">`json:&#34;user&#34; binding:&#34;required&#34;`</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Password</span> <span class="kt">string</span> <span class="s">`json:&#34;password&#34; binding:&#34;required&#34;`</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="kd">var</span> <span class="nx">json</span> <span class="nx">Login</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBindJSON</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">json</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusBadRequest</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span><span class="s">&#34;error&#34;</span><span class="p">:</span> <span class="nx">err</span><span class="p">.</span><span class="nf">Error</span><span class="p">()})</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="4-header">4. header</h3>
<p><a href="https://github.com/gin-gonic/gin#bind-header">gin: Bind Header</a></p>
<blockquote>
<p>curl -H &ldquo;rate:300&rdquo; -H &ldquo;domain:music&rdquo; 127.0.0.1:8080/</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;fmt&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;github.com/gin-gonic/gin&#34;</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="kd">type</span> <span class="nx">testHeader</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Rate</span>   <span class="kt">int</span>    <span class="s">`header:&#34;Rate&#34;`</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Domain</span> <span class="kt">string</span> <span class="s">`header:&#34;Domain&#34;`</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="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">r</span> <span class="o">:=</span> <span class="nx">gin</span><span class="p">.</span><span class="nf">Default</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="nx">r</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">gin</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">h</span> <span class="o">:=</span> <span class="nx">testHeader</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBindHeader</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">h</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">err</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="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;%#v\n&#34;</span><span class="p">,</span> <span class="nx">h</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="nx">c</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="nx">gin</span><span class="p">.</span><span class="nx">H</span><span class="p">{</span><span class="s">&#34;Rate&#34;</span><span class="p">:</span> <span class="nx">h</span><span class="p">.</span><span class="nx">Rate</span><span class="p">,</span> <span class="s">&#34;Domain&#34;</span><span class="p">:</span> <span class="nx">h</span><span class="p">.</span><span class="nx">Domain</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="nx">r</span><span class="p">.</span><span class="nf">Run</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="json-body是一个对象数组">json body是一个对象数组</h2>
<p><code>ShouldBindJSON</code>并不会校验对象数组中的每个对象是否符合要求, 需要自行调用方法处理</p>
<p>可以抽象出一个通用的函数</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">ErrNotArray</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;validate array fail, only support array&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">ToSlice</span><span class="p">(</span><span class="nx">array</span> <span class="kd">interface</span><span class="p">{})</span> <span class="p">([]</span><span class="kd">interface</span><span class="p">{},</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">v</span> <span class="o">:=</span> <span class="nx">reflect</span><span class="p">.</span><span class="nf">ValueOf</span><span class="p">(</span><span class="nx">array</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">v</span><span class="p">.</span><span class="nf">Kind</span><span class="p">()</span> <span class="o">!=</span> <span class="nx">reflect</span><span class="p">.</span><span class="nx">Slice</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">ErrNotArray</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="nx">l</span> <span class="o">:=</span> <span class="nx">v</span><span class="p">.</span><span class="nf">Len</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="nx">ret</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kd">interface</span><span class="p">{},</span> <span class="nx">l</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="p">&lt;</span> <span class="nx">l</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">ret</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">=</span> <span class="nx">v</span><span class="p">.</span><span class="nf">Index</span><span class="p">(</span><span class="nx">i</span><span class="p">).</span><span class="nf">Interface</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="k">return</span> <span class="nx">ret</span><span class="p">,</span> <span class="kc">nil</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="kd">func</span> <span class="nf">ValidateArray</span><span class="p">(</span><span class="nx">data</span> <span class="kd">interface</span><span class="p">{})</span> <span class="p">(</span><span class="kt">bool</span><span class="p">,</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">array</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">ToSlice</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">err</span><span class="p">.</span><span class="nf">Error</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="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">array</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="kc">false</span><span class="p">,</span> <span class="s">&#34;the array should contain at least 1 item&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">index</span><span class="p">,</span> <span class="nx">item</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">array</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">binding</span><span class="p">.</span><span class="nx">Validator</span><span class="p">.</span><span class="nf">ValidateStruct</span><span class="p">(</span><span class="nx">item</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">message</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;data in array[%d], %s&#34;</span><span class="p">,</span> <span class="nx">index</span><span class="p">,</span> <span class="nf">ValidationErrorMessage</span><span class="p">(</span><span class="nx">err</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">message</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 class="k">return</span> <span class="kc">true</span><span class="p">,</span> <span class="s">&#34;valid&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>使用时</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">body</span> <span class="p">[]</span><span class="nx">action</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBindJSON</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">body</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// bad request
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="k">return</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="k">if</span> <span class="nx">valid</span><span class="p">,</span> <span class="nx">message</span> <span class="o">:=</span> <span class="nx">common</span><span class="p">.</span><span class="nf">ValidateArray</span><span class="p">(</span><span class="nx">body</span><span class="p">);</span> <span class="p">!</span><span class="nx">valid</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// bad request: message
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="更友好的错误提示">更友好的错误提示</h2>
<p>当validation报错的时候, 我们期望得到一个更友好的提示信息, 便于使用者确认问题</p>
<p>reference:</p>
<ol>
<li><a href="https://github.com/gin-gonic/gin/issues/430">https://github.com/gin-gonic/gin/issues/430</a></li>
<li><a href="https://medium.com/@seb.nyberg/better-validation-errors-in-go-gin-88f983564a3d">https://medium.com/@seb.nyberg/better-validation-errors-in-go-gin-88f983564a3d</a></li>
</ol>
<p>以下是一种实现, 处理的常用的 validation 规则的错误展示</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">util</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;fmt&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;io&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="s">&#34;github.com/go-playground/validator/v10&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="nx">log</span> <span class="s">&#34;github.com/sirupsen/logrus&#34;</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">// 这里是通用的 FieldError 处理, 如果需要针对某些字段或struct做定制, 需要自行定义一个
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">ValidationFieldError</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Err</span> <span class="nx">validator</span><span class="p">.</span><span class="nx">FieldError</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="kd">func</span> <span class="p">(</span><span class="nx">v</span> <span class="nx">ValidationFieldError</span><span class="p">)</span> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">e</span> <span class="o">:=</span> <span class="nx">v</span><span class="p">.</span><span class="nx">Err</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">switch</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Tag</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="s">&#34;required&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s is required&#34;</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Field</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="s">&#34;max&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s cannot be longer than %s&#34;</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Field</span><span class="p">(),</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Param</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="s">&#34;min&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s must be longer than %s&#34;</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Field</span><span class="p">(),</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Param</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="s">&#34;email&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="s">&#34;Invalid email format&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="s">&#34;len&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s must be %s characters long&#34;</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Field</span><span class="p">(),</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Param</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="s">&#34;gt&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s must greater than %s&#34;</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Field</span><span class="p">(),</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Param</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="s">&#34;gte&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s must greater or equals to %s&#34;</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Field</span><span class="p">(),</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Param</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="s">&#34;lt&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s must less than %s&#34;</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Field</span><span class="p">(),</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Param</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="s">&#34;lte&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s must less or equals to %s&#34;</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Field</span><span class="p">(),</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Param</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="s">&#34;oneof&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s must be one of &#39;%s&#39;&#34;</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Field</span><span class="p">(),</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Param</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="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%s is not valid, condition: %s&#34;</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nf">Field</span><span class="p">(),</span> <span class="nx">e</span><span class="p">.</span><span class="nf">ActualTag</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="kd">func</span> <span class="nf">ValidationErrorMessage</span><span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">==</span> <span class="nx">io</span><span class="p">.</span><span class="nx">EOF</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="s">&#34;EOF, json decode fail&#34;</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="nx">validationErrs</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">err</span><span class="p">.(</span><span class="nx">validator</span><span class="p">.</span><span class="nx">ValidationErrors</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">message</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;json decode or validate fail, err=%s&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="nx">log</span><span class="p">.</span><span class="nf">Info</span><span class="p">(</span><span class="nx">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">message</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">// currently, only return the first error
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">fieldErr</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">validationErrs</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">ValidationFieldError</span><span class="p">{</span><span class="nx">fieldErr</span><span class="p">}.</span><span class="nf">String</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="k">return</span> <span class="s">&#34;validationErrs with no error message&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="判断-空值还是没有传递">判断: 空值还是没有传递</h2>
<p>有些场景, 如果一个json对象中, 某个字段可以为空, 但是非空的时候, 要执行某些特殊的逻辑</p>
<p>此时, 使用默认的validation是无法判断, 请求中没有这个字段, 还是传了空值;</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// validate
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">body</span> <span class="nx">action</span>
</span></span><span class="line"><span class="cl"><span class="nx">err</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBindBodyWith</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">body</span><span class="p">,</span> <span class="nx">binding</span><span class="p">.</span><span class="nx">JSON</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// bad request, ValidationErrorMessage(err)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="k">return</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">data</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="nx">err</span> <span class="p">=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ShouldBindBodyWith</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">data</span><span class="p">,</span> <span class="nx">binding</span><span class="p">.</span><span class="nx">JSON</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// bad request, ValidationErrorMessage(err)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="k">return</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="k">if</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">data</span><span class="p">[</span><span class="s">&#34;name&#34;</span><span class="p">];</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// do something
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>远程办公的一点总结</title>
			<link>https://wklken.me/posts/2021/02/01/about-remote.html</link>
			<pubDate>Mon, 01 Feb 2021 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2021/02/01/about-remote.html</guid>
			<description>这是去年疫情期间, 公司实施了接近两个月的远程, 当时写下的一些小结 不好的地方 人都有惰性, 另外个人远程办公环境没搞好, 容易被打扰/分心; 协作人数</description>
			<content type="html"><![CDATA[<blockquote>
<p>这是去年疫情期间, 公司实施了接近两个月的远程, 当时写下的一些小结</p>
</blockquote>
<p><img src="/imgs/blabla/about-remote.jpeg" alt=""></p>
<h2 id="不好的地方">不好的地方</h2>
<ul>
<li>人都有惰性, 另外个人远程办公环境没搞好, 容易被打扰/分心;</li>
<li>协作人数&lt;3人, 远程效果正比于个人的自觉性/自律</li>
<li>如果没有定期晨会/例会过进度, 远程效果会大大降低</li>
<li>如果没有目标感, 会迷失, 导致效率下降, 产出降低</li>
</ul>
<h2 id="好的实践">好的实践</h2>
<ul>
<li>远程在做规划时, 需要让整体项目组的目标一致, 然后细化到每个人, 每个人每天都有目标感; (迭代用OKR)</li>
<li>需要有定期的review/检查机制, 保持节奏, 即使纠正偏差</li>
<li>具体issue有截止时间</li>
<li>协作人数&gt;=3人, 每天晨会, 互相进行code review, 每个迭代初有详细规划, 每个迭代结束有总结review</li>
</ul>
<h2 id="主观感受">主观感受</h2>
<ul>
<li>会议会少很多, 相对原先办公室的场景, 少了很多临时拉的会/被动旁听的会; 整体会议由定期的例会/晨会, 临时的讨论会组成;
<ul>
<li>整体会议时间降低30%-50%; 效率提升的主要原因是, 异步处理+同步会议的收益,</li>
<li>会议之前, 相关议题都同步到, 各自在参会前都做了思考/评论;</li>
</ul>
</li>
<li>编码效率高; 如果项目在快速迭代/产出阶段, 远程可能非常合适, 此时编码效率提升30%, 主要是少打断, 时间块相对完整</li>
<li>时间多了很多, 时间块多带来的效率提升, 少了每日来回通勤2个小时</li>
<li>要注意过劳, 有时候长时间沉浸在编码中, 导致最终筋疲力尽</li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>Go: 开发过程中的一些bug</title>
			<link>https://wklken.me/posts/2021/01/28/golang-bugs.html</link>
			<pubDate>Thu, 28 Jan 2021 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2021/01/28/golang-bugs.html</guid>
			<description>1. make slice 很容易漏掉中间参数, 引入 bug并且很难排查 package main import &amp;#34;fmt&amp;#34; func doCopy(a []string) []string { b := make([]string, len(a)) for _, i := range a { b = append(b, i) } return b } func main() { a := []string{&amp;#34;hello&amp;#34;} b := doCopy(a) fmt.Println(b, len(b)) } 得到结果 [ hello]</description>
			<content type="html"><![CDATA[<h2 id="1-make-slice">1. make slice</h2>
<p>很容易漏掉中间参数, 引入 bug并且很难排查</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;fmt&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">doCopy</span><span class="p">(</span><span class="nx">a</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">[]</span><span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">b</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">string</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">a</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">i</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">a</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">b</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span> <span class="nx">i</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="k">return</span> <span class="nx">b</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="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">a</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;hello&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="nx">b</span> <span class="o">:=</span> <span class="nf">doCopy</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">b</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">b</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>得到结果</p>
<pre tabindex="0"><code>[ hello] 2
</code></pre><p>实际上</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">a</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">int</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>  <span class="c1">// len(a)=5
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">a</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">int</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>  <span class="c1">// len(a)=0 cap(b)=5
</span></span></span></code></pre></div><h2 id="2-shadow">2. shadow</h2>
<p>本来应该使用<code>=</code>赋值, 错误地使用了<code>:=</code> (这个govet可以扫出来)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">form</span> <span class="nx">url</span><span class="p">.</span><span class="nx">Values</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">checkForm</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">form</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="nx">url</span><span class="p">.</span><span class="nx">Values</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nx">util</span><span class="p">.</span><span class="nf">CopyValues</span><span class="p">(</span><span class="nx">form</span><span class="p">,</span> <span class="nx">r</span><span class="p">.</span><span class="nx">PostForm</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="3-err--or-">3. err == or !=</h2>
<p>容易敲错, 且不好排查</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">==</span> <span class="kc">nil</span> <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></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <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></span></code></pre></div><h2 id="4-漏掉了return">4. 漏掉了return</h2>
<p>在本该return的地方漏掉了, 导致逻辑继续往后走, 这种在gin的handler和middleware特别容易漏</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">render</span><span class="p">.</span><span class="nf">JSON</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">err</span><span class="p">.</span><span class="nx">Status</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// 这里漏掉了 return
</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="nx">next</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">r</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="5-scopelint">5. scopelint</h2>
<p><a href="https://github.com/kyoh86/scopelint">scopelint</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;fmt&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">values</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;a&#34;</span><span class="p">,</span> <span class="s">&#34;b&#34;</span><span class="p">,</span> <span class="s">&#34;c&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="kd">var</span> <span class="nx">copies</span> <span class="p">[]</span><span class="o">*</span><span class="kt">string</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">val</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">values</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">copies</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">copies</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">val</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="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">copies</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>此时得到</p>
<pre tabindex="0"><code>[0xc000010200 0xc000010200 0xc000010200]
</code></pre><p>预期的应该是</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;fmt&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">values</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">&#34;a&#34;</span><span class="p">,</span> <span class="s">&#34;b&#34;</span><span class="p">,</span> <span class="s">&#34;c&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="kd">var</span> <span class="nx">copies</span> <span class="p">[]</span><span class="o">*</span><span class="kt">string</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">val</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">values</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// add := here
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>		<span class="nx">val</span> <span class="o">:=</span> <span class="nx">val</span>
</span></span><span class="line"><span class="cl">		<span class="nx">copies</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">copies</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">val</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="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">copies</span><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></span></code></pre></div><h2 id="6-erroris放错了位置">6. error.Is放错了位置</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">err</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="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 class="k">if</span> <span class="kt">error</span><span class="p">.</span><span class="nf">Is</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">DEMOError</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// will never execute
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><h2 id="7-close-the-connection">7. close the connection</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// the connection is not close
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">sqlx</span><span class="p">.</span><span class="nf">Connect</span><span class="p">(</span><span class="s">&#34;mysql&#34;</span><span class="p">,</span> <span class="nx">db</span><span class="p">.</span><span class="nx">dataSource</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// should be
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">conn</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">sqlx</span><span class="p">.</span><span class="nf">Connect</span><span class="p">(</span><span class="s">&#34;mysql&#34;</span><span class="p">,</span> <span class="nx">db</span><span class="p">.</span><span class="nx">dataSource</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</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="nx">conn</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>项目管理实践: 风险驱动开发</title>
			<link>https://wklken.me/posts/2021/01/27/risk-driven-mode.html</link>
			<pubDate>Wed, 27 Jan 2021 20:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2021/01/27/risk-driven-mode.html</guid>
			<description>风险驱动开发 快速迭代过程中的问题 在一个项目从0到1快速迭代开发的过程中, 除了常规的需求开发, 还会面临很多问题, 来自于deadline, 线上b</description>
			<content type="html"><![CDATA[<p><img src="/imgs/pm/risk-driven-mode.jpg" alt=""></p>
<h1 id="风险驱动开发">风险驱动开发</h1>
<h2 id="快速迭代过程中的问题">快速迭代过程中的问题</h2>
<p>在一个项目从0到1快速迭代开发的过程中, 除了常规的需求开发, 还会面临很多问题, 来自于deadline, 线上bug, 用户咨询的问题等等</p>
<p>多人开发过程中, 除了上下游项目依赖, 还存在协作</p>
<p>我们无法完全按照既定的规划注意处理问题, 但是也不能切换到紧急的事情上.</p>
<p>我们应当将目标focus在 <code>重要紧急</code>和<code>重要不紧急</code>的事情上, 而实际迭代过程往往会受到<code>紧急</code>的事情打断, 导致真正<code>重要</code>的事情延期.</p>
<p>那么, 我们应该如何应对这种情况?</p>
<hr>
<h2 id="风险驱动模型是什么">风险驱动模型是什么?</h2>
<p>&laquo;恰如其分的软件架构&raquo;的第一部分就是讲述风险驱动模型的.</p>
<blockquote>
<p>运用最小的架构技术集合去降低最紧迫的风险, 以求事半功倍</p>
</blockquote>
<p>步骤</p>
<ul>
<li>
<p>识别风险, 并排定优先级(从需求出发)</p>
</li>
<li>
<p>选择并运用一组技术</p>
</li>
<li>
<p>评估风险降低的程度</p>
</li>
</ul>
<hr>
<h2 id="实践">实践</h2>
<ul>
<li>首先, 我们采用了OKR的方式管理每个迭代的目标和关键结果</li>
<li>在每个迭代开始之初, 先将近期所有事项加入一个<code>池子</code>,  这其中会包含最近加入的issue, 也会包含既定规划好的issue, 以及上个迭代delay的issue.</li>
<li>然后逐一review, 评论, 这个过程需要issue相关的所有人员进行review, 补充信息. 补充信息包括, 涉及的人员/上下游状态, 交付时间, 风险点等等.</li>
<li>然后, 使用<code>风险驱动开发</code>的模式, 对<code>池子</code>里面的所有issue进行风险评估, 从而确定<code>最重要</code>的事情(注意不一定是最紧急的事情), 最终排定优先级及时间</li>
<li>对于<code>紧急而不重要</code>的事情, 采用类似GTD中的二分钟法则(如果一件事情两分钟内能搞定那么马上处理), 如果工作量不大, 那么适当优先处理, 已减少干扰项, 有更大块的时间处理重要的事情</li>
</ul>
<hr>
<h2 id="问题">问题</h2>
<h3 id="为什么每个迭代都需要对issue补充信息">为什么每个迭代都需要对issue<code>补充信息</code>?</h3>
<p>这是因为, 一切都是变化的, 相关的需求/人员/上下游等等都可能存在变化, 这样就导致一个issue的重要程度变化, 同时也关系到这个issue的风险.</p>
<h3 id="这样做的好处">这样做的好处?</h3>
<p>在项目的不同阶段, 或者在同一个阶段的不同迭代, 整个项目的风险是<code>动态变化</code>的, 你必须评估项目当前时刻的每一个风险, 最大程度地降低失败的风险</p>
]]></content>
		</item>
		
		<item>
			<title>Go: 一种error wrap调用链处理方式</title>
			<link>https://wklken.me/posts/2021/01/26/golang-error-wrap.html</link>
			<pubDate>Tue, 26 Jan 2021 14:38:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2021/01/26/golang-error-wrap.html</guid>
			<description>这是在做 Golang 项目中的一些实践 背景 在做一套内部公共服务, 提供后台API调用的时候, 某些情况下, 会返回500错误, 此时有两种做法 在后台记录详细的5</description>
			<content type="html"><![CDATA[<blockquote>
<p>这是在做 Golang 项目中的一些实践</p>
</blockquote>
<p><img src="/imgs/golang/error-wrap.png" alt=""></p>
<h2 id="背景">背景</h2>
<p>在做一套内部公共服务, 提供后台API调用的时候, 某些情况下, 会返回500错误, 此时有两种做法</p>
<ol>
<li>在后台记录详细的500日志, 返回给调用方一个<code>request_id</code>, 调用方拿request_id查询日志定位问题</li>
<li>直接在response body中返回错误信息</li>
</ol>
<p>选择了问题排查路径短一些的 <code>2</code>, 错误信息中, 需要包含调用链路以及根因错误的详细信息, 提升问题排查的效率</p>
<p>这样, 开发者在看到报错response body的同时, 可以直接知道调用链, 不需要使用请求重新复现问题, 节约了大量的复现/沟通的时间</p>
<h2 id="最终效果">最终效果</h2>
<pre tabindex="0"><code>system error[request_id=0838c4090cfa4f4f9dceea5fd8c7b029]: [Handler:validateSystemSuperUser] impls.ListSubjectRoleSystemID subjectType=`user`, subjectID=`test1` fail%!(EXTRA string=test1) =&gt; [Cache:GetSubjectRole] SubjectRoleCache.Get subjectType=`user`, subjectID=`test1` fail =&gt; [Cache:GetSubjectPK] SubjectPKCache.Get _type=`user`, id=`test1` fail =&gt; [SubjectSVC:GetPK] GetPK _type=`user`, id=`test1` fail =&gt; [Raw:Error] sql: no rows in result set
</code></pre><h2 id="处理机制">处理机制</h2>
<p>使用error wrap, 一层层抛出</p>
<h3 id="1-定义pkgerrorx">1. 定义pkg/errorx</h3>
<p>注意, package name使用<code>errorx</code>, 可以规避同标准库命名的冲突; 同时import的时候不需要alias</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Error</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">message</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">	<span class="nx">err</span>     <span class="kt">error</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="kd">func</span> <span class="p">(</span><span class="nx">e</span> <span class="nx">Error</span><span class="p">)</span> <span class="nf">Error</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">e</span><span class="p">.</span><span class="nx">message</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="kd">func</span> <span class="p">(</span><span class="nx">e</span> <span class="nx">Error</span><span class="p">)</span> <span class="nf">Is</span><span class="p">(</span><span class="nx">target</span> <span class="kt">error</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">target</span> <span class="o">==</span> <span class="kc">nil</span> <span class="o">||</span> <span class="nx">e</span><span class="p">.</span><span class="nx">err</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">e</span><span class="p">.</span><span class="nx">err</span> <span class="o">==</span> <span class="nx">target</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="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">Is</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">err</span><span class="p">,</span> <span class="nx">target</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="kd">func</span> <span class="p">(</span><span class="nx">e</span> <span class="o">*</span><span class="nx">Error</span><span class="p">)</span> <span class="nf">Unwrap</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">u</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">err</span><span class="p">.(</span><span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Unwrap</span><span class="p">()</span> <span class="kt">error</span>
</span></span><span class="line"><span class="cl">	<span class="p">})</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">e</span><span class="p">.</span><span class="nx">err</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="k">return</span> <span class="nx">u</span><span class="p">.</span><span class="nf">Unwrap</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="2-封装wrapfunc-helper">2. 封装wrapfunc helper</h3>
<p>注意,</p>
<ul>
<li>这里如果是最原始的err(即wrap前的err), 会使用<code>[Raw:Error]</code>标注</li>
<li>如果是中间层的, 会附加<code>layer</code>(哪一层)和<code>function</code>(哪个函数)这两个信息</li>
<li>这里定义了一个 helper 方法, 用于在</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">makeMessage</span><span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">,</span> <span class="nx">layer</span><span class="p">,</span> <span class="nx">function</span><span class="p">,</span> <span class="nx">msg</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kd">var</span> <span class="nx">message</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">	<span class="kd">var</span> <span class="nx">e</span> <span class="nx">Error</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">As</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">message</span> <span class="p">=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;[%s:%s] %s =&gt; %s&#34;</span><span class="p">,</span> <span class="nx">layer</span><span class="p">,</span> <span class="nx">function</span><span class="p">,</span> <span class="nx">msg</span><span class="p">,</span> <span class="nx">err</span><span class="p">.</span><span class="nf">Error</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">message</span> <span class="p">=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;[%s:%s] %s =&gt; [Raw:Error] %v&#34;</span><span class="p">,</span> <span class="nx">layer</span><span class="p">,</span> <span class="nx">function</span><span class="p">,</span> <span class="nx">msg</span><span class="p">,</span> <span class="nx">err</span><span class="p">.</span><span class="nf">Error</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="k">return</span> <span class="nx">message</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="kd">func</span> <span class="nf">Wrapf</span><span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">,</span> <span class="nx">layer</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">function</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">format</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">args</span> <span class="o">...</span><span class="kd">interface</span><span class="p">{})</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="kc">nil</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="nx">msg</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="nx">format</span><span class="p">,</span> <span class="nx">args</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="k">return</span> <span class="nx">Error</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">message</span><span class="p">:</span> <span class="nf">makeMessage</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">layer</span><span class="p">,</span> <span class="nx">function</span><span class="p">,</span> <span class="nx">msg</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">		<span class="nx">err</span><span class="p">:</span>     <span class="nx">err</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="kd">type</span> <span class="nx">WrapfFuncWithLayerFunction</span> <span class="kd">func</span><span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">,</span> <span class="nx">format</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">args</span> <span class="o">...</span><span class="kd">interface</span><span class="p">{})</span> <span class="kt">error</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewLayerFunctionErrorWrapf</span><span class="p">(</span><span class="nx">layer</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">function</span> <span class="kt">string</span><span class="p">)</span> <span class="nx">WrapfFuncWithLayerFunction</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">,</span> <span class="nx">format</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">args</span> <span class="o">...</span><span class="kd">interface</span><span class="p">{})</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nf">Wrapf</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">layer</span><span class="p">,</span> <span class="nx">function</span><span class="p">,</span> <span class="nx">format</span><span class="p">,</span> <span class="nx">args</span><span class="o">...</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></code></pre></div><h3 id="3-使用">3. 使用</h3>
<p>在某个函数体内, 涉及到需要return err或中间层</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">errorWrapf</span> <span class="o">:=</span> <span class="nx">errorx</span><span class="p">.</span><span class="nf">NewLayerFunctionErrorWrapf</span><span class="p">(</span><span class="s">&#34;Handler&#34;</span><span class="p">,</span> <span class="s">&#34;ListSubject&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">svc</span><span class="p">.</span><span class="nf">ListPaging</span><span class="p">(</span><span class="nx">body</span><span class="p">.</span><span class="nx">Type</span><span class="p">,</span> <span class="nx">body</span><span class="p">.</span><span class="nx">Limit</span><span class="p">,</span> <span class="nx">body</span><span class="p">.</span><span class="nx">Offset</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">err</span> <span class="p">=</span> <span class="nf">errorWrapf</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="s">&#34;svc.ListPaging type=`%s` limit=`%d` offset=`%d` fail!&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">			<span class="nx">body</span><span class="p">.</span><span class="nx">Type</span><span class="p">,</span> <span class="nx">body</span><span class="p">.</span><span class="nx">Limit</span><span class="p">,</span> <span class="nx">body</span><span class="p">.</span><span class="nx">Offset</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>注意, <strong>wrap时包含关键的调用参数/返回值等等, 但是不能包含敏感数据</strong></p>
]]></content>
		</item>
		
		<item>
			<title>漫谈技术选型</title>
			<link>https://wklken.me/posts/2021/01/25/something-about-tech-selection.html</link>
			<pubDate>Mon, 25 Jan 2021 18:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2021/01/25/something-about-tech-selection.html</guid>
			<description>去年八月份的一次小组分享, 去掉了敏感信息和示例</description>
			<content type="html"><![CDATA[<hr>
<p>去年八月份的一次小组分享, 去掉了敏感信息和示例</p>
<hr>
<object data="/extra/share/something_about_tech_selection.pdf" type="application/pdf" width="729" height="525">
    <embed src="/extra/share/something_about_tech_selection.pdf">
    </embed>
</object>
]]></content>
		</item>
		
		<item>
			<title>Go: 基于 apitest 做handler层单元测试</title>
			<link>https://wklken.me/posts/2021/01/22/golang-apitest.html</link>
			<pubDate>Fri, 22 Jan 2021 14:35:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2021/01/22/golang-apitest.html</guid>
			<description>apitest A simple and extensible behavioural testing library. Supports mocking external http calls and renders sequence diagrams on completion. Credit to testify which is this libraries&amp;rsquo; only dependency 相关资源: Github apitest 文档 示例: Examples / Gin integration 老的方案: postman/newman 原先使用 postman + newman 的方式做的 API 功能测试; 这种</description>
			<content type="html"><![CDATA[<h2 id="apitest">apitest</h2>
<p><img src="/imgs/golang/apitest.png" alt=""></p>
<blockquote>
<p>A simple and extensible behavioural testing library. Supports mocking external http calls and renders sequence diagrams on completion.
Credit to testify which is this libraries&rsquo; only dependency</p>
</blockquote>
<p>相关资源:</p>
<ul>
<li><a href="https://github.com/steinfletcher/apitest">Github apitest</a></li>
<li><a href="https://apitest.dev/">文档</a></li>
<li>示例: <a href="https://github.com/steinfletcher/apitest#examples">Examples</a> / <a href="https://github.com/steinfletcher/apitest/tree/master/examples/gin">Gin integration</a></li>
</ul>
<h2 id="老的方案-postmannewman">老的方案: postman/newman</h2>
<p>原先使用 postman + newman 的方式做的 API 功能测试;</p>
<p>这种方式不完全是API层的单元测试, 预先配置好数据库/初始化好数据后, 直接通过API调用, 判定响应值</p>
<p>是介于单元测试 + 集成测试 之间的一种测试, 但是既不是完备的单元测试, 也不是完整的集成测试;</p>
<p>好处:</p>
<ul>
<li>简单方便, 在本地开发调试API时, 就能将调试请求直接转为测试用例</li>
</ul>
<p>缺点:</p>
<ul>
<li>难以维护: 更新API/增加API时, 未实时同步更新测试用例; 变更上层协议时, 例如middleware, 所有case需要改</li>
<li>违背Isolate原则: 无法独立, 每个case运行时, 前面执行的数据会影响后面执行的数据; 每个case都需要考虑规避之前case的数据影响</li>
<li>缺乏完备性: 一个API一般只构造一个失败, 一个成功两个Case</li>
</ul>
<h2 id="新的方案">新的方案</h2>
<ul>
<li>Golang 使用 apitest 做handler层的<code>单元测试</code></li>
<li>当前只覆盖<code>核心逻辑</code></li>
<li>重新去寻找<code>集成测试</code>的方案</li>
</ul>
<h3 id="1-入门示例">1. 入门示例</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestApi</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">apitest</span><span class="p">.</span><span class="nf">New</span><span class="p">().</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Handler</span><span class="p">(</span><span class="nx">handler</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Get</span><span class="p">(</span><span class="s">&#34;/user/1234&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Expect</span><span class="p">(</span><span class="nx">t</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Body</span><span class="p">(</span><span class="s">`{&#34;id&#34;: &#34;1234&#34;, &#34;name&#34;: &#34;Tate&#34;}`</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Status</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusCreated</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">End</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="2-传统httptest-vs-apitest">2. 传统httptest vs apitest</h3>
<p>使用httptest</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestPong</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">r</span> <span class="o">:=</span> <span class="nx">util</span><span class="p">.</span><span class="nf">SetupRouter</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="nx">r</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/test&#34;</span><span class="p">,</span> <span class="nx">Pong</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">req</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="s">&#34;GET&#34;</span><span class="p">,</span> <span class="s">&#34;/test&#34;</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">w</span> <span class="o">:=</span> <span class="nx">httptest</span><span class="p">.</span><span class="nf">NewRecorder</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="nx">r</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">req</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="mi">200</span><span class="p">,</span> <span class="nx">w</span><span class="p">.</span><span class="nx">Code</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">body</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadAll</span><span class="p">(</span><span class="nx">w</span><span class="p">.</span><span class="nx">Body</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">assert</span><span class="p">.</span><span class="nf">NoError</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">assert</span><span class="p">.</span><span class="nf">True</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nb">string</span><span class="p">(</span><span class="nx">body</span><span class="p">),</span> <span class="s">&#34;pong&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>使用apitest</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestPong</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">r</span> <span class="o">:=</span> <span class="nx">util</span><span class="p">.</span><span class="nf">SetupRouter</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="nx">r</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/ping&#34;</span><span class="p">,</span> <span class="nx">Pong</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">apitest</span><span class="p">.</span><span class="nf">New</span><span class="p">().</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Handler</span><span class="p">(</span><span class="nx">r</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Get</span><span class="p">(</span><span class="s">&#34;/ping&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Expect</span><span class="p">(</span><span class="nx">t</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Body</span><span class="p">(</span><span class="s">`{&#34;message&#34;:&#34;pong&#34;}`</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Status</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">End</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="3-why-not-jsonpath">3. why not JSONpath?</h3>
<p>我们决定<code>禁止</code>在测试中使用<code>JSONpath</code>, 虽然这是apitest提供的一个强大的特性</p>
<p>官方示例: <code>{&quot;a&quot;: 12345, &quot;b&quot;: [{&quot;key&quot;: &quot;c&quot;, &quot;value&quot;: &quot;result&quot;}]}</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestApi</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">apitest</span><span class="p">.</span><span class="nf">New</span><span class="p">().</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Handler</span><span class="p">(</span><span class="nx">handler</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Get</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Expect</span><span class="p">(</span><span class="nx">t</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Assert</span><span class="p">(</span><span class="nx">jsonpath</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="s">`$.b[? @.key==&#34;c&#34;].value`</span><span class="p">,</span> <span class="s">&#34;result&#34;</span><span class="p">)).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">End</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>问题点: 可维护性并不好(多一层学习和理解成本, JSONPath本身是一套很强大的语法, 但也就意味着其复杂度很高); 而单元测试最重要的就是后续的可维护性</p>
<h3 id="4-jsonassert-封装-json-解析成mapstringinterface">4. JSONAssert: 封装 json 解析成map[string]interface</h3>
<p>注意力集中在json body, 而不是解析</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">JSONAssertFunc</span> <span class="kd">func</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{})</span> <span class="kt">error</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewJSONAssertFunc</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">,</span> <span class="nx">assertFunc</span> <span class="nx">JSONAssertFunc</span><span class="p">)</span> <span class="kd">func</span><span class="p">(</span><span class="nx">res</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Response</span><span class="p">,</span> <span class="nx">req</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">res</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Response</span><span class="p">,</span> <span class="nx">req</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">body</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadAll</span><span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nx">Body</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="nx">assert</span><span class="p">.</span><span class="nf">NoError</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">err</span><span class="p">,</span> <span class="s">&#34;read body from response fail&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="k">defer</span> <span class="nx">res</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="kd">var</span> <span class="nx">data</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">		<span class="c1">//var data Response
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">		<span class="nx">err</span> <span class="p">=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">(</span><span class="nx">body</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="nx">assert</span><span class="p">.</span><span class="nf">NoError</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">err</span><span class="p">,</span> <span class="s">&#34;unmarshal string to json fail&#34;</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="nf">assertFunc</span><span class="p">(</span><span class="nx">data</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></code></pre></div><p>使用</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestVersion</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">r</span> <span class="o">:=</span> <span class="nx">util</span><span class="p">.</span><span class="nf">SetupRouter</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="nx">r</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/version&#34;</span><span class="p">,</span> <span class="nx">Version</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">apitest</span><span class="p">.</span><span class="nf">New</span><span class="p">().</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Handler</span><span class="p">(</span><span class="nx">r</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Get</span><span class="p">(</span><span class="s">&#34;/version&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Expect</span><span class="p">(</span><span class="nx">t</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Assert</span><span class="p">(</span><span class="nx">util</span><span class="p">.</span><span class="nf">NewJSONAssertFunc</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">m</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{})</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">assert</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">m</span><span class="p">,</span> <span class="s">&#34;version&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="nx">assert</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">m</span><span class="p">,</span> <span class="s">&#34;commit&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="nx">assert</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">m</span><span class="p">,</span> <span class="s">&#34;buildTime&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="nx">assert</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">m</span><span class="p">,</span> <span class="s">&#34;goVersion&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="nx">assert</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">m</span><span class="p">,</span> <span class="s">&#34;env&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">		<span class="p">})).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Status</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">End</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="5-responseassert-封装成后台返回协议response">5. ResponseAssert: 封装成后台返回协议(Response)</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Response</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Code</span>    <span class="kt">int</span>         <span class="s">`json:&#34;code&#34;`</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Message</span> <span class="kt">string</span>      <span class="s">`json:&#34;message&#34;`</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Data</span>    <span class="kd">interface</span><span class="p">{}</span> <span class="s">`json:&#34;data&#34;`</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="kd">type</span> <span class="nx">ResponseAssertFunc</span> <span class="kd">func</span><span class="p">(</span><span class="nx">Response</span><span class="p">)</span> <span class="kt">error</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewResponseAssertFunc</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">	<span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="nx">responseFunc</span> <span class="nx">ResponseAssertFunc</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span> <span class="kd">func</span><span class="p">(</span><span class="nx">res</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Response</span><span class="p">,</span> <span class="nx">req</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">res</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Response</span><span class="p">,</span> <span class="nx">req</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">body</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadAll</span><span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nx">Body</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="nx">assert</span><span class="p">.</span><span class="nf">NoError</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">err</span><span class="p">,</span> <span class="s">&#34;read body from response fail&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="k">defer</span> <span class="nx">res</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="kd">var</span> <span class="nx">data</span> <span class="nx">Response</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="nx">err</span> <span class="p">=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">(</span><span class="nx">body</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="nx">assert</span><span class="p">.</span><span class="nf">NoError</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">err</span><span class="p">,</span> <span class="s">&#34;unmarshal string to response fail&#34;</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="nf">responseFunc</span><span class="p">(</span><span class="nx">data</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></code></pre></div><p>使用</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestCreateSystemBadRequestInvalidJson</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">r</span> <span class="o">:=</span> <span class="nx">util</span><span class="p">.</span><span class="nf">SetupRouter</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="nx">url</span> <span class="o">:=</span> <span class="s">&#34;/api/v1/systems&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="nx">r</span><span class="p">.</span><span class="nf">POST</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">CreateSystem</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// validate fail
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">apitest</span><span class="p">.</span><span class="nf">New</span><span class="p">().</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Handler</span><span class="p">(</span><span class="nx">r</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Post</span><span class="p">(</span><span class="nx">url</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">JSON</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{}{</span>
</span></span><span class="line"><span class="cl">			<span class="s">&#34;hello&#34;</span><span class="p">:</span> <span class="s">&#34;123&#34;</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="nf">Expect</span><span class="p">(</span><span class="nx">t</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Assert</span><span class="p">(</span><span class="nx">util</span><span class="p">.</span><span class="nf">NewResponseAssertFunc</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">resp</span> <span class="nx">util</span><span class="p">.</span><span class="nx">Response</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">resp</span><span class="p">.</span><span class="nx">Code</span><span class="p">,</span> <span class="nx">util</span><span class="p">.</span><span class="nx">BadRequestError</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="nx">resp</span><span class="p">.</span><span class="nx">Message</span><span class="p">,</span> <span class="s">&#34;bad request:ID is required&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">		<span class="p">})).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Status</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">End</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="6-能不能整合-ginkgo">6. 能不能整合 ginkgo?</h3>
<p>apitst的Expect方法接收的是 <code>*testing.T</code>, 对应interface是<code>testing.TB</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Expect marks the request spec as complete and following code will define the expected response
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">r</span> <span class="o">*</span><span class="nx">Request</span><span class="p">)</span> <span class="nf">Expect</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="o">*</span><span class="nx">Response</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">r</span><span class="p">.</span><span class="nx">apiTest</span><span class="p">.</span><span class="nx">t</span> <span class="p">=</span> <span class="nx">t</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">r</span><span class="p">.</span><span class="nx">apiTest</span><span class="p">.</span><span class="nx">response</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>并不兼容于 ginkgo 的<code>GinkgoT()</code></p>
<h3 id="7-再抽象一层-no">7. 再抽象一层? No</h3>
<p>不要再试图抽象成一个函数, 为了足够的灵活性, 势必导致函数参数数量增多, 导致使用困难;</p>
<p>最终完备的函数封装跟链式调用已经没有区别了</p>
<p>问题复杂化了. 再次封装的意义不大</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">assertRequestResponse</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">	<span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="nx">r</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="nx">url</span> <span class="kt">string</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="nx">appCode</span><span class="p">,</span> <span class="nx">appSecret</span> <span class="kt">string</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="nx">jsonData</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">interface</span><span class="p">{},</span>
</span></span><span class="line"><span class="cl">	<span class="nx">assertFunc</span> <span class="kd">func</span><span class="p">(</span><span class="nx">res</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Response</span><span class="p">,</span> <span class="nx">req</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</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"><span class="c1"></span>	<span class="c1">// 1. post/get/put/delete
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// 2. headers
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// 3. query参数等等
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">	<span class="nx">apitest</span><span class="p">.</span><span class="nf">New</span><span class="p">().</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Handler</span><span class="p">(</span><span class="nx">r</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Post</span><span class="p">(</span><span class="nx">url</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Header</span><span class="p">(</span><span class="s">&#34;X-App-Code&#34;</span><span class="p">,</span> <span class="nx">appCode</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Header</span><span class="p">(</span><span class="s">&#34;X-App-Secret&#34;</span><span class="p">,</span> <span class="nx">appSecret</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">JSON</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">			<span class="nx">jsonData</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="nf">Expect</span><span class="p">(</span><span class="nx">t</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Assert</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">			<span class="nx">assertFunc</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="nf">Status</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">End</span><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></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>Go: go-sql-driver interpolateparams参数优化</title>
			<link>https://wklken.me/posts/2021/01/22/golang-sql-driver-interpolateparams.html</link>
			<pubDate>Fri, 22 Jan 2021 12:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2021/01/22/golang-sql-driver-interpolateparams.html</guid>
			<description>这是在做 Golang 项目中的一些实践 interpolateparams 性能差异 interpolateparams=false interpolateparams=true 10261 -&amp;gt; 12117, 18% 带占位符SQL执行流程 prepared -&amp;gt; execute -&amp;gt; close 正常带占位符的sql执行过程: 客户端将该语句和参数发给mys</description>
			<content type="html"><![CDATA[<blockquote>
<p>这是在做 Golang 项目中的一些实践</p>
</blockquote>
<ul>
<li><a href="https://github.com/go-sql-driver/mysql/blob/5a8a207333b3cbdd6f50a31da2d448658343637e/README.md#interpolateparams">interpolateparams</a></li>
</ul>
<p><img src="/imgs/golang/sql-driver-1.jpg" alt=""></p>
<h2 id="性能差异">性能差异</h2>
<p><code>interpolateparams=false</code></p>
<p><img src="/imgs/golang/sql-driver-2.jpg" alt=""></p>
<p><code>interpolateparams=true</code></p>
<p><img src="/imgs/golang/sql-driver-3.jpg" alt=""></p>
<p>10261 -&gt; 12117,  18%</p>
<h2 id="带占位符sql执行流程">带占位符SQL执行流程</h2>
<blockquote>
<p><code>prepared -&gt; execute -&gt; close</code></p>
</blockquote>
<p>正常带占位符的sql执行过程:</p>
<ol>
<li>客户端将该语句和参数发给mysql服务器</li>
<li>mysql服务器编译成一个prepared语句，这个语句可以根据不同的参数多次调用</li>
</ol>
<p>好处:</p>
<ol>
<li>避免通过引号组装拼接sql语句。避免sql注入带来的安全风险</li>
<li>可以多次执行的sql语句</li>
</ol>
<h2 id="这个参数做了什么">这个参数做了什么?</h2>
<blockquote>
<p><code>execute -&gt; close</code></p>
</blockquote>
<p><code>interpolateparams=true</code>执行流程: <code>execute -&gt; close</code></p>
<p>好处:</p>
<ol>
<li>减少了<code>prepared</code>网络请求</li>
<li>会防止SQL注入, 在驱动中通过转义特殊字符实现的</li>
</ol>
<h2 id="源码">源码</h2>
<p><img src="/imgs/golang/sql-driver-4.jpg" alt=""></p>
<p><img src="/imgs/golang/sql-driver-5.jpg" alt=""></p>
<p><img src="/imgs/golang/sql-driver-6.jpg" alt=""></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl">    <span class="nx">prepared</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">mc</span><span class="p">.</span><span class="nf">interpolateParams</span><span class="p">(</span><span class="nx">query</span><span class="p">,</span> <span class="nx">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">		<span class="nx">query</span> <span class="p">=</span> <span class="nx">prepared</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="nx">如果query替换成功</span><span class="p">,</span> <span class="nx">直接执行</span> <span class="nx">writeCommandPacketStr</span><span class="p">,</span> <span class="nx">并且读取返回值</span><span class="p">,</span> <span class="k">return</span> <span class="p">(</span><span class="nx">rows</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span></code></pre></div><p>mc.interpolateParams中, 对<code>string</code>/<code>[]byte</code>等做了转义, prepared是插值后的sql</p>
<p><img src="/imgs/golang/sql-driver-7.jpg" alt=""></p>
]]></content>
		</item>
		
		<item>
			<title>Go: 基于 Ginkgo 框架进行单元测试实践</title>
			<link>https://wklken.me/posts/2021/01/22/golang-ginkgo.html</link>
			<pubDate>Fri, 22 Jan 2021 11:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2021/01/22/golang-ginkgo.html</guid>
			<description>这是在做 Golang 项目中的一些实践 ginkgo 基于BDD的测试框架; 开始前, 需要花半个小时阅读官方文档 reference github: https://github.com/onsi/ginkgo 文档: https://onsi.github.io/ginkgo/ table-driven 一个函数 // TruncateString truncate string to specific length func TruncateString(s string, n int) string { if n</description>
			<content type="html"><![CDATA[<blockquote>
<p>这是在做 Golang 项目中的一些实践</p>
</blockquote>
<p><img src="/imgs/golang/ginkgo.png" alt=""></p>
<p>ginkgo 基于BDD的测试框架; 开始前, 需要花半个小时阅读官方文档</p>
<h2 id="reference">reference</h2>
<ul>
<li>github: <a href="https://github.com/onsi/ginkgo">https://github.com/onsi/ginkgo</a></li>
<li>文档: <a href="https://onsi.github.io/ginkgo/">https://onsi.github.io/ginkgo/</a></li>
</ul>
<h2 id="table-driven">table-driven</h2>
<p>一个函数</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// TruncateString truncate string to specific length
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">TruncateString</span><span class="p">(</span><span class="nx">s</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">n</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">n</span> <span class="p">&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">s</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">s</span><span class="p">[:</span><span class="nx">n</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>使用table-driven方式生成测试代码</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestTruncateString</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kd">type</span> <span class="nx">args</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">s</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">		<span class="nx">n</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="nx">tests</span> <span class="o">:=</span> <span class="p">[]</span><span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">name</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">		<span class="nx">args</span> <span class="nx">args</span>
</span></span><span class="line"><span class="cl">		<span class="nx">want</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">	<span class="p">}{</span>
</span></span><span class="line"><span class="cl">		<span class="c1">// TODO: Add test cases.
</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 class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">tt</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">tests</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">t</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="nx">tt</span><span class="p">.</span><span class="nx">name</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="nx">got</span> <span class="o">:=</span> <span class="nx">util</span><span class="p">.</span><span class="nf">TruncateString</span><span class="p">(</span><span class="nx">tt</span><span class="p">.</span><span class="nx">args</span><span class="p">.</span><span class="nx">s</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">args</span><span class="p">.</span><span class="nx">n</span><span class="p">);</span> <span class="nx">got</span> <span class="o">!=</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">want</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">&#34;TruncateString() = %v, want %v&#34;</span><span class="p">,</span> <span class="nx">got</span><span class="p">,</span> <span class="nx">tt</span><span class="p">.</span><span class="nx">want</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 class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>如果是简单的util函数, 用例比较简单并且用例数量较少, 此时单元测试可读性和可维护性没有问题</p>
<h2 id="table-driven的问题">table-driven的问题</h2>
<p>当涉及业务相关的单元测试, 此时</p>
<ol>
<li>用例复杂, 输入可能是多层嵌套的struct, 某一层的某个变量的值影响输出</li>
<li>用例数量多, 某个函数可能有5个以上的用例</li>
</ol>
<p>由于table-driven的表达能力有限:</p>
<ol>
<li>如何复用输入结构体? (当前情况开发会复制粘贴过去改)</li>
<li><code>name</code>过于简单不被重视, 导致单元测试失败时难以快速阅读</li>
</ol>
<p>最终带来的问题是:</p>
<ol>
<li>单个用例构造复杂, 可能是十几行甚至是几十行;</li>
<li>用例和用例之间没有复用, 基本基于复制粘贴;</li>
<li>难以区分用例之间的差异</li>
<li>用例过多导致难以维护, 不能明确知道每个用例的目的, 用例和用例之间的差别;</li>
<li>单个测试集过大, 可能有几百行测试代码;</li>
</ol>
<p>难以阅读/难以理解/难以维护</p>
<h2 id="ginkgo解决什么问题">Ginkgo解决什么问题?</h2>
<p>优势:</p>
<ul>
<li>丰富的表达能力</li>
<li>复用: 减少了大量重复代码, 复用</li>
<li>让注意力更集中, 只关注每个用例的差异点</li>
<li>减少了认知负担, 每个case有明确的描述/上下文/差异</li>
<li>更好维护</li>
</ul>
<p>注意, 实践中:</p>
<ul>
<li>使用 <code>Describe</code>和<code>Context</code>描述/表示行为和逻辑, 以及层次结构</li>
<li>使用 <code>It</code> 描述及放置每一个用例</li>
<li>利用<code>BeforeEach</code>构建每个用例(<code>It</code>)用到的公共内容(setup); 利用<code>AfterEach</code>执行回收动作(teardown). 此时<code>BeforeEach</code>用到闭包, 构建闭包变量/类型声明等, 在每个<code>It</code>中可以复用到</li>
<li>使用testify 替换ginkgo的Matcher <a href="https://onsi.github.io/ginkgo/#using-other-matcher-libraries">Using Other Matcher Libraries</a>
<ul>
<li>
<ol>
<li>减少目前迁移切换的工作量</li>
</ol>
</li>
<li>
<ol start="2">
<li>testify语法更加简洁直接, ginkgo matcher语法略啰嗦多敲好多字</li>
</ol>
</li>
</ul>
</li>
</ul>
<p>请先阅读官方文档之后, 再开始阅读以下内容</p>
<h3 id="1-基于ginkgo的table-driven">1. 基于ginkgo的table-driven</h3>
<p>使用场景: util类的简单测试, input和expect都比较简单</p>
<p>使用</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">util_test</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;fmt&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;strconv&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;testing&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="p">.</span> <span class="s">&#34;github.com/onsi/ginkgo&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="p">.</span> <span class="s">&#34;github.com/onsi/ginkgo/extensions/table&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;github.com/stretchr/testify/assert&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="s">&#34;example/pkg/util&#34;</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="kd">var</span> <span class="nx">_</span> <span class="p">=</span> <span class="nf">Describe</span><span class="p">(</span><span class="s">&#34;String&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nf">Describe</span><span class="p">(</span><span class="s">&#34;TruncateBytes&#34;</span><span class="p">,</span> <span class="kd">func</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">s</span> <span class="p">=</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;helloworld&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="nf">DescribeTable</span><span class="p">(</span><span class="s">&#34;TruncateBytes cases&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">expected</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">truncatedSize</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="nf">GinkgoT</span><span class="p">(),</span> <span class="nx">expected</span><span class="p">,</span> <span class="nx">util</span><span class="p">.</span><span class="nf">TruncateBytes</span><span class="p">(</span><span class="nx">s</span><span class="p">,</span> <span class="nx">truncatedSize</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="nf">Entry</span><span class="p">(</span><span class="s">&#34;truncated size less than real size&#34;</span><span class="p">,</span> <span class="p">[]</span><span class="nb">byte</span><span class="p">(</span><span class="s">&#34;he&#34;</span><span class="p">),</span> <span class="mi">2</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">			<span class="nf">Entry</span><span class="p">(</span><span class="s">&#34;truncated size equals to real size&#34;</span><span class="p">,</span> <span class="nx">s</span><span class="p">,</span> <span class="mi">10</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">			<span class="nf">Entry</span><span class="p">(</span><span class="s">&#34;truncated size greater than real size&#34;</span><span class="p">,</span> <span class="nx">s</span><span class="p">,</span> <span class="mi">20</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 class="p">})</span>
</span></span></code></pre></div><h3 id="2-基于闭包beforeeachit的复用">2. 基于闭包/BeforeEach/It的复用</h3>
<p>使用场景: 复杂的输入</p>
<p><a href="https://onsi.github.io/ginkgo/#extracting-common-setup-beforeeach">Extracting Common Setup: BeforeEach</a></p>
<p>简单复用</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl">		<span class="nf">Describe</span><span class="p">(</span><span class="s">&#34;WithoutResourceType&#34;</span><span class="p">,</span> <span class="kd">func</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">a</span> <span class="nx">types</span><span class="p">.</span><span class="nx">Action</span>
</span></span><span class="line"><span class="cl">			<span class="nf">BeforeEach</span><span class="p">(</span><span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="nx">a</span> <span class="p">=</span> <span class="nx">types</span><span class="p">.</span><span class="nf">NewAction</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="nf">It</span><span class="p">(</span><span class="s">&#34;true&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="nx">assert</span><span class="p">.</span><span class="nf">True</span><span class="p">(</span><span class="nf">GinkgoT</span><span class="p">(),</span> <span class="nx">a</span><span class="p">.</span><span class="nf">WithoutResourceType</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="nf">It</span><span class="p">(</span><span class="s">&#34;true, empty ResourceType&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="nx">a</span><span class="p">.</span><span class="nx">Attribute</span><span class="p">.</span><span class="nf">SetResourceTypes</span><span class="p">([]</span><span class="nx">types</span><span class="p">.</span><span class="nx">ActionResourceType</span><span class="p">{})</span>
</span></span><span class="line"><span class="cl">				<span class="nx">assert</span><span class="p">.</span><span class="nf">True</span><span class="p">(</span><span class="nf">GinkgoT</span><span class="p">(),</span> <span class="nx">a</span><span class="p">.</span><span class="nf">WithoutResourceType</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="nf">It</span><span class="p">(</span><span class="s">&#34;false&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="nx">a</span><span class="p">.</span><span class="nx">Attribute</span><span class="p">.</span><span class="nf">SetResourceTypes</span><span class="p">([]</span><span class="nx">types</span><span class="p">.</span><span class="nx">ActionResourceType</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="nx">System</span><span class="p">:</span> <span class="s">&#34;test&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">						<span class="nx">Type</span><span class="p">:</span>   <span class="s">&#34;test&#34;</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 class="nx">assert</span><span class="p">.</span><span class="nf">False</span><span class="p">(</span><span class="nf">GinkgoT</span><span class="p">(),</span> <span class="nx">a</span><span class="p">.</span><span class="nf">WithoutResourceType</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></span></code></pre></div><p>复杂的复用</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl">	<span class="nf">Describe</span><span class="p">(</span><span class="s">&#34;GetPolicesAttrKeys&#34;</span><span class="p">,</span> <span class="kd">func</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">resource</span> <span class="o">*</span><span class="nx">types</span><span class="p">.</span><span class="nx">Resource</span>
</span></span><span class="line"><span class="cl">		<span class="kd">var</span> <span class="nx">policies</span> <span class="p">[]</span><span class="nx">types</span><span class="p">.</span><span class="nx">AuthPolicy</span>
</span></span><span class="line"><span class="cl">		<span class="nf">BeforeEach</span><span class="p">(</span><span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">resource</span> <span class="p">=</span> <span class="o">&amp;</span><span class="nx">types</span><span class="p">.</span><span class="nx">Resource</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="nx">System</span><span class="p">:</span>    <span class="s">&#34;test&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">				<span class="nx">Type</span><span class="p">:</span>      <span class="s">&#34;host&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">				<span class="nx">ID</span><span class="p">:</span>        <span class="s">&#34;1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">				<span class="nx">Attribute</span><span class="p">:</span> <span class="kc">nil</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="nx">policies</span> <span class="p">=</span> <span class="p">[]</span><span class="nx">types</span><span class="p">.</span><span class="nx">AuthPolicy</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="nf">It</span><span class="p">(</span><span class="s">&#34;fail&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">errExpr</span> <span class="o">:=</span> <span class="s">`[{&#34;system&#34;: &#34;test&#34;, &#34;type&#34;: &#34;host&#34;, &#34;expression&#34;:
</span></span></span><span class="line"><span class="cl"><span class="s">{&#34;OR&#34;: {&#34;content&#34;: [{&#34;NotExists&#34;: {&#34;id&#34;: []}}]}}}]`</span>
</span></span><span class="line"><span class="cl">			<span class="nx">policies</span> <span class="p">=</span> <span class="p">[]</span><span class="nx">types</span><span class="p">.</span><span class="nx">AuthPolicy</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="nx">Expression</span><span class="p">:</span> <span class="nx">errExpr</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 class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">GetPolicesAttrKeys</span><span class="p">(</span><span class="nx">resource</span><span class="p">,</span> <span class="nx">policies</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="nx">assert</span><span class="p">.</span><span class="nf">Error</span><span class="p">(</span><span class="nf">GinkgoT</span><span class="p">(),</span> <span class="nx">err</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></code></pre></div><p>with patch</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl">	<span class="nf">Describe</span><span class="p">(</span><span class="s">&#34;FillSubjectDetail&#34;</span><span class="p">,</span> <span class="kd">func</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">r</span> <span class="o">*</span><span class="nx">request</span><span class="p">.</span><span class="nx">Request</span>
</span></span><span class="line"><span class="cl">		<span class="kd">var</span> <span class="nx">ctl</span> <span class="o">*</span><span class="nx">gomock</span><span class="p">.</span><span class="nx">Controller</span>
</span></span><span class="line"><span class="cl">		<span class="kd">var</span> <span class="nx">patches</span> <span class="o">*</span><span class="nx">gomonkey</span><span class="p">.</span><span class="nx">Patches</span>
</span></span><span class="line"><span class="cl">		<span class="nf">BeforeEach</span><span class="p">(</span><span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">ctl</span> <span class="p">=</span> <span class="nx">gomock</span><span class="p">.</span><span class="nf">NewController</span><span class="p">(</span><span class="nf">GinkgoT</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">			<span class="nx">r</span> <span class="p">=</span> <span class="nx">request</span><span class="p">.</span><span class="nf">NewRequest</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="nf">AfterEach</span><span class="p">(</span><span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">ctl</span><span class="p">.</span><span class="nf">Finish</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">			<span class="k">if</span> <span class="nx">patches</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="nx">patches</span><span class="p">.</span><span class="nf">Reset</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="nf">It</span><span class="p">(</span><span class="s">&#34;pip.GetSubjectPK fail&#34;</span><span class="p">,</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">patches</span> <span class="p">=</span> <span class="nx">gomonkey</span><span class="p">.</span><span class="nf">ApplyFunc</span><span class="p">(</span><span class="nx">pip</span><span class="p">.</span><span class="nx">GetSubjectPK</span><span class="p">,</span> <span class="kd">func</span><span class="p">(</span><span class="nx">_type</span><span class="p">,</span> <span class="nx">id</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="nx">pk</span> <span class="kt">int64</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">				<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;get subject_pk fail&#34;</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="nx">err</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">FillSubjectDetail</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">			<span class="nx">assert</span><span class="p">.</span><span class="nf">Error</span><span class="p">(</span><span class="nf">GinkgoT</span><span class="p">(),</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">			<span class="nx">assert</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nf">GinkgoT</span><span class="p">(),</span> <span class="nx">err</span><span class="p">.</span><span class="nf">Error</span><span class="p">(),</span> <span class="s">&#34;get subject_pk fail&#34;</span><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></span><span class="line"><span class="cl"><span class="p">})</span>
</span></span></code></pre></div><h2 id="ginkgo-cli">ginkgo cli</h2>
<p><a href="https://onsi.github.io/ginkgo/#the-ginkgo-cli">ginkgo cli文档</a></p>
<p>bootstrap &amp; generate</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">$ <span class="nb">cd</span> pkg/util
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 生成一个模块的 {module_name}_suite_test.go</span>
</span></span><span class="line"><span class="cl">$ ginkgo bootstrap
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 生成模块下某个文件 {file_name}_test.go</span>
</span></span><span class="line"><span class="cl">$ ginkgo generate <span class="nb">set</span>
</span></span></code></pre></div><p>run</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">$ ginkgo
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 带tags和gcflags (如果做了mock)</span>
</span></span><span class="line"><span class="cl">$ ginkgo -tags<span class="o">=</span>jsoniter -gcflags<span class="o">=</span><span class="nv">all</span><span class="o">=</span>-l
</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">$ ginkgo -r -v -cover
</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">$ ginkgo  -r pkg/util pkg/cache
</span></span></code></pre></div><p>确认覆盖率 <code>make test &amp;&amp; make cov</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="c"># in Makefile
</span></span></span><span class="line"><span class="cl"><span class="c"></span><span class="nf">test</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">	go <span class="nb">test</span> -mod<span class="o">=</span>vendor -gcflags<span class="o">=</span><span class="nv">all</span><span class="o">=</span>-l <span class="k">$(</span>shell go list ./... <span class="p">|</span> grep -v mock <span class="p">|</span> grep -v docs<span class="k">)</span> -covermode<span class="o">=</span>count -coverprofile .coverage.cov
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">cov</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">	go tool cover -html<span class="o">=</span>.coverage.cov
</span></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>Better Code: 使用property</title>
			<link>https://wklken.me/posts/2019/10/07/better-code-1-use-property.html</link>
			<pubDate>Mon, 07 Oct 2019 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2019/10/07/better-code-1-use-property.html</guid>
			<description>introduction @property 是Python的一个内置函数, 通过property, 我们可以获得更好的可读性/可维护性 屏蔽细节, 对于外部使用者, 不需要知道更多的细节! 可</description>
			<content type="html"><![CDATA[<h2 id="introduction">introduction</h2>
<p><code>@property</code> 是Python的一个内置函数, 通过property, 我们可以获得更好的可读性/可维护性</p>
<ul>
<li>屏蔽细节, 对于外部使用者, 不需要知道更多的细节!</li>
<li>可读性更好, 调用方只有简单直接的逻辑, 而对象实体中包含了具体处理逻辑</li>
<li>可维护性更好, 细节不会被<code>扩散</code>到其他地方, 后续变更修改更容易</li>
</ul>
<h2 id="refactor">refactor</h2>
<p>代码坏味道:</p>
<ul>
<li>当你发现调用处, 每个地方获取到返回值后, 都做了一模一样的处理;</li>
<li>变更属性的格式/分隔符等细节, 发现需要修改多个地方代码</li>
<li>调用者需要感知到属性的当前值<code>[1, 2, 3]</code>(type: list) 以及原始值<code>1,2,3</code>(type: str)</li>
</ul>
<p>使用property之后:</p>
<ul>
<li>所有调用处, 没有重复代码</li>
<li>变更属性的格式/分隔符等细节, 只需要修改一个地方</li>
<li>调用者只知道属性的当前值<code>[1, 2, 3]</code></li>
</ul>
<h2 id="usage">usage</h2>
<p>思路: 将属性通用的处理内化, <code>fat model</code>对外屏蔽细节, 避免了使用者的冗余代码以及过多的认知负担;</p>
<ul>
<li>side effect: 可以在<code>set/get/delete</code> 操作时, 额外进行一些处理, 例如校验, 做一些额外变更等(存在副作用)</li>
<li>adapter: 也可以作为类似adapter, 组合或者拼装出对调用者更为友好的做法</li>
<li>封装/facade: 对于类/对象实例, 更多的是起到<code>facade</code> 模式的作用, 将更多的细节封装, 对外屏蔽, 只暴露有限的内容</li>
</ul>
<h4 id="1-属性校验">1. 属性校验</h4>
<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">Student</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@property</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">score</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_score</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@score</span><span class="o">.</span><span class="n">setter</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">score</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</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">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="nb">int</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s1">&#39;score must be an integer!&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">value</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">value</span> <span class="o">&gt;</span> <span class="mi">100</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s1">&#39;score must between 0 ~ 100!&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_score</span> <span class="o">=</span> <span class="n">value</span>
</span></span></code></pre></div><h4 id="2-限制只读">2. 限制只读</h4>
<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">Student</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@property</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">birth</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_birth</span>
</span></span></code></pre></div><h4 id="3-拼装组合出额外的属性">3. 拼装组合出额外的属性</h4>
<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">Staff</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@property</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">email</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="sa">f</span><span class="s1">&#39;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">username</span><span class="si">}</span><span class="s1">@</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">domain</span><span class="si">}</span><span class="s1">&#39;</span>
</span></span></code></pre></div><h4 id="4-类型格式转换">4. 类型/格式转换</h4>
<p>下面示例是django 中的一种用法, 在db中字段名为<code>maintainers</code>, 是一个字符串, 但是对于模型使用者, <code>maintainers</code>是一个列表, 通过property, 屏蔽了字段类型/分隔符等细节</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">API</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">      <span class="n">_maintainers</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">db_column</span><span class="o">=</span><span class="s1">&#39;maintainers&#39;</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">1024</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="nd">@property</span>
</span></span><span class="line"><span class="cl">      <span class="k">def</span> <span class="nf">maintainers</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</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">self</span><span class="o">.</span><span class="n">_maintainers</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">              <span class="k">return</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_maintainers</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;;&#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">@maintainers</span><span class="o">.</span><span class="n">setter</span>
</span></span><span class="line"><span class="cl">      <span class="k">def</span> <span class="nf">maintainers</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]):</span>
</span></span><span class="line"><span class="cl">          <span class="bp">self</span><span class="o">.</span><span class="n">_maintainers</span> <span class="o">=</span> <span class="s2">&#34;;&#34;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</span></span></code></pre></div><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">A</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@property</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">data</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@data</span><span class="o">.</span><span class="n">setter</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">d</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
</span></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>摘录-架构整洁之道</title>
			<link>https://wklken.me/posts/2019/10/07/clean-architecture.html</link>
			<pubDate>Mon, 07 Oct 2019 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2019/10/07/clean-architecture.html</guid>
			<description>P1: 概述 好的架构 =&amp;gt; 降低 项目构建与维护的人力成本 复杂度 =&amp;gt; 成本 大部分程序员的误区: 他们的工作是且仅是按照需求文档编写代码, 并且修复任何bug. 这是</description>
			<content type="html"><![CDATA[<p><img src="/imgs/books/clean-architecture.jpg" alt=""></p>
<h2 id="p1-概述">P1: 概述</h2>
<ul>
<li>好的架构 =&gt; <code>降低</code> 项目构建与维护的人力成本</li>
<li>复杂度 =&gt; 成本</li>
<li>大部分程序员的误区: 他们的工作是且仅是按照需求文档编写代码, 并且修复任何bug. 这是行为价值, 紧急而不重要; 架构价值, 重要不紧急.</li>
</ul>
<h2 id="p2-从基础构件开始-编程范式">P2: 从基础构件开始: 编程范式</h2>
<ul>
<li>结构化: 模块降解拆分</li>
<li>面向对象: 封装(可见/不可见), 继承, 多台(IoC依赖反转)</li>
<li>函数式: 隔离可变, 不可变</li>
</ul>
<h2 id="p3-设计原则">P3: 设计原则</h2>
<p>solid原则: 告诉我们如何将数据和函数组织成类, 以及如何将这些类链接起来成为程序</p>
<p>构建中层结构的主要目标:</p>
<ul>
<li>使软件可容忍被改动</li>
<li>使软件更容易被理解</li>
<li>构建可在多个软件系统中复用的组件</li>
</ul>
<h4 id="1-srp-单一职责原则">1. SRP 单一职责原则</h4>
<p>康威定律的一个推论: 一个软件系统的最佳结构高度依赖于开发这个系统的组织的内部结构. 每个软件模块都有且仅有一个需要被改变的理由</p>
<ul>
<li>面向底层实现细节: 每个模块都应该只做一件事; 确保一个函数只完成一个功能</li>
<li>任何一个软件模块都应该只对某一类行为者负责</li>
</ul>
<p>不同行为者所依赖的代码必须要分开; 需要将服务不同行为者的代码进行切分;</p>
<h4 id="2-ocp-开闭原则">2. OCP 开闭原则</h4>
<p>如果软件系统想要更容易被改变, 那么其设计就必须允许新增代码来修改系统的行为, 而非只能拷修改原来的代码</p>
<blockquote>
<p>设计良好的计算机软件应该易于扩展, 同时抗拒修改; 应该在不需要修改的前提下就可以轻易被扩展</p>
</blockquote>
<ul>
<li>设计原则: 如果A组件不想被B组件上发生的修改所影响, 那么就应该让B组件依赖于A组件</li>
<li>软件系统不应该依赖于其不直接使用的组件</li>
</ul>
<h4 id="3-lsp-里氏替换原则">3. LSP 里氏替换原则</h4>
<p>如果想用可替换的组件来构建软件系统, 那么这些组件就必须遵循同一个约定, 以便让这些组件可以相互替换</p>
<ul>
<li>LSP 可以且应该被应用于软件架构层面, 因为一旦违背了可替换性, 该系统架构就不得不为此增添大量复杂的应对机制</li>
</ul>
<h4 id="4-isp-接口隔离原则">4. ISP 接口隔离原则</h4>
<p>在设计中避免不必要的依赖</p>
<p>在一般情况下, 任何层次的软件设计如果依赖于不需要的东西, 都会是有害的.</p>
<h4 id="5-dip-依赖反转原则">5. DIP 依赖反转原则</h4>
<p>高层策略性的代码不应该依赖于底层细节的代码. 应该反过来</p>
<ul>
<li>如果想要设计一个灵活的系统, 在源代码层次的依赖关系就应该多引用抽象类型, 而非具体实现</li>
<li>稳定的抽象层: 接口比实现更稳定</li>
</ul>
<p>该设计原则具体的编码守则:</p>
<ul>
<li>应在代码中多使用抽象接口, 尽量避免使用哪些多变的具体实现类. 适用于所有编程语言; 对象的创建过程也应该受到严格限制, 通常会使用抽象工厂模式</li>
<li>不要在具体实现类上创建衍生类.</li>
<li>不要覆盖(override)包含具体实现的函数</li>
<li>应避免在代码中写入任何具体实现相关的名字, 或者其他容易变动的事物的名字</li>
</ul>
<h2 id="p4-组件构建原则">P4: 组件构建原则</h2>
<ul>
<li>组件是软件的部署单元, 是整个软件系统在部署过程中可以独立完成部署的最小实体.</li>
</ul>
<h3 id="41-组件聚合原则">4.1 组件聚合原则</h3>
<p><img src="/imgs/books/clean-architecture-1.png" alt=""></p>
<h4 id="1-rep-复用发布等同原则">1. REP: 复用/发布等同原则</h4>
<ul>
<li>软件复用的最小粒度应等同于其发布的最小粒度</li>
</ul>
<h4 id="2-ccp-共同闭包原则">2. CCP: 共同闭包原则</h4>
<ul>
<li>我们应该将那些会同时修改, 并且为相同目的而修改的类放到同一个组件中, 而将不会同时修改, 并且不会为了相同目的而修改的那些类放到不同的组件中</li>
<li>一个组件不应该同时存在多个变更原因</li>
<li>对于大部分程序来说, 可维护性的重要性要远远高于可复用性</li>
<li>我们应该将变更原因不同的类放入不同的组件中</li>
</ul>
<h4 id="3-crp-共同复用原则">3. CRP: 共同复用原则</h4>
<ul>
<li>不要强迫一个组件的用户依赖他们不需要的东西</li>
<li>帮助决策类和模块归属于哪一个组件的原则;</li>
<li>将经常共同复用的类和模块放到同一个组件中</li>
</ul>
<h3 id="42-组件耦合">4.2 组件耦合</h3>
<ul>
<li>无依赖环原则: 组件依赖关系图中不应该出现环; (也不应该出现<code>反向依赖</code>)</li>
<li>自下而上的设计: 组件结构图是不可能自上而下被设计出来的. 必须随着软件系统的变化而变化和扩张, 而不可能在系统构建的最初就被完美设计出来</li>
<li>稳定依赖原则: 依赖关系必须要指向更<code>稳定</code>的方向. 预期会经常变更的组件都不应该被一个难以修改的组件所依赖, 否则这个多变的组件也将会变得难以被修改. 稳定性指标=出依赖/(出依赖+入依赖)</li>
<li>稳定性依赖原则SDP: 让每个组件的I指标都必须大于其所依赖组件的I指标. 按依赖方向递减.</li>
<li>稳定抽象原则SAP: 一个组件的抽象化程度应该预期稳定性保持i一致.</li>
<li>稳定抽象原则: 为组件的稳定性于它的抽象性程度建立了一种关联. 1) 要求稳定的组件同时应该是抽象的, 这样它的稳定性就不会影响到扩展性. 2)一个不稳定的组件应该包含具体的实现代码, 这样它的不稳定性就可以通过具体的代码被轻易修改. 如果一个组件要成为稳定组件, 那么它就应该由接口和抽象类组成, 以便将来做扩展</li>
</ul>
<p><img src="/imgs/books/clean-architecture-2.png" alt=""></p>
<ul>
<li>抽象与稳定</li>
<li>痛苦区: 稳定, 难以被修改和扩展, 这个组件不是抽象的(典型案例: 数据库表结构schema, 工具型类库)</li>
<li>无用区: 无限抽象, 没有被其他组件依赖, 往往无法被使用;(典型案例: 历史原因前人写的没有实现的抽象类)</li>
<li>避开这两个区域, 让组件落在主序列线</li>
</ul>
<h2 id="p5-软件架构">P5: 软件架构</h2>
<blockquote>
<p>软件架构师自身需要是程序员, 并且必须一直坚持i做一线程序员, 绝对不要听从那些说应该让软件架构师从代码中解放出来以专心解决高阶问题的伪建议</p>
</blockquote>
<ul>
<li>
<p>如果不亲身承受因系统设计而带来的麻烦, 就体会不到设计不佳所带来的痛苦, 接着就会逐渐迷失正确的设计方向</p>
</li>
<li>
<p>如果想设计一个便于推进各项工作的系统, 其策略就是要在设计中尽可能长时间地保留尽可能多的选项</p>
</li>
<li>
<p>软件架构设计的主要目标是支撑软件系统的全生命周期, 设计良好的架构可以让系统便于理解, 易于修改, 方便维护, 并且能轻松部署. 软件架构的终极目标就是最大化程序员的生产力, 同时最小化系统的总运营成本.</p>
</li>
<li>
<p>用例: 一个系统的架构必须能支持其自身的设计意图. 非常直观地支持这类应用可能会涉及的所有用例</p>
</li>
<li>
<p>我们无法预知全部的用例: 好在架构师应该还是之道整个系统的基本设计意图的</p>
</li>
<li>
<p>软件架构设计本身就是一门划分边界的艺术.</p>
</li>
<li>
<p>边界的作用就是将软件分隔成各种元素, 一遍约束边界两侧之间的依赖关系</p>
</li>
<li>
<p>一个系统中最消耗人力资源的是什么? 系统中存在的耦合&ndash;尤其是那些过早做出的, 不成熟的决策所导致的耦合. 那些决策与系统业务需求(用例)无关的, 细节性的决策(框架/数据库/web服务器/工具库/依赖注入等)应该是辅助的, 可以被推迟.</p>
</li>
<li>
<p>一个设计良好的系统架构不应该依赖于这些细节, 而应该尽可能地推迟i这些细节性的决策, 并致力于将这种推迟i所产生的影响降到最低.</p>
</li>
<li>
<p>软件架构设计的工作重点之一就是: 将策略彼此分离, 然后将它们按照变更的方式进行重新分组. 维度: 变更原因, 时间和层次</p>
</li>
<li>
<p>业务实体 Entity: 关键业务逻辑和关键业务数据是紧密相关的, 很适合放在同一个对象中处理</p>
</li>
<li>
<p>业务逻辑: 应该是系统中最独立, 复用性最高的代码</p>
</li>
<li>
<p>架构设计的核心目标: 一个良好的架构设计应该围绕着用例展开. 尽可能地允许用户推迟和延后应该采用什么框架/数据库/web服务等. 良好的架构设计应该只关注用例, 并能将它们与其他周边的因素隔离.</p>
</li>
</ul>
<p><img src="/imgs/books/clean-architecture-3.jpg" alt=""></p>
<ul>
<li>外层代表的是机制, 内层代表的是策略</li>
<li>依赖关系规则: 源码中的依赖关系必须只指向同心圆的内层, 即由低层机制指向高层策略</li>
</ul>
<h2 id="p6-实现细节">P6: 实现细节</h2>
<ul>
<li>数据库/Web/应用程序框架 都是实现细节!</li>
</ul>
<hr>
<p>好的架构 how:</p>
<ul>
<li>对项目有整体的认识</li>
<li>对需求有足够的了解</li>
<li>对未来有所预见</li>
<li>意识到现有系统的问题, 复杂度的来源</li>
<li>知道如何去解决这个问题: 重新设计/重构/优化</li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>读书笔记-遗留系统重建实战</title>
			<link>https://wklken.me/posts/2019/08/11/re-engineering-legacy-software.html</link>
			<pubDate>Sun, 11 Aug 2019 00:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2019/08/11/re-engineering-legacy-software.html</guid>
			<description>遗留项目: 任何已存在的, 难以维护或难以扩展的项目 这本书总体来说还不错, 值得一读 第一章: 了解遗留项目中的挑战 特征: 老旧 庞大 继承而来 文档不完善 遗</description>
			<content type="html"><![CDATA[<p><img src="/imgs/books/re-engineering-legacy-software.jpg" alt="Re-Engineering Legacy software"></p>
<blockquote>
<p>遗留项目: 任何已存在的, 难以维护或难以扩展的项目</p>
</blockquote>
<p>这本书总体来说还不错, 值得一读</p>
<h2 id="第一章-了解遗留项目中的挑战">第一章: 了解遗留项目中的挑战</h2>
<p>特征:</p>
<ul>
<li>老旧</li>
<li>庞大</li>
<li>继承而来</li>
<li>文档不完善</li>
</ul>
<p>遗留代码:</p>
<ul>
<li>没有测试和无法测试的代码: 难以<code>确认</code></li>
<li>不灵活的代码: 难以修改和扩展</li>
<li>被技术债拖累的代码:</li>
</ul>
<p>遗留基础设施:</p>
<ul>
<li>开发环境: 难以搭建/搭建成本高, 构件工具/项目中构件脚本/文档中大量的手工步骤</li>
<li>过时的依赖: 没有定期维护, 带来了后期升级维护的成本和风险</li>
<li>异构环境: 不同环境不能保持一致, 这种环境不一致带来的风险(无法模拟生产环境)</li>
</ul>
<p>遗留文化:</p>
<ul>
<li>害怕变化: 缺少信息 1)哪些功能不再使用可以移除? 2)哪些bug可以被安全的修复 3) 改动之前应该咨询哪些用户? -&gt; 结果: <code>保持现状是最安全的选择</code>; 纯粹的风险, 忽略了带来的好处;</li>
<li>知识仓库: 知识的匮乏; 团队知识的共享和传承; 1) 缺乏面对面沟通 2)代码是我的 3)忙碌的面孔</li>
</ul>
<p><img src="/imgs/books/re-engineering-legacy-software-1.png" alt="Re-Engineering Legacy software"></p>
<h2 id="第二章-找到起点">第二章: 找到起点</h2>
<p>恐惧: 遗留系统带来的不确定性; 对未知的恐惧: <code>更改了一行代码可能会破坏一些根本不相关的东西</code></p>
<p>探索性重构: 尝试重命名方法, 在两个类之间移动方法, 引入新的接口, 添加注释等等<code>小变更</code></p>
<p>好处:</p>
<ul>
<li>增加你对代码的理解, 了解越多, 掌握的信息就越多, 恐惧越少;</li>
<li>提高的代码的可读性;</li>
<li>在版本控制系统/IDE/编译器/其他开发人员等帮助下, 更简单且安全</li>
</ul>
<p>特征测试: 验证系统指定部件当前的行为</p>
<p>收集软件的有用数据:</p>
<ul>
<li>静态代码分析工具 / bug查找工具</li>
<li>性能: 性能测试+生产环境性能监控</li>
<li>错误计数: 例如500错误</li>
<li>对常见的任务计时: 1) 从头开始搭建开发环境时间 2) 发布或部署项目所花的时间 3) 修复一个bug的平均时间</li>
<li>版本库中修改最多的文件</li>
</ul>
<p>静态分析工具</p>
<ul>
<li>FindBugs: 查找java中潜在的bug -&gt; 修复关键bug</li>
<li>PMD: 是否遵循最佳实践 -&gt; 修复</li>
<li>CheckStyle: 修复风格问题, 提高可读性</li>
</ul>
<h2 id="第三章-准备重构">第三章: 准备重构</h2>
<blockquote>
<p>重构时始终记得组织的目标</p>
</blockquote>
<p>两种角色:</p>
<ul>
<li>传统主义者: 不要碰任何东西, 强烈反对任何形式变化的开发者; 认为重构会带来不必要的风险;</li>
<li>反传统主义者: 所有东西都要重写; 厌恶遗留代码; 热衷于提高代码质量, 但是可能过于激进;</li>
</ul>
<p><img src="/imgs/books/re-engineering-legacy-software-2.png" alt="Re-Engineering Legacy software"></p>
<p>需要通过沟通, 确保团队每个人对项目有相同的理解, 统一目标及计划;</p>
<blockquote>
<p>获得组织的批准</p>
</blockquote>
<p>选择重构的目标: 三个维度评估, 难度/风险/价值</p>
<ul>
<li>容易实现的目标: 风险=低, 难度=低</li>
<li>痛点: 价值=高</li>
</ul>
<p>重构还是重写?</p>
<ul>
<li>不应该重写的情况: 1)风险 2)开销 3)任务总是超出预期时间</li>
<li>重写的好处: 自由/可测试性</li>
<li>重写的必要条件: 1) 尝试过重构并且失败了 2) 编程范型的转变</li>
<li>第三种方式: 增量重写, 将重写分成若干个较小的阶段, 每个阶段都应该提供业务价值, 应该可以在任何给定阶段之后停止项目, 并且仍然能获得一些好处.</li>
</ul>
<h2 id="第四章-重构">第四章: 重构</h2>
<p>有纪律的重构:</p>
<ul>
<li>把重构和其他的工作分开</li>
<li>依靠IDE的refactor!</li>
<li>依靠版本控制系统</li>
</ul>
<p>常见的遗留代码的特征和重构</p>
<p>移除陈旧代码:</p>
<ul>
<li>被注释掉的代码</li>
<li>死代码: 绝对不会被执行的代码</li>
<li>僵尸代码: 那些功能已经去掉或者不再存在</li>
<li>过期代码: 时间依赖/ab test/活动等</li>
</ul>
<p>移除有毒的测试:</p>
<ul>
<li>没有测试任何东西</li>
<li>脆弱的测试:</li>
<li>随机失败的测试</li>
</ul>
<h2 id="第五章-重搭架构">第五章: 重搭架构</h2>
<p>重搭架构是比方法和类更高级别的重构. 可以把它想像成大型的重构, 将大型应用拆分为多模块</p>
<p>好处:</p>
<ul>
<li>通过模块化内建质量</li>
<li>良好的涉及保障可维护性</li>
<li>通过独立达到自治</li>
</ul>
<p>涉及:</p>
<ul>
<li>单体应用拆分为多模块</li>
<li>前后端分离</li>
<li>微服务</li>
</ul>
<h2 id="其他">其他</h2>
<p>开发环境自动化:</p>
<ul>
<li>一个好的README</li>
<li>vagrant/ansible/docker等实现开发环境自动化</li>
</ul>
<p>多环境一致性: 测试环境/预发布环境/生产环境</p>
<p>开发/构建/部署过程自动化</p>
<p>Linux 项目持续成功: 公开和坦诚沟通的文化, <strong>重视CodeReview</strong></p>
<h2 id="主题">主题</h2>
<h4 id="1-源代码并不是项目的全部">1. 源代码并不是项目的全部</h4>
<h4 id="2-信息不能是自由的">2. 信息不能是自由的</h4>
<p>文档: 信息丰富/易于编写/易于发现/易于阅读/可信赖</p>
<p>促进沟通: 代码评审/结对编程/技术访谈/向其他团队展示你的项目/黑客日</p>
<h4 id="3-工作是做不完的">3. 工作是做不完的</h4>
<p>维护代码库质量十一个永无止境的任务; 需要保持警惕并在质量问题一出现就解决它</p>
<p>定期进行代码评审 / 破窗理论</p>
<h4 id="4-自动化一切">4. 自动化一切</h4>
<p>自动化测试/自动化构建/自动化部署等</p>
<ul>
<li>使你的生活更加轻松</li>
<li>是你的接班人的生活更加轻松</li>
</ul>
<h4 id="5-小型为佳">5. 小型为佳</h4>
<p>保持代码库小型轻量;</p>
]]></content>
		</item>
		
		<item>
			<title>[分享]深度工作</title>
			<link>https://wklken.me/posts/2019/07/02/deep-work.html</link>
			<pubDate>Tue, 02 Jul 2019 12:30:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2019/07/02/deep-work.html</guid>
			<description>之前做的一个分享</description>
			<content type="html"><![CDATA[<hr>
<p>之前做的一个分享</p>
<hr>
<object data="/extra/share/deep-work.pdf" type="application/pdf" width="729" height="525">
    <embed src="/extra/share/deep-work.pdf">
    </embed>
</object>
]]></content>
		</item>
		
		<item>
			<title>你需要更多的思考时间</title>
			<link>https://wklken.me/posts/2019/06/23/you-need-more-time-to-think-along.html</link>
			<pubDate>Sun, 23 Jun 2019 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2019/06/23/you-need-more-time-to-think-along.html</guid>
			<description>目前每天都七点半准时起, 无论是否工作日, 几个月下来, 觉得时间多了好多. 最近三个月阅读时间多了很多, 无他, 只因每天要早起坐班车, 下班也坐班车,</description>
			<content type="html"><![CDATA[<p>目前每天都七点半准时起, 无论是否工作日, 几个月下来, 觉得时间多了好多.</p>
<p>最近三个月阅读时间多了很多, 无他, 只因每天要早起坐班车, 下班也坐班车, 每天有大概一个半小时在车上, 只能用kindle打发时间.</p>
<p>每天比其他同事差不多早了一个小时到公司, 这段时间用来做什么呢?</p>
<p>我的答案是: 不要立刻开始工作</p>
<p>当然, 每个人选择不一样, 可能这一个小时没人打扰, 编码效率会高一点.</p>
<p>但是, 为什么不将这个时间空出来, 做一些其他事情,  这些事情, 可以起到类似杠杆作用, 撬动你的工作, 提升效率, 减少无畏的时间浪费, 以便更好地享受生活呢?</p>
<h2 id="当下">当下</h2>
<p>我们总是被当下的工作所淹没, 即, 注意力被现在的工作抓走. 例如: 昨天有个bug没修, 今天会有个需求会,  下午要和谁对接口,  晚上要上线等等.</p>
<p>而当一天的工作开始后, 你将马不停蹄, 奔波在各种事务中,  忙累了一天之后, 下班回家洗洗睡, 明天继续</p>
<p>你可能某天觉得好累, 厌倦, 生活过成了模式</p>
<h2 id="假设你有更多的时间思考">假设你有更多的时间思考</h2>
<p>所以, 假设你每天有一小时时间, 用来做什么?</p>
<p>思考!</p>
<p>一个人思考!</p>
<p>不管是上班前, 还是下班后, 不管是在公司, 还是回家了, 你都有必要给思考留一些时间, 如果一小时太多, 十五分钟就够了.</p>
<p>思考什么呢? 任何事情,  工作或者生活的问题, 抛出来, 思考, 做一些决策和记录</p>
<p>例如: 工作的风格是不是有需要改进的地方? 昨天与人沟通是不是太过激进了? 今天这个方案有个细节似乎有问题? 今天周五晚上去哪浪?  昨天的bug可能是什么原因?  最近有个手工操作频率很高, 自动化要花多久每次能省多少时间?</p>
<p>注意, 这时候不要开 vim 或者 IDE, 不要开文档等等, 不要在意细节, 思考, 得到结论, 记录, 该做的加入todo list.</p>
<p>例如:</p>
<pre><code>- 将xx操作自动化 =&gt; 加入到trello的近期事项中
- 晚上去xx 吃饭   =&gt; 直接发微信给老婆
- 方案 XX 有问题  =&gt; 记录事项到list, 具体确定时注意
- bug可能原因是:xxx   =&gt; 将猜测记录到对应issues
- xxx需要优化  =&gt; 到对应issue评论
</code></pre>
<p>这些都是很发散的问题, 但是有了安静思考的时间, 可以做出更好的决策, 或者有更好的想法.</p>
<p>每件事情, 都会随着时间推移, 变得更好.</p>
<p>另外, 还可以针对近期工作做下梳理, 以及对今日的todo list做下排期;</p>
<p>然后进入第二步</p>
<h2 id="learn-something">Learn something</h2>
<p>第一步, 花的时间可能10分钟不到, 你可以理清楚很多事情.</p>
<p>那么, 剩下的40-50分钟做什么?</p>
<p>学习!</p>
<p>学什么? 跟项目相关的, 跟工作相关, 跟自己思考问题得到结论相关的内容.</p>
<p>例如, 合作项目对方用的xxx技术, 你从来没有接触过, 昨天接入只是扫了眼文档完成接入, 那么你可以google下这个关键词, 然后开始了解, 甚至深入学习.</p>
<p>例如, 昨天遇到一个报错, 解决了但是没有来得及深究, 这时候可以开始深究</p>
<p>你的经验, 主要来自于你的工作, 以及工作相关的领域. 所以,  需要从这些部分学习, 深入, 得到最大化的成长.</p>
<p>注意, 尽量关注跟工作相关的领域, 那些可能跟你工作相关性很低的领域, 除非0)你在不远的将来会涉及 1) 你想转领域了 2) 你感兴趣或者对你成长有帮助 3) 你对本领域已经很有经验了想尝试下(或许有些激进, 个人观点)</p>
<p>例如, 你是个初级的前端开发, 每日忙于工作, 工作所涉及的技术还没有吃透, 看到机器学习很火想学, 每天花一两个小时在上面, 但是工作中用不到, 你短期内又不可能转,  绝大多数人, 最终是跑了一大堆开源项目的hello world, 走马观花了下文档和各种公司. 那么, 每天的这一两个小时相当于是损耗时间, 作用很小(性价比不高), 即使最终要转, 一样需要重头学习积累.</p>
<p>我的经验是:</p>
<p>跟当前工作相关,  才能事半功倍.  这样或许会有些功利, 但你我以及大部分人都是普通人, 精力有限, 用在刀刃上.</p>
<p>知行合一, 从工作内容出发去拓展, 未来实践的概率高, 对个人成长提升也大.</p>
<p>不说那么多,  40分钟过去, 差不多同事都陆续到了, 你只有几分钟时间, 就要进入战斗模式了</p>
<p>进入战斗模式之前,  建议第三步</p>
<h2 id="每日计划">每日计划</h2>
<p>近两年, 个人一直在使用 trello 作为个人看板</p>
<p>每天在开始工作前, review下今天以及本周需要做的事情, 移动下卡片, 标注下deadline, 备注下注意事项等等</p>
<p>然后, 选择一个, 开始干活.</p>
<hr>
<p>我们总是容易沉溺于繁杂的事务, 而忘了身在何处.</p>
<p>每天独处的思考时间, 可以让我们脱出局中, 而无论是工作还是生活, 都会逐步改善</p>
<p>每天学习, 然后在工作中实践, 才能感受到成长的力量</p>
<p>每日计划, 效率加成</p>
<p>2019-06-23 深圳</p>
]]></content>
		</item>
		
		<item>
			<title>Django项目重构小结</title>
			<link>https://wklken.me/posts/2018/12/06/python-refactor-django-project.html</link>
			<pubDate>Thu, 06 Dec 2018 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2018/12/06/python-refactor-django-project.html</guid>
			<description>由于历史原因, 项目组中存在很多历史悠久的项目. 这类项目, 有着一些共同特征: 历史悠久, 几度经手, 缺乏文档, 维护困难, 模块不合理, 代码坏味道, 版</description>
			<content type="html"><![CDATA[<p>由于历史原因, 项目组中存在很多<code>历史悠久</code>的项目.</p>
<p>这类项目, 有着一些共同特征: 历史悠久, 几度经手, 缺乏文档, 维护困难, 模块不合理, 代码坏味道, 版本老旧等等.</p>
<p>总之, 维护困难.</p>
<p>如果维护需求不多, 基本不动, 这类项目可以<code>保持现状</code>, 稳定运行不出问题就行.</p>
<p>但是, 另一方面, 如果项目需求多, 需要频繁修改, 由于项目的原因导致变更困难/进度缓慢/问题频出, 那么就该考虑进行代码重构了.</p>
<p>梳理了一些django项目重构的事项, 供参考.</p>
<h4 id="1-code-review">1. code review</h4>
<p>将核心代码做个pr, 组织一次线上的<code>code review</code>.</p>
<ul>
<li>不要线下review, 此时代码可能非常庞大, 本身并不适合review</li>
<li>要有本项目历史维护者参与, 也要有非项目维护者的外部人员参与</li>
</ul>
<p>线上reivew一周, 收集大家的comment, 之后做个分类汇总</p>
<h4 id="2-升级django版本">2. 升级django版本</h4>
<p>如果是django1.3及以下, 可以考虑升级到 1.8</p>
<p>做升级, 并做相应的变更, 使得程序能正常运行</p>
<p>可以考虑升级到django2.x(python3)</p>
<h4 id="3-use-cbv-or-drf">3. use CBV or DRF</h4>
<p>具体区别可以参考 <a href="https://simpleisbetterthancomplex.com/article/2017/03/21/class-based-views-vs-function-based-views.html">Class-Based Views vs. Function-Based Views</a></p>
<p>老旧项目, 90%以上都是 FBV(<code>Function-Based-View</code>), 如果项目比较小, 使用FBV可以短平快地搞定需求.</p>
<p>但是如果项目变得比较大了, 可以考虑使用 <a href="https://docs.djangoproject.com/en/2.1/topics/class-based-views/">CBV</a></p>
<p>对于大型项目, 建议使用 DRF(<a href="https://www.django-rest-framework.org/">Django REST framework</a>), 中小型项目不建议引入DRF的原因是, DRF有一定的学习门槛, 比较重, 可能引入不必要的复杂度.</p>
<ul>
<li>微型项目: FBV or CBV</li>
<li>中小型项目: CBV</li>
<li>大型项目: DRF</li>
</ul>
<p>此时会涉及(以下均是django 1.8文档)</p>
<ul>
<li><a href="https://docs.djangoproject.com/en/1.8/ref/class-based-views/">class based views</a></li>
<li><a href="https://docs.djangoproject.com/en/1.8/topics/class-based-views/intro/#decorating-the-class">Decorating the class</a></li>
</ul>
<p>可以:</p>
<ol>
<li>权限控制使用mixin, 去掉了原先的权限控制decorator</li>
<li>定义抽象出自己需要的view, 例如MakoTemplateView / JsonView</li>
<li>合并相关的view</li>
</ol>
<p>流程:　url不动, 将所有FBV改成CBV, 抽象及合并代码, 合并相关view</p>
<pre tabindex="0"><code>├── common
│   ├── __init__.py
│   ├── constants.py
│   ├── context_processors.py
│   ├── decorators.py
│   ├── exceptions.py
│   ├── http.py
│   ├── log.py
│   ├── middlewares
│   │   ├── __init__.py
│   │   └── exception.py
│   ├── mixins
│   │   ├── __init__.py
│   │   └── base.py
│   ├── responses.py
│   ├── tests.py
│   ├── utils.py
│   └── views
│       ├── __init__.py
│       └── mako.py
</code></pre><h4 id="4-分层及模块切分">4. 分层及模块切分</h4>
<p>严格的分层，使得每一层职责清晰；</p>
<blockquote>
<p>thin views, fat models</p>
</blockquote>
<ul>
<li>业务逻辑, 尽量瘦小简短</li>
<li>将view中, 跟model具体对象相关的逻辑, 挪到model的方法中, 或者<code>@property</code>中, 补充一系列<code>porperty/classmethod/staticmethod</code></li>
<li>注意, 不包含<code>{application}.objects.xxxx</code>的ORM操作</li>
</ul>
<blockquote>
<p>　fat managers</p>
</blockquote>
<ul>
<li>将view中, 跟ORM相关的复杂逻辑, 挪到manager中</li>
<li>[拆分出{application}/manager.py]</li>
<li><a href="https://docs.djangoproject.com/en/1.8/topics/db/managers/#django.db.models.Manager">Django Manager</a></li>
</ul>
<blockquote>
<p>use form</p>
</blockquote>
<ul>
<li>使用django form替换掉所有参数处理及校验相关的代码</li>
<li>去除/合并原有的逻辑代码</li>
<li>[拆分出{application}/forms.py]</li>
</ul>
<blockquote>
<p>use enum</p>
</blockquote>
<ul>
<li>replace all the <code>magic number</code> and build the choices for model</li>
<li>[拆分出common/constants.py 和 {application}/constants.py]</li>
<li>common/constants.py 放项目全局常量</li>
<li>{application}/constants.py 放app局部使用常量</li>
</ul>
<blockquote>
<p>add utils helpers</p>
</blockquote>
<ul>
<li>将工具类代码逻辑进行抽离, 减少代码容易</li>
<li>[拆分出common/utils.py 和 {applications}/utils.py]</li>
</ul>
<pre tabindex="0"><code>├── app
│   ├── __init__.py
│   ├── admin.py
│   ├── constants.py
│   ├── forms.py
│   ├── manager.py
│   ├── migrations/
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   ├── utils.py
│   └── views.py
</code></pre><blockquote>
<p>stupid template</p>
</blockquote>
<ul>
<li>将复杂逻辑, 移到后端来</li>
<li>如果使用django原生的模板, 可以善用 <a href="https://docs.djangoproject.com/en/1.8/ref/templates/builtins/">Built-in template tags and filters</a> 以及 自定义<a href="https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/">Custom template tags and filters</a></li>
<li>如果使用mako的, 尽量将数据构造/判断等等, 放在后端搞定, 不要在模板中使用大量的判断逻辑, 模板尽量只渲染</li>
</ul>
<h4 id="5-抽离第三方依赖api-拆分出componentsxxxpy">5. 抽离第三方依赖api [拆分出components/xxx.py]</h4>
<ul>
<li>重灾区: 可能每个地方都有拼url, 调用, 处理返回值, 可能前期就一两个地方, 但是随着时间推移, 会出现腐烂的趋势, 很多地方直接拷贝, 改改用. 需要全部抽出</li>
<li>将原先散落在各处的依赖第三方系统的api调用, 全部抽出到同一个文件中</li>
<li>减少代码冗余</li>
<li>同时, 可以统一处理输入/输出/异常等</li>
<li>后续接口协议变更, 可以做统一变更</li>
</ul>
<pre tabindex="0"><code>├── components
│   ├── __init__.py
│   ├── engine.py
│   ├── login.py
│   └── tests.py
</code></pre><h4 id="6-统一">6. 统一</h4>
<p>重灾区, 由于历史及人员的不同习惯, 往往一个项目会引入非常多的不同的<code>做法</code>去完成同一件事.</p>
<p>在重构的节点, 我们必须将其统一, 否则由于破窗理论, 整体项目会越来越混乱.</p>
<p>原则:　做同一件事，只存在一种方法; 不要存在重复的代码; 不能妥协.</p>
<blockquote>
<p>　常量</p>
</blockquote>
<ul>
<li>有时候, 同一个定义, 可能有几套常量, 每个人自己定义了一套</li>
</ul>
<blockquote>
<p>　utils</p>
</blockquote>
<ul>
<li>重灾区, 不同模块下, 可能每个开发者自己将历史工具类/函数拷贝过来, 而不是使用公共的</li>
<li>可能需求略有区别, 所以公共的工具模块/类, 需要重构支持</li>
</ul>
<blockquote>
<p>　错误码</p>
</blockquote>
<ul>
<li>统一所有错误码</li>
</ul>
<blockquote>
<p>异常</p>
</blockquote>
<ul>
<li>统一异常类</li>
<li>统一异常处理方式/风格</li>
<li>在同一层处理异常:　一种思路, 在middleware中统一处理所有类型异常, 而在view及其之下的调用链中可以减少<code>try-except</code>, 或者发现问题直接raise对应异常</li>
</ul>
<blockquote>
<p>　协议</p>
</blockquote>
<ul>
<li>统一的返回值结构</li>
<li>统一的http错误码/状态码</li>
<li>可能涉及前端变更调用, 但是, 该改动必须改</li>
<li>可以通过自定义view/mixin等方式处理, 也可以对django的Response/JsonResponse做一层封装</li>
</ul>
<blockquote>
<p>　unicode_literals</p>
</blockquote>
<ul>
<li>如果使用python2, 可以<code>from __future__ import unicode_literals</code></li>
<li>不强求</li>
</ul>
<h4 id="7-逐行重构">7. 逐行重构</h4>
<ul>
<li>
<p><a href="https://wklken.me/posts/2016/11/03/python-code-style.html">Python 代码规范小结</a></p>
</li>
<li>
<p><a href="https://wklken.me/posts/2017/06/17/refactoring-07.html">重构 - 读书笔记(Python示例)</a></p>
</li>
<li>
<p>删除无用的代码/注释/文档等</p>
</li>
<li>
<p>命名:　对所有变量名／常量名／函数名／类名／模块名等等重新思考，改成更合适的名字(此时编辑器或IDE的批量查找替换发挥作用)</p>
</li>
<li>
<p>归属问题:　函数／变量／方法等, 要思考是否属于这里, 不合适直接调整</p>
</li>
<li>
<p>表达式</p>
</li>
<li>
<p>控制流: <code>return early</code> / <code>guard  clauses</code>&hellip;.</p>
</li>
<li>
<p>异常处理</p>
</li>
<li>
<p>函数等</p>
</li>
<li>
<p>&hellip;&hellip;</p>
</li>
<li>
<p>同时, 根据code review结果, 逐一修改</p>
</li>
<li>
<p>该加注释加注释, 该加文档加文档</p>
</li>
</ul>
<h4 id="8-format-the-url">8. format the url</h4>
<p>因为要动前端调用, 所以放靠后会更合适</p>
<ul>
<li>推荐使用restful风格</li>
<li>格式化url</li>
<li>涉及前端调用处一并修改</li>
<li>如果被其他系统依赖, 可能需要兼容老的url/协议, 可用, 但标明<code>deprecated</code>, 并同时提供新版本的url/协议</li>
</ul>
<h4 id="9-fix-all-pep8-issues">9. fix all pep8 issues</h4>
<pre tabindex="0"><code>flake8 --config=.flake8 .
</code></pre><ul>
<li>开始不忽略任何错误, 完整修一遍</li>
<li>重构所有过于复杂的函数 <code>C901 XXXX is too complex</code></li>
<li>能不加<code># noqa</code>就不加</li>
</ul>
<h4 id="10-unittest">10. unittest</h4>
<ul>
<li>加单元测试, 优先核心模块的核心逻辑</li>
</ul>
<h4 id="11-然后">11. 然后</h4>
<ul>
<li>code review again</li>
<li>自测</li>
<li>测试</li>
<li>灰度</li>
<li>发布</li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>工作七年小结: 学习,生活及其他</title>
			<link>https://wklken.me/posts/2018/07/01/summary-15-work-7-years.html</link>
			<pubDate>Sun, 01 Jul 2018 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2018/07/01/summary-15-work-7-years.html</guid>
			<description>一切经历都是有价值的, 你要从中有所收获 再有三天, 工作七年了. 大学毕业前, 实习做了一年多的Java开发; 因为实习错过了校招, 11年一个人北上北</description>
			<content type="html"><![CDATA[<blockquote>
<p>一切经历都是有价值的, 你要从中有所收获</p>
</blockquote>
<p>再有三天, 工作七年了.</p>
<p>大学毕业前, 实习做了一年多的Java开发;</p>
<p>因为实习错过了校招, 11年一个人北上北京找工作, 阴差阳错, 找了个测试开发的工作, 没成为北漂, 向南飘到了杭州, 开始了自己的职业生涯.</p>
<p>在杭州一年多, 塑造我现在行事风格, 高效/专注/细心等等, 过多不表, 区间测试各种后端需要关注各种语言各种技术, 写自动化测试, 学习了shell和python等等, 然而国内测试开发其实更多的是测试, 发现自己并不是很喜欢, 年少冲动, 压不住内心的想法, 投了几个python简历, 之后毅然决然南下.</p>
<p>在深圳第一家公司, python后台开发, 一直做到14年, 从测试转为开发, 也算如鱼得水, 而且基本不加班, 所以有很多时间倒腾各种东西. 14年由于某些不可抵抗力公司over了, 如果没有over, 或许我现在还在那里. 离职出去浪了一个月, 回来愣头青有个抵挡不住的想法, 我想去创业公司&hellip;..</p>
<p>历时一周顺利入职创业公司, 公司团队都很nice, 从零到一开始做一些事情, 过程中对项目运作/产品开发等等, 都有了新的认识, 奈何天不遂人员, 入职十个月, 公司解散, 失业了.</p>
<p>然后, 这次浪得有点久, 整整一百天, 花了三天找到了工作, 入职了现在的公司, 一晃两年半了.</p>
<p>在深圳, 到现在已经5年半了. 五年三家公司, 也算经历丰富:)</p>
<p>想相信大部分人起点会好很多, 没那么折腾, 毕业进入公司, 可以一直呆三年, 五年, 可以很好的沉淀进步, 收获更多. 这么多年的折腾, 导致了我其实可以说正儿八经的开发生涯只有5年, 5年该到什么程度, 我也不知道, 我只知道, 我需要规划好, 不断学习和成长;</p>
<h2 id="关于学习">关于学习</h2>
<p>我一直有两个观点:</p>
<ol>
<li>
<p>从工作相关的内容出发, 向外扩散以及向下延伸, 学以致用, 不断实践-学习-实践的循环中逐步成长</p>
</li>
<li>
<p>成体系, 站在全局的角度, 思考业界的趋势和职业规划的方向所需要的技能体系, 像打游戏修炼一样, 点满技能树</p>
</li>
</ol>
<p>第一点, <code>转换率</code>最高, 因为跟每天使用接触的东西离得近, 所以胜在实践多, 成长高. 而不会了解了一堆概念,  写了一堆<code>hello world</code>, 但是平时无法使用, 逐步地这类<code>无效学习</code>的时间就沉没了. 但是前者范围小, 太过局限, 对于制定短期成长目标有效, 对长期方向性成长目标作用小.</p>
<p>第二点, 是方向性的内容, 对自己的职业发展有利. 各种技术革新非常快, 信息爆炸, 可能你一个月前关注了<code>5.0</code>版本, 一个月后<code>6.0</code>又出了, 人的精力有限, 应该用在<code>刀刃</code>上, 而那些在业界趋势以及跟自己职业规划契合的内容值得学习. 很多东西都不是一蹴而就, 是慢慢积累来的.</p>
<h4 id="盲区">盲区</h4>
<ul>
<li>买很多书, 却无法静心看完, 收获有限</li>
</ul>
<p>几年前, 我的想法是, 一本书都不买, 那么你收获永远是0, 买了书, 即使转化率低, 你的收获也是有的.</p>
<p>导致我这几年囤积了三四百本书, 其中有经典的, 也有快销品.</p>
<p>三年前出过一波, 今年因为有了一个叫<code>多抓鱼</code>的平台, 陆陆续续卖了几十本:)</p>
<p>我发现刚工作的时候, 买了很多<code>快销</code>书, 各种快速入门等等, 我追过<code>ios</code>/<code>swift</code>, 追过<code>前端开发</code>, 追过<code>推荐系统</code>, 追过&hellip;..回过头来, 发现收获有, 但是相对于投入的时间和精力, <code>转化率</code>太低了.</p>
<p>所以有了本文的第一个观点, 从工作内容出发的学以致用.</p>
<p>如果我能早明白这一点&hellip;&hellip;</p>
<ul>
<li>一亩三分地, 够用就好</li>
</ul>
<p>一路走来, 遇到过, 有些人觉得, 耕好自己的一亩三分地, 学到的内容够用就行; 但是这或许是那个<code>工作十年实际只有一年经验</code>的梗的出处</p>
<p>技术每时每刻在发展, 需要一直保持学习的信息, 不能停歇.</p>
<p>看过<code>异类</code>, 你会了解到, 每天的一点点差距, 经过时间的放大, 最终会变成鸿沟般的差距</p>
<ul>
<li>怎么深挖?</li>
</ul>
<p><code>实践&gt;看书&gt;开各种业界资料</code>, 你刚好在做这块的, 一直在不断实践, 用法/机制/原理/概念等等, 然后通过书籍和资料, 深度挖掘之.</p>
<ul>
<li>怎么成体系?</li>
</ul>
<p>问自己问题: 1.业界现在的趋势是什么? 自己关注的部分是否是趋势? 或者是否有被淘汰的趋势 2. 自己喜欢的是什么? 职业规划是什么?</p>
<p>然后, 知识爆炸和知识付费的年代, 我相信获取相关的信息并不难, 很快的, 你可以理清楚一棵技能树, 然后在未来的日子里, 不断去点满之</p>
<p>过去我一直通过<code>开源</code>的资料+<code>大牛</code>的博客去学习, <code>Google</code> 是个好东西:)</p>
<p>而基于这些, 你又可以引到各种经典的书籍, 买了, 反复阅读之. 大多是大部头, 需要有耐心.</p>
<p>这两年流行知识付费, 种类繁多, 良莠不齐, 例如知乎的live, 付费的电子书, 付费视频课程, 极客时间等等. 虽然获取知识有成本了, 但是我很高兴见到这种情况, 因为, 过去距离你很远的<code>大牛</code>, 往往你只能从其博客和演讲了解和学习到一些东西, 而现在, 你可以成体系的学习这些内容. 而这其中, 我觉得知乎live最水(不成体系, 贵, 效果不行), 付费视频课程更适合短平快的初学者, 而极客时间, 适合体系化及深挖.</p>
<p>所以我推荐下极客时间</p>
<p>在这里放个广告, 是我买的课程的推广海报, 扫码购买后你我都有返现:)</p>
<p><a href="/posts/2018/06/18/geek-time.html">我购买的课程</a></p>
<p>还是基于我上面的第一条第二条观点, 只关注工作相关的和对自己职业生涯有利的.</p>
<h2 id="关于生活">关于生活</h2>
<blockquote>
<p>生活是美好的, 生活也是操蛋的, 怎么过, 看自己.</p>
</blockquote>
<h4 id="follow-your-heart">follow your heart</h4>
<p>我想这个鸡汤大家都喝过</p>
<p>但是, 遵循内心的想法去做选择, 却又是没错的</p>
<p>想想我第一次离职转开发, 以及后来想去创业, 都是遵循了自己内心的想法, 虽然有些<code>冲动</code>,</p>
<p>现在想想, 虽然错过了<code>活得更轻松</code>或者<code>半财务自由</code>的机会.</p>
<p>我们都要为自己的决定负责. 不需要为站在现在看起来是机会, 站在当时局限性无法看到的机会而懊悔.</p>
<p>绝大多数时候, 你的命运, 你的生活, 都掌握在自己的手上.</p>
<p>父母朋友等, 或许会给你建议, 但是, 大部分境况, 只有你自己知道, 而且大部分决定, 也只有你自己能做.</p>
<h4 id="美好的细节以及注意力陷阱">美好的细节以及注意力陷阱</h4>
<p>生活中有很多美好的细节, 生活也可以过得很精彩.</p>
<p>例如, 今天天气很好, 那就出去浪, 宅在家里干什么. 阳光很好, 就去公园晒个太阳, 看看书, 我一直很喜欢这项老年人运动:) 想运动了, 就去海边跑跑步, 骑骑车; 想买东西就去逛一逛商场;</p>
<p>我们的时间, 大部分被手机, ipad, 以及电脑霸占了.</p>
<p>每天, 都需要这些东西来刺激下神经, 过后有发现什么都没有下, 很空虚.</p>
<p>你可以统计下, 自己每天花多少时间在刷手机, 每周花多少个小时看剧? 不是说这没有用, 娱乐放松还是需要的, 但是目前娱乐内容的丰富性已经超出了我们的想象.</p>
<p>我的做法是, 沉迷-反思-限制; 例如, 我以前每天花一个多小时刷微博, 后来直接卸载一年多, 近期装回来了, 取关了百分之九十的号, 现在每天几分钟看完.</p>
<p>另一个做法, 关掉所有通知, 只根据需要开启必要的应用. 取消所有微信公众号的文章推送, 只关注需要关注的.</p>
<h4 id="仪式感">仪式感</h4>
<p>生活要有仪式感, 不能过程朝九晚久, 五天工作两天休息的死气沉沉的模式化</p>
<p>例如: 周末的开始是周五晚上, 那就每周五晚上找些事情做, 例如电影之夜, 或者海边夜跑&hellip;&hellip;</p>
<h2 id="关于工作">关于工作</h2>
<blockquote>
<p>生存, 进步以及自我实现</p>
</blockquote>
<h4 id="站在他人的角度上思考问题">站在他人的角度上思考问题</h4>
<p>每个人的立场不一样.</p>
<p>我们很容易陷入是对是错这种细节. 而往往, 工作中并没有那么绝对, 更多时候, 是<code>是否合适</code></p>
<p>讨论问题, 提问等等, 多站在对方的角度思考下</p>
<p>另外, 值得一提的是沟通问题, 例如你问一个问题, 该怎么问最好? 如何问沟通效率最高? 如何问对方能100%理解并响应</p>
<h4 id="谦虚-接受意见反省并改进">谦虚, 接受意见反省并改进</h4>
<p>谦虚很重要, 你我大部分是普通人, 虽然<code>文人相轻</code>, 但是每个人都有自己的经历, 每个人看待问题的角度并不一样</p>
<p>所以时刻保持谦虚, 才能在工作中同他人更好的相处, 也能更多地学习一些不同领域的好的内容.</p>
<p>接受意见-反省-改进. 这是一个需要关注的问题, 很多时候, 人的惯性是很难纠正的, 一个不好的点, 提过, 短时间内好一点, 但是后面又复发, 这样给合作的的感觉是: 这个人不靠谱.</p>
<p>对就是对, 错就是错, 错了反省, 改进, 然后后面行事多多注意.</p>
<h4 id="多走一步">多走一步</h4>
<p>不要守着自己的一亩三分地, 凡事多往前走一步, 多思考一点, 往往会取得更好的效果.</p>
<h4 id="专业">专业</h4>
<p>首先是专业技能一定要到位, 其实是行事作风以及结果要足够靠谱.</p>
<p>树立自己的专业口碑.</p>
<hr>
<p>偶尔回想, 会想起很多事情, 在北京住过地下室实习的日志, 在杭州每周骑行龙井西湖, 在深圳住过城中村, 往事如烟;</p>
<p>未来还很长, 生活还在继续.</p>
<p>杂七杂八写了一些, 这两年由于各种原因, 博客更新少了, 打算重新拾起来, 多写写一些东西.</p>
<p>就这样吧</p>
<p>2018-07-01 于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>[分享]bash日常: bash-utils</title>
			<link>https://wklken.me/posts/2018/06/30/the-project-bash-utils.html</link>
			<pubDate>Sat, 30 Jun 2018 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2018/06/30/the-project-bash-utils.html</guid>
			<description>早上优化了下自己的一个项目 bash-utils I always use the bash as my devops script, while it&amp;rsquo;s boring to write the condition judgement/echo statement/exit. So, I want to make the bash script short and clear. I have collected some useful function into this repo, which save me a lot of time in past few years. Just source the utils.sh and write the</description>
			<content type="html"><![CDATA[<p>早上优化了下自己的一个项目 <a href="https://github.com/wklken/bash-utils">bash-utils</a></p>
<blockquote>
<p>I always use the bash as my devops script, while it&rsquo;s boring to write the condition judgement/echo statement/exit.
So, I want to make the bash script short and clear.
I have collected some useful function into this repo, which save me a lot of time in past few years.
Just source the <code>utils.sh</code> and write the expression calling the functions.
I can just focus on the logical, not the bash syntax and expression</p>
</blockquote>
<p>日常在写一些脚本的时候, 偏好于用bash, 当然, 逻辑太过复杂的时候会用python.</p>
<p>bash相对于python来说, 已经很精简了.</p>
<p>但是, 日常经常做一些逻辑比较重复的自动化任务, 写着写着发现bash这么写还是太冗长了, 而且每次需要重复类似却又不尽相同的动作.</p>
<p>所以, 逐步的, 将一些东西梳理出来了.</p>
<p>目的是, 只要<code>source utils.sh</code>, 就能将注意力集中在业务逻辑上, 而不是<code>bash</code>语法.</p>
<p>往往只需要原先四分之一左右的行数, 搞定逻辑</p>
<p>其实, bash中最多的无非条件判断+字符串处理, 以及我们写脚本需要打印日志. 所以集中将这三块经常做的抽离出函数, 十几个函数, 顶掉80%的工作</p>
<p>有兴趣可以看看, 欢迎提pr</p>
<p>2018-06 深圳</p>
]]></content>
		</item>
		
		<item>
			<title>极客时间推广海报</title>
			<link>https://wklken.me/posts/2018/06/18/geek-time.html</link>
			<pubDate>Mon, 18 Jun 2018 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2018/06/18/geek-time.html</guid>
			<description></description>
			<content type="html"><![CDATA[<p><img src="/imgs/share/1.jpg" alt="1">
<img src="/imgs/share/2.jpg" alt="2">
<img src="/imgs/share/3.jpg" alt="3">
<img src="/imgs/share/4.jpg" alt="4">
<img src="/imgs/share/5.jpg" alt="5">
<img src="/imgs/share/6.jpg" alt="6"></p>
]]></content>
		</item>
		
		<item>
			<title>2017总结: 予时光以意义</title>
			<link>https://wklken.me/posts/2017/12/31/summary-14-2017end-2018begin.html</link>
			<pubDate>Sun, 31 Dec 2017 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2017/12/31/summary-14-2017end-2018begin.html</guid>
			<description>予时光以意义 我的2017 这句话， 算是过去一段时间的一个思考; 最初应该是在看三体“给岁月以文明，而不是给文明以岁月”, 后来偶然间, 在匆匆忙忙后</description>
			<content type="html"><![CDATA[<blockquote>
<p>予时光以意义</p>
</blockquote>
<p>我的2017</p>
<p>这句话， 算是过去一段时间的一个思考; 最初应该是在看三体“给岁月以文明，而不是给文明以岁月”, 后来偶然间, 在匆匆忙忙后, 闲暇时的一个感悟;</p>
<p>过去的2017, 一如既往地繁忙, 有段时间, 还是陷入了<code>周一到周五</code>的模式, 时间匆匆流逝, 转眼快2018了, 回首一年, 收获良多.</p>
<p>絮絮叨叨, 做一次总结, 坚持了好几年的总结, 前两年断了, 这次看能否续上一续.</p>
<h2 id="关于工作">关于工作</h2>
<p>效率上, 今年又又又对自己的工作流重构了下; 目前全面使用trello看板在管理自己的任务; 买了个实体番茄钟, 严格按照35分钟工作/5分钟休息, 在持续运转, 每天正常7个番茄钟, 火力全开的时候可以到达11个(不过会很累啊…….)</p>
<p>沟通交流上, 变得更淡定了吧, 对于一些突发的变更不会再那么慌乱;</p>
<p>今年对自己做的工作, 大体上只能到70分的样子, 起码没做到优秀;</p>
<p>盯了一个项目, 一年半了, 非常冗长琐碎, 事情比较多, 虽然经历一次次优化和调整, 也算是走上正轨, 但是后期感觉个人精力上占用比较多,  导致没法全面投入去做其他项目,  所以导致总体结果并没有达到自己预定的目标, 几个项目都不错, 但是没有做到最好;</p>
<p>其他几个项目, 涉及业务及基础功能开发, 也都保质保量按时交付, 对很多东西也都有了成体系的了解和实践, 但是还是欠缺深入;</p>
<p>总之一年下来,  逐步地有些缺乏定位, 不知道自己的定位和方向在哪里.</p>
<p>接近年底的时候, 也做了一次重新梳理, 大体确认了后续深入的方向, 也算是一个收获.</p>
<p>明年, 仔细做个规划, 深入去做一些事情, 希望有所改善;
 今年尝试开了下知乎专栏, 写一些东西, 然而最终没有坚持下来……博客更新也有限, 主要是刚迈入新的领域, 还没有更深入和深刻的了解, 后面再慢慢来吧.</p>
<p>工作, 占据了生活很大一部分, 起码时间上, 目前会占用很多, 所以很多时候, 要让自己开心, 有快乐地去完成一些事情, 做自己想做的事情.</p>
<p>如何自己充电提升能力, 如何持续优化和改进工作流, 如何愉快的合作, 如何专注工作, 如何按时交付…..这些都是需要在不断的思考和实践中持续前进, 往前走.</p>
<h2 id="关于读书">关于读书</h2>
<p>2017年, 读书相对以前少了很多;</p>
<p>今年书目中多了一项: 经济学; 主要是因为穷, 继去年看了&laquo;贫穷的本质&raquo;之后, 一月份看了&laquo;稀缺&raquo;, 但是这个书更多的是对个人工作方法论有所帮助, 开始在意工作中各种稀缺, 也学习利用这种稀缺变相提升某种场景下的效率; 经济学另外读了下&laquo;穷爸爸富爸爸&raquo;/&laquo;小岛经济学&raquo;/&laquo;微观经济学&raquo;(在读)/聪明的投资者…….要成体系, 目前还差很多</p>
<p>四月, 补了东野圭吾的&laquo;虚无的十字架&raquo;和&laquo;幻夜&raquo;, 一如既往地好看, 还有好多后面慢慢读了, 不轻易开启一本, 因为这类小说一读就停不下来;</p>
<p>八月, 在去台湾的旅途中, 开启了&laquo;巨人的陨落&raquo;, 篇幅很长, 多线并行, 整体还不错, 可以一读, 但是没有&laquo;平凡的世界&raquo;那种厚重感, 更多的是对那个时代的那个情况下的好奇;</p>
<p>技术方面, 七月中旬再次过了一遍&laquo;代码大全&raquo;, 其他时候, 补了三四本容器相关/k8s相关的书, 算是自己在技术方面的一个补充和充电吧.</p>
<p>快要搬家才发现买那么多书的痛苦;  但是依然会持续地买, 因为书真真的很便宜啊;</p>
<h2 id="关于游戏">关于游戏</h2>
<p>生活中, 比以前少了一项<code>骑行</code>(可惜了我在墙角落灰的大行 P8), 多了一项<code>打游戏</code></p>
<p>十一的时候, 入了ps4, 哈哈, 第一次股票操作换得的奖励;</p>
<p>然后, 带着妹子打通<code>Knack</code>,  然后一起分屏打<code>植物大战僵尸</code>, 一个人<code>猥琐</code>地过<code>美国末日</code>, 后来又开新坑, 打<code>神海四</code>;</p>
<p>想起了很小的时候, 打小霸王的日子; 也算是圆了一个梦;</p>
<p>手机这边,  <code>纪念碑谷2</code>第一时间下载通关, 感觉沿袭了经典, 但是没有更多的出彩;  十一的时候, 最后一天跟妹子一人一个手机, 玩<code>To the moon</code>.  直到玩完最后一幕, 也没有太大感伤, 跟妹子交流里面的剧情, 才恍然大悟, 唏嘘不已;</p>
<h2 id="关于生活">关于生活</h2>
<p>2017, 年初的时候, 见了回家长</p>
<p>2017, 年中的时候, 订了个婚</p>
<p>2017, 八月份, 公司outing去了趟台湾,</p>
<p>2017, 快年底的时候, 买了个房, 途中虽然有些波折, 但是最终还是走完流程, 顺利交房;</p>
<p>2017, 快年底的时候, 结了个婚 
予时光以意义,  2017, 做了很多事情, 也改变了很多;</p>
<p>首先一个改变, 是从一个大龄单身男青年变成一个已婚人士, 并且步入了法定的中年; 身上的担子和责任更大了, 也需要以不一样的思维去面对这个世界;</p>
<p>其次, 提前开始了<code>中年危机</code>的思考, 危机感更强了, 早几年的时候, 是技术焦虑, 而现在, 变成了定位焦虑, 人总是在不断焦虑中, 从一个阶段进入另一个; 磕磕碰碰, 一路向前;</p>
<p>另外, 年中的时候, 咬咬牙, 彻底戒掉了微博, 改成订阅; 每天刷微博的习惯去掉后, 发现生活其实并不需要信息流的那些刺激, 没了那些, 每天其实可以有更多的时间去做一些有意义的事情;</p>
<p>到十月份的时候, 还在坚持跑步, 虽然已经退化成一个只能跑七八公里的渣渣, 但是毕竟坚持下来了, 体重也得到了很好的控制;</p>
<hr>
<p>予时光以意义, 时间并不会等你, 你必须一点点往前走, 而这一切时光, 不能虚度;</p>
<p>2017-12-31 于老家</p>
]]></content>
		</item>
		
		<item>
			<title>k8s APIServer源码: api注册详细细节</title>
			<link>https://wklken.me/posts/2017/09/23/source-apiserver-04.html</link>
			<pubDate>Sat, 23 Sep 2017 15:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2017/09/23/source-apiserver-04.html</guid>
			<description>基于版本 1.6.7 前面介绍了, api注册过程 问题: go-restful github的route中, handler和path是如何绑定在一起的? handler在哪里定义</description>
			<content type="html"><![CDATA[<p>基于版本 1.6.7</p>
<p>前面介绍了, api注册过程</p>
<p>问题: <a href="https://github.com/emicklei/go-restful">go-restful github</a>的<code>route</code>中, <code>handler</code>和<code>path</code>是如何绑定在一起的? <code>handler</code>在哪里定义的?</p>
<p>以<code>/api</code>为例</p>
<p><img src="/imgs/k8s/apiserver-register-02.jpg" alt=""></p>
<p>前面介绍到<code>/api</code>和<code>/apis</code>分别注册加入到<code>Container</code>, 而最终, 二者调用<code>installer.Install(ws)</code>. 这一步, 我们需要进一步了解细节.</p>
<p><img src="/imgs/k8s/apiserver-register-03.jpg" alt=""></p>
<h2 id="webserviceaddroute">webservice.add(route)</h2>
<p>问题: 构建Route加入到WebService在哪里处理的?</p>
<ul>
<li>vendor/k8s.io/apiserver/pkg/endpoints/installer.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">a</span> <span class="o">*</span><span class="nx">APIInstaller</span><span class="p">)</span> <span class="nf">Install</span><span class="p">(</span><span class="nx">ws</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">WebService</span><span class="p">)</span> <span class="p">(</span><span class="nx">apiResources</span> <span class="p">[]</span><span class="nx">metav1</span><span class="p">.</span><span class="nx">APIResource</span><span class="p">,</span> <span class="nx">errors</span> <span class="p">[]</span><span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// Register the paths in a deterministic (sorted) order to get a deterministic swagger spec.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">paths</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">string</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">a</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">Storage</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">	<span class="kd">var</span> <span class="nx">i</span> <span class="kt">int</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">path</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">a</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">Storage</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">paths</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">=</span> <span class="nx">path</span>
</span></span><span class="line"><span class="cl">		<span class="nx">i</span><span class="o">++</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="nx">sort</span><span class="p">.</span><span class="nf">Strings</span><span class="p">(</span><span class="nx">paths</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">path</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">paths</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">apiResource</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">a</span><span class="p">.</span><span class="nf">registerResourceHandlers</span><span class="p">(</span><span class="nx">path</span><span class="p">,</span> <span class="nx">a</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">Storage</span><span class="p">[</span><span class="nx">path</span><span class="p">],</span> <span class="nx">ws</span><span class="p">,</span> <span class="nx">proxyHandler</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></code></pre></div><ul>
<li>apiserver/pkg/endpoints/installer.go</li>
</ul>
<p>仅摘录部分核心代码, 这里, 获取<code>handler</code>之后, 构建<code>route</code>, 然后加入到<code>webservice</code>中</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">a</span> <span class="o">*</span><span class="nx">APIInstaller</span><span class="p">)</span> <span class="nf">registerResourceHandlers</span><span class="p">(</span><span class="nx">path</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">storage</span> <span class="nx">rest</span><span class="p">.</span><span class="nx">Storage</span><span class="p">,</span> <span class="nx">ws</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">WebService</span><span class="p">,</span> <span class="nx">proxyHandler</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">metav1</span><span class="p">.</span><span class="nx">APIResource</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</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 class="nx">creater</span><span class="p">,</span> <span class="nx">isCreater</span> <span class="o">:=</span> <span class="nx">storage</span><span class="p">.(</span><span class="nx">rest</span><span class="p">.</span><span class="nx">Creater</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="o">...</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">	<span class="nx">actions</span> <span class="p">=</span> <span class="nf">appendIf</span><span class="p">(</span><span class="nx">actions</span><span class="p">,</span> <span class="nx">action</span><span class="p">{</span><span class="s">&#34;POST&#34;</span><span class="p">,</span> <span class="nx">resourcePath</span><span class="p">,</span> <span class="nx">resourceParams</span><span class="p">,</span> <span class="nx">namer</span><span class="p">,</span> <span class="kc">false</span><span class="p">},</span> <span class="nx">isCreater</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">case</span> <span class="s">&#34;POST&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	 <span class="c1">// 获取handler
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">handler</span> <span class="p">=</span> <span class="nx">handlers</span><span class="p">.</span><span class="nf">CreateResource</span><span class="p">(</span><span class="nx">creater</span><span class="p">,</span> <span class="nx">reqScope</span><span class="p">,</span> <span class="nx">a</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">Typer</span><span class="p">,</span> <span class="nx">admit</span><span class="p">)</span> <span class="c1">// =&gt; next
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 构建route,  action.Path -&gt; handler
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	  <span class="nx">route</span> <span class="o">:=</span> <span class="nx">ws</span><span class="p">.</span><span class="nf">POST</span><span class="p">(</span><span class="nx">action</span><span class="p">.</span><span class="nx">Path</span><span class="p">).</span><span class="nf">To</span><span class="p">(</span><span class="nx">handler</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">				<span class="nf">Doc</span><span class="p">(</span><span class="nx">doc</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">				<span class="nf">Param</span><span class="p">(</span><span class="nx">ws</span><span class="p">.</span><span class="nf">QueryParameter</span><span class="p">(</span><span class="s">&#34;pretty&#34;</span><span class="p">,</span> <span class="s">&#34;If &#39;true&#39;, then the output is pretty printed.&#34;</span><span class="p">)).</span>
</span></span><span class="line"><span class="cl">				<span class="nf">Operation</span><span class="p">(</span><span class="s">&#34;create&#34;</span><span class="o">+</span><span class="nx">namespaced</span><span class="o">+</span><span class="nx">kind</span><span class="o">+</span><span class="nx">strings</span><span class="p">.</span><span class="nf">Title</span><span class="p">(</span><span class="nx">subresource</span><span class="p">)</span><span class="o">+</span><span class="nx">operationSuffix</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">				<span class="nf">Produces</span><span class="p">(</span><span class="nb">append</span><span class="p">(</span><span class="nx">storageMeta</span><span class="p">.</span><span class="nf">ProducesMIMETypes</span><span class="p">(</span><span class="nx">action</span><span class="p">.</span><span class="nx">Verb</span><span class="p">),</span> <span class="nx">mediaTypes</span><span class="o">...</span><span class="p">)</span><span class="o">...</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">				<span class="nf">Returns</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusOK</span><span class="p">,</span> <span class="s">&#34;OK&#34;</span><span class="p">,</span> <span class="nx">versionedObject</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">				<span class="nf">Reads</span><span class="p">(</span><span class="nx">versionedObject</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">				<span class="nf">Writes</span><span class="p">(</span><span class="nx">versionedObject</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="nf">addParams</span><span class="p">(</span><span class="nx">route</span><span class="p">,</span> <span class="nx">action</span><span class="p">.</span><span class="nx">Params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="c1">// 添加route到webservice
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>		<span class="nx">ws</span><span class="p">.</span><span class="nf">Route</span><span class="p">(</span><span class="nx">route</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="create-handler">create handler</h2>
<ul>
<li>vendor/k8s.io/apiserver/pkg/endpoints/handlers/rest.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// CreateResource returns a function that will handle a resource creation.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">CreateResource</span><span class="p">(</span><span class="nx">r</span> <span class="nx">rest</span><span class="p">.</span><span class="nx">Creater</span><span class="p">,</span> <span class="nx">scope</span> <span class="nx">RequestScope</span><span class="p">,</span> <span class="nx">typer</span> <span class="nx">runtime</span><span class="p">.</span><span class="nx">ObjectTyper</span><span class="p">,</span> <span class="nx">admit</span> <span class="nx">admission</span><span class="p">.</span><span class="nx">Interface</span><span class="p">)</span> <span class="nx">restful</span><span class="p">.</span><span class="nx">RouteFunction</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nf">createHandler</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">namedCreaterAdapter</span><span class="p">{</span><span class="nx">r</span><span class="p">},</span> <span class="nx">scope</span><span class="p">,</span> <span class="nx">typer</span><span class="p">,</span> <span class="nx">admit</span><span class="p">,</span> <span class="kc">false</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="kd">func</span> <span class="nf">createHandler</span><span class="p">(</span><span class="nx">r</span> <span class="nx">rest</span><span class="p">.</span><span class="nx">NamedCreater</span><span class="p">,</span> <span class="nx">scope</span> <span class="nx">RequestScope</span><span class="p">,</span> <span class="nx">typer</span> <span class="nx">runtime</span><span class="p">.</span><span class="nx">ObjectTyper</span><span class="p">,</span> <span class="nx">admit</span> <span class="nx">admission</span><span class="p">.</span><span class="nx">Interface</span><span class="p">,</span> <span class="nx">includeName</span> <span class="kt">bool</span><span class="p">)</span> <span class="nx">restful</span><span class="p">.</span><span class="nx">RouteFunction</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="kd">func</span><span class="p">(</span><span class="nx">req</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">Request</span><span class="p">,</span> <span class="nx">res</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">Response</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">original</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span>  <span class="c1">// =&gt; here
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>		<span class="nx">responsewriters</span><span class="p">.</span><span class="nf">WriteObject</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">StatusCreated</span><span class="p">,</span> <span class="nx">scope</span><span class="p">.</span><span class="nx">Kind</span><span class="p">.</span><span class="nf">GroupVersion</span><span class="p">(),</span> <span class="nx">scope</span><span class="p">.</span><span class="nx">Serializer</span><span class="p">,</span> <span class="nx">result</span><span class="p">,</span> <span class="nx">w</span><span class="p">,</span> <span class="nx">req</span><span class="p">.</span><span class="nx">Request</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></code></pre></div><p>即, 最终<code>handler</code>执行时, 调用的是<code>rest.Creater.New()</code></p>
<p>这里的 <code>Creater</code> 是一个<code>interface</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="err">#</span> <span class="nx">vendor</span><span class="o">/</span><span class="nx">k8s</span><span class="p">.</span><span class="nx">io</span><span class="o">/</span><span class="nx">apiserver</span><span class="o">/</span><span class="nx">pkg</span><span class="o">/</span><span class="nx">registry</span><span class="o">/</span><span class="nx">rest</span><span class="o">/</span><span class="nx">rest</span><span class="p">.</span><span class="k">go</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Creater is an object that can create an instance of a RESTful object.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Creater</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// New returns an empty object that can be used with Create after request data has been put into it.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nf">New</span><span class="p">()</span> <span class="nx">runtime</span><span class="p">.</span><span class="nx">Object</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// Create creates a new version of a resource.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nf">Create</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">genericapirequest</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">obj</span> <span class="nx">runtime</span><span class="p">.</span><span class="nx">Object</span><span class="p">)</span> <span class="p">(</span><span class="nx">runtime</span><span class="p">.</span><span class="nx">Object</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="回到最初的问题">回到最初的问题</h2>
<p>最终, <code>handler</code>调用的是<code>rest.Creater.New()</code></p>
<p>而<code>creater</code>声明的位置</p>
<ul>
<li>apiserver/pkg/endpoints/installer.go</li>
</ul>
<pre tabindex="0"><code>	creater, isCreater := storage.(rest.Creater)
</code></pre><p>这里, 想要知道<code>handler</code>最终调用的是哪里定义的方法, 我们需要分析<code>storage</code>的来源</p>
<h2 id="第一步--链路分析">第一步:  链路分析</h2>
<p><img src="/imgs/k8s/apiserver-register-04.jpg" alt=""></p>
<p>调用链</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// pkg/master/master.go
</span></span></span><span class="line"><span class="cl"><span class="c1">// =&gt; got: apiGroupInfo 初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">Master</span><span class="p">)</span> <span class="nf">InstallLegacyAPI</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">Config</span><span class="p">,</span> <span class="nx">restOptionsGetter</span> <span class="nx">generic</span><span class="p">.</span><span class="nx">RESTOptionsGetter</span><span class="p">,</span> <span class="nx">legacyRESTStorageProvider</span> <span class="nx">corerest</span><span class="p">.</span><span class="nx">LegacyRESTStorageProvider</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">legacyRESTStorage</span><span class="p">,</span> <span class="nx">apiGroupInfo</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">legacyRESTStorageProvider</span><span class="p">.</span><span class="nf">NewLegacyRESTStorage</span><span class="p">(</span><span class="nx">restOptionsGetter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">m</span><span class="p">.</span><span class="nx">GenericAPIServer</span><span class="p">.</span><span class="nf">InstallLegacyAPIGroup</span><span class="p">(</span><span class="nx">genericapiserver</span><span class="p">.</span><span class="nx">DefaultLegacyAPIPrefix</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">apiGroupInfo</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="c1">// vendor/k8s.io/apiserver/pkg/server/genericapiserver.go
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">GenericAPIServer</span><span class="p">)</span> <span class="nf">InstallLegacyAPIGroup</span><span class="p">(</span><span class="nx">apiPrefix</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">apiGroupInfo</span> <span class="o">*</span><span class="nx">APIGroupInfo</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">s</span><span class="p">.</span><span class="nf">installAPIResources</span><span class="p">(</span><span class="nx">apiPrefix</span><span class="p">,</span> <span class="nx">apiGroupInfo</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="c1">// vendor/k8s.io/apiserver/pkg/server/genericapiserver.go
</span></span></span><span class="line"><span class="cl"><span class="c1">// NOTE =&gt; apigroup TO apigroupversion
</span></span></span><span class="line"><span class="cl"><span class="c1">// =&gt; got: apigroupversion
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">GenericAPIServer</span><span class="p">)</span> <span class="nf">installAPIResources</span><span class="p">(</span><span class="nx">apiPrefix</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">apiGroupInfo</span> <span class="o">*</span><span class="nx">APIGroupInfo</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">groupVersion</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">apiGroupInfo</span><span class="p">.</span><span class="nx">GroupMeta</span><span class="p">.</span><span class="nx">GroupVersions</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">apiGroupVersion</span> <span class="o">:=</span> <span class="nx">s</span><span class="p">.</span><span class="nf">getAPIGroupVersion</span><span class="p">(</span><span class="nx">apiGroupInfo</span><span class="p">,</span> <span class="nx">groupVersion</span><span class="p">,</span> <span class="nx">apiPrefix</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="nx">apiGroupVersion</span><span class="p">.</span><span class="nf">InstallREST</span><span class="p">(</span><span class="nx">s</span><span class="p">.</span><span class="nx">HandlerContainer</span><span class="p">.</span><span class="nx">Container</span><span class="p">);</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="c1">// vendor/k8s.io/apiserver/pkg/server/genericapiserver.go
</span></span></span><span class="line"><span class="cl"><span class="c1">// =&gt; got: APIGroupVersion.Storage = make(map[string]rest.Storage
</span></span></span><span class="line"><span class="cl"><span class="c1">//         APIGroupVersion.Storage[path] = apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version][path]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">GenericAPIServer</span><span class="p">)</span> <span class="nf">getAPIGroupVersion</span><span class="p">(</span><span class="nx">apiGroupInfo</span> <span class="o">*</span><span class="nx">APIGroupInfo</span><span class="p">,</span> <span class="nx">groupVersion</span> <span class="nx">schema</span><span class="p">.</span><span class="nx">GroupVersion</span><span class="p">,</span> <span class="nx">apiPrefix</span> <span class="kt">string</span><span class="p">)</span> <span class="o">*</span><span class="nx">genericapi</span><span class="p">.</span><span class="nx">APIGroupVersion</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">storage</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="nx">rest</span><span class="p">.</span><span class="nx">Storage</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">k</span><span class="p">,</span> <span class="nx">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">apiGroupInfo</span><span class="p">.</span><span class="nx">VersionedResourcesStorageMap</span><span class="p">[</span><span class="nx">groupVersion</span><span class="p">.</span><span class="nx">Version</span><span class="p">]</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">storage</span><span class="p">[</span><span class="nx">strings</span><span class="p">.</span><span class="nf">ToLower</span><span class="p">(</span><span class="nx">k</span><span class="p">)]</span> <span class="p">=</span> <span class="nx">v</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="nx">version</span> <span class="o">:=</span> <span class="nx">s</span><span class="p">.</span><span class="nf">newAPIGroupVersion</span><span class="p">(</span><span class="nx">apiGroupInfo</span><span class="p">,</span> <span class="nx">groupVersion</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">version</span><span class="p">.</span><span class="nx">Root</span> <span class="p">=</span> <span class="nx">apiPrefix</span>
</span></span><span class="line"><span class="cl">	<span class="nx">version</span><span class="p">.</span><span class="nx">Storage</span> <span class="p">=</span> <span class="nx">storage</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">version</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">// vendor/k8s.io/apiserver/pkg/endpoints/groupversion.go
</span></span></span><span class="line"><span class="cl"><span class="c1">// =&gt; got: installer.group = APIGroupVersion
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">g</span> <span class="o">*</span><span class="nx">APIGroupVersion</span><span class="p">)</span> <span class="nf">InstallREST</span><span class="p">(</span><span class="nx">container</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">Container</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">installer</span> <span class="o">:=</span> <span class="nx">g</span><span class="p">.</span><span class="nf">newInstaller</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nx">installer</span><span class="p">.</span><span class="nf">Install</span><span class="p">(</span><span class="nx">ws</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="kd">func</span> <span class="p">(</span><span class="nx">g</span> <span class="o">*</span><span class="nx">APIGroupVersion</span><span class="p">)</span> <span class="nf">newInstaller</span><span class="p">()</span> <span class="o">*</span><span class="nx">APIInstaller</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">prefix</span> <span class="o">:=</span> <span class="nx">path</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="nx">g</span><span class="p">.</span><span class="nx">Root</span><span class="p">,</span> <span class="nx">g</span><span class="p">.</span><span class="nx">GroupVersion</span><span class="p">.</span><span class="nx">Group</span><span class="p">,</span> <span class="nx">g</span><span class="p">.</span><span class="nx">GroupVersion</span><span class="p">.</span><span class="nx">Version</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">installer</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">APIInstaller</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">group</span><span class="p">:</span>             <span class="nx">g</span><span class="p">,</span>           <span class="c1">// group = APIGroupVersion
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>		<span class="nx">prefix</span><span class="p">:</span>            <span class="nx">prefix</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="nx">minRequestTimeout</span><span class="p">:</span> <span class="nx">g</span><span class="p">.</span><span class="nx">MinRequestTimeout</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="k">return</span> <span class="nx">installer</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">// vendor/k8s.io/apiserver/pkg/endpoints/installer.go
</span></span></span><span class="line"><span class="cl"><span class="c1">// got: a.group.Storage[path] = APIInstaller.group.Storage[path] = APIGroupVersion.Storage[path]
</span></span></span><span class="line"><span class="cl"><span class="c1">//      APIGroupVersion.Storage[path] = apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version][path]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">a</span> <span class="o">*</span><span class="nx">APIInstaller</span><span class="p">)</span> <span class="nf">Install</span><span class="p">(</span><span class="nx">ws</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">WebService</span><span class="p">)</span> <span class="p">(</span><span class="nx">apiResources</span> <span class="p">[]</span><span class="nx">metav1</span><span class="p">.</span><span class="nx">APIResource</span><span class="p">,</span> <span class="nx">errors</span> <span class="p">[]</span><span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// Register the paths in a deterministic (sorted) order to get a deterministic swagger spec.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">paths</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">string</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">a</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">Storage</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">	<span class="kd">var</span> <span class="nx">i</span> <span class="kt">int</span> <span class="p">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">path</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">a</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">Storage</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">paths</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="p">=</span> <span class="nx">path</span>
</span></span><span class="line"><span class="cl">		<span class="nx">i</span><span class="o">++</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="nx">sort</span><span class="p">.</span><span class="nf">Strings</span><span class="p">(</span><span class="nx">paths</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">path</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">paths</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">apiResource</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">a</span><span class="p">.</span><span class="nf">registerResourceHandlers</span><span class="p">(</span><span class="nx">path</span><span class="p">,</span> <span class="nx">a</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">Storage</span><span class="p">[</span><span class="nx">path</span><span class="p">],</span> <span class="nx">ws</span><span class="p">,</span> <span class="nx">proxyHandler</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">// apiserver/pkg/endpoints/installer.go
</span></span></span><span class="line"><span class="cl"><span class="c1">// got: storage = apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version][path]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">a</span> <span class="o">*</span><span class="nx">APIInstaller</span><span class="p">)</span> <span class="nf">registerResourceHandlers</span><span class="p">(</span><span class="nx">path</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">storage</span> <span class="nx">rest</span><span class="p">.</span><span class="nx">Storage</span><span class="p">,</span> <span class="nx">ws</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">WebService</span><span class="p">,</span> <span class="nx">proxyHandler</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="nx">metav1</span><span class="p">.</span><span class="nx">APIResource</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">creater</span><span class="p">,</span> <span class="nx">isCreater</span> <span class="o">:=</span> <span class="nx">storage</span><span class="p">.(</span><span class="nx">rest</span><span class="p">.</span><span class="nx">Creater</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>到了这里, 其实有了一个清晰的结论</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// apiGroupInfo.VersionedResourcesStorageMap
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">storage</span> <span class="p">=</span> <span class="nx">apiGroupInfo</span><span class="p">.</span><span class="nx">VersionedResourcesStorageMap</span><span class="p">[</span><span class="nx">groupVersion</span><span class="p">.</span><span class="nx">Version</span><span class="p">][</span><span class="nx">path</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">creater</span><span class="p">,</span> <span class="nx">isCreater</span> <span class="o">:=</span> <span class="nx">storage</span><span class="p">.(</span><span class="nx">rest</span><span class="p">.</span><span class="nx">Creater</span><span class="p">)</span>
</span></span></code></pre></div><p>此时, 我们再反向寻找<code>apiGroupInfo</code>初始化的位置</p>
<h2 id="第二步-apigroupinfo-初始化">第二步: apiGroupInfo 初始化</h2>
<ul>
<li>pkg/master/master.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">Master</span><span class="p">)</span> <span class="nf">InstallLegacyAPI</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">Config</span><span class="p">,</span> <span class="nx">restOptionsGetter</span> <span class="nx">generic</span><span class="p">.</span><span class="nx">RESTOptionsGetter</span><span class="p">,</span> <span class="nx">legacyRESTStorageProvider</span> <span class="nx">corerest</span><span class="p">.</span><span class="nx">LegacyRESTStorageProvider</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">legacyRESTStorage</span><span class="p">,</span> <span class="nx">apiGroupInfo</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">legacyRESTStorageProvider</span><span class="p">.</span><span class="nf">NewLegacyRESTStorage</span><span class="p">(</span><span class="nx">restOptionsGetter</span><span class="p">)</span> <span class="c1">// =&gt; next
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">m</span><span class="p">.</span><span class="nx">GenericAPIServer</span><span class="p">.</span><span class="nf">InstallLegacyAPIGroup</span><span class="p">(</span><span class="nx">genericapiserver</span><span class="p">.</span><span class="nx">DefaultLegacyAPIPrefix</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">apiGroupInfo</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>pkg/registry/core/rest/storage_core.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="nx">LegacyRESTStorageProvider</span><span class="p">)</span> <span class="nf">NewLegacyRESTStorage</span><span class="p">(</span><span class="nx">restOptionsGetter</span> <span class="nx">generic</span><span class="p">.</span><span class="nx">RESTOptionsGetter</span><span class="p">)</span> <span class="p">(</span><span class="nx">LegacyRESTStorage</span><span class="p">,</span> <span class="nx">genericapiserver</span><span class="p">.</span><span class="nx">APIGroupInfo</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// 初始化: VersionedResourcesStorageMap
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">apiGroupInfo</span> <span class="o">:=</span> <span class="nx">genericapiserver</span><span class="p">.</span><span class="nx">APIGroupInfo</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">GroupMeta</span><span class="p">:</span>                    <span class="o">*</span><span class="nx">api</span><span class="p">.</span><span class="nx">Registry</span><span class="p">.</span><span class="nf">GroupOrDie</span><span class="p">(</span><span class="nx">api</span><span class="p">.</span><span class="nx">GroupName</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">		<span class="nx">VersionedResourcesStorageMap</span><span class="p">:</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="nx">rest</span><span class="p">.</span><span class="nx">Storage</span><span class="p">{},</span>
</span></span><span class="line"><span class="cl">		<span class="nx">Scheme</span><span class="p">:</span>                      <span class="nx">api</span><span class="p">.</span><span class="nx">Scheme</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="nx">ParameterCodec</span><span class="p">:</span>              <span class="nx">api</span><span class="p">.</span><span class="nx">ParameterCodec</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="nx">NegotiatedSerializer</span><span class="p">:</span>        <span class="nx">api</span><span class="p">.</span><span class="nx">Codecs</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="nx">SubresourceGroupVersionKind</span><span class="p">:</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="nx">schema</span><span class="p">.</span><span class="nx">GroupVersionKind</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="c1">// ......
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">	<span class="c1">// 初始化了一个restStorage的map，然后赋值给APIGroupInfo.VersionedResourcesStorageMap[&#34;v1&#34;]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">restStorageMap</span> <span class="o">:=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="nx">rest</span><span class="p">.</span><span class="nx">Storage</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;pods&#34;</span><span class="p">:</span>             <span class="nx">podStorage</span><span class="p">.</span><span class="nx">Pod</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;pods/attach&#34;</span><span class="p">:</span>      <span class="nx">podStorage</span><span class="p">.</span><span class="nx">Attach</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;pods/status&#34;</span><span class="p">:</span>      <span class="nx">podStorage</span><span class="p">.</span><span class="nx">Status</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;services&#34;</span><span class="p">:</span>        <span class="nx">serviceRest</span><span class="p">.</span><span class="nx">Service</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;nodes&#34;</span><span class="p">:</span>        <span class="nx">nodeStorage</span><span class="p">.</span><span class="nx">Node</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="o">...</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="nx">apiGroupInfo</span><span class="p">.</span><span class="nx">VersionedResourcesStorageMap</span><span class="p">[</span><span class="s">&#34;v1&#34;</span><span class="p">]</span> <span class="p">=</span> <span class="nx">restStorageMap</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">restStorage</span><span class="p">,</span> <span class="nx">apiGroupInfo</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>即</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">apiGroupInfo</span><span class="p">.</span><span class="nx">VersionedResourcesStorageMap</span><span class="p">[</span><span class="s">&#34;v1&#34;</span><span class="p">]</span> <span class="p">=</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="nx">rest</span><span class="p">.</span><span class="nx">Storage</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;pods&#34;</span><span class="p">:</span>             <span class="nx">podStorage</span><span class="p">.</span><span class="nx">Pod</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;pods/attach&#34;</span><span class="p">:</span>      <span class="nx">podStorage</span><span class="p">.</span><span class="nx">Attach</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;pods/status&#34;</span><span class="p">:</span>      <span class="nx">podStorage</span><span class="p">.</span><span class="nx">Status</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;services&#34;</span><span class="p">:</span>        <span class="nx">serviceRest</span><span class="p">.</span><span class="nx">Service</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="s">&#34;nodes&#34;</span><span class="p">:</span>        <span class="nx">nodeStorage</span><span class="p">.</span><span class="nx">Node</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="o">...</span><span class="p">..</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span></code></pre></div><p>此时, 根据</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// apiGroupInfo.VersionedResourcesStorageMap
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">storage</span> <span class="p">=</span> <span class="nx">apiGroupInfo</span><span class="p">.</span><span class="nx">VersionedResourcesStorageMap</span><span class="p">[</span><span class="nx">groupVersion</span><span class="p">.</span><span class="nx">Version</span><span class="p">][</span><span class="nx">path</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">creater</span><span class="p">,</span> <span class="nx">isCreater</span> <span class="o">:=</span> <span class="nx">storage</span><span class="p">.(</span><span class="nx">rest</span><span class="p">.</span><span class="nx">Creater</span><span class="p">)</span>
</span></span></code></pre></div><p>我们可以得到</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">storage</span> <span class="p">=</span> <span class="nx">apiGroupInfo</span><span class="p">.</span><span class="nx">VersionedResourcesStorageMap</span><span class="p">[</span><span class="s">&#34;v1&#34;</span><span class="p">][</span><span class="s">&#34;pods&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="c1">// equals
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">storage</span> <span class="p">=</span> <span class="nx">podStorage</span><span class="p">.</span><span class="nx">Pod</span>
</span></span><span class="line"><span class="cl"><span class="nx">creater</span><span class="p">,</span> <span class="nx">isCreater</span> <span class="o">:=</span> <span class="p">(</span><span class="nx">podStorage</span><span class="p">.</span><span class="nx">Pod</span><span class="p">).(</span><span class="nx">rest</span><span class="p">.</span><span class="nx">Creater</span><span class="p">)</span>
</span></span></code></pre></div><p>然后, 我们再看下<code>podStorage.Pod</code>的实现</p>
<h2 id="第三步-podstoragepod">第三步: podStorage.Pod</h2>
<ul>
<li>pkg/registry/core/pod/storage/storage.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">PodStorage</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Pod</span>         <span class="o">*</span><span class="nx">REST</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</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">// REST implements a RESTStorage for pods
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">REST</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="o">*</span><span class="nx">genericregistry</span><span class="p">.</span><span class="nx">Store</span>     <span class="c1">// =&gt; NOTE
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">proxyTransport</span> <span class="nx">http</span><span class="p">.</span><span class="nx">RoundTripper</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>即, <code>PodStorage.Pod</code> 类型是 <code>REST</code>, 而<code>REST.genericregistry.Store</code>, 其定义文件中存在</p>
<ul>
<li>vendor/k8s.io/apiserver/pkg/registry/generic/registry/store.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// New implements RESTStorage.New.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">e</span> <span class="o">*</span><span class="nx">Store</span><span class="p">)</span> <span class="nf">New</span><span class="p">()</span> <span class="nx">runtime</span><span class="p">.</span><span class="nx">Object</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">e</span><span class="p">.</span><span class="nf">NewFunc</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="kd">func</span> <span class="p">(</span><span class="nx">e</span> <span class="o">*</span><span class="nx">Store</span><span class="p">)</span> <span class="nf">Create</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">genericapirequest</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">obj</span> <span class="nx">runtime</span><span class="p">.</span><span class="nx">Object</span><span class="p">)</span> <span class="p">(</span><span class="nx">runtime</span><span class="p">.</span><span class="nx">Object</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>即,</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">storage</span> <span class="p">=</span> <span class="nx">apiGroupInfo</span><span class="p">.</span><span class="nx">VersionedResourcesStorageMap</span><span class="p">[</span><span class="s">&#34;v1&#34;</span><span class="p">][</span><span class="s">&#34;pods&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="c1">// equals
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">storage</span> <span class="p">=</span> <span class="nx">podStorage</span><span class="p">.</span><span class="nx">Pod</span>
</span></span><span class="line"><span class="cl"><span class="nx">creater</span><span class="p">,</span> <span class="nx">isCreater</span> <span class="o">:=</span> <span class="p">(</span><span class="nx">podStorage</span><span class="p">.</span><span class="nx">Pod</span><span class="p">).(</span><span class="nx">rest</span><span class="p">.</span><span class="nx">Creater</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// equals
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">creater</span><span class="p">,</span> <span class="nx">isCreater</span> <span class="o">:=</span> <span class="p">(</span><span class="nx">REST</span><span class="p">).(</span><span class="nx">rest</span><span class="p">.</span><span class="nx">Creater</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">creater</span><span class="p">,</span> <span class="nx">isCreater</span> <span class="o">:=</span> <span class="p">(</span><span class="o">*</span><span class="nx">genericregistry</span><span class="p">.</span><span class="nx">Store</span><span class="p">).(</span><span class="nx">rest</span><span class="p">.</span><span class="nx">Creater</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="第四步-creaternew">第四步: creater.New()</h2>
<p><img src="/imgs/k8s/apiserver-register-05.jpg" alt=""></p>
<ul>
<li>vendor/k8s.io/apiserver/pkg/registry/generic/registry/store.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// New implements RESTStorage.New.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">e</span> <span class="o">*</span><span class="nx">Store</span><span class="p">)</span> <span class="nf">New</span><span class="p">()</span> <span class="nx">runtime</span><span class="p">.</span><span class="nx">Object</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">e</span><span class="p">.</span><span class="nf">NewFunc</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>pkg/registry/core/pod/storage/storage.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewStorage</span><span class="p">(</span><span class="nx">optsGetter</span> <span class="nx">generic</span><span class="p">.</span><span class="nx">RESTOptionsGetter</span><span class="p">,</span> <span class="nx">k</span> <span class="nx">client</span><span class="p">.</span><span class="nx">ConnectionInfoGetter</span><span class="p">,</span> <span class="nx">proxyTransport</span> <span class="nx">http</span><span class="p">.</span><span class="nx">RoundTripper</span><span class="p">,</span> <span class="nx">podDisruptionBudgetClient</span> <span class="nx">policyclient</span><span class="p">.</span><span class="nx">PodDisruptionBudgetsGetter</span><span class="p">)</span> <span class="nx">PodStorage</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">store</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">genericregistry</span><span class="p">.</span><span class="nx">Store</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">NewFunc</span><span class="p">:</span>     <span class="kd">func</span><span class="p">()</span> <span class="nx">runtime</span><span class="p">.</span><span class="nx">Object</span> <span class="p">{</span> <span class="k">return</span> <span class="o">&amp;</span><span class="nx">api</span><span class="p">.</span><span class="nx">Pod</span><span class="p">{}</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">		<span class="o">...</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">// pkg/api/types.go
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Pod</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">metav1</span><span class="p">.</span><span class="nx">TypeMeta</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// +optional
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">metav1</span><span class="p">.</span><span class="nx">ObjectMeta</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// Spec defines the behavior of a pod.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// +optional
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">Spec</span> <span class="nx">PodSpec</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// Status represents the current information about a pod. This data may not be up
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// to date.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// +optional
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">Status</span> <span class="nx">PodStatus</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>etcd相关的, 在后面介绍</p>
]]></content>
		</item>
		
		<item>
			<title>k8s APIServer源码: api注册主体流程</title>
			<link>https://wklken.me/posts/2017/09/23/source-apiserver-03.html</link>
			<pubDate>Sat, 23 Sep 2017 14:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2017/09/23/source-apiserver-03.html</guid>
			<description>基于版本 1.6.7 k8s使用了go-restful github, 在前面, 已经介绍了container如何初始化的. 这里, 需要关注, api是如何注册进来的. 即, route</description>
			<content type="html"><![CDATA[<p>基于版本 1.6.7</p>
<p><img src="/imgs/k8s/apiserver-register-01.jpg" alt=""></p>
<p>k8s使用了<a href="https://github.com/emicklei/go-restful">go-restful github</a>, 在前面, 已经介绍了<code>container</code>如何初始化的.</p>
<p>这里, 需要关注, api是如何注册进来的. 即, <code>route -&gt; webservice -&gt; container</code></p>
<h3 id="begin">begin</h3>
<ul>
<li>pkg/master/master.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="nx">completedConfig</span><span class="p">)</span> <span class="nf">New</span><span class="p">()</span> <span class="p">(</span><span class="o">*</span><span class="nx">Master</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">   <span class="c1">//  register /api
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>   <span class="nx">m</span><span class="p">.</span><span class="nf">InstallLegacyAPI</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">Config</span><span class="p">,</span> <span class="nx">c</span><span class="p">.</span><span class="nx">Config</span><span class="p">.</span><span class="nx">GenericConfig</span><span class="p">.</span><span class="nx">RESTOptionsGetter</span><span class="p">,</span> <span class="nx">legacyRESTStorageProvider</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="c1">//  register /apis
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>   <span class="nx">m</span><span class="p">.</span><span class="nf">InstallAPIs</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">Config</span><span class="p">.</span><span class="nx">APIResourceConfigSource</span><span class="p">,</span> <span class="nx">c</span><span class="p">.</span><span class="nx">Config</span><span class="p">.</span><span class="nx">GenericConfig</span><span class="p">.</span><span class="nx">RESTOptionsGetter</span><span class="p">,</span> <span class="nx">restStorageProviders</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="1-api">1. /api</h3>
<ul>
<li>pkg/master/master.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">Master</span><span class="p">)</span> <span class="nf">InstallLegacyAPI</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">Config</span><span class="p">,</span> <span class="nx">restOptionsGetter</span> <span class="nx">generic</span><span class="p">.</span><span class="nx">RESTOptionsGetter</span><span class="p">,</span> <span class="nx">legacyRESTStorageProvider</span> <span class="nx">corerest</span><span class="p">.</span><span class="nx">LegacyRESTStorageProvider</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">legacyRESTStorage</span><span class="p">,</span> <span class="nx">apiGroupInfo</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">legacyRESTStorageProvider</span><span class="p">.</span><span class="nf">NewLegacyRESTStorage</span><span class="p">(</span><span class="nx">restOptionsGetter</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">m</span><span class="p">.</span><span class="nx">GenericAPIServer</span><span class="p">.</span><span class="nf">InstallLegacyAPIGroup</span><span class="p">(</span><span class="nx">genericapiserver</span><span class="p">.</span><span class="nx">DefaultLegacyAPIPrefix</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">apiGroupInfo</span><span class="p">)</span>
</span></span></code></pre></div><ul>
<li>vendor/k8s.io/apiserver/pkg/server/genericapiserver.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">GenericAPIServer</span><span class="p">)</span> <span class="nf">InstallLegacyAPIGroup</span><span class="p">(</span><span class="nx">apiPrefix</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">apiGroupInfo</span> <span class="o">*</span><span class="nx">APIGroupInfo</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">s</span><span class="p">.</span><span class="nf">installAPIResources</span><span class="p">(</span><span class="nx">apiPrefix</span><span class="p">,</span> <span class="nx">apiGroupInfo</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="2-apis">2. /apis</h3>
<ul>
<li>pkg/master/master.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">Master</span><span class="p">)</span> <span class="nf">InstallAPIs</span><span class="p">(</span><span class="nx">apiResourceConfigSource</span> <span class="nx">serverstorage</span><span class="p">.</span><span class="nx">APIResourceConfigSource</span><span class="p">,</span> <span class="nx">restOptionsGetter</span> <span class="nx">generic</span><span class="p">.</span><span class="nx">RESTOptionsGetter</span><span class="p">,</span> <span class="nx">restStorageProviders</span> <span class="o">...</span><span class="nx">RESTStorageProvider</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">i</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">apiGroupsInfo</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">m</span><span class="p">.</span><span class="nx">GenericAPIServer</span><span class="p">.</span><span class="nf">InstallAPIGroup</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">apiGroupsInfo</span><span class="p">[</span><span class="nx">i</span><span class="p">])</span>	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>vendor/k8s.io/apiserver/pkg/server/genericapiserver.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">GenericAPIServer</span><span class="p">)</span> <span class="nf">InstallAPIGroup</span><span class="p">(</span><span class="nx">apiGroupInfo</span> <span class="o">*</span><span class="nx">APIGroupInfo</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">s</span><span class="p">.</span><span class="nf">installAPIResources</span><span class="p">(</span><span class="nx">APIGroupPrefix</span><span class="p">,</span> <span class="nx">apiGroupInfo</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="3-all-to-installapiresources">3. all to installAPIResources</h3>
<ul>
<li>vendor/k8s.io/apiserver/pkg/server/genericapiserver.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">GenericAPIServer</span><span class="p">)</span> <span class="nf">installAPIResources</span><span class="p">(</span><span class="nx">apiPrefix</span> <span class="kt">string</span><span class="p">,</span> <span class="nx">apiGroupInfo</span> <span class="o">*</span><span class="nx">APIGroupInfo</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">groupVersion</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">apiGroupInfo</span><span class="p">.</span><span class="nx">GroupMeta</span><span class="p">.</span><span class="nx">GroupVersions</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">apiGroupVersion</span><span class="p">.</span><span class="nf">InstallREST</span><span class="p">(</span><span class="nx">s</span><span class="p">.</span><span class="nx">HandlerContainer</span><span class="p">.</span><span class="nx">Container</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></code></pre></div><ul>
<li>vendor/k8s.io/apiserver/pkg/endpoints/groupversion.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">g</span> <span class="o">*</span><span class="nx">APIGroupVersion</span><span class="p">)</span> <span class="nf">InstallREST</span><span class="p">(</span><span class="nx">container</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">Container</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">installer</span> <span class="o">:=</span> <span class="nx">g</span><span class="p">.</span><span class="nf">newInstaller</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// 新建一个WebService
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">ws</span> <span class="o">:=</span> <span class="nx">installer</span><span class="p">.</span><span class="nf">NewWebService</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// 关键, URL注册, add router into ws
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">apiResources</span><span class="p">,</span> <span class="nx">registrationErrors</span> <span class="o">:=</span> <span class="nx">installer</span><span class="p">.</span><span class="nf">Install</span><span class="p">(</span><span class="nx">ws</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">lister</span> <span class="o">:=</span> <span class="nx">g</span><span class="p">.</span><span class="nx">ResourceLister</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">lister</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">lister</span> <span class="p">=</span> <span class="nx">staticLister</span><span class="p">{</span><span class="nx">apiResources</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="nf">AddSupportedResourcesWebService</span><span class="p">(</span><span class="nx">g</span><span class="p">.</span><span class="nx">Serializer</span><span class="p">,</span> <span class="nx">ws</span><span class="p">,</span> <span class="nx">g</span><span class="p">.</span><span class="nx">GroupVersion</span><span class="p">,</span> <span class="nx">lister</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// container.add(webservice)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">container</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="nx">ws</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">utilerrors</span><span class="p">.</span><span class="nf">NewAggregate</span><span class="p">(</span><span class="nx">registrationErrors</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>在这里</p>
<ol>
<li>新建一个<code>WebService</code></li>
<li>由<code>installer.Install(ws)</code>将API 对应的<code>route</code>新建初始化后, 加入到 <code>WebService</code></li>
<li>将<code>WebService</code>加入到<code>Container</code></li>
</ol>
<p>完成了<code>router -&gt; webservice -&gt; container</code>的流程</p>
<p>后面, 分析 <code>installer.Install(ws)</code> 具体做了哪些事情(<code>vendor/k8s.io/apiserver/pkg/endpoints/installer.go</code>)</p>
]]></content>
		</item>
		
		<item>
			<title>k8s APIServer源码: 服务启动</title>
			<link>https://wklken.me/posts/2017/09/23/source-apiserver-02.html</link>
			<pubDate>Sat, 23 Sep 2017 13:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2017/09/23/source-apiserver-02.html</guid>
			<description>基于版本 1.6.7 启动流程 cmd/kube-apiserver/apiserver.go func main() { app.Run(s) } cmd/kube-apiserver/app/server.go func Run(s *options.ServerRunOptions) error { // 构建master配置信息 config, sharedInformers, err := BuildMasterConfig(s) // 调用RunServer return RunServer(config, sharedInformers, wait.NeverStop) } func RunServer(config *master.Config, sharedInformers informers.SharedInformerFactory, stopCh &amp;lt;-chan struct{}) error { // 执行相</description>
			<content type="html"><![CDATA[<p>基于版本 1.6.7</p>
<h2 id="启动流程">启动流程</h2>
<p><img src="/imgs/k8s/apiserver-start-01.jpg" alt=""></p>
<ul>
<li>cmd/kube-apiserver/apiserver.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="nx">app</span><span class="p">.</span><span class="nf">Run</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><ul>
<li>cmd/kube-apiserver/app/server.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Run</span><span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">options</span><span class="p">.</span><span class="nx">ServerRunOptions</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// 构建master配置信息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">config</span><span class="p">,</span> <span class="nx">sharedInformers</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">BuildMasterConfig</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// 调用RunServer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="k">return</span> <span class="nf">RunServer</span><span class="p">(</span><span class="nx">config</span><span class="p">,</span> <span class="nx">sharedInformers</span><span class="p">,</span> <span class="nx">wait</span><span class="p">.</span><span class="nx">NeverStop</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="kd">func</span> <span class="nf">RunServer</span><span class="p">(</span><span class="nx">config</span> <span class="o">*</span><span class="nx">master</span><span class="p">.</span><span class="nx">Config</span><span class="p">,</span> <span class="nx">sharedInformers</span> <span class="nx">informers</span><span class="p">.</span><span class="nx">SharedInformerFactory</span><span class="p">,</span> <span class="nx">stopCh</span> <span class="o">&lt;-</span><span class="kd">chan</span> <span class="kd">struct</span><span class="p">{})</span> <span class="kt">error</span> <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"></span>	<span class="nx">m</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">config</span><span class="p">.</span><span class="nf">Complete</span><span class="p">().</span><span class="nf">New</span><span class="p">()</span>     <span class="c1">// =&gt; TO: Container初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// 启动
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="k">return</span> <span class="nx">m</span><span class="p">.</span><span class="nx">GenericAPIServer</span><span class="p">.</span><span class="nf">PrepareRun</span><span class="p">().</span><span class="nf">Run</span><span class="p">(</span><span class="nx">stopCh</span><span class="p">)</span>  <span class="c1">// =&gt; next
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>vendor/k8s.io/apiserver/pkg/server/genericapiserver.go</li>
</ul>
<p>启动主体函数都在这个文件中, 绑定地址/端口号, 并最终启动</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="nx">preparedGenericAPIServer</span><span class="p">)</span> <span class="nf">Run</span><span class="p">(</span><span class="nx">stopCh</span> <span class="o">&lt;-</span><span class="kd">chan</span> <span class="kd">struct</span><span class="p">{})</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">s</span><span class="p">.</span><span class="nf">NonBlockingRun</span><span class="p">(</span><span class="nx">stopCh</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="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="nx">preparedGenericAPIServer</span><span class="p">)</span> <span class="nf">NonBlockingRun</span><span class="p">(</span><span class="nx">stopCh</span> <span class="o">&lt;-</span><span class="kd">chan</span> <span class="kd">struct</span><span class="p">{})</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">s</span><span class="p">.</span><span class="nf">serveSecurely</span><span class="p">(</span><span class="nx">internalStopCh</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// or
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">s</span><span class="p">.</span><span class="nf">serveInsecurely</span><span class="p">(</span><span class="nx">internalStopCh</span><span class="p">)</span> <span class="c1">// =&gt; next
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>vendor/k8s.io/apiserver/pkg/server/serve.go</li>
</ul>
<pre tabindex="0"><code>func (s *GenericAPIServer) serveInsecurely(stopCh &lt;-chan struct{}) error {
	insecureServer := &amp;http.Server{
		Addr:           s.InsecureServingInfo.BindAddress,
		Handler:        s.InsecureHandler,   // s.Hnalder for secure
		MaxHeaderBytes: 1 &lt;&lt; 20,
	}
   runServer(insecureServer, s.InsecureServingInfo.BindNetwork, stopCh) // =&gt; next
}


func runServer(server *http.Server, network string, stopCh &lt;-chan struct{}) (int, error) {
	go func() {
		for {
			var listener net.Listener
			listener = tcpKeepAliveListener{ln.(*net.TCPListener)}
			// *http.Server
			err := server.Serve(listener)
			}
	}()
}
</code></pre><h2 id="container初始化">Container初始化</h2>
<ul>
<li>cmd/kube-apiserver/app/server.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">RunServer</span><span class="p">(</span><span class="nx">config</span> <span class="o">*</span><span class="nx">master</span><span class="p">.</span><span class="nx">Config</span><span class="p">,</span> <span class="nx">sharedInformers</span> <span class="nx">informers</span><span class="p">.</span><span class="nx">SharedInformerFactory</span><span class="p">,</span> <span class="nx">stopCh</span> <span class="o">&lt;-</span><span class="kd">chan</span> <span class="kd">struct</span><span class="p">{})</span> <span class="kt">error</span> <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"></span>	<span class="nx">m</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">config</span><span class="p">.</span><span class="nf">Complete</span><span class="p">().</span><span class="nf">New</span><span class="p">()</span>     <span class="c1">// =&gt; TO: Container初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// 启动
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="k">return</span> <span class="nx">m</span><span class="p">.</span><span class="nx">GenericAPIServer</span><span class="p">.</span><span class="nf">PrepareRun</span><span class="p">().</span><span class="nf">Run</span><span class="p">(</span><span class="nx">stopCh</span><span class="p">)</span>  <span class="c1">// =&gt; next
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>kubernetes/pkg/master/master.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="nx">completedConfig</span><span class="p">)</span> <span class="nf">New</span><span class="p">()</span> <span class="p">(</span><span class="o">*</span><span class="nx">Master</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="c1">// m.GenericAPIServer.HandlerContainer = APIContainer,   APIContainer.Container =  restful.NewContainer()
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">s</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">Config</span><span class="p">.</span><span class="nx">GenericConfig</span><span class="p">.</span><span class="nf">SkipComplete</span><span class="p">().</span><span class="nf">New</span><span class="p">()</span> <span class="c1">// completion is done in Complete, no need for a second time
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>   <span class="nx">m</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">Master</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">GenericAPIServer</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><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>vendor/k8s.io/apiserver/pkg/server/config.go</li>
</ul>
<p>到这里, 完成了 <code>s.Handler, s.InsecureHandler</code> 的初始化</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="nx">completedConfig</span><span class="p">)</span> <span class="nf">New</span><span class="p">()</span> <span class="p">(</span><span class="o">*</span><span class="nx">GenericAPIServer</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">s</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">GenericAPIServer</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="c1">// s.HandlerContainer = APIContainer
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  	<span class="nx">s</span><span class="p">.</span><span class="nx">HandlerContainer</span> <span class="p">=</span> <span class="nx">mux</span><span class="p">.</span><span class="nf">NewAPIContainer</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nf">NewServeMux</span><span class="p">(),</span> <span class="nx">c</span><span class="p">.</span><span class="nx">Serializer</span><span class="p">)</span>  <span class="c1">// =&gt; next 1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="c1">// 生成 Handler
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  	<span class="nx">s</span><span class="p">.</span><span class="nx">Handler</span><span class="p">,</span> <span class="nx">s</span><span class="p">.</span><span class="nx">InsecureHandler</span> <span class="p">=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">BuildHandlerChainsFunc</span><span class="p">(</span><span class="nx">s</span><span class="p">.</span><span class="nx">HandlerContainer</span><span class="p">.</span><span class="nx">ServeMux</span><span class="p">,</span> <span class="nx">c</span><span class="p">.</span><span class="nx">Config</span><span class="p">)</span>  <span class="c1">// =&gt; next 2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>1: vendor/k8s.io/apiserver/pkg/server/mux/container.go</li>
</ul>
<p>新建一个<code>APIContainer</code>, 包含</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// NewAPIContainer constructs a new container for APIs
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">NewAPIContainer</span><span class="p">(</span><span class="nx">mux</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">ServeMux</span><span class="p">,</span> <span class="nx">s</span> <span class="nx">runtime</span><span class="p">.</span><span class="nx">NegotiatedSerializer</span><span class="p">)</span> <span class="o">*</span><span class="nx">APIContainer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">c</span> <span class="o">:=</span> <span class="nx">APIContainer</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="c1">// 新建一个Container
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>		<span class="nx">Container</span><span class="p">:</span> <span class="nx">restful</span><span class="p">.</span><span class="nf">NewContainer</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">		<span class="nx">NonSwaggerRoutes</span><span class="p">:</span> <span class="nx">PathRecorderMux</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">mux</span><span class="p">:</span> <span class="nx">mux</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="nx">UnlistedRoutes</span><span class="p">:</span> <span class="nx">mux</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="c1">// 配置 http.ServerMux
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">c</span><span class="p">.</span><span class="nx">Container</span><span class="p">.</span><span class="nx">ServeMux</span> <span class="p">=</span> <span class="nx">mux</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// 配置路由方式, 使用CurlyRouter
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">c</span><span class="p">.</span><span class="nx">Container</span><span class="p">.</span><span class="nf">Router</span><span class="p">(</span><span class="nx">restful</span><span class="p">.</span><span class="nx">CurlyRouter</span><span class="p">{})</span> <span class="c1">// e.g. for proxy/{kind}/{name}/{*}
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="k">return</span> <span class="o">&amp;</span><span class="nx">c</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>2: vendor/k8s.io/apiserver/pkg/server/config.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Config</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">BuildHandlerChainsFunc</span> <span class="kd">func</span><span class="p">(</span><span class="nx">apiHandler</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">,</span> <span class="nx">c</span> <span class="o">*</span><span class="nx">Config</span><span class="p">)</span> <span class="p">(</span><span class="nx">secure</span><span class="p">,</span> <span class="nx">insecure</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</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="kd">func</span> <span class="nf">NewConfig</span><span class="p">()</span> <span class="o">*</span><span class="nx">Config</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="o">&amp;</span><span class="nx">Config</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">BuildHandlerChainsFunc</span><span class="p">:</span>      <span class="nx">DefaultBuildHandlerChain</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></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">DefaultBuildHandlerChain</span><span class="p">(</span><span class="nx">apiHandler</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">,</span> <span class="nx">c</span> <span class="o">*</span><span class="nx">Config</span><span class="p">)</span> <span class="p">(</span><span class="nx">secure</span><span class="p">,</span> <span class="nx">insecure</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nf">generic</span><span class="p">(</span><span class="nf">protect</span><span class="p">(</span><span class="nx">apiHandler</span><span class="p">)),</span> <span class="nf">generic</span><span class="p">(</span><span class="nf">audit</span><span class="p">(</span><span class="nx">apiHandler</span><span class="p">))</span> <span class="c1">// add filters to handler
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>注意, 这里传递的参数是: <code>s.HandlerContainer.ServeMux</code>, <code>DefaultBuildHandlerChain</code>的参数是<code>apiHandler http.Handler</code>, 前者包含后者<code>interface</code>定义的方法.</p>
<ul>
<li>net/http/server.go</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Handler</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="o">*</span><span class="nx">Request</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="c1">// ServeHTTP dispatches the request to the handler whose
</span></span></span><span class="line"><span class="cl"><span class="c1">// pattern most closely matches the request URL.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">mux</span> <span class="o">*</span><span class="nx">ServeMux</span><span class="p">)</span> <span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span> <span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">r</span><span class="p">.</span><span class="nx">RequestURI</span> <span class="o">==</span> <span class="s">&#34;*&#34;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="nx">r</span><span class="p">.</span><span class="nf">ProtoAtLeast</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nx">w</span><span class="p">.</span><span class="nf">Header</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;Connection&#34;</span><span class="p">,</span> <span class="s">&#34;close&#34;</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="nx">w</span><span class="p">.</span><span class="nf">WriteHeader</span><span class="p">(</span><span class="nx">StatusBadRequest</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="nx">h</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">mux</span><span class="p">.</span><span class="nf">Handler</span><span class="p">(</span><span class="nx">r</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">h</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">r</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><img src="/imgs/k8s/apiserver-start-02.jpg" alt=""></p>
<p>初始化后, <code>Hnalder</code> 以及 <code>InsecureHandler</code>赋值Container, 然后在<code>new Server</code>前, 将<code>handler</code>放入</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Server</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">Addr</span><span class="p">:</span>           <span class="nx">s</span><span class="p">.</span><span class="nx">InsecureServingInfo</span><span class="p">.</span><span class="nx">BindAddress</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">		<span class="nx">Handler</span><span class="p">:</span>        <span class="nx">s</span><span class="p">.</span><span class="nx">InsecureHandler</span><span class="p">,</span>   <span class="c1">// s.Hanlder for secure
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>		<span class="nx">MaxHeaderBytes</span><span class="p">:</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">20</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>k8s APIServer源码: go-restful框架</title>
			<link>https://wklken.me/posts/2017/09/23/source-apiserver-01.html</link>
			<pubDate>Sat, 23 Sep 2017 12:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2017/09/23/source-apiserver-01.html</guid>
			<description>基于版本 1.6.7 k8s的APIServer, 使用了go-restful作为其处理框架, 注册资源并接收处理 HTTP 请求. 在阅读APIServer源码之前,</description>
			<content type="html"><![CDATA[<p>基于版本 1.6.7</p>
<p>k8s的APIServer, 使用了<a href="https://github.com/emicklei/go-restful">go-restful</a>作为其处理框架, 注册资源并接收处理 HTTP 请求.</p>
<p>在阅读APIServer源码之前, 需先理解go-restful主要概念.</p>
<h2 id="intro">intro</h2>
<blockquote>
<p>package for building REST-style Web Services using Google Go</p>
</blockquote>
<p><a href="https://github.com/emicklei/go-restful">go-restful github</a></p>
<p><img src="/imgs/k8s/go-restful.png" alt="go-restfu"></p>
<h2 id="concepts">concepts</h2>
<ul>
<li>Container: 一组WebService的集合, 目的: <code>Containers for WebServices on different HTTP endpoints.</code></li>
<li>WebService: Route的集合; 为一组Route定义统一的 root path / 请求类型 / 响应类型</li>
<li>Route: 定义method/ULR path/调用函数/文档/参数/  <a href="https://github.com/emicklei/go-restful/blob/master/curly.go">curly</a> route; 支持正则及动态谭树</li>
<li>Filter: <code>Filters for intercepting the request → response flow on Service or Route level</code>, 可以加<code>global / Webservice / Route</code> 各自的filter</li>
</ul>
<h2 id="init-steps">init steps:</h2>
<ul>
<li>create container</li>
<li>create resource WebService</li>
<li>
<pre><code>define route, path, filter and bind to route handler
</code></pre>
</li>
<li>
<pre><code>add router to WebService
</code></pre>
</li>
<li>add WebService to container</li>
<li>new server with <code>Handler=container</code></li>
<li>start server</li>
</ul>
<h2 id="examples">examples</h2>
<ul>
<li>示例1: use default containers</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">ws</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">restful</span><span class="p">.</span><span class="nx">WebService</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">ws</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">	<span class="nf">Path</span><span class="p">(</span><span class="s">&#34;/users&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">	<span class="nf">Consumes</span><span class="p">(</span><span class="nx">restful</span><span class="p">.</span><span class="nx">MIME_XML</span><span class="p">,</span> <span class="nx">restful</span><span class="p">.</span><span class="nx">MIME_JSON</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">	<span class="nf">Produces</span><span class="p">(</span><span class="nx">restful</span><span class="p">.</span><span class="nx">MIME_JSON</span><span class="p">,</span> <span class="nx">restful</span><span class="p">.</span><span class="nx">MIME_XML</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">ws</span><span class="p">.</span><span class="nf">Route</span><span class="p">(</span><span class="nx">ws</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/{user-id}&#34;</span><span class="p">).</span><span class="nf">To</span><span class="p">(</span><span class="nx">u</span><span class="p">.</span><span class="nx">findUser</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">	<span class="nf">Doc</span><span class="p">(</span><span class="s">&#34;get a user&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">	<span class="nf">Param</span><span class="p">(</span><span class="nx">ws</span><span class="p">.</span><span class="nf">PathParameter</span><span class="p">(</span><span class="s">&#34;user-id&#34;</span><span class="p">,</span> <span class="s">&#34;identifier of the user&#34;</span><span class="p">).</span><span class="nf">DataType</span><span class="p">(</span><span class="s">&#34;string&#34;</span><span class="p">)).</span>
</span></span><span class="line"><span class="cl">	<span class="nf">Writes</span><span class="p">(</span><span class="nx">User</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"><span class="kd">func</span> <span class="p">(</span><span class="nx">u</span> <span class="nx">UserResource</span><span class="p">)</span> <span class="nf">findUser</span><span class="p">(</span><span class="nx">request</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">Request</span><span class="p">,</span> <span class="nx">response</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">Response</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">id</span> <span class="o">:=</span> <span class="nx">request</span><span class="p">.</span><span class="nf">PathParameter</span><span class="p">(</span><span class="s">&#34;user-id&#34;</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 class="p">}</span>
</span></span></code></pre></div><ul>
<li>示例2: 含多个container</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// https://github.com/emicklei/go-restful/blob/master/examples/restful-multi-containers.go
</span></span></span><span class="line"><span class="cl"><span class="c1">// GET http://localhost:8080/hello
</span></span></span><span class="line"><span class="cl"><span class="c1">// GET http://localhost:8081/hello
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;github.com/emicklei/go-restful&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;io&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;log&#34;</span>
</span></span><span class="line"><span class="cl">	<span class="s">&#34;net/http&#34;</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="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// add to default container
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">ws</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">restful</span><span class="p">.</span><span class="nx">WebService</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">ws</span><span class="p">.</span><span class="nf">Route</span><span class="p">(</span><span class="nx">ws</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">).</span><span class="nf">To</span><span class="p">(</span><span class="nx">hello</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">	<span class="nx">restful</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="nx">ws</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="s">&#34;:8080&#34;</span><span class="p">,</span> <span class="kc">nil</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="c1">// container 2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">container2</span> <span class="o">:=</span> <span class="nx">restful</span><span class="p">.</span><span class="nf">NewContainer</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">	<span class="nx">ws2</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">restful</span><span class="p">.</span><span class="nx">WebService</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">ws2</span><span class="p">.</span><span class="nf">Route</span><span class="p">(</span><span class="nx">ws2</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">).</span><span class="nf">To</span><span class="p">(</span><span class="nx">hello2</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">	<span class="nx">container2</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="nx">ws2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">server</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">http</span><span class="p">.</span><span class="nx">Server</span><span class="p">{</span><span class="nx">Addr</span><span class="p">:</span> <span class="s">&#34;:8081&#34;</span><span class="p">,</span> <span class="nx">Handler</span><span class="p">:</span> <span class="nx">container2</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">server</span><span class="p">.</span><span class="nf">ListenAndServe</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="kd">func</span> <span class="nf">hello</span><span class="p">(</span><span class="nx">req</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">Request</span><span class="p">,</span> <span class="nx">resp</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">Response</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">io</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">resp</span><span class="p">,</span> <span class="s">&#34;default world&#34;</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="kd">func</span> <span class="nf">hello2</span><span class="p">(</span><span class="nx">req</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">Request</span><span class="p">,</span> <span class="nx">resp</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">Response</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">io</span><span class="p">.</span><span class="nf">WriteString</span><span class="p">(</span><span class="nx">resp</span><span class="p">,</span> <span class="s">&#34;second world&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>示例3: 包含 filter</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// filter https://github.com/emicklei/go-restful/blob/master/examples/restful-filters.go
</span></span></span><span class="line"><span class="cl"><span class="c1">// Global Filter
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">globalLogging</span><span class="p">(</span><span class="nx">req</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">Request</span><span class="p">,</span> <span class="nx">resp</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">Response</span><span class="p">,</span> <span class="nx">chain</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">FilterChain</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">log</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;[global-filter (logger)] %s,%s\n&#34;</span><span class="p">,</span> <span class="nx">req</span><span class="p">.</span><span class="nx">Request</span><span class="p">.</span><span class="nx">Method</span><span class="p">,</span> <span class="nx">req</span><span class="p">.</span><span class="nx">Request</span><span class="p">.</span><span class="nx">URL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">chain</span><span class="p">.</span><span class="nf">ProcessFilter</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">resp</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="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// install a global (=DefaultContainer) filter (processed before any webservice in the DefaultContainer)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">restful</span><span class="p">.</span><span class="nf">Filter</span><span class="p">(</span><span class="nx">globalLogging</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">restful</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="nf">NewUserService</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">	<span class="nx">log</span><span class="p">.</span><span class="nf">Print</span><span class="p">(</span><span class="s">&#34;start listening on localhost:8080&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="s">&#34;:8080&#34;</span><span class="p">,</span> <span class="kc">nil</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="kd">func</span> <span class="nf">NewUserService</span><span class="p">()</span> <span class="o">*</span><span class="nx">restful</span><span class="p">.</span><span class="nx">WebService</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">ws</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">restful</span><span class="p">.</span><span class="nx">WebService</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="nx">ws</span><span class="p">.</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Path</span><span class="p">(</span><span class="s">&#34;/users&#34;</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Consumes</span><span class="p">(</span><span class="nx">restful</span><span class="p">.</span><span class="nx">MIME_XML</span><span class="p">,</span> <span class="nx">restful</span><span class="p">.</span><span class="nx">MIME_JSON</span><span class="p">).</span>
</span></span><span class="line"><span class="cl">		<span class="nf">Produces</span><span class="p">(</span><span class="nx">restful</span><span class="p">.</span><span class="nx">MIME_JSON</span><span class="p">,</span> <span class="nx">restful</span><span class="p">.</span><span class="nx">MIME_XML</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// install a webservice filter (processed before any route)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">ws</span><span class="p">.</span><span class="nf">Filter</span><span class="p">(</span><span class="nx">webserviceLogging</span><span class="p">).</span><span class="nf">Filter</span><span class="p">(</span><span class="nx">measureTime</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// install a counter filter
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">ws</span><span class="p">.</span><span class="nf">Route</span><span class="p">(</span><span class="nx">ws</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;&#34;</span><span class="p">).</span><span class="nf">Filter</span><span class="p">(</span><span class="nf">NewCountFilter</span><span class="p">().</span><span class="nx">routeCounter</span><span class="p">).</span><span class="nf">To</span><span class="p">(</span><span class="nx">getAllUsers</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// install 2 chained route filters (processed before calling findUser)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="nx">ws</span><span class="p">.</span><span class="nf">Route</span><span class="p">(</span><span class="nx">ws</span><span class="p">.</span><span class="nf">GET</span><span class="p">(</span><span class="s">&#34;/{user-id}&#34;</span><span class="p">).</span><span class="nf">Filter</span><span class="p">(</span><span class="nx">routeLogging</span><span class="p">).</span><span class="nf">Filter</span><span class="p">(</span><span class="nf">NewCountFilter</span><span class="p">().</span><span class="nx">routeCounter</span><span class="p">).</span><span class="nf">To</span><span class="p">(</span><span class="nx">findUser</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">ws</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h1 id="reference">reference</h1>
<ul>
<li><a href="https://github.com/emicklei/go-restful">go-restful github</a></li>
<li><a href="http://ernestmicklei.com/2012/11/go-restful-api-design/">api design</a></li>
<li><a href="https://github.com/emicklei/go-restful/tree/master/examples">code examples</a></li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>重构 - 读书笔记(Python示例)</title>
			<link>https://wklken.me/posts/2017/06/17/refactoring-07.html</link>
			<pubDate>Sat, 17 Jun 2017 23:39:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2017/06/17/refactoring-07.html</guid>
			<description>去年十二月, 重读时, 输出了几篇博文, 主要几章重构技巧梳理 6/7/8/9/10/11, 这周重读时, 从另一个角度总结一下 我们总是想着, 找个时间重构, 额, 其实, 重构更应该放</description>
			<content type="html"><![CDATA[<p>去年十二月, 重读时, 输出了几篇博文, 主要几章重构技巧梳理 <a href="http://www.wklken.me/posts/2016/12/03/refactoring-01.html">6</a>/<a href="http://www.wklken.me/posts/2016/12/03/refactoring-02.html">7</a>/<a href="http://www.wklken.me/posts/2016/12/03/refactoring-03.html">8</a>/<a href="http://www.wklken.me/posts/2016/12/04/refactoring-04.html">9</a>/<a href="http://www.wklken.me/posts/2016/12/04/refactoring-05.html">10</a>/<a href="http://www.wklken.me/posts/2016/12/04/refactoring-06.html">11</a>, 这周重读时, 从另一个角度总结一下</p>
<hr>
<p>我们总是想着, 找个时间重构, 额, 其实, 重构更应该放在平时, 每一次去变更代码时处理. 毕竟, 所谓的重构契机有时候太过遥远; 而如果不做重构, 痛苦的是每时每刻维护代码的自己</p>
<blockquote>
<p>如果你发现自己需要为程序添加一个特性, 而代码结构使你无法很方便地达成目的, 那就先重构那个程序, 使特性的添加比较容易进行, 然后再添加特性</p>
</blockquote>
<p>另外, 如果可能, 尽量加单元测试, 哪怕一次只增加一两个, 一段时间后, 你会发现, 你会感谢过去的自己</p>
<h2 id="原则">原则</h2>
<ul>
<li>小步前进, 频繁测试</li>
<li>隔离变化</li>
<li>控制可见范围, 让变量/常量/函数/类等, 在最小的范围内可见. 例如设为私有变量/私有函数, 移除不必要的设值函数</li>
<li>重构时, 不要关注性能. 到性能优化阶段, 再关注性能. 不同阶段关注点不一样, 不要过早优化. 很多时候, 性能并不是瓶颈, 可读性和可维护性更重要</li>
<li>任何时候, 都不要拷贝代码, 拷贝类, 甚至拷贝源码文件</li>
</ul>
<hr>
<h2 id="1-命名">1. 命名</h2>
<ul>
<li>好的名字, 清晰表达其含义. 命名至关重要</li>
<li>好的代码应该清楚表达出自己的功能, 变量名称是代码清晰的关键</li>
<li>如果为了提高代码的可读性, 需要修改某些名字, 大胆去改!</li>
<li>IDE/单元测试/好的查找替换工具</li>
<li>建议读<code>编写可读代码的艺术</code>这本书.</li>
</ul>
<h2 id="2-常量和临时变量">2. 常量和临时变量</h2>
<h4 id="提取常量">提取常量</h4>
<blockquote>
<p>你有一个字面数值, 带有特别含义. 创建一个常量, 根据其意义为它命名, 并将上述字面数值替换为这个常量</p>
</blockquote>
<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">potential_energy</span><span class="p">(</span><span class="n">mass</span><span class="p">,</span> <span class="n">height</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">mass</span> <span class="o">*</span> <span class="mf">9.81</span> <span class="o">*</span> <span class="n">height</span>
</span></span></code></pre></div><p>to</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">GRAVITATIONAL_CONSTANT</span> <span class="o">=</span> <span class="mf">9.81</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">potential_energy</span><span class="p">(</span><span class="n">mass</span><span class="p">,</span> <span class="n">height</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">mass</span> <span class="o">*</span> <span class="n">GRAVITATIONAL_CONSTANT</span> <span class="o">*</span> <span class="n">height</span>
</span></span></code></pre></div><p>任何时候, 都不要拷贝常量, 当你发现要改一个数据, 要到非常多的文件去改字面值时, 你就需要意识到, 该提取常量了</p>
<h4 id="加入-引入解释性变量">加入: 引入解释性变量</h4>
<blockquote>
<p>一个复杂的表达式, 将复杂表达式或其中一部分放入临时变量, 以变量名称来解释表达式用途</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">if</span> <span class="s2">&#34;MAC&#34;</span> <span class="ow">in</span> <span class="n">platform</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span> <span class="ow">and</span> <span class="s2">&#34;IE&#34;</span> <span class="ow">in</span> <span class="n">browser</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span> <span class="ow">and</span> <span class="n">was_initialized</span><span class="p">()</span> <span class="ow">and</span> <span class="n">resize</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1">#do something</span>
</span></span></code></pre></div><p>to</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">is_macos</span> <span class="o">=</span> <span class="s2">&#34;MAC&#34;</span> <span class="ow">in</span> <span class="n">platform</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">is_ie_browser</span> <span class="o">=</span> <span class="s2">&#34;IE&#34;</span> <span class="ow">in</span> <span class="n">browser</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">was_resized</span> <span class="o">=</span> <span class="n">resize</span> <span class="o">&gt;</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">is_macos</span> <span class="ow">and</span> <span class="n">is_ie_browser</span> <span class="ow">and</span> <span class="n">was_initialized</span><span class="p">()</span> <span class="ow">and</span> <span class="n">was_resized</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># do something</span>
</span></span></code></pre></div><h4 id="分解-分解临时变量">分解: 分解临时变量</h4>
<blockquote>
<p>某个临时变量被赋值超过一次, 非循环变量, 也不用于收集计算结果.每次赋值, 创砸一个独立, 对应的临时变量</p>
</blockquote>
<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="n">tmp</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="p">(</span><span class="n">height</span> <span class="o">*</span> <span class="n">width</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="n">tmp</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">tmp</span> <span class="o">=</span> <span class="n">height</span> <span class="o">*</span> <span class="n">width</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="n">tmp</span>
</span></span></code></pre></div><p>to</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">perimeter</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="p">(</span><span class="n">height</span> <span class="o">*</span> <span class="n">width</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="n">perimeter</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">area</span> <span class="o">=</span> <span class="n">height</span> <span class="o">*</span> <span class="n">width</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="n">area</span>
</span></span></code></pre></div><h4 id="去除-移除临时变量">去除: 移除临时变量</h4>
<blockquote>
<p>临时变量仅被一个简单表达式赋值一次, 可以去除这个临时变量</p>
</blockquote>
<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="n">best_price</span> <span class="o">=</span> <span class="n">order</span><span class="o">.</span><span class="n">base_price</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="n">best_price</span> <span class="o">&gt;</span> <span class="mi">1000</span>
</span></span></code></pre></div><p>to</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">return</span> <span class="n">order</span><span class="o">.</span><span class="n">base_price</span> <span class="o">&gt;</span> <span class="mi">1000</span>
</span></span></code></pre></div><h4 id="移除-控制标记">移除: 控制标记</h4>
<blockquote>
<p>在一系列布尔表达式中, 某个变量带有&quot;控制标记&quot;(control flag)的作用. 以break语句或return取代控制标记</p>
</blockquote>
<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">dosomething</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">is_success</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">xxx</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">       <span class="n">is_success</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">yyy</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">       <span class="n">is_success</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">is_success</span>
</span></span></code></pre></div><p>to</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">dosomething</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">xxx</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">yyy</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">False</span> <span class="c1"># 一定不要忘记</span>
</span></span></code></pre></div><p>注意力相关.</p>
<p>这类逻辑中, 很痛苦的是, 你必须无时无刻关注这些控制标记的值, <code>追踪</code>变量在每一个逻辑之后的变化, 会带来额外的思考负担, 从而让代码变得不易读.</p>
<h2 id="3-函数">3. 函数</h2>
<h4 id="拆分-extract-method提炼函数">拆分: Extract Method提炼函数</h4>
<blockquote>
<p>你有一段代码可以被组织在一起并独立出来, 将这段代码放进一个独立函数中, 并让函数名称解释该函数的用途</p>
</blockquote>
<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">print_owing</span><span class="p">(</span><span class="n">double</span> <span class="n">amount</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">print_banner</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">//</span> <span class="nb">print</span> <span class="n">details</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="s2">&#34;this is the detail: &#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="s2">&#34;amnount: </span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="n">amount</span>
</span></span></code></pre></div><p>to</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">print_details</span><span class="p">(</span><span class="n">amount</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="s2">&#34;this is the detail: &#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="s2">&#34;amnount: </span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="n">amount</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">print_owing</span><span class="p">(</span><span class="n">double</span> <span class="n">amount</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">print_banner</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">print_details</span><span class="p">(</span><span class="n">amount</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="去除-inline-method内联函数">去除: Inline Method内联函数</h4>
<blockquote>
<p>一个函数的本体与名称同样清楚易懂, 在函数调用点插入函数本体, 然后移除该函数</p>
</blockquote>
<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">is_length_valid</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="s1">&#39;the length is </span><span class="si">%s</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="s1">&#39;valid&#39;</span> <span class="k">if</span> <span class="n">is_length_valid</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">else</span> <span class="s1">&#39;invalid&#39;</span><span class="p">)</span>
</span></span></code></pre></div><p>to</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">print</span> <span class="s1">&#39;the length is </span><span class="si">%s</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="s1">&#39;valid&#39;</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">10</span> <span class="k">else</span> <span class="s1">&#39;invalid)</span>
</span></span></code></pre></div><h4 id="合并-合并多个函数-使用参数">合并: 合并多个函数, 使用参数</h4>
<blockquote>
<p>若干函数做了类似的工作. 但在函数本体中却包含了不同的值. 建立单一函数, 以参数表达那些不同的值</p>
</blockquote>
<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">five_percent_raise</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">def</span> <span class="nf">ten_percent_raise</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span></code></pre></div><p>to</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">percent_raise</span><span class="p">(</span><span class="n">percent</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span></code></pre></div><h4 id="副作用-函数不应该有副作用">副作用: 函数不应该有副作用</h4>
<blockquote>
<p>某个函数既返回对象状态值, 又修改对象状态. 建立两个不同函数, 一个负责查询, 一个负责修改.</p>
</blockquote>
<p>单一职责原则, 一个函数不应该做两件事, 函数粒度尽量小.</p>
<h2 id="4-表达式">4. 表达式</h2>
<h4 id="guard注意力相关">guard(注意力相关)</h4>
<blockquote>
<p>过多的条件逻辑, 难以理解正常的执行路径. 在python中的特征是, 缩进太深</p>
</blockquote>
<p>coolshell中曾经讨论过的问题 <a href="http://coolshell.cn/articles/17757.html">如何重构“箭头型”代码</a>, 而在python中的现象是, 缩进嵌套层级太深, 有时候甚至有十几层缩进, 整体难以理解</p>
<p>而减少嵌套缩进的方式是, 使用<code>guard</code>语句, 尽早返回,</p>
<p>注意力相关, 尽早<code>return</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">if</span> <span class="n">_is_dead</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">dead_amount</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="k">if</span> <span class="n">_is_separated</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">separated_amount</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="k">if</span> <span class="n">_is_retired</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">retired_amount</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">result</span> <span class="o">=</span> <span class="n">normal_payamount</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></code></pre></div><p>to</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">if</span> <span class="n">_is_dead</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">dead_amount</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_is_separated</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">separated_amount</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_is_retired</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">retired_amount</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">normal_payamount</span><span class="p">()</span>
</span></span></code></pre></div><h4 id="合并-合并条件表达式">合并: 合并条件表达式</h4>
<blockquote>
<p>你有一系列条件测试, 都得到相同结果. 将这些测试合并成一个条件表达式, 并将这个条件表达式提炼成为一个独立函数</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_seniority</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_months_disabled</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_is_part_time</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span>
</span></span></code></pre></div><p>to</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">if</span> <span class="n">is_not_eligible_for_disability</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span>
</span></span></code></pre></div><h4 id="分解-分解复杂条件表达式">分解: 分解复杂条件表达式</h4>
<blockquote>
<p>你有一个复杂的条件语句(if-then-else). 从if, the, else三个段落中分别提炼出独立函数</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">date</span> <span class="o">&lt;</span> <span class="n">SUMMER_START</span><span class="p">)</span> <span class="ow">or</span> <span class="n">date</span> <span class="o">&gt;</span> <span class="n">SUMMER_END</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">charge</span> <span class="o">=</span> <span class="n">quantity</span> <span class="o">*</span> <span class="n">_winter_rate</span> <span class="o">+</span> <span class="n">_winter_servioce_charge</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">charge</span> <span class="o">=</span> <span class="n">quantity</span> <span class="o">*</span> <span class="n">_summer_rate</span>
</span></span></code></pre></div><p>to</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">if</span> <span class="n">not_summber</span><span class="p">(</span><span class="n">date</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">charge</span> <span class="o">=</span> <span class="n">winter_charge</span><span class="p">(</span><span class="n">quantity</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">charge</span> <span class="o">=</span> <span class="n">summber_charge</span><span class="p">(</span><span class="n">quantity</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="提取-合并重复的条件片段">提取: 合并重复的条件片段</h4>
<blockquote>
<p>在条件表达式的每个分支上有着相同的一段代码. 将这段重复代码搬移到条件表达式之外</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">is_special</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">total</span> <span class="o">=</span> <span class="n">price</span> <span class="o">*</span> <span class="mf">0.95</span>
</span></span><span class="line"><span class="cl">    <span class="n">send</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">total</span> <span class="o">=</span> <span class="n">price</span> <span class="o">*</span> <span class="mf">0.98</span>
</span></span><span class="line"><span class="cl">    <span class="n">send</span><span class="p">()</span>
</span></span></code></pre></div><p>to</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">is_special</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">total</span> <span class="o">=</span> <span class="n">price</span> <span class="o">*</span> <span class="mf">0.95</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">total</span> <span class="o">=</span> <span class="n">price</span> <span class="o">*</span> <span class="mf">0.98</span>
</span></span><span class="line"><span class="cl"><span class="n">send</span><span class="p">()</span>
</span></span></code></pre></div><p>这是维护系统, 特别是中后期很容易忽略的问题. 很容易在代码中出现, 特别是遇到那种<code>加需求</code>的地方, 通常, 会选择不动原来的代码, 加个分支, 复制代码下来改. 但这样的后果是, 逐步地, 会发现每个分支中都有重复代码.</p>
<h2 id="5-参数及返回值">5. 参数及返回值</h2>
<h4 id="参数和返回值-提取对象">参数和返回值: 提取对象</h4>
<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">get_width_height</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">    <span class="k">return</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</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">get_area</span><span class="p">(</span><span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span>
</span></span></code></pre></div><p>to</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">Rectangle</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">width</span><span class="p">,</span> <span class="n">height</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">width</span> <span class="o">=</span> <span class="n">width</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">height</span> <span class="o">=</span> <span class="n">height</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">area</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">width</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">height</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">get_shape</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 class="k">return</span> <span class="n">Rectangle</span><span class="p">(</span><span class="n">height</span><span class="p">,</span> <span class="n">width</span><span class="p">)</span>
</span></span></code></pre></div><p>类似的还有: <code>start_time/end_time -&gt; TimeRange</code> /</p>
<h4 id="减少参数">减少参数</h4>
<blockquote>
<p>对象调用了某个函数, 并将所得结果作为参数, 传递给另一个函数. 而接受该参数的函数本身也能调用前一个函数. 让参数接收者去除该参数, 并直接调用前一个函数</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">base_price</span> <span class="o">=</span> <span class="n">quantity</span> <span class="o">*</span> <span class="n">item_price</span>
</span></span><span class="line"><span class="cl"><span class="n">discount_level</span> <span class="o">=</span> <span class="n">get_discount_level</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">final_price</span> <span class="o">=</span> <span class="n">discounted_price</span><span class="p">(</span><span class="n">base_price</span><span class="p">,</span> <span class="n">discount_level</span><span class="p">)</span>
</span></span></code></pre></div><p>to</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">base_price</span> <span class="o">=</span> <span class="n">quantity</span> <span class="o">*</span> <span class="n">item_price</span>
</span></span><span class="line"><span class="cl"><span class="n">final_price</span> <span class="o">=</span> <span class="n">discounted_price</span><span class="p">(</span><span class="n">base_price</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="6-类">6. 类</h2>
<h4 id="搬移-函数字段">搬移: 函数/字段</h4>
<ul>
<li>搬移函数: 某个函数与所在类之外的另一个类有更多的交互, 调用或被调用(例如: 使用另一个对象的次数比使用自己所在对象的次数还多). 即, 跟另一个类更相关. 则搬移过去</li>
<li>搬移字段: 某个字段被其所在类之外的另一个类更多地用到</li>
</ul>
<h4 id="拆分-拆分类">拆分: 拆分类</h4>
<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="k">class</span> <span class="nc">Persion</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">,</span> <span class="n">office_area_code</span><span class="p">,</span> <span class="n">office_number</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">age</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">office_area_code</span> <span class="o">=</span> <span class="n">office_area_code</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">office_number</span> <span class="o">=</span> <span class="n">office_number</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_phone_number</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;</span><span class="si">%s</span><span class="s2">-</span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">office_area_code</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">office_number</span><span class="p">)</span>
</span></span></code></pre></div><p>to</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">Person</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">,</span> <span class="n">office_area_code</span><span class="p">,</span> <span class="n">office_number</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">age</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">phone_number</span> <span class="o">=</span> <span class="n">PhoneNumber</span><span class="p">(</span><span class="n">office_area_code</span><span class="p">,</span> <span class="n">office_number</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">get_phone_number</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">phone_number</span><span class="o">.</span><span class="n">get_number</span><span class="p">()</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">PhoneNumber</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">area_code</span> <span class="p">,</span><span class="n">number</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">area_code</span> <span class="o">=</span> <span class="n">area_code</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">number</span> <span class="o">=</span> <span class="n">number</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_number</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;</span><span class="si">%s</span><span class="s2">-</span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">area_code</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">number</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="去除">去除</h4>
<p>一个类没有做太多的事情, 不再有独立存在的理由.</p>
<h2 id="7-模式">7. 模式</h2>
<p>原则:</p>
<ul>
<li>慎用</li>
<li>只使用你理解的模式</li>
<li>只在符合的业务场景使用对应模式</li>
</ul>
<h4 id="adapter">adapter</h4>
<p>你需要为提供服务的类增加功能, 但是你无法修改这个类.</p>
<p>使用组合(推荐, 持有对象)/继承(加子类), 持有该对象, 增加对应附加功能</p>
<p>adapter思维.</p>
<p>使用场景: 使用一些第三方库处理外部依赖, 例如依赖一个系统, <code>业务A</code>(requests)/<code>es</code>(Elasticsearch)/<code>redis</code>(redispy), 但是, 基于第三方系统, 你需要有自己业务相关的统一处理逻辑, 此时, 你可以建立一个<code>XXClient</code>, 持有第三方组件底层调用逻辑, 同时封装自身业务逻辑, 在上层直接调用</p>
<h4 id="facade">facade</h4>
<p>适配模式中举的例子, 也有<code>facade</code>的思想, 将复杂的东西, 统一封装, 对外提供相对简单清晰地接口</p>
<h4 id="template-method">template method</h4>
<p>出现的次数也很高</p>
<h4 id="装饰器">装饰器</h4>
<p>python中最常用</p>
<h4 id="其他">其他</h4>
<p>根据使用场景, 应用策略/桥梁/工厂/观察者等等, 具体看业务场景</p>
<hr>
<h2 id="举例">举例</h2>
<p>重构一个相对较大的<code>django</code>项目</p>
<ul>
<li>明确业务对象, 对象概念, 对象边界</li>
<li>明确分层</li>
<li>明确代码目录结构, 划分模块, 明确每个模块可以放入的东西</li>
<li>粗粒度重构: 移动模块/类/函数, 根据前几步的划分, 将模块/类/函数等, 移动到对应模块中, 同时, 修改<code>import</code>和调用点</li>
<li>中粒度重构: 根据<code>django</code>项目本身划分, 移动函数</li>
<li>中粒度重构: Extract Method. 读具体函数代码, 遇到 <code>重复代码 / 过长函数 / 过大的类 / 超大的if-else或switch / 包含大段注释的代码</code> 等, 思考, 提炼函数, 放入对应模块</li>
<li>细粒度重构: 提取常量 / 提取枚举 / 修改模块名类名函数名变量名</li>
</ul>
<p>举例:</p>
<ul>
<li>对于<code>django</code>项目, 原则<code>fat models, helper modules, thin views, stupid templates</code></li>
<li><code>fat model</code>, 将对象本身相关的, 尽量放入<code>models</code>, 这个对象相关的, 可以加入补充一系列<code>porperty</code>/<code>classmethod</code>/<code>staticmethod</code>, 可以有效地降低使用这个对象时调用处的代码复杂度. 例如, 每次取兑现改一个字段都需要进行转换, 则搞个<code>property</code>替换每次都需要的转换逻辑. (找拿到<code>model</code>对象后的处理逻辑代码中那些反复出现的, 重复的)</li>
<li>将对象查询相关的, 全部迁移到<code>manager</code>中, 需要先通过<code>Model.objects</code>查询然后做各种事情的, 迁移放入到<code>manager</code>中</li>
<li><code>utils</code>, 将业务逻辑无关的工具函数等, 统一归入<code>utils</code>模块中; 将业务有关但多个<code>application</code>共用的<code>utils</code>放入到<code>common.utils</code>模块中, 而将<code>appication</code>依赖的局部<code>utils</code>, 放入到<code>application.utils</code>中</li>
<li><code>constants</code>, 同上, 区分通用, 还是某个<code>applications</code>中使用</li>
<li><code>thin view</code>, 业务逻辑, 尽量瘦小简短</li>
<li><code>stupid template</code>, 模板, 尽量傻瓜, 不要包含复杂计算/判断逻辑, 将复杂迁移到后端代码</li>
</ul>
<h2 id="其他-1">其他</h2>
<p>善用工具, 有方案设计评审, 平时通过<code>pull request</code>, 走<code>code review</code>, 有代码风格自动检查, 要求单元测试, 走cicd流程. 在平时, 就有意识地控制代码质量</p>
]]></content>
		</item>
		
		<item>
			<title>写给新人的沟通建议</title>
			<link>https://wklken.me/posts/2017/04/09/suggestions-about-communication.html</link>
			<pubDate>Sun, 09 Apr 2017 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2017/04/09/suggestions-about-communication.html</guid>
			<description>最近也碰上抄袭的号, 转了我几年前两个文章, 只改了标题, 然后删掉每篇文章中我个人的一两段吐槽, 然后发出来. 抄袭当原创, 然后在评论里以作者的角度</description>
			<content type="html"><![CDATA[<p>最近也碰上抄袭的号, 转了我几年前两个文章, 只改了标题, 然后删掉每篇文章中我个人的一两段吐槽, 然后发出来. 抄袭当原创, 然后在评论里以作者的角度回复别人, 看着有点恶心. 这种成本还是太低了, 举报两次, 知乎给删掉了, 但是对于我来说还是很不爽的, 举报的成本太高了, 来一篇举报一篇. 这个号下面的文章(<a href="https://www.zhihu.com/people/calj/pins/posts">入口</a>), 目测都是直接用别人的文章改个标题了事.</p>
<hr>
<p>很多年前写过一篇在自己博客里面, <a href="http://www.wklken.me/posts/2014/04/24/unhappy-about-cooperation-and-communication.html">后端不高兴——关于协作和沟通</a>, 做后端的同学可以看看吐槽哈.</p>
<p>工作也好多年了, 前前后后跟不少新人合作过, 实际合作中不免各种问题, 都是从新人过来的, 所以打算写一些点, 算是一些感受吧</p>
<h2 id="关于问题描述">关于问题描述</h2>
<p>好像之前有人也讲过.</p>
<p>不要发: <code>hi, 在吗</code>, 你可能会发现过了很久对方回复:<code>在</code>, 然后你可能也没立即接下一句, 晚一会回复:<code>xxxx问题</code>, 然后对方又隔了很长一段时间才回&hellip;.如此来来往往, 一次沟通跨度从一两个小时到好几天</p>
<p>一般新人会觉得心累, 有点<code>玻璃心</code>的会觉得委屈, 如果事情紧急, 光自己干着急了.</p>
<p>其实<code>老鸟</code>也很累, 一般事情比较多, 各种事情混在一起, 面对这种类型沟通, 无法获取足够多的信息, 自然无法快速解决. 其实心里也想事情快速处理掉.</p>
<p>这种有点类似<code>打乒乓球</code>的沟通方式是不对的. 每次没有提供足够的信息, 而又期待对方快速反馈, 然而每个人都有自己忙碌的事情, 势必导致这类沟通十分之低效.</p>
<p>正确的沟通方式应该是: <code>倾倒式</code>的沟通.</p>
<pre tabindex="0"><code>hi xxxx:
我是xxxxx
目前遇到一个问题
现象
数据
结果

这个问题是xxxxxx?
</code></pre><p>而对方在看到你的消息后, 一次性就可以全面了解你的问题, 了然于胸, 很快就能解决问题.</p>
<p>而区别是: 一次传达的有效信息量.</p>
<h2 id="如何问题">如何问题</h2>
<p>建议不管是新手还是老鸟, 都读一读 <a href="https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md">提问的智慧</a></p>
<p>实际上, 有些时候某些问题看起来十分的<code>无奈</code></p>
<pre tabindex="0"><code>- 系统挂了       -- 一脸懵逼, 什么系统
- 接口返回错误   -- excuse me? 哪个接口? 给的参数是? 返回结果/状态码是?
- 页面有点问题   -- 哪个页面? 什么问题?

......
</code></pre><p>很是<code>无奈</code>, 很多问题看起来<code>言简意赅</code>, 但是蛋疼的也是这个, 缺乏信息量, 缺乏对问题的准确描述, 往往站在信息接收人的角度是: 一脸懵逼. 然后, 就开始来来往往的<code>沟通</code></p>
<p>所以: 如何准确描述问题, 算是提问的基本要求了</p>
<h2 id="基础问题不要问">基础问题不要问</h2>
<p>基础问题, 例如某些库的方法/参数, http状态码, 某个异常堆栈信息.</p>
<p>很多时候, 拿这类问题去问别人, 是十分低效且有害的!</p>
<p>因为: 这个时代, 搜索引擎这么强大, 技术资料如此丰富, 各类社区如此多前人踩坑, 团队的wiki如此完善, 你还拿一个如此基础的问题去问别人, 似乎说不过去吧.</p>
<p><a href="http://mp.weixin.qq.com/s?__biz=MzAxNzI4MTMwMw==&amp;mid=402360586&amp;idx=1&amp;sn=49ee00777438718a73f519742ed5c5d6#rd">只要是搜索引擎能回答的就别问别人……</a></p>
<p>也不是说不能问, 但是前提是, 自己搜索无果, 无法解决的情况下.(google, 且会基本搜索技巧)</p>
<p>这类问题, 看起来很简单是吧, 对其他人也许也就一句话的事情, 但是, 这样造成不好的结果是, 对方被你打断了! 无论多短暂, 都是打断, 断点-思考-回答-回到断点.</p>
<p>因为回答你一个本可以自己解决的问题, 而手头正在做的事情被打断.</p>
<p>而这个, 对效率影响是非常大的.</p>
<p>所以, 忠告: 不要把同事当做搜索引擎, 尊重对方.</p>
<h2 id="遇到问题-先从自己查起">遇到问题, 先从自己查起</h2>
<p>很多人遇到问题后, 第一意识是, 我的代码是ok的, 你的接口有问题.</p>
<p>然后, 直接抛给对方(对方大写懵逼)</p>
<p>而此时, 对方心里肯定第一意识也是: 我的代码是ok的, 一定不是我的问题. 此时心态上会有些变化, 然后去确认接口是不是有问题, 如果有问题也就罢了, 没问题会反向再反馈回来, 自己排查.</p>
<p>而很多时候, 要么没看文档, 没配host, 配错host, 环境不对, 没有遵循协议, 参数传错等等一系列自己代码的低级错误导致的.</p>
<p>也许这都是小事, 但当次数多了, 而大多数问题不是对方问题时, 你的信誉点已经降到了最低, 你的任何问题反馈, 无形中会被降级.</p>
<p>想象一下, 有人说你接口有问题, 然后你确认服务没问题(几分钟), 找对方要参数数据复现没问题(十几分钟), 然后到对方电脑查问题(几分钟), 一折腾半个多小时没了. 最后发现是一个低级问题(例如没配host), 你心里&hellip;..</p>
<p>所以, 先确认错误, 排查下是否是自己的问题</p>
<h2 id="不要胡乱猜测-拿数据说话">不要胡乱猜测, 拿数据说话</h2>
<p>接上个问题, 当你认为别人系统有问题时, 请拿数据说话. 当别人认为你系统有问题的时候, 请对方提供数据.</p>
<p>发现自己在跟人沟通时, 问的最多的问题是: 把调用的接口/参数/返回值/状态码/日志等等, 发给我, 谢谢.</p>
<p>发现有问题, 不要猜, 代码是确定的, 查就是了.</p>
<p>当看到报错, 从上往下查, 一层层向下跟踪, 确认输入/输出/异常/状态码等. 学会追踪调用是必备技能.</p>
<p>有足够的数据才能断定是哪的问题</p>
<p>如果是自己的问题, 修正.</p>
<p>如果是别人的问题, 拿着数据, 也方便别人定位问题.</p>
<p>特别忌讳的是: 没有数据的情况下, 猜测, 然后把问题抛出去了, 会造成组织效率低下, 可能你一个小小猜测, 花费别人很多时间. 每个人的时间都需要尊重.</p>
<h2 id="该问就问-不要害怕打断">该问就问, 不要害怕打断</h2>
<p>基于上面的几点, 假设问题自己没法解决, 需要求助, 那就求助吧.</p>
<p>不要害怕打扰别人, 一般程序员都很nice, 有问题, 把问题准备好, 精确描述, 自己的尝试, 目前遇到的点等等, 将信息汇聚, 一次性问, 然后直接去问.</p>
<p>紧急请当面, 次紧急用电话, 不大紧急, 可以用IM 工具, 对方实在忙, 可以先约个时间.</p>
<h2 id="小结">小结</h2>
<p>学会基本的沟通技巧, 善用之, 同时, 尊重他人的时间, 他人也会尊重你的时间.</p>
]]></content>
		</item>
		
		<item>
			<title>vim 杂谈 - 关于快速编辑</title>
			<link>https://wklken.me/posts/2017/03/25/vim-about-edit.html</link>
			<pubDate>Sat, 25 Mar 2017 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2017/03/25/vim-about-edit.html</guid>
			<description>在vim中写代码, 并不是, 我们所见到的代码都要一个个字符输进去, 一定不是这样的. 这篇, 让我们来谈谈如何进行快速编辑. 当然, 一篇文章只能概述,</description>
			<content type="html"><![CDATA[<p>在<code>vim</code>中写代码, 并不是, 我们所见到的代码都要一个个字符输进去, 一定不是这样的.</p>
<p>这篇, 让我们来谈谈如何进行快速编辑.</p>
<p>当然, 一篇文章只能概述, 给大家一些参考, 具体每项都可以自行找相关资料. 很多外链, 莫跳丢了</p>
<p>注意, 其中会涉及到比较多的插件, 而 <a href="https://github.com/wklken/k-vim">k-vim</a> 用的是 <a href="https://github.com/junegunn/vim-plug">vim-plug</a>, 如果你用得是其他的插件管理工具, 自行转换.</p>
<hr>
<h2 id="textobject">textobject</h2>
<p>文本对象, 是进行快速编辑的基础秘诀. 将一个单词, 句子, 段落当成一个对象看待, 可以进行快速选中/替换/删除等操作</p>
<p>有一篇文章解释得很清楚 <a href="http://blog.carbonfive.com/2011/10/17/vim-text-objects-the-definitive-guide/">Vim Text Objects: The Definitive Guide</a></p>
<p>简单说明</p>
<pre tabindex="0"><code>命令格式:  操作+范围+对象
</code></pre><p>对象</p>
<pre tabindex="0"><code>w  -  word单词
s  -  sentence句子
p  -  paragraph段落
&#39; &#34; ) ] } &gt; 等成对的
t  -  Tag标签
</code></pre><p>范围</p>
<pre tabindex="0"><code>i  -  在里面
a  -  所有, 包括成对的引号等
</code></pre><p>操作</p>
<pre tabindex="0"><code>d  -  删除
v  -  选中
c  -  替换
</code></pre><p>例子, <code>|</code>代表光标位置</p>
<pre tabindex="0"><code>123(a|bc)456

di)   删除引号内的内容   =&gt; 123()456
da)   删除引号内容, 包括引号  =&gt; 123456
vi)   选中引号内内容 abc
....
</code></pre><h2 id="textobject增强">textobject增强</h2>
<p>vim自带了很多文本对象, 但是还可以进一步增强, 例如, 以行<code>l</code>(<code>line</code>)/以文件<code>e</code>(<code>entire file</code>)/以缩进<code>i</code>(<code>indent</code>)</p>
<p>在 <a href="https://github.com/wklken/k-vim">k-vim</a> 中, 加了如下的几个文本对象, 这样, 在写<code>python</code>代码时, 你可以很方便的批量选中同一个缩进里面的所有代码块, 即使代码之间有空行.</p>
<pre tabindex="0"><code>&#34; text object
&#34; 支持自定义文本对象
Plug &#39;kana/vim-textobj-user&#39;
&#34; 增加行文本对象: l   dal yal cil
Plug &#39;kana/vim-textobj-line&#39;
&#34; 增加文件文本对象: e   dae yae cie
Plug &#39;kana/vim-textobj-entire&#39;
&#34; 增加缩进文本对象: i   dai yai cii - 相同缩进属于同一块
Plug &#39;kana/vim-textobj-indent&#39;
</code></pre><p><img src="/imgs/vim/vim-about-edit/14904505226654.jpg" alt=""></p>
<p>还有很多插件, 提供了更加丰富的文本对象, 例如, 函数中的参数等. 可以根据需要自行加入</p>
<hr>
<h2 id="代码补全">代码补全</h2>
<p>代码补全, 是必需品, 也是提升效率的大杀器.</p>
<p>对比试用过非常多的补全插件之后, 最终选定了 <a href="https://github.com/Valloric/YouCompleteMe">YouCompleteMe</a>. 快速提示/模糊匹配/跳转到函数定义等等, 总之, 非常流畅, 体验很好.</p>
<p>这个插件唯一缺点是: <code>太难装</code>. 没错, 就是太难装, 对<code>vim</code>版本有要求, 然后在天朝这种网络环境下要拉接近<code>300M</code>的文件下来不是一件很容易的事情, 下完之后还得编译<code>&gt;_&lt;#</code></p>
<p>但是, 历经千辛万苦之后, 你会发现这是值得的, 有了 <code>YCM</code>, 写代码的速度和质量能提升非常非常非常非常多.</p>
<pre tabindex="0"><code>Plug &#39;Valloric/YouCompleteMe&#39;
</code></pre><p>相关配置: <a href="https://github.com/wklken/k-vim/blob/master/vimrc.bundles#L368">k-vim vimrc.bundles#L368</a></p>
<p>几个常用快捷键<code>,jd</code>/<code>,gd</code>跳转到定义处, <code>ctrl+空格</code>主动触发补全(默认输入2个字符以上自动补, 可以什么都没输入触发补全), <code>ctrl+j/k</code>或者<code>ctrl+p/n</code>进行补全上下选中</p>
<p><img src="/imgs/vim/vim-about-edit/14904511192878.jpg" alt=""></p>
<p>如果是<code>golang</code>, 使用 <a href="https://github.com/fatih/vim-go">vim-go</a></p>
<h2 id="代码片段">代码片段</h2>
<p>代码片段, 是代码补全的互补. 当你预先定义一些常用的片段, 就能输入关键字后, 触发补全整个代码片段</p>
<p>这里推荐的插件是 <a href="https://github.com/SirVer/ultisnips">ultisnips</a>(代码片段补全工具) 配合 <a href="https://github.com/honza/vim-snippets">vim-snippets</a>(常用代码片段, 包含各类预研)</p>
<pre tabindex="0"><code>&#34; Group dependencies, vim-snippets depends on ultisnips
&#34; 代码片段快速插入 (snippets中,是代码片段资源,需要学习)
&#34; Snippets are separated from the engine. Add this if you want them:
Plug &#39;SirVer/ultisnips&#39; | Plug &#39;honza/vim-snippets&#39;
</code></pre><p>相关配置: <a href="https://github.com/wklken/k-vim/blob/master/vimrc.bundles#L410">k-vim vimrc.bundles#L410</a></p>
<p>注意, <code>ultisnips</code>快捷键和<code>YCM</code>冲突, 所以配置两个插件时需注意</p>
<p>举个栗子: 输入<code>class</code> 然后 <code>Tab</code> 触发补全, 就能进行类似完形填空的流程, 一路输入, 然后<code>Tab</code>到下一个框, 几下就能完成一段代码</p>
<p><img src="/imgs/vim/vim-about-edit/14904519598794.jpg" alt=""></p>
<p>当然, 可以自己添加补充代码片段.</p>
<pre tabindex="0"><code>let g:UltiSnipsSnippetsDir = &#39;~/.vim/UltiSnips&#39;
</code></pre><p>以<code>python</code>为例, 除了<code>vim-snippets</code>中带的那些补全关键字, 我还配置了很多单字符直接补全<code>python</code>关键字(<a href="https://github.com/wklken/k-vim/blob/master/UltiSnips/python.snippets">github python.snippets</a>)</p>
<pre tabindex="0"><code>t -&gt; True
f -&gt; False
n -&gt; None
r -&gt; return
p -&gt; print
....
</code></pre><h2 id="引号括号等及-html-标签不全">引号括号等及 html 标签不全</h2>
<p>另一个必须品, 当我们输入引号(<code>'' &quot;&quot;</code>), 括号(<code>() [] {}</code>) 以及 <code>html</code>的标签<code>&lt;a&gt;</code>, 这类都是成对出现的,</p>
<p>当我们输入一半, 自动补全另一半字符. 这里用到的插件是 <a href="https://github.com/Raimondi/delimitMate">delimitMate</a> 和 <a href="https://github.com/docunext/closetag.vim">closetag.vim</a></p>
<pre tabindex="0"><code>&#34; 自动补全单引号，双引号等
Plug &#39;Raimondi/delimitMate&#39;
&#34; 自动补全html/xml标签
Plug &#39;docunext/closetag.vim&#39;, { &#39;for&#39;: [&#39;html&#39;, &#39;xml&#39;] }
</code></pre><p>然后, 针对Python做了个优化, 可以快速补全<code>docstring</code></p>
<pre tabindex="0"><code>&#34;&#34;&#34;
&#34;&#34;&#34;
</code></pre><p>相关配置: <a href="https://github.com/wklken/k-vim/blob/master/vimrc.bundles#L442">k-vim vimrc.bundles#L442</a></p>
<p>之前一篇博客: <a href="http://www.wklken.me/posts/2015/06/07/vim-plugin-delimitmate.html">VIM插件: DELIMITMATE(符号自动补全)</a></p>
<hr>
<h2 id="多光标编辑">多光标编辑</h2>
<p>在编辑一段代码时, 例如, 想修改函数体内某个变量名, 除了用替换(查找+替换相对复杂), 还可以用 <a href="https://github.com/terryma/vim-multiple-cursors">vim-multiple-cursors</a>, 之前写的一篇博客 <a href="http://www.wklken.me/posts/2015/06/07/vim-plugin-multiplecursors.html">VIM插件: MULTIPLE-CURSORS(多光标操作)</a></p>
<pre tabindex="0"><code>&#34; 多光标选中编辑
&#34; multiplecursors
Plug &#39;terryma/vim-multiple-cursors&#39;
</code></pre><p><img src="/imgs/vim/vim-about-edit/14904526993739.jpg" alt=""></p>
<p>相关配置: <a href="https://github.com/wklken/k-vim/blob/master/vimrc.bundles#L518">k-vim vimrc.bundles#L518</a></p>
<p>配合 <a href="https://github.com/dyng/ctrlsf.vim">ctrlsf</a> 插件, 搜索后多光标直接编辑保存, 简直是重构神器, 后面细说</p>
<h2 id="快速注释">快速注释</h2>
<p>很多时候要注释多行的代码, 或者解开注释. 你需要 <a href="https://github.com/scrooloose/nerdcommenter">nerdcommenter</a></p>
<p>你只需要 <code>选中</code>(<code>shift+v+j/k</code>) 然后 <code>,cc</code>.  如果仅注释当前行的话, 可以省了<code>选中</code>这一步, 直接<code>,cc</code></p>
<p>解开注释 <code>,cu</code>(注意, 这里的<code>,</code>等于<code>&lt;leader&gt;</code>键)</p>
<pre tabindex="0"><code>&#34; quick edit
&#34; 快速注释
Plug &#39;scrooloose/nerdcommenter&#39;
</code></pre><p>演示博客: <a href="http://www.wklken.me/posts/2015/06/07/vim-plugin-nerdcommenter.html">VIM插件: NERDCOMMENTER(快速注释)</a></p>
<h2 id="修改环绕字符">修改环绕字符</h2>
<p>场景: 给单词加引号, 给十个单词加引号, 删除某一对引号/括号, 加<code>&lt;h1&gt;&lt;/h1&gt;</code>将字符串括起来</p>
<p>用到两个插件 <a href="https://github.com/tpope/vim-surround">vim-surround</a>(负责环绕字符编辑) 和 <a href="https://github.com/tpope/vim-repeat">vim-repeat</a>(负责重复)</p>
<pre tabindex="0"><code>&#34; 快速加入修改环绕字符
&#34; for repeat -&gt; enhance surround.vim, . to repeat command
Plug &#39;tpope/vim-repeat&#39; | Plug &#39;tpope/vim-surround&#39;
</code></pre><p>吐槽下, <code>vim-surround</code>快捷键特别感人&hellip;..</p>
<p>博客说明: <a href="http://www.wklken.me/posts/2015/06/13/vim-plugin-surround-repeat.html">VIM插件: SURROUND &amp; REPEAT(成对符号编辑)</a></p>
<h2 id="对齐">对齐</h2>
<p>偶尔的需求, 需要根据<code>=</code>或<code>,</code>或者空格, 将多行数据对齐, 之前的一篇博客说明 <a href="http://www.wklken.me/posts/2015/06/07/vim-plugin-easyalign.html">VIM插件: EASY-ALIGN(快速对齐)</a></p>
<pre tabindex="0"><code>&#34; easyalign
&#34; 快速赋值语句对齐
Plug &#39;junegunn/vim-easy-align&#39;
</code></pre><p>相关配置: <a href="https://github.com/wklken/k-vim/blob/master/vimrc.bundles#L466">k-vim vimrc.bundles#L466</a></p>
<h2 id="去行尾空格">去行尾空格</h2>
<p>轻微强迫症, 忍不了每行代码行尾没用的空格</p>
<pre tabindex="0"><code>&#34; trailingwhitespace
&#34; 快速去行尾空格 [, + &lt;Space&gt;]
Plug &#39;bronson/vim-trailing-whitespace&#39;
</code></pre><p>配了 <code>,+空格</code>, 一键去除当前文件所有的行尾空格</p>
<pre tabindex="0"><code>&#34; trailingwhitespace {{{
    map &lt;leader&gt;&lt;space&gt; :FixWhitespace&lt;cr&gt;
&#34; }}}
</code></pre><p>后面文章会提及<code>vim中那些被动技能</code>, 会提到如何配置保存时自动去除行尾空格</p>
<h2 id="缩进">缩进</h2>
<p>写代码时, 有时候要进行缩进, 选中后, 例如<code>shift-v</code>加<code>jk</code>上下选中多行, <code>&lt;</code>或者<code>&gt;</code>可以进行整体缩进, 但是遇到个问题, 有时候需要多次缩进, 而默认, 缩进一次后, 选中消失, 要操作, 得再次选中&hellip;..(<code>&gt;_&lt;#</code>)</p>
<p>增加如下配置, 缩进完自动选中, 可以再次缩进</p>
<pre tabindex="0"><code>&#34; 调整缩进后自动选中，方便再次操作
vnoremap &lt; &lt;gv
vnoremap &gt; &gt;gv
</code></pre><h2 id="快速替换删除最近插入的内容">快速替换/删除最近插入的内容</h2>
<p>当我们插入一段内容, 然后切换到普通模式后, 相对插入的内容进行快速选中, 然后删除或者替换, 可以</p>
<pre tabindex="0"><code>&#34; 选中并高亮最后一次插入的内容
nnoremap gv `[v`]
</code></pre><h2 id="python开发">python开发</h2>
<p>两个插件推荐</p>
<pre tabindex="0"><code>&#34; 改变编辑时缩进行为, 更友好的缩进
Plug &#39;hynek/vim-python-pep8-indent&#39;

&#34; import 排序, 强迫症福音
Plug &#39;fisadev/vim-isort&#39;
</code></pre><p>预告: 下一篇, 将快速导航, 如何在项目维度导航跳转</p>
]]></content>
		</item>
		
		<item>
			<title>vim 杂谈 - 关于移动</title>
			<link>https://wklken.me/posts/2017/03/20/vim-about-move.html</link>
			<pubDate>Mon, 20 Mar 2017 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2017/03/20/vim-about-move.html</guid>
			<description>这个专栏的主要目的, 是介绍后台开发, vim, python, 工具, 效率, 项目等等一些所思所想. 欢迎关注及交流. k-vim 这个项目, 虽然一年只更新一两次, 但是里面很多配置</description>
			<content type="html"><![CDATA[<p>这个专栏的主要目的, 是介绍后台开发, vim, python, 工具, 效率, 项目等等一些所思所想. 欢迎关注及交流.</p>
<hr>
<p><a href="https://github.com/wklken/k-vim">k-vim</a> 这个项目, 虽然一年只更新一两次, 但是里面很多配置, 都是基于使用中的痛点, 以及 <code>符合自觉</code> 的原则进行的变更.</p>
<p>整体而言, <a href="https://github.com/wklken/k-vim">k-vim</a> 虽然做成了一个开箱即用的配置, 但是很多使用者更想知道一些更具体的说明, 包含配置项, 配置原因等等.</p>
<p>之前有个blog有提过一句</p>
<blockquote>
<p>Don&rsquo;t put anything in your .vimrc you don&rsquo;t understand!</p>
</blockquote>
<p>虽然我不是十分赞同(我更倾向于基于一个完备的配置再进行深入了解和 DIY, 毕竟从0打造成本略高).</p>
<p>但是觉得有必要, 写一写, 谈一谈<code>vim</code>中的一些配置的原因.</p>
<p>目测会成系列, 从前到后你也能个性化自己的完整配置. 也可以借鉴一些技巧和插件使用(总共会提及约<code>60+</code>插件)</p>
<p>第一篇, 移动.</p>
<h2 id="hjkl">HJKL</h2>
<p>一谈到移动, 首先想到的是<code>hjkl</code>, 最基本的上下左右</p>
<pre tabindex="0"><code>h 左移
l 右移

k 上移
j 下移 (记忆 jump)
</code></pre><p>当然, 左右只是字符间移动, 上下是行之间的移动</p>
<h2 id="优化hjkl">优化HJKL</h2>
<p>首先, 为了防止自己<code>情不自禁</code>使用 <code>上下左右</code> 方向键, 可以禁用之.</p>
<pre tabindex="0"><code>&#34; 关闭方向键, 强迫自己用 hjkl
map &lt;Left&gt; &lt;Nop&gt;
map &lt;Right&gt; &lt;Nop&gt;
map &lt;Up&gt; &lt;Nop&gt;
map &lt;Down&gt; &lt;Nop&gt;
</code></pre><p>其次, 当一行超长之后, <code>se wrap</code>, 一行就显示为多行(一个物理行 - 多个展示行), 如果是默认配置, 使用 <code>jk</code> 移动时, 将会是物理行维度的, 而直觉上应该在展示行维度跳转(视觉上), 所以加配置, 使得<code>jk</code>在展示行之间上下跳转</p>
<pre tabindex="0"><code>&#34;Treat long lines as break lines (useful when moving around in them)
&#34;se swap之后，同物理行上直接跳
nnoremap k gk
nnoremap gk k
nnoremap j gj
nnoremap gj j
</code></pre><p>另外, 当上下移动时, 默认光标到顶/到底后, 再用<code>jk</code>时, 光标是贴着终端顶部或底部的, 需要配置, 保证光标距离顶部或底部一定行数, 这样显示视觉效果更好.</p>
<pre tabindex="0"><code>&#34; 在上下移动光标时，光标的上方或下方至少会保留显示的行数
set scrolloff=7
</code></pre><h2 id="单词间移动">单词间移动</h2>
<p><code>wbe</code>, 原先是<code>wWbBeE</code>, 大小写都是有各自含义的, 但是, 更懒的做法, 只用小写<code>wbe</code>, 毕竟, 使用大写<code>WBE</code>, 你还得多按一个<code>shift</code>键, 以及, 肌肉记忆下, 操作的一瞬间, 你很难将你想要的和<code>使用大写还是小写分别代表什么动作, 句子里哪个是标点</code>关联起来.</p>
<p>所以, 直接无视大写吧</p>
<pre tabindex="0"><code>w 移到下一个单词 (记忆 next word)
b 移动到单词开头 (记忆 back)
e 移动到单词尾部
</code></pre><h2 id="关于-0和-以及-hml-的优化">关于 0和$, 以及 HML 的优化</h2>
<p><code>0</code>和<code>$</code>, 分别是</p>
<pre tabindex="0"><code>0 移动到行首
$ 移动到行尾
</code></pre><p>而, <code>0</code>, 需要手指离开字母区, <code>$</code>更惨, 还得多按一个<code>shift</code>. 另外其实还有个<code>^</code>的, 实在按不着<code>&gt;_&lt;#</code></p>
<p><code>HML</code> 是同屏间, 快速移动到屏幕<code>顶部</code>/<code>中间</code>/<code>底部</code>, 顶部和底部还好, 中间到底在哪? 具体哪一行, 这个是比较范的操作, 非精确操作(不能一次性移动到想要的位置), 所以我的结论是: 废掉</p>
<p>将<code>H</code>映射成<code>移动到行首(最左边)</code>, 将<code>L</code>映射成<code>移动到行尾(最右边)</code>, 和原先<code>hjkl</code>意义同, 且在字母区. 更符合直觉</p>
<pre tabindex="0"><code>&#34; Go to home and end using capitalized directions
noremap H ^
noremap L $
</code></pre><h2 id="行内-fftt">行内: fFtT</h2>
<p><code>f/F</code>, 同一行内向前/向后跳转, 而<code>t/T</code>同, 只是会调到目标位置的前面一个字符</p>
<p>我的做法是, 忘掉<code>tT</code>, <code>fF</code>更符合直觉, 真正的<code>指哪到哪</code>, 而且<code>f=find</code>也好记</p>
<pre tabindex="0"><code>f  跳转到向前搜索的字母位置
F  跳转到向后搜索的字母位置
</code></pre><p>在这里, 推荐一个插件(被动生效), 可以高亮显示目标字母: <a href="https://github.com/unblevable/quick-scope">quick-scope</a> A Vim plugin that highlights which characters to target for f, F and family. No mappings are needed.</p>
<h2 id="文件-关于-ctrl--fbud">文件: 关于 ctrl + fbud</h2>
<p><code>ctrl + f/b</code> 下翻/上翻一页,  而<code>ctrl + u/d</code> 下翻/上翻半页.</p>
<p>我的做法是, 忘掉<code>ctrl + f/b</code>, 只用<code>ctrl + u/d</code></p>
<p>我们经常做的操作是, 不停的上翻/下翻, 连续动作, 一整页太多, 看内容很容易思维上<code>断开</code>, 需要返回去重看, 而半页有一半的东西是连着的; 而对于使用<code>压掌大法</code>按<code>ctrl</code>的人来说,  按住<code>ctrl</code>再反复<code>fb</code>, 特别是 <code>b</code>, 特别难按, <code>fb</code>都在左边, 左手太累</p>
<p>而<code>ud</code>, 可以将操作放到两手, 也方便(肌肉)记忆</p>
<pre tabindex="0"><code>ctrl + u 上翻半页(记忆 up)
ctrl + d 下翻半页(记忆 down)
</code></pre><h2 id="文件-头尾和某一行">文件: 头尾和某一行</h2>
<pre tabindex="0"><code>gg  跳转到文件头
G   跳转到文件尾

:n  精确移动到第几行
</code></pre><p>记住这三个, 基本够了吧?</p>
<h2 id="分屏移动">分屏移动</h2>
<p><code>ctrl + w + hjkl</code> 是默认分分屏的快捷键, 去掉<code>w</code>更快些</p>
<pre tabindex="0"><code>&#34; 分屏窗口移动, Smart way to move between windows
map &lt;C-j&gt; &lt;C-W&gt;j
map &lt;C-k&gt; &lt;C-W&gt;k
map &lt;C-h&gt; &lt;C-W&gt;h
map &lt;C-l&gt; &lt;C-W&gt;l
</code></pre><h2 id="插件-标记跳转增强-vim-signature">插件: 标记跳转增强 <code>vim-signature</code></h2>
<p>默认, <code>m + 字母</code>, 可以在当前打一个标记, 然后 <code>反引号+字母</code> 可以跳转到标记行</p>
<p>但是存在的缺点是: 标记不可见, 且反引号实在难按</p>
<p>所以做了些增强</p>
<p>安装这个插件: <a href="https://github.com/kshenoy/vim-signature">vim-signature</a></p>
<p>具体安装/配置/演示: <a href="http://www.wklken.me/posts/2015/06/07/vim-plugin-signature.html">VIM插件: VIM-SIGNATURE(快速标记跳转)</a></p>
<pre tabindex="0"><code>&#34; 交换 单引号/反引号, 使得可以快速使用 单引号 跳到marked位置
nnoremap &#39; `
nnoremap ` &#39;
</code></pre><p>其常用的几个快捷键</p>
<pre tabindex="0"><code>&#34; 显示marks - 方便自己进行标记和跳转
&#34; m[a-zA-Z] add mark
&#34; &#39;[a-zA-Z] go to mark
&#34; m&lt;Space&gt;  del all marks
&#34; m/        list all marks
&#34; m.        add new mark just follow previous mark
</code></pre><p><img src="/imgs/vim/vim-about-move/14899311413274.gif" alt=""></p>
<h2 id="插件-终极跳转-easymotion">插件: 终极跳转 easymotion</h2>
<p>有一个插件, 对跳转做了深入的定制, 那就是: <a href="https://github.com/easymotion/vim-easymotion">vim-easymotion</a></p>
<p>安装了之后, 简直想怎么跳就怎么跳. 作为一个必备插件, 大家有必要深入学习下用法, 跳转效率翻倍</p>
<p>之前写的一篇博文介绍: <a href="http://www.wklken.me/posts/2015/06/07/vim-plugin-easymotion.html">VIM插件: EASYMOTION(快速跳转)</a></p>
<ul>
<li>用法1: 跳转到当前光标前后的位置(w/b)</li>
<li>用法2: 搜索跳转(s)</li>
<li>用法3: 行级跳转(jk)</li>
<li>用法4: 行内跳转(hl)</li>
<li>用法5: 重复上一次动作(.)</li>
</ul>
<p>具体配置及演示见博文</p>
<h2 id="其他">其他</h2>
<p>vim中跳转的配置大抵这些就够用了.</p>
<p>下一篇, 将介绍如何快速编辑. 后续还有搜索替换等等一系列内容</p>
]]></content>
		</item>
		
		<item>
			<title>读书笔记-重构: 章11 处理概括关系</title>
			<link>https://wklken.me/posts/2016/12/04/refactoring-06.html</link>
			<pubDate>Sun, 04 Dec 2016 20:56:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/12/04/refactoring-06.html</guid>
			<description>重构的读书笔记, 简单转成python版本的code, 供参考 章11 处理概括关系 多是类相关, 浅显易懂, 不写示例代码了, 有兴趣可以看看原书的例子 11.1</description>
			<content type="html"><![CDATA[<p>重构的读书笔记, 简单转成python版本的code, 供参考</p>
<h2 id="章11-处理概括关系">章11 处理概括关系</h2>
<p>多是类相关, 浅显易懂, 不写示例代码了, 有兴趣可以看看原书的例子</p>
<h4 id="111-pull-up-field-字段上移">11.1 Pull Up Field 字段上移</h4>
<blockquote>
<p>两个子类拥有相同的字段. 将该字段移至超类</p>
</blockquote>
<h4 id="112-pull-up-method-函数上移">11.2 Pull Up Method 函数上移</h4>
<blockquote>
<p>游戏函数, 在各个子类中产生完全相同的结果. 将该函数移至超类</p>
</blockquote>
<h4 id="113-pull-up-constructor-body-构造函数本体上移">11.3 Pull Up Constructor Body 构造函数本体上移</h4>
<blockquote>
<p>你在各个子类中拥有一些构造函数, 他们的本体几乎完全一致. 在超类中新建一个构造函数, 并在子类的构造函数中调用它</p>
</blockquote>
<h4 id="114-push-down-method-函数下移">11.4 Push Down Method 函数下移</h4>
<blockquote>
<p>超类中的某个函数只与部分(而非全部)子类有关. 将这个函数移到相关的那些子类中</p>
</blockquote>
<h4 id="115-push-down-field-字段下移">11.5 Push Down Field 字段下移</h4>
<blockquote>
<p>超类中的某个字段只被部分(而非全部)子类用到. 将这个字段移到需要它的那些子类去</p>
</blockquote>
<h4 id="116-extract-subclass-提炼子类">11.6 Extract Subclass 提炼子类</h4>
<blockquote>
<p>类中的某些特性只被某些(而非全部)实例用到. 新建一个子类, 将上面所说的那一部分特性移到子类中</p>
</blockquote>
<h4 id="117-extract-superclass-提炼超类">11.7 Extract Superclass 提炼超类</h4>
<blockquote>
<p>两个类有相似特性. 为这两个类建立一个超类, 将相同特性移至超类</p>
</blockquote>
<h4 id="118-extract-interface-提炼接口">11.8 Extract Interface 提炼接口</h4>
<blockquote>
<p>若干客户使用类接口中的同一子集, 或者两个类的接口有部分相同. 将相同的子集提炼到一个独立接口中</p>
</blockquote>
<h4 id="119-collapse-hierarchy-折叠继承体系">11.9 Collapse Hierarchy 折叠继承体系</h4>
<blockquote>
<p>超类和子类之间无太大区别. 将它们合为一体</p>
</blockquote>
<h4 id="1110-from-template-method-塑造模板函数">11.10 From Template Method 塑造模板函数</h4>
<blockquote>
<p>你有一些子类, 其中相应的某些函数以相同顺序执行类似的操作, 但各个操作细节上有所不同. 将这些操作分别放进独立函数中, 并保持他们有相同的签名, 于是原函数也就变得相同了. 然后将原函数上移至超类.</p>
</blockquote>
<p>模板方法模式</p>
<h4 id="1111-replace-inheritance-with-delegation-以委托取代继承">11.11 Replace Inheritance with Delegation 以委托取代继承</h4>
<blockquote>
<p>某个子类只是用超类接口中的一部分, 或是根本不需要继承而来的数据. 在子类中新建一个字段用以保存超类; 调整子函数, 令它改而委托超类; 然后去掉两者之间的继承关系</p>
</blockquote>
<p>判断是否误用/滥用了继承</p>
<h4 id="1112-replace-delegation-with-inheritance-以继承取代委托">11.12 Replace Delegation with Inheritance 以继承取代委托</h4>
<blockquote>
<p>你在两个类之间使用了委托关系, 并经常为整个接口编写许多极简单的委托函数. 让委托类继承受托类.</p>
</blockquote>
]]></content>
		</item>
		
		<item>
			<title>读书笔记-重构: 章10 简化函数调用</title>
			<link>https://wklken.me/posts/2016/12/04/refactoring-05.html</link>
			<pubDate>Sun, 04 Dec 2016 20:55:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/12/04/refactoring-05.html</guid>
			<description>重构的读书笔记, 简单转成python版本的code, 供参考 章10: 简化函数调用 10.1 Rename Method 函数改名 函数的名称未能揭示函数的用途. 修改函数名称 给函数</description>
			<content type="html"><![CDATA[<p>重构的读书笔记, 简单转成python版本的code, 供参考</p>
<h2 id="章10-简化函数调用">章10: 简化函数调用</h2>
<h4 id="101-rename-method-函数改名">10.1 Rename Method 函数改名</h4>
<blockquote>
<p>函数的名称未能揭示函数的用途. 修改函数名称</p>
</blockquote>
<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">get_number</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;</span><span class="si">%s</span><span class="s2">-</span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="n">area_code</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
</span></span></code></pre></div><p>to</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">get_office_telephone_numer</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;</span><span class="si">%s</span><span class="s2">-</span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="n">area_code</span><span class="p">,</span> <span class="n">number</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="102-add-parameter-添加参数">10.2 Add Parameter 添加参数</h4>
<blockquote>
<p>某个函数需要从调用端得到更多信息. 为此函数添加一个对象参数, 让对象带进函数所需要的信息</p>
</blockquote>
<p>动机: 必须修改一个函数, 而修改后的函数需要一些过去没有的信息, 此时需要添加一个参数</p>
<h4 id="103-remove-parameter-移除参数">10.3 Remove Parameter 移除参数</h4>
<blockquote>
<p>函数本体不再需要某个参数. 将该参数去除</p>
</blockquote>
<p>程序员可能经常添加参数, 却往往不愿意去去除它们.</p>
<h4 id="104-separate-query-from-modifier-将查询函数和修改函数分离">10.4 Separate Query from Modifier 将查询函数和修改函数分离</h4>
<blockquote>
<p>某个函数既返回对象状态值, 又修改对象状态. 建立两个不同的函数, 其中一个负责查询, 另一个负责修改</p>
</blockquote>
<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">get_total_and_set_ready_state</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span></code></pre></div><p>to</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">get_total</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="k">def</span> <span class="nf">set_ready_ste</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span></code></pre></div><h4 id="105-parameterize-method-令函数携带参数">10.5 Parameterize Method 令函数携带参数</h4>
<blockquote>
<p>若干函数做了类似的工作. 但在函数本体中却包含了不同的值. 建立单一函数, 以参数表达那些不同的值</p>
</blockquote>
<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">five_percent_raise</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">def</span> <span class="nf">ten_percent_raise</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span></code></pre></div><p>to</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">percent_raise</span><span class="p">(</span><span class="n">percent</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span></code></pre></div><h4 id="106-replace-parameter-with-explicit-methods-以明确函数取代参数">10.6 Replace Parameter with Explicit Methods 以明确函数取代参数</h4>
<blockquote>
<p>你有一个函数, 其中完全取决于参数值而采取不同行为. 针对该参数的每一个可能值, 建立一个独立函数</p>
</blockquote>
<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">set_value</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s1">&#39;height&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">_height</span> <span class="o">=</span> <span class="n">value</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s1">&#39;width&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">_width</span> <span class="o">=</span> <span class="n">value</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span>
</span></span></code></pre></div><p>to</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">set_height</span><span class="p">(</span><span class="n">value</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="k">def</span> <span class="nf">set_width</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span></code></pre></div><h4 id="107-preserve-whole-object-保持对象完整">10.7 Preserve Whole Object 保持对象完整</h4>
<blockquote>
<p>你从某个对象中取出若干值, 将他们作为某一次函数调用时的参数. 改为传递整个对象.</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">low</span> <span class="o">=</span> <span class="n">rangle</span><span class="o">.</span><span class="n">get_low</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">high</span> <span class="o">=</span> <span class="n">rangle</span><span class="o">.</span><span class="n">get_high</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">plan</span> <span class="o">=</span> <span class="n">plan</span><span class="o">.</span><span class="n">within_rangle</span><span class="p">(</span><span class="n">low</span><span class="p">,</span> <span class="n">high</span><span class="p">)</span>
</span></span></code></pre></div><p>to</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">plan</span> <span class="o">=</span> <span class="n">plan</span><span class="o">.</span><span class="n">within_rangle</span><span class="p">(</span><span class="n">rangle</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="108-replace-parameter-with-methods-以函数取代参数">10.8 Replace Parameter with Methods 以函数取代参数</h4>
<blockquote>
<p>对象调用了某个函数, 并将所得结果作为参数, 传递给另一个函数. 而接受该参数的函数本身也能调用前一个函数. 让参数接收者去除该参数, 并直接调用前一个函数</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">base_price</span> <span class="o">=</span> <span class="n">quantity</span> <span class="o">*</span> <span class="n">item_price</span>
</span></span><span class="line"><span class="cl"><span class="n">discount_level</span> <span class="o">=</span> <span class="n">get_discount_level</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">final_price</span> <span class="o">=</span> <span class="n">discounted_price</span><span class="p">(</span><span class="n">base_price</span><span class="p">,</span> <span class="n">discount_level</span><span class="p">)</span>
</span></span></code></pre></div><p>to</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">base_price</span> <span class="o">=</span> <span class="n">quantity</span> <span class="o">*</span> <span class="n">item_price</span>
</span></span><span class="line"><span class="cl"><span class="n">final_price</span> <span class="o">=</span> <span class="n">discounted_price</span><span class="p">(</span><span class="n">discount_level</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="109-introduce-parameter-object-引入参数对象">10.9 Introduce Parameter Object 引入参数对象</h4>
<blockquote>
<p>某些参数总是很自然地同时出现. 以一个对象取代这些参数</p>
</blockquote>
<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">method_1</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">end</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="k">def</span> <span class="nf">method_2</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span></code></pre></div><p>to</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">method_1</span><span class="p">(</span><span class="n">date_range</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="k">def</span> <span class="nf">method_2</span><span class="p">(</span><span class="n">date_range</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span></code></pre></div><h4 id="1010-remove-setting-method-移除设值函数">10.10 Remove Setting Method 移除设值函数</h4>
<blockquote>
<p>类中某个字段应该在类创建时被设值, 然后就不再改变. 去掉该字段的所有设值函数.</p>
</blockquote>
<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">A</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">__value</span> <span class="o">=</span> <span class="n">value</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@property</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">value</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">__value</span>
</span></span></code></pre></div><h4 id="1011-hide-method-隐藏函数">10.11 Hide Method 隐藏函数</h4>
<blockquote>
<p>有一个函数, 从来没有被其他任何类用到. 将这个函数改成private</p>
</blockquote>
<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">A</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="nf">__get_value</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">pass</span>
</span></span></code></pre></div><h4 id="1012-replace-constructor-with-factory-method-以工厂函数取代构造函数">10.12 Replace Constructor with Factory Method 以工厂函数取代构造函数</h4>
<blockquote>
<p>你希望在创建对象时不仅仅是做简单的建构动作. 将构造函数替换为工厂函数</p>
</blockquote>
<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">Employee</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">__init__</span><span class="p">(</span><span class="bp">self</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">if</span> <span class="nb">type</span><span class="o">....</span>
</span></span></code></pre></div><p>to</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">Employee</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@staticmethod</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">create</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">if</span> <span class="nb">type</span><span class="o">....</span>
</span></span></code></pre></div><h4 id="1013-encapsulate-downcast-封装向下转型">10.13 Encapsulate Downcast 封装向下转型</h4>
<blockquote>
<p>某个函数返回的对象, 需要由函数调用者执行向下转型(downcast). 将向下转型动作移到函数中.</p>
</blockquote>
<p>强类型OO语言中. python不涉及, 但是道理相通. 如果调用某个函数, 每次都要对返回值进行处理. 那么, 可以将处理逻辑放入到函数中</p>
<h4 id="1014-replace-error-code-with-exception-以异常取代错误码">10.14 Replace Error Code with Exception 以异常取代错误码</h4>
<blockquote>
<p>某个函数返回一个特定的代码, 用以表示某种错误情况. 改用异常.</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">amount</span> <span class="o">&gt;</span> <span class="n">_balance</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="o">-</span><span class="mi">1</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">_balance</span> <span class="o">-=</span> <span class="n">amount</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span>
</span></span></code></pre></div><p>to</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">if</span> <span class="n">amount</span> <span class="o">&gt;</span> <span class="n">_balance</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="ne">Exception</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">_balance</span> <span class="o">-=</span> <span class="n">amount</span>
</span></span></code></pre></div><h4 id="1015-replace-exception-with-test-以测试取代异常">10.15 Replace Exception with Test 以测试取代异常</h4>
<blockquote>
<p>面对一个调用者可以预先检查的条件, 你抛出了一个一行. 修改调用者, 使它在调用函数之前先做检查</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">values</span><span class="p">[</span><span class="n">index</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl"><span class="k">except</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span>
</span></span></code></pre></div><p>to</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">if</span> <span class="n">index</span> <span class="o">&gt;=</span> <span class="nb">len</span><span class="p">(</span><span class="n">values</span><span class="p">)</span> <span class="ow">or</span> <span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="n">values</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
</span></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>读书笔记-重构: 章9 简化表达式</title>
			<link>https://wklken.me/posts/2016/12/04/refactoring-04.html</link>
			<pubDate>Sun, 04 Dec 2016 20:50:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/12/04/refactoring-04.html</guid>
			<description>重构的读书笔记, 简单转成python版本的code, 供参考 章9: 简化表达式 9.1 Decompose Conditional 分解条件表达式 你有一个复杂的条件语句(if-then-els</description>
			<content type="html"><![CDATA[<p>重构的读书笔记, 简单转成python版本的code, 供参考</p>
<h2 id="章9-简化表达式">章9: 简化表达式</h2>
<h4 id="91-decompose-conditional-分解条件表达式">9.1 Decompose Conditional 分解条件表达式</h4>
<blockquote>
<p>你有一个复杂的条件语句(if-then-else). 从if, the, else三个段落中分别提炼出独立函数</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">date</span> <span class="o">&lt;</span> <span class="n">SUMMER_START</span><span class="p">)</span> <span class="ow">or</span> <span class="n">date</span> <span class="o">&gt;</span> <span class="n">SUMMER_END</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">charge</span> <span class="o">=</span> <span class="n">quantity</span> <span class="o">*</span> <span class="n">_winter_rate</span> <span class="o">+</span> <span class="n">_winter_servioce_charge</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">charge</span> <span class="o">=</span> <span class="n">quantity</span> <span class="o">*</span> <span class="n">_summer_rate</span>
</span></span></code></pre></div><p>to</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">if</span> <span class="n">not_summber</span><span class="p">(</span><span class="n">date</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">charge</span> <span class="o">=</span> <span class="n">winter_charge</span><span class="p">(</span><span class="n">quantity</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">charge</span> <span class="o">=</span> <span class="n">summber_charge</span><span class="p">(</span><span class="n">quantity</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="92-consolidate-cnditional-expression-合并条件表达式">9.2 Consolidate Cnditional Expression 合并条件表达式</h4>
<blockquote>
<p>你有一系列条件测试, 都得到相同结果. 将这些测试合并成一个条件表达式, 并将这个条件表达式提炼成为一个独立函数</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_seniority</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_months_disabled</span> <span class="o">&gt;</span> <span class="mi">10</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_is_part_time</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span>
</span></span></code></pre></div><p>to</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">if</span> <span class="n">is_not_eligible_for_disability</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span>
</span></span></code></pre></div><h4 id="93-consolidate-dumplicate-conditional-fragments-合并重复的条件判断">9.3 Consolidate Dumplicate Conditional Fragments 合并重复的条件判断</h4>
<blockquote>
<p>在条件表达式的每个分支上有着相同的一段代码. 将这段重复代码搬移到条件表达式之外</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">is_special</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">total</span> <span class="o">=</span> <span class="n">price</span> <span class="o">*</span> <span class="mf">0.95</span>
</span></span><span class="line"><span class="cl">    <span class="n">send</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">total</span> <span class="o">=</span> <span class="n">price</span> <span class="o">*</span> <span class="mf">0.98</span>
</span></span><span class="line"><span class="cl">    <span class="n">send</span><span class="p">()</span>
</span></span></code></pre></div><p>to</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">if</span> <span class="n">is_special</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">total</span> <span class="o">=</span> <span class="n">price</span> <span class="o">*</span> <span class="mf">0.95</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">total</span> <span class="o">=</span> <span class="n">price</span> <span class="o">*</span> <span class="mf">0.98</span>
</span></span><span class="line"><span class="cl"><span class="n">send</span><span class="p">()</span>
</span></span></code></pre></div><h4 id="94-remove-control-flag-移除控制标记">9.4 Remove Control Flag 移除控制标记</h4>
<blockquote>
<p>在一系列布尔表达式中, 某个变量带有&quot;控制标记&quot;(control flag)的作用. 以break语句或return取代控制标记</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">found</span> <span class="o">=</span> <span class="kc">False</span>
</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">5</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="n">i</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">found</span> <span class="o">=</span> <span class="kc">True</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">found</span>
</span></span></code></pre></div><p>to</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">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="n">i</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">True</span>
</span></span></code></pre></div><h4 id="95-replace-nested-conditional-with-guard-clauses-以守卫语句取代嵌套条件表达式">9.5 Replace Nested Conditional with Guard Clauses 以守卫语句取代嵌套条件表达式</h4>
<blockquote>
<p>函数中的条件逻辑使人难以看清正常的执行路径. 使用守卫语句表现所有特殊情况</p>
</blockquote>
<p>在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="k">if</span> <span class="n">_is_dead</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">dead_amount</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="k">if</span> <span class="n">_is_separated</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">separated_amount</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="k">if</span> <span class="n">_is_retired</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">retired_amount</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">result</span> <span class="o">=</span> <span class="n">normal_payamount</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></code></pre></div><p>to</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">if</span> <span class="n">_is_dead</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">dead_amount</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_is_separated</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">separated_amount</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">_is_retired</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">retired_amount</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">normal_payamount</span><span class="p">()</span>
</span></span></code></pre></div><h4 id="96-replace-conditional-with-polymorphism-以多态取代条件表达式">9.6 Replace Conditional with Polymorphism 以多态取代条件表达式</h4>
<blockquote>
<p>你手上有多个条件表达式, 它根据对象类型的不同而选择不同的行为. 将这个条件表达式的每个分支放进一个子类内的覆写函数中, 然后将原始函数声明为抽象函数</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">area</span> <span class="o">==</span> <span class="s1">&#39;EUROPEAN&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">get_base_speed</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">elif</span> <span class="n">area</span> <span class="o">==</span> <span class="s1">&#39;AFRICAN&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">get_base_speed</span><span class="p">()</span> <span class="o">-</span> <span class="n">get_load_factor</span><span class="p">()</span> <span class="o">*</span> <span class="n">_number_of_coconuts</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="k">return</span> <span class="mi">0</span> <span class="k">if</span> <span class="n">_is_nailed</span> <span class="k">else</span> <span class="n">get_base_speed</span><span class="p">(</span><span class="n">_voltage</span><span class="p">)</span>
</span></span></code></pre></div><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">Bird</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="nf">get_base_speed</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="k">class</span> <span class="nc">European</span><span class="p">(</span><span class="n">Bird</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_base_speed</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="k">class</span> <span class="nc">African</span><span class="p">(</span><span class="n">Bird</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_base_speed</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">pass</span>
</span></span></code></pre></div><h4 id="97-introduce-null-object-引入null对象">9.7 Introduce Null Object 引入Null对象</h4>
<blockquote>
<p>你需要再三检查某对象是否为null. 将null值替换为null对象</p>
</blockquote>
<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">if</span> <span class="n">customer</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">plan</span> <span class="o">=</span> <span class="n">billing_plan</span><span class="o">.</span><span class="n">basic</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">plan</span> <span class="o">=</span> <span class="n">customer</span><span class="o">.</span><span class="n">get_plan</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">customer</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">customer_name</span> <span class="o">=</span> <span class="s1">&#39;occupant&#39;</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">customer_name</span> <span class="o">=</span> <span class="n">customer</span><span class="o">.</span><span class="n">get_name</span><span class="p">()</span>
</span></span></code></pre></div><p>to</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">Customer</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 class="k">class</span> <span class="nc">NullCustomer</span><span class="p">(</span><span class="n">Customer</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_plan</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">billing_plan</span><span class="o">.</span><span class="n">basic</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_name</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s1">&#39;occupant&#39;</span>
</span></span></code></pre></div><h4 id="98-introduce-assertion-引入断言">9.8 Introduce Assertion 引入断言</h4>
<blockquote>
<p>某一段代码需要对程序状态做出某种假设. 以断言明确表示这种假设</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># note: limit must greater 100 here</span>
</span></span><span class="line"><span class="cl"><span class="n">do</span> <span class="n">something</span>
</span></span></code></pre></div><p>to</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">assert</span> <span class="n">limit</span> <span class="o">&gt;</span> <span class="mi">100</span>
</span></span><span class="line"><span class="cl"><span class="n">do</span> <span class="n">something</span>
</span></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>读书笔记-重构: 章8 重新组织数据</title>
			<link>https://wklken.me/posts/2016/12/03/refactoring-03.html</link>
			<pubDate>Sat, 03 Dec 2016 11:10:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/12/03/refactoring-03.html</guid>
			<description>重构的读书笔记, 简单转成python版本的code, 供参考 章8: 重新组织数据 8.1 Self Encapsulate Field 自封装字段 你直接访问一个字段, 但字段间的耦合关系逐渐变额</description>
			<content type="html"><![CDATA[<p>重构的读书笔记, 简单转成python版本的code, 供参考</p>
<h2 id="章8-重新组织数据">章8: 重新组织数据</h2>
<h4 id="81-self-encapsulate-field-自封装字段">8.1 Self Encapsulate Field 自封装字段</h4>
<blockquote>
<p>你直接访问一个字段, 但字段间的耦合关系逐渐变额笨拙. 为这个字段建立取值/设置函数, 并且只以这些函数来访问字段</p>
</blockquote>
<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">A</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="nf">include</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">arg</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">low</span> <span class="o">&lt;</span> <span class="n">arg</span> <span class="o">&lt;</span> <span class="bp">self</span><span class="o">.</span><span class="n">high</span>
</span></span></code></pre></div><p>to</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">A</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="nf">include</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">arg</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">low</span> <span class="o">&lt;</span> <span class="n">arg</span> <span class="o">&lt;</span> <span class="bp">self</span><span class="o">.</span><span class="n">high</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@property</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">low</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_low</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@property</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">height</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_high</span>
</span></span></code></pre></div><h4 id="82-replace-data-value-with-object-以对象取代数据值">8.2 Replace Data Value with Object 以对象取代数据值</h4>
<blockquote>
<p>你有一个数据项, 需要与其他数据和行为一起使用才有意义</p>
</blockquote>
<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">Customer</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">def__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">area_code</span><span class="p">,</span> <span class="n">number</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">    <span class="k">def</span> <span class="nf">get_phone_number</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;</span><span class="si">%s</span><span class="s2">-</span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">area_code</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">number</span><span class="p">)</span>
</span></span></code></pre></div><p>to</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">Phone</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">area_code</span> <span class="p">,</span><span class="n">number</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 class="nd">@property</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">number</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;</span><span class="si">%s</span><span class="s2">-</span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">area_code</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">number</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">class</span> <span class="nc">Customer</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">def__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">area_code</span><span class="p">,</span> <span class="n">number</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="o">....</span>
</span></span></code></pre></div><h4 id="83-change-value-to-reference-将值对象改为饮用对象">8.3 Change Value to Reference 将值对象改为饮用对象</h4>
<blockquote>
<p>你从一个类衍生出许多彼此相等的实例, 希望将他们替换为同一个对象. 将这个值对象变成引用对象</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># only 4 types, but 10000 A instance</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">A</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">type_id</span><span class="p">,</span> <span class="n">type_name</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">type_id</span> <span class="o">=</span> <span class="n">type_id</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">type_name</span> <span class="o">=</span> <span class="n">type_name</span>
</span></span></code></pre></div><p>to</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">Type</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">__init__</span><span class="p">(</span><span class="n">type_id</span><span class="p">,</span> <span class="n">type_name</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">type_id</span> <span class="o">=</span> <span class="n">type_id</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">type_name</span> <span class="o">=</span> <span class="n">type_name</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">A</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">_type</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_type</span> <span class="o">=</span> <span class="n">_type</span>
</span></span></code></pre></div><h4 id="84-change-reference-to-value-将引用对象改为值对象">8.4 Change Reference to Value 将引用对象改为值对象</h4>
<blockquote>
<p>你有一个引用对象, 很小且值不可变, 而且不易管理. 将它变成一个值对象</p>
</blockquote>
<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">Currency</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">code</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">code</span> <span class="o">=</span> <span class="n">code</span>
</span></span></code></pre></div><p>to</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">Currency</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">code</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_code</span> <span class="o">=</span> <span class="n">code</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@property</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">code</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_code</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">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">code</span> <span class="o">==</span> <span class="n">other</span><span class="o">.</span><span class="n">code</span>
</span></span></code></pre></div><h4 id="85-replace-array-with-object-以对象取代数组">8.5 Replace Array with Object 以对象取代数组</h4>
<blockquote>
<p>你有一个数组, 其中元素各自代表不同的东西. 以对象替换数组, 对于数组中的每个元素, 以一个字段来表示</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">row</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;Liverpoo&#34;</span><span class="p">,</span> <span class="mi">15</span><span class="p">]</span>
</span></span></code></pre></div><p>to</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">claas</span> <span class="n">Record</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">wins</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">wins</span> <span class="o">=</span> <span class="n">wins</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">record</span> <span class="o">=</span> <span class="n">Record</span><span class="p">(</span><span class="s2">&#34;Liverpoo&#34;</span><span class="p">,</span> <span class="mi">15</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="86-duplicate-observed-data-复制被监视数据">8.6 Duplicate Observed Data 复制&quot;被监视数据&quot;</h4>
<blockquote>
<p>你有一些领域数据置身于 GUI 控件中, 而领域函数需要访问这些数据. 将数据复制到一个领域对象中. 建立Observer模式, 用以同步领域对象和GUI对象内的重复数据</p>
</blockquote>
<p>observer模式, 不解释</p>
<h4 id="87-change-unidirectional-association-to-bidirectional-将单向关联改为双向关联">8.7 Change Unidirectional Association to Bidirectional 将单向关联改为双向关联</h4>
<blockquote>
<p>两个类都需要使用对方特性, 但其间只有一条单向连接. 添加一个反向指针, 并使修改函数能够同时更新到两条连接</p>
</blockquote>
<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">Customer</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="nf">get_orders</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># need a lot of codes here</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">Order</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">__init__</span><span class="p">(</span><span class="bp">self</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="nd">@property</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">customer</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_customer</span>
</span></span><span class="line"><span class="cl">    <span class="nd">@customer</span><span class="o">.</span><span class="n">setter</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">customer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">customer</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_customer</span> <span class="o">=</span> <span class="n">customer</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">get_customer_address</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_customer</span><span class="o">.</span><span class="n">address</span>
</span></span></code></pre></div><p>to</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">Customer</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">orders</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="k">def</span> <span class="nf">add_order</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">order</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">orders</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">order</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">get_orders</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">orders</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">Order</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">__init__</span><span class="p">(</span><span class="bp">self</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="nd">@property</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">customer</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_customer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@customer</span><span class="o">.</span><span class="n">setter</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">customer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">customer</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_customer</span> <span class="o">=</span> <span class="n">customer</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_customer</span><span class="o">.</span><span class="n">add_order</span><span class="p">(</span><span class="bp">self</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">get_customer_address</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_customer</span><span class="o">.</span><span class="n">address</span>
</span></span></code></pre></div><h4 id="88-change-bidirectional-association-to-unidirectional-将双向关联改为单向关联">8.8 Change Bidirectional Association to Unidirectional 将双向关联改为单向关联</h4>
<blockquote>
<p>两个类之间有双向关联, 但其中一个类如今不再需要另一个类的特性了</p>
</blockquote>
<p>上面的例子, 如果<code>Customer</code>不再需要<code>get_orders</code>. 则可以去掉双向连接, 防止僵尸对象出现</p>
<h4 id="89-replace-magic-number-with-symbolic-constant-以字面常量取代魔法数">8.9 Replace Magic Number with Symbolic Constant 以字面常量取代魔法数</h4>
<blockquote>
<p>你有一个字面数值, 带有特别含义. 创建一个常量, 根据其意义为它命名, 并将上述字面数值替换为这个常量</p>
</blockquote>
<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">potential_energy</span><span class="p">(</span><span class="n">mass</span><span class="p">,</span> <span class="n">height</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">mass</span> <span class="o">*</span> <span class="mf">9.81</span> <span class="o">*</span> <span class="n">height</span>
</span></span></code></pre></div><p>to</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">GRAVITATIONAL_CONSTANT</span> <span class="o">=</span> <span class="mf">9.81</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">potential_energy</span><span class="p">(</span><span class="n">mass</span><span class="p">,</span> <span class="n">height</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">mass</span> <span class="o">*</span> <span class="n">GRAVITATIONAL_CONSTANT</span> <span class="o">*</span> <span class="n">height</span>
</span></span></code></pre></div><h4 id="810-encapsulate-field-封装字段">8.10 Encapsulate Field 封装字段</h4>
<blockquote>
<p>你的类中存在一个public字段, 将它声明为private, 并提供相应访问函数</p>
</blockquote>
<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">A</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span>
</span></span></code></pre></div><p>to</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">A</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">__value</span> <span class="o">=</span> <span class="n">value</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@property</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">value</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">__value</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@value</span><span class="o">.</span><span class="n">setter</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">value</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">v</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">__value</span> <span class="o">=</span> <span class="n">v</span>
</span></span></code></pre></div><h4 id="811-encapsulate-collection-封装集合">8.11 Encapsulate Collection 封装集合</h4>
<blockquote>
<p>有个函数返回一个集合. 让这个函数返回该集合的一个只读副本, 并在这个类中提供添加/移除集合元素的函数</p>
</blockquote>
<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">A</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_members</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_members</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_members</span>
</span></span></code></pre></div><p>to</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">A</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_members</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_members</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">tuple</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_members</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">add_member</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">member</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">members</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">member</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">remove_member</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">member</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">members</span><span class="o">.</span><span class="n">remove</span><span class="p">(</span><span class="n">member</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="812-replace-record-with-data-class-以数据类取代记录">8.12 Replace Record with Data Class 以数据类取代记录</h4>
<blockquote>
<p>你需要面对传统编程环境中的记录结构. 为该记录创建一个&quot;哑&quot;数据对象</p>
</blockquote>
<p>可能面对的是一个遗留程序, 需要与其记录进行结构交流. 例如从数据库读出来的记录, 接口调用返回数据等</p>
<h4 id="813-replace-type-code-with-class-以类取代类型码">8.13 Replace Type Code with Class 以类取代类型码</h4>
<blockquote>
<p>类之中有一个类型码, 但它并不影响类的行为. 以一个新的类替换该数值类型码</p>
</blockquote>
<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">Person</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">O</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">A</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">B</span> <span class="o">=</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">        <span class="n">slef</span><span class="o">.</span><span class="n">AB</span> <span class="o">=</span> <span class="mi">3</span>
</span></span></code></pre></div><p>to</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">Person</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">O</span> <span class="o">=</span> <span class="n">BloodGroup</span><span class="o">.</span><span class="n">O</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">A</span> <span class="o">=</span> <span class="n">BloodGroup</span><span class="o">.</span><span class="n">A</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">B</span> <span class="o">=</span> <span class="n">BloodGroup</span><span class="o">.</span><span class="n">B</span>
</span></span><span class="line"><span class="cl">        <span class="n">slef</span><span class="o">.</span><span class="n">AB</span> <span class="o">=</span> <span class="n">BloodGroup</span><span class="o">.</span><span class="n">AB</span>
</span></span></code></pre></div><h4 id="814-replace-type-code-with-subclasses-以子类取代类型码">8.14 Replace Type Code with Subclasses 以子类取代类型码</h4>
<blockquote>
<p>你有一个不可变的类型码, 它会影响到类行为. 以子类取代类型码</p>
</blockquote>
<p>使用多态来处理, 需要在一个类中使用<code>if-else</code>或者<code>switch</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">Employee</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">ENGINEER</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">SALESMAN</span> <span class="o">=</span> <span class="mi">0</span>
</span></span></code></pre></div><p>to</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">Employee</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Engineer</span><span class="p">(</span><span class="n">Employee</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">class</span> <span class="nc">Salesman</span><span class="p">(</span><span class="n">Employee</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">pass</span>
</span></span></code></pre></div><h4 id="815-replace-type-code-with-statestrategy-以statstrategy取代类型码">8.15 Replace Type Code with State/Strategy 以Stat/Strategy取代类型码</h4>
<blockquote>
<p>你有一个类型码, 它会影响类的行为, 但你无法通过继承手法消除它. 以状态对象取代类型码</p>
</blockquote>
<p>State模式和Strategy模式</p>
<h4 id="816-replace-subclass-with-fields-以字段取代子类">8.16 Replace Subclass with Fields 以字段取代子类</h4>
<blockquote>
<p>你的各个子类的唯一差别只在&quot;返回常量数据&quot;的函数上. 修改这些函数, 使它们返回超类中的某个(新增)字段, 然后销毁子类</p>
</blockquote>
<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">Person</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="nf">is_male</span><span class="p">(</span><span class="bp">self</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">def</span> <span class="nf">get_code</span><span class="p">(</span><span class="bp">self</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="k">class</span> <span class="nc">Male</span><span class="p">(</span><span class="n">Person</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">is_male</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">True</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_code</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;M&#34;</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">Feale</span><span class="p">(</span><span class="n">Person</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">is_male</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="kc">False</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_code</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;F&#34;</span>
</span></span></code></pre></div><p>to</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">Person</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="nf">is_male</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_is_male</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_code</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s1">&#39;M&#39;</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_is_male</span> <span class="k">else</span> <span class="s2">&#34;F&#34;</span>
</span></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>读书笔记-重构: 章7 在对象之间搬移特性</title>
			<link>https://wklken.me/posts/2016/12/03/refactoring-02.html</link>
			<pubDate>Sat, 03 Dec 2016 11:05:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/12/03/refactoring-02.html</guid>
			<description>重构的读书笔记, 简单转成python版本的code, 供参考 章7: 在对象之间搬移特性 7.1 Move Method 搬移函数 你的程序中, 有个函数与其所驻之外的另一个类进</description>
			<content type="html"><![CDATA[<p>重构的读书笔记, 简单转成python版本的code, 供参考</p>
<h2 id="章7-在对象之间搬移特性">章7: 在对象之间搬移特性</h2>
<h4 id="71-move-method-搬移函数">7.1 Move Method 搬移函数</h4>
<blockquote>
<p>你的程序中, 有个函数与其所驻之外的另一个类进行更多交流: 调用后者, 或者被后者调用. 在该函数最常引用的类中建立一个有着类似行为的新函数. 将就函数变成一个单纯的委托函数, 或是将就函数完全移除</p>
</blockquote>
<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">A</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="nf">count</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">b</span> <span class="o">=</span> <span class="n">B</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># a lot operations with b</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">b</span><span class="o">.</span><span class="n">compute</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">B</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="nf">compute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">pass</span>
</span></span></code></pre></div><p>to</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">A</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 class="k">class</span> <span class="nc">B</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="nf">count</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># a lot operations with b</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">compute</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">compute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">pass</span>
</span></span></code></pre></div><h4 id="72-move-field-搬移字段">7.2 Move Field 搬移字段</h4>
<blockquote>
<p>某个字段被其所驻类之外的另一个类更多地用到. 在目标类新建一个字段, 修改原字段的所有用户, 令他们改用新字段</p>
</blockquote>
<h4 id="73-extract-class-提炼类">7.3 Extract Class 提炼类</h4>
<blockquote>
<p>某个类做了应该由两个类做的事情. 建立以一个新的类, 将相关字段和函数从旧类版移到新的类</p>
</blockquote>
<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">Persion</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">,</span> <span class="n">office_area_code</span><span class="p">,</span> <span class="n">office_number</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">age</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">office_area_code</span> <span class="o">=</span> <span class="n">office_area_code</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">office_number</span> <span class="o">=</span> <span class="n">office_number</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_phone_number</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;</span><span class="si">%s</span><span class="s2">-</span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">office_area_code</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">office_number</span><span class="p">)</span>
</span></span></code></pre></div><p>to</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">Person</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">,</span> <span class="n">office_area_code</span><span class="p">,</span> <span class="n">office_number</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">age</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">phone_number</span> <span class="o">=</span> <span class="n">PhoneNumber</span><span class="p">(</span><span class="n">office_area_code</span><span class="p">,</span> <span class="n">office_number</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">get_phone_number</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">phone_number</span><span class="o">.</span><span class="n">get_number</span><span class="p">()</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">PhoneNumber</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="nf">get_number</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;</span><span class="si">%s</span><span class="s2">-</span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">area_code</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">number</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="74-inline-class-将类内聚化">7.4 Inline Class 将类内聚化</h4>
<blockquote>
<p>某个类没有做太多事情. 将这个类的所有特性移动到另一个类中, 然后移除原类</p>
</blockquote>
<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">A</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">fmt</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;[</span><span class="si">%s</span><span class="s2">]&#34;</span> <span class="o">%</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">B</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="nf">format</span><span class="p">(</span><span class="nb">object</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">a</span> <span class="o">=</span> <span class="n">A</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span> <span class="n">a</span><span class="o">.</span><span class="n">fmt</span><span class="p">()</span>
</span></span></code></pre></div><p>to</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">B</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="nf">format</span><span class="p">(</span><span class="nb">object</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">values</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span> <span class="s2">&#34;[</span><span class="si">%s</span><span class="s2">]&#34;</span> <span class="o">%</span> <span class="n">value</span>
</span></span></code></pre></div><h4 id="75-hide-delegate-隐藏委托关系">7.5 Hide Delegate 隐藏&quot;委托关系&quot;</h4>
<blockquote>
<p>客户通过一个委托来调用另一个对象. 在服务类上建立客户所需要的所有函数, 用以隐藏委托关系</p>
</blockquote>
<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">Employee</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="nf">get_department</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">Department</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">john</span> <span class="o">=</span> <span class="n">Employee</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">manager</span> <span class="o">=</span> <span class="n">john</span><span class="o">.</span><span class="n">get_department</span><span class="p">()</span><span class="o">.</span><span class="n">get_manager</span><span class="p">()</span>
</span></span></code></pre></div><p>to</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">Employee</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="nf">get_department</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">Department</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">get_manager</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">Department</span><span class="p">()</span><span class="o">.</span><span class="n">get_manager</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">john</span> <span class="o">=</span> <span class="n">Employee</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">manager</span> <span class="o">=</span> <span class="n">john</span><span class="o">.</span><span class="n">get_manager</span><span class="p">()</span>
</span></span></code></pre></div><h4 id="76-remove-middle-man-移除中间人">7.6 Remove Middle Man 移除中间人</h4>
<blockquote>
<p>某个类做了过多的简单委托动作. 让客户直接调用受托类</p>
</blockquote>
<p>当上一步实例中<code>Employee</code>承载了大量的委托行为. 可以反向处理, 移除中间人. (当受托类功能越来越多, 完全变成了一个&quot;中间人&quot;)</p>
<h4 id="77-introduce-foreign-method-引入外加函数">7.7 Introduce Foreign Method 引入外加函数</h4>
<blockquote>
<p>你需要为提供服务的类增加一个函数, 但你无法修改这个类. 在客户类中建立一个函数, 并以第一参数形式传入一个服务类实例</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">new_start</span> <span class="o">=</span> <span class="n">Date</span><span class="p">(</span><span class="n">previous_end</span><span class="o">.</span><span class="n">get_year</span><span class="p">(),</span> <span class="n">previous_end</span><span class="o">.</span><span class="n">get_month</span><span class="p">(),</span> <span class="n">previous_end</span><span class="o">.</span><span class="n">get_date</span><span class="p">()</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
</span></span></code></pre></div><p>to</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">new_start</span> <span class="o">=</span> <span class="n">next_day</span><span class="p">(</span><span class="n">previous_end</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">next_day</span><span class="p">(</span><span class="n">previous_end</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">Date</span><span class="p">(</span><span class="n">previous_end</span><span class="o">.</span><span class="n">get_year</span><span class="p">(),</span> <span class="n">previous_end</span><span class="o">.</span><span class="n">get_month</span><span class="p">(),</span> <span class="n">previous_end</span><span class="o">.</span><span class="n">get_date</span><span class="p">()</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="78-introduce-local-extension-引入本地扩展">7.8 Introduce Local Extension 引入本地扩展</h4>
<blockquote>
<p>你需要为服务类提供一些额外函数, 但你无法修改这个类. 建立一个新类, 使它包含这些额外函数. 让这个扩展品成为原类的子类或者包装类</p>
</blockquote>
<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">A</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></code></pre></div><p>to</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">A</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 class="k">class</span> <span class="nc">B</span><span class="p">(</span><span class="n">A</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># add extra methods</span>
</span></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>读书笔记-重构: 章6 重新组织函数</title>
			<link>https://wklken.me/posts/2016/12/03/refactoring-01.html</link>
			<pubDate>Sat, 03 Dec 2016 11:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/12/03/refactoring-01.html</guid>
			<description>重构的读书笔记, 简单转成python版本的code, 供参考 章6: 重新组织函数 6.1 Extract Method 提炼函数 你有一段代码可以被组织在一起并独立出来, 将这段代码</description>
			<content type="html"><![CDATA[<p>重构的读书笔记, 简单转成python版本的code, 供参考</p>
<h2 id="章6-重新组织函数">章6: 重新组织函数</h2>
<h4 id="61-extract-method-提炼函数">6.1 Extract Method 提炼函数</h4>
<blockquote>
<p>你有一段代码可以被组织在一起并独立出来, 将这段代码放进一个独立函数中, 并让函数名称解释该函数的用途</p>
</blockquote>
<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">print_owing</span><span class="p">(</span><span class="n">double</span> <span class="n">amount</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">print_banner</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">//</span> <span class="nb">print</span> <span class="n">details</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="s2">&#34;this is the detail: &#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="s2">&#34;amnount: </span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="n">amount</span>
</span></span></code></pre></div><p>to</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">print_details</span><span class="p">(</span><span class="n">amount</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="s2">&#34;this is the detail: &#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="s2">&#34;amnount: </span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="n">amount</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">print_owing</span><span class="p">(</span><span class="n">double</span> <span class="n">amount</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">print_banner</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">print_details</span><span class="p">(</span><span class="n">amount</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="62-inline-method-内联函数">6.2 Inline Method 内联函数</h4>
<blockquote>
<p>一个函数的本体与名称同样清楚易懂. 在函数调用点插入函数本体, 然后移除该函数</p>
</blockquote>
<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">get_rating</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">2</span> <span class="k">if</span> <span class="n">more_than_five_late_deliveries</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">else</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">more_than_five_late_deliveries</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="mi">5</span>
</span></span></code></pre></div><p>to</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">get_rating</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">2</span> <span class="k">if</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="mi">5</span> <span class="k">else</span> <span class="mi">1</span>
</span></span></code></pre></div><h4 id="63-inline-temp-内联临时变量">6.3 Inline Temp 内联临时变量</h4>
<blockquote>
<p>你有一个临时变量, 只被一个简单表达式赋值一次, 而它妨碍了其他重构手法. 将所有对该变量的引用动作, 替换为对它复制的哪个表达式自身</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">base_price</span> <span class="o">=</span> <span class="n">order</span><span class="o">.</span><span class="n">get_base_price</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="n">base_price</span> <span class="o">&gt;</span> <span class="mi">1000</span>
</span></span></code></pre></div><p>to</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">return</span> <span class="n">order</span><span class="o">.</span><span class="n">get_base_price</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">1000</span>
</span></span></code></pre></div><h4 id="64-replace-temp-with-query-以查询取代临时变量">6.4 Replace Temp with Query 以查询取代临时变量</h4>
<blockquote>
<p>你的程序以一个临时变量保存着某一表达式的运算结果. 将表达式提炼到一个独立函数中. 将这个临时变量的所有引用点替换为对新函数的调用. 伺候新函数就可以被其他函数使用</p>
</blockquote>
<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="n">base_price</span> <span class="o">=</span> <span class="n">quantity</span> <span class="o">*</span> <span class="n">item_price</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">base_price</span> <span class="o">&gt;</span> <span class="mi">1000</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">base_price</span> <span class="o">*</span> <span class="mf">0.95</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="k">return</span> <span class="n">base_price</span> <span class="o">*</span> <span class="mf">0.98</span>
</span></span></code></pre></div><p>to</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">if</span> <span class="n">base_price</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">1000</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">base_price</span><span class="p">()</span> <span class="o">*</span> <span class="mf">0.95</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="k">return</span> <span class="n">base_price</span><span class="p">()</span> <span class="o">*</span> <span class="mf">0.98</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">base_price</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">quantity</span> <span class="o">*</span> <span class="n">itme_price</span>
</span></span></code></pre></div><h4 id="65--introduce-explaining-variable-引入解释性变量">6.5  Introduce Explaining Variable 引入解释性变量</h4>
<blockquote>
<p>你有一个复杂的表达式, 将该复杂表达式或其中一部分的结果放入一个临时变量, 以此变量名称来解释表达式的用途</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">if</span> <span class="s2">&#34;MAC&#34;</span> <span class="ow">in</span> <span class="n">platform</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span> <span class="ow">and</span> <span class="s2">&#34;IE&#34;</span> <span class="ow">in</span> <span class="n">browser</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span> <span class="ow">and</span> <span class="n">was_initialized</span><span class="p">()</span> <span class="ow">and</span> <span class="n">resize</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1">#do something</span>
</span></span></code></pre></div><p>to</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">is_macos</span> <span class="o">=</span> <span class="s2">&#34;MAC&#34;</span> <span class="ow">in</span> <span class="n">platform</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">is_ie_browser</span> <span class="o">=</span> <span class="s2">&#34;IE&#34;</span> <span class="ow">in</span> <span class="n">browser</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">was_resized</span> <span class="o">=</span> <span class="n">resize</span> <span class="o">&gt;</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">is_macos</span> <span class="ow">and</span> <span class="n">is_ie_browser</span> <span class="ow">and</span> <span class="n">was_initialized</span><span class="p">()</span> <span class="ow">and</span> <span class="n">was_resized</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># do something</span>
</span></span></code></pre></div><h4 id="66-split-temporary-variable-分解临时变量">6.6 Split Temporary Variable 分解临时变量</h4>
<blockquote>
<p>你的程序有某个临时变量被赋值过一次, 它既不是循环变量, 也不被利用与收集计算结果. 针对每次赋值, 创造一个独立, 对应的临时变量</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">tmp</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="p">(</span><span class="n">height</span> <span class="o">*</span> <span class="n">width</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="n">tmp</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">tmp</span> <span class="o">=</span> <span class="n">height</span> <span class="o">*</span> <span class="n">width</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="n">tmp</span>
</span></span></code></pre></div><p>to</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">perimeter</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="p">(</span><span class="n">height</span> <span class="o">*</span> <span class="n">width</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="n">perimeter</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">area</span> <span class="o">=</span> <span class="n">height</span> <span class="o">*</span> <span class="n">width</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="n">area</span>
</span></span></code></pre></div><h4 id="67--remove-assignments-to-parameters-移除对参数的赋值">6.7  Remove Assignments to Parameters 移除对参数的赋值</h4>
<blockquote>
<p>代码对一个参数进行赋值. 以一个临时变量取代该参数的位置</p>
</blockquote>
<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">discount</span><span class="p">(</span><span class="nb">int</span> <span class="n">input_value</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">input_value</span> <span class="o">&gt;</span> <span class="mi">50</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">input_value</span> <span class="o">-=</span> <span class="mi">2</span>
</span></span></code></pre></div><p>to</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">discount</span><span class="p">(</span><span class="nb">int</span> <span class="n">input_value</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">input_value</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">input_value</span> <span class="o">&gt;</span> <span class="mi">50</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">result</span> <span class="o">-=</span> <span class="mi">2</span>
</span></span></code></pre></div><h4 id="68-replace-method-with-method-object-以函数对象取代函数">6.8 Replace Method with Method Object 以函数对象取代函数</h4>
<blockquote>
<p>你有一个大型函数, 其中对局部变量的使用使你无法采用Extract method. 将这个函数放进一个单独对象中, 如此一来局部变量就成了对象内的字段, 然后你可以在同一个对象中将这个大型函数分解成多个小型函数</p>
</blockquote>
<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">Account</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="nf">delta</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># do something</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">x</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">gamma</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">input_value</span><span class="p">,</span> <span class="n">quantity</span><span class="p">,</span> <span class="n">year_to_date</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">i_value_1</span> <span class="o">=</span> <span class="p">(</span><span class="n">input_value</span> <span class="o">*</span> <span class="n">quantity</span><span class="p">)</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">delta</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">i_value_2</span> <span class="o">=</span> <span class="p">(</span><span class="n">input_value</span> <span class="o">*</span> <span class="n">year_to_date</span><span class="p">)</span> <span class="o">+</span> <span class="mi">100</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">year_to_date</span> <span class="o">-</span> <span class="n">i_value_1</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">i_value_2</span> <span class="o">-=</span> <span class="mi">20</span>
</span></span><span class="line"><span class="cl">        <span class="n">i_value_3</span> <span class="o">=</span> <span class="n">i_value_2</span> <span class="o">*</span> <span class="mi">7</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># and so on.</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">i_value_3</span> <span class="o">-</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">i_value_1</span>
</span></span></code></pre></div><p>to</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">Account</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="nf">delta</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># do something</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">x</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">gamma</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">input_value</span><span class="p">,</span> <span class="n">quantity</span><span class="p">,</span> <span class="n">year_to_date</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">Gamma</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">input_value</span><span class="p">,</span> <span class="n">quantity</span><span class="p">,</span> <span class="n">year_to_date</span><span class="p">)</span><span class="o">.</span><span class="n">compute</span><span class="p">()</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">Gamma</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">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">account</span><span class="p">,</span> <span class="n">input_value</span><span class="p">,</span> <span class="n">quantity</span><span class="p">,</span> <span class="n">year_to_date</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">account</span> <span class="o">=</span> <span class="n">account</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">input_value</span> <span class="o">=</span> <span class="n">input_value</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">quantity</span> <span class="o">=</span> <span class="n">quantity</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">year_to_date</span> <span class="o">=</span> <span class="n">year_to_date</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">i_value_1</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">i_value_2</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">i_value_3</span> <span class="o">=</span> <span class="mi">0</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">compute</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">i_value_1</span> <span class="o">=</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">input_value</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">quantity</span><span class="p">)</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">account</span><span class="o">.</span><span class="n">delta</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">i_value_2</span> <span class="o">=</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">input_value</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">year_to_date</span><span class="p">)</span> <span class="o">+</span> <span class="mi">100</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">important_thing</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">i_value_3</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">i_value_2</span> <span class="o">*</span> <span class="mi">7</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># and so on.</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">i_value_3</span> <span class="o">-</span> <span class="mi">2</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">i_value_1</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">important_thing</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">year_to_date</span> <span class="o">-</span> <span class="bp">self</span><span class="o">.</span><span class="n">i_value_1</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="bp">self</span><span class="o">.</span><span class="n">i_value_2</span> <span class="o">-=</span> <span class="mi">20</span>
</span></span></code></pre></div><h4 id="69-substitute-algorithm-替换算法">6.9 Substitute Algorithm 替换算法</h4>
<blockquote>
<p>你想要把某个算法那替换成另一个更清晰的算法. 将函数本体替换成另一个算法. 用更清晰的替换原先的</p>
</blockquote>
<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">find_person</span><span class="p">(</span><span class="n">i</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="s2">&#34;Don&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;D&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="s2">&#34;John&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;J&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="s2">&#34;Kent&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;K&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="s2">&#34;&#34;</span>
</span></span></code></pre></div><p>to</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">find_person</span><span class="p">(</span><span class="n">i</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{</span><span class="s2">&#34;Don&#34;</span><span class="p">:</span> <span class="s2">&#34;D&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;John&#34;</span><span class="p">:</span> <span class="s2">&#34;J&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;Kent&#34;</span><span class="p">:</span> <span class="s2">&#34;k&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">)</span>
</span></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>Python 代码规范小结</title>
			<link>https://wklken.me/posts/2016/11/03/python-code-style.html</link>
			<pubDate>Thu, 03 Nov 2016 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/11/03/python-code-style.html</guid>
			<description>code review中一些小结, 还没来得及加例子, 简要记录, 供参考 law 一: 一切都与复杂度有关 二: 代码应当易于理解 对人: &amp;ldquo;好程序员”应当竭</description>
			<content type="html"><![CDATA[<hr>
<p>code review中一些小结, 还没来得及加例子, 简要记录, 供参考</p>
<h2 id="law">law</h2>
<blockquote>
<p>一: 一切都与复杂度有关
二: 代码应当易于理解</p>
</blockquote>
<p>对人:</p>
<blockquote>
<p>&ldquo;好程序员”应当竭尽全力, 把程序写得让其他程序员(以及以后的自己)容易理解.</p>
</blockquote>
<p>对代码:</p>
<blockquote>
<ol>
<li>代码被阅读的次数远多于编写和修改的次数</li>
<li>E = mc2 (Error = more codes)</li>
</ol>
</blockquote>
<p>对项目:</p>
<blockquote>
<p>公式: 可行性=(当前价值+未来价值)/(实现成本+维护成本). 即相比降低实现成本, 降低维护成本更加重要</p>
</blockquote>
<h2 id="基础-风格">基础: 风格</h2>
<blockquote>
<p>团队成员遵守统一的风格, 保持风格的一致性, 减少理解难度</p>
</blockquote>
<p>遵循基础的编码风格:</p>
<p>请仔细阅读, 使用对应编辑器插件工具协助检查</p>
<ul>
<li>
<p>遵循  <a href="https://www.python.org/dev/peps/pep-0008/">pep8</a> 风格
利用pep8工具(编辑器相关插件)来解决这个问题, 在review之前处理. 以避免在review过程中出现此类问题.</p>
</li>
<li>
<p>遵循 <a href="https://google.github.io/styleguide/pyguide.html">Google Code Style</a> / <a href="http://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/">中文版</a></p>
</li>
<li>
<p>不要吝啬空行, 把相关的代码行分组, 形成代码块. 声明按块组织起来, 并且把代码分成”段落”(按步骤/顺序/逻辑结构分), 排版合理</p>
</li>
<li>
<p>每行只写一个语句, 每行只声明一个变量</p>
</li>
</ul>
<h2 id="注释">注释</h2>
<blockquote>
<p>注释应该有很高的<code>价值</code>(传递信息/空间占用)</p>
</blockquote>
<ul>
<li>
<p>代码本身应该尽力做到自说明</p>
</li>
<li>
<p>注释, 记录了在写代码过程中的思考, 保持紧凑, 简单准确的描述</p>
</li>
<li>
<p>不要使用尾注释. 容易被整行拷贝/不容易被编辑修改/逐渐腐烂</p>
<pre><code>  x = 1  # bad comment

  # good comment
  x = 1
</code></pre>
</li>
<li>
<p>不需要的代码, 维护到版本库后(写明<code>commit info</code>), 然后删除. 不要注释起来</p>
</li>
<li>
<p>不要给不好的命名加注释, 应该去修改命名</p>
</li>
<li>
<p>不要给那些从代码本身就能<code>快速</code>推断出来的事实写注释.(不要为了注释而注释)</p>
</li>
<li>
<p>对于复杂的计算逻辑, 要给出注释, 可以通过列举例子, 简单的输入输出来描述</p>
</li>
<li>
<p>对于大段的逻辑或模块, 需要给总结性注释</p>
</li>
<li>
<p>注释代码时, 应注重-为何做, 而不是-怎么做</p>
</li>
<li>
<p>每行注释前用一个空行分开. 注释缩进要和相应代码一致</p>
</li>
</ul>
<h2 id="命名">命名</h2>
<blockquote>
<p>把信息装入名字中.(自说明)</p>
</blockquote>
<ul>
<li>尽量短, 但是要包含足够的信息(刨掉其中毫无意义的词)</li>
<li>命名一定要有意义, 尽量少使用单个字符作为命名, 除非短表达式(列表解析/lambda等)以及小的作用域范围</li>
<li>常量大写, 变量小写, 类名驼峰, 函数名小写加下划线, 不要混用下划线和驼峰.</li>
<li>不要使用关键字命名, 例如<code>type</code> 和 <code>dir</code></li>
<li>避免使用容易混淆的命名, 防歧义</li>
<li>慎用首字母缩略词和缩写, 除非团队成员都理解(不要妄图用注释来解决这个问题, 即, 不要注释不好的命名)</li>
<li>不要使用大小写来区分不同对象</li>
<li>同一个变量, 在多个地方, 前后端/数据库/不同函数/请求等, 尽量保持命名一致性</li>
<li>不要害怕过长的命名, 保证易于理解和阅读(现代编辑器可以搞定自动补全和批量变更的问题)</li>
<li>使用具体的名字, 而不是泛化的名字, 例如<code>params/args</code>等, 没有隐含任何信息</li>
<li><code>bool</code>类型, 除非名字本身有<code>True/False</code>的含义, 否则建议统一使用<code>is_</code>前缀</li>
<li>不要使用双重否定的命名: <code>is_not_pass</code></li>
<li><code>for a in b</code>, 注意 <code>a</code> 和 <code>b</code> 的单复数区分</li>
</ul>
<h2 id="常量">常量</h2>
<ul>
<li>常量大写</li>
<li>作用于同一个模块/逻辑的多个常量, 建议使用统一的前缀</li>
<li>将常量统一组织到某个文件/某几个文件, 并写明注释.</li>
<li>函数/循环中的正则, 请预先<code>compile</code>, 放入变量中.</li>
<li>善用<code>Enum</code>, 对可读性提升很大</li>
<li>同一个枚举变量中, 其包含类型应当一致</li>
</ul>
<h2 id="变量">变量</h2>
<ul>
<li>减少变量: 变量越多, 越难全部追踪其动向. a.减少没有价值的中间变量 b.减少中间结果(可以通过<code>提前返回</code>来消除) c.减少控制流变量</li>
<li>缩小变量作用域: 避免全局变量(命名空间污染). 需要做到让你的变量对尽量少的代码行可见.</li>
<li>变量定义尽量靠近其使用的地方, 或者, 在使用时定义.</li>
<li>不要使用<code>import *</code>, 会出现各种<code>突如其来</code>的变量名, 可能导致名字空间污染, 造成诡异问题</li>
</ul>
<h2 id="数据结构">数据结构</h2>
<ul>
<li><code>dict</code>, 不要使用<code>for key in d.keys()</code>, 直接使用<code>for key in d</code></li>
</ul>
<h2 id="表达式">表达式</h2>
<blockquote>
<p>原则: 保持简短, 易懂.(拆分超长表达式)</p>
</blockquote>
<ul>
<li>抽取反复出现的长表达式到变量或者函数调用</li>
<li>使用解释变量, 将超长表达式中的自表达式抽取城一个解释变量.(抽取, 然后使用变量, 而不是每次都重复表达式)</li>
<li>总结变量: 一个表达式不需要解释, 但是装入一个新的变量中仍然有用. 短名字替代一大块代码. 例如: <code>numbers[0]['obj'].name</code></li>
<li>使用摩根定理: <code>not a and not b</code> to <code>not (a or b)</code></li>
<li>删除公共子表达式：如果发现某个表达式老是在你面前出现，就把它赋值给一个变量</li>
<li>中文, 请统一使用<code>u&quot;中文&quot;</code></li>
<li>表达式中避免使用<code>魔数</code>, 使用常量/枚举替代之</li>
</ul>
<h2 id="控制流-分支">控制流: 分支</h2>
<ul>
<li><code>if/else</code>顺序: a. 先处理正逻辑而不是负逻辑. b. 先处理掉简单的情况, 还能保证if/else在同一个屏幕内都可见(否则到了<code>else</code>需要回头查) c.先处理有趣或可疑的逻辑</li>
<li><code>return early</code>, 从函数中提前返回. 使用<code>guard clause</code>来实现. 某些情况返回后, 将不必要思考某个分支出口, 剩余注意力集中在为数不多的情况. 另一个好处是, 能有效减少代码缩进.</li>
<li>减少嵌套: 嵌套很深的代码很难理解, 每个嵌套层次会在读者’思维栈’上又增加了一个条件. 使用<code>return early</code>来减少嵌套. 而循环中的减少嵌套方式, 可以使用<code>if condition: continue/break</code>来进行<code>提早返回</code>.</li>
<li>减少嵌套: 当你对代码进行改动的时候, 从全新的角度审视它, 把它作为一个整体来看待.只关心局部, 不敢动旧有代码, 很容易一层层逻辑嵌套往里加导致深层嵌套</li>
<li>使用<code>is</code>来判定是否是<code>None</code>, 而不是<code>==</code></li>
<li>条件语句中参数顺序: 左侧变量, 右侧字面值/常量</li>
<li>默认情况都使用<code>if...else</code>, 三目运算只有在最简单的情况下才使用</li>
<li><code>if condition: return</code> 则不需要<code>else</code></li>
<li>注意<code>if/else</code>的多层嵌套, 在某些情况下, 判断条件中恒真/恒假的情况</li>
</ul>
<h2 id="控制流-循环">控制流: 循环</h2>
<ul>
<li>善用<code>enumerate</code>而不是维护<code>index</code>变量( <code>enumerate</code> 还可以从1开始计数)</li>
<li>除非必要(逻辑确实如此且带<code>break</code>), 否则不要使用<code>for...else</code>.(增加理解成本)</li>
<li>不要使用<code>for _ in l: _.x</code>, 可读性太差</li>
<li>减少循环内的<code>if...else...</code>嵌套层次, 可以使用<code>if condition: continue</code></li>
</ul>
<h2 id="控制流-异常处理">控制流: 异常处理</h2>
<blockquote>
<p>异常日志同注释, 应该有很高的<code>价值</code>(传递信息/空间占用)</p>
</blockquote>
<ul>
<li>不要把所有代码放到<code>try except</code>中, 只捕获会出异常的代码片段. 注意粒度, 不要放入不必要的代码</li>
<li>不要吞掉异常, 处理或抛出, 同时要打日志(使用<code>logging</code>而不是<code>print</code>打日志)</li>
<li>谨慎使用<code>except Exception</code>捕获所有异常.</li>
<li>不要在<code>finally</code>语句中使用return进行返回, 有坑.</li>
<li>异常的错误信息要<code>有用</code>, 即足够明确, 对问题排查有帮助.</li>
<li>不要使用异常控制程序的流程. 滥用异常, 异常不应该处理正常逻辑</li>
<li>不要滥用异常, 底层被调用函数早已<code>try...except</code>处理了, 调用方不需要再次处理</li>
</ul>
<h2 id="函数">函数</h2>
<blockquote>
<p>函数不要太大, 嵌套不要太深</p>
</blockquote>
<ul>
<li>参数命名的一致性: 多个参数, 选择一个有意义的顺序, 并始终一致地使用它(可读性更好, 更容易发现问题)</li>
<li>不要使用可变对象作为函数默认参数的值</li>
<li><strong>一次只做一件事</strong>, 注意函数大小, 注意抽象/拆分</li>
<li>抽取不相关的子问题到独立的函数中, 例如纯工具代码, 通用代码, 项目专属代码</li>
<li>抽取反复出现重复的代码到独立函数中</li>
<li><code>return</code> 值不要使用<code>0/1</code>来代表<code>True/False</code></li>
<li>同一个函数可能存在多个<code>return</code>, 返回值要保持一致(个数/类型)</li>
<li><code>return early</code>, 减少阅读代码时的逻辑堆积, 减少贯穿函数始终用于最终判断return的变量数量. 超过3个就变得有些难以维护了, 阅读过程中确定其值有困难</li>
<li>如果函数调用链中, 参数或者return的值反复出现pack/unpack, 可以考虑用<code>dict</code>封装来进行传递.</li>
<li>如果发现每次调用一个函数后, 还需要对返回值进行二次处理, 则是函数封装得不够导致的. 需重构函数, 将处理加进去. (防止某次调用忘了二次处理导致的bug)</li>
</ul>
<h2 id="类">类</h2>
<ul>
<li>不要使用type进行类型检查, 用<code>isinstance</code></li>
<li>使用新式类, 驼峰命名</li>
<li>假设类的某个属性, 每次取出来都需要进行处理(格式化, 转换等, 例如日期格式), 使用<code>property</code>封装这层处理, 同时处理异常情况.</li>
</ul>
<h2 id="模块">模块</h2>
<ul>
<li><code>import</code>顺序: 标准库/第三方库/本项目, 之间使用空行隔开</li>
<li>多行<code>import</code>, 请使用<code>from a import (b, c, d)</code>而不是<code>\</code>来进行换行</li>
<li>不要使用<code>from A import *</code></li>
<li>当相对独立的多个逻辑代码混杂放在一起, 或者发现constant文件超大包含了大量不同逻辑的产量, 可以考虑模块切分</li>
</ul>
<h2 id="抽象">抽象</h2>
<ul>
<li>一定不要机械地复制粘贴代码(会出现大量的重复代码), 应该从全局考虑是否可以抽象</li>
<li>多个函数之间, 如果仅有一两行代码不同, 则可以进行抽象提取</li>
<li>当一段相似的代码出现两次以上, 需要考虑封装(注意粒度)</li>
</ul>
<h2 id="设计">设计</h2>
<blockquote>
<p>软件设计三大误区: 1.编写不必要的代码 2.代码难以修改 3.过分追求通用</p>
</blockquote>
<ul>
<li>思考足够充分, 减少过度设计</li>
</ul>
<h2 id="其他">其他</h2>
<ul>
<li>熟悉标准库, 减少土制轮子的概率, 可以少写代码</li>
<li>熟悉框架/优秀第三方库提供的接口及特性, 原因同上</li>
<li>项目发布前, 移除所有<code>print</code>语句</li>
<li>文件/函数是否写明作者信息? 不, 版本记录中有作者信息. 容易形成<code>领地</code>, 他人不敢修改/不敢大改, 容易造成代码腐烂. 占用空间且没啥用.</li>
</ul>
<h2 id="参考书目">参考书目</h2>
<ul>
<li>编写可读代码的艺术</li>
<li>简约之美—软件设计之道</li>
<li>编写高质量代码—改善Python程序的91个建议</li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>[分享]关于vim</title>
			<link>https://wklken.me/posts/2016/07/24/about-vim.html</link>
			<pubDate>Sun, 24 Jul 2016 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/07/24/about-vim.html</guid>
			<description>很早之前的小组分享, 整理成pdf</description>
			<content type="html"><![CDATA[<hr>
<p>很早之前的小组分享, 整理成pdf</p>
<hr>
<object data="/extra/share/about-vim.pdf" type="application/pdf" width="729" height="525">
    <embed src="/extra/share/about-vim.pdf">
    </embed>
</object>
]]></content>
		</item>
		
		<item>
			<title>ElasticSearch集群部署文档</title>
			<link>https://wklken.me/posts/2016/06/29/deploy-es.html</link>
			<pubDate>Wed, 29 Jun 2016 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/06/29/deploy-es.html</guid>
			<description>官方es搭建步骤写的很简略, 但是实际搭建过程中, 会涉及一系列环境配置. 以下的流程, 是在搭建过程中梳理出来的详细步骤(实践过3遍以上) 其实, 这</description>
			<content type="html"><![CDATA[<p>官方es搭建步骤写的很简略, 但是实际搭建过程中, 会涉及一系列环境配置. 以下的流程, 是在搭建过程中梳理出来的详细步骤(实践过3遍以上)</p>
<p>其实, 这些流程在具体应用的时候, 都可以变成自动化脚本, 或者直接用docker好了, 以便扩容足够快(目前我们用的打包成集成安装包, 实现脚本自动部署)</p>
<p>只是简单集群的基本设置, 不涉及调优的参数配置, 不涉及<code>client/master/data</code>节点区分等等. 可以参照搭建的主体流程.</p>
<hr>
<h2 id="版本及连接">版本及连接</h2>
<p>elasticseearch版本: 2.3.3</p>
<p>相关链接:</p>
<ul>
<li><a href="https://www.elastic.co/products/elasticsearch">官网</a></li>
<li><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html">文档</a></li>
</ul>
<h2 id="系统要求">系统要求</h2>
<p>如果仅作测试用, 不需要两天机器, 可以将两个节点部署在同一台机器上, 对磁盘/cpu要求不高, 内存大于2g基本足够了</p>
<p>如果是正式环境, 需要根据日志量进行评估, 例如, 每天日志量占硬盘约约10G, 且保留30天日志, 则磁盘会占用约300g, es设定的阈值是磁盘空间占满85%则日志开始告警. 所以, 需要至少 <code>300/0.85=354g</code>.</p>
<p>准备两台机器, 在同一个局域网内(可ping通), 分别在每台机器上部署相应es节点, 搭建一套日志集群.</p>
<p>两台机器, 最少的资源了, 但是没法做到高可用, 所以, 还需要再加一台机器, 防止脑裂, 具体见最后(两台主力机器+一台稳定的机器就行)</p>
<ul>
<li>集群节点: 最少两台机器</li>
<li>内存: 16G及以上</li>
<li>cpu: 4核及以上</li>
<li>硬盘: 800G及以上, 建议1T, 集群容量约10亿级(取决于对应日志大小)</li>
<li>操作系统: centos</li>
</ul>
<p>这里假设, 两台机器ip分别为</p>
<pre tabindex="0"><code>第一台机器: 10.0.0.1
第二台机器: 10.0.0.2
</code></pre><p>机器系统为<code>centos6.5</code></p>
<h2 id="部署">部署</h2>
<h4 id="1-确认jdk版本及安装">1. 确认JDK版本及安装</h4>
<p>es依赖java的版本最小为1.7</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">java -version
</span></span></code></pre></div><ul>
<li>
<p>如果系统中未安装<code>JDK</code></p>
<p>则命令返回<code>bash: java: command not found</code>, 需要安装<code>JDK</code></p>
</li>
<li>
<p>如果系统中安装了JDK, 需确认版本是否大于<code>java 1.7</code>, 否则需要升级</p>
<pre tabindex="0"><code>java version &#34;1.7.0_51&#34;
Java(TM) SE Runtime Environment (build 1.7.0_51-b13)
Java HotSpot(TM) Server VM (build 24.51-b03, mixed mode)
</code></pre></li>
</ul>
<p>安装及升级<code>java</code>(注意根据系统不同运行对应安装命令)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Redhat/Centos/Fedora</span>
</span></span><span class="line"><span class="cl">sudo yum install java-1.7.0-openjdk
</span></span></code></pre></div><p>或者到官网, 下载最新的jdk的rpm包, 然后安装</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wget http://download.oracle.com/otn-pub/java/jdk/8u91-b14/jdk-8u91-linux-x64.rpm
</span></span><span class="line"><span class="cl">rpm -Uvh jdk-8u91-linux-x64.rpm
</span></span></code></pre></div><p>再次确认安装成功</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">java -version
</span></span></code></pre></div><h4 id="2-下载es">2. 下载es</h4>
<p>版本: 2.3.3</p>
<p>下载地址:</p>
<ul>
<li><a href="https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/tar/elasticsearch/2.3.3/elasticsearch-2.3.3.tar.gz">elasticsearch-2.3.3.tar.gz (tar.gz格式)</a></li>
</ul>
<p>命令行中的下载命令:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -L -O https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/tar/elasticsearch/2.3.3/elasticsearch-2.3.3.tar.gz
</span></span></code></pre></div><p>解压:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">tar -xzf elasticsearch-2.3.3.tar.gz
</span></span></code></pre></div><h4 id="3-用户目录权限设置">3. 用户/目录/权限设置</h4>
<p>新建用户, 假设为<code>es</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo useradd es
</span></span></code></pre></div><p>新建目录, 假设<code>/data/</code>目录挂载的硬盘最大(<code>500G</code>以上)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p /data/LogTool
</span></span><span class="line"><span class="cl">mkdir -p /data/LogData
</span></span></code></pre></div><p>将解压后的目录移动至新建的目录<code>/data/LogTool</code>下, 并改名为<code>elasticsearch</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mv elasticsearch-2.3.3 /data/LogTool/elasticsearch
</span></span></code></pre></div><p>将目录所有者修改为<code>test</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">chown -R es:es /data/LogTool
</span></span><span class="line"><span class="cl">chown -R es:es /data/LogData
</span></span></code></pre></div><h4 id="5-切换用户">5. 切换用户</h4>
<p>切换到<code>es</code>用户, 并进入<code>elasticsearch</code>目录</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">su es
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> /data/LogTool/elasticsearch/
</span></span></code></pre></div><p>以用户<code>es</code>的身份进行后续操作</p>
<h4 id="6-修改配置文件">6. 修改配置文件</h4>
<p>以用户<code>es</code>的身份进行操作</p>
<p>文件路径: <code>config/elasticsearch.yml</code></p>
<p>修改该文件中配置项: (注意, 原始文件中都是被<code>#</code>号注释掉了, 需要去掉对应注释并修改配置值)</p>
<ul>
<li>集群名: <code>cluster.name</code>, 注意: 两台机器配置一致</li>
</ul>
<pre tabindex="0"><code>cluster.name: inner_es_cluster
</code></pre><ul>
<li>节点名: <code>node.name</code>, 注意: 两台机器配置不同, 一台为01, 另一台为02</li>
</ul>
<pre tabindex="0"><code># 第一台机器
node.name: inner_es_node_01

# 第二台机器
node.name: inner_es_node_02
</code></pre><ul>
<li>数据路径: <code>path.data</code>, 为新建立的目录</li>
</ul>
<pre tabindex="0"><code>path.data: /data/LogData/
</code></pre><ul>
<li>日志路径: <code>path.logs</code></li>
</ul>
<pre tabindex="0"><code>path.logs: /data/LogData/logs
</code></pre><ul>
<li>LockMemory:</li>
</ul>
<pre tabindex="0"><code>bootstrap.mlockall: true
</code></pre><ul>
<li>本机ip: <code>network.host</code>, 注意两台机器配置不同, 分贝配置为对应机器的内网ip</li>
</ul>
<pre tabindex="0"><code># 第一台机器
network.host: 10.0.0.1

# 第二台机器
network.host: 10.0.0.2
</code></pre><ul>
<li>Discovery配置: 注意这里是两台机器内网ip+9300端口, 注意这里<code>minimum_master_nodes=2</code>, 见最后一点防脑裂说明</li>
</ul>
<pre tabindex="0"><code>discovery.zen.ping.unicast.hosts: [&#34;10.0.0.1:9300&#34;, &#34;10.0.0.2:9300&#34;]
discovery.zen.minimum_master_nodes: 2
</code></pre><ul>
<li>gatewary配置:</li>
</ul>
<pre tabindex="0"><code>gateway.recover_after_nodes: 2
gateway.recover_after_time: 5m
gateway.expected_nodes: 1
</code></pre><ul>
<li>新增其他配置到文件末尾, 根据需求加, 这里用到了<code>script</code>, 同时增大了<code>recovery</code>的配置(要大些保证recovery速度, 但是又不能太大, 会将带宽占满)</li>
</ul>
<pre tabindex="0"><code>script.engine.groovy.inline.search: on
script.engine.groovy.inline.aggs: on
indices.recovery.max_bytes_per_sec: 100mb
indices.recovery.concurrent_streams: 10
</code></pre><h4 id="7-设置es占用内存">7. 设置es占用内存</h4>
<p>修改文件<code>bin/elasticsearch.in.sh</code>, 将文件如下变量变更为<code>4g</code>(根据自身机器配置, 配置的内存最大不超过机器物理内存的75%. 两个变量值相等, 以获取最大的性能). 当然, 实际使用中<code>4g</code>可能远远不够, 这个值仅是个示例</p>
<pre tabindex="0"><code>ES_MIN_MEM=4g
ES_MAX_MEM=4g
</code></pre><p>修改centos配置: <code>/etc/security/limits.conf</code>, 以便启用memlock, 提升性能</p>
<p>加入, 注意, 示例中用户为<code>es</code></p>
<pre tabindex="0"><code>es soft memlock unlimited
es hard memlock unlimited
</code></pre><p>确认<code>max descriptiors</code></p>
<p>查看系统数量</p>
<ul>
<li>如果结果是<code>unlimited</code>, 则无需任何处理, 直接进入下一步</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">ulimit</span> -n
</span></span><span class="line"><span class="cl">unlimited
</span></span></code></pre></div><ul>
<li>如果结果是一个整数, 且小于<code>204800</code></li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">ulimit</span> -n
</span></span><span class="line"><span class="cl"><span class="m">4096</span>
</span></span></code></pre></div><p>此时, 需要编辑<code>/etc/security/limits.conf</code>, 加入</p>
<pre tabindex="0"><code>es soft nofile 204800
es hard nofile 204800
</code></pre><p>另一种方法, 修改<code>bin/elasticsearch</code>, 在文件的前半部分加入下面这行代码, 保证在启动前执行即可.</p>
<pre tabindex="0"><code>ulimit -n 204800
</code></pre><h4 id="8-启动测试">8. 启动测试</h4>
<p>以用户<code>es</code>的身份进行操作</p>
<p>在命令行中执行启动命令</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> /data/elasticsearch/
</span></span><span class="line"><span class="cl">./bin/elasticsearch
</span></span></code></pre></div><p>可以看到程序启动日志</p>
<pre tabindex="0"><code>[2016-06-30 17:20:26,677][WARN ][bootstrap                ] unable to install syscall filter: seccomp unavailable: requires kernel 3.5+ with CONFIG_SECCOMP and CONFIG_SECCOMP_FILTER compiled in
[2016-06-30 17:20:27,390][INFO ][node                     ] [inner_es_node_01] version[2.3.3], pid[6415], build[218bdf1/2016-05-17T15:40:04Z]
[2016-06-30 17:20:27,390][INFO ][node                     ] [inner_es_node_01] initializing ...
[2016-06-30 17:20:27,948][INFO ][plugins                  ] [inner_es_node_01] modules [lang-groovy, reindex, lang-expression], plugins [], sites []
[2016-06-30 17:20:27,974][INFO ][env                      ] [inner_es_node_01] using [1] data paths, mounts [[/data (/dev/xvdb1)]], net usable_space [67.4gb], net total_space [98.4gb], spins? [no], types [ext3]
[2016-06-30 17:20:27,974][INFO ][env                      ] [inner_es_node_01] heap size [990.7mb], compressed ordinary object pointers [true]
[2016-06-30 17:20:29,926][INFO ][node                     ] [inner_es_node_01] initialized
[2016-06-30 17:20:29,926][INFO ][node                     ] [inner_es_node_01] starting ...
[2016-06-30 17:20:30,083][INFO ][transport                ] [inner_es_node_01] publish_address {10.0.0.1:9300}, bound_addresses {10.0.0.1:9300}
[2016-06-30 17:20:30,088][INFO ][discovery                ] [inner_es_node_01] inner_es_cluster/odmTjZRHRVaa8Zn4vTPcxA
[2016-06-30 17:21:00,091][WARN ][discovery                ] [inner_es_node_01] waited for 30s and no initial state was set by the discovery
[2016-06-30 17:21:00,099][INFO ][http                     ] [inner_es_node_01] publish_address {10.0.0.1:9200}, bound_addresses {10.0.0.1:9200}
[2016-06-30 17:21:00,099][INFO ][node                     ] [inner_es_node_01] started
</code></pre><p>等待约一分钟后, 看到如下日志代表启动成功</p>
<pre tabindex="0"><code>[2016-06-30 17:21:00,099][INFO ][node                     ] [inner_es_node_01] started
</code></pre><p>确认集群是否启动成功</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl http://10.0.0.1:9200/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;name&#34;</span> : <span class="s2">&#34;inner_es_node_01&#34;</span>,
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;cluster_name&#34;</span> : <span class="s2">&#34;inner_es_cluster&#34;</span>,
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;version&#34;</span> : <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;number&#34;</span> : <span class="s2">&#34;2.3.3&#34;</span>,
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;build_hash&#34;</span> : <span class="s2">&#34;218bdf10790eef486ff2c41a3df5cfa32dadcfde&#34;</span>,
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;build_timestamp&#34;</span> : <span class="s2">&#34;2016-05-17T15:40:04Z&#34;</span>,
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;build_snapshot&#34;</span> : false,
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;lucene_version&#34;</span> : <span class="s2">&#34;5.5.0&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>,
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;tagline&#34;</span> : <span class="s2">&#34;You Know, for Search&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><p>启动第二个节点时日志</p>
<pre tabindex="0"><code>[2016-06-30 17:32:42,494][WARN ][bootstrap                ] unable to install syscall filter: seccomp unavailable: requires kernel 3.5+ with CONFIG_SECCOMP and CONFIG_SECCOMP_FILTER compiled in
[2016-06-30 17:32:43,295][INFO ][node                     ] [inner_es_node_02] version[2.3.3], pid[10240], build[218bdf1/2016-05-17T15:40:04Z]
[2016-06-30 17:32:43,295][INFO ][node                     ] [inner_es_node_02] initializing ...
[2016-06-30 17:32:43,879][INFO ][plugins                  ] [inner_es_node_02] modules [lang-groovy, reindex, lang-expression], plugins [], sites []
[2016-06-30 17:32:43,905][INFO ][env                      ] [inner_es_node_02] using [1] data paths, mounts [[/data (/dev/xvdb1)]], net usable_space [67.4gb], net total_space [98.4gb], spins? [no], types [ext3]
[2016-06-30 17:32:43,905][INFO ][env                      ] [inner_es_node_02] heap size [990.7mb], compressed ordinary object pointers [true]
[2016-06-30 17:32:45,876][INFO ][node                     ] [inner_es_node_02] initialized
[2016-06-30 17:32:45,876][INFO ][node                     ] [inner_es_node_02] starting ...
[2016-06-30 17:32:45,978][INFO ][transport                ] [inner_es_node_02] publish_address {10.0.0.2:9300}, bound_addresses {10.0.0.2:9300}
[2016-06-30 17:32:45,983][INFO ][discovery                ] [inner_es_node_02] inner_es_cluster/VBsHeFjXQXau59hkjTuhTA
[2016-06-30 17:32:49,067][INFO ][cluster.service          ] [inner_es_node_02] detected_master {inner_es_node_01}{1BktktzhQ_y6BN-lNIKhHg}{10.0.0.1}{10.0.0.1:9300}, added {{inner_es_node_01}{1BktktzhQ_y6BN-lNIKhHg}{10.0.0.1}{10.0.0.1:9300},}, reason: zen-disco-receive(from master [{inner_es_node_01}{1BktktzhQ_y6BN-lNIKhHg}{10.0.0.1}{10.0.0.1:9300}])
[2016-06-30 17:32:49,077][INFO ][http                     ] [inner_es_node_02] publish_address {10.0.0.2:9200}, bound_addresses {10.213.136.23:9201}
[2016-06-30 17:32:49,077][INFO ][node                     ] [inner_es_node_02] started
</code></pre><p>注意, 日志中<code>cluster.service</code>部分, 表示发现了第一台机器的节点</p>
<pre tabindex="0"><code>[2016-06-30 17:32:49,067][INFO ][cluster.service          ] [inner_es_node_02] detected_master {inner_es_node_01}{1BktktzhQ_y6BN-lNIKhHg}{10.0.0.1}{10.0.0.1:9300}, added {{inner_es_node_01}{1BktktzhQ_y6BN-lNIKhHg}{10.0.0.1}{10.0.0.1:9300},}, reason: zen-disco-receive(from master [{inner_es_node_01}{1BktktzhQ_y6BN-lNIKhHg}{10.0.0.1}{10.0.0.1:9300}])
</code></pre><p>启动第二个节点后, 同样确认是否启动成功</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl http://10.0.0.1:9200/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">{</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;name&#34;</span> : <span class="s2">&#34;inner_es_node_02&#34;</span>,
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;cluster_name&#34;</span> : <span class="s2">&#34;inner_es_cluster&#34;</span>,
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;version&#34;</span> : <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;number&#34;</span> : <span class="s2">&#34;2.3.3&#34;</span>,
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;build_hash&#34;</span> : <span class="s2">&#34;218bdf10790eef486ff2c41a3df5cfa32dadcfde&#34;</span>,
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;build_timestamp&#34;</span> : <span class="s2">&#34;2016-05-17T15:40:04Z&#34;</span>,
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;build_snapshot&#34;</span> : false,
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;lucene_version&#34;</span> : <span class="s2">&#34;5.5.0&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="o">}</span>,
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;tagline&#34;</span> : <span class="s2">&#34;You Know, for Search&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span></code></pre></div><h4 id="9-正式启动">9. 正式启动</h4>
<p><code>ctrl+c</code> 关掉原先的进程</p>
<p>使用命令, 以daemon形式启动, 进程pid写入<code>es.pid</code>, 可以用于重启等</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">bin/elasticsearch -d -p es.pid
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="nv">$?</span>
</span></span><span class="line"><span class="cl"><span class="m">0</span>
</span></span></code></pre></div><p>查看对应进程是否启动</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ps aux <span class="p">|</span> grep elasticsearch
</span></span></code></pre></div><p>使用<code>curl</code>请求服务确定是否正常</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl http://10.0.0.1:9200/
</span></span></code></pre></div><p>或者, 更好的方式, 使用<code>supervisord</code>管理进程, 以下为<code>supervisord.conf</code>示例</p>
<pre tabindex="0"><code>[program:es]
directory=/data/LogTool/elasticsearch
command=/data/LogTool/elasticsearch/bin/elasticsearch
autostart=true
autorestart=true
stdout_logfile=/data/LogTool/elasticsearch/log/supervisord_es_out.log
stderr_logfile=/data/LogTool/elasticsearch/log/supervisord_es_err.log
</code></pre><h4 id="10-脑裂">10. 脑裂</h4>
<p>单机测试开发的时候, 其实一个节点就够了. 上线, 使用两个节点, 目的是利用es本身的特性做到高可用.</p>
<p>但是两个节点是远远不够的. 启动后, 集群会选举一个<code>master</code>, 一切ok. 但是如果存在网络问题或者某个节点无响应(负载过高), 就会认为对方dead了, 然后两个节点自动选举为<code>master</code>, 在后续建索引的时候造成数据不一致.</p>
<p>两个节点防脑裂的配置, <code>minimum_master_nodes</code>决定了选主需要的最少节点数, <code>N/2+1</code>, 两个节点即<code>2</code></p>
<pre tabindex="0"><code>discovery.zen.minimum_master_nodes: 2 
</code></pre><p>但是, 此时一个节点挂了, 则整个集群挂了(无法选举主节点了)</p>
<p>所以, 要再加一个节点, 这个节点只要保证稳定即可, 对cpu和磁盘要求不高. 这个<code>es</code>节点的配置同其他节点的区别<code>node.data: false</code>, 不存储索引数据.</p>
<pre tabindex="0"><code># split brain prevent
node.data: false
</code></pre>]]></content>
		</item>
		
		<item>
			<title>Logstash&#43;ElasticSearch处理mysql慢查询日志</title>
			<link>https://wklken.me/posts/2016/05/24/elk-mysql-slolog.html</link>
			<pubDate>Tue, 24 May 2016 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/05/24/elk-mysql-slolog.html</guid>
			<description>遇到一个需求, 需要查询某些业务的慢查询日志. 结果DBA平台那边提供的慢查询日志不能解决实际的业务场景(上报的字段补全), 无奈, 自己挽起袖子上</description>
			<content type="html"><![CDATA[<p>遇到一个需求, 需要查询某些业务的慢查询日志. 结果DBA平台那边提供的慢查询日志不能解决实际的业务场景(上报的字段补全), 无奈, 自己挽起袖子上</p>
<p>参考了 <a href="https://www.phase2technology.com/blog/adding-mysql-slow-query-logs-to-logstash/">这篇文章</a>, 不过自己根据需求做了较多的变更</p>
<p>开始吧</p>
<h2 id="1-找到日志的位置">1. 找到日志的位置</h2>
<p>先确认是否开启了, 然后找到日志文件的位置</p>
<pre tabindex="0"><code>&gt; show variables like &#39;%slow%&#39;;
+---------------------+-------------------------------------+
| Variable_name       | Value                               |
+---------------------+-------------------------------------+
| log_slow_queries    | ON                                  |
| slow_launch_time    | 2                                   |
| slow_query_log      | ON                                  |
| slow_query_log_file | /data/mysqllog/20000/slow-query.log |
+---------------------+-------------------------------------+
</code></pre><h2 id="2-慢查询日志">2. 慢查询日志</h2>
<p>格式基本是如下, 当然, 格式如果有差异, 需要根据具体格式进行小的修改</p>
<pre tabindex="0"><code># Time: 160524  5:12:29
# User@Host: user_a[xxxx] @  [10.166.140.109]
# Query_time: 1.711086  Lock_time: 0.000040 Rows_sent: 385489  Rows_examined: 385489
use dbname;
SET timestamp=1464037949;
SELECT 1 from dbname;
</code></pre><h2 id="3-使用-logstash-采集">3. 使用 logstash 采集</h2>
<p>采集, 无非是用<code>multiline</code>进行多行解析</p>
<p>但是, 需要处理的几个问题</p>
<p>第一个是, 去除掉没用的信息</p>
<p>第二个, 慢查询sql, 是会反复出现的, 所以, 执行次数成了一个很重要的指标. 我们要做的, 就是<code>降噪</code>(将参数去掉, 涉及带引号的内容+数字), 将参数类信息过滤掉, 留下核心的sql, 然后计算出一个hash, 这样就可以在查询, 根据这个字段进行聚合. 这里用到了 <a href="https://www.elastic.co/guide/en/logstash/current/plugins-filters-mutate.html#plugins-filters-mutate-add_field">mutate</a> 以及 <a href="https://www.elastic.co/guide/en/logstash/current/plugins-filters-checksum.html">checksum</a></p>
<pre tabindex="0"><code>  # calculate unique hash
  mutate {
    add_field =&gt; {&#34;sql_for_hash&#34; =&gt; &#34;%{sql}&#34;}
  }
  mutate {
    gsub =&gt; [
        &#34;sql_for_hash&#34;, &#34;&#39;.+?&#39;&#34;, &#34;&#34;,
        &#34;sql_for_hash&#34;, &#34;-?\d*\.{0,1}\d+&#34;, &#34;&#34;
    ]
  }
  checksum {
    algorithm =&gt; &#34;md5&#34;
    keys =&gt; [&#34;sql_for_hash&#34;]
  }
</code></pre><p>最后算出来的md5, 放入了<code>logstash_checksum</code></p>
<p>第三个, 某些sql会非常大, 例如某些不规范的sql可能到几百M或是上G&hellip;.会直接导致采集进程OOM, 所以, 处理时, 设定超过100k丢弃掉</p>
<p>第四个, 默认多行处理, 一条sql可能停留在采集端没有上报, 需要等到下一条sql进来, 这样是有问题的, 如果一直没有后续, 最后一条将不会进入引擎. 所以, 在配置中设定了超过5s自动上报</p>
<p>完整的logstash配置文件(具体使用可能需要根据自身日志格式做些小调整)
注意, 里面的pattern <code>ALLWORD [\s\S]*</code></p>
<pre tabindex="0"><code>input {
  file {
    path =&gt; [&#34;/data/mysqllog/20000/slow-query.log&#34;]
    sincedb_path =&gt; &#34;/data/LogNew/logstash/sincedb/mysql.sincedb&#34;
    type =&gt; &#34;mysql-slow-log&#34;
    add_field =&gt; [&#34;env&#34;, &#34;PRODUCT&#34;]
    codec =&gt; multiline {
      pattern =&gt; &#34;^# User@Host:&#34;
      negate =&gt; true
      what =&gt; previous
      max_bytes =&gt; &#34;100kib&#34;
      auto_flush_interval =&gt; 5
    }
  }
}
filter {
  if (&#34;multiline_codec_max_bytes_reached&#34; in [tags]) {
      drop {}
  }
  grok {
    # User@Host: logstash[logstash] @ localhost [127.0.0.1]
    # User@Host: logstash[logstash] @  [127.0.0.1]
    match =&gt; [ &#34;message&#34;, &#34;^# User@Host: %{ALLWORD:user}\[%{ALLWAORD}\] @ %{ALLWORD:dbhost}? \[%{IP:ip}\]&#34; ]
  }
  grok {
    # Query_time: 102.413328  Lock_time: 0.000167 Rows_sent: 0  Rows_examined: 1970
    match =&gt; [ &#34;message&#34;, &#34;^# Query_time: %{NUMBER:duration:float}%{SPACE}Lock_time: %{NUMBER:lock_wait:float}%{SPACE}Rows_sent: %{NUMBER:results:int}%{SPACE}Rows_examined:%{SPACE}%{NUMBER:scanned:int}%{ALLWORD:sql}&#34;]
  }

  # Capture the time the query happened
  grok {
    match =&gt; [ &#34;message&#34;, &#34;^SET timestamp=%{NUMBER:timestamp};&#34; ]
  }
  # if codec multiline parse failure
  if (&#34;_grokparsefailure&#34; in [tags]) {
      drop {}
  }
  date {
    match =&gt; [ &#34;timestamp&#34;, &#34;UNIX&#34; ]
  }

  mutate {
    gsub =&gt; [
        &#34;sql&#34;, &#34;\nSET timestamp=\d+?;\n&#34;, &#34;&#34;,
        &#34;sql&#34;, &#34;\nuse [a-zA-Z0-9\-\_]+?;&#34;, &#34;&#34;,
        &#34;sql&#34;, &#34;\n# Time: \d+\s+\d+:\d+:\d+&#34;, &#34;&#34;,
        &#34;sql&#34;, &#34;\n/usr/local/mysql/bin/mysqld.+$&#34;, &#34;&#34;,
        &#34;sql&#34;, &#34;\nTcp port:.+$&#34;, &#34;&#34;,
        &#34;sql&#34;, &#34;\nTime .+$&#34;, &#34;&#34;
    ]
  }



  # calculate unique hash
  mutate {
    add_field =&gt; {&#34;sql_for_hash&#34; =&gt; &#34;%{sql}&#34;}
  }
  mutate {
    gsub =&gt; [
        &#34;sql_for_hash&#34;, &#34;&#39;.+?&#39;&#34;, &#34;&#34;,
        &#34;sql_for_hash&#34;, &#34;-?\d*\.{0,1}\d+&#34;, &#34;&#34;
    ]
  }
  checksum {
    algorithm =&gt; &#34;md5&#34;
    keys =&gt; [&#34;sql_for_hash&#34;]
  }

  # Drop the captured timestamp field since it has been moved to the time of the event
  mutate {
    # TODO: remove the message field
    remove_field =&gt; [&#34;timestamp&#34;, &#34;message&#34;, &#34;sql_for_hash&#34;]
  }
}
output {
    #stdout{
    #    codec =&gt; rubydebug
    #}
    #if (&#34;_grokparsefailure&#34; not in [tags]) {
    #    stdout{
    #        codec =&gt; rubydebug
    #    }
    #}
    if (&#34;_grokparsefailure&#34; not in [tags]) {
        elasticsearch {
          hosts =&gt; [&#34;192.168.1.1:9200&#34;]
          index =&gt; &#34;logstash-slowlog&#34;
        }
    }
}
</code></pre><p>采集进去的内容</p>
<pre tabindex="0"><code>{
           &#34;@timestamp&#34; =&gt; &#34;2016-05-23T21:12:59.000Z&#34;,
             &#34;@version&#34; =&gt; &#34;1&#34;,
                 &#34;tags&#34; =&gt; [
        [0] &#34;multiline&#34;
    ],
                 &#34;path&#34; =&gt; &#34;/Users/ken/tx/elk/logstash/data/slow_sql.log&#34;,
                 &#34;host&#34; =&gt; &#34;Luna-mac-2.local&#34;,
                 &#34;type&#34; =&gt; &#34;mysql-slow&#34;,
                  &#34;env&#34; =&gt; &#34;PRODUCT&#34;,
                 &#34;user&#34; =&gt; &#34;dba_bak_all_sel&#34;,
                   &#34;ip&#34; =&gt; &#34;10.166.140.109&#34;,
             &#34;duration&#34; =&gt; 28.812601,
            &#34;lock_wait&#34; =&gt; 0.000132,
              &#34;results&#34; =&gt; 749414,
              &#34;scanned&#34; =&gt; 749414,
                  &#34;sql&#34; =&gt; &#34;SELECT /*!40001 SQL_NO_CACHE */ * FROM `xxxxx`;&#34;,
    &#34;logstash_checksum&#34; =&gt; &#34;3e3ccb89ee792de882a57e2bef6c5371&#34;
}
</code></pre><h2 id="4-写查询">4. 写查询</h2>
<p>查询, 我们需要按<code>logstash_checksum</code>进行聚合, 然后按照次数由多到少降序展示, 同时, 每个<code>logstash_checksum</code>需要有一条具体的sql进行展示</p>
<p>通过 es 的 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-top-hits-aggregation.html">Top hits Aggregation</a> 可以完美地解决这个查询需求</p>
<p>查询的query</p>
<pre tabindex="0"><code>body = {
    &#34;from&#34;: 0,
    &#34;size&#34;: 0,
    &#34;query&#34;: {
        &#34;filtered&#34;: {
            &#34;query&#34;: {
                &#34;match&#34;: {
                    &#34;user&#34;: &#34;test&#34;
                }
            },
            &#34;filter&#34;: {
                &#34;range&#34;: {
                    &#34;@timestamp&#34;: {
                        &#34;gte&#34;: &#34;now-1d&#34;,
                        &#34;lte&#34;: &#34;now&#34;
                    }
                }
            }
        }
    },
    &#34;aggs&#34;: {
        &#34;top_errors&#34;: {
            &#34;terms&#34;: {
                &#34;field&#34;: &#34;logstash_checksum&#34;,
                &#34;size&#34;: 20
            },
            &#34;aggs&#34;: {
                &#34;top_error_hits&#34;: {
                    &#34;top_hits&#34;: {
                        &#34;sort&#34;: [
                            {
                                &#34;@timestamp&#34;:{
                                    &#34;order&#34;: &#34;desc&#34;
                                }
                            }
                        ],
                        &#34;_source&#34;: {
                            &#34;include&#34;: [
                               &#34;user&#34; , &#34;sql&#34;, &#34;logstash_checksum&#34;, &#34;@timestamp&#34;, &#34;duration&#34;, &#34;lock_wait&#34;, &#34;results&#34;, &#34;scanned&#34;
                            ]
                        },
                        &#34;size&#34; : 1
                    }
                }
            }
        }
    }
}
</code></pre><p>跟这个写法相关的几个参考链接: <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html#search-aggregations-bucket-terms-aggregation">Terms Aggregation</a> /  <a href="http://stackoverflow.com/questions/25986538/elasticsearch-filter-document-group-by-field">Elasticsearch filter document group by field</a></p>
<h2 id="5-渲染页面">5. 渲染页面</h2>
<p>python的后台, 使用<code>sqlparse</code>包, 将sql进行格式化(换行/缩进/大小写), 再往前端传. <a href="https://pypi.python.org/pypi/sqlparse">sqlparse</a></p>
<pre tabindex="0"><code>&gt;&gt;&gt; sql = &#39;select * from foo where id in (select id from bar);&#39;
&gt;&gt;&gt; print sqlparse.format(sql, reindent=True, keyword_case=&#39;upper&#39;)
SELECT *
FROM foo
WHERE id IN
  (SELECT id
   FROM bar);
</code></pre><p>然后在页面上, 使用js进行语法高亮  <a href="https://github.com/google/code-prettify">code-prettify</a></p>
]]></content>
		</item>
		
		<item>
			<title>[分享]关于代码调试DE那些事</title>
			<link>https://wklken.me/posts/2016/05/12/how-to-debug.html</link>
			<pubDate>Thu, 12 May 2016 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/05/12/how-to-debug.html</guid>
			<description>之前写过一篇博文, 做分享, 重新梳理了下</description>
			<content type="html"><![CDATA[<hr>
<p>之前写过一篇博文, 做分享, 重新梳理了下</p>
<hr>
<object data="/extra/share/how-to-debug.pdf" type="application/pdf" width="729" height="525">
    <embed src="/extra/share/how-to-debug.pdf">
    </embed>
</object>
]]></content>
		</item>
		
		<item>
			<title>Logstash&#43;ElasticSearch&#43;Kibana- 实现相对通用的数据收集分析</title>
			<link>https://wklken.me/posts/2016/05/08/elk-data-collect.html</link>
			<pubDate>Sun, 08 May 2016 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/05/08/elk-data-collect.html</guid>
			<description>ElasticSearch + Logstash + Kibana, 目前应该算是一套完整的日志收集/存储/统计解决方案. 在上一篇 Logstash+ElasticSearch+Kibana处理ngin</description>
			<content type="html"><![CDATA[<p>ElasticSearch + Logstash + Kibana, 目前应该算是一套完整的日志收集/存储/统计解决方案.</p>
<p>在上一篇 <a href="http://www.wklken.me/posts/2015/04/26/elk-for-nginx-log.html">Logstash+ElasticSearch+Kibana处理nginx访问日志</a> 中, 介绍了如何统一处理<code>nginx</code>日志</p>
<p>最近正好在做应用日志及上报日志的汇聚和统计工作, 分享下处理方式.</p>
<p>目标是: 一次搭建, 后面只需要关心输入(日志记录)以及输出(Kibana统计展示)</p>
<h2 id="日志">日志</h2>
<p>日志的来源:</p>
<pre tabindex="0"><code>1. 服务日志: 服务端记录下来的日志, 例如搜索日志等, 内容较为详尽
2. 上报日志: 来自于前端/android/ios/桌面端等, 根据用户操作行为, 上报一些数据, 例如按钮点击量, 转化率等, 也可以上报崩溃日志
</code></pre><p>其中, 上报日志, 可以制定一套协议, 不同端统一走上报服务接口. 这个服务对性能有要求, 具体协议需要足够灵活, 支持各类统计分析需求. 使用golang写了一个 <a href="https://github.com/wklken/http_json_logger">http_json_logger</a></p>
<p>日志在不同应用/机器记录后, 可以通过<code>rsync</code>/<code>nfs</code>/<code>scp</code>等, 汇总到一个地方进行统一处理, 也可以通过多个<code>logstash shipper</code>进行汇聚</p>
<p>关于日志格式,  统一使用<code>json</code>格式, 落地过程中包含平台<code>{platform}</code>, 以及 项目<code>{project}</code>, 模块<code>{module}</code>,  落地时间<code>ts</code>等</p>
<p>为什么要落地: 落地成文件, 定时压缩备份存档, 不论日志处理系统是否有问题, 都能保证数据已经存下来了.</p>
<p>当然, 也可以考虑使用logstash监听端口, 分别落地到文件及转入es, 没具体实践过.</p>
<p>在这一步, 我的处理方案是:</p>
<pre tabindex="0"><code>1. 使用统一上报接口, 日志落地到上报服务的数据盘

2. 服务端服务日志, 同一台机器在相同数据盘, 使用同一个logstash shipper进行汇聚

对日志文件名等, 不强求一致性, 你可以认为, 不同项目/不同模块的json都可以直接记录到同一个日志文件(虽然不鼓励这么做), 通过日志body内容而不是日志文件名来处理
</code></pre><h2 id="汇聚-logstash-shipper">汇聚: logstash shipper</h2>
<p>不得不说, logstash的确是神器</p>
<p>上一步, 日志中强制日志中每一行是一条json记录.  同时json body中记录了时间戳(timestamp, <code>ts</code>字段)</p>
<p>这一步, 配置 <code>logstash</code>将某些目录下的所有日志文件进行汇聚</p>
<p>配置示例:</p>
<pre tabindex="0"><code>input {
  file {
    path =&gt; [ &#34;/data/collect/ios/*.log&#34;, &#34;/data/collect/android/*.log&#34;, &#34;/data/collect/web/*.log&#34;, &#34;/data/collect/wap/*.log&#34; ]
    start_position =&gt; &#34;beginning&#34;
    codec =&gt; json
  }
}

# make ts to @timestamp
filter {
  date {
    match =&gt; [ &#34;ts&#34; , &#34;dd/MMM/YYYY:HH:mm:ss Z&#34;, &#34;UNIX&#34; ]
  }
}


output {
    redis { host =&gt; &#34;127.0.0.1&#34; data_type =&gt; &#34;list&#34; key =&gt; &#34;logstash:collect:log&#34; }
}
</code></pre><p>这时候, 所有日志集中到了一个地方</p>
<h2 id="处理并存储到es">处理并存储到es</h2>
<p>首先, 从redis中读取消息体, 检查并丢弃一些信息, 然后,  根据消息体内<code>platform</code>/ <code>project</code>/<code>module</code>, 分配到es不同的<code>index</code>
可以根据需要控制粒度</p>
<p>配置示例</p>
<pre tabindex="0"><code>input {
  redis {
    host =&gt; &#34;127.0.0.1&#34;
    port =&gt; &#34;6379&#34;
    key =&gt; &#34;logstash:collect:log&#34;
    data_type =&gt; &#34;list&#34;
    codec  =&gt; &#34;json&#34;
    type =&gt; &#34;logstash-collect-log&#34;
    tags =&gt; [&#34;collect&#34;]
  }
}


# drop invalid record
filter {
    if ![platform] {
        drop {}
    }
    if ![project] {
        drop {}
    }
    if ![module] {
        drop {}
    }
}

output {
    elasticsearch {
      host =&gt; &#34;127.0.0.1&#34;
      index =&gt; &#34;%{platform}-%{project}-%{module}-%{+YYYY.MM.dd}&#34;
    }
}
</code></pre><h2 id="es">ES</h2>
<p>你会发现, es已经的index中已经有了具体的数据. json中的每个字段都有&hellip;&hellip;</p>
<h2 id="然后呢">然后呢?</h2>
<p>整套调通之后, 接下来的工作呢?</p>
<p>假设来了个统计需求</p>
<ol>
<li>分析需求, 拆解, 确定统计维度, 需要上报的字段等, 根据协议, 确定<code>{platform}/{project}/{module}</code></li>
<li>前端/客户端等, 根据协议, 调用上报接口, 执行数据上报</li>
<li>到<code>kibana</code>,  找到对应<code>index</code>, 根据需求配置对应的展现</li>
</ol>
<p>所有的一般性统计需求, 都可以通过<code>三板斧</code>直接搞定, 只需处理输入以及输出, 没有任何额外工作.</p>
<h2 id="最后">最后</h2>
<p>目前量不大, 完美解决了快速迭代中各类原先处理起来十分困难的统计需求和日志分析(原来要自己上报汇聚数据, 自己拷贝到同一台机器, 自己写统计脚本, 存库, 还得自己撸前端, 搞完之后还被黑说: <code>花了那么多时间, 只搞出个这么反人类的/丑/不是我想要的.....界面</code>)</p>
<p>当然, 随着业务发展, 各类日志的量都会逐渐上来, 对性能/存储的要求会越来越高, 但是<code>elk</code>本身对横向扩容只是非常完美, 在相当长一段时间内, 应该可以<code>hold</code>住.(老大, 我要加机器/硬盘/内存/CPU)</p>
<p>ok, 先这些</p>
<p>wklken</p>
<p>2015-05-08 于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>ELK维护的一些点(二)</title>
			<link>https://wklken.me/posts/2016/05/07/elk-about-2.html</link>
			<pubDate>Sat, 07 May 2016 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/05/07/elk-about-2.html</guid>
			<description>很杂, 涉及到最近处理的一些点 根据string转浮点数的某个字段排序 一个字段, resp_time, mapping中是string, 有需求是, 按照响应时间降序排序</description>
			<content type="html"><![CDATA[<p>很杂, 涉及到最近处理的一些点</p>
<hr>
<h3 id="根据string转浮点数的某个字段排序">根据string转浮点数的某个字段排序</h3>
<p>一个字段, <code>resp_time</code>, mapping中是string, 有需求是, 按照响应时间降序排序, 此时需要构造qsl(在search中使用), 使用该字段转换为浮点数, 降序排列</p>
<p>第一步, 修改es配置, 增加groovy支持</p>
<p>elasticsearch.yml中加入</p>
<pre tabindex="0"><code>script.engine.groovy.inline.search: on
</code></pre><p>然后, 执行 <a href="http://www.wklken.me/posts/2016/02/16/elk-about-upgrade.html#rolling-restart">rolling restart</a>, 逐一重启集群每个节点</p>
<p>第二步, 构造qsl,  <code>sort</code>中,  增加<code>_script</code> 使用groovy脚本, 将对应字段从string转成数字, 再进行排序</p>
<pre tabindex="0"><code>&#39;sort&#39;: [{&#39;_script&#39;: {&#39;lang&#39;: &#39;groovy&#39;,
                       &#39;order&#39;: &#39;desc&#39;,
                       &#39;script&#39;: &#39;Float.parseFloat(doc[&#34;resp_time&#34;].value)&#39;,
                       &#39;type&#39;: &#39;number&#39;}},
          {&#39;@timestamp&#39;: &#39;desc&#39;}
          ]
</code></pre><p>附 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting.html">scripting文档</a></p>
<h3 id="fielddata-format-disabled导致的排序失效"><code>fielddata-format-disabled</code>导致的排序失效</h3>
<p>有个集群, 升级后, 发现<code>resp_time</code>字段的mapping是</p>
<pre tabindex="0"><code>&#34;resp_time&#34; : {
&#34;type&#34; : &#34;string&#34;,
&#34;norms&#34; : {
    &#34;enabled&#34; : false
},
&#34;fielddata&#34; : {
    &#34;format&#34; : &#34;disabled&#34;
},
&#34;fields&#34; : {
    &#34;raw&#34; : {
    &#34;type&#34; : &#34;string&#34;,
    &#34;index&#34; : &#34;not_analyzed&#34;,
    &#34;ignore_above&#34; : 256
    }
}
</code></pre><p>注意这里的, 是因为升级es 2.0之后, 默认值变更带来的问题</p>
<pre tabindex="0"><code>&#34;fielddata&#34; : {
  &#34;format&#34; : &#34;disabled&#34;
},
</code></pre><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/fielddata.html">fielddata文档</a></p>
<p>此时, 排序的qsl将会报错, 无法按照对应要求排序</p>
<pre tabindex="0"><code>Field data loading is forbidden on resp_time
</code></pre><p>解决方案, 挺简单的, 使用<code>foo.raw</code>即可</p>
<pre tabindex="0"><code>&#39;sort&#39;: [{&#39;_script&#39;: {&#39;lang&#39;: &#39;groovy&#39;,
    &#39;order&#39;: &#39;desc&#39;,
    &#39;script&#39;: &#39;Float.parseFloat(doc[&#34;resp_time.raw&#34;].value)&#39;,
    &#39;type&#39;: &#39;number&#39;}},
{&#39;@timestamp&#39;: &#39;desc&#39;}
]
</code></pre><h3 id="使用聚合">使用聚合</h3>
<p>把string类型的<code>resp_time</code>放到<code>aggs</code>中做聚合的时候.</p>
<pre tabindex="0"><code>&#34;aggs&#34;: {
     &#34;resp_time_stats&#34;: {&#34;stats&#34;: {&#34;script&#34;: &#39;Float.parseFloat(doc[&#34;resp_time.raw&#34;].value)&#39;}}
}
</code></pre><p>此时, 会报错</p>
<pre tabindex="0"><code>{u&#39;error&#39;: {u&#39;failed_shards&#39;: [{u&#39;index&#39;: u&#39;logstash-2016.04.10&#39;,
                                u&#39;node&#39;: u&#39;AvemqKN-RGKy68zJXUapBg&#39;,
                                u&#39;reason&#39;: {u&#39;reason&#39;: u&#39;scripts of type [inline], operation [aggs] and lang [groovy] are disabled&#39;,
                                            u&#39;type&#39;: u&#39;script_exception&#39;},
                                u&#39;shard&#39;: 0}],
            u&#39;grouped&#39;: True,
            u&#39;phase&#39;: u&#39;query&#39;,
            u&#39;reason&#39;: u&#39;all shards failed&#39;,
            u&#39;root_cause&#39;: [{u&#39;reason&#39;: u&#39;scripts of type [inline], operation [aggs] and lang [groovy] are disabled&#39;,
                             u&#39;type&#39;: u&#39;script_exception&#39;}],
            u&#39;type&#39;: u&#39;search_phase_execution_exception&#39;},
 u&#39;status&#39;: 500}
</code></pre><p>处理, es加配置, 逐一重启</p>
<pre tabindex="0"><code>script.engine.groovy.inline.aggs: on
</code></pre><p>相关 <a href="https://discuss.elastic.co/t/scripts-of-type-inline-operation-aggs-and-lang-groovy-are-disabled/2493">文档</a></p>
<h3 id="logstash-grok-default-patterns">logstash grok default patterns</h3>
<p>默认的一些pattern, 见 <a href="https://github.com/logstash-plugins/logstash-patterns-core/blob/master/patterns/grok-patterns">grok-patterns</a></p>
<p>grok检查在线实时编辑, <a href="https://grokdebug.herokuapp.com/">https://grokdebug.herokuapp.com/</a></p>
<h3 id="logstash-codec-multiline-限制行数和日志大小">logstash codec multiline 限制行数和日志大小</h3>
<p>配置, 具体见 <a href="https://www.elastic.co/guide/en/logstash/current/plugins-codecs-multiline.html">multiline文档</a></p>
<pre tabindex="0"><code>input {
        codec =&gt; multiline {
            patterns_dir =&gt; &#34;./patterns&#34;
            pattern =&gt; &#34;&#34;
            what =&gt; &#34;previous&#34;
            negate  =&gt; true
            max_lines =&gt; 100
            max_bytes =&gt; &#34;50kib&#34;
        }
}
</code></pre><p>单位 <a href="https://www.elastic.co/guide/en/logstash/current/configuration-file-structure.html#bytes">bytes</a></p>
<p>实践中, 使用<code>max_bytes</code>, 当<code>what=previous + negate=true</code>的情况下, 即不匹配模式的, 归属前一部分, 这种情况下, 性能ok, 反之<code>what=next + negate=true</code>的情况下, 不匹配成功归属于后半部分, 此时产生的cpu消耗非常之大, 可以将一台机器跑满.</p>
<p>另外, 假设配置<code>max_bytes=1M</code>, 此时用户打了50M, 会给这个event打上tag <code>multiline_codec_max_bytes_reache</code>, 但是, 这50M 最终还是会经logstash灌入到es里面. 即, 超了, 但是并不自动截掉</p>
<p>这时候, 我们可以, 使用<code>mutate-replace</code>直接替换掉</p>
<pre tabindex="0"><code>    # if multiline_codec_max_lines_reached
    if (&#34;multiline_codec_max_bytes_reached&#34; in [tags]) {
        mutate {
            replace =&gt; {
                &#34;message&#34; =&gt; &#34;Log System Warnning: multiline_codec_max_lines_reached, Your log has exceeded 50kB(51200 chars), it was blocked by log system. Please check your code to make your log info shorter and useful&#34;
                &#34;msg&#34; =&gt; &#34;Log System Warnning: multiline_codec_max_lines_reached, Your log has exceeded 50kB(51200 chars), it was blocked by log system. Please check your code to make your log info shorter and useful&#34;
            }
        }
    }
</code></pre><h3 id="使用supervisord管理logstash进程">使用supervisord管理logstash进程</h3>
<p>之前提到, 升级集群后, 使用supervisord统一管理logstash进程, <a href="http://www.wklken.me/posts/2016/02/16/elk-about-upgrade.html#supervisord">链接</a></p>
<h3 id="查看当前机器logstash进程top">查看当前机器logstash进程top</h3>
<p>有时, 需要上机器看看对应采集端所有logstash进程是否存在问题, 常常用到<code>top</code>命令, 所以写了个简单的脚本, 配合supervisord的脚本使用</p>
<p>ltop.sh</p>
<pre tabindex="0"><code>#!/bin/bash
./logstashd.sh status
top -p $(./logstashd.sh status | awk &#39;{print $4}&#39; | awk -F&#39;,&#39; &#39;{print $1}&#39; | tr &#39;\n&#39; &#39;,&#39; | sed &#39;s/,$//g&#39;)
</code></pre><h3 id="进程占用cpu检测脚本">进程占用cpu检测脚本</h3>
<pre tabindex="0"><code>#!/bin/bash
BASEDIR=$(dirname $0)
cd $BASEDIR
CURRENT_DIR=`pwd`

exec &gt;&gt; /tmp/log/monitor.log 2&gt;&amp;1
echo &#34;==============================================&#34;
date
function check() {
    PNAME=$1
    PID=$2
    CPU_USE=$(ps -p $PID -o %cpu | sed -n &#39;2p&#39;)
    INT_CPU_USE=$(printf &#34;%.0f\n&#34; $CPU_USE)
    echo $PNAME&#34; - &#34;$CPU_USE&#34; - &#34;$INT_CPU_USE

    if [ $INT_CPU_USE -gt 85 ]
    then
       echo &#34;$PNAME cpu usage greater than 85%,do restart&#34;
       ./logstashd.sh restart $PNAME
    fi
}
export -f check
./logstashd.sh status | awk &#39;{print &#34;-&#34;, $1, $4}&#39; | awk -F&#39;,&#39; &#39;{print $1}&#39; | xargs -n3 bash -c &#39;check $@&#39;
</code></pre><h3 id="数据盘满了导致集群状态yellow">数据盘满了导致集群状态yellow</h3>
<p>机器节点本身有1T 硬盘, 由两块盘组成, 配置es的时候, 数据分别写到了两个盘上, 然后有一天集群状态告警了</p>
<pre tabindex="0"><code>&#34;status&#34; : &#34;yellow&#34;,
</code></pre><p>查看es的日志</p>
<pre tabindex="0"><code>[2016-03-21 12:43:45,934][INFO ][cluster.routing.allocation.decider] [node_01] low disk watermark [85%] exceeded
on [AvemqKN-RGKy68zJXUapBg][node_01][/data/LogNewData/xxx/nodes/0] free: 75.5gb[14.1%], replicas will not be assigned to this node
</code></pre><p>处理: 腾磁盘空间出来, es会自动检测恢复</p>
<p>PS: 磁盘大小要预估好</p>
<h3 id="查看redis中队列的堆积">查看redis中队列的堆积</h3>
<p>历史遗留问题, 有些节点采集发送到redis的key, 在indexer阶段并没有被消费, 导致越堆越多&hellip;.</p>
<p>这时候, 可以通过redis查下哪些队列堆积了</p>
<pre tabindex="0"><code>bin/redis-cli -h 127.0.0.1 -p 6379 -a blueking_log --bigkeys
</code></pre><p>需要redis版本支持<code>bigkeys</code> =&gt; This is a &ldquo;new&rdquo; feature beginning with 2.8</p>
<h3 id="解析失败丢弃及黑名单实现">解析失败丢弃及黑名单实现</h3>
<p>grok解析失败, 丢弃</p>
<pre tabindex="0"><code>if (&#34;_grokparsefailure&#34; in [tags]) {
    drop {}
}
</code></pre><p>有时候, 需要禁止采集某些文件, 但由于<code>file</code>类型的<code>exclude</code>只能用文件名, 而没有更强大的规则, 所以只能采集进来再丢弃, 此时, 可以根据路径grok解析出关键字, 然后判断丢弃</p>
<pre tabindex="0"><code>if ([keyworod] in [&#34;data&#34;, &#34;not_exists&#34;])
{
    drop {}
}
</code></pre><h3 id="启动限制使用的worker数">启动限制使用的worker数</h3>
<p>默认情况, 有可能把所有cpu跑满, 这时候, 可以专门加下</p>
<pre tabindex="0"><code>-w, --pipeline-workers COUNT  Sets the number of pipeline workers to run. (default: 24)

logstash agent -f conf/xxx.conf -w 2
</code></pre><h3 id="几个简单脚本">几个简单脚本</h3>
<p>health.sh</p>
<pre tabindex="0"><code>#!/bin/bash
curl &#39;http://127.0.0.1:9200/_cluster/health?pretty=true&#39;
</code></pre><p>indices.sh</p>
<pre tabindex="0"><code>#!/bin/bash
curl &#39;http://127.0.0.1:9200/_cat/indices?v&#39; | sort -k 3
</code></pre>]]></content>
		</item>
		
		<item>
			<title>[分享]Python源码剖析-数据结构</title>
			<link>https://wklken.me/posts/2016/03/01/python-source-datastructure.html</link>
			<pubDate>Tue, 01 Mar 2016 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/03/01/python-source-datastructure.html</guid>
			<description></description>
			<content type="html"><![CDATA[<object data="/extra/share/python-source-datastructure.pdf" type="application/pdf" width="729" height="525">
    <embed src="/extra/share/python-source-datastructure.pdf">
    </embed>
</object>
]]></content>
		</item>
		
		<item>
			<title>一些Centos Python生产环境的部署命令</title>
			<link>https://wklken.me/posts/2016/02/18/python-env-in-centos.html</link>
			<pubDate>Thu, 18 Feb 2016 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/02/18/python-env-in-centos.html</guid>
			<description>Just notes 拿到一台干净的centos之后, 初始化Python环境, 一些命令和问题记录而已 可以搞成脚本自动初始化, 当然, 用docker更好 基础环境 1.</description>
			<content type="html"><![CDATA[<p>Just notes</p>
<p>拿到一台干净的centos之后, 初始化Python环境, 一些命令和问题记录而已</p>
<p>可以搞成脚本自动初始化, 当然, 用docker更好</p>
<hr>
<h2 id="基础环境">基础环境</h2>
<h4 id="1-创建用户">1. 创建用户</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo adduser newuser
</span></span><span class="line"><span class="cl">sudo passwd newuser
</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">sudo /usr/sbin/visudo
</span></span><span class="line"><span class="cl">newuser       <span class="nv">ALL</span><span class="o">=</span>NOPASSWD: ALL
</span></span></code></pre></div><h4 id="2-epelfedora-extra-packages-for-enterprise-linux-repository-">2. EPEL(Fedora Extra Packages for Enterprise Linux repository )</h4>
<p><a href="http://www.rackspace.com/knowledge_center/article/install-epel-and-additional-repositories-on-centos-and-red-hat">参考文档</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
</span></span><span class="line"><span class="cl">sudo rpm -Uvh epel-release-6*.rpm
</span></span></code></pre></div><h4 id="3-加ius源">3. 加ius源</h4>
<p><a href="http://dl.iuscommunity.org/pub/ius/stable/CentOS/6/x86_64/ius-release-1.0-13.ius.centos6.noarch.rpm">包地址</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wget http://dl.iuscommunity.org/pub/ius/stable/CentOS/6/x86_64/ius-release-1.0-13.ius.centos6.noarch.rpm
</span></span><span class="line"><span class="cl">sudo rpm -Uvh ius-release-1.0-13.ius.centos6.noarch.rpm
</span></span></code></pre></div><h4 id="4-安装python27--python3--pip">4. 安装python2.7 / python3 / pip</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo yum install python27
</span></span><span class="line"><span class="cl">sudo yum install python27-devel
</span></span></code></pre></div><p>then</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py <span class="p">|</span> sudo /usr/bin/python2.7 -
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># pip</span>
</span></span><span class="line"><span class="cl">curl https://raw.githubusercontent.com/pypa/pip/master/contrib/get-pip.py <span class="p">|</span> sudo /usr/bin/python2.7 -
</span></span></code></pre></div><p>extra: install python3</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo yum install python34u python34u-devel
</span></span></code></pre></div><h4 id="5-virtualenv">5. virtualenv</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo pip install virtualenv
</span></span></code></pre></div><h4 id="6-gen-ssh-key">6. gen ssh key</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ssh-keygen -t rsa
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">add ~/.ssh/id_rsa.pub to git or github
</span></span></code></pre></div><h2 id="一些服务">一些服务</h2>
<h4 id="1-install-git">1. install git</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo yum install git
</span></span></code></pre></div><h4 id="2-mysql">2. mysql</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo yum install mysql
</span></span><span class="line"><span class="cl">sudo yum install mysql-devel* -y
</span></span><span class="line"><span class="cl">sudo yum install mysql-server
</span></span><span class="line"><span class="cl">sudo /sbin/service mysqld start
</span></span></code></pre></div><h4 id="3-redis">3. redis</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo yum install redis
</span></span></code></pre></div><h4 id="4-rabbitmq">4. rabbitmq</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo yum install rabbitmq-server
</span></span></code></pre></div><h2 id="问题">问题</h2>
<h4 id="errno-14-peer-cert-cannot-be-verified-or-peer-cert-invalid">[Errno 14] Peer cert cannot be verified or peer cert invalid</h4>
<p>add &ldquo;sslverify=false&rdquo; in /etc/yum.conf</p>
<h4 id="pip-error-importerror-cannot-import-name-httpshandler">pip error: ImportError: cannot import name HTTPSHandler</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yum install openssl openssl-devel -y
</span></span></code></pre></div><p><a href="http://stackoverflow.com/questions/20688034/importerror-cannot-import-name-httpshandler-using-pip">see in sof</a></p>
<h4 id="mysql-python">MySQL-python</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">    _mysql.c:2642: error: initializer element is not constant
</span></span><span class="line"><span class="cl">    _mysql.c:2642: error: <span class="o">(</span>near initialization <span class="k">for</span> ‘_mysql_ResultObject_memberlist<span class="o">[</span>0<span class="o">]</span>.offset’<span class="o">)</span>
</span></span><span class="line"><span class="cl">    _mysql.c: In <span class="k">function</span> ‘_mysql_ConnectionObject_getattr’:
</span></span><span class="line"><span class="cl">    _mysql.c:2666: error: ‘_mysql_ConnectionObject’ has no member named ‘open’
</span></span><span class="line"><span class="cl">    error: <span class="nb">command</span> <span class="s1">&#39;gcc&#39;</span> failed with <span class="nb">exit</span> status <span class="m">1</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">Command <span class="s2">&#34;/data/home/alarm/env/bin/python -c &#34;</span>import setuptools, tokenize<span class="p">;</span><span class="nv">__file__</span><span class="o">=</span><span class="s1">&#39;/tmp/pip-build-HgXAQT/MySQL-python/setup.py&#39;</span><span class="p">;</span>exec<span class="o">(</span>compile<span class="o">(</span>getattr<span class="o">(</span>tokenize, <span class="s1">&#39;open&#39;</span>, open<span class="o">)(</span>__file__<span class="o">)</span>.read<span class="o">()</span>.replace<span class="o">(</span><span class="s1">&#39;\r\n&#39;</span>, <span class="s1">&#39;\n&#39;</span><span class="o">)</span>, __file__, <span class="s1">&#39;exec&#39;</span><span class="o">))</span><span class="s2">&#34; install --record /tmp/pip-kx2cSu-record/install-record.txt --single-version-externally-managed --compile&#34;</span> failed with error code <span class="m">1</span> in /tmp/pip-build-HgXAQT/MySQL-python
</span></span></code></pre></div><p>处理:  <code>yum install mysql-devel</code></p>
]]></content>
		</item>
		
		<item>
			<title>摘录&lt;&lt;6个月学会任何一种外语&gt;&gt;</title>
			<link>https://wklken.me/posts/2016/02/17/master-en-in-six-months.html</link>
			<pubDate>Wed, 17 Feb 2016 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/02/17/master-en-in-six-months.html</guid>
			<description>在高铁上顺手刷完的一本书, 学习英语的方法论吧, 和学习编程有很多类似的地方, 互相印证. 还是值得一读的, 建议想掌握英文的同学买一本读一读 额, 英文</description>
			<content type="html"><![CDATA[<p>在高铁上顺手刷完的一本书, 学习英语的方法论吧, 和学习编程有很多类似的地方, 互相印证.</p>
<p>还是值得一读的, 建议想掌握英文的同学买一本读一读</p>
<p>额, 英文这个问题困扰了我很久, 当然, 学习的主要目的是能更快更好的读懂文档以及进行一些基本交流(其实是为了刷电影美剧更方便些ORZ)</p>
<p>从去年开始就实行<code>泡脑子</code>的策略, 上下班各半个小时, 反复在听老友记十季的音频(安利app: 喜马拉雅). 然后固定在刷几部美剧, 买了本英文著作一字一句反复读中, 也开始在国外的论坛提问题, 发言, 用kindle开启生词模式在刷书. 当然, 俺的英文还是别别扭扭的&hellip;..</p>
<p>程序员的一大优势是, 文档/搜索, 基本都是英文的, 这个环境还是不错的, 另外, mac/iphone/ipad全部切成英文系统, 取词软件全开, 哈哈哈&hellip;..</p>
<p>然而, 还是不会说啊&hellip;..痛定思痛</p>
<hr>
<p>以下读书摘要</p>
<h4 id="观点">观点</h4>
<p>本书的观点</p>
<ul>
<li>我特别主张自然规律，你越能理解和运用自然规律，你就越能成功</li>
<li>一个大脑健全的成年人完全有能力在6个月内从0掌握任何一种外语！</li>
<li>只要掌握规律，跟随规律去练习，一个人能做到的结果，任何人都能做到！</li>
</ul>
<p>长期误解的错误的观点</p>
<ul>
<li>有语言天赋的人才能学会外语</li>
<li>到国外待一段时间就能学会外语</li>
</ul>
<h4 id="掌握一种外语的标准">掌握一种外语的标准</h4>
<ul>
<li>（1）你已经掌握了有关语言的最高频单词和词组，你能听懂，也能自己独立使用。在外语里边，掌握1000个最高频词，完全满足日常生活沟通所需的85%，3000个高频词可以覆盖日常沟通、工作及商务交流的98%以上。</li>
<li>（2）你完全可以很自然地使用你已经会的词组和单词，来创造你想说的任何句子，沟通你想表达的意思。当然，有时候你会找不到最确切和最巧妙的说法，但是你完全有能力找到能用的词来表达你想说的东西。</li>
<li>（3）听到陌生单词的时候，你会轻松地邀请别人解释给你听。而在这个过程当中，你完全有能力用外语来了解和接受这个新的概念。同时，你也有能力吸收这个概念带给你的生词。</li>
<li>（4）你的发音已经接近母语者。可能有的地方不是100%一样，但是这些绝对不会对你的沟通造成阻碍。</li>
<li>（5）你说外语的节奏、速度、轻重、停顿等，已经完全符合外语母语者的习惯，而且你在说的时候，总是感到很自然。你也会很恰当地用礼节和最普遍的感叹方式来进行日常“共鸣”，从而达到友好沟通的目的。</li>
<li>（6）你已经完全掌握了新的肢体语言的表达方式，包括一些面部表情、不同手势等。</li>
<li>（7）你已经建立了语感，在肚子里能知道哪些说法算是大家都认同的，也会感觉到哪些说法有一点儿偏离大家习惯的规矩。</li>
<li>（8）同时，为了学得更快，你也要懂得累了就休息。</li>
</ul>
<h4 id="学会外语的五项核心原则">学会外语的五项核心原则</h4>
<ul>
<li>从和你有密切关联的外语内容学起: 只要信息跟你个人没有重要关联，你就自然会觉得没有重要意义，因此不会给注意力。不给注意力的自然结果是记不得、学得慢甚至学不会。反之，任何对你本人有重要意义的信息，都会引起你的注意，并且当你可以给它足够注意力时，你就会自然而然学得特别快 =&gt; 结论: 为了把外语学好，你要找到这门外语跟你的重要关联。在学习时，你一定要选择跟自己的兴趣、动力有重要关联的外语内容</li>
</ul>
<p>和学习技术挺类似, 技术深入以及眼界扩展, 从自己工作内容相关的东西开始才能事半功倍, 才能有效率</p>
<ul>
<li>把外语当成沟通工具: 1. 把外语当成工具，会让你通过“用”来学外语，而不是先学后用. 把外语当成沟通工具的第一个必然结果是，只要你会一点，就会马上去用，从而体验这个工具发挥的效力。2. 把外语当成工具，会让你从功能出发，把形式放在后面学通过用外语来学外语的关键，是把自己的注意力先放在单词和词组的功能。这样，你会先用功能最明显的几个单词和词组来沟通，不让形式成为卡住自己学习的节点。3. 把外语当成工具，会让你自然接受多种说法，因此不拘泥于一个“标准答案” 4. 把外语当成工具，可以获取“反馈”带来的巨大帮助，因此效果好</li>
</ul>
<p>做技术的这种机会还是比较多的. 从实践中学习, 在实践中验证理论.</p>
<ul>
<li>
<p>理解了含义，自然能“获取”外语: 1. 可理解输入就是首先理解含义，然后下意识自动“获取”外语的过程。为了把你的外语学习速度加快，你需要在学习的过程中，给自己创造可理解输入的条件。2. 相信自己的潜意识学外语的能力</p>
</li>
<li>
<p>生理训练为主: 要学好外语，更重要的是要进行合适的生理训练，也就是说，外语学习的一个非常重要的部分，是训练大脑神经和外语肌肉。与练健美体操一样，练好外语的过程是一个肌肉训练的过程。1. 一定要用足够时间锻炼耳朵里的外语听觉神经！ 2. 发音训练绝对是肌肉训练的一个过程，因此要用体育训练的思路，指导自己找对路，这包括经常练，同时在练习的过程中注意动作的准确度。3. 创造外语条件反射！</p>
</li>
<li>
<p>掌握好心理状态: 用深度放松的状态来学习, 保持良好学习状态的另外一个重要因素，就是管理自己的渴望。必须开心忍受听不懂的阶段</p>
</li>
</ul>
<h4 id="学会外语的七个关键行为">学会外语的七个关键行为</h4>
<ul>
<li>多听——快速长好“外语DNA”: “泡脑子”表面的意思很简单，就是说要把自己的大脑“泡”在外语的声音里。除此之外，也有一些自我管理的方式和态度方面的问题需要注意，这样才能把“泡脑子”的作用发挥到极致。</li>
<li>先认识含义，后明白词: 作为学外语的成年人，也必须运用这个基本原理。不管怎么样，要把注意力先放在明白含义上，在这个基础上，你的大脑就很容易吸收你正在学的语言。换句话说，为了学会外语，要先懂意思，不是为了懂意思要先学会外语！ 肢体语言/整体环境和场景/通过已知”获取”未知/</li>
<li>大胆组合，大量去用</li>
<li>从核心高频内容开始学起: 为了把英语学得透彻，并且达到完全像母语者一样，根本不需要学那么多，更不需要同时什么都学，只需要把注意力放在外语的核心，掌握外语的高频词即可。所谓高频词就是沟通中出现频率高的外语单词，高频词是外语沟通的核心。所以，学到8000个单词以后，你的英文水平已经和普通的英语母语者一样好！外语学习内容贵精不贵多，学习的过程一定要遵守质量第一，数量第二的原则的</li>
<li>找一个好的外语家长</li>
<li>掌握发音的绝招: 除了听声音并调整自己面部肌肉之外，练发音的时候，还有一个“绝招”能用，就是“看脸说话”，意思是模仿外语母语者的面部和口型来练发音。</li>
<li>一个盒子两条路（Same Box-Different Path）: 我相信你很清楚，为了真正会外语，迟早必须懂得用外语思考。也就是说，自己的大脑能够直接将头脑中的意义联结到外语的声音。根本不需要经过中文翻译. 只有把外语的声音和内心的画面含义联结到一起，才是下意识高效率的学习，也就是用外语沟通的时候，自己沟通的感觉和效果与说母语一样。其实所谓“外语思维”就是这个意思——听到外语，在头脑中看到画面，同时注意到自己的感觉，说话时也是直接从画面和感觉联结到外语. “同一个盒子，两条路”的主要含义，来自于我们对人类大脑处理信息的科学认识。人对世界的认识和记忆，最主要来源不是文字或语言，更基层的，是神经层面的5种感觉，包括：视觉、听觉、触觉、嗅觉和味觉。 方法: 故意创造和运用画面/用比喻找到含义的本质/找到沟通的对象, 用外语交流/</li>
</ul>
<p>通过已知获取未知, 有点类似<code>如何高效学习</code>中提到的观点, 建立高速公路, 建立关联, 比喻.</p>
<p>还有, kindle中生词模式就是类似这样的&hellip;.</p>
<p>外语家长, 其实类似mentor的角色, 虽然多年以来&hellip;.还有, 实际的code review</p>
<h4 id="具体的学习计划">具体的学习计划</h4>
<p>自己看书吧, 有挺多借鉴的地方</p>
<p>不过, 对于我这个散漫闲人, 严格按部就班有些困难, 所以, 只能培养所谓的<code>习惯</code>, 侵占覆盖掉碎片时间, 同时把相关的方法论给<code>践行</code>了, 至于效果, 额, 过段时间看看&hellip;&hellip;</p>
<h4 id="一些书中提到的习惯">一些书中提到的习惯</h4>
<ul>
<li>习惯一：开始用自己的右脑，调整到每天进入英语频道。</li>
<li>习惯二：想到任何关于英语问题的时候，自动想起如何用英语发问。</li>
<li>习惯三：把自己的耳朵和嘴巴连接起来，变成一个“发音准确度反馈循环”。</li>
<li>习惯四：把英语声音直接连接到脑海中的画面和感觉。</li>
<li>习惯五：把玩词变成每天的习惯和乐趣。拼凑词，创造含义。</li>
<li>习惯六：完全建立自言自语的习惯</li>
<li>习惯七：每天用英语开口说话，进行真实沟通。</li>
<li>习惯八：随时开口练习发音。</li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>ELK 维护的一些点</title>
			<link>https://wklken.me/posts/2016/02/16/elk-about-upgrade.html</link>
			<pubDate>Tue, 16 Feb 2016 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/02/16/elk-about-upgrade.html</guid>
			<description>去年入职新公司之后, 负责维护平台的elk 这套东西是2013年搭建的, 年久失修, 所以做了个方案, 开始了批量升级 将logstash从1.3升级到</description>
			<content type="html"><![CDATA[<p>去年入职新公司之后, 负责维护平台的elk</p>
<p>这套东西是2013年搭建的, 年久失修, 所以做了个方案, 开始了批量升级</p>
<p>将logstash从1.3升级到2.1, 将elasticsearch从1.4.1升级到2.0</p>
<p>期间踩了很多坑, 搞了一个多月, 总算搞完</p>
<p>从纯手工落后隔三差五有人找查问题的自行车, 改成自动化最新版本新架构运维便捷上了两个月无人反馈的, 额, 小汽车:) - 集成安装包/shell脚本/fabric实现部署/升级/增删/加黑名单等等功能</p>
<p>每天日志量大概10G上下, 几十个采集端, 两个redis, 两个indexer, 两台es机器扛起</p>
<p>以下, 不那么严谨地, 记录一些遇到的问题</p>
<h4 id="1-logstash升级策略">1. logstash升级策略</h4>
<p>logstash1.3到2.x, 变化点还是很多的</p>
<p>所以, 首先第一步要去阅读官方文档, 将所有change log过一遍, 对一些关键性的东西进行了解, 比如, 干掉了哪些语法(旧的功能需要如何实现), 哪些语法有变更, 新增了哪些特性等.</p>
<p>然后, 将线上不同类型agent的配置文件拉下来, 先, 归类, 然后, 开始改-测-改-测-直到测试通过</p>
<pre tabindex="0"><code>bin/logstash agent -t -f test.conf
</code></pre><p>直到, 语法验证通过</p>
<p>现在要做的是, 验证数据正确性</p>
<p>从线上拉取对应日志, 启动, 查看输出</p>
<pre tabindex="0"><code>output {
    stdout{
        debug =&gt; true
    }
}
</code></pre><p>这里需要验证的是, 1. 过滤, 该过滤的过滤了 2. 转换, 该转换的转换了 3.新增, 新增字段</p>
<p>注意, 测试时, 使用逻辑分支覆盖到所有配置文件中的分支即可.</p>
<p>然后, 可以挑一台机器, 停老的服务, 部署新的服务进行测试</p>
<p>建议, 部署agent的时候, 如果读的是文件, 建议配置<code>sincedb_path</code> 这样假设下次升级, 就可以从老的服务最后读取的位置开始了</p>
<pre tabindex="0"><code>input {
    file {
        path =&gt; [&#34;/data/logs/*.log&#34;]
        sincedb_path =&gt; &#34;/data/LogNew/logstash/sincedb/celery.sincedb&#34;
    }
}
</code></pre><h4 id="2-elasticsearch升级的策略">2. elasticsearch升级的策略</h4>
<p>elasticsearch从1.4到2.0, 部署上变化不大, 变化最大的是存储doc的schema变了&hellip;&hellip;</p>
<p>使用原来的语法查询, 发现查不到, 因为字段名以及嵌套层级完全不一样了, 这里, 要修改查询端, 兼容新老版本的格式</p>
<pre tabindex="0"><code>{&#39;from&#39;: 0,
 &#39;query&#39;: {&#39;filtered&#39;: {&#39;filter&#39;: {&#39;bool&#39;: {&#39;must&#39;: [{&#39;bool&#39;: {&#39;should&#39;: [{&#39;term&#39;: {&#39;type&#39;: &#39;app&#39;}},
                                                                          {&#39;term&#39;: {&#39;@type&#39;: &#39;app&#39;}}]}},
                                                     {&#39;bool&#39;: {&#39;should&#39;: [{&#39;term&#39;: {&#39;log_level&#39;: u&#39;error&#39;}},
                                                                          {&#39;term&#39;: {&#39;@fields.log_level&#39;: u&#39;error&#39;}}]}},
                                                     {&#39;range&#39;: {&#39;@timestamp&#39;: {&#39;gt&#39;: &#39;now-5h&#39;}}},
                                                     {&#39;bool&#39;: {&#39;should&#39;: [{&#39;term&#39;: {&#39;log_type&#39;: u&#39;celery&#39;}},
                                                                          {&#39;term&#39;: {&#39;@fields.log_type&#39;: u&#39;celery&#39;}}]}}]}}}},
 &#39;size&#39;: 100,
 &#39;sort&#39;: [{&#39;@timestamp&#39;: &#39;desc&#39;}]}
</code></pre><p>另一个是, 取到数据进行解析的时候, 发现解析逻辑跪了, 没办法, 返回的json也完全变了, 这里, 要修改解析逻辑, 兼容新老版本格式</p>
<pre tabindex="0"><code>for hit in log_hits:
    try:
        source = hit.get(&#39;_source&#39;)
        if &#39;@fields&#39; in source:
            log = source.get(&#39;@fields&#39;, {})
        else:
            log = source
</code></pre><p>为了让用户感觉不到集群升级, 首先要做的就是上面两个变更</p>
<p>然后, 搭建新的集群, 最好找新的机器搭建(我在新的机器搭完才发现妈蛋硬盘才100G, 坑死, 无奈在老集群上搭新的集群, 硬盘1t)</p>
<p>ready, 所有节点起好维护好, 然后, 改indexer, 将同一份日志灌到两个集群</p>
<pre tabindex="0"><code>output {
    elasticsearch {
        hosts =&gt; [&#34;10.1.1.1:9100&#34;, &#34;10.1.1.2:9100&#34;]
    }
    elasticsearch {
        hosts =&gt; [&#34;10.1.1.1:9110&#34;, &#34;10.1.1.2:9110&#34;]
    }
}
</code></pre><p>简单测试下, 没问题就放着甭管了, 等数据攒齐了&hellip;.</p>
<p>数据够了, 就, 停indexer, 停老集群, 停新集群, 改新集群端口, 起来&hellip;.同时去掉indexer只输出到新的集群, 起来&hellip;&hellip;测试, 切换完毕, 收工吧.</p>
<h4 id="优化点-集成安装包和supervisord">优化点: 集成安装包和supervisord</h4>
<p>额, logstash和es, 如果要配置节点, 其实还是挺蛋疼的</p>
<p>要做的, 就是, logstash+不同类型配置文件+运维脚本, 达成一个包</p>
<p>然后, 如果要部署一台机器, 扔上去一键执行安装, 测试, 启动即可</p>
<p>例如, 运维脚本 <code>logstashd.sh</code></p>
<pre tabindex="0"><code>#!/bin/bash

BASEDIR=$(dirname $0)
cd $BASEDIR
CURRENT_DIR=`pwd`

function help_msg() {
    echo &#34;===================== usage =====================&#34;
    echo &#34;./logstashd.sh  - enter command line&#34;
    echo &#34;./logstashd.sh status - show all configured process&#34;
    echo &#34;./logstashd.sh start ${name} - start program&#34;
    echo &#34;./logstashd.sh stop ${name} - stop program&#34;
    echo &#34;./logstashd.sh restart ${name} - restart program&#34;
    echo &#34;./logstashd.sh reread &amp;&amp; ./logstashd.sh update - update config and just update the modified programs&#34;
    echo &#34;./logstashd.sh reload - reload config files and restart all programs(stopeed not included)&#34;
    echo &#34;=================================================&#34;
    echo &#34;&#34;
}

if [ &#34;${1}&#34; = &#34;-h&#34; -o &#34;${1}&#34; = &#34;--help&#34; ]
then
    help_msg
    exit 0
fi

SUPERVISORCTL=&#39;/data/LogNew/python27/bin/supervisorctl&#39;

CONFIG_FILE_PATH=&#34;${CURRENT_DIR}/conf/supervisord.conf&#34;

$SUPERVISORCTL -c $CONFIG_FILE_PATH $@
</code></pre><p>使用</p>
<pre tabindex="0"><code>./logstashd.sh
===================== usage =====================
./logstashd.sh  - enter command line
./logstashd.sh status - show all configured process
./logstashd.sh start  - start program
./logstashd.sh stop  - stop program
./logstashd.sh restart  - restart program
./logstashd.sh reread &amp;&amp; ./logstashd.sh update - update config and just update the modified programs
./logstashd.sh reload - reload config files and restart all programs(stopeed not included)
=================================================

111_indexer                      RUNNING   pid 27058, uptime 1:25:10
indexer                          RUNNING   pid 24731, uptime 1:31:29
supervisor&gt; restart indexer
</code></pre><p>这里, 我引入了<a href="http://www.stackless.com/binaries/">stackless python</a> (独立), 然后装pip/supervisord, 使用supervisord对logstash/es进程进行管理</p>
<p>使用supervisord管理进程, 有个注意点</p>
<p>默认supervisord相关的文件在</p>
<pre tabindex="0"><code>/tmp/supervisor*
</code></pre><p>而线上, 存在tmp被删/清理了情况, 导致要进行进程启停操作才发现,妈蛋找不到</p>
<p>处理方式 =&gt; 放到集成安装包的run目录下</p>
<h4 id="注意点-logstash存在两个output时-必须要保证二者的可用性">注意点: logstash存在两个output时, 必须要保证二者的可用性</h4>
<p>logstash indexer, 分别转发数据到两个不同的output</p>
<pre tabindex="0"><code>output {
    elasticsearch {
        hosts =&gt; [&#34;10.1.1.1:8080&#34;, &#34;10.1.1.2:8080&#34;]
    }
    redis {
        host =&gt; &#34;10.1.1.3&#34;
        port =&gt; 6379
        password =&gt; &#34;7oEsjqUNoTdgE4&#34;
        data_type =&gt; &#34;list&#34;
        key =&gt; &#34;log_queue&#34;
        db =&gt; 0
        batch =&gt; true
    }
}
</code></pre><p>此时, 若是redis挂了, 则日志也不会刷到es中, 所以, 需要同时保证所有output的可用性</p>
<p>对于redis, 可以进行进程监控, 发现挂了的话, 告警并同时重启(可以crontab一分钟检查一次)</p>
<h4 id="优化点-elk增加agent_ip字段">优化点: ELK增加agent_ip字段</h4>
<p>需求: 在实际使用中, 有时候需要反向根据查询结果, 获知日志的来源机器</p>
<p>处理:</p>
<p>1.先获取ip</p>
<pre tabindex="0"><code>GetLanIp () {
     ## get associated LAN ip address
     ## usage: GetLanIp
     /sbin/ifconfig | awk &#39;
         /eth/{
             getline;
             if (/inet addr:(172|10|192)\./) {
                 gsub(&#34;.*addr:|  *Bcast.*&#34;,&#34;&#34;);
                 print $0;
                 exit;
             }
         }&#39;
     return 0
}
</code></pre><p>2.放入环境变量</p>
<pre tabindex="0"><code>ETH1_IP=10.1.1.1
</code></pre><p>3.修改logstash配置</p>
<p>注意, 这里是logstash2.x的语法</p>
<pre tabindex="0"><code>    environment {
        add_metadata_from_env =&gt; [&#34;agent_ip&#34;, &#34;ETH1_IP&#34;]
        add_field =&gt; {&#34;agent_ip&#34; =&gt;  &#34;%{[@metadata][agent_ip]}&#34; }
    }
</code></pre><h4 id="问题-elk的utc问题">问题: elk的utc问题</h4>
<p>elasticsearch内部使用的是utc, 存储为long (milliseconds since the epoch)  e.g. timestamp=1420070400000</p>
<p>可以看下 <a href="https://github.com/chenryn/logstash-best-practice-cn/blob/master/filter/date.md">es 时间处理</a></p>
<p>logstash 接受了这种设定, 往es传数据的时候, 根据UTC, 每天00:00新建一个index</p>
<p>kibana也接受这种设定, 在查询和展示时根据用户的时区进行处理</p>
<p>问题描述</p>
<p>这导致了, 对于东八区, 2015-11-6日, 8点之前, 只有<code>logstash-2015.11.05</code>这个index, 到8点的时候, 创建新的index <code>logstash-2015.11.06</code>, 即, 对于我们这个时区的人来说, 一天的数据存在了两个index里面</p>
<p>同类问题 <a href="https://github.com/elastic/elasticsearch/issues/7375">Elasticsearch doesn&rsquo;t care about timezone and creates indexes with UTC</a></p>
<p>修正方案1: 修改logstash的数据时间</p>
<p>logstash团队对于支持localtime的问题, 不予修复 <a href="https://logstash.jira.com/browse/LOGSTASH-973">讨论</a>, 但是可以自行去修改logstash的代码</p>
<p>当然, 可以修改每个logstash indexer的时间, 但是会带来问题 <a href="https://github.com/chenryn/logstash-best-practice-cn/blob/master/filter/date.md#%E6%97%B6%E5%8C%BA%E9%97%AE%E9%A2%98%E7%9A%84%E8%A7%A3%E9%87%8A">问题</a>: 1. logstash都要修改<code>getLocalTime</code> 2.相对时间搜索 3. kibana等相关插件/组件要修正</p>
<p>运维/升级和后续使用上会有很多地雷</p>
<p>修正方案2: 不修正</p>
<p>接受这种设定, 学习kibana, 类似自行确定要搜索的index
对于<code>00:00-08:00</code>的, 程序处理使用昨天的<code>indexer</code></p>
<p>所以, 更好的方式是, 不修正&hellip;&hellip;原来不变的才是最好的</p>
<h4 id="rolling-restart">rolling restart</h4>
<p>当存在配置变更时, 需要重启es集群, 不可能全部重启的, 这样会导致服务不可用&hellip;.</p>
<p>所以, 要一个个重启</p>
<p>先执行</p>
<pre tabindex="0"><code>curl -XPUT &#39;http://localhost:9200/_cluster/settings&#39; -d &#39;
{
    &#34;transient&#34; : {
        &#34;cluster.routing.allocation.enable&#34; : &#34;none&#34;
    }
}&#39;
</code></pre><p>然后, shutdown, 改配置, start</p>
<p>then : 一定要记得执行, 否则不会执行recovery&hellip;..会一直等着</p>
<pre tabindex="0"><code>curl -XPUT &#39;http://localhost:9200/_cluster/settings&#39; -d &#39;
{
    &#34;transient&#34; : {
        &#34;cluster.routing.allocation.enable&#34; : &#34;all&#34;
    }
}&#39;
</code></pre><h4 id="logstash文本解析配置grok语法">logstash文本解析配置grok语法</h4>
<p>一个线上的工具, <a href="https://grokdebug.herokuapp.com/">https://grokdebug.herokuapp.com/</a></p>
<p>挺好用的, 但是有时候变更频繁相应有些缓慢</p>
<p>暂时没有找到命令行工具</p>
<h4 id="坑-grok语法">坑: GROK语法</h4>
<p>上线后发现, 尼玛, 部分应用日志没有被采集</p>
<p>定位发现, 原来在<code>grok</code>的解析中使用了<code>WORD</code></p>
<p>而 <code>WORD</code>: 不支持连字符和下划线</p>
<p>跪了, 需要自定义<code>LOGFILENAME [a-z\-A-Z0-9_\.]+</code>放到pattern中</p>
<p>然后, 搜索的时候, 尼玛, 也搜不到&hellip;.语法要做处理, 使用<code>raw</code>, es建索引的时候自动拆掉了导致搜索不到</p>
<pre tabindex="0"><code>{&#39;term&#39;: {&#39;app_name.raw&#39;: &#39;nms-t&#39;}}
</code></pre><h4 id="做一些exclude">做一些exclude</h4>
<p>有时候需要做一些exclude, 去除比必要采集和监控的日志(进入采集逻辑纯粹是浪费cpu和内存)</p>
<p>例如, 目录树, 不要监控celery.log</p>
<pre tabindex="0"><code>logs
├── a
│   ├── a.log
│   ├── b.log
│   └── celery.log
└── b
    ├── c.log
        └── d.log
</code></pre><p>排除部分文件</p>
<pre tabindex="0"><code>file {
    path =&gt; [&#34;/data/logs/*/*.log&#34;]
    exclude =&gt; [&#34;celery.log&#34;, ]
    sincedb_path =&gt; &#34;/data/LogNew/logstash/sincedb/django.sincedb&#34;
}
</code></pre><p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-file.html#plugins-inputs-file-exclude">文档</a></p>
<h4 id="一些相关用到的命令">一些相关用到的命令</h4>
<ul>
<li>查看plugin版本</li>
</ul>
<p><a href="https://www.elastic.co/guide/en/logstash/current/working-with-plugins.html">https://www.elastic.co/guide/en/logstash/current/working-with-plugins.html</a></p>
<pre tabindex="0"><code>bin/plugin list --verbose
</code></pre><ul>
<li>create empty index</li>
</ul>
<pre tabindex="0"><code>curl -XPUT &#39;http://localhost:9100/logstash-2015.12.15/&#39;
</code></pre><ul>
<li>查看健康度</li>
</ul>
<pre tabindex="0"><code>curl &#39;http://localhost:9100/_cluster/health?pretty=true&#39;
</code></pre><ul>
<li>查看indices</li>
</ul>
<pre tabindex="0"><code>#!/bin/bash

curl &#39;http://localhost:9100/_cat/indices?v&#39; | sort -k 3
</code></pre><h4 id="删除30天前crontab脚本">删除30天前crontab脚本</h4>
<pre tabindex="0"><code>#!/bin/bash

now=`date +%Y%m%d`
echo $now
days_30_before=`date -d &#34;$now 31 days ago&#34; +%Y.%m.%d`
echo $days_30_before
echo &#34;http://10.1.1.1:9100/logstash-$days_30_before&#34;
curl -XDELETE &#34;http://10.1.1.1:9100/logstash-$days_30_before&#34; &gt; /dev/null 2&gt;&amp;1
</code></pre><h4 id="尚未处理">尚未处理</h4>
<p>logstash2.1 muline codec, 配置多个数据来源, 存在串的情况, 生产中大数据量有, 小规模没有复现&hellip;.</p>
<hr>
<p>好了, 先这些, 还有一些窝在某些目录下, 后续整理好了发</p>
<p>wklken</p>
<p>2016-02-16</p>
]]></content>
		</item>
		
		<item>
			<title>也许是一个新的开始</title>
			<link>https://wklken.me/posts/2016/02/16/maybe-a-new-start.html</link>
			<pubDate>Tue, 16 Feb 2016 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/02/16/maybe-a-new-start.html</guid>
			<description>也许是一个新的开始吧. 原先写blog都是深思熟虑, 偶尔发发微博, 以及几乎不发朋友圈&amp;hellip;.. 年后, 发现, 其实blog就是个个人吐吐</description>
			<content type="html"><![CDATA[<p>也许是一个新的开始吧.</p>
<p>原先写blog都是深思熟虑, 偶尔发发微博, 以及几乎不发朋友圈&hellip;..</p>
<p>年后, 发现, 其实blog就是个个人吐吐槽, 以及一些积累的地方, 深思熟虑个蛋(好多笔记, 半成品等等蹲在wiki里不见天日, 也不见得有时间完善好)</p>
<p>所以, 打算开启话唠模式. 至于那些不是很&quot;深思熟虑&quot;的东西, 通过长时间不断迭代来优化吧, 然后会有一些吐槽什么的.</p>
<p>不大确定有多少人订阅了, 可以取关哈:)</p>
<p>从此, 不想太多, 看看今年的产出吧. 当然, 尽量保质保量.</p>
<p>从此文开始(够短吧)</p>
<p>2016-02-16 于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>一些vim的个性化配置</title>
			<link>https://wklken.me/posts/2016/02/03/some-vim-configs.html</link>
			<pubDate>Wed, 03 Feb 2016 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2016/02/03/some-vim-configs.html</guid>
			<description>在咖啡馆kill time, 时间不多但实在无聊, 随便写写 入坑vim, 四年有余了 自从2012年将自己的配置发布到github后, 持续性的维护着, 逐步解决</description>
			<content type="html"><![CDATA[<p>在咖啡馆kill time, 时间不多但实在无聊, 随便写写</p>
<hr>
<p>入坑vim, 四年有余了</p>
<p>自从2012年将自己的配置发布到github后, 持续性的维护着, 逐步解决一些使用中的痛点, 反直觉的东西, 慢慢形成了现有的配置.</p>
<p><a href="https://github.com/wklken/k-vim">k-vim</a></p>
<p>以下, 就列下, 在k-vim中, 做了哪些配置, 下面这些在 <a href="https://github.com/wklken/k-vim/blob/master/vimrc">vimrc</a> 中都可以找到</p>
<p>首先, 一上来就把<code>leader</code>键改掉</p>
<pre tabindex="0"><code>&#34; 修改leader键
let mapleader = &#39;,&#39;
let g:mapleader = &#39;,&#39;
</code></pre><p>移动时, 保留到底部的空间</p>
<pre tabindex="0"><code>&#34; 在上下移动光标时，光标的上方或下方至少会保留显示的行数
set scrolloff=7
</code></pre><p>自定义代码折叠toggle</p>
<pre tabindex="0"><code>&#34; 代码折叠自定义快捷键 &lt;leader&gt;zz
let g:FoldMethod = 0
map &lt;leader&gt;zz :call ToggleFold()&lt;cr&gt;
fun! ToggleFold()
    if g:FoldMethod == 0
        exe &#34;normal! zM&#34;
        let g:FoldMethod = 1
    else
        exe &#34;normal! zR&#34;
        let g:FoldMethod = 0
    endif
endfun
</code></pre><p><code>ctrl-n</code>进行相对行号/绝对行号切换</p>
<pre tabindex="0"><code>&#34; 相对行号: 行号变成相对，可以用 nj/nk 进行跳转
set relativenumber number
au FocusLost * :set norelativenumber number
au FocusGained * :set relativenumber
&#34; 插入模式下用绝对行号, 普通模式下用相对
autocmd InsertEnter * :set norelativenumber number
autocmd InsertLeave * :set relativenumber
function! NumberToggle()
  if(&amp;relativenumber == 1)
    set norelativenumber number
  else
    set relativenumber
  endif
endfunc
nnoremap &lt;C-n&gt; :call NumberToggle()&lt;cr&gt;
</code></pre><p>quickfix的使用, 回车跳转到报错点, <code>s</code>或<code>v</code>分屏打开</p>
<pre tabindex="0"><code>&#34; In the quickfix window, &lt;CR&gt; is used to jump to the error under the
&#34; cursor, so undefine the mapping there.
autocmd BufReadPost quickfix nnoremap &lt;buffer&gt; &lt;CR&gt; &lt;CR&gt;
&#34; quickfix window  s/v to open in split window,  ,gd/,jd =&gt; quickfix window =&gt; open it
autocmd BufReadPost quickfix nnoremap &lt;buffer&gt; v &lt;C-w&gt;&lt;Enter&gt;&lt;C-w&gt;L
autocmd BufReadPost quickfix nnoremap &lt;buffer&gt; s &lt;C-w&gt;&lt;Enter&gt;&lt;C-w&gt;K
</code></pre><p>打开vim, 自动定位到上次最后变更位置</p>
<pre tabindex="0"><code>&#34; 打开自动定位到最后编辑的位置, 需要确认 .viminfo 当前用户可写
if has(&#34;autocmd&#34;)
  au BufReadPost * if line(&#34;&#39;\&#34;&#34;) &gt; 1 &amp;&amp; line(&#34;&#39;\&#34;&#34;) &lt;= line(&#34;$&#34;) | exe &#34;normal! g&#39;\&#34;&#34; | endif
endif
</code></pre><p>干掉方向键, 强迫自己使用<code>hjkl</code></p>
<pre tabindex="0"><code>&#34; 关闭方向键, 强迫自己用 hjkl
map &lt;Left&gt; &lt;Nop&gt;
map &lt;Right&gt; &lt;Nop&gt;
map &lt;Up&gt; &lt;Nop&gt;
map &lt;Down&gt; &lt;Nop&gt;
</code></pre><p><code>swap</code>换行展示时, 使用<code>hjkl</code>体验更好</p>
<pre tabindex="0"><code>&#34;Treat long lines as break lines (useful when moving around in them)
&#34;se swap之后，同物理行上线直接跳
nnoremap k gk
nnoremap gk k
nnoremap j gj
nnoremap gj j
</code></pre><p>F键区的映射</p>
<pre tabindex="0"><code>&#34; F2 行号开关，用于鼠标复制代码用
&#34; 为方便复制，用&lt;F2&gt;开启/关闭行号显示:
function! HideNumber()
  if(&amp;relativenumber == &amp;number)
    set relativenumber! number!
  elseif(&amp;number)
    set number!
  else
    set relativenumber!
  endif
  set number?
endfunc
nnoremap &lt;F2&gt; :call HideNumber()&lt;CR&gt;

&#34; F3 显示可打印字符开关
nnoremap &lt;F3&gt; :set list! list?&lt;CR&gt;
&#34; F4 换行开关
nnoremap &lt;F4&gt; :set wrap! wrap?&lt;CR&gt;

set pastetoggle=&lt;F5&gt;            &#34;    when in insert mode, press &lt;F5&gt; to go to
                                &#34;    paste mode, where you can paste mass data
                                &#34;    that won&#39;t be autoindented

&#34; disbale paste mode when leaving insert mode
au InsertLeave * set nopaste

&#34; F6 语法开关，关闭语法可以加快大文件的展示
nnoremap &lt;F6&gt; :exec exists(&#39;syntax_on&#39;) ? &#39;syn off&#39; : &#39;syn on&#39;&lt;CR&gt;
</code></pre><p>分屏窗口切换时, 直接使用<code>ctrl-h/j/k/l</code></p>
<pre tabindex="0"><code>&#34; 分屏窗口移动, Smart way to move between windows
map &lt;C-j&gt; &lt;C-W&gt;j
map &lt;C-k&gt; &lt;C-W&gt;k
map &lt;C-h&gt; &lt;C-W&gt;h
map &lt;C-l&gt; &lt;C-W&gt;l
</code></pre><p>多窗口编辑时, 临时放大某个窗口, 编辑完再切回原来的布局</p>
<pre tabindex="0"><code>&#34; http://stackoverflow.com/questions/13194428/is-better-way-to-zoom-windows-in-vim-than-zoomwin
&#34; Zoom / Restore window.
function! s:ZoomToggle() abort
    if exists(&#39;t:zoomed&#39;) &amp;&amp; t:zoomed
        execute t:zoom_winrestcmd
        let t:zoomed = 0
    else
        let t:zoom_winrestcmd = winrestcmd()
        resize
        vertical resize
        let t:zoomed = 1
    endif
endfunction
command! ZoomToggle call s:ZoomToggle()
nnoremap &lt;silent&gt; &lt;Leader&gt;z :ZoomToggle&lt;CR&gt;
</code></pre><p>分号映射为冒号, 省得要进入命令模式需要按<code>shift</code></p>
<pre tabindex="0"><code>&#34; Map ; to : and save a million keystrokes 用于快速进入命令行
nnoremap ; :
</code></pre><p><code>H</code>和<code>L</code>跳转到行首行末, 实在不想按<code>0</code>和<code>$</code>, 太远</p>
<pre tabindex="0"><code>&#34; Go to home and end using capitalized directions
noremap H ^
noremap L $
</code></pre><p>命令行模式快捷键, <code>ctrl-a/e</code>跳转到行首行尾</p>
<pre tabindex="0"><code>&#34; 命令行模式增强，ctrl - a到行首， -e 到行尾
cnoremap &lt;C-j&gt; &lt;t_kd&gt;
cnoremap &lt;C-k&gt; &lt;t_ku&gt;
cnoremap &lt;C-a&gt; &lt;Home&gt;
cnoremap &lt;C-e&gt; &lt;End&gt;
</code></pre><p>空格进入搜索</p>
<pre tabindex="0"><code>&#34; 搜索相关
&#34; Map &lt;Space&gt; to / (search) and Ctrl-&lt;Space&gt; to ? (backwards search)
map &lt;space&gt; /
&#34; 进入搜索Use sane regexes&#34;
nnoremap / /\v
vnoremap / /\v
</code></pre><p>搜索时, 进入下一个上一个始终放在屏幕中间</p>
<pre tabindex="0"><code>&#34; Keep search pattern at the center of the screen.
nnoremap &lt;silent&gt; n nzz
nnoremap &lt;silent&gt; N Nzz
nnoremap &lt;silent&gt; * *zz
nnoremap &lt;silent&gt; # #zz
nnoremap &lt;silent&gt; g* g*zz
</code></pre><p><code>leader-/</code> 关闭掉上次搜索的高亮</p>
<pre tabindex="0"><code>&#34; 去掉搜索高亮
noremap &lt;silent&gt;&lt;leader&gt;/ :nohls&lt;CR&gt;
</code></pre><p>交换<code>#</code>和<code>*</code>, <code>#</code>更近</p>
<pre tabindex="0"><code>&#34; switch # *
nnoremap # *
nnoremap * #
</code></pre><p>Python 写入注释<code>#</code>号时, 不每次都跳到行首</p>
<pre tabindex="0"><code>&#34; for # indent, python文件中输入新行时#号注释不切回行首
autocmd BufNewFile,BufRead *.py inoremap # X&lt;c-h&gt;#
</code></pre><p>buffer切换相关, 使用不多</p>
<pre tabindex="0"><code>&#34; 切换前后buffer
nnoremap [b :bprevious&lt;cr&gt;
nnoremap ]b :bnext&lt;cr&gt;
&#34; 使用方向键切换buffer
noremap &lt;left&gt; :bp&lt;CR&gt;
noremap &lt;right&gt; :bn&lt;CR&gt;
</code></pre><p>tab操作&hellip;.看个人喜好, 不过自从用了crtlspace之后, 逐渐很少用了</p>
<pre tabindex="0"><code>&#34; tab 操作
&#34; http://vim.wikia.com/wiki/Alternative_tab_navigation
&#34; http://stackoverflow.com/questions/2005214/switching-to-a-particular-tab-in-vim

&#34; tab切换
map &lt;leader&gt;th :tabfirst&lt;cr&gt;
map &lt;leader&gt;tl :tablast&lt;cr&gt;

map &lt;leader&gt;tj :tabnext&lt;cr&gt;
map &lt;leader&gt;tk :tabprev&lt;cr&gt;
map &lt;leader&gt;tn :tabnext&lt;cr&gt;
map &lt;leader&gt;tp :tabprev&lt;cr&gt;

map &lt;leader&gt;te :tabedit&lt;cr&gt;
map &lt;leader&gt;td :tabclose&lt;cr&gt;
map &lt;leader&gt;tm :tabm&lt;cr&gt;

&#34; normal模式下切换到确切的tab
noremap &lt;leader&gt;1 1gt
noremap &lt;leader&gt;2 2gt
noremap &lt;leader&gt;3 3gt
noremap &lt;leader&gt;4 4gt
noremap &lt;leader&gt;5 5gt
noremap &lt;leader&gt;6 6gt
noremap &lt;leader&gt;7 7gt
noremap &lt;leader&gt;8 8gt
noremap &lt;leader&gt;9 9gt
noremap &lt;leader&gt;0 :tablast&lt;cr&gt;

&#34; Toggles between the active and last active tab &#34;
&#34; The first tab is always 1 &#34;
let g:last_active_tab = 1
&#34; nnoremap &lt;leader&gt;gt :execute &#39;tabnext &#39; . g:last_active_tab&lt;cr&gt;
&#34; nnoremap &lt;silent&gt; &lt;c-o&gt; :execute &#39;tabnext &#39; . g:last_active_tab&lt;cr&gt;
&#34; vnoremap &lt;silent&gt; &lt;c-o&gt; :execute &#39;tabnext &#39; . g:last_active_tab&lt;cr&gt;
nnoremap &lt;silent&gt; &lt;leader&gt;tt :execute &#39;tabnext &#39; . g:last_active_tab&lt;cr&gt;
autocmd TabLeave * let g:last_active_tab = tabpagenr()

&#34; 新建tab  Ctrl+t
nnoremap &lt;C-t&gt;     :tabnew&lt;CR&gt;
inoremap &lt;C-t&gt;     &lt;Esc&gt;:tabnew&lt;CR&gt;
</code></pre><p>选中后, 调整缩进, 可能需要多次调整, 默认调整一次后退出了选中, 需要再次选.</p>
<p>这个变更, 保证调整缩进后, 还是选中状态</p>
<pre tabindex="0"><code>&#34; 调整缩进后自动选中，方便再次操作
vnoremap &lt; &lt;gv
vnoremap &gt; &gt;gv
</code></pre><p>复制动作的变更, <code>Y</code>, 复制到行末</p>
<pre tabindex="0"><code>&#34; y$ -&gt; Y Make Y behave like other capitals
map Y y$

&#34; 复制选中区到系统剪切板中
vnoremap &lt;leader&gt;y &#34;+y
</code></pre><p>选中全部/选中段落</p>
<pre tabindex="0"><code>&#34; select all
map &lt;Leader&gt;sa ggVG&#34;

&#34; select block
nnoremap &lt;leader&gt;v V`}
</code></pre><p>保存, 没权限的时候</p>
<pre tabindex="0"><code>&#34; w!! to sudo &amp; write a file
cmap w!! w !sudo tee &gt;/dev/null %
</code></pre><p>使用<code>kj</code>, 替换<code>ESC</code></p>
<pre tabindex="0"><code>&#34; kj 替换 Esc
inoremap kj &lt;Esc&gt;
</code></pre><p>调整<code>Ctrl-e/y</code>滚动</p>
<pre tabindex="0"><code>&#34; 滚动Speed up scrolling of the viewport slightly
nnoremap &lt;C-e&gt; 2&lt;C-e&gt;
nnoremap &lt;C-y&gt; 2&lt;C-y&gt;
</code></pre><p>快捷保存和退出<code>leader-q/w</code></p>
<pre tabindex="0"><code>&#34; Quickly close the current window
nnoremap &lt;leader&gt;q :q&lt;CR&gt;

&#34; Quickly save the current file
nnoremap &lt;leader&gt;w :w&lt;CR&gt;
</code></pre><p>交换</p>
<pre tabindex="0"><code>&#34; 交换 &#39; `, 使得可以快速使用&#39;跳到marked位置
nnoremap &#39; `
nnoremap ` &#39;
</code></pre><p>变更<code>U</code>, 撤销重做的时候更快</p>
<pre tabindex="0"><code>&#34; remap U to &lt;C-r&gt; for easier redo
nnoremap U &lt;C-r&gt;
</code></pre><p>保存文件时, 自动移除多余空格</p>
<pre tabindex="0"><code>&#34; 保存python文件时删除多余空格
fun! &lt;SID&gt;StripTrailingWhitespaces()
    let l = line(&#34;.&#34;)
    let c = col(&#34;.&#34;)
    %s/\s\+$//e
    call cursor(l, c)
endfun
autocmd FileType c,cpp,java,go,php,javascript,puppet,python,rust,twig,xml,yml,perl autocmd BufWritePre &lt;buffer&gt; :call &lt;SID&gt;StripTrailingWhitespaces()
</code></pre><p>shell和python文件新建时, 自动插入行头</p>
<pre tabindex="0"><code>&#34; 定义函数AutoSetFileHead，自动插入文件头
autocmd BufNewFile *.sh,*.py exec &#34;:call AutoSetFileHead()&#34;
function! AutoSetFileHead()
    &#34;如果文件类型为.sh文件
    if &amp;filetype == &#39;sh&#39;
        call setline(1, &#34;\#!/bin/bash&#34;)
    endif

    &#34;如果文件类型为python
    if &amp;filetype == &#39;python&#39;
        call setline(1, &#34;\#!/usr/bin/env python&#34;)
        call append(1, &#34;\# encoding: utf-8&#34;)
    endif

    normal G
    normal o
    normal o
endfunc
</code></pre><p>自定义一些高亮的关键字</p>
<pre tabindex="0"><code>&#34; 设置可以高亮的关键字
if has(&#34;autocmd&#34;)
  &#34; Highlight TODO, FIXME, NOTE, etc.
  if v:version &gt; 701
    autocmd Syntax * call matchadd(&#39;Todo&#39;,  &#39;\W\zs\(TODO\|FIXME\|CHANGED\|DONE\|XXX\|BUG\|HACK\)&#39;)
    autocmd Syntax * call matchadd(&#39;Debug&#39;, &#39;\W\zs\(NOTE\|INFO\|IDEA\|NOTICE\)&#39;)
  endif
endif
</code></pre><p>其他一些设置</p>
<pre tabindex="0"><code>&#34; 启动的时候不显示那个援助索马里儿童的提示
set shortmess=atI

&#34; 设置 退出vim后，内容显示在终端屏幕, 可以用于查看和复制, 不需要可以去掉
&#34; 好处：误删什么的，如果以前屏幕打开，可以找回
set t_ti= t_te=

&#34; 鼠标暂不启用, 键盘党....
set mouse-=a

&#34; 回车即选中当前项, 慎用
inoremap &lt;expr&gt; &lt;CR&gt;       pumvisible() ? &#34;\&lt;C-y&gt;&#34; : &#34;\&lt;CR&gt;&#34;
</code></pre>]]></content>
		</item>
		
		<item>
			<title>读书笔记-调试九法</title>
			<link>https://wklken.me/posts/2015/11/29/debugging-9-rules.html</link>
			<pubDate>Sun, 29 Nov 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/11/29/debugging-9-rules.html</guid>
			<description>去年十一月份, 写过一篇blog, 讲了一些自己平时进行代码调试的观点, 列了21条 关于代码调试de那些事 上周多看上买了这本书, 花了两小时读完, 做</description>
			<content type="html"><![CDATA[<p>去年十一月份, 写过一篇blog, 讲了一些自己平时进行代码调试的观点, 列了21条</p>
<p><a href="http://www.wklken.me/posts/2014/11/23/how-to-debug.html">关于代码调试de那些事</a></p>
<p>上周多看上买了这本书, 花了两小时读完, 做下笔记, 发现很多观点其实是类似的.</p>
<p>这本书是九条原则, 即方法论, 了解和学习, 在实践中遵守, 有利于提升自己调试的效率:)</p>
<p><img src="/imgs/books/debug-rules.jpg" alt="debug-rules"></p>
<h4 id="1-理解系统">1. 理解系统</h4>
<blockquote>
<p>你必须掌握系统的工作原理以及它是如何设计的。在某些情况下，还要知道为什么这样设计。如果你没有理解系统中的某个部分，那么这通常就是出问题的地方。（这不仅仅是“墨菲定律”的问题，如果你不能理解你所设计的系统，你的工作可能会变得一团糟。）</p>
</blockquote>
<p>理解是怎么设计的, 机制, 原理, 需求等等, 面对的东西才是一个白盒.</p>
<blockquote>
<p>理解系统的基本方法就是阅读手册</p>
</blockquote>
<p>手册, 手册, 很多时候, 我们并不能抑制住自己, 去编码, 去调试, 去解决问题, 而忽略了一些重要的东西, 例如: 文档, 我们总以为自己了解一切, 很多时候很诡异的问题查查文档, 才发现, 原来文档有说明:)</p>
<p>或者, 跳过去看源码也是不错的选择</p>
<blockquote>
<p>理解了你自己的系统后，还会获得一个额外的好处。当你找到bug时，必须在不破坏其他地方的前提下修复它们。理解系统行为是不破坏系统的第一步。</p>
</blockquote>
<p>有个段子, 修了一个, 结果改出了3个bug&hellip;只有对系统足够了解, 才能修复而不破坏</p>
<blockquote>
<p>人们在调试的时候，通常都不会彻底地阅读系统手册。他们采取跳读的方式，查看他们认为重要的一些章节，但问题的线索可能就隐藏在被略过的那些章节中</p>
</blockquote>
<p>有时是这样的, 但是可能时间有限, 这个没法子, 只能在闲时, 多多阅读, 例如, 隔段时间回去浏览一遍</p>
<blockquote>
<p>知道什么是正常的</p>
</blockquote>
<p>什么是正常的, 什么是异常的?</p>
<blockquote>
<p>知道工作流程, 当你尝试寻找bug时，必须知道要查找的路线</p>
</blockquote>
<p>不多说, 捞数据, 追数据, 基本功</p>
<blockquote>
<p>了解你的工具</p>
</blockquote>
<p>要修车, 有趁手的工具才能更高效地解决问题</p>
<h4 id="2-制造失败">2. 制造失败</h4>
<blockquote>
<p>关键是在发生失败的时候要看到它</p>
</blockquote>
<p>日志, 现场, 栈等</p>
<blockquote>
<p>“当你发现一个故障时该怎么办？” “试着让它再次发生。”</p>
</blockquote>
<p>复现</p>
<blockquote>
<p>仔细观察你做了什么，然后再做一次，并且记下你做的每个步骤。然后，按照你自己所写的步骤去做，确定这样做确实导致了错误。</p>
</blockquote>
<p>复现的步骤</p>
<blockquote>
<p>要引发失败, 而不是模拟失败&hellip;&hellip;如果你猜测失败机理，模拟往往不会成功。原因通常有两个，要么你的猜测是错误的；要么测试改变了条件，模拟的系统可以正确工作，或者更糟，发生新的错误，因而分散了你对正在查找的问题的注意力&hellip;&hellip;注意，不要用一个看似完全相同（而实际上不同）的环境来代替并希望看到相同的错误</p>
</blockquote>
<p>关注问题本身, 不要错误的转移到了猜测的东西</p>
<blockquote>
<p>仔细观察失败</p>
</blockquote>
<p>仔细观察失败! 仔细观察! 仔细&hellip;.</p>
<blockquote>
<p>是已修复bug，还是仅仅由于运气好，它不再发生了</p>
</blockquote>
<p>你必须确认这一点</p>
<blockquote>
<p>永远不要丢掉调试工具</p>
</blockquote>
<p>调试工具, 不要当成一次性的工具用完即仍, 可能是错误的</p>
<h4 id="3-不要想-而要看">3. 不要想, 而要看</h4>
<blockquote>
<p>亲眼看到底层的失败是非常重要的。如果你猜测失败是如何发生的，那常常会修复一些根本不是bug的问题。这样的修复不仅不会解决问题，而且还会浪费时间和金钱，甚至会破坏其他地方。请记住，不要这样做。</p>
</blockquote>
<p>不要猜测</p>
<blockquote>
<p>观察是很难的</p>
</blockquote>
<p>的确很难</p>
<blockquote>
<p>你必须仔细观察，找到足够多的问题细节，才能调试它&hellip;&hellip;如果你不能留意实际情况发生的全过程，那么你极有可能曲解很多问题。你猜测某个地方出了问题，于是修复它，但实际上错误发生在另一个地方&hellip;&hellip;一定要亲眼看到实际错误是如何发生的。观察往往比猜测能够更快地找到问题。因为猜测虽然看起来是捷径，但这条捷径并不会带你找到问题的根源。</p>
</blockquote>
<p>观察而不是猜测</p>
<blockquote>
<p>在停下来思考问题之前，对细节的观察应该到什么程度才合适呢？简单的答案是：“一直观察，直到把问题的原因锁定在几种可能性之内。”</p>
</blockquote>
<blockquote>
<p>海森堡测不准原理: 换言之，测试工具影响了被测系统&hellip;&hellip;。任何插装都可能对系统造成影响，只是程度不同而已</p>
</blockquote>
<p>插装, 类似于工具的调试模式, 会影响系统, 所以要注意</p>
<h4 id="4-分而治之">4. 分而治之</h4>
<blockquote>
<p>缩小搜索范围&hellip;&hellip;在查找问题时，“分而治之”实际上是第一条需要使用的原则。事实上，在查找问题时它也是唯一需要应用的规则。所有其他规则都只是帮助你遵循这条规则。分而治之是调试的核心，很多人都知道它，但很多人都没有遵守它，这也正是我写本章的原因。</p>
</blockquote>
<p>二分法</p>
<blockquote>
<p>确定范围&hellip;你必须知道搜索范围，而且必须知道在一端一切正常，而在另一端出现了问题&hellip;..从有问题的支路开始查找问题&hellip;&hellip;</p>
</blockquote>
<p>原则</p>
<blockquote>
<p>如果同时出现了多个问题，当你确实查明了其中的一个问题时，应该立即修复它，然后再查找其他问题&hellip;&hellip;有时修复了一个问题，另一个问题也解决了，两个问题实际上是同一个bug</p>
</blockquote>
<p>是有这种情况</p>
<h4 id="5-一次只改一个地方">5. 一次只改一个地方</h4>
<blockquote>
<p>使用步枪，而不要用散弹枪&hellip;&hellip;一次只改一个地方&hellip;&hellip;此外，如果你真的看到了错误，应该只修复这个地方</p>
</blockquote>
<p>这点很重要</p>
<blockquote>
<p>如果你在两个测试之间更改了很多代码，或者为两个测试设置了不同的环境，那么这两个测试将很难对比。它们之间有很多差别并不是由bug引起的，而你必须不断地解释这些差别。你必须把它们之间的差别减少到只与bug有关。排除其他的干扰因素</p>
</blockquote>
<blockquote>
<p>自从上一次能够正常工作以来你更改了什么&hellip;&hellip;有时，正常的系统和错误的系统之间的区别是由于一项更改造成的。做了更改之后，正常的系统开始出现故障。一种非常有效的办法是找出第一个导致系统出错的版本，尽管这可能需要连续测试原来的版本，直到找到没有故障的版本</p>
</blockquote>
<p>大部分问题都是出现在最近一次修改</p>
<h4 id="6-保持审计跟踪">6. 保持审计跟踪</h4>
<blockquote>
<p>有时看起来最不起眼的事情实际上却是导致发生bug的关键&hellip;&hellip;因此，你必须记录下每一件事情，不起眼的事情可能会很重要</p>
</blockquote>
<p>细节, 步骤</p>
<blockquote>
<p>记下你的每步操作、顺序和结果</p>
</blockquote>
<p>还是步骤</p>
<blockquote>
<p>魔鬼隐藏在细节中</p>
</blockquote>
<p>细节</p>
<blockquote>
<p>在细节方面，永远都不要相信你的记忆，而要把它写下来。如果你相信你的记忆，将会制造很多麻烦。你会忘掉一些你认为不重要的细节，当然，这些细节将会被证明是非常重要的。你会忘掉一些在你看来不重要的细节，而这些细节对于后来解决另一个不同问题的人可能很重要。除了口头表述以外，你无法将信息传递给别人，而这会浪费所有人的时间。</p>
</blockquote>
<h4 id="7-检查插头">7. 检查插头</h4>
<blockquote>
<p>怀疑自己的假设&hellip;&hellip;当我们看到一个问题时，通常在某个特定位置看到了问题，但导致这个问题的原因却在上游或者是一个基础性的问题。系统不具备正确操作的条件，于是出现了非常奇怪的行为。当你看到完全来自另一个世界的问题时，应该停下来，看看你是不是还在地球上</p>
</blockquote>
<blockquote>
<p>从头开始检查</p>
</blockquote>
<h4 id="8-获得全新观点">8. 获得全新观点</h4>
<blockquote>
<p>寻求帮助: 向别人寻求帮助至少有3个原因（还不算把整个问题甩给别人）：获得全新观点、专业知识和经验。而且，人们通常很愿意帮忙，因为这给了他们一个证明自己很聪明的机会</p>
</blockquote>
<blockquote>
<p>事实上，有时向别人解释问题也会使你有全新的认识，之后你自己就解决了问题</p>
</blockquote>
<p>小黄鸭调试法</p>
<blockquote>
<p>报告症状, 而不是理论: 让他提出自己的观点。他们的观点可能与你的观点相符，也可能全然不同，而这正是你想要的</p>
</blockquote>
<h4 id="9如果你不修复bug-它将依然存在">9.如果你不修复bug, 它将依然存在</h4>
<blockquote>
<p>检查问题确实已被修复</p>
</blockquote>
<blockquote>
<p>bug 从来不会自己消失</p>
</blockquote>
]]></content>
		</item>
		
		<item>
			<title>这段时间的一些想法</title>
			<link>https://wklken.me/posts/2015/11/08/summary-13-some-points.html</link>
			<pubDate>Sun, 08 Nov 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/11/08/summary-13-some-points.html</guid>
			<description>发现自己有一段时间没更新 blog 了, 写一写吧, 至于技术方面的东西, 后面慢慢发吧:) 这一年经历了很多事情, 休息了一百天, 然后找工作, 入职. 入职期间还</description>
			<content type="html"><![CDATA[<p>发现自己有一段时间没更新 blog 了, 写一写吧, 至于技术方面的东西, 后面慢慢发吧:)</p>
<p>这一年经历了很多事情, 休息了一百天, 然后找工作, 入职.</p>
<p>入职期间还有些波折, offer那边说发了我这边却一直没收到, 还有确认入职事宜后没有发入职通知, 不过波波折折, 也算入职了, 三周, 到了深圳的加班圣地, 开启新的副本.</p>
<p>工作四年多了, 这是第四家公司了, 这四年发生了很多事情, 阿里上市了, 快播消失了, 甜品也歇业了, 刚到企鹅,  新生, 归零心态, 逐渐适应中, 算是一个新的开始, 重新审视自己, 审视工作, 审视学习, 审视生活,  以刚入行时的状态, 重新开始这趟征程.</p>
<p>总之, 这许多年, 是一些经历, 做了很多事情, 结识了很多人. 逐步形成了自己现在的样子.</p>
<hr>
<p>尽量写得不像鸡汤, 很多东西还是要自己慢慢去感受体悟, 诸君共勉.</p>
<p><img src="/imgs/life/simple.png" alt="simple"></p>
<h3 id="我只是一个普通人">我只是一个普通人</h3>
<p>刚毕业那会, 初生牛犊, 总想着去做一些牛逼的事情, 学一些给力的东西, 变得强大, 变成自己想象中的<code>牛人</code></p>
<p>而, 后来这些年, 逐渐对自己, 对生活, 以及对这整个世界, 有了新的认识.</p>
<p>当然, 也碰到了很多人, 碰到了很多事. 期间感受过智商被碾压,  感受过业务成就技术的喜悦, 也感受过业务夭折的无奈.</p>
<p>最终, 有了自己的定位.</p>
<p>我只是个普通人, 智商不高不低, 资质一般, 没有所谓的天赋异禀.</p>
<p>当然, 有天才的存在, 而对于普通人来说, 只能, 靠着自己的努力一点点慢慢地成长, 变得强大.</p>
<p>勤能补拙? 答案是否定的, 普通人的天赋和精力有限, 想明白自己感兴趣的, 想做的是什么, 然后投入进去, 坚持下来, 也就是了. 把有限精力浪费在无限的地方上是非常愚蠢的.</p>
<p>普通人做普通事, 很多时候, 选择更重要, 运气也很重要:) 所以, 思考, 投入, 坚持, 每天朝着目标前进, 这就够了, 你会发现, 你比以前的自己更加强大.</p>
<h3 id="战线太长有好有坏-但大多数时候-需要关注跟自己工作相关的东西">战线太长有好有坏, 但大多数时候, 需要关注跟自己工作相关的东西</h3>
<p>工作四年多, 中间有两年, 走了弯路吧感觉.</p>
<p>耗费了大量的时间, 每天晚上及周末, 去接触, 所谓的<code>学习</code>各种技术, 而绝大多数, 在自己工作中并没有用到.</p>
<p>战线太长, 好处就是, 可能视野广了许多, 对很多事情有了新的看法, 也逐渐能归纳得到一些共性.</p>
<p>缺点嘛, 很明显, 太耗费时间, 而且中间绝大多数成了沉没成本. 学了忘忘了学, 重复太多.</p>
<p>其实最佳的策略应该是, 从日常工作入手, 工作中接触的各种事情, 遇到的各种问题, 涉及的各种技术栈, 以点带面, 深挖和发散, 这就会占用掉大部分时间和经历了, 而事实也证明, 目前自己受益最大的,  反而是这些东西.</p>
<p>至于其他, 自己感兴趣, 去学, 去用.</p>
<p>不要为了学而学, 学以致用.</p>
<h3 id="收集资料没什么用-善用搜索-另外-记得踩过的坑-以及-深挖和扩展">收集资料没什么用, 善用搜索, 另外, 记得踩过的坑, 以及, 深挖和扩展</h3>
<p>刚毕业那会, 硬盘上收集了很多java的资料. 后来, 右键删了&gt;_&lt;</p>
<p>刷了几年微博, 收藏了很多东西, 估计有几千条, 然而, 再也没去看过.</p>
<p>买了很多书, 最终却发现, 只看了一半多点.</p>
<p>现在网络那么发达, 资源过载的年代, 其实资源永远触手可及, 并不需要花时间去下载/分类/储存.</p>
<p>而很多人, 往往会将<code>整理</code>作为主业, 而不是学习和吸收. 花了大量时间<code>整理</code>得到一套看似庞大的东西.</p>
<p>其实我在想, 看大家很多<code>mark</code>, 不知道有多少人会再去看过.</p>
<p>知识管理, 分级, 自己积累沉淀得到的东西, 一级, 工作涉及的东西, 二级, 相关领域自己感兴趣的东西, 三级, 杂七杂八八卦等等, 四级.</p>
<p>完全可以用wiki/evernote/pocket等工具, 自行分级</p>
<p>重要的是, 搜索, 用到的时候, 可以在最短的时间内汇集, 掌握和使用.</p>
<p>分级+搜索</p>
<h3 id="很遗憾-看很多技术书或许没什么用">很遗憾, 看很多技术书或许没什么用</h3>
<p>这些年, 看了很多书, 刚开始都是买技术的, 后来给自己定了个规矩, 买一本技术要对应买一本非技术.</p>
<p>后来的后来, 开始用kindle, 然后发现看技术书还是duokan来得舒服, 毕竟用pad看大一些:)</p>
<p>市面上绝大多数, 90%, 可以算是入门书籍, 剩下7%, 算是进阶, 然后, 3%才是经典.</p>
<p>然而 ,很多时候, 作为初学者, 看<code>经典</code>一般是自虐的过程, 大多数需要从一本好的<code>入门</code>书开始.</p>
<p>而<code>入门</code>, 需要自己挑选, 数量庞大, 有点类似<code>快销品</code>, 读完入门了, 再也不会看第二遍.</p>
<p>所以后来我的原则变成了, 经典书, 买纸质, 反复读, 而其他的, 通过文档+电子书的形式, 快速学习入门.</p>
<p>看书, 得到一些东西, 想法, 观点等等, 记录, 便够了.</p>
<p>虽然很多书没什么用 ,但是, 你总得找到, 并读完那些<code>有用的</code>书</p>
<p>数量的多少, 并没有什么用. 例如所谓的成功学的书籍, 一目十行一本书刷完花不了多久</p>
<p>但是一本书, 只要能学到一点, 那便是好的.</p>
<p>有人不买书, 说是买了大部分也没读, 我的观点是, 你不买, 一年到头一本书也没读, 但是你买书, 假设买了十本, 只读了两本, 那也比没读好吧.</p>
<p>另外, 买书要谨慎筛选, 花时间去甄别, 然后, 读书要快, 趁着热情还在秒掉, 最后, 要做笔记, 哪怕只是摘录(后续重读你会感谢自己的摘录和笔记的)</p>
<h3 id="学会如何分析问题-解决问题">学会如何分析问题, 解决问题</h3>
<p>工作这些年, 其实发现, 很多东西是通的.</p>
<p>例如, 学一门语言, 深入下去, 再深入下去, 切另一门的时候, 发现其实有很多共性, 比什么都是皮毛好多了.</p>
<p>所以, 没必要纠结自己什么东西还不会.</p>
<p>而这些年, 最大的感受是, 写代码, 无非是分析问题, 解决问题.</p>
<p>而我们要做的, 就是学会这些, 从书本, 从他人, 从自己的经验中去学习, 掌握</p>
<h3 id="学会从一点一滴开始--优化自己的生活">学会从一点一滴开始,  优化自己的生活</h3>
<p>不管是工作, 生活</p>
<p>例如, 学习 GTD, 学习番茄时间工作法, 学习某个工具的使用</p>
<p>学会怎么快速搜索, 从很多同类 app 中挑选适合自己的</p>
<p>定制自己的环境, 符合自己操作习惯的东西</p>
<p>每次, 发现重复或者比较繁琐蛋疼的事情发生了, 记录下来, 如果可以花点时间一劳永逸, 那花这些时间是值得的. 至于解决方案, 会google就能解决绝大多数的问题.</p>
<h3 id="极简的生活">极简的生活</h3>
<p>每个人有自己的选择, 不过我还是倾向于活得简单些.</p>
<p>天生喜欢逻辑, 不喜欢复杂的不可量化的东西. 所以所谓的<code>情商</code>方面 , 低了一些.</p>
<p>生活是自己的, 过成自己想要过的样子便好. 不活在别人的看法里, 不活在别人的嘴里.</p>
<p>自己快乐, 才是真的快乐不是么?</p>
<p><code>极简</code>主义, 虽然还做不到<code>不以物喜不以己悲</code>的境界, 但是, 简简单单的生活, 带来的是简简单单的快乐.</p>
<h3 id="写日记吧">写日记吧</h3>
<p>度过了很闲很闲的三个月</p>
<p>也度过了很忙很忙的三周</p>
<p>发现, 人的记忆, 并不是很靠谱, 曾几何时我能记得过去一周或者一个月, 每天都怎么过的, 而现在, 却发现, 有时候都想不起来昨天怎么过的</p>
<p>或许老了吧, 但是主要问题还是, 这个世界熟悉之后, 我们的生活成了&quot;日常&quot;, 每天过得模式化了, 太过相似, 以至于很难记录.</p>
<p>所以, 写日记吧, 电子或者纸笔, 即使是流水账也好, 否则过往的时光, 很容易被压缩成一小段重复的记忆.</p>
<p>其实, 每天都是新的一天, 善于发现各种细节, 行走得慢些, 多看看, 多想想.</p>
<p>日记里,写写流水, 吐吐槽, 反思反思, 定期翻一翻.</p>
<p>现在, 即使回来再累, 也要睡前花点时间写写:), 已坚持快两个月了</p>
<h3 id="定期总结">定期总结</h3>
<p>这个还是很重要的, 可以暴露很多问题, 例如自己是不是跑偏了&hellip;&hellip;</p>
<p>按自己的习惯, 写写总结, 算是一种回顾, 也是对未来的一些计划.</p>
<h3 id="活在当下">活在当下</h3>
<p>不为过去而懊悔, 不为未来而担忧</p>
<p>昨天已经过去, 时间已经流逝, 懊悔昨天没有什么作用, 想一想, 写写总结, 记录下哪些后面可以避免或者搞定, 然后去睡觉</p>
<p>未来的事情, 来了再说, 在这之前, 准备好就行, 再多的焦虑也是无用, 担忧得睡不着对明天一点作用都没有</p>
<p>快乐也是一天, 不快乐也是一天, 那就快快乐乐的呗</p>
<p>每天过得充实, 每天学到一些东西, 感受到一些东西, 有所进步, 就够了</p>
<blockquote>
<p>不乱于心, 不困于情, 不念过去, 不畏将来</p>
</blockquote>
<p>如果发现自己得了<code>晚睡症</code>, 那就要反思下了, 自己为什么不能让这一天结束而非要拖到第二天呢?</p>
<hr>
<p>2015-11-08 于深圳</p>
<p>wklken</p>
]]></content>
		</item>
		
		<item>
			<title>Python 源码阅读 - 垃圾回收机制</title>
			<link>https://wklken.me/posts/2015/09/29/python-source-gc.html</link>
			<pubDate>Tue, 29 Sep 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/09/29/python-source-gc.html</guid>
			<description>概述 无论何种垃圾收集机制, 一般都是两阶段: 垃圾检测和垃圾回收. 在Python中, 大多数对象的生命周期都是通过对象的引用计数来管理的. 问题: 但</description>
			<content type="html"><![CDATA[<h2 id="概述">概述</h2>
<p>无论何种垃圾收集机制, 一般都是两阶段: 垃圾检测和垃圾回收.</p>
<p>在Python中, 大多数对象的生命周期都是通过对象的引用计数来管理的.</p>
<p>问题: 但是存在循环引用的问题: a 引用 b, b 引用 a, 导致每一个对象的引用计数都不为0, 所占用的内存永远不会被回收</p>
<p>要解决循环引用: 必需引入其他垃圾收集技术来打破循环引用. Python中使用了<code>标记-清除</code>以及<code>分代收集</code></p>
<p>即, Python 中垃圾回收机制: 引用计数(主要), 标记清除, 分代收集(辅助)</p>
<h2 id="引用计数">引用计数</h2>
<p>引用计数, 意味着必须在每次分配和释放内存的时候, 加入管理引用计数的动作</p>
<p>引用计数的优点: 最直观最简单, 实时性, 任何内存, 一旦没有指向它的引用, 就会立即被回收</p>
<h3 id="计数存储">计数存储</h3>
<p>回顾  <a href="http://www.wklken.me/posts/2014/08/05/python-source-object.html">Python 的对象</a></p>
<p><img src="http://www.wklken.me/imgs/python-source/PyObject.png" alt="">￼</p>
<p><img src="http://www.wklken.me/imgs/python-source/PyVarObject.png" alt="">￼</p>
<p>e.g. 引用计数增加以及减少</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">sys</span> <span class="kn">import</span> <span class="n">getrefcount</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">a</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">getrefcount</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">b</span> <span class="o">=</span> <span class="n">a</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">getrefcount</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mi">3</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="k">del</span> <span class="n">b</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">getrefcount</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mi">2</span>
</span></span></code></pre></div><h3 id="计数增加">计数增加</h3>
<p>增加对象引用计数, refcnt incr</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define Py_INCREF(op) (                         \
</span></span></span><span class="line"><span class="cl"><span class="cp">	_Py_INC_REFTOTAL  _Py_REF_DEBUG_COMMA       \
</span></span></span><span class="line"><span class="cl"><span class="cp">	((PyObject*)(op))-&gt;ob_refcnt++)
</span></span></span></code></pre></div><h3 id="计数减少">计数减少</h3>
<p>减少对象引用计数, refcnt desc</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define _Py_DEC_REFTOTAL        _Py_RefTotal--
</span></span></span><span class="line"><span class="cl"><span class="cp">#define _Py_REF_DEBUG_COMMA     ,
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define Py_DECREF(op)                                   \
</span></span></span><span class="line"><span class="cl"><span class="cp">	do {                                                \
</span></span></span><span class="line"><span class="cl"><span class="cp">		if (_Py_DEC_REFTOTAL  _Py_REF_DEBUG_COMMA       \
</span></span></span><span class="line"><span class="cl"><span class="cp">		--((PyObject*)(op))-&gt;ob_refcnt != 0)            \
</span></span></span><span class="line"><span class="cl"><span class="cp">			_Py_CHECK_REFCNT(op)                        \
</span></span></span><span class="line"><span class="cl"><span class="cp">		else                                            \
</span></span></span><span class="line"><span class="cl"><span class="cp">		_Py_Dealloc((PyObject *)(op));                  \
</span></span></span><span class="line"><span class="cl"><span class="cp">	} while (0)
</span></span></span></code></pre></div><p>即, 发现refcnt变成0的时候, 会调用<code>_Py_Dealloc</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">PyAPI_FUNC</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="n">_Py_Dealloc</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define _Py_REF_DEBUG_COMMA     ,
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define _Py_Dealloc(op) (                               \
</span></span></span><span class="line"><span class="cl"><span class="cp">	_Py_INC_TPFREES(op) _Py_COUNT_ALLOCS_COMMA          \
</span></span></span><span class="line"><span class="cl"><span class="cp">	(*Py_TYPE(op)-&gt;tp_dealloc)((PyObject *)(op)))
</span></span></span><span class="line"><span class="cl"><span class="cp">#endif </span><span class="cm">/* !Py_TRACE_REFS */</span><span class="cp">
</span></span></span></code></pre></div><p>会调用各自类型的<code>tp_dealloc</code></p>
<p>例如dict</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">PyTypeObject</span> <span class="n">PyDict_Type</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyVarObject_HEAD_INIT</span><span class="p">(</span><span class="o">&amp;</span><span class="n">PyType_Type</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;dict&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="k">sizeof</span><span class="p">(</span><span class="n">PyDictObject</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="n">destructor</span><span class="p">)</span><span class="n">dict_dealloc</span><span class="p">,</span>                   <span class="cm">/* tp_dealloc */</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></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span>
</span></span><span class="line"><span class="cl"><span class="n">dict_dealloc</span><span class="p">(</span><span class="k">register</span> <span class="n">PyDictObject</span> <span class="o">*</span><span class="n">mp</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 class="c1">// 如果满足条件, 放入到缓冲池freelist中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">numfree</span> <span class="o">&lt;</span> <span class="n">PyDict_MAXFREELIST</span> <span class="o">&amp;&amp;</span> <span class="n">Py_TYPE</span><span class="p">(</span><span class="n">mp</span><span class="p">)</span> <span class="o">==</span> <span class="o">&amp;</span><span class="n">PyDict_Type</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">free_list</span><span class="p">[</span><span class="n">numfree</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">mp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 否则, 调用tp_free
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="nf">Py_TYPE</span><span class="p">(</span><span class="n">mp</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">tp_free</span><span class="p">((</span><span class="n">PyObject</span> <span class="o">*</span><span class="p">)</span><span class="n">mp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Py_TRASHCAN_SAFE_END</span><span class="p">(</span><span class="n">mp</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Python基本类型的<code>tp_dealloc</code>, 通常都会与各自的缓冲池机制相关, 释放会优先放入缓冲池中(对应的分配会优先从缓冲池取). 这个内存分配与回收同缓冲池机制相关</p>
<p>当无法放入缓冲池时, 会调用各自类型的<code>tp_free</code></p>
<p>int, 比较特殊</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// int, 通用整数对象缓冲池机制
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="p">(</span><span class="n">freefunc</span><span class="p">)</span><span class="n">int_free</span><span class="p">,</span>                         <span class="cm">/* tp_free */</span>
</span></span></code></pre></div><p>string</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// string
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">PyObject_Del</span><span class="p">,</span>                               <span class="cm">/* tp_free */</span>
</span></span></code></pre></div><p>dict/tuple/list</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">    <span class="n">PyObject_GC_Del</span><span class="p">,</span>                            <span class="cm">/* tp_free */</span>
</span></span></code></pre></div><p>然后, 我们再回头看, 自定义对象的<code>tp_free</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">PyTypeObject</span> <span class="n">PyType_Type</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyVarObject_HEAD_INIT</span><span class="p">(</span><span class="o">&amp;</span><span class="n">PyType_Type</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;type&#34;</span><span class="p">,</span>                                     <span class="cm">/* tp_name */</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject_GC_Del</span><span class="p">,</span>                            <span class="cm">/* tp_free */</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>即, 最终, 当计数变为0, 触发内存回收动作. 涉及函数<code>PyObject_Del</code>和<code>PyObject_GC_Del</code>, 并且, 自定义类以及容器类型(dict/list/tuple/set等)使用的都是后者<code>PyObject_GC_Del</code>.</p>
<h3 id="内存回收-pyobject_del--pyobject_gc_del">内存回收 PyObject_Del / PyObject_GC_Del</h3>
<p>如果引用计数=0:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-plain" data-lang="plain"><span class="line"><span class="cl">1. 放入缓冲池
</span></span><span class="line"><span class="cl">2. 真正销毁, PyObject_Del/PyObject_GC_Del内存操作
</span></span></code></pre></div><p>这两个操作都是进行内存级别的操作</p>
<ul>
<li>PyObject_Del</li>
</ul>
<blockquote>
<p>PyObject_Del(op) releases the memory allocated for an object.  It does not
run a destructor &ndash; it only frees the memory.  PyObject_Free is identical.</p>
</blockquote>
<p>这块删除, <code>PyObject_Free</code> 涉及到了Python底层内存的分配和管理机制, 具体见前面的博文</p>
<ul>
<li>PyObject_GC_Del</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span>
</span></span><span class="line"><span class="cl"><span class="nf">PyObject_GC_Del</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">op</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="n">PyGC_Head</span> <span class="o">*</span><span class="n">g</span> <span class="o">=</span> <span class="n">AS_GC</span><span class="p">(</span><span class="n">op</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// Returns true if a given object is tracked
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="k">if</span> <span class="p">(</span><span class="n">IS_TRACKED</span><span class="p">(</span><span class="n">op</span><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"></span>		<span class="n">gc_list_remove</span><span class="p">(</span><span class="n">g</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">(</span><span class="n">generations</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">count</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="n">generations</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">count</span><span class="o">--</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="n">PyObject_FREE</span><span class="p">(</span><span class="n">g</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>IS_TRACKED</code> 涉及到标记-清除的机制</p>
<p><code>generations</code> 涉及到了分代回收</p>
<p><code>PyObject_FREE</code>, 则和Python底层内存池机制相关</p>
<h2 id="标记-清除">标记-清除</h2>
<h3 id="问题-什么对象可能产生循环引用">问题: 什么对象可能产生循环引用?</h3>
<p>只需要关注关注可能产生循环引用的对象</p>
<p>PyIntObject/PyStringObject等不可能</p>
<p>Python中的循环引用总是发生在container对象之间, 所谓containser对象即是内部可持有对其他对象的引用: list/dict/class/instance等等</p>
<p>垃圾收集带来的开销依赖于container对象的数量, 必需跟踪所创建的每一个container对象, 并将这些对象组织到一个集合中.</p>
<h3 id="可收集对象链表">可收集对象链表</h3>
<p>可收集对象链表: 将需要被收集和跟踪的container, 放到可收集的链表中</p>
<p>任何一个python对象都分为两部分: PyObject_HEAD + 对象本身数据</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/* PyObject_HEAD defines the initial segment of every PyObject. */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define PyObject_HEAD                   \
</span></span></span><span class="line"><span class="cl"><span class="cp">    _PyObject_HEAD_EXTRA                \
</span></span></span><span class="line"><span class="cl"><span class="cp">    Py_ssize_t ob_refcnt;               \
</span></span></span><span class="line"><span class="cl"><span class="cp">    struct _typeobject *ob_type;
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">//----------------------------------------------------
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="cp">#define _PyObject_HEAD_EXTRA            \
</span></span></span><span class="line"><span class="cl"><span class="cp">      struct _object *_ob_next;           \
</span></span></span><span class="line"><span class="cl"><span class="cp">      struct _object *_ob_prev;
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 双向链表结构, 垃圾回收
</span></span></span></code></pre></div><p>可收集对象链表</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">Modules</span><span class="o">/</span><span class="n">gcmodule</span><span class="p">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* GC information is stored BEFORE the object structure. */</span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">union</span> <span class="n">_gc_head</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <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"></span>        <span class="k">union</span> <span class="n">_gc_head</span> <span class="o">*</span><span class="n">gc_next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">union</span> <span class="n">_gc_head</span> <span class="o">*</span><span class="n">gc_prev</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 在初始化时会被初始化为 GC_UNTRACED
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">Py_ssize_t</span> <span class="n">gc_refs</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="n">gc</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">long</span> <span class="kt">double</span> <span class="n">dummy</span><span class="p">;</span>  <span class="cm">/* force worst-case alignment */</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">PyGC_Head</span><span class="p">;</span>
</span></span></code></pre></div><p>创建container的过程: <code>container对象 = pyGC_Head | PyObject_HEAD | Container Object</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">PyObject</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl"><span class="nf">_PyObject_GC_New</span><span class="p">(</span><span class="n">PyTypeObject</span> <span class="o">*</span><span class="n">tp</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="n">PyObject</span> <span class="o">*</span><span class="n">op</span> <span class="o">=</span> <span class="n">_PyObject_GC_Malloc</span><span class="p">(</span><span class="n">_PyObject_SIZE</span><span class="p">(</span><span class="n">tp</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">op</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">op</span> <span class="o">=</span> <span class="n">PyObject_INIT</span><span class="p">(</span><span class="n">op</span><span class="p">,</span> <span class="n">tp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">op</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="o">=&gt;</span> <span class="n">_PyObject_GC_Malloc</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#define _PyGC_REFS_UNTRACKED                    (-2)
</span></span></span><span class="line"><span class="cl"><span class="cp">#define GC_UNTRACKED                    _PyGC_REFS_UNTRACKED
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="n">PyObject</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl"><span class="n">_PyObject_GC_Malloc</span><span class="p">(</span><span class="n">size_t</span> <span class="n">basicsize</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="n">PyObject</span> <span class="o">*</span><span class="n">op</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyGC_Head</span> <span class="o">*</span><span class="n">g</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">basicsize</span> <span class="o">&gt;</span> <span class="n">PY_SSIZE_T_MAX</span> <span class="o">-</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">PyGC_Head</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">PyErr_NoMemory</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 为 对象本身+PyGC_Head申请内存, 注意分配的size
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">g</span> <span class="o">=</span> <span class="p">(</span><span class="n">PyGC_Head</span> <span class="o">*</span><span class="p">)</span><span class="n">PyObject_MALLOC</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="k">sizeof</span><span class="p">(</span><span class="n">PyGC_Head</span><span class="p">)</span> <span class="o">+</span> <span class="n">basicsize</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">g</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">PyErr_NoMemory</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 初始化 GC_UNTRACED
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">g</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_refs</span> <span class="o">=</span> <span class="n">GC_UNTRACKED</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">generations</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">count</span><span class="o">++</span><span class="p">;</span> <span class="cm">/* number of allocated GC objects */</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"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">generations</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">count</span> <span class="o">&gt;</span> <span class="n">generations</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">threshold</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">        <span class="n">enabled</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">        <span class="n">generations</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">threshold</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">        <span class="o">!</span><span class="n">collecting</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">        <span class="o">!</span><span class="n">PyErr_Occurred</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="n">collecting</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">collect_generations</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="n">collecting</span> <span class="o">=</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 class="n">op</span> <span class="o">=</span> <span class="n">FROM_GC</span><span class="p">(</span><span class="n">g</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">op</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="pyobject_head-and-pygc_head">PyObject_HEAD and PyGC_HEAD</h3>
<p>注意, <code>FROM_GC</code>和<code>AS_GC</code>用于 <code>PyObject_HEAD &lt;=&gt; PyGC_HEAD</code>地址相互转换</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// =&gt; Modules/gcmodule.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/* Get an object&#39;s GC head */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define AS_GC(o) ((PyGC_Head *)(o)-1)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/* Get the object given the GC head */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define FROM_GC(g) ((PyObject *)(((PyGC_Head *)g)+1))
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// =&gt; objimpl.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define _Py_AS_GC(o) ((PyGC_Head *)(o)-1)
</span></span></span></code></pre></div><h3 id="问题-什么时候将container放到这个对象链表中">问题: 什么时候将container放到这个对象链表中</h3>
<p>e.g list</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// =&gt; listobject.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="n">PyObject</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl"><span class="nf">PyList_New</span><span class="p">(</span><span class="n">Py_ssize_t</span> <span class="n">size</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="n">PyListObject</span> <span class="o">*</span><span class="n">op</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">op</span> <span class="o">=</span> <span class="n">PyObject_GC_New</span><span class="p">(</span><span class="n">PyListObject</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">PyList_Type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">_PyObject_GC_TRACK</span><span class="p">(</span><span class="n">op</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="p">)</span> <span class="n">op</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="c1">// =&gt;  _PyObject_GC_TRACK
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// objimpl.h
</span></span></span><span class="line"><span class="cl"><span class="c1">// 加入到可收集对象链表中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define _PyObject_GC_TRACK(o) do { \
</span></span></span><span class="line"><span class="cl"><span class="cp">    PyGC_Head *g = _Py_AS_GC(o); \
</span></span></span><span class="line"><span class="cl"><span class="cp">    if (g-&gt;gc.gc_refs != _PyGC_REFS_UNTRACKED) \
</span></span></span><span class="line"><span class="cl"><span class="cp">        Py_FatalError(&#34;GC object already tracked&#34;); \
</span></span></span><span class="line"><span class="cl"><span class="cp">    g-&gt;gc.gc_refs = _PyGC_REFS_REACHABLE; \
</span></span></span><span class="line"><span class="cl"><span class="cp">    g-&gt;gc.gc_next = _PyGC_generation0; \
</span></span></span><span class="line"><span class="cl"><span class="cp">    g-&gt;gc.gc_prev = _PyGC_generation0-&gt;gc.gc_prev; \
</span></span></span><span class="line"><span class="cl"><span class="cp">    g-&gt;gc.gc_prev-&gt;gc.gc_next = g; \
</span></span></span><span class="line"><span class="cl"><span class="cp">    _PyGC_generation0-&gt;gc.gc_prev = g; \
</span></span></span><span class="line"><span class="cl"><span class="cp">    } while (0);
</span></span></span></code></pre></div><h3 id="问题-什么时候将container从这个对象链表中摘除">问题: 什么时候将container从这个对象链表中摘除</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Objects/listobject.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span>
</span></span><span class="line"><span class="cl"><span class="nf">list_dealloc</span><span class="p">(</span><span class="n">PyListObject</span> <span class="o">*</span><span class="n">op</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="n">Py_ssize_t</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject_GC_UnTrack</span><span class="p">(</span><span class="n">op</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">// =&gt; PyObject_GC_UnTrack =&gt; _PyObject_GC_UNTRACK
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 对象销毁的时候
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define _PyObject_GC_UNTRACK(o) do { \
</span></span></span><span class="line"><span class="cl"><span class="cp">    PyGC_Head *g = _Py_AS_GC(o); \
</span></span></span><span class="line"><span class="cl"><span class="cp">    assert(g-&gt;gc.gc_refs != _PyGC_REFS_UNTRACKED); \
</span></span></span><span class="line"><span class="cl"><span class="cp">    g-&gt;gc.gc_refs = _PyGC_REFS_UNTRACKED; \
</span></span></span><span class="line"><span class="cl"><span class="cp">    g-&gt;gc.gc_prev-&gt;gc.gc_next = g-&gt;gc.gc_next; \
</span></span></span><span class="line"><span class="cl"><span class="cp">    g-&gt;gc.gc_next-&gt;gc.gc_prev = g-&gt;gc.gc_prev; \
</span></span></span><span class="line"><span class="cl"><span class="cp">    g-&gt;gc.gc_next = NULL; \
</span></span></span><span class="line"><span class="cl"><span class="cp">    } while (0);
</span></span></span></code></pre></div><h3 id="问题-如何进行标记-清除">问题: 如何进行标记-清除</h3>
<p>现在, 我们得到了一个链表</p>
<p>Python将自己的垃圾收集限制在这个链表上, 循环引用一定发生在这个链表的一群独享之间.</p>
<h4 id="0-概览">0. 概览</h4>
<p><code>_PyObject_GC_Malloc</code> 分配内存时, 发现超过阈值, 此时, 会触发gc, <code>collect_generations</code>
然后调用<code>collect</code>, <code>collect</code>包含标记-清除逻辑</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">gcmodule</span><span class="p">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="cm">/* This is the main function.  Read this to understand how the
</span></span></span><span class="line"><span class="cl"><span class="cm">   * collection process works. */</span>
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="n">Py_ssize_t</span>
</span></span><span class="line"><span class="cl">  <span class="n">collect</span><span class="p">(</span><span class="kt">int</span> <span class="n">generation</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="c1">// 第1步: 将所有比 当前代 年轻的代中的对象 都放到 当前代 的对象链表中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="cm">/* merge younger generations with one we are currently collecting */</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">generation</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">gc_list_merge</span><span class="p">(</span><span class="n">GEN_HEAD</span><span class="p">(</span><span class="n">i</span><span class="p">),</span> <span class="n">GEN_HEAD</span><span class="p">(</span><span class="n">generation</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="c1">// 第2步
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">update_refs</span><span class="p">(</span><span class="n">young</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 第3步
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">subtract_refs</span><span class="p">(</span><span class="n">young</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 第4步
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">gc_list_init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">unreachable</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">move_unreachable</span><span class="p">(</span><span class="n">young</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">unreachable</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 第5步
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="cm">/* Move reachable objects to next generation. */</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">young</span> <span class="o">!=</span> <span class="n">old</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="n">generation</span> <span class="o">==</span> <span class="n">NUM_GENERATIONS</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="n">long_lived_pending</span> <span class="o">+=</span> <span class="n">gc_list_size</span><span class="p">(</span><span class="n">young</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="n">gc_list_merge</span><span class="p">(</span><span class="n">young</span><span class="p">,</span> <span class="n">old</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="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="cm">/* We only untrack dicts in full collections, to avoid quadratic
</span></span></span><span class="line"><span class="cl"><span class="cm">             dict build-up. See issue #14775. */</span>
</span></span><span class="line"><span class="cl">          <span class="n">untrack_dicts</span><span class="p">(</span><span class="n">young</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="n">long_lived_pending</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">long_lived_total</span> <span class="o">=</span> <span class="n">gc_list_size</span><span class="p">(</span><span class="n">young</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="c1">// 第6步
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">delete_garbage</span><span class="p">(</span><span class="o">&amp;</span><span class="n">unreachable</span><span class="p">,</span> <span class="n">old</span><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></span></code></pre></div><h4 id="1-第一步--gc_list_merge">1. 第一步:  gc_list_merge</h4>
<p>将所有比 当前代 年轻的代中的对象 都放到 当前代 的对象链表中</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// =&gt; gc_list_merge
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 执行拷贝而已
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cm">/* append list `from` onto list `to`; `from` becomes an empty list */</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span>
</span></span><span class="line"><span class="cl"><span class="nf">gc_list_merge</span><span class="p">(</span><span class="n">PyGC_Head</span> <span class="o">*</span><span class="n">from</span><span class="p">,</span> <span class="n">PyGC_Head</span> <span class="o">*</span><span class="n">to</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="n">PyGC_Head</span> <span class="o">*</span><span class="n">tail</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span><span class="p">(</span><span class="n">from</span> <span class="o">!=</span> <span class="n">to</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">gc_list_is_empty</span><span class="p">(</span><span class="n">from</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">tail</span> <span class="o">=</span> <span class="n">to</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_prev</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">tail</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_next</span> <span class="o">=</span> <span class="n">from</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">tail</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_next</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_prev</span> <span class="o">=</span> <span class="n">tail</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">to</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_prev</span> <span class="o">=</span> <span class="n">from</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_prev</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">to</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_prev</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_next</span> <span class="o">=</span> <span class="n">to</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="c1">// 清空
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">gc_list_init</span><span class="p">(</span><span class="n">from</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="o">=&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span>
</span></span><span class="line"><span class="cl"><span class="n">gc_list_init</span><span class="p">(</span><span class="n">PyGC_Head</span> <span class="o">*</span><span class="n">list</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="n">list</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_prev</span> <span class="o">=</span> <span class="n">list</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">list</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_next</span> <span class="o">=</span> <span class="n">list</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>即, 此刻, 所有待进行处理的对象都集中在同一个链表中</p>
<p>处理,</p>
<p>其逻辑是, 要去除循环引用, 得到有效引用计数</p>
<p>有效引用计数: 将循环引用的计数去除, 最终得到的 =&gt; 将环从引用中摘除, 各自引用计数数值-1</p>
<p>实际操作, 并不要直接修改对象的 ob_refcnt, 而是修改其副本, <code>PyGC_Head</code>中的<code>gc.gc_ref</code></p>
<h4 id="2-第二步-update_refs">2. 第二步: update_refs</h4>
<p>遍历对象链表, 将每个对象的gc.gc_ref值设置为ob_refcnt</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// =&gt; gcmodule.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span>
</span></span><span class="line"><span class="cl"><span class="nf">update_refs</span><span class="p">(</span><span class="n">PyGC_Head</span> <span class="o">*</span><span class="n">containers</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="n">PyGC_Head</span> <span class="o">*</span><span class="n">gc</span> <span class="o">=</span> <span class="n">containers</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(;</span> <span class="n">gc</span> <span class="o">!=</span> <span class="n">containers</span><span class="p">;</span> <span class="n">gc</span> <span class="o">=</span> <span class="n">gc</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_next</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">assert</span><span class="p">(</span><span class="n">gc</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_refs</span> <span class="o">==</span> <span class="n">GC_REACHABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">gc</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_refs</span> <span class="o">=</span> <span class="n">Py_REFCNT</span><span class="p">(</span><span class="n">FROM_GC</span><span class="p">(</span><span class="n">gc</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* Python&#39;s cyclic gc should never see an incoming refcount
</span></span></span><span class="line"><span class="cl"><span class="cm">         * of 0:  if something decref&#39;ed to 0, it should have been
</span></span></span><span class="line"><span class="cl"><span class="cm">         * deallocated immediately at that time.
</span></span></span><span class="line"><span class="cl"><span class="cm">         * Possible cause (if the assert triggers):  a tp_dealloc
</span></span></span><span class="line"><span class="cl"><span class="cm">         * routine left a gc-aware object tracked during its teardown
</span></span></span><span class="line"><span class="cl"><span class="cm">         * phase, and did something-- or allowed something to happen --
</span></span></span><span class="line"><span class="cl"><span class="cm">         * that called back into Python.  gc can trigger then, and may
</span></span></span><span class="line"><span class="cl"><span class="cm">         * see the still-tracked dying object.  Before this assert
</span></span></span><span class="line"><span class="cl"><span class="cm">         * was added, such mistakes went on to allow gc to try to
</span></span></span><span class="line"><span class="cl"><span class="cm">         * delete the object again.  In a debug build, that caused
</span></span></span><span class="line"><span class="cl"><span class="cm">         * a mysterious segfault, when _Py_ForgetReference tried
</span></span></span><span class="line"><span class="cl"><span class="cm">         * to remove the object from the doubly-linked list of all
</span></span></span><span class="line"><span class="cl"><span class="cm">         * objects a second time.  In a release build, an actual
</span></span></span><span class="line"><span class="cl"><span class="cm">         * double deallocation occurred, which leads to corruption
</span></span></span><span class="line"><span class="cl"><span class="cm">         * of the allocator&#39;s internal bookkeeping pointers.  That&#39;s
</span></span></span><span class="line"><span class="cl"><span class="cm">         * so serious that maybe this should be a release-build
</span></span></span><span class="line"><span class="cl"><span class="cm">         * check instead of an assert?
</span></span></span><span class="line"><span class="cl"><span class="cm">         */</span>
</span></span><span class="line"><span class="cl">        <span class="n">assert</span><span class="p">(</span><span class="n">gc</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_refs</span> <span class="o">!=</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 class="p">}</span>
</span></span></code></pre></div><h4 id="3-第三步-计算有效引用计数">3. 第三步: 计算有效引用计数</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">  <span class="cm">/* A traversal callback for subtract_refs. */</span>
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">  <span class="nf">visit_decref</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">op</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">data</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="n">assert</span><span class="p">(</span><span class="n">op</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// 判断op指向的对象是否是被垃圾收集监控的, 对象的type对象中有Py_TPFLAGS_HAVE_GC符号
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="k">if</span> <span class="p">(</span><span class="n">PyObject_IS_GC</span><span class="p">(</span><span class="n">op</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">PyGC_Head</span> <span class="o">*</span><span class="n">gc</span> <span class="o">=</span> <span class="n">AS_GC</span><span class="p">(</span><span class="n">op</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="cm">/* We&#39;re only interested in gc_refs for objects in the
</span></span></span><span class="line"><span class="cl"><span class="cm">           * generation being collected, which can be recognized
</span></span></span><span class="line"><span class="cl"><span class="cm">           * because only they have positive gc_refs.
</span></span></span><span class="line"><span class="cl"><span class="cm">           */</span>
</span></span><span class="line"><span class="cl">          <span class="n">assert</span><span class="p">(</span><span class="n">gc</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_refs</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">);</span> <span class="cm">/* else refcount was too small */</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="n">gc</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_refs</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">              <span class="n">gc</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_refs</span><span class="o">--</span><span class="p">;</span>  <span class="c1">// -1
</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 class="k">return</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></span><span class="line"><span class="cl">  <span class="cm">/* Subtract internal references from gc_refs.  After this, gc_refs is &gt;= 0
</span></span></span><span class="line"><span class="cl"><span class="cm">   * for all objects in containers, and is GC_REACHABLE for all tracked gc
</span></span></span><span class="line"><span class="cl"><span class="cm">   * objects not in containers.  The ones with gc_refs &gt; 0 are directly
</span></span></span><span class="line"><span class="cl"><span class="cm">   * reachable from outside containers, and so can&#39;t be collected.
</span></span></span><span class="line"><span class="cl"><span class="cm">   */</span>
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="kt">void</span>
</span></span><span class="line"><span class="cl">  <span class="nf">subtract_refs</span><span class="p">(</span><span class="n">PyGC_Head</span> <span class="o">*</span><span class="n">containers</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="n">traverseproc</span> <span class="n">traverse</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">PyGC_Head</span> <span class="o">*</span><span class="n">gc</span> <span class="o">=</span> <span class="n">containers</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_next</span><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"></span>      <span class="k">for</span> <span class="p">(;</span> <span class="n">gc</span> <span class="o">!=</span> <span class="n">containers</span><span class="p">;</span> <span class="n">gc</span><span class="o">=</span><span class="n">gc</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_next</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="c1">// 与特定的类型相关, 得到类型对应的traverse函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="n">traverse</span> <span class="o">=</span> <span class="n">Py_TYPE</span><span class="p">(</span><span class="n">FROM_GC</span><span class="p">(</span><span class="n">gc</span><span class="p">))</span><span class="o">-&gt;</span><span class="n">tp_traverse</span><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"></span>          <span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="n">traverse</span><span class="p">(</span><span class="n">FROM_GC</span><span class="p">(</span><span class="n">gc</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                         <span class="p">(</span><span class="n">visitproc</span><span class="p">)</span><span class="n">visit_decref</span><span class="p">,</span> <span class="c1">// 回调形式传入
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                         <span class="nb">NULL</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></code></pre></div><p>我们可以看看dictobject的traverse函数</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">  <span class="k">static</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">  <span class="nf">dict_traverse</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">op</span><span class="p">,</span> <span class="n">visitproc</span> <span class="n">visit</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">arg</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="n">Py_ssize_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">PyObject</span> <span class="o">*</span><span class="n">pk</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">PyObject</span> <span class="o">*</span><span class="n">pv</span><span class="p">;</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"><span class="c1"></span>      <span class="k">while</span> <span class="p">(</span><span class="n">PyDict_Next</span><span class="p">(</span><span class="n">op</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">i</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">pk</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">pv</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">Py_VISIT</span><span class="p">(</span><span class="n">pk</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="n">Py_VISIT</span><span class="p">(</span><span class="n">pv</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="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span></code></pre></div><p>逻辑大概是: 遍历容器对象里面的所有对象, 通过<code>visit_decref</code>将这些对象的引用计数都-1,</p>
<p>最终, 遍历完链表之后, 整个可收集对象链表中所有container对象之间的循环引用都被去掉了</p>
<h4 id="4-第四步-垃圾标记">4. 第四步: 垃圾标记</h4>
<p><code>move_unreachable</code>, 将可收集对象链表中, 根据有效引用计数 不等于0(root对象) 和 等于0(非root对象, 垃圾, 可回收), 一分为二</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"> <span class="cm">/* Move the unreachable objects from young to unreachable.  After this,
</span></span></span><span class="line"><span class="cl"><span class="cm">   * all objects in young have gc_refs = GC_REACHABLE, and all objects in
</span></span></span><span class="line"><span class="cl"><span class="cm">   * unreachable have gc_refs = GC_TENTATIVELY_UNREACHABLE.  All tracked
</span></span></span><span class="line"><span class="cl"><span class="cm">   * gc objects not in young or unreachable still have gc_refs = GC_REACHABLE.
</span></span></span><span class="line"><span class="cl"><span class="cm">   * All objects in young after this are directly or indirectly reachable
</span></span></span><span class="line"><span class="cl"><span class="cm">   * from outside the original young; and all objects in unreachable are
</span></span></span><span class="line"><span class="cl"><span class="cm">   * not.
</span></span></span><span class="line"><span class="cl"><span class="cm">   */</span>
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="kt">void</span>
</span></span><span class="line"><span class="cl">  <span class="nf">move_unreachable</span><span class="p">(</span><span class="n">PyGC_Head</span> <span class="o">*</span><span class="n">young</span><span class="p">,</span> <span class="n">PyGC_Head</span> <span class="o">*</span><span class="n">unreachable</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="n">PyGC_Head</span> <span class="o">*</span><span class="n">gc</span> <span class="o">=</span> <span class="n">young</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="cm">/* Invariants:  all objects &#34;to the left&#34; of us in young have gc_refs
</span></span></span><span class="line"><span class="cl"><span class="cm">       * = GC_REACHABLE, and are indeed reachable (directly or indirectly)
</span></span></span><span class="line"><span class="cl"><span class="cm">       * from outside the young list as it was at entry.  All other objects
</span></span></span><span class="line"><span class="cl"><span class="cm">       * from the original young &#34;to the left&#34; of us are in unreachable now,
</span></span></span><span class="line"><span class="cl"><span class="cm">       * and have gc_refs = GC_TENTATIVELY_UNREACHABLE.  All objects to the
</span></span></span><span class="line"><span class="cl"><span class="cm">       * left of us in &#39;young&#39; now have been scanned, and no objects here
</span></span></span><span class="line"><span class="cl"><span class="cm">       * or to the right have been scanned yet.
</span></span></span><span class="line"><span class="cl"><span class="cm">       */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="k">while</span> <span class="p">(</span><span class="n">gc</span> <span class="o">!=</span> <span class="n">young</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">PyGC_Head</span> <span class="o">*</span><span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="c1">// 对于root object,
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="k">if</span> <span class="p">(</span><span class="n">gc</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_refs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="cm">/* gc is definitely reachable from outside the
</span></span></span><span class="line"><span class="cl"><span class="cm">               * original &#39;young&#39;.  Mark it as such, and traverse
</span></span></span><span class="line"><span class="cl"><span class="cm">               * its pointers to find any other objects that may
</span></span></span><span class="line"><span class="cl"><span class="cm">               * be directly reachable from it.  Note that the
</span></span></span><span class="line"><span class="cl"><span class="cm">               * call to tp_traverse may append objects to young,
</span></span></span><span class="line"><span class="cl"><span class="cm">               * so we have to wait until it returns to determine
</span></span></span><span class="line"><span class="cl"><span class="cm">               * the next object to visit.
</span></span></span><span class="line"><span class="cl"><span class="cm">               */</span>
</span></span><span class="line"><span class="cl">              <span class="n">PyObject</span> <span class="o">*</span><span class="n">op</span> <span class="o">=</span> <span class="n">FROM_GC</span><span class="p">(</span><span class="n">gc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">              <span class="n">traverseproc</span> <span class="n">traverse</span> <span class="o">=</span> <span class="n">Py_TYPE</span><span class="p">(</span><span class="n">op</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">tp_traverse</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">assert</span><span class="p">(</span><span class="n">gc</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_refs</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">              <span class="c1">// 设置其gc-&gt;gc.gc_refs = GC_REACHABLE
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="n">gc</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_refs</span> <span class="o">=</span> <span class="n">GC_REACHABLE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">              <span class="c1">// 注意这里逻辑, visit_reachable, 意图是?
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="n">traverse</span><span class="p">(</span><span class="n">op</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                              <span class="p">(</span><span class="n">visitproc</span><span class="p">)</span><span class="n">visit_reachable</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                              <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">young</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">              <span class="n">next</span> <span class="o">=</span> <span class="n">gc</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="k">if</span> <span class="p">(</span><span class="n">PyTuple_CheckExact</span><span class="p">(</span><span class="n">op</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                  <span class="n">_PyTuple_MaybeUntrack</span><span class="p">(</span><span class="n">op</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 class="c1">// 有效引用计数=0, 非root对象, 移动到unreachable链表中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="cm">/* This *may* be unreachable.  To make progress,
</span></span></span><span class="line"><span class="cl"><span class="cm">               * assume it is.  gc isn&#39;t directly reachable from
</span></span></span><span class="line"><span class="cl"><span class="cm">               * any object we&#39;ve already traversed, but may be
</span></span></span><span class="line"><span class="cl"><span class="cm">               * reachable from an object we haven&#39;t gotten to yet.
</span></span></span><span class="line"><span class="cl"><span class="cm">               * visit_reachable will eventually move gc back into
</span></span></span><span class="line"><span class="cl"><span class="cm">               * young if that&#39;s so, and we&#39;ll see it again.
</span></span></span><span class="line"><span class="cl"><span class="cm">               */</span>
</span></span><span class="line"><span class="cl">              <span class="n">next</span> <span class="o">=</span> <span class="n">gc</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">gc_list_move</span><span class="p">(</span><span class="n">gc</span><span class="p">,</span> <span class="n">unreachable</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">              <span class="n">gc</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_refs</span> <span class="o">=</span> <span class="n">GC_TENTATIVELY_UNREACHABLE</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="n">gc</span> <span class="o">=</span> <span class="n">next</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></code></pre></div><h4 id="5-第五步-将存活对象放入下一代">5. 第五步: 将存活对象放入下一代</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">      <span class="cm">/* Move reachable objects to next generation. */</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">young</span> <span class="o">!=</span> <span class="n">old</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="n">generation</span> <span class="o">==</span> <span class="n">NUM_GENERATIONS</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="n">long_lived_pending</span> <span class="o">+=</span> <span class="n">gc_list_size</span><span class="p">(</span><span class="n">young</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="n">gc_list_merge</span><span class="p">(</span><span class="n">young</span><span class="p">,</span> <span class="n">old</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="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="cm">/* We only untrack dicts in full collections, to avoid quadratic
</span></span></span><span class="line"><span class="cl"><span class="cm">             dict build-up. See issue #14775. */</span>
</span></span><span class="line"><span class="cl">          <span class="n">untrack_dicts</span><span class="p">(</span><span class="n">young</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="n">long_lived_pending</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">long_lived_total</span> <span class="o">=</span> <span class="n">gc_list_size</span><span class="p">(</span><span class="n">young</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span></code></pre></div><h4 id="6-第六步-执行回收">6. 第六步: 执行回收</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">gcmoudle</span><span class="p">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">  <span class="n">gc_list_is_empty</span><span class="p">(</span><span class="n">PyGC_Head</span> <span class="o">*</span><span class="n">list</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="k">return</span> <span class="p">(</span><span class="n">list</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_next</span> <span class="o">==</span> <span class="n">list</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="cm">/* Break reference cycles by clearing the containers involved.  This is
</span></span></span><span class="line"><span class="cl"><span class="cm">   * tricky business as the lists can be changing and we don&#39;t know which
</span></span></span><span class="line"><span class="cl"><span class="cm">   * objects may be freed.  It is possible I screwed something up here.
</span></span></span><span class="line"><span class="cl"><span class="cm">   */</span>
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="kt">void</span>
</span></span><span class="line"><span class="cl">  <span class="n">delete_garbage</span><span class="p">(</span><span class="n">PyGC_Head</span> <span class="o">*</span><span class="n">collectable</span><span class="p">,</span> <span class="n">PyGC_Head</span> <span class="o">*</span><span class="n">old</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="n">inquiry</span> <span class="n">clear</span><span class="p">;</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"><span class="c1"></span>      <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">gc_list_is_empty</span><span class="p">(</span><span class="n">collectable</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">PyGC_Head</span> <span class="o">*</span><span class="n">gc</span> <span class="o">=</span> <span class="n">collectable</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_next</span><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"></span>          <span class="n">PyObject</span> <span class="o">*</span><span class="n">op</span> <span class="o">=</span> <span class="n">FROM_GC</span><span class="p">(</span><span class="n">gc</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="n">assert</span><span class="p">(</span><span class="n">IS_TENTATIVELY_UNREACHABLE</span><span class="p">(</span><span class="n">op</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="n">debug</span> <span class="o">&amp;</span> <span class="n">DEBUG_SAVEALL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="n">PyList_Append</span><span class="p">(</span><span class="n">garbage</span><span class="p">,</span> <span class="n">op</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="k">else</span> <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"></span>              <span class="k">if</span> <span class="p">((</span><span class="n">clear</span> <span class="o">=</span> <span class="n">Py_TYPE</span><span class="p">(</span><span class="n">op</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">tp_clear</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                  <span class="n">Py_INCREF</span><span class="p">(</span><span class="n">op</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                  <span class="c1">// 这个操作会调整container对象中每个引用所有对象的引用计数, 从而完成打破循环的最终目标
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                  <span class="n">clear</span><span class="p">(</span><span class="n">op</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                  <span class="n">Py_DECREF</span><span class="p">(</span><span class="n">op</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">// 重新送回到reachable链表.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="c1">// 原因: 在进行clear动作, 如果成功, 会把自己从垃圾收集机制维护的链表中摘除, 由于某些原因, 对象可能在clear的时候, 没有成功完成必要动作, 还不能被销毁, 所以放回去
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="k">if</span> <span class="p">(</span><span class="n">collectable</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_next</span> <span class="o">==</span> <span class="n">gc</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="cm">/* object is still alive, move it, it may die later */</span>
</span></span><span class="line"><span class="cl">              <span class="n">gc_list_move</span><span class="p">(</span><span class="n">gc</span><span class="p">,</span> <span class="n">old</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">              <span class="n">gc</span><span class="o">-&gt;</span><span class="n">gc</span><span class="p">.</span><span class="n">gc_refs</span> <span class="o">=</span> <span class="n">GC_REACHABLE</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 class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">=&gt;</span> <span class="err">来看下</span><span class="p">,</span> <span class="n">list的clear</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="n">list_clear</span><span class="p">(</span><span class="n">PyListObject</span> <span class="o">*</span><span class="n">a</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="n">Py_ssize_t</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">**</span><span class="n">item</span> <span class="o">=</span> <span class="n">a</span><span class="o">-&gt;</span><span class="n">ob_item</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">item</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* Because XDECREF can recursively invoke operations on
</span></span></span><span class="line"><span class="cl"><span class="cm">           this list, we make it empty first. */</span>
</span></span><span class="line"><span class="cl">        <span class="n">i</span> <span class="o">=</span> <span class="n">Py_SIZE</span><span class="p">(</span><span class="n">a</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">Py_SIZE</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">a</span><span class="o">-&gt;</span><span class="n">ob_item</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">a</span><span class="o">-&gt;</span><span class="n">allocated</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">while</span> <span class="p">(</span><span class="o">--</span><span class="n">i</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">)</span> <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"></span>            <span class="n">Py_XDECREF</span><span class="p">(</span><span class="n">item</span><span class="p">[</span><span class="n">i</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="n">PyMem_FREE</span><span class="p">(</span><span class="n">item</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="cm">/* Never fails; the return value can be ignored.
</span></span></span><span class="line"><span class="cl"><span class="cm">       Note that there is no guarantee that the list is actually empty
</span></span></span><span class="line"><span class="cl"><span class="cm">       at this point, because XDECREF may have populated it again! */</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</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></span><span class="line"><span class="cl"><span class="c1">// e.g. 处理list3, 调用其list_clear, 减少list4的引用计数, list4.ob_refcnt=0, 引发对象销毁, 调用list4的list_dealloc
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span>
</span></span><span class="line"><span class="cl"><span class="n">list_dealloc</span><span class="p">(</span><span class="n">PyListObject</span> <span class="o">*</span><span class="n">op</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="n">Py_ssize_t</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject_GC_UnTrack</span><span class="p">(</span><span class="n">op</span><span class="p">);</span>  <span class="c1">//  从可收集对象链表中去除, 会影响到list4所引用所有对象的引用计数, =&gt; list3.refcnt=0, list3的销毁动作也被触发
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    <span class="n">Py_TRASHCAN_SAFE_BEGIN</span><span class="p">(</span><span class="n">op</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">op</span><span class="o">-&gt;</span><span class="n">ob_item</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* Do it backwards, for Christian Tismer.
</span></span></span><span class="line"><span class="cl"><span class="cm">           There&#39;s a simple test case where somehow this reduces
</span></span></span><span class="line"><span class="cl"><span class="cm">           thrashing when a *very* large list is created and
</span></span></span><span class="line"><span class="cl"><span class="cm">           immediately deleted. */</span>
</span></span><span class="line"><span class="cl">        <span class="n">i</span> <span class="o">=</span> <span class="n">Py_SIZE</span><span class="p">(</span><span class="n">op</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">while</span> <span class="p">(</span><span class="o">--</span><span class="n">i</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">Py_XDECREF</span><span class="p">(</span><span class="n">op</span><span class="o">-&gt;</span><span class="n">ob_item</span><span class="p">[</span><span class="n">i</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="n">PyMem_FREE</span><span class="p">(</span><span class="n">op</span><span class="o">-&gt;</span><span class="n">ob_item</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="k">if</span> <span class="p">(</span><span class="n">numfree</span> <span class="o">&lt;</span> <span class="n">PyList_MAXFREELIST</span> <span class="o">&amp;&amp;</span> <span class="n">PyList_CheckExact</span><span class="p">(</span><span class="n">op</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">free_list</span><span class="p">[</span><span class="n">numfree</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">op</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="nf">Py_TYPE</span><span class="p">(</span><span class="n">op</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">tp_free</span><span class="p">((</span><span class="n">PyObject</span> <span class="o">*</span><span class="p">)</span><span class="n">op</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">Py_TRASHCAN_SAFE_END</span><span class="p">(</span><span class="n">op</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h4 id="7-gc逻辑">7. gc逻辑</h4>
<pre tabindex="0"><code>分配内存
-&gt; 发现超过阈值了
-&gt; 触发垃圾回收
-&gt; 将所有可收集对象链表放到一起
-&gt; 遍历, 计算有效引用计数
-&gt; 分成 有效引用计数=0 和 有效引用计数 &gt; 0 两个集合
-&gt; 大于0的, 放入到更老一代
-&gt; =0的, 执行回收
-&gt; 回收遍历容器内的各个元素, 减掉对应元素引用计数(破掉循环引用)
-&gt; 执行-1的逻辑, 若发现对象引用计数=0, 触发内存回收
-&gt; python底层内存管理机制回收内存
</code></pre><h2 id="分代回收">分代回收</h2>
<p>分代收集: 以空间换时间</p>
<p>思想: 将系统中的所有内存块根据其存货的时间划分为不同的集合, 每个集合就成为一个&quot;代&quot;, 垃圾收集的频率随着&quot;代&quot;的存活时间的增大而减小(活得越长的对象, 就越不可能是垃圾, 就应该减少去收集的频率)</p>
<p>Python中, 引入了分代收集, 总共三个&quot;代&quot;. Python 中, 一个代就是一个链表, 所有属于同一&quot;代&quot;的内存块都链接在同一个链表中</p>
<h3 id="表头数据结构">表头数据结构</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">gcmodule</span><span class="p">.</span><span class="n">c</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">struct</span> <span class="n">gc_generation</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">PyGC_Head</span> <span class="n">head</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="kt">int</span> <span class="n">threshold</span><span class="p">;</span> <span class="cm">/* collection threshold */</span>  <span class="c1">// 阈值
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="kt">int</span> <span class="n">count</span><span class="p">;</span> <span class="cm">/* count of allocations or collections of younger
</span></span></span><span class="line"><span class="cl"><span class="cm">                    generations */</span>    <span class="c1">// 实时个数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">};</span>
</span></span></code></pre></div><h3 id="三个代的定义">三个代的定义</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">  <span class="cp">#define NUM_GENERATIONS 3
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="cp">#define GEN_HEAD(n) (&amp;generations[n].head)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">  <span class="c1">//  三代都放到这个数组中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="cm">/* linked lists of container objects */</span>
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="k">struct</span> <span class="n">gc_generation</span> <span class="n">generations</span><span class="p">[</span><span class="n">NUM_GENERATIONS</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="cm">/* PyGC_Head,                               threshold,      count */</span>
</span></span><span class="line"><span class="cl">      <span class="p">{{{</span><span class="n">GEN_HEAD</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="n">GEN_HEAD</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="mi">0</span><span class="p">}},</span>           <span class="mi">700</span><span class="p">,</span>            <span class="mi">0</span><span class="p">},</span>    <span class="c1">//700个container, 超过立即触发垃圾回收机制
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="p">{{{</span><span class="n">GEN_HEAD</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="n">GEN_HEAD</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="mi">0</span><span class="p">}},</span>           <span class="mi">10</span><span class="p">,</span>             <span class="mi">0</span><span class="p">},</span>    <span class="c1">// 10个
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="p">{{{</span><span class="n">GEN_HEAD</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span> <span class="n">GEN_HEAD</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span> <span class="mi">0</span><span class="p">}},</span>           <span class="mi">10</span><span class="p">,</span>             <span class="mi">0</span><span class="p">},</span>    <span class="c1">// 10个
</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="n">PyGC_Head</span> <span class="o">*</span><span class="n">_PyGC_generation0</span> <span class="o">=</span> <span class="n">GEN_HEAD</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span></code></pre></div><h3 id="超过阈值-触发垃圾回收">超过阈值, 触发垃圾回收</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"> <span class="n">PyObject</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl">  <span class="nf">_PyObject_GC_Malloc</span><span class="p">(</span><span class="n">size_t</span> <span class="n">basicsize</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="c1">// 执行分配
</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 class="n">generations</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">count</span><span class="o">++</span><span class="p">;</span> <span class="cm">/* number of allocated GC objects */</span>  <span class="c1">//增加一个
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="k">if</span> <span class="p">(</span><span class="n">generations</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">count</span> <span class="o">&gt;</span> <span class="n">generations</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">threshold</span> <span class="o">&amp;&amp;</span> <span class="c1">// 发现大于预支了
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="n">enabled</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">          <span class="n">generations</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">threshold</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">          <span class="o">!</span><span class="n">collecting</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">          <span class="o">!</span><span class="n">PyErr_Occurred</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="n">collecting</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">collect_generations</span><span class="p">();</span>  <span class="c1">//  执行收集
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="n">collecting</span> <span class="o">=</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 class="n">op</span> <span class="o">=</span> <span class="n">FROM_GC</span><span class="p">(</span><span class="n">g</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="n">op</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="o">=&gt;</span> <span class="n">collect_generations</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">static</span> <span class="n">Py_ssize_t</span>
</span></span><span class="line"><span class="cl">  <span class="n">collect_generations</span><span class="p">(</span><span class="kt">void</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="kt">int</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">Py_ssize_t</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="cm">/* Find the oldest generation (highest numbered) where the count
</span></span></span><span class="line"><span class="cl"><span class="cm">       * exceeds the threshold.  Objects in the that generation and
</span></span></span><span class="line"><span class="cl"><span class="cm">       * generations younger than it will be collected. */</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"><span class="c1"></span>      <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="n">NUM_GENERATIONS</span><span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span>  <span class="c1">// 遍历所有generation
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="k">if</span> <span class="p">(</span><span class="n">generations</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">count</span> <span class="o">&gt;</span> <span class="n">generations</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">threshold</span><span class="p">)</span> <span class="p">{</span>  <span class="c1">// 如果超过了阈值
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="cm">/* Avoid quadratic performance degradation in number
</span></span></span><span class="line"><span class="cl"><span class="cm">                 of tracked objects. See comments at the beginning
</span></span></span><span class="line"><span class="cl"><span class="cm">                 of this file, and issue #4074.
</span></span></span><span class="line"><span class="cl"><span class="cm">              */</span>
</span></span><span class="line"><span class="cl">              <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">==</span> <span class="n">NUM_GENERATIONS</span> <span class="o">-</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">                  <span class="o">&amp;&amp;</span> <span class="n">long_lived_pending</span> <span class="o">&lt;</span> <span class="n">long_lived_total</span> <span class="o">/</span> <span class="mi">4</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                  <span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">n</span> <span class="o">=</span> <span class="n">collect</span><span class="p">(</span><span class="n">i</span><span class="p">);</span> <span class="c1">// 执行收集
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="k">break</span><span class="p">;</span>  <span class="c1">// notice: break了
</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 class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="n">n</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span></code></pre></div><h2 id="python-中的gc模块">Python 中的gc模块</h2>
<p>gc模块, 提供了观察和手动使用gc的接口</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">gc</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">gc</span><span class="o">.</span><span class="n">set_debug</span><span class="p">(</span><span class="n">gc</span><span class="o">.</span><span class="n">DEBUG_STATS</span> <span class="o">|</span> <span class="n">gc</span><span class="o">.</span><span class="n">DEBUG_LEAK</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">gc</span><span class="o">.</span><span class="n">collect</span><span class="p">()</span>
</span></span></code></pre></div><p>注意<code>__del__</code>给gc带来的影响</p>
]]></content>
		</item>
		
		<item>
			<title>我为什么要写博客</title>
			<link>https://wklken.me/posts/2015/09/26/why-i-keep-blogging.html</link>
			<pubDate>Sat, 26 Sep 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/09/26/why-i-keep-blogging.html</guid>
			<description>从七月中旬, 到八月底, 彻彻底底休息了一个半月, 九月初开始找工作, 目测国庆节后才能完成入职开始干活. 周期略长, 主要节点不好, 哎 最近在想一个问题</description>
			<content type="html"><![CDATA[<p>从七月中旬, 到八月底, 彻彻底底休息了一个半月, 九月初开始找工作, 目测国庆节后才能完成入职开始干活.</p>
<p>周期略长, 主要节点不好, 哎</p>
<p>最近在想一个问题, 我为什么要写博客?</p>
<p>开始写博客, 是在大学期间吧, 当时用的是新浪博客, 写了很多杂七杂八生活的点点滴滴, 临毕业前, 应该是刚从北京实习回学校, 2011年的4月1日(没错是愚人节), 注册了CSDN, 开始了写技术博客</p>
<p>13年7月从 CSDN 迁移走, 到了独立博客, 主要是那时候CSDN不支持markdown, 而且感觉自己维护一个更为便利些, 便买了主机, 申请了域名, 开始了独立博客的时光.</p>
<p>到现在, 也有两年了吧.</p>
<p>现在博客, 每个月UV大概1W的样子. 没有任何推广渠道, 全靠用户回访以及搜索引擎带来的流量.</p>
<p><img src="/imgs/blabla/blog_stats.png" alt="blog_stats"></p>
<p>偶尔隔一阵, 便会收到一份donation, 虽不多, 但是每次都会觉得很开心, 就像是隔一阵的惊喜, 也算是给自己的坚持注入了一些动力, 再次感谢各位:)</p>
<p>回去看了下CSDN, 访问接近90W 次, 排名834&hellip;&hellip;已经放弃维护好久了(两年多了), CSDN 留下了我那一两年青葱的时光.</p>
<p>很快, 工作四年多了, 时间是把杀猪刀, 改变了很多东西.</p>
<p>工作四年, 经历了三家公司, 不幸的是后面两家夭折了, 庆幸的是, 做了很多事情, 认识了很多朋友.</p>
<p>做的事情多, 杂, 也接触了很多东西, 感觉遗憾的是没能完完整整地经历一个公司的发展, 或者一套系统的进化过程.</p>
<p>四年, 改变了很多, 唯独没变的, 是偶尔会更新下博客, 输出一些东西, 吐吐槽神马的.</p>
<p>写博客的目的, 对我来说, 不外乎如是:</p>
<ul>
<li>记录</li>
</ul>
<p>活得越久, 越发现记忆的不靠谱.</p>
<p>很难会到过去某一刻, 某个时期的所思所想, 当时的心境, 当时的境遇.</p>
<p>所以, 记录, 写一写小结, 阶段性看看, 过去<code>无知</code>/<code>青葱</code>的自己, 也是必须的.</p>
<p>当然, 更有效的做法是, 每天坚持写写日记</p>
<ul>
<li>分享</li>
</ul>
<p>有一些坑, 自己踩过就好&hellip;&hellip;</p>
<p>而有一些观点, 可以发出来一起看看, 或多或少得到一些感受</p>
<ul>
<li>梳理思路</li>
</ul>
<p>我主要的知识管理工具是gollum, 以及evernote作为辅助, 在工作以及学习的过程中, 会碰到某个问题的方方面面, 各种细节, 散而且乱, 分布在不同地方, 而发个博客出来, 主要是可以花一两个小时, 对某块进行汇总, 然后重新梳理, 得到一个较为完整的内容, 顺手从笔记中清掉.</p>
<p>总之, 类似于一个精粹的过程.</p>
<p>还有, 例如在读python源码的时候, 读源码, 做笔记, 写注释, 同时还要参考书籍和文档, 往往当时读懂了, 觉得很清晰, 但是过阵子再回去看下, 面对一堆杂乱无从下手, 而出博文的过程, 会顺着思路走, 绘制一些结构图流程图, 再回头看的时候, 往往几分钟就能全盘了然</p>
<ul>
<li>搜索</li>
</ul>
<p>gollum和evernote的搜索其实做的都还不错, 但是有时候搜一个点, 得到不止一篇的结果, 往往要点好多次才能找到, 而发博文后, 顺手笔记中清掉, 此时在脑中已经有个印象, 某个点我详细写过什么东西, 那么剩下的只是一键打开博客, 找到那篇文章即可.</p>
<p>另一个好处是, gollum和evernote是本地的, 在别人电脑上处理问题的时候, 博客反正到哪都能访问到</p>
<hr>
<p>写博的过程, 更多的是自己记录, 思考, 成长的过程, 坚持下来, 感觉还是收获颇多.</p>
<p>明确目的, 不要把这个写博客的日常变成功利, 其实这次找工作的过程发现, 写博客或许加分了, 但是就是过简历那个环节而已, 太过功利会没必要, 花费大量时间并不值得. 更多的是, 做事情, 把事情做好, 然后得到一些积累和感悟, 而不是反之.</p>
<p>PS:  其实github开源也是一样, 做自己感兴趣的事情, 而不是去为了开源而开源</p>
<p>后续, 会主要关注两方面, 一块是偏底层的东西, 构建基础的知识体系, 另一块是做业务/项目过程中的总结.</p>
<p>编码不易, 且行且珍惜, 共勉</p>
<p>wklken</p>
<p>2015-09-26 于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>APUE笔记-第一章 UNIX基础知识</title>
			<link>https://wklken.me/posts/2015/09/23/apue-note-chapter-1.html</link>
			<pubDate>Wed, 23 Sep 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/09/23/apue-note-chapter-1.html</guid>
			<description>最近在找工作, 额, 从七月份一直休息到九月初, 开始找, 结果发现快到十月了, 节点不是很好, 要过两个节, 所以估计入职什么的要到节后了&amp;gt;_&amp;l</description>
			<content type="html"><![CDATA[<hr>
<p>最近在找工作, 额, 从七月份一直休息到九月初, 开始找, 结果发现快到十月了, 节点不是很好, 要过两个节, 所以估计入职什么的要到节后了&gt;_&lt;#</p>
<p>这几个月也在思考一些东西, 顺手也解决掉了<code>Python源码剖析</code>, 目前在逐步梳理笔记</p>
<p>发现自己首次做笔记还是太杂太乱, 堆在wiki里面一大坨的感觉, 还是要梳理画图, 思路更清晰些, 静候吧, 还是十篇左右的样子</p>
<p>读APUE, 做法比较<code>残忍</code>, 把书切开, 拆成一章一章地装订, 方便携带和阅读, 最后发现画满了一堆东西</p>
<p>发现还是不方便自己查阅, 所以还是决定重读, 转成笔记, 放到博客上方便搜索/查阅</p>
<p>对了, 代码之前只是下了看到的时候run下, 这次重读写写注释, 放到github了, <a href="https://github.com/wklken/apue.3e">链接</a></p>
<p>在此感谢作者W.Richard Stevens :) 这本书五星好评, 建议如果搞linux相关后端, 可以读下</p>
<hr>
<h1 id="第一章-unix基础知识">第一章 UNIX基础知识</h1>
<h2 id="unix体系结构-内核与系统调用">Unix体系结构: 内核与系统调用</h2>
<p><img src="/imgs/apue/1-1.jpg" alt="">￼</p>
<p>内核(kernel): 严格意义上, 将操作系统定义为一种软件, 它控制计算机硬件资源, 提供程序运行环境(相对较小, 位于环境的中心)</p>
<p>系统调用(system call): 内核的接口</p>
<p>关系: 公用函数库构建在系统调用接口之上, 应用软件既可以使用公用函数库, 也可以使用系统调用</p>
<h2 id="登陆">登陆</h2>
<p>口令文件<code>/etc/passwd</code>, 七个字段,冒号分隔</p>
<pre tabindex="0"><code>登录名:加密口令:数值用户 ID: 数值组 ID: 注释字段:起始目录:shell
</code></pre><p>加密口令已经转移到另一个文件</p>
<p>shell: 一个命令行解释器, 它读取用户输入, 然后执行命令</p>
<p>Linux 默认shell是Bourne-again shell</p>
<h2 id="文件和目录">文件和目录</h2>
<p>文件系统: 目录和文件组成的一种层次接口, 目录的起点称为根(root), 其名字是<code>\\</code></p>
<p>目录(directory)是一个包含许多目录项的文件</p>
<p>文件名(filename): 不能出现斜线<code>/</code>和空操作符<code>null</code>(好的习惯只使用印刷字符的一个子集作为文件名字符)</p>
<p>创建目录时, 会自动创建两个文件名, 当前目录<code>.</code> 以及父目录<code>..</code></p>
<p>路径名(pathname): 一个或多个以斜线分割的文件名序列. 以斜线开头的是绝对路径(absolute pathname), 否则是相对路径(relative pathname)</p>
<p><a href="https://github.com/wklken/apue.3e/blob/master/intro/ls1.c">ls.c源代码</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;apue.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;dirent.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 可编译执行
</span></span></span><span class="line"><span class="cl"><span class="c1">// apue.h, 包含某些标准头文件, 定义了很多常量及库函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</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="n">DIR</span>                <span class="o">*</span><span class="n">dp</span><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"></span>    <span class="k">struct</span> <span class="n">dirent</span>    <span class="o">*</span><span class="n">dirp</span><span class="p">;</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"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">argc</span> <span class="o">!=</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// apue.h自定义函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">err_quit</span><span class="p">(</span><span class="s">&#34;usage: ls directory_name&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 赋值后判断, opendir返回指向 DIR 结构体的指针
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">((</span><span class="n">dp</span> <span class="o">=</span> <span class="n">opendir</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]))</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// apue.h自定义函数err_sys
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">err_sys</span><span class="p">(</span><span class="s">&#34;can&#39;t open %s&#34;</span><span class="p">,</span> <span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 赋值后判断, 读每一项, 返回指向readdir结构的指针或null(没有目录项可读时)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">while</span> <span class="p">((</span><span class="n">dirp</span> <span class="o">=</span> <span class="n">readdir</span><span class="p">(</span><span class="n">dp</span><span class="p">))</span> <span class="o">!=</span> <span class="nb">NULL</span><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"></span>        <span class="n">printf</span><span class="p">(</span><span class="s">&#34;%s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">dirp</span><span class="o">-&gt;</span><span class="n">d_name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">closedir</span><span class="p">(</span><span class="n">dp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 终止程序, 0正常结束, 1-255出错
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">exit</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></code></pre></div><p>工作目录(working directory), 每个进程都有一个, 优势成为当前工作目录, 进程可以通过chdir函数更改其工作目录.</p>
<h2 id="输入和输出">输入和输出</h2>
<p>文件描述符(file descriptor), 一个小的负整数, 内核使用它标识一个特定进程正在访问的文件. 当内核打开或创建一个新文件时, 返回一个文件描述符, 在读写的时候使用</p>
<p>每当运行一个新程序时, 所有的shell都为其打开三个文件描述符: 标准输入(standard input), 标准输出(standard output)以及标准错误(standard error)</p>
<p>不用缓冲的I/O
函数open/read/write/lseek/close</p>
<p><a href="https://github.com/wklken/apue.3e/blob/master/intro/mycat.c">mycat.c源码</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;apue.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/* 缓冲区大小, 常量 */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define    BUFFSIZE    4096
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="nf">main</span><span class="p">(</span><span class="kt">void</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="kt">int</span>     <span class="n">n</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">char</span>    <span class="n">buf</span><span class="p">[</span><span class="n">BUFFSIZE</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* STDIN_FILENO/STDOUT_FILENO -&gt; apue.h -&gt; unisted.h, 标准输入文件描述符0/标准输出文件描述符1 */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* 从标准输入读, read返回读得的字节数, 读到末端返回0, 发生错误返回-1 */</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">((</span><span class="n">n</span> <span class="o">=</span> <span class="n">read</span><span class="p">(</span><span class="n">STDIN_FILENO</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">BUFFSIZE</span><span class="p">))</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* 写到标准输入 */</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">write</span><span class="p">(</span><span class="n">STDOUT_FILENO</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span> <span class="o">!=</span> <span class="n">n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">err_sys</span><span class="p">(</span><span class="s">&#34;write error&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* 发生错误 */</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">err_sys</span><span class="p">(</span><span class="s">&#34;read error&#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">exit</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></code></pre></div><p>标准I/O函数: 提供了一种对不用缓冲 I/O 函数的带缓冲接口, 可以无需担心如何选取最佳的缓冲区大小</p>
<p><a href="https://github.com/wklken/apue.3e/blob/master/intro/getcputc.c">getputc.c源码</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;apue.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* stdin/stdout -&gt; apue.h -&gt; stdio.h 标准输入文件/标准输出文件 */</span>
</span></span><span class="line"><span class="cl"><span class="cm">/* EOF为stdio.h中定义的常量 */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="nf">main</span><span class="p">(</span><span class="kt">void</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="kt">int</span> <span class="n">c</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* 从标准输入中读入一个字符 */</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">((</span><span class="n">c</span> <span class="o">=</span> <span class="n">getc</span><span class="p">(</span><span class="n">stdin</span><span class="p">))</span> <span class="o">!=</span> <span class="n">EOF</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* 输出到标准输出 */</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">putc</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">stdout</span><span class="p">)</span> <span class="o">==</span> <span class="n">EOF</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">err_sys</span><span class="p">(</span><span class="s">&#34;output error&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">ferror</span><span class="p">(</span><span class="n">stdin</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">err_sys</span><span class="p">(</span><span class="s">&#34;input error&#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">exit</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></code></pre></div><h2 id="程序和进程">程序和进程</h2>
<p>程序(program): 存放在磁盘上, 处于某个目录中的一个可执行文件.(使用6个exec函数中的一个有内核将程序读入存储器, 并使其执行)</p>
<p>进程(process): 程序的执行实例</p>
<p>进程ID(process ID), 每个进程都有一个唯一的数字标识符, 总是一非负整数</p>
<p><a href="https://github.com/wklken/apue.3e/blob/master/intro/hello.c">hello.c 源码</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;apue.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="nf">main</span><span class="p">(</span><span class="kt">void</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="cm">/* getpid得到进程pid */</span>
</span></span><span class="line"><span class="cl">    <span class="n">printf</span><span class="p">(</span><span class="s">&#34;hello world from process ID %ld</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="p">(</span><span class="kt">long</span><span class="p">)</span><span class="n">getpid</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="n">exit</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></code></pre></div><p>进程控制: 三个主要函数, fork/exec(六种变体)/waitpid</p>
<p><a href="https://github.com/wklken/apue.3e/blob/master/intro/shell1.c">shell1.c 源码</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;apue.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;sys/wait.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* fork创建一个新进程, 它被调用一次(由父进程调用), 返回两次(在父进程中返回子进程的进程ID, 在子进程中返回0) */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="nf">main</span><span class="p">(</span><span class="kt">void</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="kt">char</span>    <span class="n">buf</span><span class="p">[</span><span class="n">MAXLINE</span><span class="p">];</span>    <span class="cm">/* from apue.h */</span>
</span></span><span class="line"><span class="cl">    <span class="n">pid_t</span>    <span class="n">pid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span>        <span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">printf</span><span class="p">(</span><span class="s">&#34;%% &#34;</span><span class="p">);</span>    <span class="cm">/* print prompt (printf requires %% to print %) */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* 读入一行, 每一行命令会产生一个子进程用于执行 */</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="n">fgets</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">MAXLINE</span><span class="p">,</span> <span class="n">stdin</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* 去掉换行符 */</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="n">strlen</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\n&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">buf</span><span class="p">[</span><span class="n">strlen</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="cm">/* replace newline with null */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* 执行读入的命令 */</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* fork创建一个子进程, 返回&lt;0则表示fork发生了错误 */</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">((</span><span class="n">pid</span> <span class="o">=</span> <span class="n">fork</span><span class="p">())</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">err_sys</span><span class="p">(</span><span class="s">&#34;fork error&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* 对于子进程, fork返回的pid=0(父进程fork返回的pid&gt;0) */</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">pid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>        <span class="cm">/* child */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="cm">/* 调用execlp以执行从标准输入读入的命令 */</span>
</span></span><span class="line"><span class="cl">            <span class="cm">/* fork+exec组合, 是某些操作系统所称的产生(spawn)一个新的进程 */</span>
</span></span><span class="line"><span class="cl">            <span class="n">execlp</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">err_ret</span><span class="p">(</span><span class="s">&#34;couldn&#39;t execute: %s&#34;</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="cm">/* 退出 */</span>
</span></span><span class="line"><span class="cl">            <span class="n">exit</span><span class="p">(</span><span class="mi">127</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="cm">/* 父进程, 等待子进程终止 */</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* pid为子进程id, status为子进程终止状态(用于判断其实如何终止的) */</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* parent */</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">((</span><span class="n">pid</span> <span class="o">=</span> <span class="n">waitpid</span><span class="p">(</span><span class="n">pid</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">status</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">err_sys</span><span class="p">(</span><span class="s">&#34;waitpid error&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">printf</span><span class="p">(</span><span class="s">&#34;%% &#34;</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="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
</span></span></code></pre></div><p>通常, 一个进程只有一个控制线程(thread), 同一时刻只执行一组机器指令.(对于某些问题, 如果不同部分各使用一个控制线程, 那么整个问题解决相对容易, 多个控制线程也能充分利用多处理器系统的并行性)</p>
<p>在一个进程内的所有线程共享同一地址空间/文件描述符/栈以及与进程相关的属性(所以各线程在访问共享数据时需要采取同步措施以避免不一致性)</p>
<p>线程也用 ID 标识, 但只在其所属进程内起作用</p>
<h2 id="出错处理">出错处理</h2>
<p>UNIX 函数出错的时候, 常常返回一个负值, 而整型变量errno通常被设置为含有附加信息的一个值</p>
<p><code>errno.h</code>中, 定义了符号errno以及可以赋予它的各种常量.(errno(3)手册中)</p>
<p>对于errno两条规则</p>
<pre tabindex="0"><code>1. 如果没有出错, 其值则不会被一个例程清楚, 因此, 仅当函数的返回值指明出错时, 才检验其值
2. 任一函数都不会将errno值设置为0, 在errnoh中定义的所有常量都不为0
</code></pre><p><a href="https://github.com/wklken/apue.3e/blob/master/intro/testerror.c">testerror.c 源码</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;apue.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;errno.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* strerror, 将errnum映射为一个出错信息字符串, 并且返回此字符串的指针 */</span>
</span></span><span class="line"><span class="cl"><span class="cm">/* perror, 基于errno的当前值, 在标准出错上产生一条出错信息, 然后返回 */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</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="cm">/* 常量 EACCES / ENOENT */</span>
</span></span><span class="line"><span class="cl">    <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">&#34;EACCES: %s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">strerror</span><span class="p">(</span><span class="n">EACCES</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">    <span class="n">errno</span> <span class="o">=</span> <span class="n">ENOENT</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">perror</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">    <span class="n">exit</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></code></pre></div><p>出错恢复: errno.h中定义的各种出错分为致命性和非致命性两类.</p>
<p>致命性出错: 无法执行恢复动作, 最多只能在屏幕上打印一条出错信息, 或写入日志, 然后终止</p>
<p>非致命性出错: 可以较为妥善地处理</p>
<h2 id="用户标识">用户标识</h2>
<p>用户ID(user ID), 数值, 系统中标识各个不同的用户, 每个用户唯一(用户不能更改其用户 ID)</p>
<p>用户 ID 为0的用户为根( root) 或超级用户(superuser)</p>
<p>组ID(group ID), 一个数值, 指定用户登陆名时同时指定的. 允许同组各个成员之间共享资源</p>
<p>组文件将组名映射为数字 ID, <code>/etc/group</code></p>
<p>口令文件包含: 登录名 = 用户 ID 的映射</p>
<p>组文件包含: 组名 = 组ID 的映射</p>
<p>打印用户ID和组ID</p>
<p><a href="https://github.com/wklken/apue.3e/blob/master/intro/uidgid.c">uidgid.c 源码</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;apue.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="nf">main</span><span class="p">(</span><span class="kt">void</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="cm">/* getuid / getgid */</span>
</span></span><span class="line"><span class="cl">    <span class="n">printf</span><span class="p">(</span><span class="s">&#34;uid = %d, gid = %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">getuid</span><span class="p">(),</span> <span class="n">getgid</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="n">exit</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></code></pre></div><p>附加组ID</p>
<p>允许一个用户属于多个组, 最多16个.</p>
<h2 id="信号">信号</h2>
<p>信号(signal): 通知进程已发生某种情况的一种技术.</p>
<p>e.g. 一个进程执行除法操作, 其除数为0, 则将名为SIGFPE的信号发给该进程</p>
<p>进程如何处理信号?</p>
<pre tabindex="0"><code>1. 忽略该信号
2. 按系统默认方式处理.
3. 提供一个函数, 信号发生时则调用该函数(捕捉信号)
</code></pre><p>信号捕捉</p>
<p><a href="https://github.com/wklken/apue.3e/blob/master/intro/shell2.c">shell2.c 源码</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;apue.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;sys/wait.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/* 声明信号处理函数 */</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="kt">void</span>    <span class="nf">sig_int</span><span class="p">(</span><span class="kt">int</span><span class="p">);</span>        <span class="cm">/* our signal-catching function */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="nf">main</span><span class="p">(</span><span class="kt">void</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="kt">char</span>    <span class="n">buf</span><span class="p">[</span><span class="n">MAXLINE</span><span class="p">];</span>    <span class="cm">/* from apue.h */</span>
</span></span><span class="line"><span class="cl">    <span class="n">pid_t</span>    <span class="n">pid</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span>        <span class="n">status</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="cm">/* signal函数, 指定SIGINT 到处理函数 sig_int */</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* 机制, 类似于直接注册到了进程, 观察是否异常发生后捕获处理 */</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">signal</span><span class="p">(</span><span class="n">SIGINT</span><span class="p">,</span> <span class="n">sig_int</span><span class="p">)</span> <span class="o">==</span> <span class="n">SIG_ERR</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">err_sys</span><span class="p">(</span><span class="s">&#34;signal error&#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">printf</span><span class="p">(</span><span class="s">&#34;%% &#34;</span><span class="p">);</span>    <span class="cm">/* print prompt (printf requires %% to print %) */</span>
</span></span><span class="line"><span class="cl">    <span class="k">while</span> <span class="p">(</span><span class="n">fgets</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">MAXLINE</span><span class="p">,</span> <span class="n">stdin</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">buf</span><span class="p">[</span><span class="n">strlen</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="sc">&#39;\n&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">buf</span><span class="p">[</span><span class="n">strlen</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="cm">/* replace newline with null */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">((</span><span class="n">pid</span> <span class="o">=</span> <span class="n">fork</span><span class="p">())</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">err_sys</span><span class="p">(</span><span class="s">&#34;fork error&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">pid</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>        <span class="cm">/* child */</span>
</span></span><span class="line"><span class="cl">            <span class="n">execlp</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">err_ret</span><span class="p">(</span><span class="s">&#34;couldn&#39;t execute: %s&#34;</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="n">exit</span><span class="p">(</span><span class="mi">127</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="cm">/* parent */</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">((</span><span class="n">pid</span> <span class="o">=</span> <span class="n">waitpid</span><span class="p">(</span><span class="n">pid</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">status</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">err_sys</span><span class="p">(</span><span class="s">&#34;waitpid error&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">printf</span><span class="p">(</span><span class="s">&#34;%% &#34;</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="n">exit</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></span><span class="line"><span class="cl"><span class="cm">/* 处理函数, 打印 */</span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span>
</span></span><span class="line"><span class="cl"><span class="nf">sig_int</span><span class="p">(</span><span class="kt">int</span> <span class="n">signo</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="n">printf</span><span class="p">(</span><span class="s">&#34;interrupt</span><span class="se">\n</span><span class="s">%% &#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="时间值">时间值</h2>
<p>UNIX系统两种不同的时间值</p>
<p>日历时间, time_t, 从1970年1月1日00:00:00以来的国际标准时间UTC锁经过的秒数</p>
<p>进程时间, clock_t, CPU时间, 度量进程使用的中央处理器资源, 以始终滴答计算</p>
<p>Unix系统使用三个进程时间</p>
<pre tabindex="0"><code>1. 时钟时间, 总时间, real
2. 用户cpu时间, 执行用户指令耗时, user
3. 系统cpu时间, 执行内核程序耗时, sys
</code></pre><h2 id="系统调用和库函数">系统调用和库函数</h2>
<p>所有操作系统都提供多种服务的入口点(系统调用), 程序由此想内核请求服务</p>
<p>UNIX所使用的技术是为每个系统调用在标准 C 库中设置一个具有同样名字的函数. 用户进程用标准 C 调用序列来调用这些函数, 然后函数又用系统所要求的技术调用相应的内核服务</p>
<p>系统调用通常提供一种最小接口，而库函数通常提供比较复杂的功能。</p>
<p><img src="/imgs/apue/1-2.jpg" alt="">￼</p>
<p>系统调用: 最小接口, 单一职责, 不可替换</p>
<p>C库函数: 复杂功能, 可替换, 可自行定义</p>
]]></content>
		</item>
		
		<item>
			<title>Python源码阅读-闭包的实现</title>
			<link>https://wklken.me/posts/2015/09/04/python-source-closure.html</link>
			<pubDate>Fri, 04 Sep 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/09/04/python-source-closure.html</guid>
			<description>闭包 e.g. def add(x): def do_add(value): return x + value return do_add add_5 = add(5) print add_5(1) # 6 print add_5(2) # 7 需要回答, 什么是闭包, CPython底层是如何实现的? PyCodeObject typedef struct { PyObject_HEAD int co_argcount; /* #arguments, except *args */ int co_nlocals; /* #local variables */ int</description>
			<content type="html"><![CDATA[<h3 id="闭包">闭包</h3>
<p>e.g.</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">add</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">do_add</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">value</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">do_add</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">add_5</span> <span class="o">=</span> <span class="n">add</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="n">add_5</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>  <span class="c1"># 6</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span> <span class="n">add_5</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>  <span class="c1"># 7</span>
</span></span></code></pre></div><p>需要回答, 什么是闭包, CPython底层是如何实现的?</p>
<h3 id="pycodeobject">PyCodeObject</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject_HEAD</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">co_argcount</span><span class="p">;</span>		<span class="cm">/* #arguments, except *args */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">co_nlocals</span><span class="p">;</span>		    <span class="cm">/* #local variables */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">co_stacksize</span><span class="p">;</span>		<span class="cm">/* #entries needed for evaluation stack */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">co_flags</span><span class="p">;</span>		    <span class="cm">/* CO_..., see below */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">*</span><span class="n">co_code</span><span class="p">;</span>		<span class="cm">/* instruction opcodes */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">*</span><span class="n">co_consts</span><span class="p">;</span>	<span class="cm">/* list (constants used) */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">*</span><span class="n">co_names</span><span class="p">;</span>		<span class="cm">/* list of strings (names used) */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">*</span><span class="n">co_varnames</span><span class="p">;</span>	<span class="cm">/* tuple of strings (local variable names) */</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"><span class="c1"></span>    <span class="n">PyObject</span> <span class="o">*</span><span class="n">co_freevars</span><span class="p">;</span>	<span class="cm">/* tuple of strings (free variable names) */</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 保存嵌套作用域中使用的变量名集合, (编译时就知道的! 包含嵌套函数时有用)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">PyObject</span> <span class="o">*</span><span class="n">co_cellvars</span><span class="p">;</span>      <span class="cm">/* tuple of strings (cell variable names) */</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="cm">/* The rest doesn&#39;t count for hash/cmp */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">*</span><span class="n">co_filename</span><span class="p">;</span>	<span class="cm">/* string (where it was loaded from) */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">*</span><span class="n">co_name</span><span class="p">;</span>		<span class="cm">/* string (name, for reference) */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">co_firstlineno</span><span class="p">;</span>		<span class="cm">/* first source line number */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">*</span><span class="n">co_lnotab</span><span class="p">;</span>	<span class="cm">/* string (encoding addr&lt;-&gt;lineno mapping) See
</span></span></span><span class="line"><span class="cl"><span class="cm">				   Objects/lnotab_notes.txt for details. */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="o">*</span><span class="n">co_zombieframe</span><span class="p">;</span>     <span class="cm">/* for optimization only (see frameobject.c) */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">*</span><span class="n">co_weakreflist</span><span class="p">;</span>   <span class="cm">/* to support weakrefs to code objects */</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">PyCodeObject</span><span class="p">;</span>
</span></span></code></pre></div><p>我们关注两个, <code>co_freevars</code> 和 <code>co_cellvars</code></p>
<pre tabindex="0"><code>co_freevars, 保存使用了的外层作用域中的变量名集合 (编译时就知道的! 被嵌套的时候有用)

co_cellvars, 保存嵌套作用域中使用的变量名集合, (编译时就知道的! 包含嵌套函数时有用)
</code></pre><p>对于我们上面的那个示例, <code>add</code>是外层函数, <code>do_add</code>是嵌套函数, 我们可以通过<code>func_code</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">add</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>    <span class="c1"># 外层函数</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 外层函数, 没有使用了外层作用域变量, 被嵌套函数使用了&#39;x&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="n">add</span><span class="o">.</span><span class="n">func_code</span><span class="o">.</span><span class="n">co_freevars</span>    <span class="c1"># ()</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="n">add</span><span class="o">.</span><span class="n">func_code</span><span class="o">.</span><span class="n">co_cellvars</span>    <span class="c1"># (&#39;x&#39;,)</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">do_add</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>  <span class="c1"># 嵌套函数</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">value</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 内层函数, 使用了外层作用域便令&#39;x&#39;, 没有嵌套函数故嵌套作用域变量名集合空</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="n">do_add</span><span class="o">.</span><span class="n">func_code</span><span class="o">.</span><span class="n">co_freevars</span> <span class="c1"># (&#39;x&#39;,)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="n">do_add</span><span class="o">.</span><span class="n">func_code</span><span class="o">.</span><span class="n">co_cellvars</span> <span class="c1"># ()</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">do_add</span>
</span></span></code></pre></div><p>此时图示</p>
<p><img src="/imgs/python-source/python-closure.png" alt="closure"></p>
<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="n">add</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
</span></span></code></pre></div><p>此时<code>x=5</code>, 这个是在<code>add</code>的名字空间里面的, 那么, <code>x=5</code>是怎么传递到嵌套函数内? 嵌套函数又是如何知晓<code>x</code>的值?</p>
<p>记住这两个问题, 然后我们首先来看一个新的数据结构</p>
<h3 id="pycellobject">PyCellObject</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">  <span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject_HEAD</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">*</span><span class="n">ob_ref</span><span class="p">;</span>   <span class="cm">/* Content of the cell or NULL when empty */</span> <span class="o">=&gt;</span> <span class="err">指向一个</span><span class="n">PyObject</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="n">PyCellObject</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="n">PyObject</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl">  <span class="nf">PyCell_New</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">obj</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="n">PyCellObject</span> <span class="o">*</span><span class="n">op</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">op</span> <span class="o">=</span> <span class="p">(</span><span class="n">PyCellObject</span> <span class="o">*</span><span class="p">)</span><span class="n">PyObject_GC_New</span><span class="p">(</span><span class="n">PyCellObject</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">PyCell_Type</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">op</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="n">op</span><span class="o">-&gt;</span><span class="n">ob_ref</span> <span class="o">=</span> <span class="n">obj</span><span class="p">;</span>  <span class="c1">//建立关系
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="n">Py_XINCREF</span><span class="p">(</span><span class="n">obj</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">_PyObject_GC_TRACK</span><span class="p">(</span><span class="n">op</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="p">)</span><span class="n">op</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span></code></pre></div><p>这是个很简单的基本对象, 有一个<code>ob_ref</code>指向另一个<code>PyObject</code>, 仅此而已</p>
<p>图示</p>
<p><img src="/imgs/python-source/python-closure2.png" alt="closure"></p>
<p>作用呢?</p>
<h3 id="值的确认与传递过程">值的确认与传递过程</h3>
<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="n">add</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
</span></span></code></pre></div><p>此时, 开始调用函数</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">CALL_FUNCTION</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">=&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="n">call_function</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sp</span><span class="p">,</span> <span class="n">oparg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">=&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="n">fast_function</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="n">pp_stack</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">na</span><span class="p">,</span> <span class="n">nk</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="nf">PyEval_EvalCodeEx</span><span class="p">(</span><span class="n">co</span><span class="p">,</span> <span class="n">globals</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                               <span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="p">)</span><span class="nb">NULL</span><span class="p">,</span> <span class="p">(</span><span class="o">*</span><span class="n">pp_stack</span><span class="p">)</span><span class="o">-</span><span class="n">n</span><span class="p">,</span> <span class="n">na</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                               <span class="p">(</span><span class="o">*</span><span class="n">pp_stack</span><span class="p">)</span><span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">nk</span><span class="p">,</span> <span class="n">nk</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">nd</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                               <span class="n">PyFunction_GET_CLOSURE</span><span class="p">(</span><span class="n">func</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">PyEval_EvalCodeEx</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">add</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span> <span class="err">此时其</span><span class="n">co_cellvars</span> <span class="o">=</span> <span class="p">(</span><span class="sc">&#39;x&#39;</span><span class="p">,)</span> <span class="err">非空</span><span class="p">,</span> <span class="err">将会执行的逻辑代码</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="cm">/* Allocate and initialize storage for cell vars, and copy free
</span></span></span><span class="line"><span class="cl"><span class="cm">         vars into frame.  This isn&#39;t too efficient right now. */</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="n">PyTuple_GET_SIZE</span><span class="p">(</span><span class="n">co</span><span class="o">-&gt;</span><span class="n">co_cellvars</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="kt">int</span> <span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">,</span> <span class="n">nargs</span><span class="p">,</span> <span class="n">found</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="kt">char</span> <span class="o">*</span><span class="n">cellname</span><span class="p">,</span> <span class="o">*</span><span class="n">argname</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">PyObject</span> <span class="o">*</span><span class="n">c</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="n">nargs</span> <span class="o">=</span> <span class="n">co</span><span class="o">-&gt;</span><span class="n">co_argcount</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="n">co</span><span class="o">-&gt;</span><span class="n">co_flags</span> <span class="o">&amp;</span> <span class="n">CO_VARARGS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">              <span class="n">nargs</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="n">co</span><span class="o">-&gt;</span><span class="n">co_flags</span> <span class="o">&amp;</span> <span class="n">CO_VARKEYWORDS</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">              <span class="n">nargs</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="cm">/* Initialize each cell var, taking into account
</span></span></span><span class="line"><span class="cl"><span class="cm">             cell vars that are initialized from arguments.
</span></span></span><span class="line"><span class="cl"><span class="cm">
</span></span></span><span class="line"><span class="cl"><span class="cm">             Should arrange for the compiler to put cellvars
</span></span></span><span class="line"><span class="cl"><span class="cm">             that are arguments at the beginning of the cellvars
</span></span></span><span class="line"><span class="cl"><span class="cm">             list so that we can march over it more efficiently?
</span></span></span><span class="line"><span class="cl"><span class="cm">          */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="c1">// for 循环遍历 co_cellvars = (&#39;x&#39;, ), i = 0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">PyTuple_GET_SIZE</span><span class="p">(</span><span class="n">co</span><span class="o">-&gt;</span><span class="n">co_cellvars</span><span class="p">);</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">              <span class="c1">// cellname = &#39;x&#39;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="n">cellname</span> <span class="o">=</span> <span class="n">PyString_AS_STRING</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                  <span class="n">PyTuple_GET_ITEM</span><span class="p">(</span><span class="n">co</span><span class="o">-&gt;</span><span class="n">co_cellvars</span><span class="p">,</span> <span class="n">i</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">              <span class="n">found</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">              <span class="c1">// 遍历函数的参数变量, narg=1, j=0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="k">for</span> <span class="p">(</span><span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">nargs</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</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"><span class="c1"></span>                  <span class="n">argname</span> <span class="o">=</span> <span class="n">PyString_AS_STRING</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                      <span class="n">PyTuple_GET_ITEM</span><span class="p">(</span><span class="n">co</span><span class="o">-&gt;</span><span class="n">co_varnames</span><span class="p">,</span> <span class="n">j</span><span class="p">));</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"><span class="c1"></span>                  <span class="k">if</span> <span class="p">(</span><span class="n">strcmp</span><span class="p">(</span><span class="n">cellname</span><span class="p">,</span> <span class="n">argname</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                      <span class="c1">// new 一个 PyCellObject, ob_ref指向变量的PyObject
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                      <span class="n">c</span> <span class="o">=</span> <span class="n">PyCell_New</span><span class="p">(</span><span class="n">GETLOCAL</span><span class="p">(</span><span class="n">j</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">                      <span class="k">if</span> <span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                          <span class="k">goto</span> <span class="n">fail</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                      <span class="c1">// #define GETLOCAL(i)     (fastlocals[i])
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                      <span class="c1">// fastlocals = f-&gt;f_localsplus;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                      <span class="c1">// 即 f-&gt;f_localsplus[co-&gt;co_nlocals + i] = c, 相当于放到下一层freevars变量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                      <span class="n">GETLOCAL</span><span class="p">(</span><span class="n">co</span><span class="o">-&gt;</span><span class="n">co_nlocals</span> <span class="o">+</span> <span class="n">i</span><span class="p">)</span> <span class="o">=</span> <span class="n">c</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                      <span class="n">found</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                      <span class="k">break</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">// 没有匹配, 给个指向NULL的PyCellObject, 先New一个对象占位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="k">if</span> <span class="p">(</span><span class="n">found</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                  <span class="n">c</span> <span class="o">=</span> <span class="n">PyCell_New</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                  <span class="k">if</span> <span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                      <span class="k">goto</span> <span class="n">fail</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                  <span class="n">SETLOCAL</span><span class="p">(</span><span class="n">co</span><span class="o">-&gt;</span><span class="n">co_nlocals</span> <span class="o">+</span> <span class="n">i</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span> <span class="c1">//注意内存地址
</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 class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span></code></pre></div><p>逻辑即, 如果发现当前函数<code>co_cellvars</code>非空, 即表示存在被内层函数调用的变量, 那么遍历这个<code>co_cellvars</code>集合, 拿到集合中每个变量名在当前名字空间中的值, 然后放到当前函数的<code>f-&gt;f_localsplus</code>中.</p>
<p>这里, 我们可以知道<code>x=5</code>被放进去了</p>
<p>为什么放到<code>f-&gt;f_localsplus</code>中呢?</p>
<h3 id="看看pyframeobject">看看PyFrameObject</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">_frame</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject_VAR_HEAD</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">_frame</span> <span class="o">*</span><span class="n">f_back</span><span class="p">;</span>	<span class="cm">/* previous frame, or NULL */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyCodeObject</span> <span class="o">*</span><span class="n">f_code</span><span class="p">;</span>	<span class="cm">/* code segment */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">*</span><span class="n">f_builtins</span><span class="p">;</span>	<span class="cm">/* builtin symbol table (PyDictObject) */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">*</span><span class="n">f_globals</span><span class="p">;</span>	<span class="cm">/* global symbol table (PyDictObject) */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">*</span><span class="n">f_locals</span><span class="p">;</span>		<span class="cm">/* local symbol table (any mapping) */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">**</span><span class="n">f_valuestack</span><span class="p">;</span>	<span class="cm">/* points after the last local */</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* Next free slot in f_valuestack.  Frame creation sets to f_valuestack.
</span></span></span><span class="line"><span class="cl"><span class="cm">       Frame evaluation usually NULLs it, but a frame that yields sets it
</span></span></span><span class="line"><span class="cl"><span class="cm">       to the current stack top. */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">**</span><span class="n">f_stacktop</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">*</span><span class="n">f_trace</span><span class="p">;</span>		<span class="cm">/* Trace function */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* If an exception is raised in this frame, the next three are used to
</span></span></span><span class="line"><span class="cl"><span class="cm">     * record the exception info (if any) originally in the thread state.  See
</span></span></span><span class="line"><span class="cl"><span class="cm">     * comments before set_exc_info() -- it&#39;s not obvious.
</span></span></span><span class="line"><span class="cl"><span class="cm">     * Invariant:  if _type is NULL, then so are _value and _traceback.
</span></span></span><span class="line"><span class="cl"><span class="cm">     * Desired invariant:  all three are NULL, or all three are non-NULL.  That
</span></span></span><span class="line"><span class="cl"><span class="cm">     * one isn&#39;t currently true, but &#34;should be&#34;.
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">*</span><span class="n">f_exc_type</span><span class="p">,</span> <span class="o">*</span><span class="n">f_exc_value</span><span class="p">,</span> <span class="o">*</span><span class="n">f_exc_traceback</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">PyThreadState</span> <span class="o">*</span><span class="n">f_tstate</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">f_lasti</span><span class="p">;</span>		<span class="cm">/* Last instruction if called */</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* Call PyFrame_GetLineNumber() instead of reading this field
</span></span></span><span class="line"><span class="cl"><span class="cm">       directly.  As of 2.3 f_lineno is only valid when tracing is
</span></span></span><span class="line"><span class="cl"><span class="cm">       active (i.e. when f_trace is set).  At other times we use
</span></span></span><span class="line"><span class="cl"><span class="cm">       PyCode_Addr2Line to calculate the line from the current
</span></span></span><span class="line"><span class="cl"><span class="cm">       bytecode index. */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">f_lineno</span><span class="p">;</span>		<span class="cm">/* Current line number */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">f_iblock</span><span class="p">;</span>		<span class="cm">/* index in f_blockstack */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyTryBlock</span> <span class="n">f_blockstack</span><span class="p">[</span><span class="n">CO_MAXBLOCKS</span><span class="p">];</span> <span class="cm">/* for try and loop blocks */</span>
</span></span><span class="line"><span class="cl">    <span class="n">PyObject</span> <span class="o">*</span><span class="n">f_localsplus</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>	<span class="cm">/* locals+stack, dynamically sized */</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">PyFrameObject</span><span class="p">;</span>
</span></span></code></pre></div><p>注意<code>f_localsplus</code></p>
<pre tabindex="0"><code>f_localsplus为一个PyObject的指针数组，大小为1。

c语言中, 当申请一个大小超过sizeof(PyFrameObject)的结构体对象时，超过的部分就自动分配给f_localsplus
</code></pre><p>创建过程</p>
<p>在<code>call_function</code>的时候, <code>new</code>了一个PyFrameObject</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">f</span> <span class="o">=</span> <span class="n">PyFrame_New</span><span class="p">(</span><span class="n">tstate</span><span class="p">,</span> <span class="n">co</span><span class="p">,</span> <span class="n">globals</span><span class="p">,</span> <span class="n">locals</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">PyFrameObject</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl"><span class="n">PyFrame_New</span><span class="p">(</span><span class="n">PyThreadState</span> <span class="o">*</span><span class="n">tstate</span><span class="p">,</span> <span class="n">PyCodeObject</span> <span class="o">*</span><span class="n">code</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">globals</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">PyObject</span> <span class="o">*</span><span class="n">locals</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="n">Py_ssize_t</span> <span class="n">extras</span><span class="p">,</span> <span class="n">ncells</span><span class="p">,</span> <span class="n">nfrees</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">ncells</span> <span class="o">=</span> <span class="n">PyTuple_GET_SIZE</span><span class="p">(</span><span class="n">code</span><span class="o">-&gt;</span><span class="n">co_cellvars</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">nfrees</span> <span class="o">=</span> <span class="n">PyTuple_GET_SIZE</span><span class="p">(</span><span class="n">code</span><span class="o">-&gt;</span><span class="n">co_freevars</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">extras</span> <span class="o">=</span> <span class="n">code</span><span class="o">-&gt;</span><span class="n">co_stacksize</span> <span class="o">+</span> <span class="n">code</span><span class="o">-&gt;</span><span class="n">co_nlocals</span> <span class="o">+</span> <span class="n">ncells</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">            <span class="n">nfrees</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">f</span> <span class="o">=</span> <span class="n">PyObject_GC_NewVar</span><span class="p">(</span><span class="n">PyFrameObject</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">PyFrame_Type</span><span class="p">,</span> <span class="n">extras</span><span class="p">);</span>
</span></span></code></pre></div><p>即</p>
<pre tabindex="0"><code>f_localsplus =&gt; 局部变量 + cell对象 + free对象 + 运行时栈
</code></pre><p>原因: 因为函数中的局部变量总是固定不变的, 在编译时就能确定局部变量使用的内存空间的位置, 也能确定访问局部变量的字节码应该如何访问内存, 有了这些信息, Python就能借助静态的方法实现局部变量, 而不是动态查找PyDictObject, 提高执行效率</p>
<p><img src="/imgs/python-source/python-closure3.png" alt="closure"></p>
<h3 id="示例函数的f_localsplus">示例函数的f_localsplus</h3>
<p>看一下上面赋值用的宏定义</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">  <span class="n">fastlocals</span> <span class="o">=</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">f_localsplus</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="cp">#define GETLOCAL(i)     (fastlocals[i])
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="cp">#define SETLOCAL(i, value)      do { PyObject *tmp = GETLOCAL(i); \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                       GETLOCAL(i) = value; \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                       Py_XDECREF(tmp); } while (0)
</span></span></span></code></pre></div><p>最终得到</p>
<p><img src="/imgs/python-source/python-closure4.png" alt="closure"></p>
<p>接下去呢? <code>CALL_FUNCTION</code>最后怎么处理将cell传入嵌套函数?</p>
<h3 id="传递">传递</h3>
<p><code>CALL_FUNCTION</code> 完成<code>new</code>一个<code>PyFrameObject</code>之后,</p>
<p>最终执行这个frame</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">retval</span> <span class="o">=</span> <span class="n">PyEval_EvalFrameEx</span><span class="p">(</span><span class="n">f</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
</span></span></code></pre></div><p>PyEval_EvalFrameEx</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">  <span class="n">PyObject</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl">  <span class="nf">PyEval_EvalFrameEx</span><span class="p">(</span><span class="n">PyFrameObject</span> <span class="o">*</span><span class="n">f</span><span class="p">,</span> <span class="kt">int</span> <span class="n">throwflag</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="n">fastlocals</span> <span class="o">=</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">f_localsplus</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">freevars</span> <span class="o">=</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">f_localsplus</span> <span class="o">+</span> <span class="n">co</span><span class="o">-&gt;</span><span class="n">co_nlocals</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="o">=&gt;</span> <span class="err">此时涉及</span><span class="n">op_code的执行了</span>
</span></span></code></pre></div><p>查看一下dis的结果</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">add</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">do_add</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">value</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">do_add</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="mi">5</span>           <span class="mi">0</span> <span class="n">LOAD_CLOSURE</span>             <span class="mi">0</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">              <span class="mi">3</span> <span class="n">BUILD_TUPLE</span>              <span class="mi">1</span>
</span></span><span class="line"><span class="cl">              <span class="mi">6</span> <span class="n">LOAD_CONST</span>               <span class="mi">1</span> <span class="p">(</span><span class="o">&lt;</span><span class="n">code</span> <span class="nb">object</span> <span class="n">do_add</span> <span class="n">at</span> <span class="mh">0x10c9cec30</span><span class="p">,</span> <span class="n">file</span> <span class="s2">&#34;a.py&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">5</span><span class="o">&gt;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">              <span class="mi">9</span> <span class="n">MAKE_CLOSURE</span>             <span class="mi">0</span>
</span></span><span class="line"><span class="cl">             <span class="mi">12</span> <span class="n">STORE_FAST</span>               <span class="mi">1</span> <span class="p">(</span><span class="n">do_add</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="mi">7</span>          <span class="mi">15</span> <span class="n">LOAD_FAST</span>                <span class="mi">1</span> <span class="p">(</span><span class="n">do_add</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">             <span class="mi">18</span> <span class="n">RETURN_VALUE</span>
</span></span></code></pre></div><p>首先<code>LOAD_CLOSURE 0</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">          <span class="k">case</span> <span class="nl">LOAD_CLOSURE</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">              <span class="n">x</span> <span class="o">=</span> <span class="n">freevars</span><span class="p">[</span><span class="n">oparg</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">              <span class="n">Py_INCREF</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">              <span class="n">PUSH</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">              <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="k">continue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">入栈</span><span class="p">,</span> <span class="err">此时得到一个</span><span class="n">PyCellObject</span><span class="p">,</span> <span class="err">指向</span><span class="mi">2</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="sc">&#39;x&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">LOAD_CLOSURE</span> <span class="err">在编译时会根据嵌套函数中</span> <span class="n">co_freevars</span><span class="p">,</span> <span class="err">决定了取得参数位置和个数</span>
</span></span></code></pre></div><p>然后, <code>BUILD_TUPLE</code>, 将cell对象打包成tuple, 得到<code>('x', )</code></p>
<p>然后, 开始, 载入嵌套函数<code>do_add</code>, 入栈</p>
<p>调用<code>MAKE_CLOSURE</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">          <span class="k">case</span> <span class="nl">MAKE_CLOSURE</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="n">v</span> <span class="o">=</span> <span class="n">POP</span><span class="p">();</span> <span class="cm">/* code object */</span>  <span class="c1">// do_add函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="n">x</span> <span class="o">=</span> <span class="n">PyFunction_New</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">f_globals</span><span class="p">);</span> <span class="c1">//绑定global名字空间
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="c1">// 到这里, 得到一个PyFunctionObject
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">              <span class="n">Py_DECREF</span><span class="p">(</span><span class="n">v</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">              <span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                  <span class="n">v</span> <span class="o">=</span> <span class="n">POP</span><span class="p">();</span>   <span class="c1">// 得到tuple, (&#39;x&#39;, )
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">                  <span class="c1">// 注意这里
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                  <span class="k">if</span> <span class="p">(</span><span class="n">PyFunction_SetClosure</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                      <span class="cm">/* Can&#39;t happen unless bytecode is corrupt. */</span>
</span></span><span class="line"><span class="cl">                      <span class="n">why</span> <span class="o">=</span> <span class="n">WHY_EXCEPTION</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="n">Py_DECREF</span><span class="p">(</span><span class="n">v</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 class="p">}</span>
</span></span></code></pre></div><p>来关注一下 <code>PyFunction_SetClosure</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="nf">PyFunction_SetClosure</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">op</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">closure</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 class="n">Py_XDECREF</span><span class="p">(((</span><span class="n">PyFunctionObject</span> <span class="o">*</span><span class="p">)</span> <span class="n">op</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">func_closure</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">((</span><span class="n">PyFunctionObject</span> <span class="o">*</span><span class="p">)</span> <span class="n">op</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">func_closure</span> <span class="o">=</span> <span class="n">closure</span><span class="p">;</span>  <span class="c1">// 注意这里
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>即<code>do_add</code>的 <code>PyFunctionObject</code>的<code>func_closure</code>指向一个tuple</p>
<p>注意: 这时候, 外层变量已经固定下来了!!!!!!</p>
<h3 id="然后-在嵌套函数被调用的时候">然后, 在嵌套函数被调用的时候</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">CALL_FUNCTION</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">x</span> <span class="o">=</span> <span class="n">call_function</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sp</span><span class="p">,</span> <span class="n">oparg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">x</span> <span class="o">=</span> <span class="n">fast_function</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="n">pp_stack</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">na</span><span class="p">,</span> <span class="n">nk</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">=&gt;</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">PyEval_EvalCodeEx</span><span class="p">(</span><span class="n">co</span><span class="p">,</span> <span class="n">globals</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                               <span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="p">)</span><span class="nb">NULL</span><span class="p">,</span> <span class="p">(</span><span class="o">*</span><span class="n">pp_stack</span><span class="p">)</span><span class="o">-</span><span class="n">n</span><span class="p">,</span> <span class="n">na</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                               <span class="p">(</span><span class="o">*</span><span class="n">pp_stack</span><span class="p">)</span><span class="o">-</span><span class="mi">2</span><span class="o">*</span><span class="n">nk</span><span class="p">,</span> <span class="n">nk</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">nd</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                               <span class="n">PyFunction_GET_CLOSURE</span><span class="p">(</span><span class="n">func</span><span class="p">));</span>
</span></span></code></pre></div><p>看下<code>PyFunction_GET_CLOSURE</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">  <span class="cp">#define PyFunction_GET_CLOSURE(func) \
</span></span></span><span class="line"><span class="cl"><span class="cp">      (((PyFunctionObject *)func) -&gt; func_closure)
</span></span></span></code></pre></div><p>然后, 进入 <code>PyEval_EvalCodeEx</code>, 注意这里的<code>closure</code>参数即上一步取出来的<code>func_closure</code>, 即外层函数传进来的tuple</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">  <span class="n">PyObject</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl">  <span class="nf">PyEval_EvalCodeEx</span><span class="p">(</span><span class="n">PyCodeObject</span> <span class="o">*</span><span class="n">co</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">globals</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">locals</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">             <span class="n">PyObject</span> <span class="o">**</span><span class="n">args</span><span class="p">,</span> <span class="kt">int</span> <span class="n">argcount</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">**</span><span class="n">kws</span><span class="p">,</span> <span class="kt">int</span> <span class="n">kwcount</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">             <span class="n">PyObject</span> <span class="o">**</span><span class="n">defs</span><span class="p">,</span> <span class="kt">int</span> <span class="n">defcount</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">closure</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 class="c1">//  嵌套函数do_add, 使用到了外层函数的变量, 所以co-&gt;co_freevars非空, 这里得到 (&#39;x&#39;, )
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="k">if</span> <span class="p">(</span><span class="n">PyTuple_GET_SIZE</span><span class="p">(</span><span class="n">co</span><span class="o">-&gt;</span><span class="n">co_freevars</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="kt">int</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">PyTuple_GET_SIZE</span><span class="p">(</span><span class="n">co</span><span class="o">-&gt;</span><span class="n">co_freevars</span><span class="p">);</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <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"></span>              <span class="n">PyObject</span> <span class="o">*</span><span class="n">o</span> <span class="o">=</span> <span class="n">PyTuple_GET_ITEM</span><span class="p">(</span><span class="n">closure</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">              <span class="n">Py_INCREF</span><span class="p">(</span><span class="n">o</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">              <span class="c1">// 放到freevars里面, 编译时已经确定了顺序
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="c1">// 在上一步多LOAD_CLOSURE =&gt; tuple 已经保证了顺序
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="n">freevars</span><span class="p">[</span><span class="n">PyTuple_GET_SIZE</span><span class="p">(</span><span class="n">co</span><span class="o">-&gt;</span><span class="n">co_cellvars</span><span class="p">)</span> <span class="o">+</span> <span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">o</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 class="p">......</span>
</span></span></code></pre></div><h3 id="最后-再来看一个闭包的dis">最后, 再来看一个闭包的dis</h3>
<p>注意<code>BUILD_TUPLE</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">add</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</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">do_add</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">value</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">do_add2</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">y</span> <span class="o">+</span> <span class="n">value</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">do_add3</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> <span class="o">+</span> <span class="n">value</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">do_add</span>
</span></span></code></pre></div><p>dis结果</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"> <span class="mi">18</span>           <span class="mi">0</span> <span class="n">LOAD_CLOSURE</span>             <span class="mi">0</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">              <span class="mi">3</span> <span class="n">BUILD_TUPLE</span>              <span class="mi">1</span>
</span></span><span class="line"><span class="cl">              <span class="mi">6</span> <span class="n">LOAD_CONST</span>               <span class="mi">1</span> <span class="p">(</span><span class="o">&lt;</span><span class="n">code</span> <span class="n">object</span> <span class="n">do_add</span> <span class="n">at</span> <span class="mh">0x10560dc30</span><span class="p">,</span> <span class="n">file</span> <span class="s">&#34;a.py&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">18</span><span class="o">&gt;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">              <span class="mi">9</span> <span class="n">MAKE_CLOSURE</span>             <span class="mi">0</span>
</span></span><span class="line"><span class="cl">             <span class="mi">12</span> <span class="n">STORE_FAST</span>               <span class="mi">2</span> <span class="p">(</span><span class="n">do_add</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="mi">21</span>          <span class="mi">15</span> <span class="n">LOAD_CLOSURE</span>             <span class="mi">1</span> <span class="p">(</span><span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">             <span class="mi">18</span> <span class="n">BUILD_TUPLE</span>              <span class="mi">1</span>
</span></span><span class="line"><span class="cl">             <span class="mi">21</span> <span class="n">LOAD_CONST</span>               <span class="mi">2</span> <span class="p">(</span><span class="o">&lt;</span><span class="n">code</span> <span class="n">object</span> <span class="n">do_add2</span> <span class="n">at</span> <span class="mh">0x10560d8b0</span><span class="p">,</span> <span class="n">file</span> <span class="s">&#34;a.py&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">21</span><span class="o">&gt;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">             <span class="mi">24</span> <span class="n">MAKE_CLOSURE</span>             <span class="mi">0</span>
</span></span><span class="line"><span class="cl">             <span class="mi">27</span> <span class="n">STORE_FAST</span>               <span class="mi">3</span> <span class="p">(</span><span class="n">do_add2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="mi">24</span>          <span class="mi">30</span> <span class="n">LOAD_CLOSURE</span>             <span class="mi">0</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">             <span class="mi">33</span> <span class="n">LOAD_CLOSURE</span>             <span class="mi">1</span> <span class="p">(</span><span class="n">y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">             <span class="mi">36</span> <span class="n">BUILD_TUPLE</span>              <span class="mi">2</span>
</span></span><span class="line"><span class="cl">             <span class="mi">39</span> <span class="n">LOAD_CONST</span>               <span class="mi">3</span> <span class="p">(</span><span class="o">&lt;</span><span class="n">code</span> <span class="n">object</span> <span class="n">do_add3</span> <span class="n">at</span> <span class="mh">0x10560e3b0</span><span class="p">,</span> <span class="n">file</span> <span class="s">&#34;a.py&#34;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">24</span><span class="o">&gt;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">             <span class="mi">42</span> <span class="n">MAKE_CLOSURE</span>             <span class="mi">0</span>
</span></span><span class="line"><span class="cl">             <span class="mi">45</span> <span class="n">STORE_FAST</span>               <span class="mi">4</span> <span class="p">(</span><span class="n">do_add3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="mi">32</span>          <span class="mi">48</span> <span class="n">LOAD_FAST</span>                <span class="mi">2</span> <span class="p">(</span><span class="n">do_add</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">             <span class="mi">51</span> <span class="n">RETURN_VALUE</span>
</span></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>Python源码阅读-内存管理机制(二)</title>
			<link>https://wklken.me/posts/2015/08/29/python-source-memory-2.html</link>
			<pubDate>Sat, 29 Aug 2015 20:54:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/08/29/python-source-memory-2.html</guid>
			<description>Python 的内存分配策略 arena arena: 多个pool聚合的结果 arena size pool的大小默认值位4KB arena的大小默认值256KB, 能放置 256/4=64 个pool obmallo</description>
			<content type="html"><![CDATA[<h2 id="python-的内存分配策略">Python 的内存分配策略</h2>
<h3 id="arena">arena</h3>
<p>arena: 多个pool聚合的结果</p>
<h4 id="arena-size">arena size</h4>
<p>pool的大小默认值位4KB</p>
<p>arena的大小默认值256KB, 能放置 256/4=64 个pool</p>
<p><code>obmalloc.c</code>中代码</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define ARENA_SIZE              (256 &lt;&lt; 10)     </span><span class="cm">/* 256KB */</span><span class="cp">
</span></span></span></code></pre></div><h4 id="arena-结构">arena 结构</h4>
<blockquote>
<p>一个完整的arena = arena_object + pool集合</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="n">uchar</span> <span class="n">block</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* Record keeping for arenas. */</span>
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">arena_object</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* The address of the arena, as returned by malloc.  Note that 0
</span></span></span><span class="line"><span class="cl"><span class="cm">     * will never be returned by a successful malloc, and is used
</span></span></span><span class="line"><span class="cl"><span class="cm">     * here to mark an arena_object that doesn&#39;t correspond to an
</span></span></span><span class="line"><span class="cl"><span class="cm">     * allocated arena.
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="n">uptr</span> <span class="n">address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* Pool-aligned pointer to the next pool to be carved off. */</span>
</span></span><span class="line"><span class="cl">    <span class="n">block</span><span class="o">*</span> <span class="n">pool_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* The number of available pools in the arena:  free pools + never-
</span></span></span><span class="line"><span class="cl"><span class="cm">     * allocated pools.
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">nfreepools</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* The total number of pools in the arena, whether or not available. */</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">ntotalpools</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* Singly-linked list of available pools. */</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 单链表, 可用pool集合
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">struct</span> <span class="n">pool_header</span><span class="o">*</span> <span class="n">freepools</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/* Whenever this arena_object is not associated with an allocated
</span></span></span><span class="line"><span class="cl"><span class="cm">     * arena, the nextarena member is used to link all unassociated
</span></span></span><span class="line"><span class="cl"><span class="cm">     * arena_objects in the singly-linked `unused_arena_objects` list.
</span></span></span><span class="line"><span class="cl"><span class="cm">     * The prevarena member is unused in this case.
</span></span></span><span class="line"><span class="cl"><span class="cm">     *
</span></span></span><span class="line"><span class="cl"><span class="cm">     * When this arena_object is associated with an allocated arena
</span></span></span><span class="line"><span class="cl"><span class="cm">     * with at least one available pool, both members are used in the
</span></span></span><span class="line"><span class="cl"><span class="cm">     * doubly-linked `usable_arenas` list, which is maintained in
</span></span></span><span class="line"><span class="cl"><span class="cm">     * increasing order of `nfreepools` values.
</span></span></span><span class="line"><span class="cl"><span class="cm">     *
</span></span></span><span class="line"><span class="cl"><span class="cm">     * Else this arena_object is associated with an allocated arena
</span></span></span><span class="line"><span class="cl"><span class="cm">     * all of whose pools are in use.  `nextarena` and `prevarena`
</span></span></span><span class="line"><span class="cl"><span class="cm">     * are both meaningless in this case.
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// arena链表
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">struct</span> <span class="n">arena_object</span><span class="o">*</span> <span class="n">nextarena</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">arena_object</span><span class="o">*</span> <span class="n">prevarena</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>arena_object的作用</p>
<pre tabindex="0"><code>1. 与其他arena连接, 组成双向链表
2. 维护arena中可用的pool, 单链表
3. 其他信息
</code></pre><p><code>pool_header</code> 与 <code>arena_object</code></p>
<pre tabindex="0"><code>pool_header和管理的blocks内存是一块连续的内存 =&gt; pool_header被申请时, 其管理的block集合的内存一并被申请
arena_object和其管理的内存是分离的 =&gt; arena_object被申请时, 其管理的pool集合的内存没有被申请, 而是在某一时刻建立的联系
</code></pre><p><img src="/imgs/python-source/python-memory-arena.png" alt="arena"></p>
<h4 id="arena的两种状态">arena的两种状态</h4>
<p>arena存在两种状态: 未使用(没有建立联系)/可用(建立了联系)</p>
<p>全局由两个链表维护着</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/* The head of the singly-linked, NULL-terminated list of available
</span></span></span><span class="line"><span class="cl"><span class="cm"> * arena_objects.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 单链表
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="k">struct</span> <span class="n">arena_object</span><span class="o">*</span> <span class="n">unused_arena_objects</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* The head of the doubly-linked, NULL-terminated at each end, list of
</span></span></span><span class="line"><span class="cl"><span class="cm"> * arena_objects associated with arenas that have pools available.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 双向链表
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="k">struct</span> <span class="n">arena_object</span><span class="o">*</span> <span class="n">usable_arenas</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span></code></pre></div><h4 id="arena的初始化">arena的初始化</h4>
<p>首先, 来看下初始化相关的一些参数定义</p>
<p>代码<code>obmalloc.c</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/* Array of objects used to track chunks of memory (arenas). */</span>
</span></span><span class="line"><span class="cl"><span class="c1">// arena_object 数组
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="k">struct</span> <span class="n">arena_object</span><span class="o">*</span> <span class="n">arenas</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* Number of slots currently allocated in the `arenas` vector. */</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 当前arenas中管理的arena_object的个数, 初始化时=0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">uint</span> <span class="n">maxarenas</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* How many arena_objects do we initially allocate?
</span></span></span><span class="line"><span class="cl"><span class="cm"> * 16 = can allocate 16 arenas = 16 * ARENA_SIZE = 4MB before growing the
</span></span></span><span class="line"><span class="cl"><span class="cm"> * `arenas` vector.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 初始化时申请的arena_object个数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define INITIAL_ARENA_OBJECTS 16
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/* Number of arenas allocated that haven&#39;t been free()&#39;d. */</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="n">size_t</span> <span class="n">narenas_currently_allocated</span> <span class="o">=</span> <span class="mi">0</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="cm">/* The head of the singly-linked, NULL-terminated list of available
</span></span></span><span class="line"><span class="cl"><span class="cm"> * arena_objects.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 未使用状态arena的单链表
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="k">struct</span> <span class="n">arena_object</span><span class="o">*</span> <span class="n">unused_arena_objects</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* The head of the doubly-linked, NULL-terminated at each end, list of
</span></span></span><span class="line"><span class="cl"><span class="cm"> * arena_objects associated with arenas that have pools available.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 可用状态arena的双向链表
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="k">struct</span> <span class="n">arena_object</span><span class="o">*</span> <span class="n">usable_arenas</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span></code></pre></div><p>然后, 看下<code>obmalloc.c</code>中<code>arena</code>初始化的代码</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/* Allocate a new arena.  If we run out of memory, return NULL.  Else
</span></span></span><span class="line"><span class="cl"><span class="cm"> * allocate a new arena, and return the address of an arena_object
</span></span></span><span class="line"><span class="cl"><span class="cm"> * describing the new arena.  It&#39;s expected that the caller will set
</span></span></span><span class="line"><span class="cl"><span class="cm"> * `usable_arenas` to the return value.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="k">static</span> <span class="k">struct</span> <span class="n">arena_object</span><span class="o">*</span>
</span></span><span class="line"><span class="cl"><span class="nf">new_arena</span><span class="p">(</span><span class="kt">void</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="k">struct</span> <span class="n">arena_object</span><span class="o">*</span> <span class="n">arenaobj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">excess</span><span class="p">;</span>        <span class="cm">/* number of bytes above pool alignment */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">void</span> <span class="o">*</span><span class="n">address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span> <span class="n">err</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 判断是否需要扩充&#34;未使用&#34;的arena_object列表
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">unused_arena_objects</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">uint</span> <span class="n">i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">uint</span> <span class="n">numarenas</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">size_t</span> <span class="n">nbytes</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* Double the number of arena objects on each allocation.
</span></span></span><span class="line"><span class="cl"><span class="cm">         * Note that it&#39;s possible for `numarenas` to overflow.
</span></span></span><span class="line"><span class="cl"><span class="cm">         */</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 确定需要申请的个数, 首次初始化, 16, 之后每次翻倍
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">numarenas</span> <span class="o">=</span> <span class="n">maxarenas</span> <span class="o">?</span> <span class="n">maxarenas</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span> <span class="o">:</span> <span class="n">INITIAL_ARENA_OBJECTS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">numarenas</span> <span class="o">&lt;=</span> <span class="n">maxarenas</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>                <span class="cm">/* overflow */</span>  <span class="c1">//溢出了
</span></span></span><span class="line"><span class="cl"><span class="c1"></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="n">nbytes</span> <span class="o">=</span> <span class="n">numarenas</span> <span class="o">*</span> <span class="k">sizeof</span><span class="p">(</span><span class="o">*</span><span class="n">arenas</span><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"></span>        <span class="n">arenaobj</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">arena_object</span> <span class="o">*</span><span class="p">)</span><span class="n">realloc</span><span class="p">(</span><span class="n">arenas</span><span class="p">,</span> <span class="n">nbytes</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">arenaobj</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">arenas</span> <span class="o">=</span> <span class="n">arenaobj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* We might need to fix pointers that were copied.  However,
</span></span></span><span class="line"><span class="cl"><span class="cm">         * new_arena only gets called when all the pages in the
</span></span></span><span class="line"><span class="cl"><span class="cm">         * previous arenas are full.  Thus, there are *no* pointers
</span></span></span><span class="line"><span class="cl"><span class="cm">         * into the old array. Thus, we don&#39;t have to worry about
</span></span></span><span class="line"><span class="cl"><span class="cm">         * invalid pointers.  Just to be sure, some asserts:
</span></span></span><span class="line"><span class="cl"><span class="cm">         */</span>
</span></span><span class="line"><span class="cl">        <span class="n">assert</span><span class="p">(</span><span class="n">usable_arenas</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">assert</span><span class="p">(</span><span class="n">unused_arena_objects</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">);</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"><span class="c1"></span>        <span class="cm">/* Put the new arenas on the unused_arena_objects list. */</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="n">maxarenas</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">numarenas</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">arenas</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">address</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>              <span class="cm">/* mark as unassociated */</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 新申请的一律为0, 标识着这个arena处于&#34;未使用&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">arenas</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">nextarena</span> <span class="o">=</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">numarenas</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">?</span>
</span></span><span class="line"><span class="cl">                                   <span class="o">&amp;</span><span class="n">arenas</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">:</span> <span class="nb">NULL</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="c1">// 将其放入unused_arena_objects链表中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// unused_arena_objects 为新分配内存空间的开头
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="cm">/* Update globals. */</span>
</span></span><span class="line"><span class="cl">        <span class="n">unused_arena_objects</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">arenas</span><span class="p">[</span><span class="n">maxarenas</span><span class="p">];</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"><span class="c1"></span>        <span class="n">maxarenas</span> <span class="o">=</span> <span class="n">numarenas</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="cm">/* Take the next available arena object off the head of the list. */</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span><span class="p">(</span><span class="n">unused_arena_objects</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 从unused_arena_objects中, 获取一个未使用的object
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">arenaobj</span> <span class="o">=</span> <span class="n">unused_arena_objects</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">unused_arena_objects</span> <span class="o">=</span> <span class="n">arenaobj</span><span class="o">-&gt;</span><span class="n">nextarena</span><span class="p">;</span> <span class="c1">// 更新链表
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 开始处理这个 arenaobject
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span><span class="p">(</span><span class="n">arenaobj</span><span class="o">-&gt;</span><span class="n">address</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 申请内存, 256KB, 内存地址赋值给arena的address. 这块内存可用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#ifdef ARENAS_USE_MMAP
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>    <span class="n">address</span> <span class="o">=</span> <span class="n">mmap</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">ARENA_SIZE</span><span class="p">,</span> <span class="n">PROT_READ</span><span class="o">|</span><span class="n">PROT_WRITE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                   <span class="n">MAP_PRIVATE</span><span class="o">|</span><span class="n">MAP_ANONYMOUS</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">err</span> <span class="o">=</span> <span class="p">(</span><span class="n">address</span> <span class="o">==</span> <span class="n">MAP_FAILED</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="cp">#else
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>    <span class="n">address</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">ARENA_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">err</span> <span class="o">=</span> <span class="p">(</span><span class="n">address</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* The allocation failed: return NULL after putting the
</span></span></span><span class="line"><span class="cl"><span class="cm">         * arenaobj back.
</span></span></span><span class="line"><span class="cl"><span class="cm">         */</span>
</span></span><span class="line"><span class="cl">        <span class="n">arenaobj</span><span class="o">-&gt;</span><span class="n">nextarena</span> <span class="o">=</span> <span class="n">unused_arena_objects</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">unused_arena_objects</span> <span class="o">=</span> <span class="n">arenaobj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">NULL</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="n">arenaobj</span><span class="o">-&gt;</span><span class="n">address</span> <span class="o">=</span> <span class="p">(</span><span class="n">uptr</span><span class="p">)</span><span class="n">address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">++</span><span class="n">narenas_currently_allocated</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 设置pool集合相关信息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">arenaobj</span><span class="o">-&gt;</span><span class="n">freepools</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>  <span class="c1">// 设置为NULL, 只有在释放一个pool的时候才有用
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="cm">/* pool_address &lt;- first pool-aligned address in the arena
</span></span></span><span class="line"><span class="cl"><span class="cm">       nfreepools &lt;- number of whole pools that fit after alignment */</span>
</span></span><span class="line"><span class="cl">    <span class="n">arenaobj</span><span class="o">-&gt;</span><span class="n">pool_address</span> <span class="o">=</span> <span class="p">(</span><span class="n">block</span><span class="o">*</span><span class="p">)</span><span class="n">arenaobj</span><span class="o">-&gt;</span><span class="n">address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">arenaobj</span><span class="o">-&gt;</span><span class="n">nfreepools</span> <span class="o">=</span> <span class="n">ARENA_SIZE</span> <span class="o">/</span> <span class="n">POOL_SIZE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">assert</span><span class="p">(</span><span class="n">POOL_SIZE</span> <span class="o">*</span> <span class="n">arenaobj</span><span class="o">-&gt;</span><span class="n">nfreepools</span> <span class="o">==</span> <span class="n">ARENA_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 将pool的起始地址调整为系统页的边界
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 申请到 256KB, 放弃了一些内存, 而将可使用的内存边界pool_address调整到了与系统页对齐
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">excess</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">arenaobj</span><span class="o">-&gt;</span><span class="n">address</span> <span class="o">&amp;</span> <span class="n">POOL_SIZE_MASK</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">excess</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="o">--</span><span class="n">arenaobj</span><span class="o">-&gt;</span><span class="n">nfreepools</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">arenaobj</span><span class="o">-&gt;</span><span class="n">pool_address</span> <span class="o">+=</span> <span class="n">POOL_SIZE</span> <span class="o">-</span> <span class="n">excess</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="n">arenaobj</span><span class="o">-&gt;</span><span class="n">ntotalpools</span> <span class="o">=</span> <span class="n">arenaobj</span><span class="o">-&gt;</span><span class="n">nfreepools</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">arenaobj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>图示: 初始化arenas数组, 初始化后的所有arena都在<code>unused_arena_objects</code>单链表里面</p>
<p><img src="/imgs/python-source/python-memory-arena2.png" alt="arena"></p>
<p>图示: 从arenas取一个arena进行初始化</p>
<p><img src="/imgs/python-source/python-memory-arena3.png" alt="arena"></p>
<h4 id="没有可用的arena">没有可用的arena?</h4>
<p>此时</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">    <span class="c1">// 判断成立
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">unused_arena_objects</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</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="c1">// 确定需要申请的个数, 首次初始化, 16, 之后每次翻倍
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">numarenas</span> <span class="o">=</span> <span class="n">maxarenas</span> <span class="o">?</span> <span class="n">maxarenas</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span> <span class="o">:</span> <span class="n">INITIAL_ARENA_OBJECTS</span><span class="p">;</span>
</span></span></code></pre></div><p>然后, 假设第一次分配了16个, 发现没有arena之后, 第二次处理结果: <code>numarenas = 32</code></p>
<p>即, 数组扩大了一倍</p>
<h4 id="arena分配">arena分配</h4>
<p><code>new</code>了一个全新的 arena之后,</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">  <span class="kt">void</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl">  <span class="nf">PyObject_Malloc</span><span class="p">(</span><span class="n">size_t</span> <span class="n">nbytes</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="c1">// 刚开始没有可用的arena
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">if</span> <span class="p">(</span><span class="n">usable_arenas</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="c1">// new一个, 作为双向链表的表头
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="n">usable_arenas</span> <span class="o">=</span> <span class="n">new_arena</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">              <span class="k">if</span> <span class="p">(</span><span class="n">usable_arenas</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                  <span class="n">UNLOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                  <span class="k">goto</span> <span class="n">redirect</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="n">usable_arenas</span><span class="o">-&gt;</span><span class="n">nextarena</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">                  <span class="n">usable_arenas</span><span class="o">-&gt;</span><span class="n">prevarena</span> <span class="o">=</span> <span class="nb">NULL</span><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></span><span class="line"><span class="cl">
</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">// 从arena中获取一个pool
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="n">pool</span> <span class="o">=</span> <span class="p">(</span><span class="n">poolp</span><span class="p">)</span><span class="n">usable_arenas</span><span class="o">-&gt;</span><span class="n">pool_address</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">assert</span><span class="p">((</span><span class="n">block</span><span class="o">*</span><span class="p">)</span><span class="n">pool</span> <span class="o">&lt;=</span> <span class="p">(</span><span class="n">block</span><span class="o">*</span><span class="p">)</span><span class="n">usable_arenas</span><span class="o">-&gt;</span><span class="n">address</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                                 <span class="n">ARENA_SIZE</span> <span class="o">-</span> <span class="n">POOL_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="n">pool</span><span class="o">-&gt;</span><span class="n">arenaindex</span> <span class="o">=</span> <span class="n">usable_arenas</span> <span class="o">-</span> <span class="n">arenas</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">assert</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arenas</span><span class="p">[</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">arenaindex</span><span class="p">]</span> <span class="o">==</span> <span class="n">usable_arenas</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="n">pool</span><span class="o">-&gt;</span><span class="n">szidx</span> <span class="o">=</span> <span class="n">DUMMY_SIZE_IDX</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="c1">// 更新 pool_address 向下一个节点
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="n">usable_arenas</span><span class="o">-&gt;</span><span class="n">pool_address</span> <span class="o">+=</span> <span class="n">POOL_SIZE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="c1">// 可用节点数量-1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="o">--</span><span class="n">usable_arenas</span><span class="o">-&gt;</span><span class="n">nfreepools</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></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>图示: 从全新的arena中获取一个pool</p>
<p><img src="/imgs/python-source/python-memory-arena4.png" alt="arena"></p>
<p>假设arena是旧的, 怎么分配的pool</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">          <span class="n">pool</span> <span class="o">=</span> <span class="n">usable_arenas</span><span class="o">-&gt;</span><span class="n">freepools</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="n">pool</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span></code></pre></div><p>这个<code>arena-&gt;freepools</code>是何方神圣?</p>
<p>当arena中一整块pool被释放的时候</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">  <span class="kt">void</span>
</span></span><span class="line"><span class="cl">  <span class="nf">PyObject_Free</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">p</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="k">struct</span> <span class="n">arena_object</span><span class="o">*</span> <span class="n">ao</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">uint</span> <span class="n">nf</span><span class="p">;</span>  <span class="cm">/* ao-&gt;nfreepools */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">              <span class="cm">/* Link the pool to freepools.  This is a singly-linked
</span></span></span><span class="line"><span class="cl"><span class="cm">               * list, and pool-&gt;prevpool isn&#39;t used there.
</span></span></span><span class="line"><span class="cl"><span class="cm">              */</span>
</span></span><span class="line"><span class="cl">              <span class="n">ao</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">arenas</span><span class="p">[</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">arenaindex</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">              <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextpool</span> <span class="o">=</span> <span class="n">ao</span><span class="o">-&gt;</span><span class="n">freepools</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">ao</span><span class="o">-&gt;</span><span class="n">freepools</span> <span class="o">=</span> <span class="n">pool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">nf</span> <span class="o">=</span> <span class="o">++</span><span class="n">ao</span><span class="o">-&gt;</span><span class="n">nfreepools</span><span class="p">;</span>
</span></span></code></pre></div><p>也就是说, 在pool整块被释放的时候, 会将pool加入到<code>arena-&gt;freepools</code>作为单链表的表头, 然后, 在从非全新arena中分配pool时, 优先从<code>arena-&gt;freepools</code>里面取, 如果取不到, 再从arena内存块里面获取</p>
<p>图示</p>
<p><img src="/imgs/python-source/python-memory-arena5.png" alt="arena"></p>
<h4 id="一个arena满了之后呢">一个arena满了之后呢</h4>
<p>很自然, 从下一个arena中获取</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">  <span class="kt">void</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl">  <span class="nf">PyObject_Malloc</span><span class="p">(</span><span class="n">size_t</span> <span class="n">nbytes</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="c1">// 当发现用完了最后一个pool!!!!!!!!!!!
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="c1">// nfreepools = 0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="k">if</span> <span class="p">(</span><span class="n">usable_arenas</span><span class="o">-&gt;</span><span class="n">nfreepools</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="n">assert</span><span class="p">(</span><span class="n">usable_arenas</span><span class="o">-&gt;</span><span class="n">nextarena</span> <span class="o">==</span> <span class="nb">NULL</span> <span class="o">||</span>
</span></span><span class="line"><span class="cl">                     <span class="n">usable_arenas</span><span class="o">-&gt;</span><span class="n">nextarena</span><span class="o">-&gt;</span><span class="n">prevarena</span> <span class="o">==</span>
</span></span><span class="line"><span class="cl">                     <span class="n">usable_arenas</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">              <span class="cm">/* Unlink the arena:  it is completely allocated. */</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"><span class="c1"></span>              <span class="n">usable_arenas</span> <span class="o">=</span> <span class="n">usable_arenas</span><span class="o">-&gt;</span><span class="n">nextarena</span><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"></span>              <span class="k">if</span> <span class="p">(</span><span class="n">usable_arenas</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                  <span class="n">usable_arenas</span><span class="o">-&gt;</span><span class="n">prevarena</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span> <span class="c1">// 更新下一个节点的prevarens
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                  <span class="n">assert</span><span class="p">(</span><span class="n">usable_arenas</span><span class="o">-&gt;</span><span class="n">address</span> <span class="o">!=</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 class="c1">// 没有下一个, 此时 usable_arenas = NULL, 下次进行内存分配的时候, 就会从arenas数组中取一个
</span></span></span><span class="line"><span class="cl"><span class="c1"></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></span></code></pre></div><p>注意: 这里有个逻辑, 就是每分配一个pool, 就检查是不是用到了最后一个, 如果是, 需要变更<code>usable_arenas</code>到下一个可用的节点, 如果没有可用的, 那么下次进行内存分配的时候, 会判定从arenas数组中取一个</p>
<h4 id="arena回收">arena回收</h4>
<p>内存分配和回收最小单位是block, 当一个block被回收的时候, 可能触发pool被回收, pool被回收, 将会触发arena的回收机制</p>
<p>四种情况</p>
<pre tabindex="0"><code>1. arena中所有pool都是闲置的(empty), 将arena内存释放, 返回给操作系统
2. 如果arena中之前所有的pool都是占用的(used), 现在释放了一个pool(empty), 需要将 arena加入到usable_arenas, 会加入链表表头
3. 如果arena中empty的pool个数n, 则从useable_arenas开始寻找可以插入的位置. 将arena插入. (useable_arenas是一个有序链表, 按empty pool的个数, 保证empty pool数量越多, 被使用的几率越小, 最终被整体释放的机会越大)
4. 其他情况, 不对arena 进行处理
</code></pre><p>具体可以看<code>PyObject_Free</code>的代码</p>
<h3 id="内存分配步骤">内存分配步骤</h3>
<p>好的, 到这里, 我们已经知道了block和pool的关系(包括pool怎么管理block的), 以及arena和pool的关系(怎么从arena中拉到可用的pool)</p>
<p>那么, 在分析<code>PyObject_Malloc(size_t nbytes)</code>如何进行内存分配的时候, 我们就刨除掉这些管理代码</p>
<p>关注: 如何寻找得到一块可用的nbytes的block内存</p>
<p>其实代码那么多, 寻址得到对应的block也就这么几行代码, 其他代码都是pool没有, 找arena, 申请arena, arena没有, 找arenas, 最终的到一块pool, 初始化, 返回第一个block</p>
<p>如果有的情况, 用现成的</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">pool</span> <span class="o">=</span> <span class="n">usedpools</span><span class="p">[</span><span class="n">size</span> <span class="o">+</span> <span class="n">size</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nl">pool可用</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">pool</span> <span class="err">没满</span><span class="p">,</span> <span class="err">取一个</span><span class="n">block返回</span>
</span></span><span class="line"><span class="cl">    <span class="n">pool</span> <span class="err">满了</span><span class="p">,</span> <span class="err">从下一个</span><span class="n">pool取一个block返回</span>
</span></span><span class="line"><span class="cl"><span class="err">否则</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">    <span class="err">获取</span><span class="n">arena</span><span class="p">,</span> <span class="err">从里面初始化一个</span><span class="n">pool</span><span class="p">,</span> <span class="err">拿到第一个</span><span class="n">block</span><span class="p">,</span> <span class="err">返回</span>
</span></span></code></pre></div><p>从上面这个判断逻辑来看, 内存分配其实主要操作的是pool, 跟arena并不是基本的操作单元(只是用来管理pool的)</p>
<p>结论: 进行内存分配和销毁, 所有操作都是在pool上进行的</p>
<p><code>usedpools</code> 是什么鬼? 其实是可用pool缓冲池, 后面说</p>
<h3 id="内存池">内存池</h3>
<h4 id="arena-内存池的大小">arena 内存池的大小</h4>
<p>取决于用户, Python提供的编译符号, 用于决定是否控制</p>
<p><code>obmalloc.c</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#ifdef WITH_MEMORY_LIMITS
</span></span></span><span class="line"><span class="cl"><span class="cp">#ifndef SMALL_MEMORY_LIMIT
</span></span></span><span class="line"><span class="cl"><span class="cp">#define SMALL_MEMORY_LIMIT      (64 * 1024 * 1024)      </span><span class="cm">/* 64 MB -- more? */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#ifdef WITH_MEMORY_LIMITS
</span></span></span><span class="line"><span class="cl"><span class="cp">#define MAX_ARENAS              (SMALL_MEMORY_LIMIT / ARENA_SIZE)
</span></span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span></code></pre></div><p>具体使用中, python并不直接与arenas和arena打交道, 当Python申请内存时, 最基本的操作单元并不是arena, 而是pool</p>
<p>问题: pool中所有block的size一样, 但是在arena中, 每个pool的size都可能不一样, 那么最终这些pool是怎么维护的? 怎么根据大小找到需要的block所在的pool? =&gt; <code>usedpools</code></p>
<h4 id="pool在内存池中的三种状态">pool在内存池中的三种状态</h4>
<pre tabindex="0"><code>1. used状态: pool中至少有一个block已经被使用, 并且至少有一个block未被使用. 这种状态的pool受控于Python内部维护的usedpool数组

2. full状态: pool中所有的block都已经被使用, 这种状态的pool在arena中, 但不在arena的freepools链表中
处于full的pool各自独立, 不会被链表维护起来

3. empty状态: pool中所有block都未被使用, 处于这个状态的pool的集合通过其pool_header中的nextpool构成一个链表, 链表的表头是arena_object中的freepools
</code></pre><h4 id="usedpools">usedpools</h4>
<p>usedpools数组: 维护着所有处于used状态的pool, 当申请内存的时候, 会通过usedpools寻找到一块可用的(处于used状态的)pool, 从中分配一个block</p>
<p>结构:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">  <span class="cp">#define SMALL_REQUEST_THRESHOLD 512
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="c1">// 512/8 = 64
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="cp">#define NB_SMALL_SIZE_CLASSES   (SMALL_REQUEST_THRESHOLD / ALIGNMENT)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">  <span class="cp">#define PTA(x)  ((poolp )((uchar *)&amp;(usedpools[2*(x)]) - 2*sizeof(block *)))
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="cp">#define PT(x)   PTA(x), PTA(x)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">  <span class="c1">// 2 * ((64 + 7) / 8) * 8 = 128, 大小为128的数组
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="k">static</span> <span class="n">poolp</span> <span class="n">usedpools</span><span class="p">[</span><span class="mi">2</span> <span class="o">*</span> <span class="p">((</span><span class="n">NB_SMALL_SIZE_CLASSES</span> <span class="o">+</span> <span class="mi">7</span><span class="p">)</span> <span class="o">/</span> <span class="mi">8</span><span class="p">)</span> <span class="o">*</span> <span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">PT</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">3</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">4</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">5</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">6</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="cp">#if NB_SMALL_SIZE_CLASSES &gt; 8
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>      <span class="p">,</span> <span class="n">PT</span><span class="p">(</span><span class="mi">8</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">9</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">11</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">12</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">13</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">14</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">15</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="cp">#if NB_SMALL_SIZE_CLASSES &gt; 16
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>      <span class="p">,</span> <span class="n">PT</span><span class="p">(</span><span class="mi">16</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">17</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">18</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">19</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">20</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">21</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">22</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">23</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="cp">#if NB_SMALL_SIZE_CLASSES &gt; 24
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>      <span class="p">,</span> <span class="n">PT</span><span class="p">(</span><span class="mi">24</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">25</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">26</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">27</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">28</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">29</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">30</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">31</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="cp">#if NB_SMALL_SIZE_CLASSES &gt; 32
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>      <span class="p">,</span> <span class="n">PT</span><span class="p">(</span><span class="mi">32</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">33</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">34</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">35</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">36</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">37</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">38</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">39</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="cp">#if NB_SMALL_SIZE_CLASSES &gt; 40
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>      <span class="p">,</span> <span class="n">PT</span><span class="p">(</span><span class="mi">40</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">41</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">42</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">43</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">44</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">45</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">46</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">47</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="cp">#if NB_SMALL_SIZE_CLASSES &gt; 48
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>      <span class="p">,</span> <span class="n">PT</span><span class="p">(</span><span class="mi">48</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">49</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">50</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">51</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">52</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">53</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">54</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">55</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="cp">#if NB_SMALL_SIZE_CLASSES &gt; 56
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>      <span class="p">,</span> <span class="n">PT</span><span class="p">(</span><span class="mi">56</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">57</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">58</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">59</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">60</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">61</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">62</span><span class="p">),</span> <span class="n">PT</span><span class="p">(</span><span class="mi">63</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="cp">#if NB_SMALL_SIZE_CLASSES &gt; 64
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="cp">#error &#34;NB_SMALL_SIZE_CLASSES should be less than 64&#34;
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="cp">#endif </span><span class="cm">/* NB_SMALL_SIZE_CLASSES &gt; 64 */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="cp">#endif </span><span class="cm">/* NB_SMALL_SIZE_CLASSES &gt; 56 */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="cp">#endif </span><span class="cm">/* NB_SMALL_SIZE_CLASSES &gt; 48 */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="cp">#endif </span><span class="cm">/* NB_SMALL_SIZE_CLASSES &gt; 40 */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="cp">#endif </span><span class="cm">/* NB_SMALL_SIZE_CLASSES &gt; 32 */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="cp">#endif </span><span class="cm">/* NB_SMALL_SIZE_CLASSES &gt; 24 */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="cp">#endif </span><span class="cm">/* NB_SMALL_SIZE_CLASSES &gt; 16 */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="cp">#endif </span><span class="cm">/* NB_SMALL_SIZE_CLASSES &gt;  8 */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="err">即</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 得到usedpools数组
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">static</span> <span class="n">poolp</span> <span class="n">usedpools</span><span class="p">[</span><span class="mi">128</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="n">PTA</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="n">PTA</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="n">PTA</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="n">PTA</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="n">PTA</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span> <span class="n">PTA</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span> <span class="n">PTA</span><span class="p">(</span><span class="mi">3</span><span class="p">),</span> <span class="n">PTA</span><span class="p">(</span><span class="mi">3</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="n">PTA</span><span class="p">(</span><span class="mi">63</span><span class="p">),</span> <span class="n">PTA</span><span class="p">(</span><span class="mi">63</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>解开看(<code>obmalloc.c</code>)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">typedef</span> <span class="n">uchar</span> <span class="n">block</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="cm">/* Pool for small blocks. */</span>
</span></span><span class="line"><span class="cl">  <span class="k">struct</span> <span class="n">pool_header</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">union</span> <span class="p">{</span> <span class="n">block</span> <span class="o">*</span><span class="n">_padding</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">uint</span> <span class="n">count</span><span class="p">;</span> <span class="p">}</span> <span class="n">ref</span><span class="p">;</span>          <span class="cm">/* number of allocated blocks    */</span>
</span></span><span class="line"><span class="cl">      <span class="n">block</span> <span class="o">*</span><span class="n">freeblock</span><span class="p">;</span>                   <span class="cm">/* pool&#39;s free list head         */</span>
</span></span><span class="line"><span class="cl">      <span class="k">struct</span> <span class="n">pool_header</span> <span class="o">*</span><span class="n">nextpool</span><span class="p">;</span>       <span class="cm">/* next pool of this size class  */</span>
</span></span><span class="line"><span class="cl">      <span class="k">struct</span> <span class="n">pool_header</span> <span class="o">*</span><span class="n">prevpool</span><span class="p">;</span>       <span class="cm">/* previous pool       &#34;&#34;        */</span>
</span></span><span class="line"><span class="cl">      <span class="n">uint</span> <span class="n">arenaindex</span><span class="p">;</span>                    <span class="cm">/* index into arenas of base adr */</span>
</span></span><span class="line"><span class="cl">      <span class="n">uint</span> <span class="n">szidx</span><span class="p">;</span>                         <span class="cm">/* block size class index        */</span>
</span></span><span class="line"><span class="cl">      <span class="n">uint</span> <span class="n">nextoffset</span><span class="p">;</span>                    <span class="cm">/* bytes to virgin block         */</span>
</span></span><span class="line"><span class="cl">      <span class="n">uint</span> <span class="n">maxnextoffset</span><span class="p">;</span>                 <span class="cm">/* largest valid nextoffset      */</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="k">typedef</span> <span class="k">struct</span> <span class="n">pool_header</span> <span class="o">*</span><span class="n">poolp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="n">usedpools</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">PTA</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">=</span> <span class="p">((</span><span class="n">poolp</span> <span class="p">)((</span><span class="n">uchar</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="p">(</span><span class="n">usedpools</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">-</span> <span class="mi">2</span><span class="o">*</span><span class="k">sizeof</span><span class="p">(</span><span class="n">block</span> <span class="o">*</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">  <span class="n">usedpools</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">PTA</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="o">=</span> <span class="p">((</span><span class="n">poolp</span> <span class="p">)((</span><span class="n">uchar</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="p">(</span><span class="n">usedpools</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">-</span> <span class="mi">2</span><span class="o">*</span><span class="k">sizeof</span><span class="p">(</span><span class="n">block</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></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="o">*</span><span class="n">p</span> <span class="o">=</span> <span class="n">usedpools</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=&gt;</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">nextpool</span> <span class="n">and</span> <span class="n">p</span><span class="o">-&gt;</span><span class="n">prevpool</span> <span class="n">are</span> <span class="n">both</span> <span class="n">p</span>
</span></span></code></pre></div><p>为了看懂这步的trick, 心好累&gt;_&lt;#</p>
<p>直接上图</p>
<p><img src="/imgs/python-source/python-memory-usedpool.png" alt="arena"></p>
<h4 id="new一个pool时维护">new一个pool时维护</h4>
<p><code>init</code>获得的情况, 其实就是将刚刚从arena中获取的pool加入到 usedpools 对应的双向链表中, 然后初始化, 然后返回block</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">         <span class="nl">init_pool</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">              <span class="cm">/* Frontlink to used pools. */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">              <span class="c1">// 1. 获取得到usedpools链表头
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="n">next</span> <span class="o">=</span> <span class="n">usedpools</span><span class="p">[</span><span class="n">size</span> <span class="o">+</span> <span class="n">size</span><span class="p">];</span> <span class="cm">/* == prev */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">              <span class="c1">// 2. 将新的pool加入到双向链表
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextpool</span> <span class="o">=</span> <span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">pool</span><span class="o">-&gt;</span><span class="n">prevpool</span> <span class="o">=</span> <span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">next</span><span class="o">-&gt;</span><span class="n">nextpool</span> <span class="o">=</span> <span class="n">pool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">next</span><span class="o">-&gt;</span><span class="n">prevpool</span> <span class="o">=</span> <span class="n">pool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">pool</span><span class="o">-&gt;</span><span class="n">ref</span><span class="p">.</span><span class="n">count</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">              <span class="c1">// 3. 后面的是具体pool和block的了
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>              <span class="k">if</span> <span class="p">(</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">szidx</span> <span class="o">==</span> <span class="n">size</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                  <span class="cm">/* Luckily, this pool last contained blocks
</span></span></span><span class="line"><span class="cl"><span class="cm">                   * of the same size class, so its header
</span></span></span><span class="line"><span class="cl"><span class="cm">                   * and free list are already initialized.
</span></span></span><span class="line"><span class="cl"><span class="cm">                   */</span>
</span></span><span class="line"><span class="cl">                  <span class="n">bp</span> <span class="o">=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                  <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">block</span> <span class="o">**</span><span class="p">)</span><span class="n">bp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                  <span class="n">UNLOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                  <span class="k">return</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">bp</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="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">               * Initialize the pool header, set up the free list to
</span></span></span><span class="line"><span class="cl"><span class="cm">               * contain just the second block, and return the first
</span></span></span><span class="line"><span class="cl"><span class="cm">               * block.
</span></span></span><span class="line"><span class="cl"><span class="cm">               */</span>
</span></span><span class="line"><span class="cl">              <span class="n">pool</span><span class="o">-&gt;</span><span class="n">szidx</span> <span class="o">=</span> <span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">size</span> <span class="o">=</span> <span class="n">INDEX2SIZE</span><span class="p">(</span><span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">              <span class="n">bp</span> <span class="o">=</span> <span class="p">(</span><span class="n">block</span> <span class="o">*</span><span class="p">)</span><span class="n">pool</span> <span class="o">+</span> <span class="n">POOL_OVERHEAD</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextoffset</span> <span class="o">=</span> <span class="n">POOL_OVERHEAD</span> <span class="o">+</span> <span class="p">(</span><span class="n">size</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">              <span class="n">pool</span><span class="o">-&gt;</span><span class="n">maxnextoffset</span> <span class="o">=</span> <span class="n">POOL_SIZE</span> <span class="o">-</span> <span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span> <span class="o">=</span> <span class="n">bp</span> <span class="o">+</span> <span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="o">*</span><span class="p">(</span><span class="n">block</span> <span class="o">**</span><span class="p">)(</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span><span class="p">)</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">UNLOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">              <span class="k">return</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">bp</span><span class="p">;</span>   <span class="c1">// here
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="p">}</span>
</span></span></code></pre></div><h4 id="从现有pool中获取block">从现有pool中获取block</h4>
<p>从现有的pool, 其实就是 usedpools得到双向链表头部, 判断是不是空链表, 不是的话代表有可用的pool, 直接从里面获取</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">((</span><span class="n">nbytes</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">SMALL_REQUEST_THRESHOLD</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="n">LOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">          <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">           * Most frequent paths first
</span></span></span><span class="line"><span class="cl"><span class="cm">           */</span>
</span></span><span class="line"><span class="cl">          <span class="n">size</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">nbytes</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="n">ALIGNMENT_SHIFT</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">          <span class="n">pool</span> <span class="o">=</span> <span class="n">usedpools</span><span class="p">[</span><span class="n">size</span> <span class="o">+</span> <span class="n">size</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">          <span class="c1">// 注意这里的判断, pool != pool-&gt; nextpool 表示得到的链表不是空的
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="k">if</span> <span class="p">(</span><span class="n">pool</span> <span class="o">!=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextpool</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">               * There is a used pool for this size class.
</span></span></span><span class="line"><span class="cl"><span class="cm">               * Pick up the head block of its free list.
</span></span></span><span class="line"><span class="cl"><span class="cm">               */</span>
</span></span><span class="line"><span class="cl">              <span class="o">++</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">ref</span><span class="p">.</span><span class="n">count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">bp</span> <span class="o">=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">assert</span><span class="p">(</span><span class="n">bp</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">              <span class="k">if</span> <span class="p">((</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">block</span> <span class="o">**</span><span class="p">)</span><span class="n">bp</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                  <span class="n">UNLOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                  <span class="k">return</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">bp</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="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">               * Reached the end of the free list, try to extend it.
</span></span></span><span class="line"><span class="cl"><span class="cm">               */</span>
</span></span><span class="line"><span class="cl">              <span class="k">if</span> <span class="p">(</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextoffset</span> <span class="o">&lt;=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">maxnextoffset</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                  <span class="cm">/* There is room for another block. */</span>
</span></span><span class="line"><span class="cl">                  <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span> <span class="o">=</span> <span class="p">(</span><span class="n">block</span><span class="o">*</span><span class="p">)</span><span class="n">pool</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                                    <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextoffset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                  <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextoffset</span> <span class="o">+=</span> <span class="n">INDEX2SIZE</span><span class="p">(</span><span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                  <span class="o">*</span><span class="p">(</span><span class="n">block</span> <span class="o">**</span><span class="p">)(</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span><span class="p">)</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                  <span class="n">UNLOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                  <span class="k">return</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">bp</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="cm">/* Pool is full, unlink from used pools. */</span>
</span></span><span class="line"><span class="cl">              <span class="n">next</span> <span class="o">=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextpool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">pool</span> <span class="o">=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">prevpool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">next</span><span class="o">-&gt;</span><span class="n">prevpool</span> <span class="o">=</span> <span class="n">pool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextpool</span> <span class="o">=</span> <span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">              <span class="n">UNLOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">              <span class="k">return</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">bp</span><span class="p">;</span>   <span class="c1">// here
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>          <span class="p">}</span>
</span></span></code></pre></div><h4 id="全局结构">全局结构</h4>
<p><img src="/imgs/python-source/python-memory-usedpool2.png" alt="arena"></p>
<hr>
<p>先这样吧, Python中整个内存池基本结构和机制大概如此, 是不是发现有好多数组/链表等等, 在分配/回收上处理下做成各种池&hellip;..</p>
<p>后面还有内存相关的就是垃圾收集了, 后面再说了吧</p>
<p>wklken</p>
<p>2015-08-29</p>
]]></content>
		</item>
		
		<item>
			<title>Python源码阅读-内存管理机制(一)</title>
			<link>https://wklken.me/posts/2015/08/29/python-source-memory-1.html</link>
			<pubDate>Sat, 29 Aug 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/08/29/python-source-memory-1.html</guid>
			<description>========================== 基本阅读完了, 只是没时间梳理, 趁着这今天时间比较空 逐步梳理, 发上来&amp;hellip;&amp;hellip;也算是小结下, 要开始准备简历找工作了&amp;g</description>
			<content type="html"><![CDATA[<p>==========================</p>
<p>基本阅读完了, 只是没时间梳理, 趁着这今天时间比较空</p>
<p>逐步梳理, 发上来&hellip;&hellip;也算是小结下, 要开始准备简历找工作了&gt;_&lt;#</p>
<p>这篇略长, 带很多图, 所以一分为二</p>
<hr>
<h2 id="python的内存管理架构">Python的内存管理架构</h2>
<h3 id="基本分层">基本分层</h3>
<p>在<code>Objects/obmalloc.c</code>源码中, 给了一个分层划分</p>
<pre tabindex="0"><code>    _____   ______   ______       ________
   [ int ] [ dict ] [ list ] ... [ string ]       Python core         |
+3 | &lt;----- Object-specific memory -----&gt; | &lt;-- Non-object memory --&gt; |
    _______________________________       |                           |
   [   Python&#39;s object allocator   ]      |                           |
+2 | ####### Object memory ####### | &lt;------ Internal buffers ------&gt; |
    ______________________________________________________________    |
   [          Python&#39;s raw memory allocator (PyMem_ API)          ]   |
+1 | &lt;----- Python memory (under PyMem manager&#39;s control) ------&gt; |   |
    __________________________________________________________________
   [    Underlying general-purpose allocator (ex: C library malloc)   ]
 0 | &lt;------ Virtual memory allocated for the python process -------&gt; |

   =========================================================================
    _______________________________________________________________________
   [                OS-specific Virtual Memory Manager (VMM)               ]
-1 | &lt;--- Kernel dynamic storage allocation &amp; management (page-based) ---&gt; |
    __________________________________   __________________________________
   [                                  ] [                                  ]
-2 | &lt;-- Physical memory: ROM/RAM --&gt; | | &lt;-- Secondary storage (swap) --&gt; |
</code></pre><p>可以看到</p>
<pre tabindex="0"><code>layer 3: Object-specific memory(int/dict/list/string....)
         Python 实现并维护
         更高抽象层次的内存管理策略, 主要是各类特定对象的缓冲池机制. 具体见前面几篇涉及的内存分配机制

layer 2: Python&#39;s object allocator
         Python 实现并维护
         实现了创建/销毁Python对象的接口(PyObject_New/Del), 涉及对象参数/引用计数等

layer 1: Python&#39;s raw memory allocator (PyMem_ API)
         Python 实现并维护, 包装了第0层的内存管理接口, 提供统一的raw memory管理接口
         封装的原因: 不同操作系统 C 行为不一定一致, 保证可移植性, 相同语义相同行为

layer 0: Underlying general-purpose allocator (ex: C library malloc)
         操作系统提供的内存管理接口, 由操作系统实现并管理, Python不能干涉这一层的行为
</code></pre><p>第三层<code>layer 3</code>前面已经介绍过了, 几乎每种常用的数据类型都伴有一套缓冲池机制.</p>
<p>在这里, 我们关注的是<code>layer 2/1</code></p>
<p>简要介绍下<code>layer 1</code>, 然后重点关注<code>layer 2</code>, 这才是重点</p>
<h3 id="layer-1-pymem_-api">layer 1: PyMem_ API</h3>
<p><code>PyMem_ API</code>是对操作系统内存管理接口进行的封装</p>
<p>查看<code>pymem.h</code>可以看到</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// Raw memory interface
</span></span></span><span class="line"><span class="cl"><span class="c1">// 这里存在三个宏定义, 宏可以避免一次函数调用的开销, 提高运行效率
</span></span></span><span class="line"><span class="cl"><span class="c1">// 不允许非配空间大小为0的内存空间
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define PyMem_MALLOC(n)     ((size_t)(n) &gt; (size_t)PY_SSIZE_T_MAX ? NULL \
</span></span></span><span class="line"><span class="cl"><span class="cp">                : malloc((n) ? (n) : 1))
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#define PyMem_REALLOC(p, n) ((size_t)(n) &gt; (size_t)PY_SSIZE_T_MAX  ? NULL \
</span></span></span><span class="line"><span class="cl"><span class="cp">                : realloc((p), (n) ? (n) : 1))
</span></span></span><span class="line"><span class="cl"><span class="cp">#define PyMem_FREE      free
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 这里做了三个函数的声明, 平台独立的 malloc/realloc/free
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">PyAPI_FUNC</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span> <span class="n">PyMem_Malloc</span><span class="p">(</span><span class="n">size_t</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">PyAPI_FUNC</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span> <span class="n">PyMem_Realloc</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">,</span> <span class="n">size_t</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="n">PyAPI_FUNC</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="n">PyMem_Free</span><span class="p">(</span><span class="kt">void</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="c1">// ============================================================
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// Type-oriented memory interface
</span></span></span><span class="line"><span class="cl"><span class="c1">// 这里还有三个类型相关的内存接口, 批量分配/重分配 n 个 类型为 type内存
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define PyMem_New(type, n) \
</span></span></span><span class="line"><span class="cl"><span class="cp">  ( ((size_t)(n) &gt; PY_SSIZE_T_MAX / sizeof(type)) ? NULL :  \
</span></span></span><span class="line"><span class="cl"><span class="cp">    ( (type *) PyMem_Malloc((n) * sizeof(type)) ) )
</span></span></span><span class="line"><span class="cl"><span class="cp">#define PyMem_NEW(type, n) \
</span></span></span><span class="line"><span class="cl"><span class="cp">  ( ((size_t)(n) &gt; PY_SSIZE_T_MAX / sizeof(type)) ? NULL :  \
</span></span></span><span class="line"><span class="cl"><span class="cp">    ( (type *) PyMem_MALLOC((n) * sizeof(type)) ) )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define PyMem_Resize(p, type, n) \
</span></span></span><span class="line"><span class="cl"><span class="cp">  ( (p) = ((size_t)(n) &gt; PY_SSIZE_T_MAX / sizeof(type)) ? NULL :    \
</span></span></span><span class="line"><span class="cl"><span class="cp">    (type *) PyMem_Realloc((p), (n) * sizeof(type)) )
</span></span></span><span class="line"><span class="cl"><span class="cp">#define PyMem_RESIZE(p, type, n) \
</span></span></span><span class="line"><span class="cl"><span class="cp">  ( (p) = ((size_t)(n) &gt; PY_SSIZE_T_MAX / sizeof(type)) ? NULL :    \
</span></span></span><span class="line"><span class="cl"><span class="cp">    (type *) PyMem_REALLOC((p), (n) * sizeof(type)) )
</span></span></span></code></pre></div><p>然后<code>object.c</code>中, 我们关注<code>实现</code>, 三个<code>实现</code>的函数调用了对应的宏</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 使用 C 写Python扩展模块时使用函数而不是对应的宏
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl"><span class="nf">PyMem_Malloc</span><span class="p">(</span><span class="n">size_t</span> <span class="n">nbytes</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="k">return</span> <span class="n">PyMem_MALLOC</span><span class="p">(</span><span class="n">nbytes</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="kt">void</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl"><span class="nf">PyMem_Realloc</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">p</span><span class="p">,</span> <span class="n">size_t</span> <span class="n">nbytes</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="k">return</span> <span class="n">PyMem_REALLOC</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">nbytes</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="kt">void</span>
</span></span><span class="line"><span class="cl"><span class="nf">PyMem_Free</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">p</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="n">PyMem_FREE</span><span class="p">(</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这些接口都相对简单</p>
<p>好了, 结束, 开始关注<code>layer 2: Python's object allocator</code></p>
<hr>
<h2 id="python-的内存分配策略">Python 的内存分配策略</h2>
<p>先来看<code>Objects/obmalloc.c</code>中的一段注释</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm"> * &#34;Memory management is where the rubber meets the road -- if we do the wrong
</span></span></span><span class="line"><span class="cl"><span class="cm"> * thing at any level, the results will not be good. And if we don&#39;t make the
</span></span></span><span class="line"><span class="cl"><span class="cm"> * levels work well together, we are in serious trouble.&#34; (1)
</span></span></span><span class="line"><span class="cl"><span class="cm"> *
</span></span></span><span class="line"><span class="cl"><span class="cm"> * (1) Paul R. Wilson, Mark S. Johnstone, Michael Neely, and David Boles,
</span></span></span><span class="line"><span class="cl"><span class="cm"> *    &#34;Dynamic Storage Allocation: A Survey and Critical Review&#34;,
</span></span></span><span class="line"><span class="cl"><span class="cm"> *    in Proc. 1995 Int&#39;l. Workshop on Memory Management, September 1995.
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span></code></pre></div><p>Python引入了内存池机制, 用于管理对小块内存的申请和释放</p>
<p>逻辑</p>
<pre tabindex="0"><code>1. 如果要分配的内存空间大于 SMALL_REQUEST_THRESHOLD bytes(512 bytes), 将直接使用layer 1的内存分配接口进行分配
2. 否则, 使用不同的block来满足分配需求
</code></pre><p>整个小块内存池可以视为一个层次结构</p>
<pre tabindex="0"><code>1. 内存池(概念上的, 标识Python对于整个小块内存分配和释放的内存管理机制)
2. arena
3. pool
4. block
</code></pre><h3 id="block">block</h3>
<p>Python内存的最小单位, 所有block长度都是8字节对齐的</p>
<p>注意这里block只是一个概念, 在源代码中并没有实体存在.</p>
<p>不同类型block, 对应不同内存大小, 这个内存大小的值被称为<code>size class</code>.</p>
<p>不同长度的block</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"> <span class="o">*</span> <span class="n">Request</span> <span class="n">in</span> <span class="n">bytes</span>     <span class="n">Size</span> <span class="n">of</span> <span class="n">allocated</span> <span class="n">block</span>      <span class="n">Size</span> <span class="n">class</span> <span class="n">idx</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span> <span class="o">----------------------------------------------------------------</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span>        <span class="mi">1</span><span class="o">-</span><span class="mi">8</span>                     <span class="mi">8</span>                       <span class="mi">0</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span>        <span class="mi">9</span><span class="o">-</span><span class="mi">16</span>                   <span class="mi">16</span>                       <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span>       <span class="mi">17</span><span class="o">-</span><span class="mi">24</span>                   <span class="mi">24</span>                       <span class="mi">2</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span>       <span class="mi">25</span><span class="o">-</span><span class="mi">32</span>                   <span class="mi">32</span>                       <span class="mi">3</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span>       <span class="mi">33</span><span class="o">-</span><span class="mi">40</span>                   <span class="mi">40</span>                       <span class="mi">4</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span>       <span class="mi">41</span><span class="o">-</span><span class="mi">48</span>                   <span class="mi">48</span>                       <span class="mi">5</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span>       <span class="mi">49</span><span class="o">-</span><span class="mi">56</span>                   <span class="mi">56</span>                       <span class="mi">6</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span>       <span class="mi">57</span><span class="o">-</span><span class="mi">64</span>                   <span class="mi">64</span>                       <span class="mi">7</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span>       <span class="mi">65</span><span class="o">-</span><span class="mi">72</span>                   <span class="mi">72</span>                       <span class="mi">8</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span>        <span class="p">...</span>                   <span class="p">...</span>                     <span class="p">...</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span>      <span class="mi">497</span><span class="o">-</span><span class="mi">504</span>                 <span class="mi">504</span>                      <span class="mi">62</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span>      <span class="mi">505</span><span class="o">-</span><span class="mi">512</span>                 <span class="mi">512</span>                      <span class="mi">63</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span>      <span class="mi">0</span><span class="p">,</span> <span class="n">SMALL_REQUEST_THRESHOLD</span> <span class="o">+</span> <span class="mi">1</span> <span class="n">and</span> <span class="nl">up</span><span class="p">:</span> <span class="n">routed</span> <span class="n">to</span> <span class="n">the</span> <span class="n">underlying</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span>      <span class="n">allocator</span><span class="p">.</span>
</span></span><span class="line"><span class="cl"> <span class="err">*/</span>
</span></span></code></pre></div><p>例如</p>
<pre tabindex="0"><code>申请一块大小28字节的内存, 实际从内存中划到32字节的一个block (从size class index为3的pool里面划出)
</code></pre><p>图示:</p>
<p><img src="/imgs/python-source/python-memory-blocks.png" alt="blocks"></p>
<p>注意: 这里有个<code>Size class idx</code>, 这个主要为了后面pool中用到</p>
<p><code>size class</code>和<code>size class index</code>之间的转换</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define ALIGNMENT               8               </span><span class="cm">/* must be 2^N */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#define ALIGNMENT_SHIFT         3
</span></span></span><span class="line"><span class="cl"><span class="cp">#define ALIGNMENT_MASK          (ALIGNMENT - 1)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// size class index =&gt; size class
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define INDEX2SIZE(I) (((uint)(I) + 1) &lt;&lt; ALIGNMENT_SHIFT)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/* 即
</span></span></span><span class="line"><span class="cl"><span class="cm">    (0+1) * 8 = 8
</span></span></span><span class="line"><span class="cl"><span class="cm">    (1+1) * 8 = 16
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// size class =&gt; size class index
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="n">size</span> <span class="o">=</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">nbytes</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="n">ALIGNMENT_SHIFT</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/* 即
</span></span></span><span class="line"><span class="cl"><span class="cm">    (8 - 1) / 8 = 0
</span></span></span><span class="line"><span class="cl"><span class="cm">    (16 - 8) / 8 = 1
</span></span></span><span class="line"><span class="cl"><span class="cm">*/</span>
</span></span></code></pre></div><h3 id="pool">pool</h3>
<p>pool管理block, 一个pool管理着一堆有固定大小的内存块</p>
<p>本质: pool管理着一大块内存, 它有一定的策略, 将这块大的内存划分为多个大小一致的小块内存.</p>
<h4 id="pool-size">pool size</h4>
<p>在Python中, 一个pool的大小通常为一个系统内存页. 4kB</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">obmalloc</span><span class="p">.</span><span class="n">c</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#define SYSTEM_PAGE_SIZE        (4 * 1024)
</span></span></span><span class="line"><span class="cl"><span class="cp">#define SYSTEM_PAGE_SIZE_MASK   (SYSTEM_PAGE_SIZE - 1)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define POOL_SIZE               SYSTEM_PAGE_SIZE        </span><span class="cm">/* must be 2^N */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#define POOL_SIZE_MASK          SYSTEM_PAGE_SIZE_MASK
</span></span></span></code></pre></div><h4 id="pool组成">pool组成</h4>
<blockquote>
<p>pool的4kB内存 = pool_header + block集合(N多大小一样的block)</p>
</blockquote>
<p>pool_header</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/* Pool for small blocks. */</span>
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">pool_header</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">union</span> <span class="p">{</span> <span class="n">block</span> <span class="o">*</span><span class="n">_padding</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">uint</span> <span class="n">count</span><span class="p">;</span> <span class="p">}</span> <span class="n">ref</span><span class="p">;</span>          <span class="cm">/* number of allocated blocks    */</span>
</span></span><span class="line"><span class="cl">    <span class="n">block</span> <span class="o">*</span><span class="n">freeblock</span><span class="p">;</span>                   <span class="cm">/* pool&#39;s free list head         */</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">pool_header</span> <span class="o">*</span><span class="n">nextpool</span><span class="p">;</span>       <span class="cm">/* next pool of this size class  */</span>
</span></span><span class="line"><span class="cl">    <span class="k">struct</span> <span class="n">pool_header</span> <span class="o">*</span><span class="n">prevpool</span><span class="p">;</span>       <span class="cm">/* previous pool       &#34;&#34;        */</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">arenaindex</span><span class="p">;</span>                    <span class="cm">/* index into arenas of base adr */</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">szidx</span><span class="p">;</span>                         <span class="cm">/* block size class index        */</span> <span class="o">-</span> <span class="n">size</span> <span class="n">class</span> <span class="n">index</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">nextoffset</span><span class="p">;</span>                    <span class="cm">/* bytes to virgin block         */</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">maxnextoffset</span><span class="p">;</span>                 <span class="cm">/* largest valid nextoffset      */</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>pool_header的作用</p>
<pre tabindex="0"><code>1. 与其他pool链接, 组成双向链表
2. 维护pool中可用的block, 单链表
3. 保存 szidx , 这个和该pool中block的大小有关系, (block size=8, szidx=0), (block size=16, szidx=1)...用于内存分配时匹配到拥有对应大小block的pool
4. arenaindex, 后面说
</code></pre><p>结构图:
<img src="/imgs/python-source/python-memory-pools.png" alt="pools"></p>
<h4 id="pool初始化">pool初始化</h4>
<p>从内存中初始化一个全新的空的<code>pool</code></p>
<p><code>Objects/obmalloc.c</code>的</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="o">*</span>
</span></span><span class="line"><span class="cl"><span class="nf">PyObject_Malloc</span><span class="p">(</span><span class="n">size_t</span> <span class="n">nbytes</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="nl">init_pool</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 1. 连接到 used_pools 双向链表, 作为表头
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 注意, 这里 usedpools[0] 保存着 block size = 8 的所有used_pools的表头
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="cm">/* Frontlink to used pools. */</span>
</span></span><span class="line"><span class="cl">            <span class="n">next</span> <span class="o">=</span> <span class="n">usedpools</span><span class="p">[</span><span class="n">size</span> <span class="o">+</span> <span class="n">size</span><span class="p">];</span> <span class="cm">/* == prev */</span>
</span></span><span class="line"><span class="cl">            <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextpool</span> <span class="o">=</span> <span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">pool</span><span class="o">-&gt;</span><span class="n">prevpool</span> <span class="o">=</span> <span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">next</span><span class="o">-&gt;</span><span class="n">nextpool</span> <span class="o">=</span> <span class="n">pool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">next</span><span class="o">-&gt;</span><span class="n">prevpool</span> <span class="o">=</span> <span class="n">pool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">pool</span><span class="o">-&gt;</span><span class="n">ref</span><span class="p">.</span><span class="n">count</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</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"><span class="c1"></span>            <span class="k">if</span> <span class="p">(</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">szidx</span> <span class="o">==</span> <span class="n">size</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="cm">/* Luckily, this pool last contained blocks
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * of the same size class, so its header
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * and free list are already initialized.
</span></span></span><span class="line"><span class="cl"><span class="cm">                 */</span>
</span></span><span class="line"><span class="cl">                <span class="n">bp</span> <span class="o">=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">block</span> <span class="o">**</span><span class="p">)</span><span class="n">bp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="n">UNLOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">bp</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="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">             * Initialize the pool header, set up the free list to
</span></span></span><span class="line"><span class="cl"><span class="cm">             * contain just the second block, and return the first
</span></span></span><span class="line"><span class="cl"><span class="cm">             * block.
</span></span></span><span class="line"><span class="cl"><span class="cm">             */</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 开始初始化pool_header
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 这里 size = (uint)(nbytes - 1) &gt;&gt; ALIGNMENT_SHIFT;  其实是Size class idx, 即szidx
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">pool</span><span class="o">-&gt;</span><span class="n">szidx</span> <span class="o">=</span> <span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 计算获得每个block的size
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">size</span> <span class="o">=</span> <span class="n">INDEX2SIZE</span><span class="p">(</span><span class="n">size</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 注意 #define POOL_OVERHEAD           ROUNDUP(sizeof(struct pool_header))
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// bp =&gt; 初始化为pool + pool_header size,  跳过pool_header的内存
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">bp</span> <span class="o">=</span> <span class="p">(</span><span class="n">block</span> <span class="o">*</span><span class="p">)</span><span class="n">pool</span> <span class="o">+</span> <span class="n">POOL_OVERHEAD</span><span class="p">;</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"><span class="c1"></span>            <span class="c1">// #define POOL_SIZE               SYSTEM_PAGE_SIZE        /* must be 2^N */
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// POOL_SIZE = 4kb, POOL_OVERHEAD = pool_header size
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 下一个偏移位置: pool_header size + 2 * size
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextoffset</span> <span class="o">=</span> <span class="n">POOL_OVERHEAD</span> <span class="o">+</span> <span class="p">(</span><span class="n">size</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 4kb - size
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">pool</span><span class="o">-&gt;</span><span class="n">maxnextoffset</span> <span class="o">=</span> <span class="n">POOL_SIZE</span> <span class="o">-</span> <span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// freeblock指向 bp + size = pool_header size + size
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span> <span class="o">=</span> <span class="n">bp</span> <span class="o">+</span> <span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 赋值NULL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="o">*</span><span class="p">(</span><span class="n">block</span> <span class="o">**</span><span class="p">)(</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span><span class="p">)</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">UNLOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">bp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span></code></pre></div><p>初始化后的图</p>
<p><img src="/imgs/python-source/python-memory-pools2.png" alt="pool2"></p>
<h4 id="pool进行block分配---0-总体代码">pool进行block分配 - 0 总体代码</h4>
<p>总体分配的代码如下</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">     <span class="k">if</span> <span class="p">(</span><span class="n">pool</span> <span class="o">!=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextpool</span><span class="p">)</span> <span class="p">{</span>   <span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">             * There is a used pool for this size class.
</span></span></span><span class="line"><span class="cl"><span class="cm">             * Pick up the head block of its free list.
</span></span></span><span class="line"><span class="cl"><span class="cm">             */</span>
</span></span><span class="line"><span class="cl">            <span class="o">++</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">ref</span><span class="p">.</span><span class="n">count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">bp</span> <span class="o">=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span><span class="p">;</span> <span class="c1">// 指针指向空闲block起始位置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">assert</span><span class="p">(</span><span class="n">bp</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 代码-1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 调整 pool-&gt;freeblock (假设A节点)指向链表下一个, 即bp首字节指向的下一个节点(假设B节点) , 如果此时!= NULL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 表示 A节点可用, 直接返回
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">if</span> <span class="p">((</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">block</span> <span class="o">**</span><span class="p">)</span><span class="n">bp</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">UNLOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">bp</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="c1">// 代码-2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">             * Reached the end of the free list, try to extend it.
</span></span></span><span class="line"><span class="cl"><span class="cm">             */</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 有足够的空间, 分配一个, pool-&gt;freeblock 指向后移
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">if</span> <span class="p">(</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextoffset</span> <span class="o">&lt;=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">maxnextoffset</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="cm">/* There is room for another block. */</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 变更位置信息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span> <span class="o">=</span> <span class="p">(</span><span class="n">block</span><span class="o">*</span><span class="p">)</span><span class="n">pool</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                                  <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextoffset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextoffset</span> <span class="o">+=</span> <span class="n">INDEX2SIZE</span><span class="p">(</span><span class="n">size</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="o">*</span><span class="p">(</span><span class="n">block</span> <span class="o">**</span><span class="p">)(</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span><span class="p">)</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span> <span class="c1">// 注意, 指向NULL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">UNLOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="c1">// 返回bp
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="k">return</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">bp</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="c1">// 代码-3
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="cm">/* Pool is full, unlink from used pools. */</span>  <span class="c1">// 满了, 需要从下一个pool获取
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">next</span> <span class="o">=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextpool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">pool</span> <span class="o">=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">prevpool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">next</span><span class="o">-&gt;</span><span class="n">prevpool</span> <span class="o">=</span> <span class="n">pool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextpool</span> <span class="o">=</span> <span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">UNLOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">bp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span></code></pre></div><h4 id="pool进行block分配---1-刚开始">pool进行block分配 - 1 刚开始</h4>
<p>内存块尚未分配完, 且此时不存在回收的block, 全新进来的时候, 分配第一块block</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="p">(</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">block</span> <span class="o">**</span><span class="p">)</span><span class="n">bp</span><span class="p">)</span> <span class="o">==</span> <span class="nb">NULL</span>
</span></span></code></pre></div><p>所以进入的逻辑是<code>代码-2</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">            <span class="n">bp</span> <span class="o">=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span><span class="p">;</span> <span class="c1">// 指针指向空闲block起始位置
</span></span></span><span class="line"><span class="cl"><span class="c1"></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="c1">// 代码-2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm">             * Reached the end of the free list, try to extend it.
</span></span></span><span class="line"><span class="cl"><span class="cm">             */</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 有足够的空间, 分配一个, pool-&gt;freeblock 指向后移
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">if</span> <span class="p">(</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextoffset</span> <span class="o">&lt;=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">maxnextoffset</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="cm">/* There is room for another block. */</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 变更位置信息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span> <span class="o">=</span> <span class="p">(</span><span class="n">block</span><span class="o">*</span><span class="p">)</span><span class="n">pool</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                                  <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextoffset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">                <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextoffset</span> <span class="o">+=</span> <span class="n">INDEX2SIZE</span><span class="p">(</span><span class="n">size</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="o">*</span><span class="p">(</span><span class="n">block</span> <span class="o">**</span><span class="p">)(</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span><span class="p">)</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span> <span class="c1">// 注意, 指向NULL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="n">UNLOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="c1">// 返回bp
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="k">return</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">bp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span></code></pre></div><p>结果图示</p>
<p><img src="/imgs/python-source/python-memory-pools3.png" alt="pool2"></p>
<h4 id="pool进行block分配---2-回收了某几个block">pool进行block分配 - 2 回收了某几个block</h4>
<p>回收涉及的代码</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span>
</span></span><span class="line"><span class="cl"><span class="nf">PyObject_Free</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">p</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="n">poolp</span> <span class="n">pool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">block</span> <span class="o">*</span><span class="n">lastfree</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">poolp</span> <span class="n">next</span><span class="p">,</span> <span class="n">prev</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">uint</span> <span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">pool</span> <span class="o">=</span> <span class="n">POOL_ADDR</span><span class="p">(</span><span class="n">p</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">Py_ADDRESS_IN_RANGE</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">pool</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* We allocated this address. */</span>
</span></span><span class="line"><span class="cl">        <span class="n">LOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* Link p to the start of the pool&#39;s freeblock list.  Since
</span></span></span><span class="line"><span class="cl"><span class="cm">         * the pool had at least the p block outstanding, the pool
</span></span></span><span class="line"><span class="cl"><span class="cm">         * wasn&#39;t empty (so it&#39;s already in a usedpools[] list, or
</span></span></span><span class="line"><span class="cl"><span class="cm">         * was full and is in no list -- it&#39;s not in the freeblocks
</span></span></span><span class="line"><span class="cl"><span class="cm">         * list in any case).
</span></span></span><span class="line"><span class="cl"><span class="cm">         */</span>
</span></span><span class="line"><span class="cl">        <span class="n">assert</span><span class="p">(</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">ref</span><span class="p">.</span><span class="n">count</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span>            <span class="cm">/* else it was empty */</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// p被释放, p的第一个字节值被设置为当前freeblock的值
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="o">*</span><span class="p">(</span><span class="n">block</span> <span class="o">**</span><span class="p">)</span><span class="n">p</span> <span class="o">=</span> <span class="n">lastfree</span> <span class="o">=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// freeblock被更新为指向p的首地址
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span> <span class="o">=</span> <span class="p">(</span><span class="n">block</span> <span class="o">*</span><span class="p">)</span><span class="n">p</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// 相当于往list中头插入了一个节点
</span></span></span><span class="line"><span class="cl"><span class="c1"></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 class="p">}</span>
</span></span></code></pre></div><p>没释放一个block, 该block就会变成 <code>pool-&gt;freeblock</code> 的头节点, 而单链表一个节点如何指向下一个节点呢? 通过赋值, 节点内存空间保存着下个节点的地址, 最后一个节点指向<code>NULL</code>(知道上面<code>代码-1</code>的判断条件了吧&gt;_&lt;#)</p>
<p>假设已经连续分配了5块, 第1块和第4块被释放</p>
<p>此时内存图示</p>
<p><img src="/imgs/python-source/python-memory-pools4.png" alt="pool2"></p>
<p>此时再一个block分配调用进来, 执行分配, 进入的逻辑是<code>代码-1</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">            <span class="n">bp</span> <span class="o">=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span><span class="p">;</span> <span class="c1">// 指针指向空闲block起始位置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 代码-1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 调整 pool-&gt;freeblock (假设A节点)指向链表下一个, 即bp首字节指向的下一个节点(假设B节点) , 如果此时!= NULL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 表示 A节点可用, 直接返回
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">if</span> <span class="p">((</span><span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">block</span> <span class="o">**</span><span class="p">)</span><span class="n">bp</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">UNLOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">bp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span></code></pre></div><p><img src="/imgs/python-source/python-memory-pools5.png" alt="pool2"></p>
<h4 id="pool进行block分配---3-pool用完了">pool进行block分配 - 3 pool用完了</h4>
<p>pool中内存空间都用完了, 进入<code>代码-3</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">            <span class="n">bp</span> <span class="o">=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">freeblock</span><span class="p">;</span> <span class="c1">// 指针指向空闲block起始位置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 代码-3
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="cm">/* Pool is full, unlink from used pools. */</span>  <span class="c1">// 满了, 需要从下一个pool获取
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">next</span> <span class="o">=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextpool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">pool</span> <span class="o">=</span> <span class="n">pool</span><span class="o">-&gt;</span><span class="n">prevpool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">next</span><span class="o">-&gt;</span><span class="n">prevpool</span> <span class="o">=</span> <span class="n">pool</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">pool</span><span class="o">-&gt;</span><span class="n">nextpool</span> <span class="o">=</span> <span class="n">next</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">UNLOCK</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">bp</span><span class="p">;</span>
</span></span></code></pre></div><p>获取下一个pool(链表上每个pool的block size都是一致的)</p>
<p>好了, pool到此位置, 下篇进入arena</p>
]]></content>
		</item>
		
		<item>
			<title>Python-基础-数据结构小结</title>
			<link>https://wklken.me/posts/2015/08/28/python-base-datastructures.html</link>
			<pubDate>Fri, 28 Aug 2015 23:59:59 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/08/28/python-base-datastructures.html</guid>
			<description>只是一篇笔记, 梳理了下 ==================== 序列 string 基本数据结构, 不解释 可以看下我之前的笔记 Python-基础-字符串小结 Python源码阅读-String list 基本</description>
			<content type="html"><![CDATA[<p>只是一篇笔记, 梳理了下</p>
<p>====================</p>
<h3 id="序列">序列</h3>
<h4 id="string">string</h4>
<p>基本数据结构, 不解释</p>
<p>可以看下我之前的笔记</p>
<ul>
<li><a href="http://www.wklken.me/posts/2013/03/10/python-base-string.html">Python-基础-字符串小结</a></li>
<li><a href="http://www.wklken.me/posts/2014/08/08/python-source-string.html">Python源码阅读-String</a></li>
</ul>
<h4 id="list">list</h4>
<p>基本数据结构, 不解释</p>
<p>可以看下我之前的笔记</p>
<ul>
<li><a href="http://www.wklken.me/posts/2012/12/30/python-base-list.html">Python-基础-列表及列表解析</a></li>
<li><a href="http://www.wklken.me/posts/2014/08/10/python-source-list.html">Python源码阅读-list</a></li>
</ul>
<h4 id="tuple">tuple</h4>
<p>基本数据结构, 不解释</p>
<p>可以看下我之前的笔记</p>
<ul>
<li><a href="http://www.wklken.me/posts/2013/03/09/python-base-tuple.html">Python-基础-元组小结</a></li>
<li><a href="http://www.wklken.me/posts/2014/08/10/python-source-tuple.html">Python源码阅读-tuple</a></li>
</ul>
<h4 id="namedtuple">namedtuple</h4>
<p>在collections中, 从名字可以看出是命名的tuple</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">collections</span><span class="o">.</span><span class="n">namedtuple</span><span class="p">(</span><span class="n">typename</span><span class="p">,</span> <span class="n">field_names</span><span class="p">[,</span> <span class="n">verbose</span><span class="o">=</span><span class="kc">False</span><span class="p">][,</span> <span class="n">rename</span><span class="o">=</span><span class="kc">False</span><span class="p">])</span>
</span></span></code></pre></div><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="n">Named</span> <span class="nb">tuple</span> <span class="n">instances</span> <span class="n">do</span> <span class="ow">not</span> <span class="n">have</span> <span class="n">per</span><span class="o">-</span><span class="n">instance</span> <span class="n">dictionaries</span><span class="p">,</span> <span class="n">so</span> <span class="n">they</span> <span class="n">are</span> <span class="n">lightweight</span> <span class="ow">and</span> <span class="n">require</span> <span class="n">no</span> <span class="n">more</span> <span class="n">memory</span> <span class="n">than</span> <span class="n">regular</span> <span class="n">tuples</span><span class="o">.</span>
</span></span></code></pre></div><p>和一般<code>class+自定义__slots__</code>的功能类似, 不会给每个实例定义<code>__dict__</code>, 可以节省内存</p>
<p>所以, 优点</p>
<pre tabindex="0"><code>1. 可读性更好, 可以当做轻量的类来使用(only attributes)
2. 节省内存
</code></pre><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">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">namedtuple</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">Point</span> <span class="o">=</span> <span class="n">namedtuple</span><span class="p">(</span><span class="s1">&#39;Point&#39;</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;x&#39;</span><span class="p">,</span> <span class="s1">&#39;y&#39;</span><span class="p">],</span> <span class="n">verbose</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Point</span><span class="p">(</span><span class="nb">tuple</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s1">&#39;Point(x, y)&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="vm">__slots__</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="n">_fields</span> <span class="o">=</span> <span class="p">(</span><span class="s1">&#39;x&#39;</span><span class="p">,</span> <span class="s1">&#39;y&#39;</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">__new__</span><span class="p">(</span><span class="n">_cls</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;Create new instance of Point(x, y)&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">_tuple</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="n">_cls</span><span class="p">,</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@classmethod</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">_make</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">iterable</span><span class="p">,</span> <span class="n">new</span><span class="o">=</span><span class="nb">tuple</span><span class="o">.</span><span class="fm">__new__</span><span class="p">,</span> <span class="nb">len</span><span class="o">=</span><span class="nb">len</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;Make a new Point object from a sequence or iterable&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="n">result</span> <span class="o">=</span> <span class="n">new</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">iterable</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">2</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="ne">TypeError</span><span class="p">(</span><span class="s1">&#39;Expected 2 arguments, got </span><span class="si">%d</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="nb">len</span><span class="p">(</span><span class="n">result</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="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;Return a nicely formatted representation string&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s1">&#39;Point(x=</span><span class="si">%r</span><span class="s1">, y=</span><span class="si">%r</span><span class="s1">)&#39;</span> <span class="o">%</span> <span class="bp">self</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">_asdict</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;Return a new OrderedDict which maps field names to their values&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">OrderedDict</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_fields</span><span class="p">,</span> <span class="bp">self</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">_replace</span><span class="p">(</span><span class="n">_self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwds</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;Return a new Point object replacing specified fields with new values&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="n">result</span> <span class="o">=</span> <span class="n">_self</span><span class="o">.</span><span class="n">_make</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="n">kwds</span><span class="o">.</span><span class="n">pop</span><span class="p">,</span> <span class="p">(</span><span class="s1">&#39;x&#39;</span><span class="p">,</span> <span class="s1">&#39;y&#39;</span><span class="p">),</span> <span class="n">_self</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">kwds</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s1">&#39;Got unexpected field names: </span><span class="si">%r</span><span class="s1">&#39;</span> <span class="o">%</span> <span class="n">kwds</span><span class="o">.</span><span class="n">keys</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="k">def</span> <span class="nf">__getnewargs__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;Return self as a plain tuple.  Used by copy and pickle.&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">tuple</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="vm">__dict__</span> <span class="o">=</span> <span class="n">_property</span><span class="p">(</span><span class="n">_asdict</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">__getstate__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;Exclude the OrderedDict from pickling&#39;</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="n">x</span> <span class="o">=</span> <span class="n">_property</span><span class="p">(</span><span class="n">_itemgetter</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="n">doc</span><span class="o">=</span><span class="s1">&#39;Alias for field number 0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">y</span> <span class="o">=</span> <span class="n">_property</span><span class="p">(</span><span class="n">_itemgetter</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="n">doc</span><span class="o">=</span><span class="s1">&#39;Alias for field number 1&#39;</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="o">&gt;&gt;&gt;</span> <span class="n">p</span> <span class="o">=</span> <span class="n">Point</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="mi">11</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="mi">22</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">p</span><span class="o">.</span><span class="n">x</span><span class="p">,</span> <span class="n">p</span><span class="o">.</span><span class="n">y</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="mi">11</span><span class="p">,</span> <span class="mi">22</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">p</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">p</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="mi">11</span><span class="p">,</span> <span class="mi">22</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">p</span>
</span></span><span class="line"><span class="cl"><span class="n">Point</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="mi">11</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="mi">22</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="array">array</h4>
<p><a href="https://docs.python.org/2/library/array.html">python 2 library: array</a></p>
<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="k">class</span> <span class="nc">array</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">typecode</span><span class="p">[,</span> <span class="n">initializer</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 其中, typecode</span>
</span></span><span class="line"><span class="cl"><span class="n">Type</span> <span class="n">code</span>   <span class="n">C</span> <span class="n">Type</span>                            <span class="n">Python</span> <span class="n">Type</span> <span class="n">Minimum</span> <span class="n">size</span> <span class="ow">in</span> <span class="nb">bytes</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;c&#39;</span>         <span class="n">char</span>                              <span class="n">character</span>   <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;b&#39;</span>         <span class="n">signed</span> <span class="n">char</span>                       <span class="nb">int</span>         <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;B&#39;</span>         <span class="n">unsigned</span> <span class="n">char</span>                     <span class="nb">int</span>         <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;u&#39;</span>         <span class="n">Py_UNICODE</span>          <span class="n">Unicode</span> <span class="n">character</span>         <span class="mi">2</span><span class="p">(</span><span class="n">see</span> <span class="n">note</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;h&#39;</span>         <span class="n">signed</span> <span class="n">short</span>                      <span class="nb">int</span>         <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;H&#39;</span>         <span class="n">unsigned</span> <span class="n">short</span>                    <span class="nb">int</span>         <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;i&#39;</span>         <span class="n">signed</span> <span class="nb">int</span>                        <span class="nb">int</span>         <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;I&#39;</span>         <span class="n">unsigned</span> <span class="nb">int</span>                      <span class="n">long</span>        <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;l&#39;</span>         <span class="n">signed</span> <span class="n">long</span>                       <span class="nb">int</span>         <span class="mi">4</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;L&#39;</span>         <span class="n">unsigned</span> <span class="n">long</span>                     <span class="n">long</span>        <span class="mi">4</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;f&#39;</span>         <span class="nb">float</span>                             <span class="nb">float</span>       <span class="mi">4</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;d&#39;</span>         <span class="n">double</span>                            <span class="nb">float</span>       <span class="mi">8</span>
</span></span></code></pre></div><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">array</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">l</span> <span class="o">=</span> <span class="n">array</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="s1">&#39;i&#39;</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">l</span>
</span></span><span class="line"><span class="cl"><span class="n">array</span><span class="p">(</span><span class="s1">&#39;i&#39;</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">l</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mi">5</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">l</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">l</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">l</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">l</span>
</span></span><span class="line"><span class="cl"><span class="n">array</span><span class="p">(</span><span class="s1">&#39;i&#39;</span><span class="p">,</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">])</span>
</span></span></code></pre></div><h4 id="linked-list">linked list</h4>
<p>似乎要在Python中用这个的场景非常之少&hellip;..</p>
<p>也似乎有两种选择</p>
<ol>
<li>自己写一个</li>
<li>用其他数据结构替代</li>
</ol>
<p>具体可以看看这个 <a href="http://stackoverflow.com/questions/280243/python-linked-list">Python Linked List</a></p>
<h3 id="set">set</h3>
<h4 id="base-set">base set</h4>
<p>基本数据结构, 不解释</p>
<p>可以看下我之前的笔记</p>
<ul>
<li><a href="http://www.wklken.me/posts/2013/03/10/python-base-set.html">Python-基础-集合小结</a></li>
</ul>
<h4 id="frozenset">frozenset</h4>
<p>标准库带, 简而言之: frozenset是set的不可变版本, 类似tuple和list的关系</p>
<p><a href="https://docs.python.org/2/library/stdtypes.html#frozenset">文档</a></p>
<p>frozenset可以作为字典键</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="n">s</span> <span class="o">=</span> <span class="nb">set</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">s</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">s</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">s</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">hash</span><span class="p">(</span><span class="n">s</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">TypeError</span><span class="p">:</span> <span class="n">unhashable</span> <span class="nb">type</span><span class="p">:</span> <span class="s1">&#39;set&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">s2</span> <span class="o">=</span> <span class="nb">frozenset</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">s2</span>
</span></span><span class="line"><span class="cl"><span class="nb">frozenset</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">s2</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="mi">4</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">AttributeError</span><span class="p">:</span> <span class="s1">&#39;frozenset&#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;add&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">hash</span><span class="p">(</span><span class="n">s2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span><span class="mi">7699079583225461316</span>
</span></span></code></pre></div><h3 id="dict">dict</h3>
<h4 id="base-dict">base dict</h4>
<p>基本数据结构, 不解释</p>
<p>可以看下我之前的笔记</p>
<ul>
<li><a href="http://www.wklken.me/posts/2013/03/09/python-base-dict.html">Python-基础-字典小结</a></li>
<li><a href="http://www.wklken.me/posts/2014/08/11/python-source-dict.html">Python源码阅读-dict</a></li>
</ul>
<h4 id="ordered-dict">ordered dict</h4>
<p>dict的子类, 会记住放入字典键值对的顺序, <a href="https://docs.python.org/2/library/collections.html#collections.OrderedDict">文档</a></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">collections</span> <span class="kn">import</span> <span class="n">OrderedDict</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span> <span class="o">=</span> <span class="n">OrderedDict</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;c&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;b&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;a&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl"><span class="n">OrderedDict</span><span class="p">([(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;a&#39;</span><span class="p">)])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="o">.</span><span class="n">items</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">[(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;a&#39;</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="o">.</span><span class="n">items</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">[(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;a&#39;</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d2</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d2</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;c&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d2</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;b&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d2</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;a&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d2</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span><span class="mi">1</span><span class="p">:</span> <span class="s1">&#39;b&#39;</span><span class="p">,</span> <span class="mi">2</span><span class="p">:</span> <span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="mi">3</span><span class="p">:</span> <span class="s1">&#39;c&#39;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d2</span><span class="o">.</span><span class="n">items</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">[(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;a&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span><span class="p">)]</span>
</span></span></code></pre></div><h4 id="default-dict">default dict</h4>
<p>defaultdict, 同样是dict的子类, 会自动设置value的默认值, <a href="https://docs.python.org/2/library/collections.html#collections.defaultdict">文档</a></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">collections</span> <span class="kn">import</span> <span class="n">defaultdict</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl"><span class="n">defaultdict</span><span class="p">(</span><span class="o">&lt;</span><span class="nb">type</span> <span class="s1">&#39;list&#39;</span><span class="o">&gt;</span><span class="p">,</span> <span class="p">{})</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="p">[</span><span class="s1">&#39;a&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl"><span class="n">defaultdict</span><span class="p">(</span><span class="o">&lt;</span><span class="nb">type</span> <span class="s1">&#39;list&#39;</span><span class="o">&gt;</span><span class="p">,</span> <span class="p">{</span><span class="s1">&#39;a&#39;</span><span class="p">:</span> <span class="p">[</span><span class="mi">1</span><span class="p">]})</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="p">[</span><span class="s1">&#39;a&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="p">[</span><span class="s1">&#39;a&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="p">[</span><span class="s1">&#39;a&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="p">[</span><span class="s1">&#39;notexists&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">[]</span>
</span></span></code></pre></div><h4 id="others---multidict">others - MultiDict</h4>
<p>一键多值的dict</p>
<p><a href="https://github.com/wklken/pyutils/blob/master/dict/MultiDict.py">bottle里面的MultiDict</a></p>
<p><a href="https://github.com/wklken/pyutils/blob/master/dict/MultiDict2.py">werkzeug里面的版本</a></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="n">d</span> <span class="o">=</span> <span class="n">MultiDict</span><span class="p">([(</span><span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">),</span> <span class="p">(</span><span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span><span class="p">)])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl"><span class="n">MultiDict</span><span class="p">([(</span><span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">),</span> <span class="p">(</span><span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span><span class="p">)])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="p">[</span><span class="s1">&#39;a&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;b&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="o">.</span><span class="n">getlist</span><span class="p">(</span><span class="s1">&#39;a&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="s1">&#39;b&#39;</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="s1">&#39;a&#39;</span> <span class="ow">in</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl"><span class="kc">True</span>
</span></span></code></pre></div><h4 id="others---caseinsensitivedict">others - CaseInsensitiveDict</h4>
<p>key大小写不明感的dict</p>
<p><a href="https://github.com/kennethreitz/requests/blob/master/requests/structures.py#L14">CaseInsensitiveDict</a></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">cid</span> <span class="o">=</span> <span class="n">CaseInsensitiveDict</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">cid</span><span class="p">[</span><span class="s1">&#39;Accept&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;application/json&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">cid</span><span class="p">[</span><span class="s1">&#39;aCCEPT&#39;</span><span class="p">]</span> <span class="o">==</span> <span class="s1">&#39;application/json&#39;</span>  <span class="c1"># True</span>
</span></span></code></pre></div><h4 id="others---callbackdict">others - CallbackDict</h4>
<p>更新时会调用回调函数</p>
<p><a href="https://github.com/wklken/pyutils/blob/master/dict/CallbackDict.py">CallbackDict</a></p>
<h3 id="stack">stack</h3>
<p>Python标准库没有stack实现, 如果要处理, 可以自己写一个, 或者使用现有数据结构替代</p>
<p>use list as stack</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">Stack</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">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">items</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="k">def</span> <span class="nf">isEmpty</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">items</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</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">push</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">item</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">items</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">item</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">pop</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">items</span><span class="o">.</span><span class="n">pop</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">peek</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">items</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">items</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</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">size</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">items</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="queue">queue</h3>
<h4 id="base-queue">base queue</h4>
<p>use list as queue</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">Queue</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">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">items</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="k">def</span> <span class="nf">isEmpty</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">items</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</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">enqueue</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">item</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">items</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">item</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">dequeue</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">items</span><span class="o">.</span><span class="n">pop</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">size</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">items</span><span class="p">)</span>
</span></span></code></pre></div><p>其他, python标准库中的<code>Queue</code>模块, <a href="https://docs.python.org/2/library/queue.html">文档</a></p>
<p>包含</p>
<pre tabindex="0"><code>Queue    FIFO
LifoQueue  LIFO
PriorityQueue  带优先级的
</code></pre><p>多用于多线程资源共享中(一般情况下很少用), 因为是线程安全的</p>
<h4 id="deque">deque</h4>
<p>双端队列, 线程安全, 且左右两端出入队复杂度O(1), <a href="https://docs.python.org/2/library/collections.html#collections.deque">文档</a></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">collections</span> <span class="kn">import</span> <span class="n">deque</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span> <span class="o">=</span> <span class="n">deque</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl"><span class="n">deque</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="o">.</span><span class="n">appendleft</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl"><span class="n">deque</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="mi">4</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl"><span class="n">deque</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span><span class="o">.</span><span class="n">popleft</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="mi">0</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">d</span>
</span></span><span class="line"><span class="cl"><span class="n">deque</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span>
</span></span></code></pre></div><h3 id="堆">堆</h3>
<p>最小堆实现</p>
<p><a href="https://docs.python.org/2/library/heapq.html">文档</a></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">heapq</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">h</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">heapq</span><span class="o">.</span><span class="n">heappush</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;e&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">heapq</span><span class="o">.</span><span class="n">heappush</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;a&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">heapq</span><span class="o">.</span><span class="n">heappush</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">heapq</span><span class="o">.</span><span class="n">heappush</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s1">&#39;d&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">heapq</span><span class="o">.</span><span class="n">heappush</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">h</span>
</span></span><span class="line"><span class="cl"><span class="p">[(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;a&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;e&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s1">&#39;d&#39;</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">heapq</span><span class="o">.</span><span class="n">heappop</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;a&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">h</span>
</span></span><span class="line"><span class="cl"><span class="p">[(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s1">&#39;d&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;e&#39;</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">heapq</span><span class="o">.</span><span class="n">heappop</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">h</span>
</span></span><span class="line"><span class="cl"><span class="p">[(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;e&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s1">&#39;d&#39;</span><span class="p">)]</span>
</span></span></code></pre></div><h3 id="树">树</h3>
<p>标准库没有tree的实现</p>
<p>可以看看这本书的讲解 <a href="http://cbio.ufs.ac.za/live_docs/nbn_tut/trees.html">Introductory Programming in Python
Advanced Data Structures: Trees</a></p>
<h4 id="base-tree">base tree</h4>
<p>自己写一个&gt;_&lt;</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">collections</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">Tree</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">collections</span><span class="o">.</span><span class="n">defaultdict</span><span class="p">(</span><span class="n">Tree</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="binary-tree">binary tree</h4>
<p>二叉树, 关注下这个包 <a href="https://pypi.python.org/pypi/bintrees">bintree</a></p>
<p>包括二叉树/红黑树/AVL树</p>
<h3 id="图">图</h3>
<p>这个暂时没有好的推荐, 一般处理成二维数组, 或者使用类机制实现节点/边</p>
<h3 id="其他">其他</h3>
<h4 id="计数counter">计数counter</h4>
<p><a href="https://docs.python.org/2/library/collections.html#collections.Counter">Counter文档</a></p>
<p>dict子类, 会记录某个key出现的次数, 在做计数/统计的时候非常有用</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">collections</span> <span class="kn">import</span> <span class="n">Counter</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">c</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">(</span><span class="s1">&#39;abracadabra&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">c</span>
</span></span><span class="line"><span class="cl"><span class="n">Counter</span><span class="p">({</span><span class="s1">&#39;a&#39;</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;r&#39;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;b&#39;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;d&#39;</span><span class="p">:</span> <span class="mi">1</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">c</span><span class="o">.</span><span class="n">most_common</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">[(</span><span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="mi">5</span><span class="p">),</span> <span class="p">(</span><span class="s1">&#39;r&#39;</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="p">(</span><span class="s1">&#39;b&#39;</span><span class="p">,</span> <span class="mi">2</span><span class="p">)]</span>
</span></span></code></pre></div><h4 id="bisect">bisect</h4>
<p><a href="https://docs.python.org/2/library/bisect.html">bisect</a>, 维持一个有序列表, 可以用于快速检索</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">bisect</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">bisect</span><span class="o">.</span><span class="n">insort_left</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">bisect</span><span class="o">.</span><span class="n">insort_left</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">bisect</span><span class="o">.</span><span class="n">insort_left</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">bisect</span><span class="o">.</span><span class="n">insort_left</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="mi">7</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">l</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">7</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">bisect</span><span class="o">.</span><span class="n">bisect_left</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>  <span class="c1"># 返回位置或插入后的位置</span>
</span></span><span class="line"><span class="cl"><span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">bisect</span><span class="o">.</span><span class="n">bisect_left</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mi">4</span>
</span></span></code></pre></div><h4 id="struct">struct</h4>
<p>处理和存储二进制数据的时候用到, <a href="https://docs.python.org/2/library/struct.html">文档</a></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">struct</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="n">pack</span><span class="p">(</span><span class="s1">&#39;hhl&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;</span><span class="se">\x00\x01\x00\x02\x00\x00\x00\x03</span><span class="s1">&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">unpack</span><span class="p">(</span><span class="s1">&#39;hhl&#39;</span><span class="p">,</span> <span class="s1">&#39;</span><span class="se">\x00\x01\x00\x02\x00\x00\x00\x03</span><span class="s1">&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
</span></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>&#39;活动&#39;设计的一些trick</title>
			<link>https://wklken.me/posts/2015/08/28/tricks-about-promotion-codes.html</link>
			<pubDate>Fri, 28 Aug 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/08/28/tricks-about-promotion-codes.html</guid>
			<description>在梳理过去做过的项目 之前做项目的时候, 十个月期间做了不少活动, 类型比较多, 有推广/抽奖/小游戏等等. 有些活动需求明确一气呵成, 也有些活动需求</description>
			<content type="html"><![CDATA[<p>在梳理过去做过的项目</p>
<p>之前做项目的时候, 十个月期间做了不少活动, 类型比较多, 有推广/抽奖/小游戏等等.</p>
<p>有些活动需求明确一气呵成, 也有些活动需求模糊一度推倒重来, 还有的活动被刷了&gt;<em>&lt;#</em></p>
<p>好吧, 来说说活动</p>
<p>一些想法, 可以是技术, 非技术</p>
<hr>
<h3 id="目的">目的</h3>
<p>活动的目的是什么? 有没有存在的意义?</p>
<p>存在由于没有想清楚, 耗费人力物力搞了结果不尽人意&hellip;&hellip;所以目的应该先想清楚, 并且明学下来</p>
<h3 id="活动开发简要流程">活动开发简要流程</h3>
<ul>
<li>需求分析: 整体流程, 用户侧细节, 管理侧需求, 统计需求等等, 对整个流程达成一致, 对每个环节的条件/处理逻辑/后续出口等等明确下来, 对一些资质/数字限制等确定下来</li>
<li>设计出图/后端建模-逻辑代码-API/前端开发</li>
<li>测试环境上线</li>
<li>内部测试, 试玩, 针对活动本身给意见. 测试人员测试, 针对逻辑本身, 同时进行网速测试(2g/3g/wifi) / 浏览器测试 / 不同手机-系统测试</li>
<li>修正迭代, 测试</li>
<li>正式上线</li>
<li>管理侧/统计侧上线</li>
<li>推广/监控</li>
<li>活动结束</li>
<li>奖品派发/数据统计等</li>
<li>活动下线</li>
</ul>
<h3 id="quick-and-maybe-dirty">quick and maybe dirty</h3>
<p>由于<code>活动</code>本身的性质, 这类代码逻辑属于<code>短平快</code>一类的.</p>
<p>简而言之: 怎么快怎么来</p>
<ol>
<li>可以不要考虑复用</li>
<li>当然, 有些代码是复用的, 例如CRUD/get some list/check permission/call base service等等</li>
<li>不要考虑将来/以后, 很多活动上了就下了, 不会有所谓的<code>将来</code>, 切忌过渡设计, 空耗费许多精力没有必要</li>
<li>要快</li>
</ol>
<h3 id="注意代码数据部署隔离">注意代码/数据/部署隔离</h3>
<p>前面说过, 很多活动逻辑没有将来</p>
<p>所以, 活动的代码尽量独立, 保证随写随测, 随上随下, 尽量隔离于主体代码之外, 这样上下线也方便</p>
<p>当然, 不可能完全独立, 依赖外部尽量使用独立的服务接口, 被外部依赖提供也尽量通过提供接口解决(情况很少)</p>
<p>数据独立, 包括, 数据库实例/redis or memcached/文件等, 活动需要记录一些数据, 和主体业务独立开来, 尽量不共用, 有条件的话单独提供实例</p>
<p>部署隔离, 尽量不要和关键服务在同一台机器或者共用带宽, 由于<code>活动</code>本身的特质, 可能带来突发的流量, 可能导致带宽/IO/缓存占用/机器负载等变高, 会影响到其他服务.(可以给定独立url, 通过反代定到活动服务)</p>
<h3 id="开发注意">开发注意</h3>
<ul>
<li>做好缓存</li>
<li>每个接口做好资质/权限控制, 这类逻辑放在api代码的前面(判断条件放到最前面), fail fast, 验证通过后才进入主体逻辑代码</li>
<li>友好的异常处理/用户提示</li>
<li>后端需要考虑<code>防刷</code>, 前端需要处理下<code>重复提交</code></li>
<li>做好事务控制(并发), 特别是涉及数字增减的情况, 例如奖品数</li>
<li>涉及步骤的活动, 做好流程限制, 第一步-第二步-第三步&hellip;&hellip;, 防止用户跳过某一步直接进入下一步.(可以通过签加密token的方式)</li>
<li>图片, 尽量放到 CDN (血的教训, 前端一张背景图导致带宽被跑满, 后续用户进不来)</li>
<li>需要有一套成熟的统计系统, 活动数据直接发送到统计系统, 由统计系统统一出数据</li>
<li>对于关键性的步骤/数据, 可以记日志</li>
<li>有必要的话, 做成一期一期的, 有开始结束时间, 自动切换(有些复杂的活动)</li>
<li>有必要的话, (传说中的开关)提供方便的配置或者入口, 可以一键上下线活动/奖品(valid/invalid/shutdown)</li>
<li>对于关键性的代码, 做好注释, 例如一些限制逻辑/数量等等</li>
</ul>
<h3 id="学会打时间差">学会打时间差</h3>
<p>很多活动, 可能是热点? 节日? 等等, 时效性比较强的.</p>
<p>然而, 当活动逻辑很复杂的时候, 又要在规定时间内上线, 这时候可以仔细切分需求,  分不同时间上线.</p>
<p>例如, 一个玩游戏/抽奖/兑奖的活动, 可能分为两部分, 用户侧和管理侧, 用户侧逻辑<code>玩游戏/兑奖/查看是否获奖</code>, 管理侧<code>查看获奖情况/颁奖/新增用户统计/渠道统计/流量统计</code>等等</p>
<p>那么, 可以先保证用户侧完成, 同时加入向统计系统发送统计数据的接口, 然后上线, 保证用户侧主体流程. 上线后开始开发管理侧, 管理侧可以按照运营优先级处理, 例如要查看实时统计信息的话, 先做统计, 保证推广效果, 获奖及颁奖可以稍稍押后, 作为第三阶段上线(如果活动兑奖都是在一个周期结束的话)</p>
<p>例如, 要发奖数据, 如果没有管理后台, 直接库里导一份出来就是了. 其实这时候应该思考, 要不要花力气做管理后台, 大不了活动结束手工操作一下, 十分钟.</p>
<h3 id="下线">下线</h3>
<p>额, 活动做完, 要下线了.</p>
<p>如果程序是带日期限制的, 到点了自动结束, 提示用户活动结束, 活动流程无法走下去.</p>
<p>如果需要人肉, 直接将外网入口去掉即可.</p>
<p>然后, 可以考虑后续了</p>
<p>首先, 要确认, 该记录的统计数据记录了, 该分析的分析了, 该发奖的也发奖了&hellip;&hellip;</p>
<p>首先, 备份代码到活动代码仓库(供后续参考/复用, 防止雷同逻辑/代码要重写), 然后从代码库删除.</p>
<p>线上, 备份数据到备份服务器, 包括数据库数据/日志/文件等等, 如果缓存中有需要dump的, dump出来. 然后下线数据库, 清空缓存, 日志等</p>
<hr>
<p>活动, 很大程度上是一堆<code>临时</code>而<code>无用</code>并且<code>没有技术含量</code>, 而且非常<code>短命</code>的代码组成的, 做多了容易烦躁, 最好一个项目组里轮流处理, 当然有人认领更好.</p>
<p>活动成不成功, 决定因素很多, 但无论如何, 下线前做好review, 防止重复犯错</p>
<p>就这些</p>
<p>wklken</p>
<p>2015-08-28</p>
]]></content>
		</item>
		
		<item>
			<title>一些简单的Python测试题</title>
			<link>https://wklken.me/posts/2015/08/26/python-some-test-questions.html</link>
			<pubDate>Wed, 26 Aug 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/08/26/python-some-test-questions.html</guid>
			<description>一些简单的测试题, 主要来源是网上:) 某种程度, 可以从代码中看出一些东西 编写代码, 打印1-1亿之内的偶数 写一个函数, 用正则表达式清除字符串中[</description>
			<content type="html"><![CDATA[<p>一些简单的测试题, 主要来源是网上:)</p>
<p>某种程度, 可以从代码中看出一些东西</p>
<hr>
<ul>
<li>
<p>编写代码, 打印1-1亿之内的偶数</p>
</li>
<li>
<p>写一个函数, 用正则表达式清除字符串中[]和其中的内容。</p>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">s</span> <span class="o">=</span> <span class="s2">&#34;[lol]你好，帮我把这些markup清掉，[smile]。谢谢！&#34;</span>
</span></span></code></pre></div><ul>
<li>请使用python, 对下面的函数进行处理,</li>
</ul>
<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">hello</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span> <span class="s2">&#34;hello, </span><span class="si">%s</span><span class="s2">&#34;</span> <span class="o">%</span> <span class="n">name</span>
</span></span></code></pre></div><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">&lt;</span><span class="n">function</span> <span class="n">name</span><span class="p">:</span> <span class="n">hello</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="n">function</span> <span class="n">call</span> <span class="n">begin</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="n">hello</span><span class="p">,</span> <span class="n">tom</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="n">function</span> <span class="n">call</span> <span class="n">end</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="n">timecosts</span><span class="p">:</span> <span class="mf">3.81469726562e-06</span><span class="n">s</span><span class="p">]</span>
</span></span></code></pre></div><ul>
<li>写一个函数, 将驼峰命名法字符串转成下划线命名字符串(需考虑各类编码中常见的命名)</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">e</span><span class="o">.</span><span class="n">g</span><span class="o">.</span>  <span class="n">GetItem</span> <span class="o">-&gt;</span> <span class="n">get_item</span>
</span></span><span class="line"><span class="cl">      <span class="n">getItem</span> <span class="o">-&gt;</span> <span class="n">get_item</span>
</span></span><span class="line"><span class="cl">      <span class="n">doIT</span>    <span class="o">-&gt;</span> <span class="n">do_IT</span>
</span></span></code></pre></div><ul>
<li>有一个列表：[1, 2, 3, 4&hellip;n]，n=20；请编写代码打印如下规律的输出：</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="mi">1</span> <span class="p">[</span><span class="mi">1</span><span class="o">*</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="mi">2</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="mi">3</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="o">*</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="mi">4</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="o">*</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="mi">5</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="o">*</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="mi">6</span> <span class="p">[</span><span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="o">*</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</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"><span class="mi">20</span> <span class="p">[</span><span class="mi">16</span><span class="p">,</span> <span class="mi">17</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">19</span><span class="p">,</span> <span class="mi">20</span><span class="o">*</span><span class="p">]</span>
</span></span></code></pre></div><ul>
<li>写一个程序模拟银行排队, 只有一个队伍, 一个用户进入时允许插队(进入队伍任意位置), 但要保证每次导致队伍变更, 队伍中受影响的人都收到通知</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">Customer</span> <span class="n">A</span> <span class="n">line</span> <span class="n">up</span> <span class="n">at</span> <span class="n">position</span> <span class="mi">11</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">Customer</span> <span class="n">B</span><span class="p">:</span> <span class="n">order</span> <span class="n">changed</span> <span class="n">to</span> <span class="mi">12</span>
</span></span><span class="line"><span class="cl"><span class="n">Customer</span> <span class="n">C</span><span class="p">:</span> <span class="n">order</span> <span class="n">changed</span> <span class="n">to</span> <span class="mi">13</span>
</span></span><span class="line"><span class="cl"><span class="n">Customer</span> <span class="n">D</span><span class="p">:</span> <span class="n">order</span> <span class="n">changed</span> <span class="n">to</span> <span class="mi">14</span>
</span></span></code></pre></div><ul>
<li>
<p>用户系统, 存在相互关注的动作, 当进入某个人的个人主页, 需要展示其粉丝数, 关注数, 粉丝列表以及关注列表. 请简要描述解决方案, 包括db建模/数据层/业务层, 以及应对高并发/关注取关等情况的处理逻辑</p>
</li>
<li>
<p>给定一些NxN的矩阵，对于任意的路线，定义其【和】为其线路上所有节点的数字的和，计算从左上角到右下角的路线和最小值。每条路线只能从某一点到其周围（上下左右）的点，不可斜行。
例如，</p>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="mi">4</span><span class="p">,</span><span class="mi">6</span>
</span></span><span class="line"><span class="cl"><span class="mi">2</span><span class="p">,</span><span class="mi">8</span> <span class="n">的路线和最小值为</span> <span class="mi">4</span><span class="o">-</span><span class="mi">2</span><span class="o">-</span><span class="mi">8</span> <span class="mi">14</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span>
</span></span><span class="line"><span class="cl"><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span>
</span></span><span class="line"><span class="cl"><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">9</span> <span class="n">的路线和最小值为</span> <span class="mi">1</span><span class="o">-</span><span class="mi">2</span><span class="o">-</span><span class="mi">3</span><span class="o">-</span><span class="mi">6</span><span class="o">-</span><span class="mi">9</span> <span class="mi">21</span>
</span></span></code></pre></div><p>程序只需输出最小和值即可（一个数字）</p>
]]></content>
		</item>
		
		<item>
			<title>我的tmux配置及说明【k-tmux】</title>
			<link>https://wklken.me/posts/2015/08/06/linux-tmux.html</link>
			<pubDate>Thu, 06 Aug 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/08/06/linux-tmux.html</guid>
			<description>配置了一份 k-tmux 以下快捷键是对这份配置的说明, 大部分为tmux通用, 部分为修改自定义 安装 mac $ brew install tmux $ brew install reattach-to-user-namespace ubuntu $ sudo apt-get install tmux 简要说明 tmux -&amp;gt; session -&amp;gt; window -&amp;gt; pane Tm</description>
			<content type="html"><![CDATA[<p>配置了一份 <a href="https://github.com/wklken/k-tmux">k-tmux</a></p>
<p>以下快捷键是对这份配置的说明, 大部分为<code>tmux</code>通用, 部分为修改自定义</p>
<h3 id="安装">安装</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mac
</span></span><span class="line"><span class="cl">$ brew install tmux
</span></span><span class="line"><span class="cl">$ brew install reattach-to-user-namespace
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">ubuntu
</span></span><span class="line"><span class="cl">$ sudo apt-get install tmux
</span></span></code></pre></div><h3 id="简要说明">简要说明</h3>
<blockquote>
<p>tmux -&gt; session -&gt; window -&gt; pane</p>
</blockquote>
<ul>
<li>Tmux可以管理多组会话</li>
<li>一个会话（Session）可以包含多个窗口，一个窗口（Window）可以包含多个窗格（Pane）</li>
</ul>
<p>操作前缀 <code>PREFIX = Ctrl-a</code></p>
<h3 id="1-session">1. session</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 创建, tmux new -s &lt;name-of-my-session&gt; 创建一个新的会话</span>
</span></span><span class="line"><span class="cl">$ tmux new -s basic
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 在tmux中创建一个会话</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>PREFIX-:<span class="o">]</span> new -s &lt;name-of-my-session&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 分离会话 detach</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>PREFIX-d<span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>detached <span class="o">(</span>from session basic<span class="o">)]</span>
</span></span><span class="line"><span class="cl">or
</span></span><span class="line"><span class="cl">$ tmux detach
</span></span><span class="line"><span class="cl">or
</span></span><span class="line"><span class="cl"><span class="o">[</span>PREFIX-Ctrl-z<span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 查看已有会话列表(list-session)</span>
</span></span><span class="line"><span class="cl">$ tmux ls
</span></span><span class="line"><span class="cl">basic: <span class="m">1</span> windows <span class="o">(</span>created Wed Aug  <span class="m">5</span> 14:54:04 2015<span class="o">)</span> <span class="o">[</span>200x49<span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 在tmux中查看会话列表并切换</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>PREFIX-s<span class="o">]</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">$ tmux attach
</span></span><span class="line"><span class="cl">$ tmux attach -t basic
</span></span><span class="line"><span class="cl">$ tmux a -t basic
</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">$ tmux kill-session -t
</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"><span class="o">[</span>PREFIX-$<span class="o">]</span>
</span></span></code></pre></div><h3 id="2-window">2. window</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 创建一个新的窗口</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>PREFIX-c<span class="o">]</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"><span class="o">[</span>PREFIX-,<span class="o">]</span> 之后输入名字回车
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 切换到下一个窗口, k-tmux另外配置了PREFIX-t/T</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>PREFIX-n<span class="o">]</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"><span class="o">[</span>PREFIX-1/2/3<span class="o">]</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"><span class="o">[</span>PREFIX-w<span class="o">]</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"><span class="o">[</span>PREFIX-f<span class="o">]</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"><span class="nb">exit</span> or
</span></span><span class="line"><span class="cl"><span class="o">[</span>PREFIX-<span class="p">&amp;</span><span class="o">]</span> 会有确认
</span></span></code></pre></div><h3 id="3-pane">3. pane</h3>
<p>分割
原先未修改键位的分割方式是<code>[PREFIX-%]</code>和<code>[PREFIX-&quot;]</code>
重新映射为</p>
<pre tabindex="0"><code># 垂直/水平分割窗口
[PREFIX-\] / [PREFIX--]
</code></pre><p>关闭pane</p>
<pre tabindex="0"><code># 关闭一个面板, 要确认
[PREFIX-x]

或者
exit [面板里执行]
</code></pre><p>切换</p>
<pre tabindex="0"><code>[PREFIX-hjkl] pane之间移动

[Ctrl-hjkl]   pane之间移动
[Ctrl-\]      最近使用两个窗口之间切换
[PREFIX-q]    展示窗口数字并选择跳转
[PREFIX-o]    循环切换
</code></pre><p>大小调整</p>
<pre tabindex="0"><code>[Ctrl-HJKL] pane大小调整
[PREFIX-z]  trigger暂时把窗口变大
</code></pre><p>关闭及移动</p>
<pre tabindex="0"><code>[PREFIX-x] 关闭当前pane, 需确认
[PREFIX-}] 当前pane移到左边
[PREFIX-{] 当前pane移到右边
</code></pre><p>其他</p>
<pre tabindex="0"><code>[PREFIX-!]     当前pane在新的window中打开
[PREFIX-space] 会自动切换依次使用这些布局(几种窗口布局轮流切换)
</code></pre><h3 id="4-复制粘贴">4. 复制粘贴</h3>
<pre tabindex="0"><code>[PREFIX-[] 进入复制模式

=&gt; 可以进行的操作
space/v    开始选择
Ctrl-v     整块选择
hjkl       方向键移动
w/b        向前向后移动一个单词
fx/Fx      行内移动到下一个字符位置
ctrl-b/f   在缓冲区里面翻页
g/G        到缓冲区最顶/底端
/ ?        向下, 向上查找
n/N        查找后下一个, 上一个
Enter/y    复制
[PREFIX-]] 粘贴
</code></pre><p>其他增强:</p>
<pre tabindex="0"><code># 复制整个pane可见区域
[PREFIX-:] capture-pane

# 查看缓冲区内容
[PREFIX-:] show-buffer

# 列出缓冲区列表
[PREFIX-:] list-buffers

# 从缓冲区列表选择并插入到当期面板
[PREFIX-:] choose-buffer =&gt; 回车
</code></pre><h3 id="5-其他">5. 其他</h3>
<p>获得快捷键列表</p>
<pre tabindex="0"><code>[PREFIX-?]
</code></pre><p>进入命令模式</p>
<pre tabindex="0"><code>[PREFIX-:]

一些命令模式下的命令
# 新建窗口
new-window -n console

# 新建并执行命令
new-window -n processes &#34;top&#34;
</code></pre><h3 id="6-增强">6. 增强</h3>
<h4 id="1-tmuxinator">1. Tmuxinator</h4>
<p>Tmuxinator 是一个 Ruby 的 gem 包，可用于创建 Tmux 的会话。它的工作方式是先在配置文件中定义会话中的细节，然后用 1 条命令创建出这些会话</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gem install tmuxinator
</span></span><span class="line"><span class="cl">tmuxinator new <span class="nv">project_a</span> <span class="o">=</span>&gt; ~/.tmuxinator/project_a.yml <span class="o">=</span>&gt; 配置
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">启动: tmuxinator start project_a
</span></span><span class="line"><span class="cl">可以别名: mux start project_a
</span></span></code></pre></div><h4 id="2-vim插件">2. vim插件</h4>
<p><code>christoomey/vim-tmux-navigator</code>, 安装更便捷的导航跳转</p>
<h3 id="7-资源及参考">7. 资源及参考</h3>
<ul>
<li><a href="https://github.com/tmux-plugins">tmux plugins</a></li>
<li><a href="http://aquaregia.gitbooks.io/tmux-productive-mouse-free-development_zh/content/index.html">《tmux: Productive Mouse-Free Development》中文翻译</a></li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>Review and Restart</title>
			<link>https://wklken.me/posts/2015/07/24/summary-12-review-and-restart.html</link>
			<pubDate>Fri, 24 Jul 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/07/24/summary-12-review-and-restart.html</guid>
			<description>7月4日, 想着正式工作四年了 7月9日, 公司这边出了点状况, 歇业, 也从公司离职了 7月10日, 衡山溜达了一圈 7月19日, 想着正好加入甜品十一个月</description>
			<content type="html"><![CDATA[<p>7月4日,  想着正式工作四年了
7月9日, 公司这边出了点状况, 歇业, 也从公司离职了
7月10日, 衡山溜达了一圈
7月19日, 想着正好加入甜品十一个月了
7月18日, 生日</p>
<p>去年的七月,  也刚好离职, 正在一路北上的旅途中. 情况似曾相识.</p>
<p><img src="/imgs/life/road.jpg" alt="coding-life"></p>
<h2 id="review">Review</h2>
<p>一些工作上的review</p>
<h4 id="1--凡事有结果-and-fail-fast快速失败">1.  凡事有结果 and Fail-fast(快速失败)</h4>
<p>作为工程师, 似乎很容易陷入, 将做项目, 变成完成任务.</p>
<p>前置是需求, 有产品帮cover, 但是项目的后置, 是结果, 更多的是依赖自己.</p>
<p>需求分析后, 系统设计之初, 就应该考虑: 日志/统计, 考虑如何去衡量&quot;结果&quot;.</p>
<p>项目上线后, 要跟!!! 分析及反馈数据, 让更多人了解现状, 以便后续决策.</p>
<p>做事如果没有结果, 会导致可能错误的不断投入精力处理一些不重要的事情, 甚至根本不需要做, 沉没成本</p>
<p>事情做了, 要有一个结果, 好/不好/不好不坏, 无论如何, 自己都要有所感知. 关乎成就感.</p>
<p>如果不好, 就快速失败, 砍掉或者修正, 总之, 不要往错误的方向越走越远.</p>
<p>当然, 这涉及和上下游多沟通交流.</p>
<p>所以, 建议无论产品或者研发, 做完一个项目, 都要有相应的review, 总结并通报相关人员.</p>
<h4 id="2-人">2. 人</h4>
<p>人员构成很重要, 要互补,  必要的时候能&quot;千斤顶&quot;</p>
<p>做事多向前走一步,  不要老是停留在自己的&quot;舒适区&quot;/&ldquo;自留地&rdquo;, 多走一步, 更高效协作</p>
<p>找准节奏, 快速度过磨合期</p>
<p>不要因为一时繁忙, 因为忙不过来而找人, 痛上一痛再做决定</p>
<p>不要给人找事做!!!!!(一时事太多=&gt; 招人 =&gt; 没事干 =&gt; 找事干&hellip;&hellip;)</p>
<p>各司其职, 信任伙伴, 不要大跨界或者插手太多, 无谓耗费自己的精力, 你的精力可以在自己擅长的地方发挥更大的作用</p>
<h4 id="3-scrum--看板--项目制">3. Scrum &amp; 看板 &amp; 项目制</h4>
<p>没有万能药, 可以实施, 试试, 阵痛, 改进, 最终摸索到适合团队的</p>
<p>一个Sprint只集中精力处理有限的目标. 不要分散了精力.</p>
<p>Sprint, 是用来实现需求的, 而不是用来砍需求的!!!</p>
<p>看板, 从整体一个大看板, 到最终根据业务线逐步拆分, 整个过程用起来感觉很有效.</p>
<p>项目制, 可以先找小的项目试验, 核心成员参与, 先run起来, 成功了再扩大范围. 很多东西不能操之过急.</p>
<h4 id="4-工具">4. 工具</h4>
<p>用好用的工具, 用好工具</p>
<p>mac/邮件/jira/stash/confluence等等,  整个团队达成一致, 高效协作</p>
<p>使用工具的整个过程中, 也势必遇到的各种问题, 例如团队规模的扩大, 业务线增多, 跟上游产品的协作, 配合sprint等等, 这都需要在不同阶段进行调整, 对工作流进行优化, 形成一些约定. 以提高协作效率为第一目标</p>
<p>工具能买就买, 找个工程师花几个人日搭开源的, 以及后续维护, 还不一定好用(这都是隐性的成本), 还不如花钱搞定, 况且如今各类工具成本已经非常低廉了:)</p>
<h4 id="5-快-以及-一切从简work就行">5. 快 以及 一切从简work就行</h4>
<p>快速实现, 快速验证.</p>
<p>不要一开始就妄图设计一个完美的系统: 需求会改/项目会砍</p>
<p>一开始不要想得太过复杂, 基本流程ok, 上线, 迭代上线完善的功能, 不要一开始就花大力气想直接搞定, 因为这里面很多成本其实是不必要的, 搞不好你花了一周赶完, 上了发现根本毫无用处.</p>
<p>当人肉顶不住的时候, 再考虑做系统. 理由同上, 费劲搞了系统, 结果要么业务没上来, 没达到&quot;人肉&quot;顶不住的情况, 要么就是业务废弃了/砍掉了. 搞系统耗时耗力不一定有用.</p>
<h4 id="6-不忘初心">6. 不忘初心</h4>
<p>很多时候, 无论是产品, 还是研发, 都会忘了最初出发的目的是什么.</p>
<p>产品被一大堆细节淹没, 而研发将大目标切碎逐步做的过程中, 很那有整体的概念.</p>
<p>可以尝试, 可以变换, 但是要明确, 所有人都明确.</p>
<p>一切, 轻重缓急, 排期, 以目标为导向</p>
<p>目标导向, 但同一个时刻, 不要有太多的目标, 不要一个目标未完就想在此基础上尝试新的目标.(精力分散/事情没做透)</p>
<h4 id="7-理想主义">7. 理想主义</h4>
<p>不要太过理想主义</p>
<hr>
<p>这十个月, 经历了很多, 团队由小到大, 项目由1到28, 从0到1, 造了很多轮子, 经历了整个团队协作方式的变更, 经历了磨合的阵痛, 经历了一轮又一轮的sprint, 经历了第一版上线, 到最后第80个安装包, 经历了一次次上线, 一个个活动, 看着后台用户数一点点涨起来, 看着全部api调用一点点涨起来, 然后掉下去&gt;_&lt;#(这似乎是个悲伤的故事&hellip;&hellip;)</p>
<p>但是, 这十个月, 从第一天到甜品, 感受到&quot;家&quot;的气息, 到最终收拾离开, 关小黑屋, 晨会, 愉快地coding, 聚餐等等, 十月风雨, 过得很充实, 也学到很多东西, 感谢所有糖厂的银, 帮助及包容.</p>
<h2 id="restart">Restart</h2>
<p>闲了一周了, 趁这段时间,  打算做做总结, 重写一些代码, 读完CPython源码及APUE, 还有, 就是学车及休息</p>
<p>整理整理, 想明白一些东西, 再重新启程.</p>
<p>希望明天会更好吧</p>
<p><code>既然选择了远方, 便只顾风雨兼程</code>, 共勉</p>
<p>wklken</p>
<p>2015-07-24 于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>工作四周年小结</title>
			<link>https://wklken.me/posts/2015/07/04/summary-11-work-four-years.html</link>
			<pubDate>Sat, 04 Jul 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/07/04/summary-11-work-four-years.html</guid>
			<description>一晃, 四年过去了 每年的7月4日, 都会想到刚毕业入职第一天 惯例, 写一些东西, 记录过去的这些日子 既然选择了远方, 便只顾风雨兼程 &amp;ndash; 汪国真 最近遇到了</description>
			<content type="html"><![CDATA[<p>一晃, 四年过去了</p>
<p>每年的7月4日, 都会想到刚毕业入职第一天</p>
<p>惯例, 写一些东西, 记录过去的这些日子</p>
<hr>
<blockquote>
<p>既然选择了远方, 便只顾风雨兼程 &ndash; 汪国真</p>
</blockquote>
<p>最近遇到了一些事情, 颇为突然, 也颇为无奈.</p>
<p>似乎, 也是去年的这阵子, 遭遇了公司的变故, 最终离职, 到北边溜达了一阵. 回来后, 宅了一阵, 花了两天找了工作, 迄今十月有余.</p>
<p>很多事情很突然, 也就在旦夕之间</p>
<p>原来没有感觉, 但是回顾, 发现却也是必然的</p>
<p>十月风雨, 一切美好, 仿佛一场梦</p>
<p>思考, 总结,  前进</p>
<p>生活, 总是要继续的.</p>
<p><code>既然选择了远方, 便只顾风雨兼程</code>, 这是我给每个新入职小伙伴的纸条,  祝好, 也送给自己, hold on.</p>
<blockquote>
<p>前进, 前进, 不顾一切地前进 &ndash; 三体</p>
</blockquote>
<p>似乎, 之前一直秉承这个信念在做一些事情</p>
<p>回头却发现, 似乎错放了重点</p>
<p>每个人在每个阶段, 都有不同的需求/追求, 而一直坚持一个信念</p>
<p>愚蠢!</p>
<p>我将重点错放了, 有些事情总要去做的, 有些责任总是要要承担的, 有些变迁, 总是要去经历的. 而, 这个信念, 成为了我逃避的理由. 规避了太多不该规避的东西, 最终还剩下什么?</p>
<p>结果是, 似乎折腾许多, 却也没折腾出什么, 步步前行, 却错失了很多生活中的美好.</p>
<p>anyway, 需要醒醒, 认真生活了.</p>
<blockquote>
<p>关于造轮子</p>
</blockquote>
<p>近期思考的问题, 关于造轮子与创业.</p>
<p>创业过程, 避免不了去造轮子, 如果顺利, 随着业务发展, 不断完善, 优化, 变革, 最终造一个大轮子:)</p>
<p>而往往, 事情总不尽人意, 如果业务没发展呢? 假设, 到再次创业, 轮子似乎又得造一遍.</p>
<p>这个过程, 或许可以造一个更好的轮子, 但是, 独轮车总归还是独轮车, 载不动.</p>
<p>然后, 如果要造第三遍呢? 呵呵</p>
<p>业务成就了技术, 这也造成了很多无奈:(</p>
<p>充电, 毕竟有限, side project, 更多的是玩具, 哎.</p>
<p>所谓的沉没?</p>
<blockquote>
<p>当一切已成往事, 谁还记得, 那些岁月</p>
</blockquote>
<p>去年的8月, 玩久了, 宅久了, 大体有个方向, 花了些时间看了下公司, 两天面了四个, 然后选了一个, 入职.</p>
<p>一切很快, 选择的原因呢? 两个因素: 1.做的事情符合价值观, 认同且喜欢 2.人</p>
<p>角色? 后端开发兼运维打杂, 除了业务系统, 底层服务依赖的开发, 还兼职做运维, 负责环境搭建/部署/自动化等等.</p>
<p>一路过来, 造了很多轮子, 很多轮子造的第二遍, 重构过几个项目, 做对过一些事情, 做错过一些事情, 对靠谱二字重新定义.</p>
<p>一路过来, 也对自己有更为多的认识. 性格上的缺陷.</p>
<p>回顾十月, 感谢很多人, 感谢一起奋斗过的小伙伴.</p>
<p>很遗憾, 我是个记性很好的人, 例如同一个话题, 谁第几遍重复说起都记得, 哎. 这也造成了, 对很多事情, 难以释怀, 记性太好或许对 INTJ 来说, 是件坏事.</p>
<p>那些一起战斗的岁月, 或许已成往事, 依然记得.</p>
<blockquote>
<p>四年</p>
</blockquote>
<p>四年是什么感觉呢?</p>
<p>感觉老了, 一眼过去, 一片都是90后</p>
<p>感觉时间匆匆, 不经意间, 一小时, 一天, 一礼拜, 一月, 一年, 就这么没了, 更多的压力, 紧迫感, 更加珍惜时间.</p>
<p>感觉自己还是弱弱的, 读很多书, 做很多事, 也对自己有更多的认识, 愈发觉得自己的渺小, 能做一些事情, 但是却也有很多无能为力的时候, 唯有努力</p>
<p>感觉生活中充满了各种不确定, 无论如何都得努力面对, 努力锻炼, 学习, 努力变得更好</p>
<blockquote>
<p>旦夕, 不确定</p>
</blockquote>
<p>上一刻还在愉快地coding, 下一刻回头发现已沧海桑田</p>
<p>生活总是充满变化, 充满了无奈, 这就是生活, 这就是现实.</p>
<p>虽然, <code>拥抱变化</code>, 但是<code>落差好大</code></p>
<p>then?</p>
<p>坚持, 努力地去做一些事情吧, 让自己变得更好</p>
<blockquote>
<p>创业的一些感受</p>
</blockquote>
<p>还在review, 等想明白了再写</p>
<blockquote>
<p>选择很重要</p>
</blockquote>
<p>然!</p>
<p>但是没有对与错, 选择了就选择了, 仅此而已</p>
<blockquote>
<p>迷茫, 是能力和理想不匹配</p>
</blockquote>
<p>不久前在知乎上看到的, 深以为然, 也在思考.</p>
<p>wklken</p>
<p>2015-07-04 于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>vim插件: surround &amp; repeat[成对符号编辑]</title>
			<link>https://wklken.me/posts/2015/06/13/vim-plugin-surround-repeat.html</link>
			<pubDate>Sat, 13 Jun 2015 12:16:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/13/vim-plugin-surround-repeat.html</guid>
			<description>k-vim配置 github surround 作用: 快速给词加环绕符号,例如单引号/双引号/括号/成对标签等 github: vim-surround 安装 Bundle &amp;#39;tpope/vim-surround&amp;#39; 使用 注意(括号, 左括号会加空格, 右括号不会) 示例</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<h2 id="surround">surround</h2>
<p>作用: 快速给词加环绕符号,例如单引号/双引号/括号/成对标签等</p>
<p>github: <a href="https://github.com/tpope/vim-surround">vim-surround</a></p>
<h3 id="安装">安装</h3>
<pre tabindex="0"><code>Bundle &#39;tpope/vim-surround&#39;
</code></pre><h3 id="使用">使用</h3>
<p>注意(括号, 左括号会加空格, 右括号不会)</p>
<p>示例:</p>
<pre tabindex="0"><code># 替换: cs&#34;&#39;
&#34;Hello world!&#34; -&gt; &#39;Hello world!&#39;

# 替换-标签(t=tag): cst&#34;
&lt;a&gt;abc&lt;/a&gt;  -&gt; &#34;abc&#34;

cst&lt;html&gt;
&lt;a&gt;abc&lt;/a&gt;  -&gt; &lt;html&gt;abc&lt;/html&gt;

# 删除: ds&#34;
&#34;Hello world!&#34; -&gt; Hello world!

# 添加(ys=you surround): ysiw&#34;
Hello -&gt; &#34;Hello&#34;

# 添加: csw&#34;
Hello -&gt; &#34;Hello&#34;

# 添加-整行: yss&#34;
Hello world -&gt; &#34;Hello world&#34;

# ySS&#34;
Hello world -&gt;
&#34;
    hello world
&#34;

# 添加-两个词: veeS&#34;
hello world -&gt; &#34;hello world&#34;

# 添加-当前到行尾: ys$&#34;

# 左符号/右符号 =&gt; 带不带空格
cs([
(hello) -&gt; [ hello ]

cs(]
(hello) -&gt; [hello]
</code></pre><p>演示:</p>
<p><img src="/imgs/vim/surround.gif" alt="surround.gif"></p>
<hr>
<h2 id="vim-repeat">vim-repeat</h2>
<p>作用: 重复一个插件的操作, 支持surround.vim, 通过 surround 操作之后的行为, <code>.</code>号重复</p>
<p>github: <a href="https://github.com/tpope/vim-repeat">vim-repeat</a></p>
<h3 id="安装-1">安装</h3>
<pre tabindex="0"><code>&#34; for repeat -&gt; enhance surround.vim, . to repeat command
Bundle &#39;tpope/vim-repeat&#39;
</code></pre><h3 id="使用-1">使用</h3>
<p>演示:</p>
<p><img src="/imgs/vim/repeat.gif" alt="repeat.gif"></p>
<h3 id="最终配置">最终配置</h3>
<pre tabindex="0"><code>Bundle &#39;tpope/vim-surround&#39;
Bundle &#39;tpope/vim-repeat&#39;
</code></pre><h3 id="建议">建议</h3>
<ol>
<li>善用<code>.</code></li>
<li><code>repeat</code>同时还支持的插件 <a href="https://github.com/tpope/vim-repeat#repeatvim">doc</a></li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>vim插件: gundo[时光机]</title>
			<link>https://wklken.me/posts/2015/06/13/vim-plugin-gundo.html</link>
			<pubDate>Sat, 13 Jun 2015 10:37:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/13/vim-plugin-gundo.html</guid>
			<description>k-vim配置 github 有时候编辑一半, 特别是删除了一些东西, 然后想找回, 但是没有存下来(只在脑子里有) 这时候时光机就发挥作用了 作用: 文件时光机, 可</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>有时候编辑一半, 特别是删除了一些东西, 然后想找回, 但是没有存下来(只在脑子里有)
这时候时光机就发挥作用了</p>
<p>作用: 文件时光机, 可以查看同一个文件之前的历史内容</p>
<p>github: <a href="https://github.com/sjl/gundo.vim">gundo</a></p>
<h2 id="安装">安装</h2>
<pre tabindex="0"><code>Bundle &#39;sjl/gundo.vim&#39;
</code></pre><h2 id="使用">使用</h2>
<p>绑定快捷键<code>&lt;leader&gt;h</code>作为<code>toggle</code>, 进入之后</p>
<pre tabindex="0"><code>j/k   上下选择
p     查看diff
回车  回滚文件到这个时刻的版本
&lt;leader&gt;h  关闭(或者wq关闭gundo打开的窗口)
</code></pre><p>注意, 你可以跳进预览, 复制一些东西出来, 不必走回滚</p>
<p><img src="/imgs/vim/gundo.gif" alt="gundo.gif"></p>
<h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>nnoremap &lt;leader&gt;h :GundoToggle&lt;CR&gt;
</code></pre><h2 id="建议">建议</h2>
<ol>
<li>只要写过的, 都能找回来</li>
<li>同类插件 <a href="https://github.com/mbbill/undotree">undotree</a></li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>vim插件: expand-region[区域选中]</title>
			<link>https://wklken.me/posts/2015/06/13/vim-plugin-expandregion.html</link>
			<pubDate>Sat, 13 Jun 2015 10:21:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/13/vim-plugin-expandregion.html</guid>
			<description>k-vim配置 github 作用: 视图模式下可伸缩选中部分，用于快速选中某些块 github: expand-region 安装 Bundle &amp;#39;terryma/vim-expand-region&amp;#39; 使用 自定义key v 增加选中范围 V 减少选中范围 演示: 最终配置 Bundle &amp;#39;terryma/vim-expand-region&amp;#39;</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>作用: 视图模式下可伸缩选中部分，用于快速选中某些块</p>
<p>github: <a href="https://github.com/terryma/vim-expand-region">expand-region</a></p>
<h3 id="安装">安装</h3>
<pre tabindex="0"><code>Bundle &#39;terryma/vim-expand-region&#39;
</code></pre><h3 id="使用">使用</h3>
<p>自定义key</p>
<pre tabindex="0"><code>v 增加选中范围
V 减少选中范围
</code></pre><p>演示:</p>
<p><img src="/imgs/vim/expand-region.gif" alt="expand-region.gif"></p>
<h3 id="最终配置">最终配置</h3>
<pre tabindex="0"><code>Bundle &#39;terryma/vim-expand-region&#39;
vmap v &lt;Plug&gt;(expand_region_expand)
vmap V &lt;Plug&gt;(expand_region_shrink)
</code></pre><h3 id="建议">建议</h3>
<ol>
<li>培养使用<code>v</code>/<code>V</code>进行区块选中的习惯, 避免每次多敲键位</li>
<li>可以进行选中规则的自定义, 具体见 <a href="https://github.com/terryma/vim-expand-region#customize-selected-regions">文档</a></li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>vim插件: quickrun[快速执行]</title>
			<link>https://wklken.me/posts/2015/06/13/vim-plugin-quickrun.html</link>
			<pubDate>Sat, 13 Jun 2015 10:09:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/13/vim-plugin-quickrun.html</guid>
			<description>k-vim配置 github 作用: 快速执行当前文件, 例如*.py/.rb/.sh等等, 用于快速验证一些代码实现 github: vim-quickrun 安装 Bundle &amp;#39;thinca/vim-quickrun&amp;#39; 使用 配置使用message进行</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>作用: 快速执行当前文件, 例如*.py/<em>.rb/</em>.sh等等, 用于快速验证一些代码实现</p>
<p>github: <a href="https://github.com/thinca/vim-quickrun">vim-quickrun</a></p>
<h2 id="安装">安装</h2>
<pre tabindex="0"><code>Bundle &#39;thinca/vim-quickrun&#39;
</code></pre><h2 id="使用">使用</h2>
<p>配置使用<code>message</code>进行结果展示, 即, 展示运行结果后, 按任意键回到vim编辑</p>
<p>同时, 映射<code>&lt;leader&gt;r</code>以及<code>F10</code>快捷键</p>
<p>在快速编写验证性代码时非常有用</p>
<p>演示:</p>
<p><img src="/imgs/vim/quick-run.gif" alt="quick-run.gif"></p>
<h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>Bundle &#39;thinca/vim-quickrun&#39;
let g:quickrun_config = {
\   &#34;_&#34; : {
\       &#34;outputter&#34; : &#34;message&#34;,
\   },
\}

let g:quickrun_no_default_key_mappings = 1
nmap &lt;Leader&gt;r &lt;Plug&gt;(quickrun)
map &lt;F10&gt; :QuickRun&lt;CR&gt;
</code></pre><h2 id="建议">建议</h2>
<p>无</p>
]]></content>
		</item>
		
		<item>
			<title>vim插件: trailing-whitespace[行尾空格处理]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-trailing-whitespace.html</link>
			<pubDate>Sun, 07 Jun 2015 18:16:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-trailing-whitespace.html</guid>
			<description>k-vim配置 github 没有什么太多需要讲的, 功能+一个快捷键 代码洁癖/强迫症必备 作用: 高亮行末空格(标红), 也可以一键去除文件中所有行行尾空格 github: vim-trailing-whitespace</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>没有什么太多需要讲的, 功能+一个快捷键</p>
<p>代码洁癖/强迫症必备</p>
<p>作用: 高亮行末空格(标红), 也可以一键去除文件中所有行行尾空格</p>
<p>github: <a href="https://github.com/bronson/vim-trailing-whitespace">vim-trailing-whitespace</a></p>
<h2 id="安装">安装</h2>
<pre tabindex="0"><code>Bundle &#39;bronson/vim-trailing-whitespace&#39;
</code></pre><h2 id="使用">使用</h2>
<p>被动技能: 自动标记行尾的空格</p>
<p><img src="/imgs/vim/trailing-whitespace.png" alt="trailing-whitespace.png"></p>
<p>主动技能: 绑定<code>&lt;leader&gt;&lt;space&gt;</code>为快捷键, 一键去除所有行尾空格
(<code>k-vim</code>中为<code>,空格</code>)</p>
<p><img src="/imgs/vim/trailing-whitespace.gif" alt="trailing-whitespace.gif"></p>
<h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>Bundle &#39;bronson/vim-trailing-whitespace&#39;
map &lt;leader&gt;&lt;space&gt; :FixWhitespace&lt;cr&gt;
</code></pre>]]></content>
		</item>
		
		<item>
			<title>vim插件: closetag[成对标签补全]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-closetag.html</link>
			<pubDate>Sun, 07 Jun 2015 18:15:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-closetag.html</guid>
			<description>k-vim配置 github 如果日常开发中要编辑xml和html文件, 一个字符一个字符敲是不聪明地, 虽然你可以这么做&amp;hellip; 作用: 编辑xml/h</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>如果日常开发中要编辑xml和html文件, 一个字符一个字符敲是不聪明地, 虽然你可以这么做&hellip;</p>
<p>作用: 编辑xml/html时, 自动补全闭合标签</p>
<p>github: <a href="https://github.com/docunext/closetag.vim">closetag</a></p>
<h2 id="安装">安装</h2>
<pre tabindex="0"><code>Bundle &#39;docunext/closetag.vim&#39;
</code></pre><h2 id="使用">使用</h2>
<p>被动技能, 自动补全</p>
<p><img src="/imgs/vim/closetag.gif" alt="closetag.gif"></p>
<h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>Bundle &#39;docunext/closetag.vim&#39;
let g:closetag_html_style=1
</code></pre><h2 id="建议">建议</h2>
<ol>
<li>被动技能, 越简单越好, 当然如果你要用这个来写前端代码, 是远远不够的, 你可能需要<a href="https://github.com/mattn/emmet-vim">emmet</a></li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>vim插件: ctrlp[文件搜索]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-ctrlp.html</link>
			<pubDate>Sun, 07 Jun 2015 18:14:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-ctrlp.html</guid>
			<description>k-vim配置 github 使用频率最高的插件之一 作用: 模糊搜索, 可以搜索文件/buffer/mru/tag等等 github: 原始kien/ctrlp, 使用的是国人</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>使用频率最高的插件之一</p>
<p>作用: 模糊搜索, 可以搜索文件/buffer/mru/tag等等</p>
<p>github: 原始<a href="https://github.com/kien/ctrlp.vim">kien/ctrlp</a>, 使用的是国人改进版本 <a href="https://github.com/ctrlpvim/ctrlp.vim">ctrlpvim/ctrlp.vim</a></p>
<h2 id="安装">安装</h2>
<pre tabindex="0"><code>Bundle &#39;ctrlpvim/ctrlp.vim&#39;
</code></pre><h2 id="使用">使用</h2>
<p>绑定快捷键</p>
<p><code>&lt;leader&gt;-f</code>模糊搜索最近打开的文件(MRU)</p>
<p><code>&lt;leader&gt;-p</code>模糊搜索当前目录及其子目录下的所有文件</p>
<p>搜索框出来后, 输入关键字, 然后</p>
<pre tabindex="0"><code>ctrl + j/k 进行上下选择

ctrl + x 在当前窗口水平分屏打开文件

ctrl + v 同上, 垂直分屏

ctrl + t 在tab中打开
</code></pre><p><img src="/imgs/vim/ctrlp.gif" alt="ctrlp.gif"></p>
<h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>Bundle &#39;ctrlpvim/ctrlp.vim&#39;
let g:ctrlp_map = &#39;&lt;leader&gt;p&#39;
let g:ctrlp_cmd = &#39;CtrlP&#39;
map &lt;leader&gt;f :CtrlPMRU&lt;CR&gt;
let g:ctrlp_custom_ignore = {
    \ &#39;dir&#39;:  &#39;\v[\/]\.(git|hg|svn|rvm)$&#39;,
    \ &#39;file&#39;: &#39;\v\.(exe|so|dll|zip|tar|tar.gz|pyc)$&#39;,
    \ }
let g:ctrlp_working_path_mode=0
let g:ctrlp_match_window_bottom=1
let g:ctrlp_max_height=15
let g:ctrlp_match_window_reversed=0
let g:ctrlp_mruf_max=500
let g:ctrlp_follow_symlinks=1
</code></pre><h2 id="其他">其他</h2>
<ol>
<li>更多操作, 详见 <a href="https://github.com/ctrlpvim/ctrlp.vim#once-ctrlp-is-open">文档</a></li>
<li>可以考虑废弃<code>fuzzyfinder</code> / <code>Command-T</code></li>
<li>可以考虑只用一个快捷键, 配置映射到<code>:CtrlPMixed</code>, 就可以一键搜索文件/buffer/mru</li>
</ol>
<hr>
<h2 id="附-ctrlp的插件ctrlp-funky">附: ctrlp的插件<code>ctrlp-funky</code></h2>
<p>作用: 模糊搜索当前文件中所有函数</p>
<p>github: <a href="https://github.com/tacahiroy/ctrlp-funky">ctrlp-funky</a></p>
<h2 id="安装-1">安装</h2>
<pre tabindex="0"><code>Bundle &#39;tacahiroy/ctrlp-funky&#39;
</code></pre><h2 id="使用-1">使用</h2>
<p>绑定快捷键</p>
<p><code>&lt;leader&gt;fu</code> 进入当前文件的函数列表搜索</p>
<p><code>&lt;leader&gt;fU</code> 搜索当前光标下单词对应的函数</p>
<p><img src="/imgs/vim/ctrlp-funky.gif" alt="ctrlp-funky.gif"></p>
<h2 id="最终配置-1">最终配置</h2>
<pre tabindex="0"><code>Bundle &#39;tacahiroy/ctrlp-funky&#39;
nnoremap &lt;Leader&gt;fu :CtrlPFunky&lt;Cr&gt;
&#34; narrow the list down with a word under cursor
nnoremap &lt;Leader&gt;fU :execute &#39;CtrlPFunky &#39; . expand(&#39;&lt;cword&gt;&#39;)&lt;Cr&gt;
let g:ctrlp_funky_syntax_highlight = 1

let g:ctrlp_extensions = [&#39;funky&#39;]
</code></pre>]]></content>
		</item>
		
		<item>
			<title>vim插件: airline[状态栏增强]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-airline.html</link>
			<pubDate>Sun, 07 Jun 2015 18:13:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-airline.html</guid>
			<description>k-vim配置 github 作用: 状态栏增强展示 github:vim-airline 之前用过powline, 最终切到airline 安装 Bundle &amp;#39;bling/vim-airline&amp;#39; 使用 状态栏增强, 集成支持ctrlp/nerdtr</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>作用: 状态栏增强展示</p>
<p>github:<a href="https://github.com/bling/vim-airline">vim-airline</a></p>
<p>之前用过powline, 最终切到airline</p>
<h2 id="安装">安装</h2>
<pre tabindex="0"><code>Bundle &#39;bling/vim-airline&#39;
</code></pre><h2 id="使用">使用</h2>
<p>状态栏增强, 集成支持ctrlp/nerdtree/tagbar等一些列插件<a href="https://github.com/bling/vim-airline#features">features</a></p>
<p>airline.png</p>
<p><img src="/imgs/vim/airline_1.png" alt="airline_1.png">
<img src="/imgs/vim/airline_2.png" alt="airline_2.png"></p>
<h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>Bundle &#39;bling/vim-airline&#39;
if !exists(&#39;g:airline_symbols&#39;)
let g:airline_symbols = {}
endif
let g:airline_left_sep = &#39;▶&#39;
let g:airline_left_alt_sep = &#39;❯&#39;
let g:airline_right_sep = &#39;◀&#39;
let g:airline_right_alt_sep = &#39;❮&#39;
let g:airline_symbols.linenr = &#39;¶&#39;
let g:airline_symbols.branch = &#39;⎇&#39;

&#34; 是否打开tabline
&#34; let g:airline#extensions#tabline#enabled = 1
</code></pre>]]></content>
		</item>
		
		<item>
			<title>vim插件: theme[主题]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-theme.html</link>
			<pubDate>Sun, 07 Jun 2015 18:12:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-theme.html</guid>
			<description>k-vim配置 github 主题, 在k-vim中, 默认配置了两个 1. solarize github: solarized 配置 Bundle &amp;#39;altercation/vim-colors-solarized&amp;#39; let g:solarized_termtrans=1 let g:solarized_contrast=&amp;#34;normal&amp;#34; let g:solarized_visibility=&amp;#34;normal&amp;#34; 2. molokai github: molokai 配置 Bundle &amp;#39;tomasr/molokai&amp;#39; &amp;#34; monokai原始背景色 let g:molokai_original = 1 启用: Bu</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>主题, 在<code>k-vim</code>中, 默认配置了两个</p>
<h3 id="1-solarize">1. solarize</h3>
<p>github: <a href="https://github.com/altercation/vim-colors-solarized">solarized</a></p>
<p><img src="/imgs/vim/solarized.png" alt="solarized.png"></p>
<p>配置</p>
<pre tabindex="0"><code>Bundle &#39;altercation/vim-colors-solarized&#39;
let g:solarized_termtrans=1
let g:solarized_contrast=&#34;normal&#34;
let g:solarized_visibility=&#34;normal&#34;
</code></pre><h3 id="2-molokai">2. molokai</h3>
<p>github: <a href="https://github.com/tomasr/molokai">molokai</a></p>
<p><img src="/imgs/vim/molokai.png" alt="molokai.png"></p>
<p>配置</p>
<pre tabindex="0"><code>Bundle &#39;tomasr/molokai&#39;
&#34; monokai原始背景色
let g:molokai_original = 1
</code></pre><hr>
<h3 id="启用">启用:</h3>
<p><code>Bundle</code>之后安装, 仅仅是安装, 要启用哪个主题, 需要在<code>vimrc</code>中显式指定</p>
<pre tabindex="0"><code>set background=dark
set t_Co=256
colorscheme solarized
&#34; colorscheme molokai
&#34; colorscheme desert
</code></pre><h3 id="建议">建议</h3>
<ol>
<li>有包含大部分主题的插件, 但是就个人而言, 够用就行, 所以<code>k-vim</code>中只保留了两个, 曾经习惯<code>monokai</code>, 后来转到<code>solarized</code>, 自己喜欢的才是最好的</li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>vim插件: tagbar[大纲式导航]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-tagbar.html</link>
			<pubDate>Sun, 07 Jun 2015 18:11:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-tagbar.html</guid>
			<description>k-vim配置 github tagbar, 可以将正在编辑的文件生成一个大纲, 包含类/方法/变量等, 可以选中快速跳转到目标位置, 编辑大文件特别有用. 评价: 五星 作用: 大</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p><code>tagbar</code>, 可以将正在编辑的文件生成一个大纲, 包含类/方法/变量等, 可以选中快速跳转到目标位置, 编辑大文件特别有用.</p>
<p>评价: 五星</p>
<p>作用: 大纲式快速导航</p>
<p>github: <a href="https://github.com/majutsushi/tagbar">tagbar</a></p>
<h2 id="安装">安装</h2>
<p>首先, vim必须是7.0以上</p>
<p>安装依赖 <a href="http://ctags.sourceforge.net/">Exuberant ctags</a></p>
<pre tabindex="0"><code># ubuntu
sudo apt-get install ctags

# centos
sudo yum install ctags

# mac
brew install ctags
</code></pre><p>在vim中安装<code>tagbar</code></p>
<pre tabindex="0"><code>Bundle &#39;majutsushi/tagbar&#39;
</code></pre><h2 id="使用">使用</h2>
<p>在<code>k-vim</code>中配置快捷键为<code>F9</code>, 编辑文件时按<code>F9</code>, 进入, 上下移动(<code>jk</code>)), 选中回车后会跳转</p>
<p>如果安装了i <a href="http://www.wklken.me/posts/2015/06/07/vim-plugin-easymotion.html">easymotion</a>, 还可以使用其快速跳转</p>
<p><img src="/imgs/vim/tagbar.gif" alt="tagbar.gif"></p>
<h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>Bundle &#39;majutsushi/tagbar&#39;
nmap &lt;F9&gt; :TagbarToggle&lt;CR&gt;
&#34; 启动时自动focus
let g:tagbar_autofocus = 1

&#34; for ruby, delete if you do not need
let g:tagbar_type_ruby = {
    \ &#39;kinds&#39; : [
        \ &#39;m:modules&#39;,
        \ &#39;c:classes&#39;,
        \ &#39;d:describes&#39;,
        \ &#39;C:contexts&#39;,
        \ &#39;f:methods&#39;,
        \ &#39;F:singleton methods&#39;
    \ ]
\ }
</code></pre><h2 id="建议">建议</h2>
<ol>
<li>只配置一个快捷键, 可以根据需要定制具体语言的<code>tagbar</code>展示内容. <a href="https://github.com/majutsushi/tagbar/wiki">文档地址</a></li>
<li>建议废弃<code>taglist</code>(年久失修了), <code>ctrlp</code>+<code>tagbar</code>实际使用效果更好</li>
<li>如果在写<code>golang</code>的时候要用到<code>tagbar</code>, 需要安装<code>gotags</code>支持</li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>vim插件: nerdcommenter[快速注释]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-nerdcommenter.html</link>
			<pubDate>Sun, 07 Jun 2015 18:10:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-nerdcommenter.html</guid>
			<description>k-vim配置 github 最经常用的插件之一 作用: 快速注释/解开注释 github: nerdcommenter 安装 Bundle &amp;#39;scrooloose/nerdcommenter&amp;#39; 使用 使用默认的快捷键, 不需要自己绑定 &amp;lt;leader&amp;gt;cc 加注释 &amp;lt;leader&amp;gt;cu 解开注释 &amp;lt;leader&amp;gt;c&amp;lt;space&amp;gt; 加上/解开注释</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>最经常用的插件之一</p>
<p>作用: 快速注释/解开注释</p>
<p>github: <a href="https://github.com/scrooloose/nerdcommenter">nerdcommenter</a></p>
<h2 id="安装">安装</h2>
<pre tabindex="0"><code>Bundle &#39;scrooloose/nerdcommenter&#39;
</code></pre><h2 id="使用">使用</h2>
<p>使用默认的快捷键, 不需要自己绑定</p>
<pre tabindex="0"><code>&lt;leader&gt;cc   加注释
&lt;leader&gt;cu   解开注释

&lt;leader&gt;c&lt;space&gt;  加上/解开注释, 智能判断
&lt;leader&gt;cy   先复制, 再注解(p可以进行黏贴)
</code></pre><p><img src="/imgs/vim/nerdcommenter.gif" alt="nerdcommenter.gif"></p>
<h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>Bundle &#39;scrooloose/nerdcommenter&#39;
&#34; 注释的时候自动加个空格, 强迫症必配
let g:NERDSpaceDelims=1
</code></pre><h2 id="建议">建议</h2>
<ol>
<li>同类插件 <a href="https://github.com/tomtom/tcomment_vim">tcomment</a> / <a href="https://github.com/tpope/vim-commentary">vim-commentary</a>,  有兴趣的话, 可以对比下后者和nerdcommenter</li>
<li>常用就三四个快捷键, 更多快捷键自取 <a href="https://github.com/scrooloose/nerdcommenter#usage">文档</a></li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>vim插件: rainbow_parentheses[括号高亮]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-rainbowparentheses.html</link>
			<pubDate>Sun, 07 Jun 2015 18:09:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-rainbowparentheses.html</guid>
			<description>k-vim配置 github 作用: 括号(小括号/中括号/大括号) github: rainbow_parentheses.vim 安装 Bundle &amp;#39;kien/rainbow_parentheses.vim&amp;#39; [DONE] 使用 被动(虽然支持主动触发), 可以设定括号高亮展示, 包括()[]{}&amp;lt</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>作用: 括号(小括号/中括号/大括号)</p>
<p>github: <a href="https://github.com/kien/rainbow_parentheses.vim">rainbow_parentheses.vim</a></p>
<h2 id="安装">安装</h2>
<pre tabindex="0"><code>Bundle &#39;kien/rainbow_parentheses.vim&#39; [DONE]
</code></pre><h2 id="使用">使用</h2>
<p>被动(虽然支持主动触发), 可以设定括号高亮展示, 包括<code>()[]{}&lt;&gt;</code></p>
<p><img src="/imgs/vim/rainbow_parentheses.png" alt="rainbow_parentheses.png"></p>
<h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>Bundle &#39;kien/rainbow_parentheses.vim&#39;
let g:rbpt_colorpairs = [
    \ [&#39;brown&#39;,       &#39;RoyalBlue3&#39;],
    \ [&#39;Darkblue&#39;,    &#39;SeaGreen3&#39;],
    \ [&#39;darkgray&#39;,    &#39;DarkOrchid3&#39;],
    \ [&#39;darkgreen&#39;,   &#39;firebrick3&#39;],
    \ [&#39;darkcyan&#39;,    &#39;RoyalBlue3&#39;],
    \ [&#39;darkred&#39;,     &#39;SeaGreen3&#39;],
    \ [&#39;darkmagenta&#39;, &#39;DarkOrchid3&#39;],
    \ [&#39;brown&#39;,       &#39;firebrick3&#39;],
    \ [&#39;gray&#39;,        &#39;RoyalBlue3&#39;],
    \ [&#39;darkmagenta&#39;, &#39;DarkOrchid3&#39;],
    \ [&#39;Darkblue&#39;,    &#39;firebrick3&#39;],
    \ [&#39;darkgreen&#39;,   &#39;RoyalBlue3&#39;],
    \ [&#39;darkcyan&#39;,    &#39;SeaGreen3&#39;],
    \ [&#39;darkred&#39;,     &#39;DarkOrchid3&#39;],
    \ [&#39;red&#39;,         &#39;firebrick3&#39;],
    \ ]

&#34; 不加入这行, 防止黑色括号出现, 很难识别
&#34; \ [&#39;black&#39;,       &#39;SeaGreen3&#39;],

let g:rbpt_max = 16
let g:rbpt_loadcmd_toggle = 0
au VimEnter * RainbowParenthesesToggle
au Syntax * RainbowParenthesesLoadRound
au Syntax * RainbowParenthesesLoadSquare
au Syntax * RainbowParenthesesLoadBraces
</code></pre><h2 id="建议">建议</h2>
<ol>
<li>开启16对括号匹配一般就够了</li>
<li>可以根据自己需求, 确认<code>&lt;&gt;</code>是否开启, 具体见github文档</li>
<li>不建议配置快捷键, 当做被动属性, 省心省事</li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>vim插件: syntastic[语法检查]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-syntastic.html</link>
			<pubDate>Sun, 07 Jun 2015 18:08:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-syntastic.html</guid>
			<description>k-vim配置 github 作用: 语法检查, 支持大部分的语言 github: syntastic 安装 Bundle &amp;#39;scrooloose/syntastic&amp;#39; 使用 被动技能, 设置打开时开启, 则打开对应文件的时候, 会自动进行语法检查, 高亮错误</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>作用: 语法检查, 支持大部分的语言</p>
<p>github: <a href="https://github.com/scrooloose/syntastic">syntastic</a></p>
<h2 id="安装">安装</h2>
<pre tabindex="0"><code>Bundle &#39;scrooloose/syntastic&#39;
</code></pre><h2 id="使用">使用</h2>
<p>被动技能, 设置打开时开启, 则打开对应文件的时候, 会自动进行语法检查, 高亮错误位置</p>
<p>注意, 针对某些具体语言, 指定了checker, 需要对应安装外部依赖, 例如<code>pyflakes</code>/<code>pep8</code>/<code>jshint</code>等等</p>
<p>主动技能, <code>k-vim</code>中配置绑定了<code>&lt;leader&gt;s</code>打开错误列表面板</p>
<p><img src="/imgs/vim/syntastic.png" alt="syntastic.png"></p>
<p>默认</p>
<pre tabindex="0"><code>:Errors 显示错误面板
:lnext  到下一个错误
:lprevious 到上一个错误
</code></pre><h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>Bundle &#39;scrooloose/syntastic&#39;
let g:syntastic_error_symbol=&#39;&gt;&gt;&#39;
let g:syntastic_warning_symbol=&#39;&gt;&#39;
let g:syntastic_check_on_open=1
let g:syntastic_check_on_wq=0
let g:syntastic_enable_highlighting=1
let g:syntastic_python_checkers=[&#39;pyflakes&#39;] &#34; 使用pyflakes,速度比pylint快
let g:syntastic_javascript_checkers = [&#39;jsl&#39;, &#39;jshint&#39;]
let g:syntastic_html_checkers=[&#39;tidy&#39;, &#39;jshint&#39;]
&#34; 修改高亮的背景色, 适应主题
highlight SyntasticErrorSign guifg=white guibg=black

&#34; to see error location list
let g:syntastic_always_populate_loc_list = 0
let g:syntastic_auto_loc_list = 0
let g:syntastic_loc_list_height = 5
function! ToggleErrors()
    let old_last_winnr = winnr(&#39;$&#39;)
    lclose
    if old_last_winnr == winnr(&#39;$&#39;)
        &#34; Nothing was closed, open syntastic error location panel
        Errors
    endif
endfunction
nnoremap &lt;Leader&gt;s :call ToggleErrors()&lt;cr&gt;
&#34; nnoremap &lt;Leader&gt;sn :lnext&lt;cr&gt;
&#34; nnoremap &lt;Leader&gt;sp :lprevious&lt;cr&gt;
</code></pre><h2 id="建议">建议</h2>
<ol>
<li>支持语言的列表, 见 <a href="https://github.com/scrooloose/syntastic#1-introduction">这里</a></li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>vim插件: delimitmate[符号自动补全]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-delimitmate.html</link>
			<pubDate>Sun, 07 Jun 2015 18:07:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-delimitmate.html</guid>
			<description>k-vim配置 github 作用: 自动补全引号(单引号/双引号/反引号), 括号(()[]{}) github: delimitMate 安装 Bundle &amp;#39;Raimondi/delimitMate&amp;#39; 使用 被动技能, 在编辑输入的时候触发 问题: 如何在</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>作用: 自动补全引号(单引号/双引号/反引号), 括号(<code>()[]{}</code>)</p>
<p>github: <a href="https://github.com/Raimondi/delimitMate">delimitMate</a></p>
<h2 id="安装">安装</h2>
<pre tabindex="0"><code>Bundle &#39;Raimondi/delimitMate&#39;
</code></pre><h2 id="使用">使用</h2>
<p>被动技能, 在编辑输入的时候触发</p>
<p><img src="/imgs/vim/delimitmate.gif" alt="delimitmate.gif"></p>
<blockquote>
<p>问题: 如何在结束输入后, 自动跳转到符号后面</p>
</blockquote>
<p>触发后, 假设你要跳到补全后的符号后面继续编辑, 按<code>Shift-Tab</code></p>
<pre tabindex="0"><code># 1. 按&#34;, 自动补全
&#34;|&#34;

# 2.输入`hello`
&#34;hello|&#34;

# 3. 按 shift-tab, 跳到补全的符号后面, 还是insert-mode
&#34;hello&#34;|
</code></pre><h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>&#34; 自动补全单引号，双引号等
Bundle &#39;Raimondi/delimitMate&#39;

&#34; for python docstring &#34;, 特别有用
au FileType python let b:delimitMate_nesting_quotes = [&#39;&#34;&#39;]
&#34; 关闭某些类型文件的自动补全
&#34;au FileType mail let b:delimitMate_autoclose = 0
</code></pre><h2 id="建议">建议</h2>
<ol>
<li>
<p>有很多款括号等自动补全的插件, 这款在功能和可配上都不错, 需要自定义的可以看文档</p>
</li>
<li>
<p>同类插件</p>
</li>
</ol>
<pre tabindex="0"><code>smartinput https://github.com/kana/vim-smartinput
lexima https://github.com/cohama/lexima.vim  类似多光标替换
auto-pairs https://github.com/jiangmiao/auto-pairs
autoclose  https://github.com/Townk/vim-autoclose
</code></pre><p>TODO:</p>
<pre><code>粘贴代码的时候自动补全括号这个特性怎么去掉?
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>vim插件: matchit[成对标签跳转]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-matchit.html</link>
			<pubDate>Sun, 07 Jun 2015 18:06:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-matchit.html</guid>
			<description>k-vim配置 github 这个插件最后一次更新是2008年, 七年前了&amp;hellip;&amp;hellip; vim的%, 会自动跳转到匹配的()[]{}&amp;lt;</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>这个插件最后一次更新是2008年, 七年前了&hellip;&hellip;</p>
<p>vim的<code>%</code>, 会自动跳转到匹配的<code>()[]{}&lt;&gt;</code>等符号, 但是在编辑<code>html</code>和<code>xml</code>的时候, 可能需要在配对标签直接跳转, 这个插件扩展实现了这个功能.</p>
<p>作用: <code>%</code>跳转到匹配的标签</p>
<p>github: <a href="https://github.com/vim-scripts/matchit.zip">matchit</a></p>
<h2 id="安装">安装</h2>
<pre tabindex="0"><code>Bundle &#39;vim-scripts/matchit.zip&#39;
</code></pre><h2 id="使用">使用</h2>
<p>在需要跳转的位置按<code>%</code>, 跳转到匹配位置</p>
<p>例如 xml/html中支持成对标签之间的跳转</p>
<p><img src="/imgs/vim/matchit.gif" alt="matchit.gif"></p>
<h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>&#34; 没错, 只有一行
Bundle &#39;vim-scripts/matchit.zip&#39;
</code></pre><h2 id="建议">建议</h2>
<ol>
<li>根据自己需求确定要不要这个插件, 虽然小, 但是没用的话就不需要装了</li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>vim插件: easy-align[快速对齐]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-easyalign.html</link>
			<pubDate>Sun, 07 Jun 2015 18:05:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-easyalign.html</guid>
			<description>k-vim配置 github 作用: 快速进行对齐/格式化 github: vim-easy-align 安装 Bundle &amp;#39;junegunn/vim-easy-align&amp;#39; 使用 绑定快捷键 &amp;lt;leader&amp;gt;a, 使用V进入选择, 选取多行, 之后触发 ,a= 对齐等号表达 ,a: 对齐冒号表达式(js</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>作用: 快速进行对齐/格式化</p>
<p>github: <a href="https://github.com/junegunn/vim-easy-align">vim-easy-align</a></p>
<h2 id="安装">安装</h2>
<pre tabindex="0"><code>Bundle &#39;junegunn/vim-easy-align&#39;
</code></pre><h2 id="使用">使用</h2>
<p>绑定快捷键 <code>&lt;leader&gt;a</code>, 使用<code>V</code>进入选择, 选取多行, 之后触发</p>
<pre tabindex="0"><code>,a=        对齐等号表达
,a:        对齐冒号表达式(json/map等)

# 默认左对齐
,a&lt;space&gt;  首个空格对齐
,a2&lt;space&gt; 第二个空格对齐
,a-&lt;space&gt; 倒数第一个空格对齐
,a-2&lt;space&gt; 倒数第二个空格对齐
,a*&lt;space&gt; 所有空格依次对齐

# 右对齐
,a&lt;Enter&gt;*&lt;space&gt;
</code></pre><p><img src="/imgs/vim/easy-align.gif" alt="easy-align.gif"></p>
<h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>Bundle &#39;junegunn/vim-easy-align&#39;
vmap &lt;Leader&gt;a &lt;Plug&gt;(EasyAlign)
nmap &lt;Leader&gt;a &lt;Plug&gt;(EasyAlign)
if !exists(&#39;g:easy_align_delimiters&#39;)
  let g:easy_align_delimiters = {}
endif
let g:easy_align_delimiters[&#39;#&#39;] = { &#39;pattern&#39;: &#39;#&#39;, &#39;ignore_groups&#39;: [&#39;String&#39;] }
</code></pre><h2 id="建议">建议</h2>
<ol>
<li>同类插件 <a href="https://github.com/godlygeek/tabular">tabular</a>
, 个人觉得<code>vim-easy-align</code>更符合直觉, 更新频繁, 文档, 教程也更全, <a href="https://github.com/junegunn/vim-easy-align/blob/master/EXAMPLES.md">例子</a></li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>vim插件: multiple-cursors[多光标操作]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-multiplecursors.html</link>
			<pubDate>Sun, 07 Jun 2015 18:03:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-multiplecursors.html</guid>
			<description>k-vim配置 github 作用: 类似sublimetext的多光标选中 github: vim=multiple-cursors 安装 Bundle &amp;#39;terryma/vim-multiple-cursors&amp;#39; 使用 默认快捷键 ctrl+m 选中一个 ctrl+p 放弃一个, 回到上一个 ctrl+x 跳过当前选中, 选中下</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>作用: 类似sublimetext的多光标选中</p>
<p>github: <a href="https://github.com/terryma/vim-multiple-cursors">vim=multiple-cursors</a></p>
<h2 id="安装">安装</h2>
<pre tabindex="0"><code>Bundle &#39;terryma/vim-multiple-cursors&#39;
</code></pre><h2 id="使用">使用</h2>
<p>默认快捷键</p>
<pre tabindex="0"><code>ctrl+m 选中一个
ctrl+p 放弃一个, 回到上一个
ctrl+x 跳过当前选中, 选中下一个
esc    退出
</code></pre><p><img src="/imgs/vim/multiple-cursors.gif" alt="multiple-cursors.gif"></p>
<p>选中后, 可以进行增删替换<code>a/c/x</code>等</p>
<h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>Bundle &#39;terryma/vim-multiple-cursors&#39;
let g:multi_cursor_use_default_mapping=0
&#34; Default mapping
let g:multi_cursor_next_key=&#39;&lt;C-m&gt;&#39;
let g:multi_cursor_prev_key=&#39;&lt;C-p&gt;&#39;
let g:multi_cursor_skip_key=&#39;&lt;C-x&gt;&#39;
let g:multi_cursor_quit_key=&#39;&lt;Esc&gt;&#39;
</code></pre><h2 id="建议">建议</h2>
<p>无</p>
]]></content>
		</item>
		
		<item>
			<title>vim插件: vim-signature[快速标记跳转]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-signature.html</link>
			<pubDate>Sun, 07 Jun 2015 18:02:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-signature.html</guid>
			<description>k-vim配置 github 作用: 标签展示以及快速跳转(增强vim的书签功能) github: signature 安装 Bundle &amp;#34;kshenoy/vim-signature&amp;#34; 使用 使用默认快捷键 m[a-zA-Z] 打标签 &amp;#39;[a-zA-Z] 跳转到标签位置 &amp;#39;. 最后一次变更的地方</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>作用: 标签展示以及快速跳转(增强vim的书签功能)</p>
<p>github: <a href="https://github.com/kshenoy/vim-signature">signature</a></p>
<h2 id="安装">安装</h2>
<pre tabindex="0"><code>Bundle &#34;kshenoy/vim-signature&#34;
</code></pre><h2 id="使用">使用</h2>
<p>使用默认快捷键</p>
<pre tabindex="0"><code>m[a-zA-Z]   打标签
&#39;[a-zA-Z]   跳转到标签位置

&#39;.          最后一次变更的地方
&#39;&#39;          跳回来的地方(最近两个位置跳转)

m&lt;space&gt;    去除所有标签
</code></pre><p><img src="/imgs/vim/signature.gif" alt="signature.gif"></p>
<h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>Bundle &#34;kshenoy/vim-signature&#34;
</code></pre><h2 id="建议">建议</h2>
<p>无</p>
]]></content>
		</item>
		
		<item>
			<title>vim插件: easymotion[快速跳转]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-easymotion.html</link>
			<pubDate>Sun, 07 Jun 2015 18:01:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-easymotion.html</guid>
			<description>k-vim配置 github 说明: &amp;lt;leader&amp;gt;全局映射为, 除却hjkl, gg, G, Ctrl-D/U, 以及 [f/F]&amp;lt;char&amp;gt;和[t/T]&amp;lt;</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>说明: <code>&lt;leader&gt;</code>全局映射为<code>,</code></p>
<p>除却<code>hjkl</code>, <code>gg</code>, <code>G</code>, <code>Ctrl-D/U</code>, 以及 <code>[f/F]&lt;char&gt;</code>和<code>[t/T]&lt;char&gt;</code>这些<code>vim</code>默认的移动方式</p>
<p>有没有更高效的移动做法么? 回答是肯定的</p>
<p>这个插件的唯一目的就是: 快速跳转</p>
<p>作用: 如何进行更快速的光标移动</p>
<p>github: <a href="https://github.com/Lokaltog/vim-easymotion">github</a></p>
<h2 id="安装">安装</h2>
<pre><code>Bundle 'Lokaltog/vim-easymotion'
</code></pre>
<h2 id="用法1-跳转到当前光标前后的位置wb">用法1: 跳转到当前光标前后的位置(w/b)</h2>
<p>快捷键<code>&lt;leader&gt;&lt;leader&gt;w</code>(即<code>,,w</code>)和<code>&lt;leader&gt;&lt;leader&gt;b</code>(即<code>,,b</code>)</p>
<p>助记: <code>word</code> and <code>back</code></p>
<p>演示:</p>
<p><img src="/imgs/vim/easy_motion_base.gif" alt="easy_motion_base.gif"></p>
<p>easy_motion_base.gif</p>
<h2 id="用法2-搜索跳转s">用法2: 搜索跳转(s)</h2>
<p>快捷键<code>&lt;leader&gt;&lt;leader&gt;s</code>(即<code>,,s</code>), 然后输入要搜索的字母, 这个跳转是双向的</p>
<p>助记: <code>search</code></p>
<p>演示:</p>
<p><img src="/imgs/vim/easy_motion_search.gif" alt="easy_motion_search.gif"></p>
<h2 id="用法3-行级跳转jk">用法3: 行级跳转(jk)</h2>
<p>配置</p>
<pre tabindex="0"><code>map &lt;Leader&gt;&lt;Leader&gt;j &lt;Plug&gt;(easymotion-j)
map &lt;Leader&gt;&lt;Leader&gt;k &lt;Plug&gt;(easymotion-k)
</code></pre><p>快捷键: <code>&lt;leader&gt;&lt;leader&gt;j</code>和<code>&lt;leader&gt;&lt;leader&gt;k</code>(即<code>,,j</code>和<code>,,k</code>)</p>
<p>助记: <code>hjkl</code>不解释</p>
<p>演示:</p>
<p><img src="/imgs/vim/easy_motion_lines.gif" alt="easy_motion_lines.gif"></p>
<h2 id="用法4-行内跳转hl">用法4: 行内跳转(hl)</h2>
<p>配置</p>
<pre tabindex="0"><code>map &lt;Leader&gt;&lt;leader&gt;h &lt;Plug&gt;(easymotion-linebackward)
map &lt;Leader&gt;&lt;leader&gt;l &lt;Plug&gt;(easymotion-lineforward)
</code></pre><p>快捷键<code>&lt;leader&gt;&lt;leader&gt;h</code>和<code>&lt;leader&gt;&lt;leader&gt;l</code>(即<code>,,h</code>和<code>,,l</code>)</p>
<p>助记: <code>hjkl</code>不解释</p>
<p><img src="/imgs/vim/easy_motion_inline.gif" alt="easy_motion_inline.gif"></p>
<h2 id="用法5-重复上一次动作">用法5: 重复上一次动作(.)</h2>
<p>配置</p>
<pre tabindex="0"><code>map &lt;Leader&gt;&lt;leader&gt;. &lt;Plug&gt;(easymotion-repeat)
</code></pre><p>快捷键<code>&lt;leader&gt;&lt;leader&gt;.</code></p>
<p>助记: 同<code>repeat</code>插件&hellip;.</p>
<p><img src="/imgs/vim/easy_motion_repeat.gif" alt="easy_motion_repeat.gif"></p>
<hr>
<h2 id="最终配置">最终配置</h2>
<pre tabindex="0"><code>Bundle &#39;Lokaltog/vim-easymotion&#39;
let g:EasyMotion_smartcase = 1
&#34;let g:EasyMotion_startofline = 0 &#34; keep cursor colum when JK motion
map &lt;Leader&gt;&lt;leader&gt;h &lt;Plug&gt;(easymotion-linebackward)
map &lt;Leader&gt;&lt;Leader&gt;j &lt;Plug&gt;(easymotion-j)
map &lt;Leader&gt;&lt;Leader&gt;k &lt;Plug&gt;(easymotion-k)
map &lt;Leader&gt;&lt;leader&gt;l &lt;Plug&gt;(easymotion-lineforward)
&#34; 重复上一次操作, 类似repeat插件, 很强大
map &lt;Leader&gt;&lt;leader&gt;. &lt;Plug&gt;(easymotion-repeat)
</code></pre><hr>
<h2 id="建议">建议</h2>
<ol>
<li>
<p>还可以<code>&lt;Leader&gt;&lt;leader&gt;f</code>和<code>&lt;Leader&gt;&lt;leader&gt;t</code>, 不过建议简单化, 一个<code>&lt;Leader&gt;&lt;leader&gt;w/b</code>走天下.</p>
</li>
<li>
<p>如果你不经常使用<code>s</code>, 可以将<code>s</code>改键, <code>nmap s &lt;Plug&gt;(easymotion-s)</code>, 这样你只需要输入<code>s</code>就可以进行搜索快速跳转(强迫症表示不能忍&hellip;.)
具体做法见<a href="https://github.com/Lokaltog/vim-easymotion#bidirectional-motions">官方文档</a></p>
</li>
<li>
<p>默认<code>&lt;leader&gt;&lt;leader&gt;</code>作为这个插件的快捷键其实挺好的, 貌似没有其他插件会导致冲突, 还可以配置一整套, 强迫症很满意</p>
</li>
<li>
<p>可以配置2/n个字符的搜索跳转, 更精准, 按需自取(个人觉得太复杂了没必要) <a href="https://github.com/Lokaltog/vim-easymotion#2-character-search-motion">文档</a>和<a href="https://github.com/Lokaltog/vim-easymotion#n-character-search-motion">文档</a></p>
</li>
<li>
<p>这个插件专心做好跳转就好, 没必要把搜索的活给做了</p>
</li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>vim插件: vundle[管理插件]</title>
			<link>https://wklken.me/posts/2015/06/07/vim-plugin-vundle.html</link>
			<pubDate>Sun, 07 Jun 2015 18:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/06/07/vim-plugin-vundle.html</guid>
			<description>k-vim配置 github 第一个需要手动安装的插件, 其他的插件通过这个来进行管理 作用: 管理其他所有插件(安装/更新/移除) github:vundle 安装 git clone https://github.com/gmarik/Vundle.vim.git ~/.vim/bundle/Vundle.vi 使用 在.vim</description>
			<content type="html"><![CDATA[<p>k-vim配置 <a href="https://github.com/wklken/k-vim">github</a></p>
<hr>
<p>第一个需要手动安装的插件, 其他的插件通过这个来进行管理</p>
<p>作用: 管理其他所有插件(安装/更新/移除)</p>
<p>github:<a href="https://github.com/gmarik/Vundle.vim">vundle</a></p>
<h2 id="安装">安装</h2>
<pre tabindex="0"><code>git clone https://github.com/gmarik/Vundle.vim.git ~/.vim/bundle/Vundle.vi
</code></pre><h2 id="使用">使用</h2>
<p>在<code>.vimrc</code>中加入/修改/删除自己需要的插件配置</p>
<pre tabindex="0"><code>Bundle &#39;scrooloose/syntastic&#39;
</code></pre><p>然后, 在命令行模式下运行</p>
<pre tabindex="0"><code>:BundleInstall     install 安装配置的插件
:BundleInstall!    update  更新
:BundleClean       remove plugin not in list 删除本地无用插件
</code></pre><p><img src="/imgs/vim/vundle.png" alt="vundle.png"></p>
<h2 id="其他">其他</h2>
<ol>
<li>
<p>同类插件 <a href="https://github.com/tpope/vim-pathogen">pathogen</a>, <a href="https://github.com/Shougo/neobundle.vim">neobundle</a></p>
</li>
<li>
<p>依旧是同类插件, <a href="https://github.com/junegunn/vim-plug">vim-plug</a>, 不过这个支持并行安装插件(目测如果是第一次配置机器会快很多&hellip;.)</p>
</li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>Elasticsearch几个问题的解决</title>
			<link>https://wklken.me/posts/2015/05/23/elasticsearch-issues.html</link>
			<pubDate>Sat, 23 May 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/05/23/elasticsearch-issues.html</guid>
			<description>今天惯例看统计报表, 才发现es集群悲剧了&amp;hellip;&amp;hellip;昨天下午到今天早上, 持续报错, 写了1G的错误日志&amp;gt;_&amp;lt;#</description>
			<content type="html"><![CDATA[<p>今天惯例看统计报表, 才发现es集群悲剧了&hellip;&hellip;昨天下午到今天早上, 持续报错, 写了1G的错误日志&gt;_&lt;#(暂无监控&hellip;.)</p>
<p>当前状态: 单台机器, 单节点(空集群), 200W 数据, 500+shrads,  约3G大小</p>
<p>以下是几个问题的处理过程</p>
<h3 id="大量unassigned-shards">大量unassigned shards</h3>
<p>其实刚搭完运行时就是<code>status: yellow</code>(所有主分片可用，但存在不可用的从分片), 只有一个节点, 主分片启动并运行正常, 可以成功处理请求, 但是存在<code>unassigned_shards</code>, 即存在没有被分配到节点的从分片.(只有一个节点&hellip;..)</p>
<p>.当时数据量小, 就暂时没关注. 然后, 随着时间推移,  出现了大量unassigned shards</p>
<pre tabindex="0"><code>curl -XGET http://localhost:9200/_cluster/health\?pretty
{
  &#34;cluster_name&#34; : &#34;elasticsearch&#34;,
  &#34;status&#34; : &#34;yellow&#34;,
  &#34;timed_out&#34; : false,
  &#34;number_of_nodes&#34; : 2,
  &#34;number_of_data_nodes&#34; : 1,
  &#34;active_primary_shards&#34; : 538,
  &#34;active_shards&#34; : 538,
  &#34;relocating_shards&#34; : 0,
  &#34;initializing_shards&#34; : 0,
  &#34;unassigned_shards&#34; : 558,
&#34;number_of_pending_tasks&#34; : 0
}
</code></pre><p>处理方式:  找了台内网机器, 部署另一个节点(保证<code>cluster.name</code>一致即可, 自动发现, 赞一个). 当然, 如果你资源有限只有一台机器,  使用相同命令再启动一个es实例也行. 再次检查集群健康, 发现<code>unassigned_shards</code>减少, <code>active_shards</code>增多.</p>
<p>操作完后, 集群健康从<code>yellow</code>恢复到 <code>green</code></p>
<h3 id="status-red">status: red</h3>
<p>集群健康恶化了&hellip;&hellip;</p>
<p>这次检查发现是<code>status: red</code>(存在不可用的主要分片)</p>
<pre tabindex="0"><code>curl -XGET http://localhost:9200/_cluster/health\?pretty
{
  &#34;cluster_name&#34; : &#34;elasticsearch&#34;,
  &#34;status&#34; : &#34;red&#34;,    // missing some primary shards
  &#34;timed_out&#34; : false,
  &#34;number_of_nodes&#34; : 4,
  &#34;number_of_data_nodes&#34; : 2,
  &#34;active_primary_shards&#34; : 538,
  &#34;active_shards&#34; : 1076,
  &#34;relocating_shards&#34; : 0,
  &#34;initializing_shards&#34; : 0,
  &#34;unassigned_shards&#34; : 20,  // where your lost primary shards are.
  &#34;number_of_pending_tasks&#34; : 0
}
</code></pre><h3 id="fix-unassigned-shards">fix unassigned shards</h3>
<p>开始着手修复</p>
<p>查看所有分片状态</p>
<pre tabindex="0"><code>curl -XGET http://localhost:9200/_cat/shards
</code></pre><p>找出<code>UNASSIGNED</code>分片</p>
<pre tabindex="0"><code>curl -s &#34;http://localhost:9200/_cat/shards&#34; | grep UNASSIGNED
pv-2015.05.22                 3 p UNASSIGNED
pv-2015.05.22                 3 r UNASSIGNED
pv-2015.05.22                 1 p UNASSIGNED
pv-2015.05.22                 1 r UNASSIGNED
</code></pre><p>查询得到master节点的唯一标识</p>
<pre tabindex="0"><code>curl &#39;localhost:9200/_nodes/process?pretty&#39;

{
  &#34;cluster_name&#34; : &#34;elasticsearch&#34;,
  &#34;nodes&#34; : {
    &#34;AfUyuXmGTESHXpwi4OExxx&#34; : {
      &#34;name&#34; : &#34;Master&#34;,
     ....
      &#34;attributes&#34; : {
        &#34;master&#34; : &#34;true&#34;
      },
.....
</code></pre><p>执行reroute(分多次, 变更shard的值为<code>UNASSIGNED</code>查询结果中编号, 上一步查询结果是1和3)</p>
<pre tabindex="0"><code>curl -XPOST &#39;localhost:9200/_cluster/reroute&#39; -d &#39;{
        &#34;commands&#34; : [ {
              &#34;allocate&#34; : {
                  &#34;index&#34; : &#34;pv-2015.05.22&#34;,
                  &#34;shard&#34; : 1,
                  &#34;node&#34; : &#34;AfUyuXmGTESHXpwi4OExxx&#34;,
                  &#34;allow_primary&#34; : true
              }
            }
        ]
    }&#39;
</code></pre><p>批量处理的脚本(当数量很多的话, 注意替换node的名字)</p>
<pre tabindex="0"><code>#!/bin/bash

for index in $(curl  -s &#39;http://localhost:9200/_cat/shards&#39; | grep UNASSIGNED | awk &#39;{print $1}&#39; | sort | uniq); do
    for shard in $(curl  -s &#39;http://localhost:9200/_cat/shards&#39; | grep UNASSIGNED | grep $index | awk &#39;{print $2}&#39; | sort | uniq); do
        echo  $index $shard

        curl -XPOST &#39;localhost:9200/_cluster/reroute&#39; -d &#34;{
            &#39;commands&#39; : [ {
                  &#39;allocate&#39; : {
                      &#39;index&#39; : $index,
                      &#39;shard&#39; : $shard,
                      &#39;node&#39; : &#39;Master&#39;,
                      &#39;allow_primary&#39; : true
                  }
                }
            ]
        }&#34;

        sleep 5
    done
done
</code></pre><h3 id="too-many-open-files">“Too many open files”</h3>
<p>发现日志中大量出现这个错误</p>
<p>执行</p>
<pre tabindex="0"><code>curl http://localhost:9200/_nodes/process\?pretty
</code></pre><p>可以看到</p>
<pre tabindex="0"><code>&#34;max_file_descriptors&#34; : 4096,
</code></pre><p>官方文档中</p>
<blockquote>
<p>Make sure to increase the number of open files descriptors on the machine (or for the user running elasticsearch). Setting it to 32k or even 64k is recommended.</p>
</blockquote>
<p>而此时, 可以在系统级做修改, 然后全局生效</p>
<p>最简单的做法, 在<code>bin/elasticsearch</code>文件开始的位置加入</p>
<pre tabindex="0"><code>ulimit -n 64000
</code></pre><p>然后重启es, 再次查询看到</p>
<pre tabindex="0"><code>&#34;max_file_descriptors&#34; : 64000,
</code></pre><p>问题解决</p>
<hr>
<p>待续, 目测还有很多坑, 而且随着数据量上来, 会遇到越来越多的坑&hellip;&hellip;</p>
<p>2015-05-23
于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>分享一份 Vim 简介PPT</title>
			<link>https://wklken.me/posts/2015/05/10/vim-intro.html</link>
			<pubDate>Sun, 10 May 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/05/10/vim-intro.html</guid>
			<description>这是近期做团队分享的一份ppt, 原稿用markdown仓促写的, 感谢markdown及weakpoint, 可以直接将markdown转成pp</description>
			<content type="html"><![CDATA[<p>这是近期做团队分享的一份ppt, 原稿用<code>markdown</code>仓促写的, 感谢<code>markdown</code>及<a href="https://github.com/onesuper/weakpoint">weakpoint</a>, 可以直接将<code>markdown</code>转成ppt</p>
<p>这次分享主要不是讲vim, 更多的是关于工具的使用和思考</p>
<p>其实, 无非是记住一些东西, 忘记一些东西, 定制和寻找适合自己的工具, 善用工具, 成为主人而不是奴隶</p>
<p>将markdown原稿放出, 很多实操演示, 在文稿中是看不到的&hellip;&hellip;</p>
<hr>
<h2 id="vim">vim</h2>
<p>wklken</p>
<hr>
<h2 id="vim-1">vim</h2>
<p>曲线:</p>
<p><img src="/imgs/vim/learn_diff.jpg" alt="learn-diff"></p>
<hr>
<h2 id="vim-2">vim</h2>
<p>Vim the Six Billion Dollar editor</p>
<blockquote>
<p>Better, Stronger, Faster.</p>
</blockquote>
<p>Maybe:</p>
<blockquote>
<p>成为你最后一个使用的编辑器</p>
</blockquote>
<hr>
<h2 id="vim-3">vim</h2>
<blockquote>
<p>文本编辑器 , 不是IDE</p>
</blockquote>
<p>能做一些事情, 但是一些事情是做不到的, 不要强求, 该用IDE的时候, 用就是了</p>
<hr>
<h2 id="how">how</h2>
<p>步骤:(简明 Vim 练级攻略)</p>
<pre><code>存活
感觉良好
觉得更好，更强，更快
使用VIM的超能力
</code></pre>
<p>诀窍:</p>
<pre><code>不断练习

肌肉记忆, 直觉-行动而不是思考-行动(十倍差距)
</code></pre>
<hr>
<h2 id="过程">过程:</h2>
<p>一个vimer必定会经历的过程</p>
<ol>
<li>什么都没有, 纯vi</li>
<li>什么都有</li>
<li>只留适合自己的, 不适合自己也要配置成适合自己的</li>
<li>什么都没有(听说)</li>
</ol>
<hr>
<h2 id="target">target</h2>
<ul>
<li>实操: vim能做什么?</li>
<li>思考:</li>
</ul>
<ol>
<li>哪些是你常用编辑器可以实现的?</li>
<li>哪些好的功能是你想要却没有的? 能否解决</li>
<li>你的痛点在哪里?</li>
<li>工作流</li>
</ol>
<hr>
<h2 id="模式">模式</h2>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/vim-modes.png?raw=true" alt="vim-mode"></p>
<hr>
<h2 id="移动1">移动1</h2>
<p>概览</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/vim-movement.png?raw=true" alt=""></p>
<hr>
<h2 id="移动2">移动2</h2>
<p>忘掉刚才那张图&hellip;&hellip;</p>
<hr>
<h2 id="移动3">移动3</h2>
<ul>
<li>
<p>hjkl (请杜绝方向键, 移动右手到方向键区浪费时间)</p>
<pre><code>  map &lt;Left&gt; &lt;Nop&gt;
  map &lt;Right&gt; &lt;Nop&gt;
  map &lt;Up&gt; &lt;Nop&gt;
  map &lt;Down&gt; &lt;Nop&gt;
</code></pre>
</li>
<li>
<p>单词: w / b / e (忘记: W/B/E-以空白为分隔符, 要多按shift键键/不好记/用得少)</p>
</li>
<li>
<p>行内: 0 / $  (忘记: ^, 够不着啊)</p>
</li>
<li>
<p>段落: { / }</p>
</li>
</ul>
<hr>
<h2 id="移动4">移动4</h2>
<ul>
<li>
<p>页: &lt;ctrl-u&gt; / &lt;ctrl-d&gt; (忘记: ctrl-f/ctrl-b, 都在左侧键区左手太累, 经常是要上要下, 而不是往一个方向走)</p>
</li>
<li>
<p>可视范围: H M L (全部忘记, 没多大用, 要按shift, 混淆记忆)</p>
</li>
<li>
<p>文件内: gg / G / :N (基本够了)</p>
</li>
<li>
<p>匹配处: % 括号匹配 /  # (忘记: *, 在键盘左侧, 太远不好敲, 可以交换#和*的功能)</p>
</li>
</ul>
<p>更强大的, 看后面的插件部分</p>
<hr>
<h2 id="编辑">编辑</h2>
<blockquote>
<p>N&lt;action&gt;</p>
</blockquote>
<ul>
<li>
<p>x</p>
</li>
<li>
<p>dd</p>
</li>
<li>
<p>dw / db / d$ / dG / dgg</p>
</li>
<li>
<p>yw / yb / y$</p>
</li>
<li>
<p>yy / p / P</p>
</li>
<li>
<p>J</p>
</li>
</ul>
<hr>
<h2 id="撤销和重复">撤销和重复</h2>
<ul>
<li>
<p>u</p>
</li>
<li>
<p>.</p>
</li>
<li>
<p>N&lt;command&gt;</p>
</li>
</ul>
<hr>
<h2 id="选中">选中</h2>
<p>select</p>
<ul>
<li>v:  vw / vb / vta / v$</li>
</ul>
<p>block select</p>
<ul>
<li>&lt;ctrl+v&gt; -&gt; hjkl/&lt;ctrl+d&gt;</li>
</ul>
<p>行首加 / 行尾加</p>
<hr>
<h2 id="搜索">搜索</h2>
<ul>
<li>行内搜索: f / t (可以选择性遗忘t, 同时, 忘记F/T, 甚至可以忘记f, 你不需要)</li>
</ul>
<p>为什么? 看后面的easy-motion插件</p>
<hr>
<h2 id="替换">替换</h2>
<p>记住这两个似乎够了(频率最高)</p>
<ul>
<li>
<p>:1,10s/a/b/g</p>
</li>
<li>
<p>:%s/a/b/g</p>
</li>
</ul>
<p>字符替换</p>
<ul>
<li>rX</li>
</ul>
<hr>
<h2 id="文本对象">文本对象</h2>
<p>初学者基本不了解的一个特性</p>
<ul>
<li>
<p>&lt;action&gt;a&lt;object&gt;  or &lt;action&gt;i&lt;object&gt;</p>
</li>
<li>
<p>a = all /  i = in</p>
</li>
<li>
<p>action: d y v c</p>
</li>
<li>
<p>object:</p>
<pre><code>  w(world) / s(sentence) / p{paragraph}
  &quot; ' ) ] }
</code></pre>
</li>
<li>
<p>装插件, 可以是: l(line) e(entire file)  i(indent)</p>
</li>
</ul>
<hr>
<h2 id="分屏">分屏</h2>
<ul>
<li>
<p>:sp file1</p>
</li>
<li>
<p>:vsp file2</p>
</li>
<li>
<p>ctrl-w-h/j/k/l (改键 ctrl-h/j/k/l)</p>
</li>
<li>
<p>ctrl-w-H/J/K/L</p>
</li>
</ul>
<hr>
<h2 id="插件1-语法检查">插件1: 语法检查</h2>
<ul>
<li><a href="https://github.com/scrooloose/syntastic">syntastic</a></li>
</ul>
<hr>
<h2 id="插件2-自动补全与代码片段">插件2: 自动补全与代码片段</h2>
<p>两大效率神器</p>
<ul>
<li><a href="https://github.com/Valloric/YouCompleteMe">YCM</a></li>
</ul>
<p>毫秒级补全/ python / c系等, 编译安装, 具体自行文档</p>
<ul>
<li><a href="https://github.com/SirVer/ultisnips">ultisnips</a></li>
<li><a href="https://github.com/honza/vim-snippets">vim-snippets</a></li>
</ul>
<p>括号补全</p>
<ul>
<li><a href="https://github.com/Raimondi/delimitMate">delimimate</a></li>
</ul>
<p>xml/html标签补全</p>
<ul>
<li><a href="https://github.com/docunext/closetag.vim">closetag</a></li>
</ul>
<hr>
<h2 id="插件3-快速编码">插件3: 快速编码</h2>
<p>快速注释:</p>
<ul>
<li><a href="https://github.com/scrooloose/nerdcommenter">nerdcommenter</a></li>
</ul>
<p>快速编辑</p>
<ul>
<li><a href="https://github.com/tpope/vim-surround">vim-surround</a></li>
<li><a href="https://github.com/tpope/vim-repeat">vim-repeat</a></li>
</ul>
<p>去空格</p>
<ul>
<li><a href="https://github.com/bronson/vim-trailing-whitespace">vim-trailing-whitespace</a></li>
</ul>
<p>代码对齐</p>
<ul>
<li><a href="https://github.com/junegunn/vim-easy-align">vim-easy-align</a></li>
</ul>
<hr>
<h2 id="插件4-快速运行">插件4: 快速运行</h2>
<ul>
<li><a href="https://github.com/thinca/vim-quickrun">vim-quickrun</a></li>
</ul>
<hr>
<h2 id="插件5-快速移动">插件5: 快速移动</h2>
<p>行/位置/搜索</p>
<ul>
<li><a href="https://github.com/Lokaltog/vim-easymotion">vim-easymotion</a></li>
</ul>
<p>mark</p>
<ul>
<li><a href="https://github.com/kshenoy/vim-signature">vim-signature</a></li>
</ul>
<hr>
<h2 id="插件6-快速选中">插件6: 快速选中</h2>
<p>区块</p>
<ul>
<li><a href="https://github.com/terryma/vim-expand-region">vim-expand-region</a></li>
</ul>
<p>多标签</p>
<ul>
<li><a href="https://github.com/terryma/vim-multiple-cursors">vim-multiple-cursors</a></li>
</ul>
<hr>
<h2 id="插件7-文件导航搜索">插件7: 文件导航/搜索</h2>
<p>目录导航</p>
<ul>
<li><a href="https://github.com/scrooloose/nerdtree">nerdtree</a></li>
</ul>
<p>标签导航</p>
<ul>
<li><a href="https://github.com/majutsushi/tagbar">tagbar</a></li>
</ul>
<p>搜索文件</p>
<ul>
<li><a href="https://github.com/kien/ctrlp.vim">ctrlp.vim</a></li>
</ul>
<p>搜索代码</p>
<ul>
<li><a href="https://github.com/dyng/ctrlsf.vim">ctrlsf.vim</a></li>
</ul>
<hr>
<h2 id="关于插件1">关于插件1</h2>
<ul>
<li>不是越多越好</li>
<li>配了用不上 = 没配 + 浪费资源</li>
<li>同一功能, 对比几个插件, 选择一个合适的</li>
<li>快捷键配置一定要容易记</li>
<li>相信我, 一个插件用的最多的快捷键就两个, 绝大多数情况下不会多于两个, 不用耗费心力在配置<code>更强大的</code>操作上</li>
</ul>
<hr>
<h2 id="关于插件2">关于插件2</h2>
<ul>
<li>更符合自觉的键位/操作</li>
<li>尽量减少敲击次数</li>
<li>杜绝一切无效的敲击</li>
<li>个性化, 定制到每个细节, 力争解决自己所有痛点</li>
</ul>
<hr>
<h2 id="高级">高级</h2>
<ul>
<li>
<p>怎么配置:</p>
<p>参考下别人的vim配置, 读插件文档, 读插件代码, 必要时改一份</p>
</li>
</ul>
<hr>
<h2 id="资源">资源</h2>
<p>配置:</p>
<ul>
<li><a href="https://github.com/spf13/spf13-vim">spf13</a></li>
<li><a href="https://github.com/wklken/k-vim">k-vim</a></li>
<li><a href="https://github.com/square/maximum-awesome">maximum-awsome</a></li>
</ul>
<p>文章:</p>
<ul>
<li><a href="http://www.jianshu.com/p/bcbe916f97e1">vim 入门基础</a></li>
<li><a href="http://coolshell.cn/articles/5426.html">简明vim练级攻略</a></li>
<li><a href="http://www.kunli.info/2013/08/13/vim/">不要复杂化vim</a></li>
<li><a href="http://segmentfault.com/blog/nightire/1190000000445598">vim 哲学</a> 一个系列, 推荐</li>
</ul>
<p>others:</p>
<ul>
<li><a href="http://vimawesome.com/">插件库 vimawsome</a></li>
<li><a href="http://vimcolors.com/">主题库 vim colors</a></li>
</ul>
<hr>
<h2 id="qa">QA</h2>
<p>Thx:)</p>
]]></content>
		</item>
		
		<item>
			<title>k-vim 更新9.0版本</title>
			<link>https://wklken.me/posts/2015/05/05/k-vim-update-v9.html</link>
			<pubDate>Tue, 05 May 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/05/05/k-vim-update-v9.html</guid>
			<description>直达链接 趁着近期有点时间, 将积累几个月的改进/痛点/更新等处理了一把, k-vim正式更新到9.0版本. 如果喜欢, 欢迎star/fork, 欢迎</description>
			<content type="html"><![CDATA[<p><a href="https://github.com/wklken/k-vim">直达链接</a></p>
<p>趁着近期有点时间, 将积累几个月的改进/痛点/更新等处理了一把, <code>k-vim</code>正式更新到9.0版本.</p>
<p>如果喜欢, 欢迎star/fork, 欢迎提pr.</p>
<p>本次更新:  <a href="https://github.com/wklken/k-vim/blob/master/UPDATE_LOG.md">完整更新日志</a></p>
<pre tabindex="0"><code class="language-markdown " data-lang="markdown ">    1. 新增依赖ag(the_silver_searcher)

    安装 [the_silver_searcher](https://github.com/ggreer/the_silver_searcher#installing)

    具体见文档

    2. 引入 thinca/vim-quickrun

        2.1 以message的方式展示, 同原先的F10行为, 按回车过掉消息
        2.2    F10 运行 / ,r  运行

    2. 引入dyng/ctrlsf.vim, 类似 sublimetext的全局搜索

        2.1 依赖于ag的全局搜索
        2.2 将光标挪到单词, 快捷键\  - 进入全局搜索, 移入分屏界面, o/t/T/q操作


    3. 代码折叠

        3.1 &lt;leader&gt;zz 折叠/打开所有代码toggle(本次新增配置)
        3.2 za 当前光标所在区域折叠toggle(vim默认的)

    4. syntastic语法检查

        4.1 修正语法检查错误高亮, 精确到具体错误单词
        4.2 开启python的pep8, 允许忽略某些warning, vimrc.bundles: line 40
        4.3 &lt;leader&gt;s  打开当前文件所有语法错误列表(新增配置)

    5.  easymotion

        5.1 &lt;leader&gt;&lt;leader&gt;.  重复上一次easymotion命令, 更高效(新增配置)

    6. 修改RainbowParentheses, 防止黑色括号出现

    7. 修改vim-expand-region快捷键

        7.1 v 扩增选中范围
        7.2 V 缩小选中范围

    8. 新增主题tomorrow
</code></pre><p>后面, 针对每个插件的使用, 之前笔记整理了一把, 后续逐步发出.</p>
<hr>
<p>另外, 开始在实际项目中更多的使用<code>golang</code>, 最近在搞ELK日志收集统计系统,  做了一个收集任意端上报数据, 落地成日志文件, 然后经由logstash转存储到es.</p>
<p><a href="https://github.com/wklken/http_json_logger">http_json_logger</a>: 一个日志上报收集服务, 可以收集从浏览器/js/android/ios等通过http上报的日志, 落地为文本文件, 用作后续日志统计/分析/数据挖掘等. logger模块是使用<code>beego</code>的logger模块精简后的, 这是第二个<code>golang</code>项目, 上一个是下拉提示 <a href="https://github.com/wklken/suggestion">suggestion</a></p>
<p>另一个更新是, 花了两个小时, 给blog文章页面加了生成目录的功能,  另外搞了下阅读时左侧展示当前所在位置标题.(还无法支持响应式&hellip;&hellip;)</p>
<hr>
<p>度过了接近一年多的瓶颈期, 过去一年多可能是近几年迷茫和挣扎的一段时间,    眼睁睁看自己在某个地方卡住, 苦苦挣扎, 一步步往前, 这个过程是痛苦的, 独自成长, 一点点地去达成1万小时的目标</p>
<p>随着时间流逝, 一切在逐渐明晰, 从测试转开发, 正式两年半了, 不短不长, 近期却有很多感悟. 或许, 该有些突破了吧.</p>
<hr>
<p>后续在自建wiki的同时, 提高blog的更新频率.</p>
<p>wklken</p>
<p>2015-05-05 于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>关于知识管理工具的思考</title>
			<link>https://wklken.me/posts/2015/05/02/about-knowledge-manage-tools.html</link>
			<pubDate>Sat, 02 May 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/05/02/about-knowledge-manage-tools.html</guid>
			<description>毕业快四年了, 测试一年半, 后端开发两年半, 时间如梭. 回顾这些年, 笔记什么的, 是一部漫长的迁移史/血泪史, 如果能早些学习git/markdow</description>
			<content type="html"><![CDATA[<p>毕业快四年了,  测试一年半, 后端开发两年半, 时间如梭.</p>
<p>回顾这些年, 笔记什么的, 是一部漫长的<code>迁移</code>史/血泪史, 如果能早些学习git/markdown等, 能早些了解到一些工具, 应该能省下不少时间吧.</p>
<p>汇总整理下自己所使用过的知识管理工具</p>
<p>工具的目标: 提升效率</p>
<p>而我们的目的很简单</p>
<ol>
<li>方便快速收集/记录/整理/沉淀</li>
<li>快速搜索</li>
</ol>
<p><img src="/imgs/blabla/mywiki.png" alt="mywiki"></p>
<h1 id="手抄笔记">手抄笔记</h1>
<p>大学那段时间</p>
<p>那时候, 学习<code>linux</code>, 学习<code>vim</code>,  学习<code>java/jsp/ssh....</code>, 学习<code>xml</code>等等, 大多数情况下, 都是看书+手抄笔记大法, 那时候完全没有意识去对知识进行组织和整理, 几年下来, 积累了各类笔记, 一大堆, 过年回家还发现当时学习java的六七本笔记, 还有oracle, 还有不知道什么时候做的c语言学习笔记(完全忘了).</p>
<p>手抄, 现在回过头来, 费时费力, 效率极低, 但是就当时环境而言, 不失为一种好的做法(相对于啥都没记来说), <code>好记性不如烂笔头</code>.</p>
<p>而且, 上课/参加培训等等, 都通过笔记来记录</p>
<p>手抄的做法, 太过古老, 如果是非技术书籍, 抑或个人喜欢纸质书籍, 不失为一种好的做法. 但是技术书籍, 手抄太过低效, 不易整理, 更不易搜索, 看似积累了很多, 实际沉淀不多.</p>
<h1 id="word">word</h1>
<p>到毕业做测试一年多, 那时大多数情况下是windows系统, 毕业那会刚刚把系统转到ubuntu, 但是工作环境主要还是windows, 用终端连接主机工作.</p>
<p>正式参加工作, 虽然还经常买纸质书, 但是正儿八经系统啃书的机会相当少, 大部分是工作中碎片化学习的东西, 所以<code>手抄笔记</code>完全从生活中消失了</p>
<p>这时候, 开始有意识记录使用电脑记录, 以及整理汇总一些东西.</p>
<p>这段时间, 系统性整理了linux &amp; shell笔记/vim笔记等, 测试理论, python入门等等.  工作中还是svn, 对git完全没有概念,  更别说markdown了.</p>
<p>这时候笔记以word形式归总在目录下, 通过金山快盘同步, 后来迁到dropbox了</p>
<p>那时候evernote/有道笔记还刚刚火起, 对云端没有太多需求, 没怎么关注</p>
<p>word记录, 相对手抄效率略高, 也方便整理复习, 搜索功能有限.</p>
<h1 id="有道笔记---印象笔记">有道笔记 - 印象笔记</h1>
<p>学习markdown了, 也开始玩github, 这时候云笔记开始火了, 刚好, 开始玩微博, 对各种人各种关注, 天天刷微博, 这个习惯延续至今.</p>
<p>其实用得最多的只有一个功能: 网页剪藏</p>
<p>每个人都会经历这么一个阶段, 碰到各种资源信息, 疯狂收集, 不管有用没用, 先搞下来再说.</p>
<p>然后, 发现有3000+剪藏, 微博上2000+收藏, 后来觉得有道不足够靠谱, 丢过笔记,  本着<code>折腾</code>的信念, 开始迁移笔记, 3000+剪藏只能以尽量高效的方式废弃: <code>当前不涉及不会接触短期内也不会关注的不管好不好以后有没有用全部删掉</code>, 这样, 完成了自己的折腾史.</p>
<p>除了剪藏, 最大的功能就是云同步了, 公司电脑和家里电脑同步. 那时候还在用android机, 移动端记录的需求倒是不怎么强烈</p>
<p>这期间, 明白了<code>资源再多, 无用就是无用</code>, 心态上改变了, 微博停止了点<code>收藏</code>, 那2000+收藏, 也就废弃了. 另外, 也开始进行<code>关注</code>的整理, 获取有效而稳定的信息流.</p>
<p>PS: 云笔记最坑的是同步, 然后你发现丢了&hellip;..还有, 同步, 你发现冲突了, 然后在两份文件里面diff&hellip;&hellip;都经历过, 心塞</p>
<h1 id="印象笔记---ulysess">印象笔记 - Ulysess</h1>
<p>印象笔记, 用了一段时间, 这时候也换了<code>mac</code>和<code>iphone</code>, 以及后来<code>ipad</code>, 开始随时记录.</p>
<p>但是用了一段时间, 发现一个问题: 不支持markdown</p>
<p>后来, 又有一个问题: 代码高亮也不支持</p>
<p>后来的后来, 发现: 这编辑功能也太渣了吧</p>
<p>这时候, 刚好博客都迁移到自己站点, 全markdown静态.</p>
<p>想着技术部分的笔记是否有一个更好的编辑/展现方式.</p>
<p>也正好, 这时候对mac app有着一股狂热, 虽然现在过去了, 但是当时刚刚使用mac, 感受到了好处, 开始折腾<code>各类工具</code>, 正好碰上了<code>ulysess</code></p>
<p>这时候, 感觉技术笔记迁移到上面会更好, 然后逐步迁移.</p>
<p><code>no zuo no die</code>,  展现不错, 支持markdown, 支持高亮, 对于外貌党来说, 很赞的<code>编辑器</code>, 是的, 迁完之后, 才想起来, 这货是个编辑器, 不是<code>笔记</code>.</p>
<p>由于搜索太弱, 不支持多端等等情况, 年前有发生了一次全部笔记给我double了一份, 老天, 几百篇笔记都变成双份了. 我花了一整天时间手工删除重复笔记. (目测可能是icloud的锅)</p>
<p>死心了, 接着迁移</p>
<h1 id="wiznote">WizNote</h1>
<p>为知笔记, 口碑不错, 试用了几天</p>
<p>支持markdown, 目测是云笔记系列为数不多能支持这个的.</p>
<p>然后, 为了<code>弃用</code> ulysess, 花了点时间迁移完(ulysess作为编辑器还是很赞的, 还在用).</p>
<p>用着用着, 发现这markdown, 这剪藏, 似乎没有那么<code>理想</code>, 体验不佳, 用得越多越感觉到</p>
<p>这时候, 搬了一回家, 发现搬书, 完全是一个自虐的活. 而且很多大部头看过后都不在打开, 很多书被我脱水了也没有阅读价值了&hellip;&hellip;开始转向电子书, 多看</p>
<p>这时候, 开始思考&hellip;&hellip;.然后, 迁移</p>
<h1 id="分级的知识管理">分级的知识管理</h1>
<p>后来, 到了现在的情况: 分级, 邮件/evernote/gollum</p>
<h4 id="1-收集">1. 收集</h4>
<p>要求: 方便, 阅读格式ok, 不丢</p>
<p>选择: Evernote国际版, 有条件上个高级版, 别问我为啥不用印象笔记</p>
<p>专门建立一个笔记本: Inbox, 对于碰到的感兴趣的东西, 一键剪藏, 或者分享到evernote, 简单直接有效, 这点是所有云笔记中做的最好的</p>
<p>另外, 支持多看读书笔记同步, 感受到这个世界深深地温暖</p>
<p>PS: 对于微博的处理, 额, 我现在是这么干的: &ldquo;分享到-邮件&rdquo;, 给自己发邮件, 然后定期处理:), 很有效.(使用unibox, 同一个发件人的邮件都在一个对话窗口里面, 很好处理 )</p>
<h4 id="2-整理">2. 整理</h4>
<p>要求: 易用, 高效</p>
<p>选择: evernote, mindnode pro, gollum</p>
<p>主要是一些点, 标注, 做思维导图</p>
<p>对于观点/经验等等东西, 可以阅读梳理后, 放置到evernote</p>
<p>对于技术类/干货类东西, 放evernote</p>
<h4 id="3-沉淀">3. 沉淀</h4>
<p>要求: 高效</p>
<p>选择: evernote, gollum</p>
<p>沉淀是再次提取思考的过程, 还是evernote</p>
<p>不过, 对于技术类东西, 其实对云同步要求不需要那么高, 所以选择了自建wiki, 写完markdown往目录一扔, 就可以在本地web server上看到, 搜索等等, 是对技术/代码沉淀的最佳工具</p>
<h4 id="4-搜索">4. 搜索</h4>
<p>要求: 精准高效</p>
<p>选择: evernote, gollum</p>
<p>用一下就知道了</p>
<hr>
<p>这完全是一部<code>折腾</code>的血泪史, 只有不断经历, 才能进步, 进化, 最终找到适合自己的方式.</p>
<p>没有最好的方法, 只有最合适的方法, 对工具同理, 所以在整个过程中, 还是建议, 明确自己的需求, 要解决的问题等, 多关注一些东西, 多尝试.</p>
<p>好了, 就这些:)</p>
<p>2015-05-02</p>
<p>wklken 于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>Logstash&#43;ElasticSearch&#43;Kibana处理nginx访问日志</title>
			<link>https://wklken.me/posts/2015/04/26/elk-for-nginx-log.html</link>
			<pubDate>Sun, 26 Apr 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/04/26/elk-for-nginx-log.html</guid>
			<description>ELK似乎是当前最为流行的日志收集-存储-分析的全套解决方案. 去年年初, 公司里已经在用, 当时自己还山寨了一个统计系统(postgresql-</description>
			<content type="html"><![CDATA[<p><code>ELK</code>似乎是当前最为流行的日志收集-存储-分析的全套解决方案.</p>
<p>去年年初, 公司里已经在用, 当时自己还<code>山寨</code>了一个统计系统(postgresql-echarts, 日志无结构化, json形式存储到postgresql, 构建统一前端配置生成, 调用统一查询接口, <a href="http://www.wklken.me/posts/2014/11/16/unit-statistics-system.html">具体细节</a>), 已经过了一年有余.</p>
<p>一年刚好, 发生了很多事, 那套系统不知现在如何了.</p>
<p>在新的公司, 一切都得从0到1, 近期开始关注日志/数据上报/统计, 以及后续的数据挖掘等.</p>
<hr>
<p>搭建, 测试并上线了一套简单的系统, 初期将所有服务器的nginx日志, 以及搜索日志进行处理.</p>
<p><img src="/imgs/system/elk.png" alt="elk"></p>
<p>下面主要介绍对nginx日志进行处理的过程, 不是针对<code>elk</code>的介绍, 所有涉及ip的地方都改成<code>127.0.0.1</code>了, 根据自己环境进行修改</p>
<h3 id="1-nginx日志---logstash-shipper---redis">1. nginx日志 -&gt; logstash shipper -&gt; redis</h3>
<p>在<code>centos</code>使用<code>yum</code>安装<code>nginx</code>后, 默认<code>/etc/nginx/nginx.conf</code>中的日志格式定义为:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nginx" data-lang="nginx"><span class="line"><span class="cl"><span class="k">log_format</span>  <span class="s">main</span>  <span class="s">&#39;</span><span class="nv">$remote_addr</span> <span class="s">-</span> <span class="nv">$remote_user</span> <span class="s">[</span><span class="nv">$time_local]</span> <span class="s">&#34;</span><span class="nv">$request&#34;</span> <span class="s">&#39;</span>
</span></span><span class="line"><span class="cl">                  <span class="s">&#39;</span><span class="nv">$status</span> <span class="nv">$body_bytes_sent</span> <span class="s">&#34;</span><span class="nv">$http_referer&#34;</span> <span class="s">&#39;</span>
</span></span><span class="line"><span class="cl">                  <span class="s">&#39;&#34;</span><span class="nv">$http_user_agent&#34;</span> <span class="s">&#34;</span><span class="nv">$http_x_forwarded_for&#34;&#39;</span><span class="p">;</span>
</span></span></code></pre></div><p>然后在具体<code>server</code>配置中使用</p>
<pre tabindex="0"><code>access_log /data/logs/nginx/{PROJECT_NAME}_access.log main;
</code></pre><p>此时, 我们需要做的是, 将<code>access log</code>通过<code>logstash shipper</code>读取, 转<code>json</code>, 发送到<code>redis</code>, 由后续的<code>logstash indexer</code>进行处理</p>
<p>步骤</p>
<p>1.在日志所在机器部署<code>logstash</code></p>
<p>2.在<code>logstash</code>安装目录下的<code>patterns</code>中加入一个文件<code>nginx</code></p>
<p>内容(与上面的<code>log_format</code>相对应)</p>
<pre tabindex="0"><code>NGUSERNAME [a-zA-Z\.\@\-\+_%]+
NGUSER %{NGUSERNAME}
NGINXACCESS %{IPORHOST:clientip} - %{NOTSPACE:remote_user} \[%{HTTPDATE:timestamp}\] \&#34;(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})\&#34; %{NUMBER:response} (?:%{NUMBER:bytes}|-) %{QS:referrer} %{QS:agent} %{NOTSPACE:http_x_forwarded_for}
</code></pre><p>3.增加一个<code>logstash</code>配置文件: <code>logstash-project-access-log.conf</code></p>
<p>注意, input的file, filter的grok, output的redis-key</p>
<pre tabindex="0"><code>    input {
    file {
        path =&gt; [ &#34;/data/logs/nginx/xxxx_access.log&#34; ]
        start_position =&gt; &#34;beginning&#34;
    }
    }

    filter {
    mutate { replace =&gt; { &#34;type&#34; =&gt; &#34;nginx_access&#34; } }
    grok {
        match =&gt; { &#34;message&#34; =&gt; &#34;%{NGINXACCESS}&#34; }
    }
    date {
        match =&gt; [ &#34;timestamp&#34; , &#34;dd/MMM/YYYY:HH:mm:ss Z&#34; ]
    }
    geoip {
        source =&gt; &#34;clientip&#34;
    }
    }


    output {
    redis { host =&gt; &#34;127.0.0.1&#34; data_type =&gt; &#34;list&#34; key =&gt; &#34;logstash:xxxx:access_log&#34; }
    }
</code></pre><p>4.使用<code>supervisor</code>启动<code>shipper</code>.</p>
<pre tabindex="0"><code>    [program:logstash_xxxx_shipper]
    command=/var/shell/logstash/bin/logstash -f /var/shell/logstash/configs/nginx-xxxx-shipper.conf
    numprocs=1
    autostart=true
    autorestart=true
    log_stdout=true
    log_stderr=true
    logfile=/data/logs/logstash/logstash_xxxx_access.log
</code></pre><h3 id="2-redis---logstash-indexer---elasticsearch">2. redis -&gt; logstash indexer -&gt; elasticsearch</h3>
<p>注意, input的redis为上一步redis配置, key要对应, output的elasticsearch配置, <code>index</code>指定了最终es中存储对应的index, 加日期, 方便对日志进行定期删除</p>
<pre tabindex="0"><code>input {
redis {
    host =&gt; &#34;127.0.0.1&#34;
    port =&gt; &#34;6379&#34;
    key =&gt; &#34;logstash:xxxx:access_log&#34;
    data_type =&gt; &#34;list&#34;
    codec  =&gt; &#34;json&#34;
    type =&gt; &#34;logstash-arthas-access&#34;
    tags =&gt; [&#34;arthas&#34;]
}
}

output {
elasticsearch {
    host =&gt; &#34;127.0.0.1&#34;
    index =&gt; &#34;logstash-arthas-access-%{+YYYY.MM.dd}&#34;
}
}
</code></pre><h3 id="3-elasticsearch---kibana">3. elasticsearch -&gt; kibana</h3>
<p>剩下的其实没什么了, 启动<code>kibana</code>后, 配置好指向的<code>es</code>, 就可以在<code>kibana</code>中查看到实时的日志数据</p>
<p>demo环境截图</p>
<p><img src="/imgs/system/kibana-nginx.png" alt="kibana-nginx"></p>
<p><code>kibana</code>中, 支持各种统计, 着实让人惊艳了一把.</p>
<p>除了基本的nginx日志, 还需要在各类url入口, 加入平台, 渠道等信息, 这样通过nginx访问日志, 可以统计到更多的信息</p>
<p>当然, 如果需要一些更为精确/特殊的统计, 需要自行进行数据上报的工作.</p>
<hr>
<h2 id="后续">后续</h2>
<ol>
<li>更多的类型的日志聚合, 包括各类访问日志, 统计上报日志等, 日志落地成文件, 永久留存, 转入es中, 只留存三个月</li>
<li>如何对各类数据进行拆分/汇总</li>
<li>ELK整体部署/运维/扩容等, 包括数据清理</li>
<li>基于ES日志的业务自定义统计后台(kibana无法满足一些具体业务的统计需求)</li>
<li>为什么不使用<code>logstash forwarder</code>, 因为目前日志组成等较为简单, 简单处理 , 后续需要用到时再考虑</li>
</ol>
<hr>
<h1 id="其他">其他</h1>
<h2 id="1-关于logformat和对应grok的配置">1. 关于<code>logformat</code>和对应<code>grok</code>的配置</h2>
<p><code>grok</code>是<code>logstash</code>的一个插件,  <a href="http://logstash.net/docs/1.4.2/filters/grok">文档</a></p>
<blockquote>
<p>Grok is currently the best way in logstash to parse crappy unstructured log data into something structured and queryable</p>
</blockquote>
<p>所以, 我们在处理<code>nginx</code>日志时, 需要根据具体<code>logformat</code>定义对应的<code>grok</code>表达式</p>
<p>除了上面例子中用的那套,  另一份</p>
<p>logformat</p>
<pre tabindex="0"><code>  log_format logstash &#39;$http_host &#39;
                      &#39;$remote_addr [$time_local] &#39;
                      &#39;&#34;$request&#34; $status $body_bytes_sent &#39;
                      &#39;&#34;$http_referer&#34; &#34;$http_user_agent&#34; &#39;
                      &#39;$request_time &#39;
                      &#39;$upstream_response_time&#39;;
</code></pre><p>patterns/nginx</p>
<pre tabindex="0"><code>NGUSERNAME [a-zA-Z\.\@\-\+_%]+
NGUSER %{NGUSERNAME}
NGINXACCESS %{IPORHOST:http_host} %{IPORHOST:clientip} \[%{HTTPDATE:timestamp}\] \&#34;(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})\&#34; %{NUMBER:response} (?:%{NUMBER:bytes}|-) %{QS:referrer} %{QS:agent} %{NUMBER:request_time:float} %{NUMBER:upstream_time:float}
</code></pre><p>如果想自行定义, 可以使用 <a href="https://grokdebug.herokuapp.com/">grokdebug</a>, 将要解析的日志和配置的正则放入, 可以查看最终得到的结构化数据</p>
<h2 id="2-elasticsearch插件">2. elasticsearch插件</h2>
<p>初期只安装了一个 <a href="https://github.com/lmenezes/elasticsearch-kopf">kopf</a>, web界面查看</p>
<h2 id="3-supervisor">3. supervisor</h2>
<p>建议使用<code>supervisor</code>对<code>elk</code>进行管理,(ps. 不要用yum自带的, 版本太旧好多坑, 浪费1小时&hellip;&hellip;使用pip install安装最新版本即可)</p>
<p>配置示例<code>elk.conf</code></p>
<pre tabindex="0"><code>[program:elasticsearch]
command=/var/shell/elk/elasticsearch/bin/elasticsearch
numprocs=1
autostart=true
autorestart=true

[program:kibana]
command=/var/shell/elk/kibana/bin/kibana
numprocs=1
autostart=true
autorestart=true

[program:logstash_arthas]
command=/var/shell/elk/logstash/bin/logstash -f /var/shell/elk/logstash/config/xxxx_access.conf
numprocs=1
autostart=true
autorestart=true
log_stdout=true
log_stderr=true
logfile=/data/logs/elk/logstash/logstash_arthas_access.log
</code></pre><h2 id="4-logstash坑">4. logstash坑</h2>
<pre tabindex="0"><code>start_position =&gt; &#34;beginning&#34;
</code></pre><p>logstash, 会记录一份文件读到的位置, 在$HOME/.sincedb_xxxxx 如果要让logstash重新读取文件, 删除之即可, 重启<code>shipper</code>.</p>
<p>但是你可能发现es中重复记录了, 这是因为, 在<code>output</code>中, 没有定义存储到es时使用的<code>document_id</code>, es全部当成新纪录存入, 导致数据重复</p>
]]></content>
		</item>
		
		<item>
			<title>2014, 在变化与坚持中前进</title>
			<link>https://wklken.me/posts/2015/03/18/summary-10-2014.html</link>
			<pubDate>Wed, 18 Mar 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/03/18/summary-10-2014.html</guid>
			<description>每年都来个总结, 似乎好多年了. 今年的总结拖了三个月, 为什么呢? 不说了, 什么都是理由罢了. 今天在角落里发现了这个, 想想, 还是发出来吧. 开始, 依</description>
			<content type="html"><![CDATA[<p>每年都来个总结, 似乎好多年了.</p>
<p>今年的总结拖了三个月, 为什么呢? 不说了, 什么都是理由罢了.  今天在角落里发现了这个, 想想, 还是发出来吧.</p>
<p>开始, 依旧很杂.</p>
<p><img src="/imgs/life/coding-life.jpeg" alt="coding-life"></p>
<hr>
<h1 id="工作">工作</h1>
<p>今年(2014, 快一年了), 发生了一件事, 然后公司没了, 囧.</p>
<p>总之, 不愉快的过程, 见证了快播的终结.</p>
<p>这或许就是所谓的变化, 以及生活. 安详平和的环境可能瞬间被撕裂. 你永远不知道明天会是什么样的.</p>
<p>年后回来的时候, 还在愉快的码代码, 写一个统计系统, 然后事情就发生了, 然后依旧继续在优化统计系统, 写了一个月, 然后就结束了.</p>
<p>一切太过仓促, 没什么时间思考.</p>
<p>思及自己似乎一年多一直不能说止步不前,  但似乎长进没有预想中的那样大, 所以决定离开. 这个决定主要是从心, 直觉—&ldquo;做事情总是追求意义&rdquo;</p>
<p>善始善终, 逐一交接完毕, 然后离开.</p>
<p>没什么后悔的. 只是曾经的战友, 如今已各在一方.</p>
<p>感谢快播, 一年多日子, 一切, 唯有感恩.</p>
<p>离职后, 念及自己上次换工作的gap之一几天, 这次决定休息一下, 北上行走, 后面说, 需要想清楚一些东西.(<a href="http://www.wklken.me/posts/2014/07/22/summary-09-longjourney-and-three-years.html">千里行纪&amp;工作三周年小结</a>)</p>
<p>回来后, 疯狂地睡了一整天.</p>
<p>然后慢悠悠地宅了一个月(物极必反), 上网, 看书, 充充电, 追美剧&hellip;&hellip;过着一个人的世界.</p>
<p>后来想想, 要找点事干了. 然后开始找工作, 历时一周(准确来说两天), 又是一个决定, 来了现在的公司.(广告时间, <a href="http://www.itianpin.com/">甜品礼物</a>)</p>
<p>我总认为, 迄今也一直坚持的一个观点, 活着, 总要做一些事情, 创造一些价值, 当然, 前置的一个观点是, 你需要做自己喜欢的事情.</p>
<p>目前的工作, 恰好都符合, 我正在做自己喜欢的事情, 也在努力做好, 也认同自己做的工作的价值, 跟随着项目以及公司, 逐步成长.</p>
<p>我能真真实实感受到那种演化成长的过程.</p>
<p>现在已经六个月了, 最大的感受是, 和一群靠谱的人, 在做一件有意义的事情. 虽然过程中, 有碰撞, 冲突, 阵痛, 但是经历了, 才一步步解决和成长</p>
<blockquote>
<p>Maybe, We are making history.</p>
</blockquote>
<p>记得面试的时候被问到<code>What do you want to build? What do you want to change?</code>, 到现在依旧反复自问.</p>
<p>新的环境, 新的伙伴, 开始的新的征程.</p>
<p>我给新伙伴的纸条里总会反复反复出现一句话(有偷懒的嫌疑)</p>
<blockquote>
<p>既然选择了远方, 便只顾风雨兼程</p>
</blockquote>
<p>So, 继续前进.</p>
<p>附, 之前写的一段话:) 对糖厂有兴趣的同学可以找我咨询&hellip;&hellip;</p>
<pre tabindex="0"><code>在甜品工作是一种怎样的体验?

蟹妖&gt;_&lt;

这是一种什么感觉呢? 就是见证一个项目从第一行代码, 逐渐壮大, 完善, 重构, 在自己手中最终进化成一套完美的系统:)。 更重要的是, 代码, 不仅仅是一个个字符, 它有了其存在的意义和价值。 在这里, 可以安安静静一气呵成码一天代码, 也可以找产品 PK 需求, 找小伙伴一起讨论方案, 吐吐槽发发呆跑跑步。 什么是时间管理? 什么是拥抱变化? 什么是团队协作? ......有困难, 有冲突, 有想法, 有方案, 有成长。 既然选择了远方, 便只顾, 风雨兼程。
</code></pre><hr>
<h1 id="learning-by-doing">learning by doing</h1>
<p>2014年, 尝试去学很多东西, 但是最后却发现, 貌似变成了记录/整理很多东西, 烂尾了很多项目&hellip;.&gt;_&lt;</p>
<p>很多沉没成本在里面.</p>
<p>解决方案无他, 做一些东西出来.</p>
<p>开搞一些东西, 尽请期待吧:)</p>
<hr>
<h1 id="冲突与成长">冲突与成长</h1>
<p>这是之前在知乎上看到的一个观点: 没有冲突, 谈何成长.</p>
<p>我个人一个弱点, 就是很容易被说服.</p>
<p>这属于性格上的缺陷, 不够tough(too nice to everyone and everything), 在生活或者工作上, 总是避免冲突.</p>
<p>极力避免冲突, 但现实是冲突是无法避免的, 而我这样做是不对的</p>
<p>这直接导致了一路走来, 似乎成长不多.</p>
<p>一路顺风顺水顺心, 似乎看起来很好, 其实失去的更多不是么?</p>
<p>所谓”稳定”与”变化”的对立, 生活总是在变化的, 而变化势必导致冲突, 如何应对, 处理冲突? 这常常考验一个人的各个方面.</p>
<blockquote>
<p>You can&rsquo;t make everyone happy.</p>
</blockquote>
<p>似乎还是明白晚了, 2014年开始的时候, 有一段时间纠结于一些不该纠结的事情,  作为一个不合格的组长, 带了一段时间的队. 不过不经历, 或许也就不会明白, 不是么.</p>
<p>所以, 结论是: 不要刻意去避免冲突, 把自己一直放在舒适区里, 就很难得到更多成长的机会.(有序, 原则, 底线, 规则)</p>
<p>不那么友好, 但是, 这才是正确的做事风格</p>
<hr>
<h1 id="变化">变化</h1>
<blockquote>
<p>拥抱变化</p>
</blockquote>
<p>这是 Ali 的文化之一, 一直铭记.</p>
<p>时间在快速向前, 一切都在变化.</p>
<p>世界上没有不变的东西, 只有相对不变的东西.</p>
<p>从偏向来说, 做后端开发会采取相对保守的策略.</p>
<p><code>以不变应万变</code>这是目标之一, 但是现实中往往不尽人意, 需求变更神马的都是家常, 很多时候要为一些问题承担后果.</p>
<p>如何快速应对, 去拥抱变化, 这是一个问题.</p>
<p>包括能力上, 以及心境上.</p>
<p>能力上, 更多的是考验对需求的理解以及编码的功底. 如何在设计初期尽可能多考虑, 编码期模块接口等足够灵活等等</p>
<p>心境上, 要接受, 不排斥, 要意识到变化总是会存在的, 去想办法解决, 坦然受之.</p>
<p>但是, <code>变化</code>也是有底线的, 例如上线前临阵改需求等, 需要坚守自己底线. 这不是一般的坑, 是大坑.</p>
<p>归到底, 不惧怕变化, 惧怕的是不可控的变化.</p>
<p>学会试着去拥抱变化.</p>
<hr>
<h1 id="生活与社交--intj-的围城">生活与社交 — INTJ 的围城</h1>
<p>内倾/直觉/思考/判断, 有兴趣可以去看看十六型人格.</p>
<p>INTJ, 对码农这门职业, 似乎有属性加成来的:)</p>
<p>但是, 对于生活社交, 似乎是灾难性的. 想太多, 看太多, 太过依赖直觉, 排斥一切无意义的事情.</p>
<p>INTJ, 在很多事情上, 注定是悲剧?!!</p>
<p>2014, 彻底<code>宅</code>的一年, 疯狂地看书, 刷电影, 刷美剧, 刷代码, 刷微博&hellip;&hellip;.没有目的和方向, 同时战线拉太长导致了很多烂尾的半成品, 还有很多看了半本的书.</p>
<p>2014的一年, 这方面, 糟透了.</p>
<p>反思下来, 似乎自己逃避很多, 彻彻底底的逃避.</p>
<p>生活, 是时候做些改变了.</p>
<p>需要, 把一些该办的事情办了.</p>
<hr>
<h1 id="its-always-about-time">It’s always about time.</h1>
<p>我们所在对抗的, 或者顺应的, 是一种叫做命运的东西</p>
<p>很多问题想不明白, 生活中存在很多悖论.</p>
<p>例如, 要成为什么样的人? 要去做什么? 要怎么做? 活着的意义? 时间的本质? 生存的本质?</p>
<p>时间是把杀猪刀, 今天度过了很多关键性节点, 例如, 毕业三周年, 例如来深两周年.</p>
<p>时间无情地收割一切, 无能为力, 无可奈何.</p>
<p>有时候, 看到一些景, 例如斑驳的阳光, 夕阳, 落叶, 车流, 人流, 总是想起以前某个时刻的感觉, 却想不清, 到底确切是哪个时刻了. 逝去的总是美好的, 而一切都在逝去.</p>
<p>我总是试图, 去寻找一切东西的意义, 却总是求而不得 ,所以迷茫</p>
<p>总感觉明白的太晚, 悟太晚, 太过纠结, 不够洒脱, 性格上还未经磨砺,</p>
<p>总告诉自己, <code>给生活做减法, 那些抛弃的, 就抛弃吧, 不用顾虑太多, 人生太短, 哪来那么多顾虑</code>, 却总做不到. 性格很多缺陷.</p>
<p>一重心障, 破之便是新世界, 呆着? 不可能!</p>
<p>我还没有能力完全掌控自己的生活, 拒绝去做那些自己不喜欢的事情, 生活尚未能完全掌控.</p>
<p>我不知道未来会怎么样</p>
<p>无力, 力争活在当下, 仅此而已.</p>
<blockquote>
<p>当下</p>
</blockquote>
<p>很多时候, 不要去想以后, 等我有空了, 等过年了什么的, 想起来, 就去做, 当天当场就去做, 想起来, 就去实施, 人生, 很容易在这种等等等的过程中消耗掉</p>
<hr>
<h1 id="生活">生活</h1>
<p>码农有生活么?</p>
<p>废话.</p>
<p>2014匆匆, 过得并不如何, 自己过得太随意, 也就没有什么回想了.</p>
<p>好像连续拍了几十天的天空, 然后断了, 连续坚持每周末暴走, 然后也断了, 然后说好的看书, 也没坚持, 然后起了好多side project, 也没坚持, 然后&hellip;&hellip;不提了</p>
<p>2015争取慢慢,  丰富些, 自由些.</p>
<p>年底的时候重读了<code>三体</code>和<code>remote</code>, 两本不搭边的书, 却给了自己很多想法.</p>
<hr>
<h1 id="2014年度盘点">2014年度盘点</h1>
<blockquote>
<p>年度iOS app: windy</p>
</blockquote>
<p>午睡神器, 抗干扰, 唯一缺点, 可能睡过头&hellip;&hellip;</p>
<blockquote>
<p>年度mac app:   things</p>
</blockquote>
<p>重回GTD, 这是继do.im/clear/omini focus/wunderlist等一系列试用后, 剩下的</p>
<blockquote>
<p>年度mac app2: dayone</p>
</blockquote>
<p>很早就入了, 但是2014用得特别多, 我浏览中竟然发现<code>2014-5-2 吃了今年夏天的第一个甜筒</code> 这种丧心病狂的记录&hellip;&hellip;不过也蛮好的,  一年点滴, 尽在其中.</p>
<blockquote>
<p>年度买的最值的东西:   gunnar眼镜</p>
</blockquote>
<p>续航能力提升不少, 缺点是, 要提醒自己起来动一动&hellip;&hellip;</p>
<blockquote>
<p>年度图书: Remote</p>
</blockquote>
<p>重读</p>
<blockquote>
<p>年度电影: 星际穿越</p>
</blockquote>
<p>电影院看了三遍&hellip;..</p>
<blockquote>
<p>年度游戏:  纪念碑谷</p>
</blockquote>
<p>我火星了?&hellip;&hellip;</p>
<blockquote>
<p>年度音乐:  Lui Si Chiama</p>
</blockquote>
<p>下了黄山, 在梅雨纷纷赶往南京路上, 电台推过来的, 循环了一路, 然后回来找不到(听不懂加不记歌名的后果), 找个半个月, 终于电台又给我推了一次, 感谢虾米&hellip;&hellip;</p>
<h2 id="2014年度计划盘点">2014年度计划盘点</h2>
<ol>
<li>学习一门新语言. 学习javascript, golang和ruby,js的话上半年工作需要去学的, 写了一个统计后台组件, golang, 写了一个完整的项目, ruby, 仅学习了下范式, 挖坑新项目中.</li>
<li><a href="https://github.com/wklken/k-vim">k-vim</a>, 完成一个版本. 两个大版本, 目前version8.0, star 1100+, 并衍生的一个服务器版本<a href="https://github.com/wklken/vim-for-server">vim-for-server</a>. <a href="https://github.com/wklken/suggestion">suggestion</a>完成golang版本重构及上线,  <a href="https://github.com/wklken/stackoverflow-py-top-qa">stackoverflow-py-top-qa</a>翻译计划一直停滞, 还剩40个问题. 其他还有几个坑慢慢走, 感觉一直不务正业&hellip;&hellip;</li>
<li>博客, 目标是50篇, 最终完成了40篇, 还好</li>
<li>读书, 目标是50本, 最终完成70的样子, 超额完成任务</li>
<li>旅行, 去一个地方. 去了, 走了十三天, 想明白了一些东西</li>
<li>回趟学校. 回了, 景依旧, 人不在</li>
<li>做几个满意的项目. 今年连续写了几个项目, 一般般, 没有什么特别满意的东西, 开始挖坑新的独立项目.</li>
</ol>
<p>ps. 今年在自己blog和相关项目放了buy me a coffee的donation链接, 收到了不少朋友的donation, 非常感谢:)</p>
<h2 id="2015新的计划">2015新的计划</h2>
<ol start="0">
<li>读完APUE/各种源码等</li>
<li>例行: 学习一门新语言, haskell/lisp(一年一门系列).</li>
<li>重要:把一些该做的事情做了, 不再逃避(******)</li>
<li>例行: keep reading * 30, 只读经典.</li>
<li>例行: 旅行, 似乎该去大西北了</li>
</ol>
<p>2014, 是变化和积累的一年</p>
<p>2015, 我希望是大后期的输出一年:)</p>
<hr>
<p>so. 把该搞定的事情都搞定了</p>
<p>放开, 往前奔跑, 前方如何, 暂时不管了</p>
]]></content>
		</item>
		
		<item>
			<title>Python-基础-时间日期处理小结</title>
			<link>https://wklken.me/posts/2015/03/03/python-base-datetime.html</link>
			<pubDate>Tue, 03 Mar 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/03/03/python-base-datetime.html</guid>
			<description>好久没写文了, 继续清理笔记, 都是一些小结之类的, 源码分析以及在做系统中一些细节难点等, 后续逐步发 另外打个广告, 甜品礼物, 坐标深圳, 有兴趣的同</description>
			<content type="html"><![CDATA[<p>好久没写文了, 继续清理笔记, 都是一些小结之类的, 源码分析以及在做系统中一些细节难点等, 后续逐步发</p>
<p>另外打个广告, <a href="http://www.itianpin.com/join">甜品礼物</a>, 坐标深圳, 有兴趣的同学可以给我发邮件, 非python职位会转到对应同事:)</p>
<pre tabindex="0"><code>      _       _       _   _
     | |     | |     | | (_)
   __| | __ _| |_ ___| |_ _ _ __ ___   ___
  / _` |/ _` | __/ _ \ __| | &#39;_ ` _ \ / _ \
 | (_| | (_| | ||  __/ |_| | | | | | |  __/
  \__,_|\__,_|\__\___|\__|_|_| |_| |_|\___|
</code></pre><hr>
<p>原则, 以<code>datetime</code>为中心, 起点或中转, 转化为目标对象, 涵盖了大多数业务场景中需要的日期转换处理</p>
<p>步骤:</p>
<pre><code>1. 掌握几种对象及其关系
2. 了解每类对象的基本操作方法
3. 通过转化关系转化
</code></pre>
<h2 id="涉及对象">涉及对象</h2>
<h3 id="1-datetime">1. datetime</h3>
<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">datetime</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">now</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">now</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">23</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">946118</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="nb">type</span><span class="p">(</span><span class="n">now</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;</span><span class="nb">type</span> <span class="s1">&#39;datetime.datetime&#39;</span><span class="o">&gt;</span>
</span></span></code></pre></div><h3 id="2-timestamp">2. timestamp</h3>
<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">time</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</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="mf">1421075455.568243</span>
</span></span></code></pre></div><h3 id="3-time-tuple">3. time tuple</h3>
<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">time</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">time</span><span class="o">.</span><span class="n">localtime</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">struct_time</span><span class="p">(</span><span class="n">tm_year</span><span class="o">=</span><span class="mi">2015</span><span class="p">,</span> <span class="n">tm_mon</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">tm_mday</span><span class="o">=</span><span class="mi">12</span><span class="p">,</span> <span class="n">tm_hour</span><span class="o">=</span><span class="mi">23</span><span class="p">,</span> <span class="n">tm_min</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">tm_sec</span><span class="o">=</span><span class="mi">30</span><span class="p">,</span> <span class="n">tm_wday</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">tm_yday</span><span class="o">=</span><span class="mi">12</span><span class="p">,</span> <span class="n">tm_isdst</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="4-string">4. string</h3>
<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">datetime</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">&#34;%Y-%m-</span><span class="si">%d</span><span class="s2"> %H:%M:%S&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;2015-01-12 23:13:08&#39;</span>
</span></span></code></pre></div><h3 id="5-date">5. date</h3>
<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">datetime</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">date</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">12</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="datetime基本操作">datetime基本操作</h2>
<h4 id="1-获取当前datetime">1. 获取当前datetime</h4>
<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">datetime</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">23</span><span class="p">,</span> <span class="mi">26</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">475680</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="2-获取当天date">2. 获取当天date</h4>
<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="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="o">.</span><span class="n">today</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">12</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="3-获取明天前n天">3. 获取明天/前N天</h4>
<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="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="o">.</span><span class="n">today</span><span class="p">()</span> <span class="o">+</span> <span class="n">datetime</span><span class="o">.</span><span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">13</span><span class="p">)</span>
</span></span></code></pre></div><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="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">23</span><span class="p">,</span> <span class="mi">38</span><span class="p">,</span> <span class="mi">55</span><span class="p">,</span> <span class="mi">492226</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span> <span class="o">-</span> <span class="n">datetime</span><span class="o">.</span><span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">23</span><span class="p">,</span> <span class="mi">38</span><span class="p">,</span> <span class="mi">57</span><span class="p">,</span> <span class="mi">59363</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="4-获取当天开始和结束时间000000-235959">4. 获取当天开始和结束时间(00:00:00 23:59:59)</h4>
<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="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">combine</span><span class="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="o">.</span><span class="n">today</span><span class="p">(),</span> <span class="n">datetime</span><span class="o">.</span><span class="n">time</span><span class="o">.</span><span class="n">min</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">combine</span><span class="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="o">.</span><span class="n">today</span><span class="p">(),</span> <span class="n">datetime</span><span class="o">.</span><span class="n">time</span><span class="o">.</span><span class="n">max</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">23</span><span class="p">,</span> <span class="mi">59</span><span class="p">,</span> <span class="mi">59</span><span class="p">,</span> <span class="mi">999999</span><span class="p">)</span>
</span></span></code></pre></div><h4 id="5-获取两个datetime的时间差">5. 获取两个datetime的时间差</h4>
<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="p">(</span><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">13</span><span class="p">,</span><span class="mi">12</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">)</span> <span class="o">-</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">())</span><span class="o">.</span><span class="n">total_seconds</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="mf">44747.768075</span>
</span></span></code></pre></div><h4 id="6-获取本周本月上月最后一天">6. 获取本周/本月/上月最后一天</h4>
<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="n">today</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="o">.</span><span class="n">today</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">today</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">12</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">sunday</span> <span class="o">=</span> <span class="n">today</span> <span class="o">+</span> <span class="n">datetime</span><span class="o">.</span><span class="n">timedelta</span><span class="p">(</span><span class="mi">6</span> <span class="o">-</span> <span class="n">today</span><span class="o">.</span><span class="n">weekday</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">sunday</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">18</span><span class="p">)</span>
</span></span></code></pre></div><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">calendar</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">today</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="o">.</span><span class="n">today</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">_</span><span class="p">,</span> <span class="n">last_day_num</span> <span class="o">=</span> <span class="n">calendar</span><span class="o">.</span><span class="n">monthrange</span><span class="p">(</span><span class="n">today</span><span class="o">.</span><span class="n">year</span><span class="p">,</span> <span class="n">today</span><span class="o">.</span><span class="n">month</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">last_day</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">(</span><span class="n">today</span><span class="o">.</span><span class="n">year</span><span class="p">,</span> <span class="n">today</span><span class="o">.</span><span class="n">month</span><span class="p">,</span> <span class="n">last_day_num</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">last_day</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">31</span><span class="p">)</span>
</span></span></code></pre></div><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">datetime</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">today</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="o">.</span><span class="n">today</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">first</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">(</span><span class="n">day</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">month</span><span class="o">=</span><span class="n">today</span><span class="o">.</span><span class="n">month</span><span class="p">,</span> <span class="n">year</span><span class="o">=</span><span class="n">today</span><span class="o">.</span><span class="n">year</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">lastMonth</span> <span class="o">=</span> <span class="n">first</span> <span class="o">-</span> <span class="n">datetime</span><span class="o">.</span><span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="关系转换">关系转换</h2>
<p>几个关系之间的转化</p>
<p><code>Datetime Object / String / timestamp / time tuple</code></p>
<h2 id="关系转换例子">关系转换例子</h2>
<h4 id="datetime--string">datetime &lt;=&gt; string</h4>
<p>datetime -&gt; string</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">datetime</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">&#34;%Y-%m-</span><span class="si">%d</span><span class="s2"> %H:%M:%S&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;2015-01-12 23:13:08&#39;</span>
</span></span></code></pre></div><p>string -&gt; datetime</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">datetime</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">strptime</span><span class="p">(</span><span class="s2">&#34;2014-12-31 18:20:10&#34;</span><span class="p">,</span> <span class="s2">&#34;%Y-%m-</span><span class="si">%d</span><span class="s2"> %H:%M:%S&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span><span class="mi">2014</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">31</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
</span></span></code></pre></div><hr>
<h4 id="datetime--timetuple">datetime &lt;=&gt; timetuple</h4>
<p>datetime -&gt; timetuple</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">datetime</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">timetuple</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">struct_time</span><span class="p">(</span><span class="n">tm_year</span><span class="o">=</span><span class="mi">2015</span><span class="p">,</span> <span class="n">tm_mon</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">tm_mday</span><span class="o">=</span><span class="mi">12</span><span class="p">,</span> <span class="n">tm_hour</span><span class="o">=</span><span class="mi">23</span><span class="p">,</span> <span class="n">tm_min</span><span class="o">=</span><span class="mi">17</span><span class="p">,</span> <span class="n">tm_sec</span><span class="o">=</span><span class="mi">59</span><span class="p">,</span> <span class="n">tm_wday</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">tm_yday</span><span class="o">=</span><span class="mi">12</span><span class="p">,</span> <span class="n">tm_isdst</span><span class="o">=-</span><span class="mi">1</span><span class="p">)</span>
</span></span></code></pre></div><p>timetuple -&gt; datetime</p>
<pre tabindex="0"><code>timetuple =&gt; timestamp =&gt; datetime [看后面datetime&lt;=&gt;timestamp]
</code></pre><hr>
<h4 id="datetime--date">datetime &lt;=&gt; date</h4>
<p>datetime -&gt; date</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">datetime</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">date</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">12</span><span class="p">)</span>
</span></span></code></pre></div><p>date -&gt; datetime</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="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="o">.</span><span class="n">today</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">12</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">today</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">date</span><span class="o">.</span><span class="n">today</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">combine</span><span class="p">(</span><span class="n">today</span><span class="p">,</span> <span class="n">datetime</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">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">combine</span><span class="p">(</span><span class="n">today</span><span class="p">,</span> <span class="n">datetime</span><span class="o">.</span><span class="n">time</span><span class="o">.</span><span class="n">min</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</span></span></code></pre></div><hr>
<h4 id="datetime--timestamp">datetime &lt;=&gt; timestamp</h4>
<p>datetime -&gt; timestamp</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="n">now</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">timestamp</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">mktime</span><span class="p">(</span><span class="n">now</span><span class="o">.</span><span class="n">timetuple</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;&gt;</span> <span class="n">timestamp</span>
</span></span><span class="line"><span class="cl"><span class="mf">1421077403.0</span>
</span></span></code></pre></div><p>timestamp -&gt; datetime</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="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">fromtimestamp</span><span class="p">(</span><span class="mf">1421077403.0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="p">(</span><span class="mi">2015</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">23</span><span class="p">,</span> <span class="mi">43</span><span class="p">,</span> <span class="mi">23</span><span class="p">)</span>
</span></span></code></pre></div>]]></content>
		</item>
		
		<item>
			<title>Linux及Bash笔记</title>
			<link>https://wklken.me/posts/2015/01/17/linux-notes.html</link>
			<pubDate>Sat, 17 Jan 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/01/17/linux-notes.html</guid>
			<description>第一次接触, 在大学时期, 但是真正去学习和了解, 大概是在刚毕业那年 到现在, 三年多, 主后端开发, 工具也就是一个command line加vim, 每</description>
			<content type="html"><![CDATA[<p>第一次接触, 在大学时期, 但是真正去学习和了解, 大概是在刚毕业那年</p>
<p>到现在, 三年多, 主后端开发, 工具也就是一个command line加vim, 每天开个终端就开始噼里啪啦干活, 兼职一些简单的部署运维工作</p>
<p>去年, 是变化和积累的一年, 今年, 希望有些产出</p>
<p>Linux和bash, 记了很多笔记, 逐一梳理出来</p>
<p>之前的两篇文章 <a href="http://www.wklken.me/posts/2013/07/04/note-of-linux-shell-scripting-cookbook.html">LINUX SHELL脚本攻略笔记</a> 以及 <a href="http://www.wklken.me/posts/2014/01/12/shell-script-base.html">如何书写SHELL脚本</a></p>
<p>也会一并汇总, 这可能是今年唯一产出的一本笔记了, python源码阅读和python基础笔记, 这两个在本地的gitbook上, 工程量太浩大, 后面以博文的形式慢慢发吧:)</p>
<p>好了, 放到<code>github</code>上了, 当前10%的样子, 欢迎 <code>star</code>以及提<code>pr</code>, 纠正以及丰富之</p>
<p>地址: <a href="https://github.com/wklken/linux-notes">https://github.com/wklken/linux-notes</a></p>
<p>先这样&hellip;&hellip;后面开始专注码业余项目去了, 博文产出估计会少点, 当然也不会太少</p>
<p>2015-01-17</p>
<p>wklken</p>
]]></content>
		</item>
		
		<item>
			<title>一些nginx配置</title>
			<link>https://wklken.me/posts/2015/01/01/some-nginx-configs.html</link>
			<pubDate>Thu, 01 Jan 2015 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2015/01/01/some-nginx-configs.html</guid>
			<description>nginx配置 开年第一篇, 梳理笔记本中&amp;hellip;. 没啥, 一些用到以及后面可能用到的nginx配置, 记录一下, 备查备用 ps. 之前一篇 ngin</description>
			<content type="html"><![CDATA[<p>nginx配置</p>
<p>开年第一篇, 梳理笔记本中&hellip;.</p>
<p>没啥, 一些用到以及后面可能用到的<code>nginx</code>配置, 记录一下, 备查备用</p>
<p>ps. 之前一篇 <a href="http://www.wklken.me/posts/2013/11/23/nginx-base.html">nginx基础笔记</a></p>
<h2 id="使用独立目录-然后include具体配置">使用独立目录, 然后include具体配置</h2>
<p>目录</p>
<pre tabindex="0"><code>nginx.conf
site/
    a.conf
    b.conf
</code></pre><p>nginx.conf</p>
<pre tabindex="0"><code>http {

    .......
    include /etc/nginx/conf.d/*.conf;
    include sites/*.conf;
}
</code></pre><h3 id="gzip-on">gzip on</h3>
<p>加到<code>http</code>模块中, 开启<code>gzip</code>, 注意<code>gzip_types</code>配置得是压缩的资源类型</p>
<p>nginx.conf</p>
<pre tabindex="0"><code>http {


    .....


    gzip on;
    gzip_min_length 1k;
    gzip_comp_level 5;
    gzip_proxied expired no-cache no-store private auth;
    gzip_types text/plain text/css application/javascript text/javascript application/x-javascript text/xml application/xml application/xml+rss application/json image/x-icon image/png image/jpg image/jpeg application/font-woff;
    gzip_vary on;
}
</code></pre><h2 id="for-multi-processers">for multi processers</h2>
<p>nginx.conf</p>
<pre tabindex="0"><code>worker_processes  4;
events {
    worker_connections  2048;
    use epoll;
    multi_accept on;
}

worker_rlimit_nofile 100000;
</code></pre><h2 id="static-file-cache">static file cache</h2>
<pre tabindex="0"><code>    location ~* \.(?:css|js)$ {
      expires 12h;
      access_log off;
      add_header Cache-Control &#34;public&#34;;
      proxy_pass http://127.0.0.1:5000;
      proxy_redirect off;
    }
</code></pre><h2 id="proxy-pass">proxy pass</h2>
<pre tabindex="0"><code>    location /
    {
        proxy_pass http://127.0.0.1:8000;
        proxy_pass_header Server;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_set_header Host $http_host;
        proxy_redirect off;
    }
</code></pre><p>可以设置超时时间</p>
<pre tabindex="0"><code>        proxy_connect_timeout 500s;
        proxy_read_timeout 500s;
        proxy_send_timeout 500s;
</code></pre><h2 id="静态目录-or-文件">静态目录 or 文件</h2>
<pre tabindex="0"><code>    location /movies/ {
        alias /Volumes/Media/Movies/;
        allow all;
    }

    location = /abc.txt {
        alias /data/www/static/abc.txt;
        expires  30d;
        access_log off;
    }
</code></pre><h2 id="静态站">静态站</h2>
<pre tabindex="0"><code>server {
    listen       192.168.1.1:80;
    server_name  www.abc.com;

    client_max_body_size 1M;
    access_log logs/blog_access.log;
    error_log logs/blog_error.log;

    root /data/static_site_dir;
    index index.html;

}
</code></pre><h2 id="服务转发">服务转发</h2>
<p>将收到的服务url/参数等, 原封不动转给另一个服务</p>
<pre tabindex="0"><code>server {
    listen 80;
    server_name  www.xxxx.com;
    location ~ ^/(.*)$ {
        proxy_pass http://192.168.1.1:80/another/service/$1$is_args$args;
        proxy_read_timeout 90;
    }
}
</code></pre><h2 id="return">return</h2>
<p>直接<code>return</code></p>
<p>语法</p>
<pre tabindex="0"><code>return http_code;
return http_code &#34;content&#34;;
</code></pre><p>e.g.</p>
<pre tabindex="0"><code>location /api/test/ {
    return 403;
}

location /stat/ {
    return 204;
}

location /ping/ {
    return 200;
}
</code></pre><h2 id="for-mobile">for mobile</h2>
<p>移动端和网站端互相跳转</p>
<pre tabindex="0"><code>    location = / {
        try_files $uri @mobile_rewrite;
    }

    location ~ ^/(login|register|search|album|404|album/\d+|item/\d+|topic)$ {
        try_files $uri @mobile_rewrite;
    }


    location @mobile_rewrite {

        if ($http_user_agent ~* &#34;(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino&#34;) {
            set $mobile_rewrite perform;
        }
        if ($http_user_agent ~* &#34;^(1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-)&#34;) {
            set $mobile_rewrite perform;
        }

        if ($arg_mobile = &#39;no&#39;) {
            set $mobile_rewrite do_not_perform;
        }

        if ($arg_mobile = &#39;yes&#39;) {
            set $mobile_rewrite perform;
        }

        if ($mobile_rewrite = perform) {
            rewrite ^ http://$server_name/m$request_uri permanent;
            break;
        }

        proxy_pass http://127.0.0.1:5000;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_redirect off;

    }


    location /m/
    {

        set $pc_rewrite 1;
        if ($http_user_agent ~* &#34;(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino&#34;) {
            set $pc_rewrite 0;
        }
        if ($http_user_agent ~* &#34;^(1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-)&#34;) {
            set $pc_rewrite 0;
        }
        if ($pc_rewrite = 1) {
            rewrite ^/m/(.*)$ http://$server_name/$1 permanent;
        }

        proxy_pass http://127.0.0.1:5000;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_redirect off;
    }
</code></pre><h2 id="redirect-to-www">redirect to www</h2>
<pre tabindex="0"><code>server {
    server_name  abc.com;
    rewrite ^(.*) http://www.abc.com$1 permanent;
}
</code></pre><h2 id="allow-and-deny">allow and deny</h2>
<p>访问ip控制</p>
<pre tabindex="0"><code>
location /test/ {
    allow 192.168.1.1;
    deny all;

}
</code></pre><h2 id="负载均衡">负载均衡</h2>
<p>nginx.conf</p>
<pre tabindex="0"><code>http {

    upstream A {
        server 192.168.1.1:5000;
        server 192.168.1.2:5000;
    }
}
</code></pre><p>sites/a.conf</p>
<pre tabindex="0"><code>server {

    location / {
        proxy_pass A;
    }

}
</code></pre><hr>
<p>其他</p>
<h2 id="centos-service-cmds">centos service cmds</h2>
<pre tabindex="0"><code>检查配置文件正确性
service nginx configtest


重新加载配置
service nginx reload
</code></pre>]]></content>
		</item>
		
		<item>
			<title>重读&lt;&lt;你的灯亮着吗? &gt;&gt;</title>
			<link>https://wklken.me/posts/2014/12/06/are-your-lights-on.html</link>
			<pubDate>Sat, 06 Dec 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/12/06/are-your-lights-on.html</guid>
			<description>书今年到是读了不少, 50本的目标目测已然超标了:), 发现今年的博客目标还差11篇(50篇), 目测完不成指标了, 随性写写吧, 能写几篇是几篇 这本</description>
			<content type="html"><![CDATA[<p>书今年到是读了不少, 50本的目标目测已然超标了:), 发现今年的博客目标还差11篇(50篇), 目测完不成指标了, 随性写写吧, 能写几篇是几篇</p>
<p><img src="/imgs/books/are-your-lights-on.jpg" alt="are-your-lights-on"></p>
<hr>
<p>这本书是原先团队老大推荐的, 看过一遍, 后来自己入了一本, 今天重读(发现我越来越喜欢这类很薄的树)</p>
<p>做事情本质上就是遇到问题，分析问题，解决问题的过程。而对如何解决问题本身，这本书做了一些讨论
           </p>
<p>三点：</p>
<pre><code>1. 问题是什么（期望和体验）
2. 谁的问题（不同人会有不同的解决方式，责任）
3. 问题来源
</code></pre>
<p>得到了一些原则性的结论，每一点仔细思考分析.</p>
<p>记得当时总结过一句话: 当”问题”成为问题时, 才是问题.</p>
<p>值得反复阅读, 以下更多的是做一些摘录</p>
<h2 id="第一部分-问题是什么">第一部分: 问题是什么?</h2>
<blockquote>
<p>这是什么类型的问题? 谁碰到了问题? 问题是什么? 或者说, 此时此刻, 问题的本质是什么?</p>
</blockquote>
<p>谁碰到了问题? 问题的本质是什么?</p>
<p>谁的问题? 如果没有负责人, 那将永远得不到解决. 具体到了细节, 责任人负责并弄清楚. 分配到人</p>
<blockquote>
<p>问题就是理想状态和现实状态之间的差别</p>
</blockquote>
<p>调整理想状态或者改变现实状态.</p>
<p>期望和结果, 你能做的, 降低期望, 或者去改变结果.</p>
<p>“忽略问题”是一种古老但有效的方法(降低敏感度, 降低期望)</p>
<blockquote>
<p>我们永远不知道问题是什么, 直到我们彻底解决了拜托了这些问题</p>
</blockquote>
<blockquote>
<p>别去费力榜缺乏幽默感的人解决问题</p>
</blockquote>
<h2 id="第二部分-这次的问题是什么">第二部分: 这次的问题是什么?</h2>
<blockquote>
<p>不要把别人的解决方法作为定义问题的方法</p>
</blockquote>
<p>你的最终目的是什么? 你的问题本质是什么. focus在问题本身, 而不是问题的解决方法上, 错误地将解决方法作为你的问题.</p>
<blockquote>
<p>别把问题的解决方案误当做问题的定义, 当这个解决方案是由你提出的时候尤其如此</p>
</blockquote>
<blockquote>
<p>如果你解决问题太过神速, 别人根本不会相信你真的解决了问题.</p>
</blockquote>
<p>头疼&hellip;..</p>
<blockquote>
<p>面对有利可图的问题时, 道德考量很可能就烟消云散了</p>
</blockquote>
<blockquote>
<p>即使问题已经解决, 你也无法确定你的问题定义是正确的.</p>
</blockquote>
<blockquote>
<p>你永远无法确定已经找到的问题定义是正确的, 但是永远不要停下寻找正确定义的脚步</p>
</blockquote>
<blockquote>
<p>不要仓促下结论, 但也不要忽视第一印象</p>
</blockquote>
<h2 id="第三部分-问题到底是什么">第三部分: 问题到底是什么?</h2>
<blockquote>
<p>每一个解决方案都是下一个问题的来源</p>
</blockquote>
<p>修复一个bug可能引起另外一个bug</p>
<blockquote>
<p>某些问题最困难的部分就在于发现问题的存在</p>
</blockquote>
<p>你已经麻木了, 无法站在新的视角对事情进行评估, 确定是否有问题存在</p>
<p>问题和习惯，一旦我们习惯了某些东西，就会对某些问题视而不见，改变习惯是非常困难的事情，每做一件事情的时候，都有必要问下自己，有必要真么做么？
             
事不过三，每次改善一点点，都能节约出很多时间和精力，放在重要的事情</p>
<blockquote>
<p>看看你对问题的理解, 如果想不出至少三个可能有出错的地方, 你就没有真正理解这个问题</p>
</blockquote>
<blockquote>
<p>问题的关键就是在于首先要意识到问题的存在, 或者让设计者意识到有问题存在</p>
</blockquote>
<p>吃自己的狗粮</p>
<blockquote>
<p>大多数不协调之处一经发现很容易解决</p>
</blockquote>
<blockquote>
<p>借助外国人/盲人/儿童来检验你给出的定义, 或者让自己设身处地地站到外国人/盲人/儿童的角度来检验&hellip;&hellip;.每转换一次视角, 都会发现新的不协调之处</p>
</blockquote>
<p>切换角度看问题, 发现不协调</p>
<blockquote>
<p>如果想得到不同的解决方案, 该怎样变换问题的表述方式?&hellip;&hellip;一旦你将一个问题描述拟成了文字, 做些文字游戏, 以确保每个人对问题的理解可以统一.</p>
</blockquote>
<p>首先, 语义层面, 问问题的方式, 都可能带有偏向或误导, 如何客观地阐述问题?</p>
<blockquote>
<p>当你沿着定义问题的道路疲倦前行时, 过一会就要回头看看, 确定字节没有走错路</p>
</blockquote>
<h2 id="第四部分-问题该由谁解决">第四部分: 问题该由谁解决?</h2>
<blockquote>
<p>当别人可以妥善解决自己的问题是, 不要越俎代庖</p>
</blockquote>
<p>特别是leader&hellip;.</p>
<blockquote>
<p>如果这是别人的问题, 就把它当成是别人的问题</p>
</blockquote>
<blockquote>
<p>如果一个人处于解决问题的位置, 却并不受问题困扰, 那就采取一些行动使他能亲身体验到问题.</p>
</blockquote>
<blockquote>
<p>你的灯亮着么?</p>
</blockquote>
<p>如何让别人意识到问题?</p>
<h2 id="第五部分-问题来自哪里">第五部分: 问题来自哪里?</h2>
<blockquote>
<p>人们觉得无力解决问题, 常常把问题归因为天性, 以逃避解决问题的责任.</p>
</blockquote>
<blockquote>
<p>大多数情况下, 问题的根源在你自己身上.</p>
</blockquote>
<p>记住这一点, 我们往往不承认这一点, 但这是无可辩驳的事实.</p>
<blockquote>
<p>世界上有两种人, 一种人做事, 另一种人制造出事来让其他人做. 远离那些找事让别人做的人, 你就能好好过日子了.</p>
</blockquote>
<blockquote>
<p>世界上有两种人, 一种人做事, 另一种人领赏, 做第一种人吧, 那里的争斗比较少.</p>
</blockquote>
<h2 id="第六部分-你真的想解决问题么">第六部分: 你真的想解决问题么?</h2>
<blockquote>
<p>大多数情况下, 只要知道问题是什么, 解决问题就是一件非常不值一提的事情.</p>
</blockquote>
<blockquote>
<p>无论表面上如何, 在你提供他们所要求的东西之前, 他们极少知道自己想要什么.</p>
</blockquote>
<blockquote>
<p>从最后的情况看, 想要真正解决问题的人并不是很多.</p>
</blockquote>
<blockquote>
<p>人们永远没有足够的时间把它做好, 但永远有足够的时间重新来过.</p>
</blockquote>
<blockquote>
<p>人们永远没有足够的时间去考虑到底是不是想要它, 但永远有足够的时间去为之后悔</p>
</blockquote>
<blockquote>
<p>鱼, 总是最后一个看到水的.</p>
</blockquote>
<p>当局者迷.</p>
<hr>
]]></content>
		</item>
		
		<item>
			<title>重读&lt;&lt;番茄时间工作法&gt;&gt;</title>
			<link>https://wklken.me/posts/2014/11/30/pomodoro-technique-illustrated.html</link>
			<pubDate>Sun, 30 Nov 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/11/30/pomodoro-technique-illustrated.html</guid>
			<description>最近在执行重读计划, 为年后搬家做准备, 一些书读完珍藏/出给需要的人, 减少搬家那一柜子书的困扰&amp;hellip;. 重新读了下这本书, 写写 大概去年</description>
			<content type="html"><![CDATA[<p>最近在执行重读计划, 为年后搬家做准备, 一些书读完珍藏/出给需要的人, 减少搬家那一柜子书的困扰&hellip;.</p>
<p>重新读了下这本书, 写写</p>
<p>大概去年的这个时候, 写了篇 <a href="http://www.wklken.me/posts/2013/11/17/the-art-of-procrastination.html">拖拉一点也无妨的读书笔记</a>, 需要的可以看下</p>
<p><img src="/imgs/books/pomodoro.jpg" alt="pomodoro"></p>
<hr>
<blockquote>
<p>番茄时间工作法, 意在让人们驻足/观察/醒悟, 并在此过程中改进自我</p>
</blockquote>
<p>人们往往容易关注未来, 而不是过去, 匆匆忙忙, 只为了尽快达到目标, 却忽视了对整个过程的review, 停留, 观察, 总结, 改进, 然后再前进, 更快更好地前进.</p>
<p>编程中有一种学习方式叫做<code>刻意练习</code>, 而在&laquo;一万小时天才理论&gt;&gt;中提到<code>精深练习</code>, 要<code>慢</code>, 要去<code>犯错</code>, 要去实际<code>体悟</code>, 要<code>总结</code>.</p>
<p>GTD 里也有review的环节.</p>
<p>总之, review很重要, 但常常被忽略(例如说我…)</p>
<blockquote>
<p>要完成大量工作, 重点不在完成工作上, 而在于能否集中注意力</p>
</blockquote>
<p>很赞同, 打断是效率杀手.</p>
<p>如果一天打断太多, 加上会太多, 很可能意味着晚上要花时间补回这段时间. That is not cool!</p>
<ul>
<li>要想做到专注, 你就得坚决抛开各种杂念</li>
</ul>
<blockquote>
<p>什么是番茄工作法? 简单来说, 就是列出你当天要做的事, 设置25分钟闹钟, 然后从第一件事开始.</p>
</blockquote>
<p>很简单的做法, 但是实践两年多, 感觉行之有效. 而自己的番茄钟, 也根据任务性质长短不一, 从10分钟到1小时不等, 特别是半夜码代码写东西根本停不下来.</p>
<p>而对于我这种比较懒散的人来说, 最简单的方式才是最好的方式, 例如每个番茄钟的休息时间, 只要自己感觉精力ok, 可以进入下一步, 就开始. 如果感觉一定要继续下去, 那么继续. 灵活运用, 不要僵化了(但是最开始的时候应该对自己严格些).</p>
<p>同样, 你没有必要买一个番茄钟(我就买了一个…现在放厨房里落灰, 原因是滴答滴答的声音太干扰了), 手机/网页/软件, 你可以发现各类好用的东西, 主旨: 能在适时通知你, 不干扰你的工作和思路, 能记录. (我用的是<code>Vitamin-R 2</code>)</p>
<p>每隔一段时间, 回顾下所有步骤, 看看有没有需要简化或干脆去掉的步骤.</p>
<p>我是<code>伪GTD</code>爱好者, 从狂热到降温, 使用一个个工具, 最终确定了<code>things</code>这个工具(足够轻). 而GTD步骤, 也被简化到了适合我工作场景的情况.</p>
<p>不要僵化于步骤, 不要狂热于工具, 关注自己, 关注做事, 工具神马的, 够轻够用就好, 适合自己的才是最好的.</p>
<blockquote>
<p>一次只做一件事</p>
</blockquote>
<p>人是并行动物, 但不意味着你可以同时干几件事.</p>
<p>同时并行干几件事, 代价太大, 先不说最坏的情况是都干砸了, 最好的都干成了, 但是你也把自己搞得筋疲力尽.</p>
<p>不断的中断, 上下文切换, 最终导致了<code>cpu</code>过载&hellip;&hellip;</p>
<p>一次只做一件事, 集中精力, 高效搞定, 然后下一件.</p>
<p>或者, 你可以将<code>想法</code>挂起后台, 到了你真正去做的时候, <code>想法</code>已经进化并成熟了很多.(亲测有效)</p>
<p>so, 不要在<code>一边....一边....</code>, 或许看起来很忙很努力, 就像&laquo;Rewrok&gt;&gt;中提到的<code>工作狂</code>, 这是很愚蠢的事情.(能多线doing的天才除外啊)</p>
<ul>
<li>为什么要用番茄工作法</li>
</ul>
<ol>
<li>面对复杂, 望而却步</li>
<li>无聊琐事, 越拖越久</li>
<li>小事忙活一天, 大事一件没办</li>
<li>最后期限, 步步紧逼</li>
<li>从休息回到工作, 心智调整不过来</li>
<li>一错再错, 不长记性</li>
<li>没想到一件事要做这么久</li>
<li>没想到一件事越做越复杂</li>
<li>头脑被各种想法占据</li>
<li>时间都用来学习适应复杂的工作方法了</li>
<li>只顾低头干活, 忘了抬头看路</li>
<li>把预估当做承诺</li>
<li>牵着不走, 打着倒退</li>
<li>完美主义, 碍手碍脚</li>
<li>前怕狼后怕虎, 害怕失败和批评</li>
</ol>
<blockquote>
<p>通过执行一套相同的动作和准备程序, 可以使大脑自我调整, 进入执行某类事务的最佳状态</p>
</blockquote>
<p>有点<code>迷信</code>的感觉. 但是, 其实目的仅仅是像书里说的, 通知大脑, 我要开始做什么了, 准备好.</p>
<p>另一个我觉得, 是为了防止中断, 例如开工前去倒好水, 调整好电脑, IM和邮件通知关掉等等,</p>
<blockquote>
<p>短暂的定期休息, 能够促进融会贯通的能力.</p>
</blockquote>
<p>It works.</p>
<p>每隔25分钟, 常规休息, 4个番茄钟后进行15~30分钟阶段休息</p>
<blockquote>
<p>“心流”</p>
</blockquote>
<p>一种精神状态, 创造性的状态.</p>
<p>明确的目标, 集中, 专注, 自我意识消失, 时间感扭曲, 直接和即时反馈, 能力水平与面对挑战的平衡, 个人控制感, 工作本身的内在奖励, 行为与认知的合一.</p>
<p>在那种状态下, 效率奇高, 你感觉自己无所不能, 集中专注, 自我意识消失(你不会意识到自己在这种状态下)</p>
<p>虽然书中提到<code>节奏</code>一说, 要定时从<code>心流</code>中跳出规划全局再进入, 但我更倾向于多进入这种状态, 尽可能长地维持, 因为要进入<code>心流</code>状态, 是很困难的. 如何快速进入也是需要逐渐训练的.</p>
<blockquote>
<p>在短时间内对工作方法做反复调整</p>
</blockquote>
<p>小步快跑, 调整得到<code>正确的姿势</code>—论如何优雅地工作</p>
<blockquote>
<p>番茄时间工作法的阶段</p>
</blockquote>
<ul>
<li>计划: 从inbox里, 得到一张<code>今日待办</code>的清单(当天的自我承诺)</li>
<li>跟踪: 开始番茄钟后, 收集一些过程指标, 比如中断次数</li>
<li>记录: 一天结束时, 将跟踪数据记录归档</li>
<li>处理: 对跟踪数据进行分析思考, 得到一些特征和规律</li>
<li>可视化: 将信息组织起来, 找出改进流程的思路</li>
</ul>
<p>以上是每天要进行的, 个人感觉太重了, 而且重心在改进, 而不再<code>do</code>.</p>
<p>前期还没有形成自己节奏的时候, 适用, 中后期, 应该将重心放在<code>do</code>上, 轻化<code>计划</code>和<code>跟踪</code>.</p>
<blockquote>
<p>处理外部中断</p>
</blockquote>
<p>如果是<code>别人的问题</code>, 进行分派.</p>
<p>如果是<code>我的问题</code></p>
<ol>
<li>邮件/IM过来的, 可视为不紧急, 完成番茄钟后处理</li>
<li>电话, 一两分钟能done的, 做掉. 不能, 确认紧急程度, 不紧急, 给承诺, add to TODO list, back to work. 紧急的, 到4</li>
<li>当面, 同2, 确认对方最晚可接受的时间, 如果不紧急, 承诺, 结束对话</li>
<li>紧急, 给当前工作打断点, 注释或纸笔, 写下当前状态思路, 然后去做紧急的事情.</li>
</ol>
<p>书中提到的处理策略: <code>告知(dong something)-协商(when)-计划(todo list)-答复(done)</code></p>
<p>怎么区分<code>紧急</code>, 需要慢慢去学习.</p>
<blockquote>
<p>邮件的处理</p>
</blockquote>
<p>如果答复时间不超过1分钟, 立即答复</p>
<p>如果需要分派, 分派</p>
<p>如果可以委托他人, 委托</p>
<p>如果需要我处理, 不紧急, 回复处理时间, 记todo list</p>
<p>如果需要我处理, 紧急?(紧急的事情会用邮件? 一般是<code>伪紧急</code>)</p>
<blockquote>
<p>过度学习</p>
</blockquote>
<p>达到熟练程度后, 继续学习或者练习的行为.</p>
<blockquote>
<p>持续改善</p>
</blockquote>
<p>一种工作方法, 以改进为目标, 对渐进变化的专注.</p>
<blockquote>
<p>事前预估与时间所花工夫之间经常不一致</p>
</blockquote>
<ol>
<li>做预估的方法或能力有待改进</li>
<li>工作开始后, 又出现新情况, 新问题, 使得环境发生改变.</li>
</ol>
<hr>
<p>最后</p>
<p>很多时候, 我们的工作倾向于<code>无序</code>的, 如何将有限的时间分配到无限的工作中, 需要一些技巧, 并且辅助以工具, 不要神化<code>技巧</code>, 也不要狂热于<code>工具</code>, 最重要的是人本身, 是完成事情本身, 学习一些方法(番茄/GTD), 应用, 思考, 改进, 这就够了:)</p>
]]></content>
		</item>
		
		<item>
			<title>关于代码调试de那些事</title>
			<link>https://wklken.me/posts/2014/11/23/how-to-debug.html</link>
			<pubDate>Sun, 23 Nov 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/11/23/how-to-debug.html</guid>
			<description>写代码最完美的就是, 想清楚, 码, 运行, perfect, DONE, 下班. 当然, 那是完美的状态. 大多数时候只存在于理想中. 现实是, 我们会被各种坑, 被环境坑, 被语言坑,</description>
			<content type="html"><![CDATA[<p>写代码最完美的就是, 想清楚, 码, 运行, perfect, DONE, 下班.</p>
<p>当然, 那是完美的状态. 大多数时候只存在于理想中.</p>
<p>现实是, 我们会被各种坑, 被环境坑, 被语言坑, 被依赖坑, 被第三方库坑, 被编辑器坑, 被自己坑(三个月前的自己/昨天的自己/几分钟前的自己), 被数据库坑, 被缓存坑, 被队友坑(这个比较惨), 被需求变更坑(这个也是)&hellip;&hellip;</p>
<p>所以, 总是避免不了代码调试.</p>
<p>今天顺带过了下&laquo;想计算机科学家一样思考Python&gt;&gt;, 里面每一章最后都有关于调试的一些观点, 例如<code>阅读 - 深思 - 修改/运行/回退</code>, 所以决定来写写关于代码调试的一些东西.</p>
<p>其实, 代码调试是<code>论如何排查问题</code>的一个过程, 根据一切蛛丝马迹, 推断出问题所在, 并消灭之.(破案的即视感)</p>
<p><img src="/imgs/blabla/debug.png" alt="sherlock"></p>
<p>下面是一些关于一些自己在写代码和调试的总结</p>
<hr>
<h3 id="1你得明白你在做什么-保持清醒">1.你得明白你在做什么, 保持清醒</h3>
<p>代码调试有时候会让你陷入无尽的自我怀疑/迷茫/愤怒/沮丧/窘迫/挫败(无限负能量), 很容易被这些情绪左右, 不清醒, 陷入怀疑自我(一定是我调用的方式不对), 或者怀疑一切(一定是数据库问题, 不对, 缓存问题, 不对, 接口问题, 好像不对, 数据问题), 或者胡乱改代码(改-跑-错了-再改-跑-又错-再改, 传说中的随机行走编程), 或者&hellip;&hellip;(挣扎吧&hellip;&hellip;)</p>
<p>此刻, 保持清醒的自我是非常重要的, 要明确: <code>我在做什么</code>, <code>问题是什么症状</code>, <code>原来逻辑是什么</code>, <code>最有可能出问题的是哪里</code>?</p>
<h3 id="2想清楚了再写代码">2.想清楚了再写代码</h3>
<p>如果连需求是什么, 想要做什么都没整明白, 就吭哧吭哧开写, 意图在实践中摸索通向胜利的道路, 是很愚蠢的行为.</p>
<p>需要去理解需求, 自己要做什么, 然后, 在大脑中构造, 现在有什么, 为了完成需求需要做什么, 完成大体的组成结构/步骤流程的思考后, 再着手去做.</p>
<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="k">def</span> <span class="nf">dosomething</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">		<span class="err">“””</span>
</span></span><span class="line"><span class="cl">		<span class="err">“””</span>
</span></span><span class="line"><span class="cl">		<span class="c1"># step1: call func test()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="c1"># step2: parse url to </span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="c1"># step3: judge</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">		<span class="c1"># step4: convert and return</span>
</span></span></code></pre></div><h3 id="3关于脚手架代码">3.关于<code>脚手架</code>代码</h3>
<p>在逻辑的关键位置, print/assert关键信息, 用于在调试中迅速确认问题. (一些中间值/状态/条件判断结果)</p>
<p>当然, 信息除了<code>关键</code>这个特性, 还需要足够<code>丰富</code>和<code>显眼</code>, 一遍一次性定位问题. (既要好看又要有用)</p>
<p>你需要确定下如何用顺手的编辑器快速输入这些代码, 可以用各类语言的snippets</p>
<p>例如, 在写python时候, 我很喜欢prt<tab>(k-vim自动补全), 快速插入一些需要的信息</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="nb">print</span> <span class="s2">&#34;TRACK ================= result&#34;</span><span class="p">,</span> <span class="nb">type</span><span class="p">(</span><span class="n">result</span><span class="p">),</span> <span class="n">result</span><span class="p">,</span> <span class="n">result</span> <span class="o">==</span> <span class="s2">&#34;test&#34;</span>
</span></span></code></pre></div><h3 id="4写完一段代码第一时间自己review一下">4.写完一段代码第一时间自己review一下</h3>
<p>事实证明, review的效果比写完直接跑再来调, 效率高多了.</p>
<p>刚写完一段代码, 思路还很清晰, 跳到开始, review过程中注意各类变量, 条件判断, 函数调用, 上下文, 一致性, 错误处理等, 花不了多少时间, 却能发现一些<code>显而易见</code>的问题, 省下很多无谓的调试时间(没问题不需要调试!).</p>
<h3 id="5review中注意-代码是抠过来的么">5.review中注意, 代码是<code>抠</code>过来的么?</h3>
<p>很多时候从其他地方copy代码过来(一行或几行, 有时候只是一个函数调用或一个判断), 但是很容易忘了根据当前情况修改一些必要的值, 导致问题</p>
<p>例如函数调用, 这个地方调用参数可能跟你copy这行代码需要参数不一样, 但是放在这里并不会报错(一切运作正常), 最终结果并不对&hellip;..</p>
<p>好了, 开始调试</p>
<h3 id="6搞明白问题的表现是什么症状">6.搞明白问题的表现是什么(症状)</h3>
<p>运行代码, 报错了, 有些人会瞬切回编辑器, 开始改代码(作高效状)&hellip;&hellip;&gt;_&lt;#</p>
<p>问题是: <code>报错提示你看了么, 看明白了么?</code></p>
<p>现在大部分语言, 其报错提示已经很明显了, 精确到行/变量, 虽然整个异常栈信息可能很长(非常长), 但是都有其特征(在最前或在最后,或在中间靠后, 有关键字), 仔细看下报错信息, 精确制导才是王道.</p>
<p>所以, 你需要从错误信息中先确认</p>
<pre tabindex="0"><code>错误类型
发生错误的地方
</code></pre><p>很多语法问题可以根据这个信息直接定位</p>
<h3 id="7调试过程中-需要时刻注意">7.调试过程中, 需要时刻注意</h3>
<p>改的是不是正确的目录下正确的文件?(大坑)</p>
<p>保存了么(编译了么)?(又一个坑)</p>
<p>服务重启了么?</p>
<p>跟数据库有没有关系/跟缓存有没有关系, 要不要清?</p>
<p>……</p>
<p>以上问题, 随便碰上一个你都可能发现, 自己书写的代码和当前运行来调试的代码不一样.(会浪费你巨量的时间)</p>
<p>自己调试半天怎么还是一样的结果</p>
<p>我一直在修改, 但是没有什么区别(出现这种情况要自问一下了)</p>
<p>可以显示在代码头部打印或者故意出错, 确认是同一套代码</p>
<h3 id="8环境数据一致性">8.环境/数据一致性</h3>
<p>当你发现在本地无法复现别人报过来的问题(在我电脑上是正常的), 这时候, 需要考虑是否是环境和数据的问题.</p>
<h3 id="9先不要动代码-假设代码是正确的">9.先不要动代码, 假设代码是正确的</h3>
<p>遇到问题, 不要急着修改代码, 需要假设, 代码是正确的, 然后去复现, 复现之后定位.</p>
<h3 id="10首先要怀疑自己">10.首先要怀疑自己</h3>
<p>你不能一旦代码跑不动就怀疑是别人的问题, 然后抛给别人, 这样做同样是很不负责任而且很愚蠢的.</p>
<p>首先, 你需要怀疑自己, 排查问题, 当确定不是自己的问题之后, 将问题定位, 输入, 预期结果, 现在的异常结果都处理好, 生成一个问题, 抛给对应负责人.
(一切没有价值的怀疑都是无意义的)</p>
<p>程序员都是好人, 每次都在想: <code>一定是我的问题</code></p>
<h3 id="11对于莫名其妙的问题-多试几种情况">11.对于莫名其妙的问题, 多试几种情况</h3>
<p>有时候碰上一些诡异的问题, 例如有一种情况的输入会报错, 这时候, 再跳过去修改代码前, 可以多尝试几种输入, 涉及边界/异常/正常等情况, 排除法, 精确制导.</p>
<p>例如, 可以变换输入值的范围(扩大或缩小, 可能用二分法), 变换输入类型和格式</p>
<h3 id="12先回到正确的代码">12.先回到<code>正确的代码</code></h3>
<p>如果这段代码是由于修改导致的, 可以注掉此次变更代码, 同样的输入再次验证定位</p>
<h3 id="13如果一段代码是没动过的代码">13.如果一段代码是<code>没动过</code>的代码</h3>
<p>如果你确保<code>确实没动过</code>, 此时, 先不要怀疑自己, 更大的可能是<code>别人的问题</code>.</p>
<p>可能情况: 依赖出了问题(调用函数返回数据不对/异常? 依赖请求挂了? ……), 数据出了问题(表结构变更/服务返回数据变更), 环境问题(数据库/缓存)</p>
<h3 id="14bug总是倾向于集中出现在一起">14.bug总是倾向于集中出现在一起</h3>
<p>很多时候, bug是扎堆的, 可以回忆下之前修改的地方, 确认问题.</p>
<h3 id="15对于很长很长-上二分法">15.对于很长很长, 上<code>二分法</code></h3>
<p>可能函数很长, 或者调用链很长, 不易调试.(光打调试信息就得打得手疼)</p>
<p>找到关键变量, 上<code>二分法</code>, 无上利器.</p>
<h3 id="16print-or-debug">16.print or debug?</h3>
<p>个人偏好简单粗暴的<code>print</code>, 主要是用的vim+sinppet, 快速高效.</p>
<p>当然, 如果用IDE, 用	<code>debug	</code>吧</p>
<h3 id="17十分十分诡异的问题">17.十分十分诡异的问题</h3>
<p>上<code>debug</code>, 打断点, 一点点调试吧, 只能这样了.</p>
<h3 id="18当一个问题超过半小时">18.当一个问题超过半小时</h3>
<p>歇一歇, 走动走动, 打个水, 呼吸下新鲜空气.</p>
<p>这时候有利于脱出情境, 去掉挫败感/愤怒/迷信等</p>
<p>很多时候突然灵感一到, 瞬间明了(这种感觉很奇妙)</p>
<h3 id="19关于google">19.关于google</h3>
<p>有些错误信息, 如果觉得比较独特诡异, 可以google下, 你会找到更多的一些信息的.</p>
<h3 id="20关于求助">20.关于求助</h3>
<p>实在搞不定, google大神也搞不定, 此时可能需要求助了.</p>
<p>前提, 你自己能把问题想清楚, 并且逻辑清晰地描述出来.(什么业务什么位置的什么逻辑, 报错类型和报错信息, 输入输出, 迄今做了哪些尝试等等) 要学会聪明地问问题, 高效, 尊重自己也尊重别人.</p>
<p>如果你自己都没整明白怎么问, 别人也无能为力.</p>
<p>遇到很多人, 直接上来就一句<code>xxx出问题了</code>, 没有前置条件后置结果中间症状&hellip;&hellip;</p>
<p>不过, 如果你会聪明地问, 那就放心大胆地问吧, 不用磨磨唧唧的, 程序员大都是善良的孩子.</p>
<h3 id="21吃一堑长一智">21.吃一堑长一智</h3>
<p>被坑了就要总结总结, 有个记录, 不被同一个问题坑两次.</p>
<p>如果被坑了就忘, 还需要去反复求助, 那这属于坑队友的行为(鄙视下)</p>
<hr>
<p>好了, 就这些:)</p>
]]></content>
		</item>
		
		<item>
			<title>基于 PostgreSQL 的数据统计系统</title>
			<link>https://wklken.me/posts/2014/11/16/unit-statistics-system.html</link>
			<pubDate>Sun, 16 Nov 2014 20:58:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/11/16/unit-statistics-system.html</guid>
			<description>看到标题就知道我要写什么了, 这是之前一个项目的小结吧, 自己对统计的一些认识和看法. 当时从前到后, 包括技术选型, 花了接近一个月的时间, 也在生产</description>
			<content type="html"><![CDATA[<p>看到标题就知道我要写什么了, 这是之前一个项目的小结吧, 自己对统计的一些认识和看法.</p>
<p>当时从前到后, 包括技术选型, 花了接近一个月的时间, 也在生产上用了两三个月, 一致在持续维护, 做完图表配置化已然接近完工, 无奈后来离开了, 不过目前应该还在运转</p>
<p>至于源代码, 暂时不考虑开源, 太渣(其中在看了几天js情况下, 自己撸了1000行js的前端框架, 质量堪忧), 全套用python实现.</p>
<p>提供一种快速实现运营统计需求的思路.</p>
<p>(图为百度 echarts 示例)</p>
<p><img src="/imgs/system/statistics.png" alt="statistics"></p>
<hr>
<h3 id="一-场景">一. 场景</h3>
<ul>
<li>统计</li>
</ul>
<p>所谓统计, 抽象出来就是计数而已(还有各个计数之间的算术运算). 再具体一些, 根据不同维度进行计数.</p>
<p>而统计后台, 无外乎数据的输入, 处理, 及输出.</p>
<p>对于实时性, 一般会以天为单位进行统计.</p>
<p>而在具体业务场景下, 需要计数的数据来源于各个项目和同一个项目的不同机器(分布式部署), 就需要考虑, 如何将日志进行汇聚, 如何更为便捷地进行处理, 存储, 以及展现.</p>
<p>其中要考虑, 需求是不断在变化的, 如何将成本降到最低?</p>
<ul>
<li>以往的统计方式:</li>
</ul>
<pre tabindex="0"><code>分析统计需求 -&gt; 修改项目记录日志内容和格式(到磁盘) -&gt; 自行将日志汇总到一台机器(rsync) -&gt; crontab脚本分析日志(要删或备份历史数据) -&gt; 新建db表, 存储统计结果 -&gt; 写管理后台, 查询统计结果(最繁琐) -&gt; 处理分页/图表等
</code></pre><p>虽然每次耗时或许并不会太长(0.5-2d, 视需求大小), 但对于不同项目和需求变更, 这些工作都是纯体力毫无技术含量的枯燥工作, 可以说是无意义的资源浪费.</p>
<ul>
<li>新的方式</li>
</ul>
<pre tabindex="0"><code>分析统计需求 -&gt; 确认日志内容和格式  -&gt; 统计后台配置输入/处理/输出逻辑 -&gt; 查看结果
</code></pre><p>说白了就是, 处理统计需求变成了 <code>写sql</code> + <code>配置</code></p>
<hr>
<h3 id="二-处理思路">二. 处理思路</h3>
<ul>
<li>大体思路如下(从后往前):</li>
</ul>
<pre tabindex="0"><code>1. 将日志进行汇总
2. 日志格式一致化
3. 将日志导入到一个容器中
4. 便捷地通过容器进行计算(计数)
5. 统计结果进行统一存储
6. 提供统一的查询接口
7. 提供前端框架组件, 可以通过配置调用统一查询接口, 并对数据进行分页及图表化
8. 提供配置入口, 可以配置日志入口, 处理逻辑, 展现逻辑. 即完全地配置化
</code></pre><ul>
<li>需要统一的地方:</li>
</ul>
<pre tabindex="0"><code>日志格式
容器存储
报告存储
查询接口
前端组件
</code></pre><ul>
<li>系统成型后</li>
</ul>
<pre tabindex="0"><code>增加/修改统计需求: 只需要在后台配置数据来源(日志), 处理逻辑(一段 sql), 展示逻辑(一段前端 json配置), 就可以实现图标
</code></pre><hr>
<h3 id="三-具体">三. 具体</h3>
<h3 id="0-基本架构">0. 基本架构</h3>
<pre tabindex="0"><code>
             ----------------------------------------------
            |      日志(UniteStats or ApplicationLogs)     |
             ----------------------------------------------
                              ||
                 ---------------------------
                |        load处理程序        |
                 ---------------------------
                              ||
                ___________________________
               |存储容器--计算容器           |
               |                          |
               |  Container(Postgresql)   |   //json - sql - 聚集函数
               |                          |
               |___________________________
                             ||
            --------------------------------------
           |        [自定义统计脚本-查询逻辑及报告表]  |
            --------------------------------------
                             ||
            ---------------------------------------
           |            统计报告                    |
            ---------------------------------------
                            ||
            ---------------------------------------
           |            统一查询接口                |
            ---------------------------------------
                            ||
             ------------------------------------
            |      [自定义前端-使用统一库-配置生成]   |
             ------------------------------------
</code></pre><h3 id="1-日志格式">1. 日志格式</h3>
<p>日志, 即文本.</p>
<p>但是文本存在各种格式, 例如常见的’\t’分隔的列, csv, json, xml等等.</p>
<p>这里的要求是: 一定要满足自描述, 易读(人), 易处理(生成和解析).</p>
<p>最终选择了<code>json</code>. 将原先无结构数据转成半结构化数据.</p>
<p>原因之一, <code>容器</code>对半结构化的数据支持已经非常完善了, 例如postgresql, mongodb等, 对于后续计算很重要.</p>
<p>原因之二, 作为一个统一的平台, 我只在乎数据是一份日志, 但是不在乎, 日志里存了些什么, 每个字段的意义, 这些只有平台的使用者需要知道. 否则带来很大一个问题是, 对于使用者在新增或变更一份日志格式时, 需要明确告诉系统这份日志各个字段是什么(名称和类型), 复杂化了</p>
<p>到这里, 我们统一了日志的格式, 记录为json, 每条记录一行.</p>
<h3 id="2-日志收集汇总">2. 日志收集汇总</h3>
<p>目的: 将日志汇总到同一台机器上, 便于统一处理</p>
<p>命名规则: <code>$THE_LOG_PATH/{projectName}/{projectName}_{moduleName}_{ip}_{yyMMdd}.log</code> (示例)</p>
<p>日志汇总的方案有很多:</p>
<pre tabindex="0"><code>scp
rsync
nfs
logstash
</code></pre><p>最终的处理方案: 数据量小, 同一个机房, 使用NFS将日志汇总到目录, 不同机房, 使用rsync进行汇总. 如果数据量大, 可以考虑使用logstash, 直接将日志经过节点处理实时写到一台机器上(就不要分别记录到各自磁盘了).</p>
<p>扩展: 使用多台机器, 只要保证最终导入同一个库即可.(同一个项目, 同一天存在一张表, 不同机器的日志导入之)</p>
<p>到这里, 我们将所有json格式的日志汇集到了一起</p>
<h3 id="3-导入容器处理">3. 导入容器处理</h3>
<p>目前每个项目的日志格式是,</p>
<pre tabindex="0"><code>{projectName}/{projectName}_{moduleName}_{ip}_{yyMMdd}.log
</code></pre><p>我们会将同一个项目, 可能来自不同机器的日志导入同一张表</p>
<pre tabindex="0"><code>{projectName}/{projectName}_{moduleName}_*_{yyMMdd}.log
=&gt;
table: projectName_moduleName_yyMMdd
</code></pre><p>处理方式: 批量入库, 并且清理保留日期外的表</p>
<p>建议使用批量导入的方式, 速度杠杠的. Postgresql请使用copy命令</p>
<h3 id="4-容器">4. 容器</h3>
<p>一个计算容器, 仅此而已</p>
<p>技术选型时, 考虑过Mysql/Mongdb/Redis/MariaDB/OrientDB/CouchDB/RethinkDB等等, 最终敲定使用postgresql, 无它, 对json的完美支持, 满足业务: 一定的数据量, 足够简单的统计方式, 足够稳定, 简单易运维等</p>
<p>提下<code>redis</code>, 当时做了整套的<code>redis</code>方案(接口文档都明确完了就差写代码了), 但是后来毙掉了. (典型的拿着锤子满世界都是钉子的案例). 思想是: 流式日志处理, 根据业务需求使用redis counter, 主从, 后台从redis直接取counter进行展示. 脑洞很大, 可以搞定实时/非实时情况, 还可以顺带把各类业务中的counter需求给做了, 以及更为灵活的展现方式, 但是学习成本较高, 对每个写统计的人要求较高(素质, 具备正确的统计思维, 否则会悲剧掉). 再加上业务本身要求实时性并不高, 所以废弃.</p>
<p><code>MySql</code> 对 <code>json</code> 的支持, 相对于 postgresql 而言逊色太多了, 对<code>json</code>格式存在限制(多层复杂嵌套的情况)</p>
<p><code>Mongodb</code> 虽然对<code>json</code>支持不错, 但是对于数据量较大的情况支持并不好, 并且查询以及运维都会带来一定困难, 对于使用者有一定学习成本</p>
<p>PostGresql作为容器的好处:</p>
<pre tabindex="0"><code>1. 支持的数据量
2. 查询简单，支持json, 所有sql查询，group by/order by/嵌套子查询，聚集等
3. 各种聚集、统计函数均可用，搞定基本统计查询无障碍（再复杂的都可以）
4. 运维简单
5. 对于开发而言几乎没有学习成本, 会sql再学习下postgresql的json查询
</code></pre><p>示例:
假设搜索日志:</p>
<pre tabindex="0"><code>{‘ip’: ‘127.0.0.1’,
 ‘keyword’: ‘test’,
 ‘result_count’: ‘1’,
}
</code></pre><p>统计 pv</p>
<pre tabindex="0"><code>select count(data-&gt;&#39;ip&#39;) from search_20141101;
</code></pre><p>统计 uv</p>
<pre tabindex="0"><code>select count(DISTINCT data-&gt;&gt;&#39;ip&#39;) from search_20141101;
</code></pre><p>无结果数</p>
<pre tabindex="0"><code>select count(*) from search_20141101 where data-&gt;&gt;&#39;result_count&#39; = &#39;0&#39;;
</code></pre><p>搜索热词排行榜</p>
<pre tabindex="0"><code>select data-&gt;&gt;’keyword’, count(*)
from search_20141101
where data-&gt;&gt;&#39;result_count&#39; != &#39;0&#39;
group by data-&gt;&gt;’keyword’
order by count(*) desc
limit 100;
</code></pre><h3 id="5-批处理">5. 批处理</h3>
<p>这里要做的事情, 需要有一个管理后台, 让开发可以配置上传自己的处理脚本, 设定脚本执行时间, 执行参数(处理日期/报告表名), 甚至是执行依赖.</p>
<p>这里需要形成一个约定</p>
<pre tabindex="0"><code>报告表名: projectName_statsModuleName
报告表一些字段名(因为统一查询接口需要用到): 日期 date,
其他约定字段
</code></pre><p>每天, 系统会扫描并调度任务, 执行, 处理得到统计结果, 存入报告表.</p>
<p>到这里, 我们每天的统计结果都存入到了报告表中</p>
<h3 id="6-输出">6. 输出</h3>
<p>报告表, 是以时间为维度的, 每条记录带有日期, 每条记录细化到要统计到的精确维度.(具体表现是一个维度会多一列字段), 原则是, 需求分析时充分考虑当前及后续可能的统计需求(要预见还是蛮容易的), 直接将统计维度最细化.</p>
<p>当然, 如果无法最细化, 后面存在变更, 可以修改统计脚本, 根据情况对历史数据进行重新统计.</p>
<h3 id="7-统一查询层">7. 统一查询层</h3>
<p>一层通用的接口, 支持传入表名, 条件, 需要结果字段, 格式等, 可以对系统中各类报告表进行各种形式的查询, 获取统计结果.</p>
<h3 id="8-前端框架及展现">8. 前端框架及展现</h3>
<p>是一整套的js款干啊</p>
<p>分成几块</p>
<ul>
<li>生成查询表单: 模块化组件, 通过json配置, 自动生成统计查询的表单, 支持各类维度</li>
</ul>
<p>配置示例:</p>
<pre tabindex="0"><code>// 产生条件html
    var condition_configs = {
        title: &#34;频道访问统计摘要&#34;,
        conditions: [
                {
                    type: &#34;date_begin_to_end&#34;, //开始结束日期选择框
                },
                {
                    type: &#34;select&#34;,   //下拉框
                    label: &#34;频道&#34;,
                    id: &#34;channel&#34;,
                    options: [
                        {
                            text: &#34;所有&#34;,
                            value: &#34;&#34;,
                        },
                        {
                            text: &#34;快速访问&#34;,
                            value: &#34;quickaccess&#34;,
                        },

                    ]
                },
                {
                    type: &#34;version&#34;, //文档框
                },
        ]
    };
</code></pre><p>就会自动生成表单</p>
<pre><code>begin_date:
end_date:
channel:
version:
</code></pre>
<ul>
<li>
<p>组合查询条件: 表单提交时, 根据json配置, 将表单内容/字段/值/表等, 拼接成统一查询层接口需要的请求串</p>
</li>
<li>
<p>查询后数据处理: 将查询后的结果, 根据json配置, 进行转化和展现, 并图表化.</p>
</li>
</ul>
<p>一个配置示例:</p>
<pre tabindex="0"><code>一般文本
{
    &#39;column&#39;: &#39;date&#39;,
    &#39;name&#39;: &#39;日期&#39;,
    &#39;type&#39;: &#39;text&#39;,
},

百分比 $后面跟的是sql查询结果列名
{
    &#39;column&#39;: &#39;uninstall_ratio&#39;,
    &#39;name&#39;: &#39;卸载率&#39;,
    &#39;type&#39;: &#39;ratio&#39;,
    &#39;value&#39;: &#39;$uninstall_pv/$install_pv&#39;
},

公式计算
{
    &#39;column&#39;: &#39;the_qvod_link_pv&#39;,
    &#39;name&#39;: &#39;导入链接数&#39;,
    &#39;type&#39;: &#39;calculate&#39;,
    &#39;value&#39;: &#39;$qvod_link_pv + $qvod_start_pv&#39;
},

列值翻译
{
    &#39;column&#39;: &#39;channel&#39;,
    &#39;name&#39;: &#39;渠道&#39;,
    &#39;type&#39;: &#39;text&#39;,
    &#39;translation&#39;: {
        &#34;all&#34;: &#34;all&#34;,
        &#34;player&#34;: &#34;播放器&#34;,
        &#34;zx&#34;: &#34;资讯&#34;,
        &#34;other&#34;: &#34;其他导入&#34;,
    }
},
</code></pre><h3 id="9-图表">9. 图表</h3>
<p>使用百度 <a href="http://echarts.baidu.com/">echats</a></p>
<p>可以根据配置, 将统一查询层的接口返回数据直接灌入echats, 生成表单</p>
<h3 id="10-过程日志及监控">10. 过程日志及监控</h3>
<p>需要一组管理表, 进行任务配置/调度/执行/执行结果, 整个过程中的操作可以配置和查看, 用于监控.</p>
<hr>
<h3 id="四-小结">四. 小结</h3>
<blockquote>
<p>It’s Simple, but it works.</p>
</blockquote>
<p>数据情况, 当时大概每天 10G 日志 load 到库(处理前&gt;10G), 每天日志数据大概是五千万条, 具体业务上了大概40个的样子, 每天30分钟左右处理完. 对于开发的改进是, 将原先0.5-2d的工作, 缩减到了1-2小时, 对生产力的提升较为显著.(对于日志数多且单一日志量较小的情况处理尤为便捷)</p>
<p>适用范围: 对于一般团队应该足够了(流量百万级别), 每个项目每天3-5百万访问量, 日志数据10-20G, 当然, 一直没机会测试上限, 不过只要PostGresql能抗住, 量再大些应该也ok.(可以考虑上elasticsearch)</p>
<p>以上思路, 仅供借鉴:) 就这样吧</p>
]]></content>
		</item>
		
		<item>
			<title>简约之美 &amp; 编写可读代码的艺术</title>
			<link>https://wklken.me/posts/2014/11/16/code-simplicity-and-the-art-of-readable-code.html</link>
			<pubDate>Sun, 16 Nov 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/11/16/code-simplicity-and-the-art-of-readable-code.html</guid>
			<description>最近陆续收到一些donation, 非常感谢哈, blog的文章说多不多说少不少, 大部分是笔记性质的, 主要目的还是积累以及方便自己查询回顾, 分享</description>
			<content type="html"><![CDATA[<p>最近陆续收到一些donation, 非常感谢哈, blog的文章说多不多说少不少, 大部分是笔记性质的, 主要目的还是积累以及方便自己查询回顾, 分享出来, 希望有所帮助:)</p>
<p>ps: 昨天将国内ip切到gitcafe了, 加载速度应该快了很多, 在此特别感谢下<a href="https://gitcafe.com/">gitcafe</a>. (关于如何国内国外切分访问, google大法)</p>
<p>今天要提的是&laquo;简约之美—软件设计之道&gt;&gt; 以及 &laquo;编写可读代码的艺术&gt;&gt;, 主要原因是, 经典, 更重要的是, 足够薄:), 建议买了珍藏, 也是属于那种不同时期反复读会有不同感受的书</p>
<p>———————————</p>
<h2 id="简约之美">简约之美</h2>
<p>这本书, 用一百页来说明, 软件开发设计中, 一些十分简单的道理.</p>
<p><img src="/imgs/books/code-simplicity.jpg" alt="封面"></p>
<blockquote>
<p>好的程序员和差的程序员的区别在于理解能力. 差劲的程序员不理解自己做的事情, 优秀的程序员则相反.</p>
</blockquote>
<p><code>理解能力</code>, 看起来蛮虚的一个词, 但是在工作中真正进行沟通时, 你会发现区别非常大, 决定了是<code>一次沟通</code> 还是 <code>反复沟通</code>; 是<code>直达目标</code>, 还是<code>不断曲折</code>; 是一次<code>搞定</code>, 还是<code>改改改</code>; 是<code>反馈有效问题</code>, 还是<code>反馈不是你的问题的问题空耗你的时间</code>. 所以花费时间去理解需求, 想明白之后再开始写代码, 这个很重要! (真正团队干活你会有直观感受的)</p>
<blockquote>
<p>问题的根源通常在于编程&hellip;&hellip;这一切都与复杂性有关&hellip;&hellip;编程就成了把复杂问题化解为简单问题的劳动&hellip;&hellip;”好程序员”应当竭尽全力, 把程序写得让其他程序员容易理解.</p>
</blockquote>
<p>bug的本质, 归根结底在于编程本身.</p>
<p>我们往往容易把问题复杂化(大而全, 追求完美, 过早优化, 过早关注细节), 而过度复杂的后果导致后期代码的难以维护.(所谓的到时候再改/重构, 都是虚妄的), 程序员遇到一坨代码(别人写的或者之前写的), 有优化的冲动, 但是迫于需求或者时间或者系统稳定性, 往往惧怕<code>变化</code>, 这段代码能工作就行.</p>
<p>但是为什么要复杂化呢? 最简单的, 莫过于在最初就做到最好, 不要给自己<code>到时候再重构</code>的念头. 写好每行代码.</p>
<p>这里的<code>其他程序员</code>, 也可能是<code>一个月</code>后的自己. 如果经常发现回头看自己的代码都看不懂, 那么说明进步的余地还是很大的:).</p>
<p>到这里, 我们的目标转向: 寻找提高代码质量的科学方法.</p>
<blockquote>
<p>每个写代码的人都是设计师</p>
</blockquote>
<p>小到一个变量名, 一个判断逻辑, 大到一个函数, 一个类, 一个算法, 从代码里可以感受到很多东西. 拿建筑设计师对比, 写代码, 如同构筑一栋建筑, 不管是小屋/公寓还是摩天大厦, 好的设计永远美好, 而糟糕的设计, 无论大小, 永远丑陋. 很多概念, 意识和技巧在里面.(建议阅读<code>编写可读代码的艺术</code>, 然后是<code>代码大全</code>)</p>
<blockquote>
<p>全部软件都有一个相同的目标: 帮助其他人&hellip;&hellip;不能理解<code>帮助其他人</code>的程序员, 只能写出糟糕的程序, 也就是说, 他们的程序提供不了什么帮助……在做与软件有关的决策时, 指导法则就是判断能够提供什么样的帮助</p>
</blockquote>
<p>同样, 这里的<code>其他人</code>, 可能是你自己.</p>
<p>需求的优先级, 取决于这个需求对于用户帮助的大小.</p>
<p>你这样做/这个功能/这么处理, 对于目标, 对于团队, 对于个人, 有何帮助?
如果没有, 为什么要这么做?</p>
<blockquote>
<p>软件设计科学的目标: 1.确保软件能够提供尽可能多的帮助. 2.确保软件能够持续提供尽可能多的帮助 3.设计程序员能尽可能简单地开发和维护的软件系统. 才能实现1/2</p>
</blockquote>
<p>1代表软件本身的价值, 2代表软件的可维护性可扩展性, 3代表, 好的/简单的设计, 决定了可维护性和可扩展性, 是万丈高楼的地基. 不过1和3, 在有限资源的情况下(资源永远是不够的), 是互相冲突的, 所以要思考如何保持平衡.</p>
<p>这里提到, 软件的开发和维护都应当简单, 要避免困难和复杂.</p>
<blockquote>
<p>软件设计方程式 可取程度=价值/成本  =&gt;  可行性=(当前价值+未来价值)/(实现成本+维护成本)</p>
</blockquote>
<p>当前价值和实现成本往往是可评估的, 人们会关注于这一点, 带来的问题就是忽略了未来价值和维护成本, 这两个和时间相关, 不易评估, 但是却更为重要. 人很容易只着眼于现在而忽略了未来. 所以写代码时需要注意, <code>存在着未来</code>.</p>
<p><code>相比降低实现成本, 降低维护成本更为重要</code>. 很直观的感觉, 一个设计良好的接口, 在需求变更的时候, 只需要动个参数或者动几行代码或者压根不需要改. 而一个糟糕的设计里, 每次需求变更, 会发现需要改动很多代码, 甚至是重写, 连带测试等时间, 你会发现很多时间耗费在里面. 所以应该一开始就理解, 往未来看一眼(预测短期未来是可行的, 预测长期未来是不靠谱的), 再进行设计, 再进行代码.</p>
<blockquote>
<p>变化定律: 程序存在的时间越久, 它的某个部分需要变化的可能性就越高.</p>
</blockquote>
<p>一切都是变化的, 你自己, 还有这个世界.</p>
<p>所以需求变更是必然的:)</p>
<p>之前学到一个很重要的观点: <code>拥抱变化</code>.</p>
<blockquote>
<p>软件设计三大误区: 1.编写不必要的代码 2.代码难以修改 3.过分追求通用</p>
</blockquote>
<p>YAGIN, <code>不要编写不是必须的代码, 并且要删除没有用到的代码</code>. 版本库干嘛用的? 提交, 然后删除那些没用的, 然后再提交:)</p>
<p>僵化设计的原因: 1.对未来做了太多假设(&hellip;&hellip;) 2.不仔细设计就编写代码(新手需注意). <code>设计程序时, 应当根据你现在确切知道的需求, 而不是你认为未来会出现的需求</code></p>
<p>避免过度设计: 仅仅根据目前确知的需求来考虑通用.</p>
<blockquote>
<p>缺陷概率定律: 在程序中新增缺陷的可能性与代码修改量成正比</p>
</blockquote>
<p>好的设计, 代码少(很大可能), 代码变更少, 而糟糕的设计, 反之. 从而, bug出现的概率显而易见</p>
<blockquote>
<p>最好的设计, 就是能够适应外界尽可能多的变化. 而软件自身的变化要尽可能少.</p>
</blockquote>
<p>不变应万变, 追求之</p>
<blockquote>
<p>永远不要<code>修正	</code>任何东西, 除非它真的可能有问题, 而且有证据表明问题确实存在.</p>
</blockquote>
<p>例如: “过早优化”!</p>
<p>当问题成为问题的时候, 才是问题, 才需要去处理!</p>
<blockquote>
<p>理想情况下, 任何系统里的任何信息, 都应当只存在一次.</p>
</blockquote>
<p>避免重复. 变更时代价最小.</p>
<blockquote>
<p>简洁定律: 软件任何一部分的维护难度, 反比于该部分的简洁程度.</p>
</blockquote>
<p>简洁是相对的.</p>
<p>保持一致/可读性(代码被阅读的次数远远多于编写和修改的次数)/命名/注释(代码的意图通常不应该用注释来说明, 直接阅读代码就应当能够理解)</p>
<blockquote>
<p>复杂性是会叠加的, 而不是简单的线性叠加</p>
</blockquote>
<p>问题复杂, 解法不一定复杂.</p>
<p>解决复杂性: 把它分解成独立的小部分, 并进行重新设计.</p>
<blockquote>
<p>测试法则: 你对软件行为的了解程度, 等于你真正测试它的程度&hellip;&hellip;除非亲自测试过, 否则你不知道软件是否能正常运行.</p>
</blockquote>
<p>about test.</p>
<h2 id="编写可读代码的艺术">编写可读代码的艺术</h2>
<p>关于如何编写高质量可读的代码的方法论:)</p>
<p><img src="/imgs/books/the-art-of-readable-code.jpg" alt="封面"></p>
<p>&laquo;The Art of Readable Code&gt;&gt;, 这本书就不细写了, 因为不到两百页, 几乎每一页都是干货.</p>
<p>强烈推荐.</p>
<p>代码大全太厚, &laquo;Clean Code&gt;&gt;太晦涩, 建议来读这本. 每次总能得到一些感悟.</p>
]]></content>
		</item>
		
		<item>
			<title>重读Rework</title>
			<link>https://wklken.me/posts/2014/11/09/rework.html</link>
			<pubDate>Sun, 09 Nov 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/11/09/rework.html</guid>
			<description>忙碌了一段时间, 终于迎来了久违的周末. 时间过得很快, 入职快三个月了, 很忙但很充实, 带来的影响是, 时间少了, 读书和写 blog 的进度随之放缓, 但是积累</description>
			<content type="html"><![CDATA[<p>忙碌了一段时间, 终于迎来了久违的周末.</p>
<p>时间过得很快, 入职快三个月了, 很忙但很充实, 带来的影响是, 时间少了, 读书和写 blog 的进度随之放缓, 但是积累了很多笔记.</p>
<p><code>&lt;&lt;Rework&gt;&gt;</code>这本书, 是<code>37signals</code>三本本书中的第二本, 读过很多遍了, 短小精悍, 但是每次重读都会有一些不同的收获, 建议买一本珍藏:)</p>
<p><img src="/imgs/books/rework.jpg" alt="封面"></p>
<p>以下是一些摘录和自己的看法, 仅供参考</p>
<p>————————</p>
<blockquote>
<p>做一些自己喜欢的事情并从中获得些回报</p>
</blockquote>
<p>要思考下自己目前的工作, 是否符合这一点? 如果是, 那就继续努力做好, 如果不是, 就需要思考下, 自己喜欢做什么?</p>
<blockquote>
<p>一种普遍的错误认知是: 人要从错误中吸取经验教训.</p>
</blockquote>
<p>要学会从错误中吸取经验, 这句话是没错的, 不断尝试, fail fast, 然后得到一些东西, 这也是没问题的. 但是, 不要认为, 不断从错误中吸取教训就能获得成功. 就像通关游戏有一千个坑, 你或许可以一个个试, 然后一次学到一些东西, 但是&hellip;.有没有想过, 失败不是成功的先决条件</p>
<blockquote>
<p>你更应该从成功中汲取养分.成功才是真正靠得住的教材.</p>
</blockquote>
<p>优秀是一种习惯, 进化是建立在成功的基础之上的. 每个人, 无时无刻都是在往前走的, 然而, 每个人最终得到的并不一样.</p>
<blockquote>
<p>计划即瞎猜&hellip;&hellip;现在就决定你这周要做什么,不必去管全年的计划, 只要找出下一项最重要的任务, 然后起而行之.</p>
</blockquote>
<p>准确理解应该是: 长期计划即瞎猜&hellip;&hellip;你不需要年度/季度/月度计划, 但是你需要年度/季度/月度目标, 计划总是赶不上变化的, 你总是花费很长一段时间或者很多精力给自己制定一个非常完美的计划, 但是计划永远只是计划而已.不去做, 只是徒费精力.我的做法是, 有目标, 然后拆解成小目标, 小目标更灵活可变, 而在do的时候, 只专注于一两个, 并且根据自己的心情/状态等灵活变动.
拥抱变化, 只要保证不偏离目标主体就行.
不过短期计划还是需要的, 例如每天的todo list. 我现在切换成了<code>things</code>管理, <code>wunderlist</code>貌似也不错, 不过你需要找一个最适合你自己的工具.</p>
<blockquote>
<p>工作狂的行为不但没有必要, 而是愚蠢至极&hellip;&hellip;真正的英雄早已想出办法, 搞定一切, 然后回家了.</p>
</blockquote>
<p>获取三年前刚毕业那会, 我不会完全赞同这个观点, 经过三年, 三家公司, 三种完全不同的工作风格和模式, 看法已经彻底改变了.</p>
<p><code>快乐工作, 认真生活</code>, 这句话我一年多前才完全认同之. <code>Work Smarter, Not Harder</code>—来自zapier的一篇博文<a href="https://zapier.com/blog/best-ways-work-smarter-not-harder/">16 of the Best Ways to Work Smarter, Not Harder
</a></p>
<p>我之前的博文提过, 恶性循环/良性循环, 所谓的工作狂, 就是处在恶性循环中而不自知, 如果觉得自己有点苗头, 需要自省.</p>
<p>拼的是效率, 而不是时间.</p>
<p>首先, 工作永远是干不完的, 你必须明确知道这一点. 其次, 效率是可以提升的, 你也必须认同这一点, 并通过不断完善自己的工作风格/模式, 优化共同方式, 积累经验, 技术栈工具栈补完, 你可以达到一个很好的状态. 再次, 努力成为<code>英雄</code>而不是<code>工作狂</code>.</p>
<blockquote>
<p>用自己的方式, 做自己喜欢做的事情, 并从中获得回报.</p>
</blockquote>
<p>这才是工作.</p>
<blockquote>
<p>在宇宙中留下你的足迹</p>
</blockquote>
<p>人的一生有限.</p>
<blockquote>
<p>挠自己痒处,&hellip;&hellip;最好还是做自己真正关心的东西</p>
</blockquote>
<p>写代码, 有一个<code>吃自己的狗粮</code>一说. 这里同, 做自己关心的东西, 感兴趣的东西, 优秀到卓越的区别在于此.</p>
<blockquote>
<p>在你的人生中真正有意义的是你做了什么, 而不是你想过什么,说过什么, 或者计划过什么</p>
</blockquote>
<p>计划党/拖延症患者需注意&hellip;.</p>
<blockquote>
<p>永远不会有正当其时的时候&hellip;..完美的时机永远不会出现</p>
</blockquote>
<p>所以, 不要找借口了吧骚年? 活在当下, 现在就开始.</p>
<blockquote>
<p>Draw a line in the sand.</p>
</blockquote>
<p>信念, 你所坚守的到底是什么?</p>
<blockquote>
<p>坚守某种信念并不仅仅是把它写下来. 不但要相信它, 还要让他成为你的生活方式.</p>
</blockquote>
<p>think about it</p>
<blockquote>
<p>你需要的是承诺策略, 而不是退出策略</p>
</blockquote>
<p>你做事情的第一反应是?</p>
<blockquote>
<p>与其做半个成品, 不如做好半个产品</p>
</blockquote>
<p><code>以后再重构</code>, 这句话是很虚无缥缈的, 可以说是遥遥无期, 所以, 经手的每一件事情, 每一行代码, 做到最好. 不要考虑<code>退出策略</code></p>
<blockquote>
<p>不要过早关注细节&hellip;&hellip;你只有在真正开始后, 才能认清到底哪些细节才是最重要的.</p>
</blockquote>
<p>同<code>不要过早优化</code>:)</p>
<blockquote>
<p>关注不变因素</p>
</blockquote>
<p>什么才是不变的? 才是核心? 才是重要的?</p>
<blockquote>
<p>人们把装备当做取胜的法宝, 却不愿花时间去练习&hellip;&hellip;</p>
</blockquote>
<p>妄图寻找捷径?</p>
<p>有一段时间是工具控, 沉迷各种工具，编辑器，插件，操作系统，效率，gtd,浏览器等等</p>
<p>其实，更重要的是事情本身, 是人本身</p>
<p>Get things done, 就足够了</p>
<p>花时间找到适合自己的工具, 不太重, 不过轻, 刚刚合适就好. 而不是花时间收集一大堆东西, 徒耗精力.</p>
<p>现在，已经没那么狂热了，只遵循一个原则，尝试新事物，针对所有工具，使用二八原则，只关心最核心的20%. 足够了.</p>
<blockquote>
<p>立马就上线&hellip;为了迅速上线,要砍掉一切不必要的东西…最有效的成功方法是不断尝试. 不要再臆测到底会发生什么事了, 到现实中去寻找答案吧.</p>
</blockquote>
<p>小步快跑才是王道</p>
<blockquote>
<p>退出的理由?</p>
</blockquote>
<p>为什么要这么做? 你在解决什么问题?这真的有用么?你加上去的东西有价值么? 这种改变真的会起作用么? 这种方法更简单吗? 有其他更值得做的事情吗? 这样做值吗?</p>
<blockquote>
<p>Interruption is the enemy of productivity……你的生活被各种干扰包围着, 只有你自己才能去发起反击.</p>
</blockquote>
<p>干扰不可避免, 只看你如何去解决. 你必须要形成行之有效的工作风格/模式, 以及沟通方式, 并<code>培训</code>你的战友.</p>
<blockquote>
<p>会议有毒</p>
</blockquote>
<p>如何更高效地开会? 这几个月已经彻底杜绝了<code>被动</code>的会议, 但是有些必要的会议时间还是太长了.</p>
<blockquote>
<p>Good enough is fine.</p>
</blockquote>
<p>没有更好, 只有合适. 简单有效就ok.</p>
<blockquote>
<p>Don’t be a hero</p>
</blockquote>
<p>有些时候, 需要考虑适时退出.</p>
<blockquote>
<p>该睡觉时睡觉</p>
</blockquote>
<p>作息规律, 高效保证, 熬夜苦撑没有任何好处</p>
<blockquote>
<p>积累动力的方法就是完成一项任务，然后紧接去完成下一项任务</p>
</blockquote>
<p>重要, 不要给自己设置一个不可能完成的东西, 然后被恐惧压垮</p>
<blockquote>
<p>预估都是垃圾</p>
</blockquote>
<p>不是不预估, 而是, 你需要聪明地去预估. 分治, 小的任务总是更容易把控些.</p>
<blockquote>
<p>要做得比你的对手少, 并以此来击败他们.</p>
</blockquote>
<p>少.</p>
<blockquote>
<p>不要相信”客户永远是正确的”这种废话</p>
</blockquote>
<p>不要相信”产品/老板永远是正确的”这句话, 思辨地看待问题, 提出问题, 解决问题. 学会说’no’. (处理需求的时候很重要)</p>
<blockquote>
<p>头脑发热不等于当务之急</p>
</blockquote>
<p>做当前最重要的事情, 而不是最想去做的事情.</p>
<blockquote>
<p>要保持低调, 你可以利用这段时间继续调整你的策略, 解决纠结的问题, 测试各种创意, 尝试新事物.</p>
</blockquote>
<p>黑暗森林?</p>
<blockquote>
<p>一夜成名只是传说&hellip;&hellip;道路很艰难, 但你必须充满信心.</p>
</blockquote>
<p>生活</p>
<blockquote>
<p>受不了时再招人&hellip;&hellip;不要提前招人.</p>
</blockquote>
<p>你最终会明确你想要招的是什么样的人.</p>
<blockquote>
<p>鸡尾酒会上的陌生人&hellip;&hellip;要营造一个能让人直言不讳并且有安全感的环境.</p>
</blockquote>
<p>所处的环境是不是这样的? 你能否感觉到自己是一份子, 能改变些什么? 而不是深深的无力感?</p>
<blockquote>
<p>多年的无关经验&hellip;&hellip;真正的差别来自于个人的努力程度, 性格差异以及智力水平.</p>
</blockquote>
<p>人和人的区别和工作年限相关性其实蛮低的, 深有体会.</p>
<blockquote>
<p>文化不是由谁创造的&hellip;&hellip;企业文化是行为, 不是语言</p>
</blockquote>
<p>连着实习, 四家公司, 企业文化? 有两家白纸黑字进去新员工培训就有, 大大的标语什么的, 迄今没有记得是什么. 杭州一年多, 是那种潜移默化的, 你从大家的身上可以感受到的东西, 对我影响很大, 受益良多. 现在感受到的, 同样是身体力行, 在一件件事情中. 所以找工作, 如果觉得契合, ok, 如果感觉所谓的<code>文化</code>很SB, 果断些.</p>
<blockquote>
<p>会写代表会思考</p>
</blockquote>
<p>代码/总结/博客</p>
<blockquote>
<p>不要一受伤就结疤, 不要因为一个人的一次错误而去指定规章制度.只有当一件事情反复发生时，才需要为之制定规章制度</p>
</blockquote>
<p>我们做事的时候是不是也这样? 一遭被蛇咬, 十年怕井绳. 这样自己的领域会不断地受限的.</p>
<p>所以, 事不过三的原则很重要:)</p>
<blockquote>
<p>灵感稍纵即逝&hellip;&hellip;想法是不朽的, 一直都会存在. 最不可能长存的是灵感.</p>
</blockquote>
]]></content>
		</item>
		
		<item>
			<title>Vim相关资源</title>
			<link>https://wklken.me/posts/2014/10/03/vim-resources.html</link>
			<pubDate>Fri, 03 Oct 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/10/03/vim-resources.html</guid>
			<description>Vim资源列表 之前在 k-vim 的readme中写了很多, 本次更新8.0版本后, 对其进行了梳理, 感觉放那并不合适, 所以迁过来这边:) .----------------. .----------------. .----------------. | .--------------. || .--------------. || .--------------.</description>
			<content type="html"><![CDATA[<h1 id="vim资源列表">Vim资源列表</h1>
<p>之前在 <a href="https://github.com/wklken/k-vim">k-vim</a> 的readme中写了很多, 本次更新8.0版本后, 对其进行了梳理, 感觉放那并不合适, 所以迁过来这边:)</p>
<pre><code>    .----------------.  .----------------.  .----------------.
    | .--------------. || .--------------. || .--------------. |
    | | ____   ____  | || |     _____    | || | ____    ____ | |
    | ||_  _| |_  _| | || |    |_   _|   | || ||_   \  /   _|| |
    | |  \ \   / /   | || |      | |     | || |  |   \/   |  | |
    | |   \ \ / /    | || |      | |     | || |  | |\  /| |  | |
    | |    \ ' /     | || |     _| |_    | || | _| |_\/_| |_ | |
    | |     \_/      | || |    |_____|   | || ||_____||_____|| |
    | |              | || |              | || |              | |
    | '--------------' || '--------------' || '--------------' |
    '----------------'  '----------------'  '----------------'
</code></pre>
<hr>
<h2 id="资源库">资源库</h2>
<ul>
<li><a href="http://u.memect.com/vim/">memect vim资源汇总</a></li>
<li><a href="https://plus.google.com/communities/105049811056605918816">Google+</a></li>
<li><a href="http://www.reddit.com/r/vim/">reddit</a></li>
<li><a href="http://vimawesome.com/">插件库 vimawsome</a></li>
<li><a href="http://vimcolors.com/">主题库 vim colors</a></li>
</ul>
<h2 id="tutorial">Tutorial</h2>
<ul>
<li><a href="http://www.openvim.com/tutorial.html">openvim tutorial</a></li>
<li><a href="http://www.vimgenius.com/">vim genius</a></li>
<li><a href="http://vim-adventures.com/">vim大冒险</a></li>
<li><a href="http://inside.github.io/vim-presentation/#/">一个很赞的ppt</a></li>
</ul>
<h2 id="tips">Tips</h2>
<ul>
<li><a href="http://www.vimbits.com/bits?sort=top">vimbits</a></li>
<li><a href="https://www.cs.oberlin.edu/~kuperman/help/vim/home.html">vim tips and tricks</a></li>
</ul>
<h2 id="国内文章">国内文章</h2>
<ul>
<li><a href="http://coolshell.cn/articles/5426.html">简明vim练级攻略</a></li>
<li><a href="http://www.kunli.info/2013/08/13/vim/">不要复杂化vim</a></li>
<li><a href="http://blog.jobbole.com/44891/">七个高效文本编辑习惯</a></li>
<li><a href="http://edyfox.codecarver.org/html/vim_fileencodings_detection.html">vim fileencodings</a></li>
<li><a href="http://www.jianshu.com/p/bcbe916f97e1">vim 入门基础</a></li>
<li><a href="http://segmentfault.com/blog/nightire/1190000000445598">vim 哲学</a> 一个系列, 推荐</li>
</ul>
<h2 id="国外文章">国外文章</h2>
<ul>
<li><a href="http://www.moolenaar.net/habits.html">Seven habits of effective text editing</a></li>
<li><a href="http://benmccormick.org/learning-vim-in-2014/">learning vim in 2014</a></li>
<li><a href="http://vimcasts.org/">vimcasts</a></li>
<li><a href="http://sheerun.net/2014/03/21/how-to-boost-your-vim-productivity/">how to boost your vim productivity</a></li>
<li><a href="http://mislav.uniqpath.com/2011/12/vim-revisited/">vim revisited</a></li>
<li><a href="http://huangjian.info/learn-vim-script/">learn vim script</a></li>
</ul>
<h2 id="视频">视频</h2>
<ul>
<li><a href="https://vimeo.com/search?q=vim">vimeo</a></li>
<li><a href="https://vimeo.com/user1690209/videos">Derek Wyatt’s Videos</a></li>
<li><a href="https://vimeo.com/vimlondon/videos">vim london</a></li>
<li><a href="https://www.youtube.com/user/MinuteVimTricks/videos">minute vim tricks</a></li>
<li><a href="http://railscasts-china.com/episodes/rails-with-vim">rails with vim</a></li>
<li><a href="http://www.youtube.com/watch?v=YhqsjUUHj6g">vim as a python ide</a> 对应 <a href="https://speakerdeck.com/mbrochh/vim-as-a-python-ide">ppt</a></li>
</ul>
<h2 id="其他vim配置">其他vim配置</h2>
<ul>
<li><a href="https://github.com/spf13/spf13-vim">spf13</a></li>
<li><a href="https://github.com/square/maximum-awesome">maximum-awsome</a></li>
<li><a href="https://github.com/exvim/main">exvim</a></li>
<li><a href="https://github.com/carlhuda/janus">janus</a></li>
<li><a href="https://github.com/mathiasbynens/dotfiles">dotfiles</a></li>
</ul>
<h2 id="sof">sof</h2>
<ul>
<li><a href="http://stackoverflow.com/questions/1218390/what-is-your-most-productive-shortcut-with-vim">What is your most productive shortcut with Vim?</a></li>
<li><a href="http://stackoverflow.com/questions/726894/what-are-the-dark-corners-of-vim-your-mom-never-told-you-about">What are the dark corners of Vim your mom never told you about? </a></li>
</ul>
<h2 id="其他">其他</h2>
<ul>
<li><a href="http://learnvimscriptthehardway.stevelosh.com/">Learning vim the hard way</a></li>
<li><a href="http://www.swaroopch.com/notes/vim/#Introduction">A Byte of vim</a></li>
<li><a href="http://painlessvim.com/">painless vim</a></li>
<li><a href="http://derekwyatt.org/">derekwyatt</a></li>
<li>BOOK: VIM 实用技巧</li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>Python 源码阅读 - dict</title>
			<link>https://wklken.me/posts/2014/08/11/python-source-dict.html</link>
			<pubDate>Mon, 11 Aug 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/08/11/python-source-dict.html</guid>
			<description>基本类型实现的最后一篇, 先告一段落, 专心找工作去&amp;hellip;&amp;hellip;搞定工作后再开始扫后面的 源码位置 Include/dictobject.h | Objects/dictobject.c PyDictObjec</description>
			<content type="html"><![CDATA[<p>基本类型实现的最后一篇, 先告一段落, 专心找工作去&hellip;&hellip;搞定工作后再开始扫后面的</p>
<hr>
<p>源码位置 <a href="https://github.com/wklken/Python-2.7.8/blob/master/Include/dictobject.h">Include/dictobject.h</a> |
<a href="https://github.com/wklken/Python-2.7.8/blob/master/Objects/dictobject.c">Objects/dictobject.c</a></p>
<hr>
<h1 id="pydictobject的存储策略">PyDictObject的存储策略</h1>
<pre><code>1. 使用散列表进行存储

2. 使用开放定址法处理冲突

    2.1 插入, 发生冲突, 通过二次探测算法, 寻找下一个位置, 直到找到可用位置, 放入(形成一条冲突探测链)

    2.2 查找, 需要遍历冲突探测链

    2.3 删除, 如果对象在探测链上, 不能直接删除, 否则会破坏整个结构(所以不是真的删)
</code></pre>
<p>关于 hash表的 <a href="http://zh.wikipedia.org/wiki/%E5%93%88%E5%B8%8C%E8%A1%A8">wiki</a></p>
<hr>
<h1 id="基本键值pydictentry">基本键值PyDictEntry</h1>
<pre><code>typedef struct {
    Py_ssize_t me_hash;
    PyObject *me_key;
    PyObject *me_value;
} PyDictEntry;
</code></pre>
<p>说明</p>
<pre><code>1. PyDictEntry 用于存储键值对信息

2. Py_ssize_t me_hash
存储了me_key计算得到的hash值, 不重复计算
</code></pre>
<p>结构</p>
<p><img src="/imgs/python-source/PyDictEntry.png" alt="PyDictEntry"></p>
<p>PyDictEntry的三个状态(图片引自-Python源码剖析)</p>
<p><img src="/imgs/python-source/PyDictEntryState.png" alt="PyDictEntryState"></p>
<hr>
<h1 id="pydictobject定义">PyDictObject定义</h1>
<p>定义</p>
<pre><code>typedef struct _dictobject PyDictObject;
struct _dictobject {
    PyObject_HEAD

    Py_ssize_t ma_fill;
    Py_ssize_t ma_used;
    Py_ssize_t ma_mask;

    PyDictEntry *ma_table;
    PyDictEntry *(*ma_lookup)(PyDictObject *mp, PyObject *key, long hash);
    PyDictEntry ma_smalltable[PyDict_MINSIZE];
};
</code></pre>
<p>说明</p>
<pre><code>1. PyObject_HEAD
反而声明为定长对象, 因为ob_size在这里用不上, 使用ma_fill和ma_used计数

2. Py_ssize_t ma_fill;
   Py_ssize_t ma_used;

    ma_fill = # Active + # Dummy
    ma_used = # Active

3. Py_ssize_t ma_mask;
散列表entry容量 = ma_mask + 1, 初始值ma_mask = PyDict_MINSIZE - 1 = 7

    ma_mask + 1 = # Unused + # Active + # Dummy

4. PyDictEntry *ma_table;
指向散列表内存, 如果是小的dict(entry数量&lt;=8). 指向ma_smalltable数组

5. ma_lookup
搜索函数

6. PyDictEntry ma_smalltable[PyDict_MINSIZE];
小dict, 大小8, 小于8个键值对的字典会直接存放在这里, 超出后再从内存分配空间
</code></pre>
<p>结构</p>
<p><img src="/imgs/python-source/PyDictObject.png" alt="PyDictObject"></p>
<p>结论</p>
<pre><code>1. PyDictObject在生命周期内, 需要维护ma_fill/ma_used/ma_mask 三个计数值

2. 初始化创建是ma_smalltable, 超过大小后, 会使用外部分配的空间
</code></pre>
<h1 id="构造过程">构造过程</h1>
<p>定义</p>
<pre><code>PyObject *
PyDict_New(void)
{
    register PyDictObject *mp;

    // 初始化dummy
    if (dummy == NULL) {
        dummy = PyString_FromString(&quot;&lt;dummy key&gt;&quot;);
        if (dummy == NULL)
            return NULL;

    // 暂时忽略
#ifdef SHOW_CONVERSION_COUNTS
        Py_AtExit(show_counts);
#endif
#ifdef SHOW_ALLOC_COUNT
        Py_AtExit(show_alloc);
#endif
#ifdef SHOW_TRACK_COUNT
        Py_AtExit(show_track);
#endif
    }

    // 如果PyDictObject缓冲池可用
    if (numfree) {
        // 取缓冲池最后一个可用对象
        mp = free_list[--numfree];

        assert (mp != NULL);
        assert (Py_TYPE(mp) == &amp;PyDict_Type);
        _Py_NewReference((PyObject *)mp);

        // 初始化
        if (mp-&gt;ma_fill) {
            // 1. 清空 ma_smalltable
            // 2. ma_used = ma_fill = 0
            // 3. ma_table -&gt; ma_smalltable
            // 4. ma_mask = PyDict_MINSIZE - 1 = 7
            EMPTY_TO_MINSIZE(mp);
        } else {
            // 1. ma_table -&gt; ma_smalltable
            // 2. ma_mask = PyDict_MINSIZE - 1 = 7
            INIT_NONZERO_DICT_SLOTS(mp);
        }

        assert (mp-&gt;ma_used == 0);
        assert (mp-&gt;ma_table == mp-&gt;ma_smalltable);
        assert (mp-&gt;ma_mask == PyDict_MINSIZE - 1);
#ifdef SHOW_ALLOC_COUNT
        count_reuse++;
#endif
    } else {
        // 创建一个
        mp = PyObject_GC_New(PyDictObject, &amp;PyDict_Type);
        if (mp == NULL)
            return NULL;
        // 初始化 1/2/3/4
        EMPTY_TO_MINSIZE(mp);

#ifdef SHOW_ALLOC_COUNT
        count_alloc++;
#endif
    }

    // 搜索方法, 关注这个
    mp-&gt;ma_lookup = lookdict_string;

#ifdef SHOW_TRACK_COUNT
    count_untracked++;
#endif
#ifdef SHOW_CONVERSION_COUNTS
    ++created;
#endif

    // 返回
    return (PyObject *)mp;
}
</code></pre>
<p>简化步骤</p>
<pre><code>1. 如果PyDictObject对象缓冲池有, 从里面直接取, 初始化

2. 否则, 创建一个, 初始化

3. 关联搜索方法lookdict_string

4. 返回
</code></pre>
<p>结论</p>
<pre><code>1. 关注对象缓冲池

2. 关注lookdict_string
</code></pre>
<h1 id="销毁过程">销毁过程</h1>
<p>方法定义</p>
<pre><code>static void
dict_dealloc(register PyDictObject *mp)
{
    register PyDictEntry *ep;
    Py_ssize_t fill = mp-&gt;ma_fill;
    PyObject_GC_UnTrack(mp);
    Py_TRASHCAN_SAFE_BEGIN(mp)

    // 遍历销毁ma_table中元素 (ma_table可能指向small_table 或 外部)
    for (ep = mp-&gt;ma_table; fill &gt; 0; ep++) {
        if (ep-&gt;me_key) {
            --fill;
            Py_DECREF(ep-&gt;me_key);
            Py_XDECREF(ep-&gt;me_value);
        }
    }

    // 如果指向外部, 销毁整个(上面一步只销毁内部元素)
    if (mp-&gt;ma_table != mp-&gt;ma_smalltable)
        PyMem_DEL(mp-&gt;ma_table);

    // 如果对象缓冲池未满且是PyDict_Type, 放入
    if (numfree &lt; PyDict_MAXFREELIST &amp;&amp; Py_TYPE(mp) == &amp;PyDict_Type)
        free_list[numfree++] = mp;
    else
        // 否则回收
        Py_TYPE(mp)-&gt;tp_free((PyObject *)mp);
    Py_TRASHCAN_SAFE_END(mp)
}
</code></pre>
<hr>
<h1 id="pydictobject对象缓冲池">PyDictObject对象缓冲池</h1>
<p>定义</p>
<pre><code>#ifndef PyDict_MAXFREELIST
#define PyDict_MAXFREELIST 80
#endif

static PyDictObject *free_list[PyDict_MAXFREELIST];
static int numfree = 0;
</code></pre>
<p>对象缓冲池的结构(跟PyListObject对象缓冲池结构基本一样)</p>
<p><img src="/imgs/python-source/PyDictObjectPool.png" alt="PyDictObjectPool"></p>
<p>结论</p>
<pre><code>1. 最多会缓存80个对象

2. 只缓存 PyDictObject 本身, 其PyDictEntry全部会被回收

3. 缓存对象进去, 旧有的值没有变化, 取出来用的时候初始化时才改变
</code></pre>
<hr>
<h1 id="dict-操作">Dict 操作</h1>
<p>查找/插入/resize/删除, 下个版本补</p>
<hr>
<p>changelog</p>
<pre><code>2014-08-11 first version
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Python 源码阅读 - tuple</title>
			<link>https://wklken.me/posts/2014/08/10/python-source-tuple.html</link>
			<pubDate>Sun, 10 Aug 2014 17:10:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/08/10/python-source-tuple.html</guid>
			<description>还差一篇&amp;hellip;&amp;hellip;写完写简历&amp;gt;_&amp;lt;# 示例 &amp;gt;&amp;gt;&amp;gt; a = () &amp;gt;&amp;gt;&amp;gt; b = () &amp;gt;&amp;gt;&amp;gt; id(a) == id(b) True &amp;gt;&amp;gt;&amp;gt; a = (1, ) &amp;gt;&amp;gt;&amp;gt; b = (1, ) &amp;gt;&amp;gt;&amp;gt; id(a) == id(b) False 源码位置 Include/tupleobject.h</description>
			<content type="html"><![CDATA[<p>还差一篇&hellip;&hellip;写完写简历&gt;_&lt;#</p>
<hr>
<p>示例</p>
<pre><code>&gt;&gt;&gt; a = ()
&gt;&gt;&gt; b = ()
&gt;&gt;&gt; id(a) == id(b)
True

&gt;&gt;&gt; a = (1, )
&gt;&gt;&gt; b = (1, )
&gt;&gt;&gt; id(a) == id(b)
False
</code></pre>
<p>源码位置 <a href="https://github.com/wklken/Python-2.7.8/blob/master/Include/tupleobject.h">Include/tupleobject.h</a> |
<a href="https://github.com/wklken/Python-2.7.8/blob/master/Objects/tupleobject.c">Objects/tupleobject.c</a></p>
<hr>
<h1 id="结构">结构</h1>
<p>定义</p>
<pre><code>typedef struct {
    PyObject_VAR_HEAD
    PyObject *ob_item[1];

} PyTupleObject;
</code></pre>
<p>说明</p>
<pre><code>1. PyObject_VAR_HEAD
PyTupleObject在底层是个变长对象(需要存储列表元素个数).
虽然, 在python中, tuple是不可变对象

2. PyObject *ob_item[1];
指向存储元素的数组

3.
</code></pre>
<p>结构</p>
<p><img src="/imgs/python-source/PyTupleObject.png" alt="PyTupleObject"></p>
<p>构造方法</p>
<pre><code>PyAPI_FUNC(PyObject *) PyTuple_New(Py_ssize_t size);
</code></pre>
<hr>
<h1 id="构造">构造</h1>
<p>看下构造方法定义</p>
<pre><code>PyObject *
PyTuple_New(register Py_ssize_t size)
{
    register PyTupleObject *op;
    Py_ssize_t i;

    // 大小为负数, return
    if (size &lt; 0) {
        PyErr_BadInternalCall();
        return NULL;
    }

    // 如果大小=0, 空元组, 直接取free_list第一个返回
#if PyTuple_MAXSAVESIZE &gt; 0
    if (size == 0 &amp;&amp; free_list[0]) {
        op = free_list[0];
        Py_INCREF(op);

#ifdef COUNT_ALLOCS
        tuple_zero_allocs++;
#endif

        return (PyObject *) op;
    }

    // 如果free_list可分配, 从free_list取一个
    if (size &lt; PyTuple_MAXSAVESIZE &amp;&amp; (op = free_list[size]) != NULL) {
        // 上面  op = free_list[size] 取得单链表头
        // free_list指向单链表下一个元素, 对应位置阈值--
        free_list[size] = (PyTupleObject *) op-&gt;ob_item[0];
        numfree[size]--;

#ifdef COUNT_ALLOCS
        fast_tuple_allocs++;
#endif

      // 初始化 ob_size和ob_type
      /* Inline PyObject_InitVar */
#ifdef Py_TRACE_REFS
        Py_SIZE(op) = size;
        Py_TYPE(op) = &amp;PyTuple_Type;
#endif
        _Py_NewReference((PyObject *)op);
    }
    else
#endif   // free_list不可用
    {
        // 计算空间
        Py_ssize_t nbytes = size * sizeof(PyObject *);
        /* Check for overflow */
        if (nbytes / sizeof(PyObject *) != (size_t)size ||
            (nbytes &gt; PY_SSIZE_T_MAX - sizeof(PyTupleObject) - sizeof(PyObject *)))
        {
            return PyErr_NoMemory();
        }

        // 分配内存
        op = PyObject_GC_NewVar(PyTupleObject, &amp;PyTuple_Type, size);
        if (op == NULL)
            return NULL;
    }

    // 初始化ob_item每个元素
    for (i=0; i &lt; size; i++)
        op-&gt;ob_item[i] = NULL;

    // 第一次分配空数组, 将其放入free_list第一个位置
#if PyTuple_MAXSAVESIZE &gt; 0
    if (size == 0) {
        free_list[0] = op;
        ++numfree[0];
        Py_INCREF(op);          /* extra INCREF so that this is never freed */
    }
#endif


#ifdef SHOW_TRACK_COUNT
    count_tracked++;
#endif

    _PyObject_GC_TRACK(op);

    // 返回
    return (PyObject *) op;
}
</code></pre>
<p>简化步骤</p>
<pre><code>1. 如果size=0, 从free_list[0]取, 直接返回

2. 否则, 确认free_list[size], 是否可用, 可用获取

3. 否则, 从内存分配新的空间

4. 初始化, 返回
</code></pre>
<hr>
<h1 id="回收">回收</h1>
<p>定义</p>
<pre><code>static void
tupledealloc(register PyTupleObject *op)
{
    register Py_ssize_t i;
    // 获取元素个数
    register Py_ssize_t len =  Py_SIZE(op);

    PyObject_GC_UnTrack(op);
    Py_TRASHCAN_SAFE_BEGIN(op)


    if (len &gt; 0) {
        i = len;
        // 遍历, 析构每个元素
        while (--i &gt;= 0)
            Py_XDECREF(op-&gt;ob_item[i]);

         // 与对象缓冲池相关
#if PyTuple_MAXSAVESIZE &gt; 0
        if (len &lt; PyTuple_MAXSAVESIZE &amp;&amp;
            numfree[len] &lt; PyTuple_MAXFREELIST &amp;&amp;
            Py_TYPE(op) == &amp;PyTuple_Type)
        {
            op-&gt;ob_item[0] = (PyObject *) free_list[len];
            numfree[len]++;
            free_list[len] = op;
            goto done; /* return */
        }
#endif

    }
    // 调用回收
    Py_TYPE(op)-&gt;tp_free((PyObject *)op);

done:
    Py_TRASHCAN_SAFE_END(op)
}
</code></pre>
<p>简化流程</p>
<pre><code>1. 回收ob_item每个元素

2. 如果符合条件, 放入到free_list

3. 否则, 回收
</code></pre>
<hr>
<h1 id="tuple对象缓冲池">tuple对象缓冲池</h1>
<p>定义</p>
<pre><code>/* Speed optimization to avoid frequent malloc/free of small tuples */
#ifndef PyTuple_MAXSAVESIZE
#define PyTuple_MAXSAVESIZE     20
#endif

#ifndef PyTuple_MAXFREELIST
#define PyTuple_MAXFREELIST  2000
#endif

#if PyTuple_MAXSAVESIZE &gt; 0

static PyTupleObject *free_list[PyTuple_MAXSAVESIZE];
static int numfree[PyTuple_MAXSAVESIZE];
#endif
</code></pre>
<p>结论</p>
<pre><code>1. 作用: 优化小tuple的mall/free

2. PyTuple_MAXSAVESIZE = 20
会被缓存的tuple长度阈值, 20, 长度&lt;20的, 才会走对象缓冲池逻辑

3. PyTuple_MAXFREELIST  2000
每种size的tuple最多会被缓存2000个

4. PyTupleObject *free_list[PyTuple_MAXSAVESIZE]
free_list, 指针数组, 每个位置, 存储了指向一个单链表头的地址

5. numfree[PyTuple_MAXSAVESIZE]
numfree, 一个计数数组, 存储free_list对应位置的单链表长度

6. free_list[0], 指向空数组, 有且仅有一个
</code></pre>
<p>free_list的结构</p>
<p><img src="/imgs/python-source/PyTupleObjectPool.png" alt="PyTupleObjectPool"></p>
<p>回头看回收跟对象缓冲池相关的逻辑</p>
<p>条件:</p>
<pre><code>if (len &lt; PyTuple_MAXSAVESIZE &amp;&amp;         // len &lt; 20
    numfree[len] &lt; PyTuple_MAXFREELIST &amp;&amp; // numfree[len] &lt; 2000
    Py_TYPE(op) == &amp;PyTuple_Type) // 是tuple类型
</code></pre>
<p>操作</p>
<pre><code>op-&gt;ob_item[0] = (PyObject *) free_list[len]; //ob_item指向free_list[len] 单链表头
numfree[len]++;  // len位置计数+1
free_list[len] = op; // op变成单链表的头
goto done; /* return */
</code></pre>
<p>即过程</p>
<pre><code>1. 如果size=0, 直接从free_list[0]取

2. 如果size!=0, 判断size&lt;20?

3.1 size &lt; 20, 从free_list对应size位置的单链表, 取头部第一个位置

3.2 size &lt; 20, free_list对应size位置还没有可用对象的话, 走内存分配逻辑

4 size &gt; 20  走内存分配逻辑


------------------

回收时

如果size&lt;20, 且free_list对应位置单链表长度没达到上限(2000), 将对象放入到单链表头
</code></pre>
<p>注意</p>
<pre><code>1. 回收时, ob_item都会被回收, 只是本身对象缓存下来

2. 这里free_list, 复用ob_item作为链表指针, 指向下一个位置(通用整数对象池也是复用指针的方式, 不过用的是ob_type)
</code></pre>
<hr>
<p>changelog</p>
<pre><code>2014-08-10 first version
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Python 源码阅读 - list</title>
			<link>https://wklken.me/posts/2014/08/10/python-source-list.html</link>
			<pubDate>Sun, 10 Aug 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/08/10/python-source-list.html</guid>
			<description>还剩 tuple 和 dict就把几个基本类型写完了, 然后歇歇先找工作&amp;gt;_&amp;lt; 源码位置 Include/listobject.h | Objects/listobject.c 定义 typedef struct { PyObject_VAR_HEAD PyObject **ob_item; Py_ssize_t allocated; } PyListObject; 说明 1. PyObject_VAR_HEAD PyListObje</description>
			<content type="html"><![CDATA[<p>还剩 tuple 和 dict就把几个基本类型写完了, 然后歇歇先找工作&gt;_&lt;</p>
<hr>
<p>源码位置 <a href="https://github.com/wklken/Python-2.7.8/blob/master/Include/listobject.h">Include/listobject.h</a> |
<a href="https://github.com/wklken/Python-2.7.8/blob/master/Objects/listobject.c">Objects/listobject.c</a></p>
<hr>
<h1 id="定义">定义</h1>
<pre><code>typedef struct {
    PyObject_VAR_HEAD

    PyObject **ob_item;

    Py_ssize_t allocated;
} PyListObject;
</code></pre>
<p>说明</p>
<pre><code>1. PyObject_VAR_HEAD
PyListObject是变长对象

2. PyObject **ob_item;
指向列表元素的指针数组, list[0] 即 ob_item[0]

3. Py_ssize_t allocated;
allocated列表分配的空间, ob_size为已使用的空间
allocated 总的申请到的内存数量
ob_size 实际使用内存数量

等式:

    0 &lt;= ob_size &lt;= allocated
    len(list) == ob_size
    ob_item == NULL implies ob_size == allocated == 0
</code></pre>
<p>结构</p>
<p><img src="/imgs/python-source/PyListObject.png" alt="PyListObject"></p>
<h1 id="构造">构造</h1>
<p>只有一个方法</p>
<p>定义如下</p>
<pre><code>PyObject *
PyList_New(Py_ssize_t size)
{
    PyListObject *op;
    size_t nbytes;
#ifdef SHOW_ALLOC_COUNT
    static int initialized = 0;
    if (!initialized) {
        Py_AtExit(show_alloc);
        initialized = 1;
    }
#endif

    // 大小为负数, return
    if (size &lt; 0) {
        PyErr_BadInternalCall();
        return NULL;
    }

    // 如果大小超过, 报错
    /* Check for overflow without an actual overflow,
     *  which can cause compiler to optimise out */
    if ((size_t)size &gt; PY_SIZE_MAX / sizeof(PyObject *))
        return PyErr_NoMemory();

    // 计算需要的字节数(PyObject指针数组)
    nbytes = size * sizeof(PyObject *);

    // 如果缓冲池非空, 从缓冲池取
    if (numfree) {
        // 取缓冲池数组最后一个对象
        numfree--;
        op = free_list[numfree];

        // set refcnt=1
        _Py_NewReference((PyObject *)op);
#ifdef SHOW_ALLOC_COUNT
        count_reuse++;
#endif
    } else {

        // 否则, GC_New分配内存空间
        op = PyObject_GC_New(PyListObject, &amp;PyList_Type);

        // 分配失败
        if (op == NULL)
            return NULL;
#ifdef SHOW_ALLOC_COUNT
        count_alloc++;
#endif
    }

    // 确定ob_item列表元素指针的值
    // 若大小&lt;=0
    if (size &lt;= 0)
        op-&gt;ob_item = NULL;
    else {
        // 分配内存
        op-&gt;ob_item = (PyObject **) PyMem_MALLOC(nbytes);
        if (op-&gt;ob_item == NULL) {
            Py_DECREF(op);
            return PyErr_NoMemory();
        }

        // 初始化, 填充
        memset(op-&gt;ob_item, 0, nbytes);
    }

    // ob_size = size
    Py_SIZE(op) = size;
    // allocated
    op-&gt;allocated = size;

    // gc用
    _PyObject_GC_TRACK(op);
    return (PyObject *) op;
}
</code></pre>
<p>简化步骤</p>
<pre><code>1. 判断列表缓冲池是否为空, 是的话从缓冲池取(复用)
2. 否则, 从内存中分配空间
3. 然后初始化数据
</code></pre>
<p>结论</p>
<pre><code>Py_SIZE(op) = size;
op-&gt;allocated = size;
第一次生成list, 其allocated = ob_size
</code></pre>
<h1 id="list_resize">list_resize</h1>
<p>同时注意list_resize方法</p>
<pre><code>extends方法, list_resize(self, m + n)
pop方法,  list_resize(self, Py_SIZE(self) - 1)
append方法, list_resize(self, n+1)
</code></pre>
<p>其定义</p>
<pre><code>list_resize(PyListObject *self, Py_ssize_t newsize)
{
  ...........

  // 如果   allocated/2 &lt;= newsize &lt;= allocated
  // 直接修改ob_size
  if (allocated &gt;= newsize &amp;&amp; newsize &gt;= (allocated &gt;&gt; 1)) {
      assert(self-&gt;ob_item != NULL || newsize == 0);
      Py_SIZE(self) = newsize;
      return 0;
  }


  //否则

  new_allocated = (newsize &gt;&gt; 3) + (newsize &lt; 9 ? 3 : 6);
  new_allocated += newsize;

  .............

  Py_SIZE(self) = newsize;
  self-&gt;allocated = new_allocated;

}
</code></pre>
<p>即</p>
<pre><code>if allocated/2 &lt;= newsize &lt;= allocated

    allocated 不变
    ob_size = newsize

else

    allocated =  newsize +   ((newsize &gt;&gt; 3) + (newsize &lt; 9 ? 3 : 6))
    ob_size = newsize
</code></pre>
<h1 id="回收和pylistobject对象缓冲池">回收和PyListObject对象缓冲池</h1>
<p>看下缓冲池相关的定义</p>
<pre><code>/* Empty list reuse scheme to save calls to malloc and free */
#ifndef PyList_MAXFREELIST
#define PyList_MAXFREELIST 80
#endif

// 80个
static PyListObject *free_list[PyList_MAXFREELIST];

static int numfree = 0;
</code></pre>
<p>我们先看下list_dealloc的定义</p>
<pre><code>static void
list_dealloc(PyListObject *op)
{
    Py_ssize_t i;
    PyObject_GC_UnTrack(op);
    Py_TRASHCAN_SAFE_BEGIN(op)

    // 遍历ob_item, 释放所有类表内元素空间
    if (op-&gt;ob_item != NULL) {
        /* Do it backwards, for Christian Tismer.
           There's a simple test case where somehow this reduces
           thrashing when a *very* large list is created and
           immediately deleted. */
        i = Py_SIZE(op);
        while (--i &gt;= 0) {
            Py_XDECREF(op-&gt;ob_item[i]);
        }
        PyMem_FREE(op-&gt;ob_item);
    }

    // 如果free_list还没满, PyListObject加入到列表中
    if (numfree &lt; PyList_MAXFREELIST &amp;&amp; PyList_CheckExact(op))
        free_list[numfree++] = op;
    else
        // free_list已经满了, 则回收内存
        Py_TYPE(op)-&gt;tp_free((PyObject *)op);

    Py_TRASHCAN_SAFE_END(op)
}
</code></pre>
<p>即</p>
<pre><code>对一个列表对象PyListObject, 回收时, ob_item会被回收, 其每个元素指向的对象引用-1.
但是PyListObject对象本身, 如果缓冲池未满, 会被放入缓冲池, 复用
</code></pre>
<p>缓冲池结构</p>
<p><img src="/imgs/python-source/PyListObjectPool.png" alt="PyListObjectPool"></p>
<h1 id="list的操作过程">List的操作过程</h1>
<h3 id="插入">插入</h3>
<pre><code>1. resize n+1
2. 确定插入点
3. 插入点后所有元素后移
4. 执行插入
</code></pre>
<p>示例</p>
<pre><code>&gt;&gt;&gt; a = [1, 2, 3]
&gt;&gt;&gt; a.insert(0, 9)
&gt;&gt;&gt; a
[9, 1, 2, 3]
</code></pre>
<h3 id="append">append</h3>
<pre><code>1. resize n+1
2. 放入最后一个位置(ob_size)
</code></pre>
<p>示例</p>
<pre><code>&gt;&gt;&gt; a = [1, 2, 3]
&gt;&gt;&gt; a.append(4)
&gt;&gt;&gt; a
[1, 2, 3, 4]
</code></pre>
<h3 id="extend">extend</h3>
<pre><code>1. 计算两个list大小 m n
2. resize m+n(此时本身被复制)
3. 遍历长度为n的数组, 从ob_item+m的位置开始加入
</code></pre>
<p>示例</p>
<pre><code>&gt;&gt;&gt; m = [1, 2, 3]
&gt;&gt;&gt; n = [4, 5]
&gt;&gt;&gt; m.extend(n)
&gt;&gt;&gt; m
[1, 2, 3, 4, 5]
</code></pre>
<h3 id="删除">删除</h3>
<pre><code>1. 找到要删除元素位置
2. 删除之, 后面元素前移
</code></pre>
<p>示例</p>
<pre><code>&gt;&gt;&gt; a = [1, 2, 3, 2]
&gt;&gt;&gt; a.remove(2)
&gt;&gt;&gt; a
[1, 3, 2]
</code></pre>
<hr>
<p>changelog</p>
<pre><code>2014-08-10 first version
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Python 源码阅读 - string</title>
			<link>https://wklken.me/posts/2014/08/08/python-source-string.html</link>
			<pubDate>Fri, 08 Aug 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/08/08/python-source-string.html</guid>
			<description>本周进展不大(去掉北上, 选择余地太小了), 下周开始投简历:( 这一章, 就一张图, 代码比较多 PyStringObject 源码位置 Include/stringobject.h | Objects/stringobject.c 定义 typedef struct { PyObject_VAR_HEAD long ob_shash; int ob_sstate; char ob_sval[1]; /* Invariants: * ob_sval contains space</description>
			<content type="html"><![CDATA[<p>本周进展不大(去掉北上, 选择余地太小了), 下周开始投简历:(</p>
<p>这一章, 就一张图, 代码比较多</p>
<hr>
<h1 id="pystringobject">PyStringObject</h1>
<p>源码位置 <a href="https://github.com/wklken/Python-2.7.8/blob/master/Include/stringobject.h">Include/stringobject.h</a> |
<a href="https://github.com/wklken/Python-2.7.8/blob/master/Objects/stringobject.c">Objects/stringobject.c</a></p>
<p>定义</p>
<pre><code>typedef struct {
  PyObject_VAR_HEAD
  long ob_shash;
  int ob_sstate;
  char ob_sval[1];

  /* Invariants:
   *     ob_sval contains space for 'ob_size+1' elements.
   *     ob_sval[ob_size] == 0.
   *     ob_shash is the hash of the string or -1 if not computed yet.
   *     ob_sstate != 0 iff the string object is in stringobject.c's
   *       'interned' dictionary; in this case the two references
   *       from 'interned' to this object are *not counted* in ob_refcnt.
   */
} PyStringObject;
</code></pre>
<p>说明</p>
<pre><code>1. PyObject_VAR_HEAD
PyStringObject是变长对象, 比定长对象多了一个ob_size字段

2. ob_shash
存储字符串的hash值, 如果还没计算等于-1
当string_hash被调用, 计算结果会被保存到这个字段一份, 后续不再进行计算

3. ob_sstate
如果是interned, !=0, 否则=0
interned后面说

4. char ob_sval[1]
字符指针指向一段内存, char数组指针, 指向一个ob_size+1大小数组(c中字符串最后要多一个字符`\0`表字符串结束)
</code></pre>
<p>结构</p>
<p><img src="/imgs/python-source/PyStringObject.png" alt="PyStringObject"></p>
<p>构造方法</p>
<pre><code>PyAPI_FUNC(PyObject *) PyString_FromString(const char *);

PyAPI_FUNC(PyObject *) PyString_FromStringAndSize(const char *, Py_ssize_t);
</code></pre>
<p>两个构造方法其实区别不大,</p>
<pre><code>PyString_FromStringAndSize 参数可以为`NULL`, 无论是否为`NULL`, 都会分配`size+1`个字节空间.(不为NULL的话字符数组会进行拷贝)

PyString_FromString, 参数不能为`NULL`, 且必须是`\0`结束的字符数组, 会调用c 语言string.h模块的strlen()函数计算字符串长度, 分配空间, 并将整个字符数组拷贝到ob_sval指向的内存
</code></pre>
<p>我们关注<code>PyString_FromString</code>就行</p>
<h1 id="创建过程-pystring_fromstring">创建过程 PyString_FromString</h1>
<p>定义</p>
<pre><code>//默认未初始化, 均为NULL
static PyStringObject *characters[UCHAR_MAX + 1];
static PyStringObject *nullstring;

PyObject *
PyString_FromString(const char *str)
{
    register size_t size;
    register PyStringObject *op;

    assert(str != NULL);

    // 计算参数字符数组长度
    size = strlen(str);

    // 超长, 报错(PY_SSIZE_T_MAX平台相关,32位2GB)
    if (size &gt; PY_SSIZE_T_MAX - PyStringObject_SIZE) {
        PyErr_SetString(PyExc_OverflowError,
            &quot;string is too long for a Python string&quot;);
        return NULL;
    }

    // 长度=0, 且nullstring已定义, 返回nullstring
    if (size == 0 &amp;&amp; (op = nullstring) != NULL) {
#ifdef COUNT_ALLOCS
        null_strings++;
#endif
        Py_INCREF(op);
        return (PyObject *)op;
    }

    // 字符缓冲池逻辑
    // 长度=1, 且characters[*str &amp; UCHAR_MAX]字符已定义
    if (size == 1 &amp;&amp; (op = characters[*str &amp; UCHAR_MAX]) != NULL) {
#ifdef COUNT_ALLOCS
        one_strings++;
#endif
        Py_INCREF(op);
        return (PyObject *)op;
    }

    // 申请内存空间
    /* Inline PyObject_NewVar */
    op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + size);
    if (op == NULL)
        return PyErr_NoMemory();

    // 初始化
    PyObject_INIT_VAR(op, &amp;PyString_Type, size);
    op-&gt;ob_shash = -1; //未计算hash, -1
    op-&gt;ob_sstate = SSTATE_NOT_INTERNED;  //未intern, 0
    // 将字符数组拷贝到PyStringObject
    Py_MEMCPY(op-&gt;ob_sval, str, size+1);


    // 在nullstring和字符缓冲池对应位置未初始化时, 会走到这个逻辑
    /* share short strings */
    if (size == 0) {
        PyObject *t = (PyObject *)op;
        // 走intern, 后面说
        PyString_InternInPlace(&amp;t);
        op = (PyStringObject *)t;

        // 初始化nullstring
        nullstring = op;
        Py_INCREF(op);
    } else if (size == 1) {
        PyObject *t = (PyObject *)op;
        // 走intern, 后面说
        PyString_InternInPlace(&amp;t);
        op = (PyStringObject *)t;

        // 初始化字符缓冲池对应位置
        characters[*str &amp; UCHAR_MAX] = op;
        Py_INCREF(op);
    }


    // 返回
    return (PyObject *) op;
}
</code></pre>
<p>步骤简化</p>
<pre><code>1. 计算长度
2. 长度0, 空字符串, 是返回已定义好的nullstring
3. 长度1, 字符, 返回字符缓冲池里面的
4. 都不是, 分配内存, 初始化
</code></pre>
<p>结论</p>
<pre><code>长度0/长度1, 会用到intern机制
注意, intern机制对长度&gt;1的字符串也适用
</code></pre>
<h1 id="interned机制">interned机制</h1>
<p>interned</p>
<pre><code>/* This dictionary holds all interned strings.  Note that references to
strings in this dictionary are *not* counted in the string's ob_refcnt.
When the interned string reaches a refcnt of 0 the string deallocation
function will delete the reference from this dictionary.

Another way to look at this is that to say that the actual reference
count of a string is:  s-&gt;ob_refcnt + (s-&gt;ob_sstate?2:0)
*/
static PyObject *interned; //指针, 指向PyDictObject
</code></pre>
<p>interned定义</p>
<pre><code>void
PyString_InternInPlace(PyObject **p)
{
    register PyStringObject *s = (PyStringObject *)(*p);

    PyObject *t;

    // 检查值使用在PyStringObject上, 派生类不适用
    if (s == NULL || !PyString_Check(s))
        Py_FatalError(&quot;PyString_InternInPlace: strings only please!&quot;);
    /* If it's a string subclass, we don't really know what putting it in the interned dict might do. */

    // 不是字符串类型, 返回
    if (!PyString_CheckExact(s))
        return;
    // 本身已经intern了(标志位ob_sstate), 不重复进行, 返回
    if (PyString_CHECK_INTERNED(s))
        return;

    // 未初始化字典, 初始化之
    if (interned == NULL) {
        // 注意这里
        interned = PyDict_New();
        if (interned == NULL) {
            PyErr_Clear(); /* Don't leave an exception */
            return;
        }
    }

    // 在interned字典中已存在, 修改, 返回intern独享
    t = PyDict_GetItem(interned, (PyObject *)s);
    if (t) {
        Py_INCREF(t);
        Py_DECREF(*p);
        *p = t;
        return;
    }

    // 在interned字典中不存在, 放进去
    if (PyDict_SetItem(interned, (PyObject *)s, (PyObject *)s) &lt; 0) {
        PyErr_Clear();
        return;
    }

    // 加入interned字典(key-value), 会导致refcnt+2, 去掉, 保证当外部没有引用时, refcnt=0, 可以进行回收处理, (不-2, refcnt永远&gt;=2)
    /* The two references in interned are not counted by refcnt.
       The string deallocator will take care of this */
    Py_REFCNT(s) -= 2;

    // 修改字符串对象的ob_sstate标志位, SSTATE_INTERNED_MORTAL
    PyString_CHECK_INTERNED(s) = SSTATE_INTERNED_MORTAL;
}
</code></pre>
<p>使用的地方</p>
<pre><code>// 构造方法
PyAPI_FUNC(PyObject *) PyString_FromString(const char *);

PyAPI_FUNC(PyObject *) PyString_FromStringAndSize(const char *, Py_ssize_t);


// SSTATE_INTERNED_MORTAL, 计数0会被回收
PyObject *
PyString_InternFromString(const char *cp)
{
    PyObject *s = PyString_FromString(cp);
    if (s == NULL)
        return NULL;
    PyString_InternInPlace(&amp;s);
    return s;
}


// SSTATE_INTERNED_IMMORTAL, 永远不会被销毁
void
PyString_InternImmortal(PyObject **p)
</code></pre>
<p>示例</p>
<pre><code>&gt;&gt;&gt; a = ''
&gt;&gt;&gt; b = ''
&gt;&gt;&gt; id(a) == id(b)
True

&gt;&gt;&gt; a = 'x'
&gt;&gt;&gt; b = 'x'
&gt;&gt;&gt; id(a) == id(b)
True

&gt;&gt;&gt; a = &quot;abc&quot;
&gt;&gt;&gt; b = &quot;abc&quot;
&gt;&gt;&gt; id(a) == id(b)
True
</code></pre>
<p>python源代码自己也大量使用</p>
<pre><code>dict_str = PyString_InternFromString(&quot;__dict__&quot;)
lenstr = PyString_InternFromString(&quot;__len__&quot;)
s_true = PyString_InternFromString(&quot;true&quot;)
empty_array = PyString_InternFromString(&quot;[]&quot;)
</code></pre>
<p>好处</p>
<pre><code>一旦字符串被intern, 会被python保存到字典中, 整个python运行期间, 系统中有且仅有一个对象. 下次相同字符串再进入, 不会重复创建对象.
</code></pre>
<h1 id="字符缓冲池">字符缓冲池</h1>
<p>定义</p>
<pre><code>UCHAR_MAX 平台相关

static PyStringObject *characters[UCHAR_MAX + 1];
</code></pre>
<p>在上面<code>PyString_FromString</code>可以看到, 字符缓冲池在使用中初始化(存在直接返回, 不存在建一个, 放interned字典中, 初始化字符缓冲池对应位置)</p>
<pre><code>PyObject *t = (PyObject *)op;
// 走intern, 后面说
PyString_InternInPlace(&amp;t);
op = (PyStringObject *)t;

// 初始化字符缓冲池对应位置
characters[*str &amp; UCHAR_MAX] = op;
</code></pre>
<h1 id="字符串销毁过程">字符串销毁过程</h1>
<pre><code>static void
string_dealloc(PyObject *op)
{
    // 是否intern
    switch (PyString_CHECK_INTERNED(op)) {
        // 非, 跳出 -&gt; 回收内存
        case SSTATE_NOT_INTERNED:
            break;

        // 普通, 从interned字典中删除, 跳出 -&gt; 回收内存
        case SSTATE_INTERNED_MORTAL:
            /* revive dead object temporarily for DelItem */
            Py_REFCNT(op) = 3;
            if (PyDict_DelItem(interned, op) != 0)
                Py_FatalError(
                    &quot;deletion of interned string failed&quot;);
            break;
        // 永不回收的对象, 报错
        case SSTATE_INTERNED_IMMORTAL:
            Py_FatalError(&quot;Immortal interned string died.&quot;);

        default:
            Py_FatalError(&quot;Inconsistent interned string state.&quot;);
    }

    // 回收内存
    Py_TYPE(op)-&gt;tp_free(op);
}
</code></pre>
<h1 id="性能相关">性能相关</h1>
<p><code>+</code>与 <code>join</code></p>
<pre><code>'a' + 'b' + 'c'

or

''.join(['a', 'b', 'c'])
</code></pre>
<p>可以查看<code>string_concat</code>方法和<code>string_join</code>方法的源代码</p>
<pre><code>string_concat, 一次加=分配一次内存空间, 拷贝两次. N次链接, 需要N-1次内存分配.
string_join, 计算序列所有元素字符串总长度, 用PyString_FromStringAndSize((char*)NULL, sz)分配内存空间, 然后逐一拷贝. 一次内存操作.
</code></pre>
<hr>
<p>changelog</p>
<pre><code>2014-08-08 first version
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Python 源码阅读 - int</title>
			<link>https://wklken.me/posts/2014/08/06/python-source-int.html</link>
			<pubDate>Wed, 06 Aug 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/08/06/python-source-int.html</guid>
			<description>========================== 代码我也仅仅是粗粗读了一遍, 可能出现疏漏和理解错误, 发现了望指出哈. 今天面了一家靠谱的创业公司, 可惜不是Python向的, 想继续玩Pyth</description>
			<content type="html"><![CDATA[<p>==========================</p>
<p>代码我也仅仅是粗粗读了一遍, 可能出现疏漏和理解错误, 发现了望指出哈.</p>
<p>今天面了一家靠谱的创业公司, 可惜不是Python向的, 想继续玩Python是有代价的, 选择余地太窄了&hellip;&hellip;</p>
<p>话说写文章很耗时间, 这个花了两个多小时&hellip;.主要还是自个绘图渣效率低:(</p>
<p>准备找工作事宜很占时间, 后面只能慢慢来了(好像还很多很多的样子)</p>
<hr>
<p>示例</p>
<pre><code>&gt;&gt;&gt; a = 1
&gt;&gt;&gt; b = 1
&gt;&gt;&gt; id(a) == id(b)
True

&gt;&gt;&gt; c = 257
&gt;&gt;&gt; d = 257
&gt;&gt;&gt; id(c) == id(d)
False

#在python2.x中, 对于大的序列生成, 建议使用xrange(100000) 而不是range(100000), why?
</code></pre>
<p>源码位置 <a href="https://github.com/wklken/Python-2.7.8/blob/master/Include/intobject.h">Include/intobject.h</a> |
<a href="https://github.com/wklken/Python-2.7.8/blob/master/Objects/intobject.c">Objects/intobject.c</a></p>
<hr>
<h3 id="pyintobject">PyIntObject</h3>
<pre><code>typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;
</code></pre>
<p>结构</p>
<p><img src="/imgs/python-source/PyIntObject.png" alt="PyIntObject"></p>
<hr>
<h3 id="几个构造方法">几个构造方法</h3>
<pre><code># 从字符串, 生成PyIntObject对象
PyAPI_FUNC(PyObject *) PyInt_FromString(char*, char**, int);

# 从Py_UNICODE, 生成PyIntObject对象
#ifdef Py_USING_UNICODE
PyAPI_FUNC(PyObject *) PyInt_FromUnicode(Py_UNICODE*, Py_ssize_t, int);
#endif

# 从long值, 生成PyIntObject对象
PyAPI_FUNC(PyObject *) PyInt_FromLong(long);

PyAPI_FUNC(PyObject *) PyInt_FromSize_t(size_t);
PyAPI_FUNC(PyObject *) PyInt_FromSsize_t(Py_ssize_t);
</code></pre>
<p>这几个方法, 只需要关注</p>
<pre><code># 因为大家最后都调用这个方法完成对象生成
PyAPI_FUNC(PyObject *) PyInt_FromLong(long);
</code></pre>
<hr>
<h3 id="具体的构造方法-pyint_fromlong">具体的构造方法 PyInt_FromLong</h3>
<p>这个方法的定义</p>
<pre><code>PyObject *
PyInt_FromLong(long ival)
{
    register PyIntObject *v;

    /* MARK: 如果, 值在小整数范围内, 直接从小整数对象池获取得到对象 */

    #if NSMALLNEGINTS + NSMALLPOSINTS &gt; 0
    if (-NSMALLNEGINTS &lt;= ival &amp;&amp; ival &lt; NSMALLPOSINTS) {

        /* MARK: small_ints是什么后面说 */
        v = small_ints[ival + NSMALLNEGINTS];
        // 引用+1
        Py_INCREF(v);

        /* 这里先忽略, 计数 */
        #ifdef COUNT_ALLOCS
            if (ival &gt;= 0)
                quick_int_allocs++;
            else
                quick_neg_int_allocs++;
        #endif

        // 返回
        return (PyObject *) v;
    }
    #endif

    // 如果free_list还不存在, 或者满了
    if (free_list == NULL) {
        // 新建一块PyIntBlock, 并将空闲空间链表头部地址给free_list
        if ((free_list = fill_free_list()) == NULL)
            // 如果失败, 返回
            return NULL;
    }

    // 从free_list分出一个位置存放新的整数

    /* Inline PyObject_New */
    // 使用单向链表头位置
    v = free_list;

    // free_list指向单向链表下一个位置
    free_list = (PyIntObject *)Py_TYPE(v);

    // 初始化对象, 类型为PyInt_type, 值为ival
    PyObject_INIT(v, &amp;PyInt_Type);
    v-&gt;ob_ival = ival;

    // 返回
    return (PyObject *) v;
}
</code></pre>
<p>注意这里的<code>Py_TYPE()</code>方法, 在我们<a href="http://www.wklken.me/posts/2014/08/05/python-source-object.html">第一篇文章</a>里面有提到, 不知道的回去复习下对象的数据结构</p>
<pre><code>#define Py_TYPE(ob)             (((PyObject*)(ob))-&gt;ob_type)
</code></pre>
<p>简而言之:</p>
<pre><code>1. 先判断数值是否是小整数, 是的话从小整数对象池里面直接返回
(这个池固定大小, 下一点讲)

2. 如果不是, 从通用整数对象池里面取一个, 初始化返回
(如果这时候通用整数对象池还不存在或者已经满了, 新建一个池加入维护. 通用整数对象池后面讲)
</code></pre>
<hr>
<h3 id="小整数对象池">小整数对象池</h3>
<p>先看定义</p>
<pre><code>#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS           257
#endif

#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS           5
#endif

#if NSMALLNEGINTS + NSMALLPOSINTS &gt; 0
/* References to small integers are saved in this array
   so that they can be shared.
   The integers that are saved are those in the range
   -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/

static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
#endif
</code></pre>
<p>其实, 小整数对象池就是一个<code>PyIntObject指针</code>数组(注意是指针数组), 大小=257+5=262, 范围是<code>[-5, 257)</code> 注意左闭右开. 即这个数组包含了262个指向PyIntObject的指针.</p>
<p>结构</p>
<p><img src="/imgs/python-source/PyInt_smallints.png" alt="small_ints"></p>
<p>创建整数时, 如果在[-5, 257)范围, 直接返回已经存在的整数对象指针, 所以我们看到开头的例子, id比较一个true/一个false</p>
<p>小整数对象池, 在一开始就初始化了, 其初始化代码</p>
<pre><code>int
_PyInt_Init(void)
{
    PyIntObject *v;
    int ival;

    // 注意这里, free_list再次出现

#if NSMALLNEGINTS + NSMALLPOSINTS &gt; 0

    // 循环, 逐一生成
    for (ival = -NSMALLNEGINTS; ival &lt; NSMALLPOSINTS; ival++) {
          if (!free_list &amp;&amp; (free_list = fill_free_list()) == NULL)
                  return 0;

        // 注意这段代码, 和上面PyInt_FromLong那段代码一样的
        /* PyObject_New is inlined */
        v = free_list;
        free_list = (PyIntObject *)Py_TYPE(v);
        PyObject_INIT(v, &amp;PyInt_Type);
        v-&gt;ob_ival = ival;

        // 放到数组里
        small_ints[ival + NSMALLNEGINTS] = v;
    }
#endif

    return 1;
}
</code></pre>
<p>代码很眼熟吧, 觉得不眼熟回上面看代码</p>
<p>结论</p>
<pre><code>1. 小整数对象池缓存 [-5, 257) 内的整数对象, 数值在这个范围的整数对象有且只存在一个...

2. 小整数对象池, 只是一个指针数组, 其真正对象依赖通用整数对象池
</code></pre>
<hr>
<h3 id="通用整数对象池1---基础结构pyintblock">通用整数对象池1 - 基础结构PyIntBlock</h3>
<p>首先, 有个数据结构PyIntBlock</p>
<pre><code>#define BLOCK_SIZE      1000    /* 1K less typical malloc overhead */
#define BHEAD_SIZE      8       /* Enough for a 64-bit pointer */
#define N_INTOBJECTS    ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))


struct _intblock {
    struct _intblock *next;
    PyIntObject objects[N_INTOBJECTS];
};

typedef struct _intblock PyIntBlock;
</code></pre>
<p>回忆一下<code>PyIntObject</code>结构(1个int, 1指针, 1个long), size=4+4+4(先这么算), N_INTOBJECTS = 82</p>
<p>结构</p>
<p><img src="/imgs/python-source/PyIntBlock.png" alt="PyIntBlock"></p>
<h3 id="通用整数对象池2---创建过程及运行时结构">通用整数对象池2 - 创建过程及运行时结构</h3>
<p>有两个指针</p>
<pre><code># 指向一个block
static PyIntBlock *block_list = NULL;

# 指向一个PyIntObject
static PyIntObject *free_list = NULL;
</code></pre>
<p>生成过程的定义</p>
<pre><code>// 初始化一个PyIntBlock
static PyIntObject *
fill_free_list(void)
{
    PyIntObject *p, *q;
    // 建立一个新的block
    /* Python's object allocator isn't appropriate for large blocks. */
    p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));

    // 建立失败(内存耗光了)
    if (p == NULL)
        return (PyIntObject *) PyErr_NoMemory();

    // block_list指向新的PyIntBlock节点
    ((PyIntBlock *)p)-&gt;next = block_list;
    block_list = (PyIntBlock *)p;

    /* Link the int objects together, from rear to front, then return
       the address of the last int object in the block. */

    // p=block里面 PyIntObjects数组头地址, q是尾地址
    p = &amp;((PyIntBlock *)p)-&gt;objects[0];
    q = p + N_INTOBJECTS;

    // 从尾部开始向首部移动, 利用对象里的ob_type指针(相当于使用这个字段, ob_type不作为原来的用途), 建立起一个单向链表
    // 这个单向链表的头部是数组的最后一个
    while (--q &gt; p)
        Py_TYPE(q) = (struct _typeobject *)(q-1);
    Py_TYPE(q) = NULL; // 单向链表最后一个元素的next指向null

    // 返回单向链表的头地址!!!
    return p + N_INTOBJECTS - 1;

}
</code></pre>
<p>新建第一个时, 只有一个</p>
<p><img src="/imgs/python-source/PyIntBlock1.png" alt="PyIntBlock"></p>
<p>从里面拿整数时, 取<code>free_list</code>指向的节点, 然后<code>free_list</code>指向链表下一个节点</p>
<p>当一个block用完了之后, 即<code>free_list=NULL</code>, 此时要新建另一个PyIntBlock</p>
<p>新建第二个</p>
<p><img src="/imgs/python-source/PyIntBlock2.png" alt="PyIntBlock"></p>
<h3 id="通用整数对象池3---删除一个整数时">通用整数对象池3 - 删除一个整数时</h3>
<p>定义</p>
<pre><code>#define PyInt_CheckExact(op) ((op)-&gt;ob_type == &amp;PyInt_Type)

static void
int_dealloc(PyIntObject *v)
{
    // 是整数类型, 将对象放入free_list单向链表头
    if (PyInt_CheckExact(v)) {
        Py_TYPE(v) = (struct _typeobject *)free_list;
        free_list = v;
    }
    else
        Py_TYPE(v)-&gt;tp_free((PyObject *)v); //不是整数类型, 对应类型析构
}
</code></pre>
<p>可以看到, 回收的时候, 把空间给放回到<code>free_list</code>了, 后面接着用</p>
<p><code>block_list</code>维护着所有<code>PyIntBlock</code>列表, 查看源码注释可以看到</p>
<pre><code>PyIntBlocks are never returned to the
 system before shutdown (PyInt_Fini).
</code></pre>
<p>即, <code>PyIntBlock</code>申请的所有内存, 在Python结束之前, 都不会被释放</p>
<pre><code>所以, 使用range(100000), 运行后, 虽然程序结束了, 但是整数占用空间还在.

建议对大范围的序列生成使用xrange

python3.x不用担心这个问题
</code></pre>
<hr>
<p>changelog</p>
<pre><code>2014-08-07 first version
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Python 源码阅读 - 类型</title>
			<link>https://wklken.me/posts/2014/08/05/python-source-type.html</link>
			<pubDate>Tue, 05 Aug 2014 19:31:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/08/05/python-source-type.html</guid>
			<description>这篇主要涉及Python对象的类型机制 有点绕, 一定要思维清晰的时候再看哦:) 一个例子 &amp;gt;&amp;gt;&amp;gt; a = 1 &amp;gt;&amp;gt;&amp;gt; a 1 &amp;gt;&amp;gt;&amp;gt; type(a) &amp;lt;type &#39;int&#39;&amp;gt; #等价的两个 &amp;gt;&amp;gt;&amp;gt; type(type(a)) &amp;lt;type &#39;type&#39;&amp;gt; &amp;gt;&amp;gt;&amp;gt; type(int) &amp;lt;type &#39;type&#39;&amp;gt; #还是</description>
			<content type="html"><![CDATA[<p>这篇主要涉及Python对象的类型机制</p>
<p>有点绕, 一定要思维清晰的时候再看哦:)</p>
<hr>
<p>一个例子</p>
<pre><code>&gt;&gt;&gt; a = 1
&gt;&gt;&gt; a
1

&gt;&gt;&gt; type(a)
&lt;type 'int'&gt;

#等价的两个
&gt;&gt;&gt; type(type(a))
&lt;type 'type'&gt;
&gt;&gt;&gt; type(int)
&lt;type 'type'&gt;

#还是等价的两个
&gt;&gt;&gt; type(type(type(a)))
&lt;type 'type'&gt;
&gt;&gt;&gt; type(type(int))
&lt;type 'type'&gt;
</code></pre>
<p>我们反向推导一个<code>int</code>对象是怎么生成的.</p>
<hr>
<h3 id="1-首先-定义一种类型叫pytypeobject">1. 首先, 定义一种类型叫PyTypeObject</h3>
<p>代码位置 <a href="https://github.com/wklken/Python-2.7.8/blob/master/Include/object.h">Include/object.h</a></p>
<p>定义</p>
<pre><code> typedef struct _typeobject {

  /* MARK: base, 注意, 是个变长对象*/
  PyObject_VAR_HEAD
  const char *tp_name; /* For printing, in format &quot;&lt;module&gt;.&lt;name&gt;&quot; */ //类型名
  Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */ // 创建该类型对象时分配的内存空间大小


  // 一堆方法定义, 函数和指针
  /* Methods to implement standard operations */
  printfunc tp_print;
  hashfunc tp_hash;

  /* Method suites for standard classes */
  PyNumberMethods *tp_as_number;   // 数值对象操作
  PySequenceMethods *tp_as_sequence; // 序列对象操作
  PyMappingMethods *tp_as_mapping; // 字典对象操作

  // 一堆属性定义
  ....

} PyTypeObject;
</code></pre>
<p>说明</p>
<pre><code>1. PyObject_VAR_HEAD
变长对象

2. const char *tp_name
tp_name, 类型名字符串数组
</code></pre>
<p>所有Type都是PyTypeObject的&quot;实例&quot;: PyType_Type/PyInt_Type</p>
<hr>
<h3 id="2-然后-用pytypeobject初始化得到一个对象pytype_type">2. 然后, 用PyTypeObject初始化得到一个对象PyType_Type</h3>
<p>代码位置 <a href="https://github.com/wklken/Python-2.7.8/blob/master/Objects/typeobject.c">Objects/typeobject.c</a></p>
<p>定义</p>
<pre><code>PyTypeObject PyType_Type = {
  PyVarObject_HEAD_INIT(&amp;PyType_Type, 0)
  &quot;type&quot;,                                     /* tp_name */
  sizeof(PyHeapTypeObject),                   /* tp_basicsize */
  sizeof(PyMemberDef),                        /* tp_itemsize */
  (destructor)type_dealloc,                   /* tp_dealloc */

  // type对象的方法和属性初始化值
  .....

};
</code></pre>
<p>说明</p>
<pre><code>1. tp_name
类型名, 这里是&quot;type&quot;

2. PyVarObject_HEAD_INIT(&amp;PyType_Type, 0)
PyVarObject_HEAD_INIT, 这个方法在 Include/object.h中,
等价于
        ob_refcnt = 1
        *ob_type = &amp;PyType_Type
        ob_size = 0

即, PyType_Type的类型是其本身!
</code></pre>
<p>结构</p>
<p>第一张图, 箭头表示<code>实例化</code>(google doc用不是很熟找不到对应类型的箭头)
<img src="/imgs/python-source/PyType_Type.png" alt="PyType_Type"></p>
<p>第二张图, 箭头表示<code>指向</code>
<img src="/imgs/python-source/PyType_Type2.png" alt="PyType_Type2"></p>
<p>使用</p>
<pre><code># 1. int 的 类型 是`type`
&gt;&gt;&gt; type(int)
&lt;type 'type'&gt;

# 2. type 的类型 还是`type`, 对应上面说明第二点
&gt;&gt;&gt; type(type(int))
&lt;type 'type'&gt;
</code></pre>
<p>注意: 无论任何时候, ob_type指向的是 PyTypeObject的实例: PyType_Type/PyInt_Type&hellip;</p>
<hr>
<h3 id="3-再然后-定义具体的类型-这里以pyint_type为例子">3. 再然后, 定义具体的类型, 这里以PyInt_Type为例子</h3>
<p>代码位置 <a href="https://github.com/wklken/Python-2.7.8/blob/master/Objects/intobject.c">Objects/intobject.c</a></p>
<p>定义</p>
<pre><code>PyTypeObject PyInt_Type = {
  PyVarObject_HEAD_INIT(&amp;PyType_Type, 0)
  &quot;int&quot;,
  sizeof(PyIntObject),
  0,

  // int类型的相关方法和属性值
  ....

  (hashfunc)int_hash,                         /* tp_hash */

};
</code></pre>
<p>说明</p>
<pre><code>1. &quot;int&quot;
PyInt_Type的类型名是int

2.PyVarObject_HEAD_INIT(&amp;PyType_Type, 0)
PyInt_Type的

    *ob_type = &amp;PyType_Type
</code></pre>
<p>结构</p>
<p><img src="/imgs/python-source/PyType_Type3.png" alt="PyType_Type2"></p>
<p>使用</p>
<pre><code>&gt;&gt;&gt; type(1)
&lt;type 'int'&gt;

&gt;&gt;&gt; type(type(1))
&lt;type 'type'&gt;
</code></pre>
<hr>
<h3 id="4-最后-生成一个整数对象int">4. 最后, 生成一个整数对象int</h3>
<p>代码位置 <a href="https://github.com/wklken/Python-2.7.8/blob/master/Include/intobject.h">Include/intobject.h</a></p>
<p>定义</p>
<pre><code>typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;
</code></pre>
<p>结构</p>
<p><img src="/imgs/python-source/PyType_Type4.png" alt="PyType_Type2"></p>
<pre><code>1. PyIntObject为整数类型

2. 声明一个整数后得到整数对象

3. 对象ob_type指向PyInt_type对象
</code></pre>
<hr>
<h3 id="到这里-总结下">到这里, 总结下</h3>
<pre><code> 1. 一切都是对象

 2. PyType_Type / PyInt_Type / PyString_Type ....等
 这些是`类型对象`, 可以认为是同级, 都是PyTypeObject这种`类型`的实例!

 3. 虽然是同级,
 但是其他PyXXX_Type, 其类型指向 PyType_Type
 PyType_Type 的类型指向自己, 它是所有类型的`类型`

 4. PyTypeObject 是一个变长对象

 5. 每个object, 例如PyIntObject都属于一种`类型`
 object初始化时进行关联
</code></pre>
<hr>
<h3 id="多态是如何实现的">多态是如何实现的?</h3>
<p>对象的多态, 例如hash</p>
<pre><code>&gt;&gt;&gt; hash(1)
1
&gt;&gt;&gt; hash(&quot;abc&quot;)
1453079729188098211
</code></pre>
<p>从上面数据结构可以看到, 方法及属性, 在不同Type实例化时就确定了</p>
<pre><code>PyTypeObject PyInt_Type = {
    ...
    (hashfunc)int_hash,                         /* tp_hash */
    ...
}


PyTypeObject PyString_Type = {
    ...
    (hashfunc)string_hash,                      /* tp_hash */
    ...
}
</code></pre>
<p>Python内部传递的是泛型指针<code>PyObject *</code>, 函数调用时, 找到其类型<code>* ob_type</code>, 然后调用</p>
<pre><code>object -&gt; ob_type -&gt; tp_hash
</code></pre>
<p>即: 大量函数指针决定了该类型的具体行为</p>
<hr>
<p>changelog</p>
<pre><code>2014-08-05 first version
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Python 源码阅读 - 对象</title>
			<link>https://wklken.me/posts/2014/08/05/python-source-object.html</link>
			<pubDate>Tue, 05 Aug 2014 17:32:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/08/05/python-source-object.html</guid>
			<description>结束休息期, 开始准备面试事宜了, 发现要准备的东西好多(╯‵□′)╯︵┻━┻, 顺手整理下Python源码阅读笔记(力争在写完前找到工作) 参考内</description>
			<content type="html"><![CDATA[<p>结束休息期, 开始准备面试事宜了, 发现要准备的东西好多(╯‵□′)╯︵┻━┻,  顺手整理下Python源码阅读笔记(力争在写完前找到工作)</p>
<p>参考内容: Python源码 + <code>&lt;&lt;Python源码剖析&gt;&gt;</code></p>
<p>用<code>源代码+说明+图解</code>的方式, 尽量说明白吧, 有一份注释在github上</p>
<p>内容:</p>
<pre><code>-&gt; 对象/类型
-&gt; 各种内建类型对象及机制
-&gt; 虚拟机相关(编译及执行)
-&gt; 内存管理/动态加载/多线程等
</code></pre>
<p>PS: 画图的话google doc很好用</p>
<p>好的, 第一篇开始</p>
<hr>
<blockquote>
<p>一切皆为对象</p>
</blockquote>
<p>对象, 在C语言是如何实现的?</p>
<p>Python中对象分为两类: 定长(int等), 非定长(list/dict等)</p>
<p>所有对象都有一些相同的东西, 源码中定义为<code>PyObject</code>和<code>PyVarObject</code>, 两个定义都有一个共同的头部定义<code>PyObject_HEAD</code>(其实PyVarObject有自己的头部定义<code>PyObject_VAR_HEAD</code>, 但其实际上用的也是<code>PyObject_HEAD</code>).</p>
<p>源码位置: <a href="https://github.com/wklken/Python-2.7.8/blob/master/Include/object.h">Include/object.h</a></p>
<h3 id="pyobject_head">PyObject_HEAD</h3>
<p>Python 内部, 每个对象拥有相同的头部.</p>
<p>定义</p>
<pre><code>/* PyObject_HEAD defines the initial segment of every PyObject. */
#define PyObject_HEAD                   \
    _PyObject_HEAD_EXTRA                \
    Py_ssize_t ob_refcnt;               \
    struct _typeobject *ob_type;
</code></pre>
<p>说明</p>
<pre><code>1. _PyObject_HEAD_EXTRA
先忽略, 双向链表结构, 后面垃圾回收再说

2. Py_ssize_t ob_refcnt
Py_ssize_t在编译时确定, 整型
ob_refcnt, 引用计数, 跟Python的内存管理机制相关(基于引用计数的垃圾回收)

3. struct _typeobject *ob_type
*ob_type 指向类型对象的指针(指向_typeobject结构体)
决定了这个对象的类型!
</code></pre>
<h3 id="pyobject">PyObject</h3>
<p>定义</p>
<pre><code> typedef struct _object {
     PyObject_HEAD
 } PyObject;
</code></pre>
<p>说明</p>
<pre><code> 1. 依赖关系
 PyObject -&gt; PyObject_HEAD
</code></pre>
<p>结构</p>
<p><img src="/imgs/python-source/PyObject.png" alt="PyObject"></p>
<h3 id="pyvarobject">PyVarObject</h3>
<p>定义</p>
<pre><code>typedef struct {
    PyObject_VAR_HEAD
} PyVarObject;

#define PyObject_VAR_HEAD               \
  PyObject_HEAD                       \
  Py_ssize_t ob_size; /* Number of items in variable part */
</code></pre>
<p>说明</p>
<pre><code> 1. 依赖关系
 PyVarObject -&gt; PyObject_VAR_HEAD -&gt; PyObject_HEAD

 2.Py_ssize_t ob_size
 ob_size, 变长对象容纳的元素个数
</code></pre>
<p>结构</p>
<p><img src="/imgs/python-source/PyVarObject.png" alt="PyVarObject"></p>
<h3 id="代码关系">代码关系</h3>
<p><img src="/imgs/python-source/PyObjectCode.png" alt="PyObjectCode"></p>
<h3 id="几个方法">几个方法</h3>
<p>跟对象相关的方法</p>
<pre><code>#define Py_REFCNT(ob)           (((PyObject*)(ob))-&gt;ob_refcnt)
读取引用计数

#define Py_TYPE(ob)             (((PyObject*)(ob))-&gt;ob_type)
获取对象类型

#define Py_SIZE(ob)             (((PyVarObject*)(ob))-&gt;ob_size)
读取元素个数(len)
</code></pre>
<p>跟引用计数相关的方法</p>
<pre><code>Py_INCREF(op)  增加对象引用计数

Py_DECREF(op)  减少对象引用计数, 如果计数位0, 调用_Py_Dealloc

_Py_Dealloc(op) 调用对应类型的 tp_dealloc 方法(每种类型回收行为不一样的, 各种缓存池机制, 后面看)
</code></pre>
<h3 id="其他">其他</h3>
<p>几个参数涉及</p>
<pre><code>ob_refcnt 引用计数, 与内存管理/垃圾回收相关
ob_type   类型, 涉及Python的类型系统
</code></pre>
<hr>
<p>changelog:</p>
<pre><code>2013-08-05 first version
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Python后端相关技术/工具栈</title>
			<link>https://wklken.me/posts/2014/07/26/python-tech-stack.html</link>
			<pubDate>Sat, 26 Jul 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/07/26/python-tech-stack.html</guid>
			<description>整理下目前涉及到的python的技术栈和工具栈(用过或了解的, 其他的后续用到再补充) 编辑器 最常见: vim / SublimeText2 / PyCharm Vim有兴趣可以看看 k-vim 适合Pyt</description>
			<content type="html"><![CDATA[<p>整理下目前涉及到的python的技术栈和工具栈(用过或了解的, 其他的后续用到再补充)</p>
<p><img src="/imgs/python/python-logo.png" alt="python"></p>
<ul>
<li>编辑器</li>
</ul>
<p>最常见: vim / <a href="http://www.sublimetext.com/2">SublimeText2</a> / <a href="http://www.jetbrains.com/pycharm/">PyCharm</a></p>
<p>Vim有兴趣可以看看 <a href="https://github.com/wklken/k-vim">k-vim</a> 适合Python/Golang开发</p>
<ul>
<li>本地环境
pip/easy_install 包管理</li>
</ul>
<p><a href="http://virtualenv.readthedocs.org/en/latest/">viertualenv</a> + <a href="http://virtualenvwrapper.readthedocs.org/en/latest/">virtualenvwrapper</a> 库/版本管理, 环境隔离</p>
<p>ipython/ipdb</p>
<ul>
<li>Web 框架
Python 的Web 框架非常多&hellip;&hellip;</li>
</ul>
<p>个人偏好[有分先后]</p>
<p><a href="http://flask.pocoo.org/">flask</a> 轻量! 可以灵活组合各类组件进行开发(第三方组件很丰富), 简单高效, 便于快速开发和维护.</p>
<p><a href="http://www.tornadoweb.org/en/stable/">tornado</a> 异步, 高性能, 最新版本4.0</p>
<p><a href="https://www.djangoproject.com/">django</a> 有些重, 配置和约定众多, 可以快速开发一些&quot;管理&quot;性质的后台, 其版本更新非常快</p>
<p>其他:</p>
<p><a href="http://bottlepy.org/docs/dev/index.html">bottle</a> 类flask, 一个文件, 足够小</p>
<ul>
<li>ORM
<a href="http://www.sqlalchemy.org/">SQLAlchemy</a></li>
</ul>
<blockquote>
<p>SQLAlchemy is the Python SQL toolkit and Object Relational Mapper that gives application developers the full power and flexibility of SQL.</p>
</blockquote>
<p>似乎这个就足够了, 无出其右</p>
<ul>
<li>模板</li>
</ul>
<p>只提第三方(很多框架自带了, 例如Django/Tornado), 话说天下模板都大同小异</p>
<p><a href="http://jinja.pocoo.org/docs/">Jinja2</a>, 最喜欢的一个, 之前写了篇 <a href="http://www.wklken.me/posts/2013/12/21/python-template-jinja2.html">PYTHON模板-JINJA</a></p>
<p><a href="http://www.makotemplates.org/">Mako</a>, 介绍<a href="http://www.wklken.me/posts/2013/12/14/python-template-mako.html">PYTHON模板-MAKO</a></p>
<p>还有挺多的, 后续了解后补充</p>
<ul>
<li>代码管理及Wiki</li>
</ul>
<p>使用git, 搭建<a href="https://about.gitlab.com/">gitlab</a>
gilt针对项目级别, 可以用markdown写一些文档, 可以简单的code review, 可以进行讨论等等.</p>
<p>Wiki的选择:</p>
<p><a href="https://github.com/gollum/gollum">gollum</a> 很赞</p>
<p><a href="http://trac.edgewall.org/">trac</a>老牌, 但是个人不大喜欢其语法编辑方式.</p>
<ul>
<li>代码发布</li>
</ul>
<p>fabric + rsync</p>
<p><a href="http://rsync.samba.org/">rsync</a> 用于生产代码目录的同步, 足够简单强大</p>
<p><a href="http://www.fabfile.org/">fabric</a> 用于自动化部署, 将流程固化到脚本中重复使用, 提高效率降低风险, <a href="http://www.wklken.me/posts/2013/03/25/python-tool-fabric.html">PYTHON FABRIC实现远程操作和部署</a></p>
<blockquote>
<p>Fabric is a Python (2.5-2.7) library and command-line tool for streamlining the use of SSH for application deployment or systems administration tasks.</p>
</blockquote>
<ul>
<li>项目部署</li>
</ul>
<p><a href="http://nginx.org/cn/">nginx</a> 服务器, 主要用于负载均衡, 反代等</p>
<p><a href="http://gunicorn.org/">gunicorn</a> a Python WSGI HTTP Server for UNIX, 用来运行Flask项目</p>
<p><a href="http://supervisord.org/">Supervisor</a> A Process Control System, 配置管理各种程序, 进程监控, 自动重启等</p>
<ul>
<li>监控</li>
</ul>
<p><a href="http://www.nagios.org/">nagios</a> 服务器各类参数监控, 负载,内存,IO, 网络等, 也可以配置对进程进行监控报警</p>
<p><a href="https://getsentry.com/welcome/">sentry</a> 实时收集事件日志, 可以在管理后台查看到当前程序异常及报错具体信息, 很强大的平台, 支持多种语言的程序</p>
<p>statsd + graphic</p>
<p><a href="https://github.com/etsy/statsd">statsd</a> 实时服务数据收集(e.g. counters and timers) 收集后数据提供给graphic进行展示</p>
<p><a href="http://graphite.wikidot.com/">graphic</a> 企业级开源监控工具(数据绘图工具)，用于采集服务器实时信息并进行统计, Graphite 自己本身并不收集具体的数据，这些数据收集的具体工作通常由第三方工具或插件完成. 其控制台前端项目 <a href="https://github.com/urbanairship/tessera">tessera</a></p>
<ul>
<li>NOSQL</li>
</ul>
<p><a href="http://redis.io/">redis</a> 缓存/持久化/特殊需求(计数-排行榜-时间线等)</p>
<p><a href="http://memcached.org/">memcached</a> 集群, 多用于有时限性质的缓存</p>
<p><a href="http://www.mongodb.org/">mongodb</a> 在技术选型玩了下, 没有正式在生产用过</p>
<ul>
<li>数据库</li>
</ul>
<p><a href="http://dev.mysql.com/downloads/">mysql</a> 不解释, 感觉是不是所有Python的底层db都是mysql&hellip;&hellip;</p>
<p><a href="http://www.postgresql.org/">postgresql</a> 开发日志统计系统使用过, 学院派, 各种牛叉的功能, 对json的支持令人印象深刻.</p>
<ul>
<li>抓取</li>
</ul>
<p><a href="http://www.crummy.com/software/BeautifulSoup/">beautifulsoup</a> 配合urllib2或者requests库进项简单的抓取分析工作</p>
<p><a href="http://scrapy.org/">scrapy</a> 很牛的抓取框架, 适合规模较大,需求复杂的的抓取任务</p>
<ul>
<li>搜索
<a href="http://lucene.apache.org/solr/">solr</a></li>
</ul>
<p>完成搜索功能, 虽然Python也有一些实现, 但是感觉还是使用成熟的方案会好些, 文档/资源丰富, 便于开发和维护. Solr, 足够简单及强大.</p>
<ul>
<li>好用的第三方</li>
</ul>
<p><a href="http://docs.python-requests.org/en/latest/">requests</a>  HTTP for humans, 非常好用, 强烈推荐</p>
<p><a href="https://github.com/fxsjy/jieba">jieba</a> 中文分词</p>
<ul>
<li>异步和队列</li>
</ul>
<p><a href="http://gearman.org/">gearman</a> 支持分布式的任务分发框架, 并行/不同语言之间的通信. 之前使用主要用来跨机器任务分发.</p>
<p><a href="http://www.celeryproject.org/">celery</a> 分布式任务队列</p>
<p><a href="http://zeromq.org/">zeromq</a> 之前使用的消息系统是基于这个的, 没有研究过</p>
<ul>
<li>日志</li>
</ul>
<p><a href="http://logstash.net/">logstash</a> 日志收集和分析, 支持不同来源不同格式, 进行统一收集和分析处理</p>
<p><a href="http://www.elasticsearch.org/overview/elasticsearch/">Elasticsearch</a> 数据</p>
<p><a href="http://www.elasticsearch.org/overview/kibana/">Kibana</a> A log analyzing web interface for logstash and elasticsearch</p>
<p>三者组合</p>
<ul>
<li>项目管理</li>
</ul>
<p><a href="https://tower.im/">tower</a></p>
<hr>
<p>Log:</p>
<pre><code>2014-07-24 first version
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>读书笔记-软件测试的艺术 </title>
			<link>https://wklken.me/posts/2014/07/26/the-art-of-software-testing.html</link>
			<pubDate>Sat, 26 Jul 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/07/26/the-art-of-software-testing.html</guid>
			<description>这本书是三年前毕业时读的, 毕业时的职位是&amp;quot;测试开发工程师&amp;quot;. 好吧, 这本书年龄比我还大:), 毕业那会绝版了, 读的是电子版的</description>
			<content type="html"><![CDATA[<p>这本书是三年前毕业时读的, 毕业时的职位是&quot;测试开发工程师&quot;.</p>
<p>好吧, 这本书年龄比我还大:), 毕业那会绝版了, 读的是电子版的. 前阵子看到有在卖就买了一本珍藏, 最近重读了一遍.</p>
<p>可以作为测试入门读本.(测试界的经典书籍), 摘录一些, 一些关键字感兴趣可以自己google.</p>
<p><img src="/imgs/books/the-art-of-software-testing.jpg" alt="software-test"></p>
<p>好吧, 在很多人眼里, 测试只是点点鼠标等没技术含量的工作, 干开发干不了才干测试. But, 这个观点是错误的, 测试还是非常博大精深的, 要求还是非常高的(需要懂各类语言, 需要写各种代码, 需要懂各种业务, 需要懂各类场景, 需要项目管理, 需要&hellip;&hellip;).</p>
<hr>
<ul>
<li>什么是软件测试</li>
</ul>
<blockquote>
<p>所谓软件测试, 就是一个过程或一系列过程, 用来确认计算机代码完成了其应该完成的功能, 不执行其不该有的操作.</p>
</blockquote>
<p>注意后半段.</p>
<ul>
<li>测试的心理学</li>
</ul>
<blockquote>
<p>测试是为了发现错误而执行的过程</p>
</blockquote>
<p>人的行为总是倾向于具有高度目的性. 所以需要将目标定为: 证明程序中存在错误(某些情况下, 测试人员的态度可能比实际的测试过程本身还重要)</p>
<p>所以, 要假设测试的程序是存在错误的.</p>
<ul>
<li>软件测试的原则
(直接摘录了, 很多观点值得借鉴)</li>
</ul>
<table>
<thead>
<tr>
<th style="text-align:center">编号</th>
<th style="text-align:left">原则</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">1</td>
<td style="text-align:left">测试用例中一个必需部分是对预期输出或结果进行定义</td>
</tr>
<tr>
<td style="text-align:center">2</td>
<td style="text-align:left">程序员应当避免测试自己编写的程序</td>
</tr>
<tr>
<td style="text-align:center">3</td>
<td style="text-align:left">编写软件的组织不应当测试自己编写的软件</td>
</tr>
<tr>
<td style="text-align:center">4</td>
<td style="text-align:left">应当彻底检查每个测试的执行结果</td>
</tr>
<tr>
<td style="text-align:center">5</td>
<td style="text-align:left">测试用例的编写不仅应当根据有限和预料到的输入情况, 而且也应当根据无效和未预料到的输入情况</td>
</tr>
<tr>
<td style="text-align:center">6</td>
<td style="text-align:left">检查程序是否&quot;未做其应该做的&quot;仅是测试的一半, 测试的另一半是检查程序是否&quot;做了其不应该做的&quot;</td>
</tr>
<tr>
<td style="text-align:center">7</td>
<td style="text-align:left">应该避免测试用例用后即弃, 除非软件本身就是一个一次性的软件</td>
</tr>
<tr>
<td style="text-align:center">8</td>
<td style="text-align:left">计划测试工作时不应默许假定不会发生错误</td>
</tr>
<tr>
<td style="text-align:center">9</td>
<td style="text-align:left">程序某部分存在更多错误的可能性, 与该部分已发生错误的数量成正比</td>
</tr>
<tr>
<td style="text-align:center">10</td>
<td style="text-align:left">软件测试是一项极富创造性, 极具智力挑战性的工作</td>
</tr>
</tbody>
</table>
<ul>
<li>错误发现的越早, 改正错误的成本越低</li>
</ul>
<p>so, 单元测试很重要, 代码走查很重要,</p>
<ul>
<li>黑盒白盒</li>
</ul>
<p>黑盒测试(数据驱动测试), 将程序视为一个黑盒, 不用去理解程序的内部结构(测试目标与程序内部机制和结构完全无关), 构造测试数据(来源于软件规范), 检查输出是否符合预期.</p>
<p>白盒测试(逻辑驱动测试), 对程序的逻辑结构进行检查, 从中获取测试数据.</p>
<ul>
<li>具体分类</li>
</ul>
<p>单元测试</p>
<p>功能测试</p>
<p>系统测试: 能力, 容量, 强度, 可用性, 安全性, 性能, 存储, 配置, 兼容性, 安装, 可靠性, 可恢复性, 服务/可维护性, 文档, 过程</p>
<p>验收测试</p>
<p>安装测试</p>
<ul>
<li>测试用例的设计</li>
</ul>
<p>白盒测试:</p>
<p>逻辑覆盖测试, 从弱到强依次是</p>
<pre><code>语句覆盖,每个语句至少执行一次
判定覆盖(也称分支覆盖) 每个判断都至少有一个为真和为假的输出结果
条件覆盖, 确保将一个判断中的每个条件的所有可能结果都至少执行一次
</code></pre>
<p>黑盒测试:</p>
<pre><code>等价划分, 穷举输入是不可能的, 但是可以将其划分成有限数量等价类, 获取一个子集输入.
边界值分析, 输入和输出等价类中那些恰好处于/大于/小于边界的状态
因果图, 需求规格 -&gt; 因果关系分析 -&gt; 因果图 -&gt; 测试用例
错误猜测, 基于直觉和经验的猜测
</code></pre>
<hr>
<ul>
<li>做测试还是做开发?</li>
</ul>
<p>我做了一年又三个月测试开发, 后来转职Python后端开发了.</p>
<p>原因? 兴趣, 仅此而已.</p>
<p>如果更在乎创造一些东西，做开发.</p>
<p>如果更喜欢发现一些东西，做测试开发(现在似乎没有单纯的测试了吧?)</p>
<p>(开发就像工匠, 测试就如寻宝的)</p>
<p>开发主动权在手中，测试需要更多的博弈.</p>
<p>(这么看来测试要求更高一些, 哈哈)</p>
<ul>
<li>每个开发都应该懂些测试的基本思想和原则</li>
</ul>
<p>写出的代码会更健壮. 多注意测试, 可以给后续维护以及重构节省一大笔时间.</p>
<p>可以拿这本书作为入门.</p>
<ul>
<li>其他</li>
</ul>
<p>不管有没有专职测试, 单元测试都是必须的.(最好要有, 重构复杂项目的时候会发现感动的哭了)</p>
<p>如果有专职测试, 开发测试比应该蛮高的, 好钢用在刀刃上, 对重要项目进行测试, 另外留出时间研究自动化测试/回归测试/测试工具等, 以及对项目流程进行优化, 最大化提高生产力.</p>
<p>没有专职测试, 需要开发人员足够靠谱, 并且需要建立一套完善的生产部署流程, 监控机制, 以及用户反馈机制, 以小步快跑, 频繁发布的方式处理需求, 同时关注反馈.</p>
]]></content>
		</item>
		
		<item>
			<title>千里行纪&amp;工作三周年小结</title>
			<link>https://wklken.me/posts/2014/07/22/summary-09-longjourney-and-three-years.html</link>
			<pubDate>Tue, 22 Jul 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/07/22/summary-09-longjourney-and-three-years.html</guid>
			<description>旅行结束好几天了, 狂睡了一天, 刷了三天书(打算把之前没看的看咯), 花一下午去仲裁(囧)&amp;hellip;&amp;hellip;突然想想, 该写写了, 合</description>
			<content type="html"><![CDATA[<p>旅行结束好几天了, 狂睡了一天, 刷了三天书(打算把之前没看的看咯), 花一下午去仲裁(囧)&hellip;&hellip;突然想想, 该写写了, 合着之前拖了两周的三周年小结一起.</p>
<p><img src="/imgs/life/coding-life.jpeg" alt="coding-life"></p>
<hr>
<h2 id="千里行纪">千里行纪</h2>
<p>7月4日, 毕业工作三周年(三年前的7月4日, 在杭州, 入职第一天), 刚好, 也在不久前正式离职, 还没想好去做什么, 还有些问题没有想清楚, 所以选择出去走走.</p>
<p>第一站杭州, 打算随走随玩, 无计划无时限, 一路北上, 走完一站再想下一站去哪, 累了就打道回府.</p>
<p>然后, 带着一个kindle, 一本笔记, 一个背包, 出发.</p>
<p>最终, 7月4日出发, 17日归, 从深圳, 动车到达杭州, 然后汽车转战安徽, 西递+黄山, 然后去了趟南京, 再转高铁到济南, 回学校逛了逛, 觉得累了, 买了张机票飞回来, 好好地睡了一整天.</p>
<table>
<thead>
<tr>
<th>日期</th>
<th>步数</th>
<th>公里</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>7.4</td>
<td>3804</td>
<td>2.4</td>
<td>动车</td>
</tr>
<tr>
<td>7.5</td>
<td>31823</td>
<td>23.3</td>
<td>西湖, 曾经工作/住的地方</td>
</tr>
<tr>
<td>7.6</td>
<td>24202</td>
<td>17.2</td>
<td>再次西湖</td>
</tr>
<tr>
<td>7.7</td>
<td>14899</td>
<td>11.1</td>
<td>淘宝城/浙大</td>
</tr>
<tr>
<td>7.8</td>
<td>18180</td>
<td>13.3</td>
<td>灵隐龙井九溪六和塔</td>
</tr>
<tr>
<td>7.9</td>
<td>9071</td>
<td>5.9</td>
<td>屯溪/西递</td>
</tr>
<tr>
<td>7.10</td>
<td>24186</td>
<td>15.9</td>
<td>九龙瀑布/黄山后山</td>
</tr>
<tr>
<td>7.11</td>
<td>12586</td>
<td>6.9</td>
<td>下山/天都峰/南京</td>
</tr>
<tr>
<td>7.12</td>
<td>29551</td>
<td>21.2</td>
<td>玄武湖/中山陵</td>
</tr>
<tr>
<td>7.13</td>
<td>24232</td>
<td>16.9</td>
<td>明孝陵/总统府/南京大屠杀遇难者纪念馆/夫子庙/夜游秦淮</td>
</tr>
<tr>
<td>7.14</td>
<td>19112</td>
<td>15.3</td>
<td>高铁/经十路</td>
</tr>
<tr>
<td>7.15</td>
<td>23788</td>
<td>17.3</td>
<td>黑虎泉/泉城广场/趵突泉/大明湖一半/山大中心校区</td>
</tr>
<tr>
<td>7.16</td>
<td>19547</td>
<td>14.1</td>
<td>解放路/山大路/中心校区/大明湖另一半/齐鲁软件学院</td>
</tr>
<tr>
<td>7.17</td>
<td>4051</td>
<td>3.2</td>
<td>飞机</td>
</tr>
<tr>
<td>总计</td>
<td>259032</td>
<td>184</td>
<td></td>
</tr>
</tbody>
</table>
<p>耗时13天, 行程应该有2800公里, 步行259032步, 共184公里, 平均每天14.1公里, 拍了1863张照片. 走最多的一天是到杭州第二天逛西湖, 原来每周骑车绕没觉得多远, 这次踏踏实实逛了一圈, 着实累, 要是车子还在就好了. 第二天陪同事再逛, 不过路线不同, 囧. 最累的一天是爬黄山, 整整一天都在爬台阶&hellip;&hellip;第二天下山也累, 不过下山顺带爬了下天都峰, 不虚此行.</p>
<p>到了四个地方, 感受了四种不同的生活节奏.</p>
<p>杭州, 很熟悉怀念的感觉, 慢节奏(我快十点到的杭州, 一如既往, 大街上一片漆黑, 店基本关了, 一个人走了一公里多, 有点类似以前加班下班后回家的感觉)/美食(复习了三天外婆家, 没复习完&hellip;)/美景无限, 适合生活的地方, 记得在杭州的时候天天把景区当后院逛, 每次骑车冲山出来后, 都要惯例绕西湖一圈, 坐一坐, 然后会公司蹭水, 再回家歇着. 这次, 走过了西湖, 走过了龙井, 到之前工作的地方, 到之前住的地方, 行走, 回忆.</p>
<p>去黄山前先去了西递, 大巴转小车, 彻彻底底在农村住了一晚. 不记得已经有多少年, 没有感受到泥土的气息了, 或许从高中外出求学开始. 水墨画的景致, 典型的农家, 荷塘月色蛙鸣, 八九点基本都熄灯了, 走在青石小巷里的感觉, 很宁谧安定.</p>
<p>黄山, 值得一爬, 记忆里爬泰山就是爬台阶去了, 黄山还好. 黄山从后山上的, 早上三个小时爬了趟九龙瀑布, 刚到后山山口, 果断下山寄存了大部分东西吃了顿午饭再坐车到山口开始爬(好重, 差点给跪了).晚上订的宾馆床位略坑, 没睡好, 另外日出没看到&hellip;..下山爬天都峰, 体验了一把在云端的感觉, 陡且险, 顿觉不虚此行.</p>
<p>南京, 六朝古都, 下了黄山之后大巴当晚到, 地图一查找了个交通方便的地方住下了, 后来才发现那是南京最繁华的地方. 第二天, 一个人背着包带着相机, 雨中漫步玄武湖, 是周六, 但是好像没什么人, 沿湖边慢慢行走, 后来上了古城墙, 走了一段, 发现没人了, 整个城墙视野里就我一个人, 坐了很久才下来.然后去了九华山公园, 玄奘寺, 午间吃了顿热腾腾的南京汤包(很赞, 对我胃口), 还没热乎走几步, 被倾盆暴雨淋了个透心凉, 回酒店洗洗, 穿着拖鞋再出发, 中山陵. 第二天, 明孝陵/总统府/南京大屠杀遇难者纪念馆/夫子庙/夜游秦淮, 可能是住的地方的问题, 没感受到这个城市真正的生活气息(新街口下面地铁人来人往, 刚到的时候被吓到了, 这人也太多了吧&hellip;.).</p>
<p>济南, 熟悉而又陌生的城市, 记得七年前北上来到这个城市. 呆了近四年, 时隔三年, 再次回到这座城. 刚下车, 很热, 非常热&hellip;&hellip;在体育馆边坐了会, 昏黄的路灯, 旁边是繁忙的立交桥, 行人, 纳凉的大爷大妈, 感受了下这里的生活气息. 空气不是很好, 但是很熟悉, 熟悉的地名, 街道名, 公交线路. 最后一天, 回软件学院看了下, 听说要撤并, 不知道下次来了还在不在. 走在之前上课, 上自习, 吃饭, 图书馆, 实验室, 那些自己曾近踏足的路上, 感觉很奇怪. 很多东西没什么变化, 只有回忆, 景依旧, 人不在. 怀念自己上学的日子, 感叹那时不好好珍惜. 生活是把杀猪刀, 时间无情的推着我们前进.</p>
<p>以上差不多流水账的感觉, 后面是一些感受.</p>
<blockquote>
<p>为何行走?</p>
</blockquote>
<p>此次出行, 主要是, 一半, 走之前走过的路, 一半, 去几个新的地方. 想一些事情, 希望有些收获.</p>
<p>在南京的酒店里, 看电视, 听到了一首歌, &laquo;生来彷徨&raquo;, 突然很有感觉.</p>
<p>生来彷徨, 小学, 初中, 中考, 到市里上学, 高考, 到山东上学, 到北京实习, 到杭州工作, 再到深圳, 一路过来, 发现在工作之前, 都是按部就班的走完了二十几年的路, 然后, 毕业进入社会, 选择权在自己手里, 突然某一天觉醒的感觉, 开始思考, 生活的意义, 如何选择, 彷徨迷茫, 不断行走与思考, 然后做一些或对或错的决定, 一路前行. 人生是一场单程票的旅行, 在一次次决定中, 我们在一个个岔路口选择下一段旅程, 最终到达终点.</p>
<p>为什么要行走? 为什么要旅行? 或许是我们在寻找些什么.</p>
<p>其实, 旅行, 远远没有宅在家里好吃好喝好玩, 行走, 往往就是一趟趟自虐的过程, 例如, 爬山&hellip;爬山是对自己体力,耐力,意志力的一次次挑战&hellip;每次爬山途中, 不上不下累得要死都告诉自己, 下回老子再也不来了, 但是, 每次依旧会乐此不疲.</p>
<p>但是, 依旧要旅行, 要行走, 特别是当你有些东西没有想明白的时候, 在自己固有的环境里, 在自己熟悉的生活模式和节奏里, 是很难想清楚的. 而在旅途中, 新的环境, 不同的人, 不同的事情, 飞快飘过的景物, 飞快发生的事情, 身体的极度劳累, 很多想法会进入大脑, 你开始意识到一些东西: 我拥有什么? 我又欠缺什么? 我该珍惜些什么? 什么地方好, 什么地方不好? 为什么? 慢慢地, 获得一些观点, 得到一些结论, 有些初步的想法. 这或许就是旅行的目的.</p>
<p>行走, 不只是为了拍照, 哈哈</p>
<blockquote>
<p>放慢脚步, 感受生活</p>
</blockquote>
<p>这次从走过之前走过的很多路, 突然发现, 过去的生活, 似乎太快了, 行色匆匆, 短短三年, 从指尖流逝, 似乎很少刻意觉察到, 然后慢下来, 感受下生活中点点美好. 这跟逛西湖类似, 以往骑车逛的时候, 一圈, 一小时不到, 效率杠杠的, 但是匆匆而过, 固定的线路, 在固定的几个地方停留坐坐, 看到那些熟悉的景色. 靠双腿行走, 却又是不同的感觉, 慢慢前行, 一步步丈量, 往往会发现很多之前没看过的东西, 曲径, 柳暗花明, 如果一直骑车, 永远不会看到这些.</p>
<p>so, 让自己成长强大, 可以更高效地搞定工作/学习等等那些事情, 让生活慢下来, 好好地感受生活. e.g. 我写这个脚本, 只为了以后每天可以省五分钟出来发发呆, 哈. 另外不要让自己陷入一种模式, 例如周一-周五+周六周日=一周=7天, 认认真真过好每一天, 不要被模式限制了, 过得充实些, 去热爱一些东西.</p>
<blockquote>
<p>关于世界的猜想</p>
</blockquote>
<p>有时候走着走着发现一个人都没有, 会想下</p>
<p>其实, 世界的本质就是一个系统, 你自个就是一个进程, 你看到的整个世界都是模拟出来的, 你跑到这个地方之所以看不到人, 是因为你跑太快了, 系统只来得及初始化地方, 还来不及初始化 NPC, 没给你挂起已经不错了, 慢点走吧, 别剧情(主线支线)逻辑还没走就空跑结束了&hellip;&hellip;^_^</p>
<blockquote>
<p>你想要的生活是什么?</p>
</blockquote>
<p>这次出行, 看了 N 本知乎周刊, 一本小说&laquo;陆犯焉识&raquo;, 半本&laquo;明朝那些事儿&raquo;.&laquo;陆犯焉识&raquo;, 有之前看&laquo;平凡的世界&raquo;的感觉. 然后, 有一个下午, 坐在杭州的马路牙子上, 看着夕阳余晖下, 马路边的车流人流.(上一次这么干好像是和在庐山下和景元邪真坐在马路边) 然后, 在黄山下, 一个小饭店里, 看着路口人来人往, 这里人们的生活, 还有游客. 然后, 刚到南京, 下地铁到了地下步行街, 穿越, 匆匆而行的人们. 到了济南, 又在马路牙子上坐了一会, 昏黄的路灯.</p>
<p>每个人有自己的生活, 我们生活在自己意识里的世界里, 抛却自己朝九晚五的日子, 到大街上看看, 并不是每个人的生活方式和你一样. 同时, 不要用自己的评价体系去评价别人的生活, 活得好不好, 快不快乐, 幸不幸福, 并不是你能评价的, 与你无关, 很多东西, 我们往往都只看到了表面, 按照自己的思维体系去判断, 但是, 很多事情, 都跟喝水一样, 冷暖自知.</p>
<p>感同身受, 大部分都是扯淡的, 很多时候, 只有亲身体验才知道.</p>
<p>很多时候, 我们生活在&quot;大家&quot;的评价中, 很多时候, 我们追求的是&quot;大家&quot;的&quot;成功&quot;, 很多时候, 我们去做&quot;大家&quot;都做的事情.</p>
<p>并不是说&quot;大家&quot;的就是错的, 而是, 我们往往很难独立地去思考, 自己真正想要的是什么? 很容易陷入到&quot;别人的眼光&quot;和&quot;大家的看法&quot;里面.</p>
<p>天龙八部, 悲剧在于&quot;求而不得&quot;, 但是&quot;求啥得啥&quot;或许就不是生活了, 人总要有点追求的吧, 追逐自己想要的.</p>
<p>但是, 这或许不重要:), 有时候想多了反而迷惘(好吧, 唉).</p>
<blockquote>
<p>It&rsquo;s easy to find out what you don&rsquo;t like, but it&rsquo;s hard to figure out what you really want.</p>
</blockquote>
<p>so, 你想要的生活又是什么样的呢?</p>
<p>最近的感受时, 慢慢接受自己是一个怎样的人了, 不再为了&quot;改变&quot;而改变, 心安理得地做自己, 顺着自己的心意. be cool about it. 改变, 只因自己想变.</p>
<p>我自己, 似乎还是飘了太久了, 现在似乎该停下来, 思考下这个问题了. 有些初步的想法了.(&laquo;平凡之路&raquo;单曲循环&hellip;&hellip;)</p>
<p>PS. 推荐一部电影&laquo;奇怪的她&raquo;.</p>
<blockquote>
<p>该以怎样的心态去看待一切</p>
</blockquote>
<p>性格内向, 情商极低(低到自己都有些鄙视的地步), 很多时候思维相对偏激, 虽然不是处女座, 但是推崇完美主义(完美主义害死人), 所以, 总成了对待事物, 总容易形成两极分化. 好的东西偏执地喜欢, 不好的东西(自己的看法), 总是避免去了解接触.</p>
<p>总之, 看待事物心态不够open, 太过偏执. 在有些事情上, 属于冲动型, 快刀乱麻一路到黑, 在有些事情上, 容易犹犹豫豫婆婆妈妈, 纠结一些不该纠结的东西.</p>
<p>不要太过苛求完美, 就像你不可能把所有景点的路遍历一遍(深度优先广度优先都不行), 只能, 选择一条最优的路线.</p>
<p>人生太短, 心态放开, 不要浪费时间.</p>
<blockquote>
<p>围城</p>
</blockquote>
<p>围城, 围城啊.</p>
<p>三年多过去了, 这次一路, 见到了很多同学, 朋友, 曾经的同事, 城市不同, 职业不同, 每个人的境遇也不同, 都变化蛮大, 回头看看自己, 似乎没什么变化, 迄今了然一身, 总是自嘲自由.</p>
<p>生活就是座围城, 不管城里城外, 做好自己的选择, 走好自己的路, 也就够了吧.</p>
<blockquote>
<p>记录&amp;早起&amp;读书</p>
</blockquote>
<p>似乎该养成, 记录的习惯.</p>
<p>之前只会到特殊的日子, 写写小结, 前阵子开始用DayOne, 变得好些了.</p>
<p>很多东西, 过了, 没有记录. 美好或不美好, 酸甜苦辣, 烈日或阴雨.</p>
<p>虽然一直号称记忆力不错, 但是, 可以按秒按天按月按年去衡量时间, 可以轻易的说出一个长度, 但是无法回顾, 那些值得记录的东西.</p>
<p>早起, 以前都是忙到半夜, 早上八点多起, 旅途过程中, 基本都五六七点就起, 发现一天变得很长很充实, 能走很多地方, 做很多事情, 回来到现在, 生物钟一直保持吧.</p>
<p>读书, 身体和灵魂, 总有一个在路上, 如果不在途中,就多读些读书吧.</p>
<blockquote>
<p>不要一个人旅行</p>
</blockquote>
<p>一个人的旅行真的很累&hellip;&hellip;</p>
<p>I don&rsquo;t want to walk alone anymore!</p>
<hr>
<h2 id="工作三年小结">工作三年小结</h2>
<p>好吧, 我工作三年了, 很多时候不大想承认哈.</p>
<p>额, 三年了.</p>
<p>经验之谈, 扯一些有用没用的</p>
<blockquote>
<p>经验很重要, 要学会积累</p>
</blockquote>
<p>记得, 在学校和刚毕业那会, 对&quot;xx年工作经验&quot;总是嗤之以鼻, 不认为很重要, 但是实际上, 或许对于天才型的人是这样的, 但是大部分人都是平凡的, 我也是, 经验很重要.</p>
<p>三年, 见过一年有 N 年经验的(加班), 也见过 N 年只一年经验的, 所以, 如何保持持续成长, 积累经验值很重要, 这跟你挂机在野外平砍小怪或者开挂刷副本是一个道理, 后者给力很多, 当然, 跟环境和团队也有一定关系.</p>
<p>但个人的成长, 主要还是跟自身相关性最大, 自己需要为自己负责.</p>
<blockquote>
<p>效率&amp;工具</p>
</blockquote>
<p>有一段时间, 我在追求高效, 应该有小半年吧, 那段时间研究了下工作中自己的瓶颈所在, 从前辈那里取得一些真经, 同时也看了很多时间管理的东西, 研究了下 GTD, 各种快捷键, 快捷工具, 很庆幸, 那时候也顺带深入搞了把VIM和Shell. 然后逐渐形成了自己的工作方式和风格.</p>
<p>后来又有段时间, 大概三个月吧, 彻彻底底的工具党, 试用各种系统, 各种浏览器, 各种编辑器, 各种 GTD 工具, 还有快速启动, 系统管理, 记录等等. 结果发现重心在工具而不是自己所作的事情上了.</p>
<p>后来的后来, 脱离了工具党(可能是某一天悟了吧), 不在花时间在各种工具上, 工具只是工具, 在精不在多, 找到了适合自己的就行, 集中一段时间研究下适合自己的工具还是值得的, 将受益终身(e.g. markdown/VIM).</p>
<p>first, 你必须在平时了解一下自己的痛点(例如以事不过三的原则, 当一个事情重复三次, 就要注意了)</p>
<p>然后想办法解决之.</p>
<p>学会从一些渠道知道一些优秀的工具, 然后选择其中的佼佼者, 以开放的心态去试用, 找到适合自己的, 然后花点时间研究下, 事半功倍</p>
<p>唯一的目标: 事半功倍. 所以不要care什么编辑器之争, 语言之争(下一个谈).很浪费时间.(当然, 选择时候要花点时间对比的, 但不要去争论)</p>
<blockquote>
<p>语言</p>
</blockquote>
<p>在毕业前, 连写了三年多Java, 当时一直认为毕业后从事Java相关的工作, 后来工作中自学了Python, 然后转职成了Python后端, 间带学习了下Golang, 今年业务需要认真学了一把之前偶尔要用到的JavaScript. 最近离职间隙, 打算重新捡一下Java, 认真学习下C.</p>
<p>Python是世界上最好的语言&hellip;&hellip;</p>
<p>语言, 你可以选择自己喜欢的语言, 但是不要局限自己不去学习其他的, 起码, 要有第二门辅助性质的语言, 然后第三门, 第四门&hellip;&hellip;一年一门, 不算过分吧?</p>
<p>每种语言的思维模式都不同, 尝试去学习和使用, 有利无害.</p>
<p>当你深入学习一门语言后, 再学习其他语言其实是非常快的, 很多东西都是相同的.</p>
<blockquote>
<p>基础知识很重要</p>
</blockquote>
<p>算法/数据结构/IO 模型等等, 很多基础的东西, 例如算法, 可能平时用不到, 但这并不意味着不需要去学习, 基础很重要.</p>
<pre><code>&gt; @左耳朵耗子: 很多人都并不知道，哪些知识是用来生存的，哪些知识是用来改变命运的//@左耳朵耗子: “学好英文，算法，系统原理，基础知识没用啊？反正工作中又用不到！”，有这样想法的人不在少数，这个想法相当务实。同理，对于一个人来说，初中毕业所掌握的知识对于生存来说就够了，也没必要上什么高中和大学了。当然，我们都知道只有初中毕业的人在这社会上会是啥样。这对于程序员也是同理。
</code></pre>
<blockquote>
<p>微博&amp;RSS&amp;知乎&amp;笔记&amp;博客&amp;github</p>
</blockquote>
<p>前三者是输入, 后三者输出</p>
<p>很多人不用微博, 但是微博作为我获取信息的主要渠道, 每天还是会花时间刷一刷的, 不停地关注/取关, 最终形成稳定的信息渠道. 例如上面那条微博, 你能获取到很多东西, 而不是把自己困守在一个狭窄的世界.</p>
<p>然后就是RSS订阅和知乎.</p>
<p>从这些渠道, 看到了很多好的文章, 好的资源, 好的工具</p>
<p>自从开始用markdown后, 所有笔记从原先doc开始逐渐全部转换成markdown了, 分门别类, 应该有上千了. 定期梳理, 主要用于在学习某些新的东西时, 能获取最优的学习路径, 或者, 能够在短暂时间内搜索到自己曾经记录的东西.</p>
<p>笔记多了, 定期总结汇总, 维持规模, 其中一些梳理出来, 变成了blog.</p>
<p>github和bitbucket作为开源和私有的代码库, 写一些东西, 存一些东西, 关注一些东西.</p>
<blockquote>
<p>读书</p>
</blockquote>
<p>单反穷三代, kindle富一生.</p>
<p>kindle成为了我今年最值得购买的东西.</p>
<p>非技术书, 可以往杂里读, 还可以研究一些自己感兴趣的东西, 或者方法论等等.</p>
<p>然后就是技术书籍, 基本都买了纸质版的, 迄今不习惯电子版的, 因为要做好多笔记, 反复看, 或者脱水.</p>
<p>对于知识体系的系统构建, 以及深入, 主要还是靠书本, 网络的很多东西作为补充.</p>
<hr>
<p>三年间, 感觉自己变化不大, 性格上, 依旧内向, 依旧是我, 很多东西不会改变. 生活上, 依旧单身(人艰自拆, maybe I need to think about: 择一城终老, 遇一人白首).</p>
<p>三年间, 感觉自己变化蛮大的, 从菜鸟开始慢慢蜕变了, 有了独立的意识, 有了目标, 开始尝试去挣脱一些东西, 追求一些东西, 去掌控一些东西. 最大的变化, 还是从 无意识 -&gt; 有意识了.</p>
<p>三年间, 形成了自己的做事方法和风格, 有了一些准则, 有了一套工具栈, 开始完善自己的知识体系.</p>
<p>三年间, 36个月, 在两个城市生活过, 跳过一次槽, 涨过五次薪, 工资翻了三倍不到, 拿过三次年终奖, 当然, 其中一次只有1000块(冲动的后果, 囧).</p>
<p>三年间, 经历了两家公司, 很庆幸遇到了两位很nice的老大, 以及很多给力的同事, 在他们的帮助下, 一步步成长, 很感激大家! Always, Thank you for everyting.</p>
<p>三年间, 感觉自己还是对得起&quot;靠谱&quot;二字, I hope I didn&rsquo;t make anyone disappointed, If I did, I am sorry.</p>
<p>三年间, 经历了很多.</p>
<p>时间飞逝, 人生又有多少个三年.</p>
<p>且行(码)且珍惜</p>
<p>(感叹完毕!)</p>
<hr>
<h2 id="关于未来">关于未来</h2>
<p>未来是多远?</p>
<p>未来一周, 刷书, 好多没看的, 看了, 该留留, 该出的出, 堆得太多压力大.</p>
<p>未来两周到一个月(or 两个月?), 更新简历, 开始找工作了, 深圳或者杭州, 创业团队or BAT, 寻找后端开发方面的工作, 希望去做一些有价值的事情, 给力的产品</p>
<p>未来一两年, 继续走技术这条路吧, 当前目标:Linux后端全栈, 长期的目标是, 成为某一方面的技术专家or系统架构师(需努力).</p>
<p>工作上, 快起来, 生活上, 慢下来.</p>
<p>要考虑更多的事情了.</p>
<p>我在三年前毕业的博文里面写的:</p>
<blockquote>
<p>未来，不敢说太远，今后五到十年，好好努力吧，好好奋斗</p>
</blockquote>
<p>好吧, 现在才过了三年, 仍需努力.</p>
<p>最后, 前不久看到的一句话</p>
<blockquote>
<p>不要放任梦想, 而要把它当做一种习惯去培育  &ndash;拉里佩奇</p>
</blockquote>
<p>诸位共勉.</p>
<hr>
<p>2014-07-22 于深圳</p>
<p>wklken</p>
]]></content>
		</item>
		
		<item>
			<title>简单搜索系统组成总结</title>
			<link>https://wklken.me/posts/2014/06/09/search-system.html</link>
			<pubDate>Mon, 09 Jun 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/06/09/search-system.html</guid>
			<description>最近在进行离职前交接工作了, 对之前做的一些东西也大概进行了下简单总结. 今天整理了下, 搜索系统组成简要描述, 一些思想, 不涉及太多具体实现. 这套</description>
			<content type="html"><![CDATA[<p>最近在进行离职前交接工作了, 对之前做的一些东西也大概进行了下简单总结.</p>
<p>今天整理了下, 搜索系统组成简要描述, 一些思想, 不涉及太多具体实现.</p>
<p>这套系统从开始设计到最终完成, 前前后后花了3个月的样子(计算所有时间投入), 也算是做得感觉比较完善的一套系统.</p>
<p>上线接近一年, 支持快玩游戏搜索业务(快玩盒子/快玩网站/移动端等), 系统每天百万级的搜索(峰值在250w左右, 应用层两台机器负载均衡, 单机核心层, 单机引擎), 很遗憾, 由于业务所限, 一直没有看到这套系统能支持的量上限, 即使在峰值, 核心层qps大概也才50左右, 预计搜索量到千万级应该没什么压力, 当然, 优化的余地还很多.</p>
<p>外面正在狂风骤雨, 开始吧</p>
<hr>
<h3 id="目标">目标</h3>
<p>当系统数据达到一定量时, 搜索就成为了除类目以外的第二大入口.</p>
<ol>
<li>更好的搜索结果(指标: 召回率, 转化率, 排序效果)</li>
<li>更好的用户体验(下拉提示点击率,相关搜索准确率等)</li>
</ol>
<h3 id="搜索流程">搜索流程</h3>
<ol>
<li>
<p>用户在输入框输入关键词, 此时输入框会下拉提示一些词, 用户可以选择进行搜索</p>
</li>
<li>
<p>用户点击, 进行搜索, 前端调用搜索接口</p>
</li>
<li>
<p>应用层</p>
<pre><code> 3.1 请求关键词改写, 获得改写后词
 3.2 查询缓存是否存在, 存在直接返回缓存内容. 此时, 会记录搜索日志
 3.3 不存在缓存, 调用解析输入, 调用核心层接口
</code></pre>
</li>
<li>
<p>核心层, 调用引擎接口, 获取搜索结果, 并整合信息, 返回应用层</p>
</li>
<li>
<p>应用层, 获取结果, 此时根据需要, 可能调用相关搜索和热门词服务, 获取必要信息, 最终进行页面渲染, 记录日志, 返回给客户端</p>
</li>
</ol>
<h3 id="系统结构图">系统结构图</h3>
<p>实现: java(solr)只需配置 + python(所有服务) + golang(suggestion)</p>
<p><img src="/imgs/system/search.png" alt="search system"></p>
<h3 id="系统组成简单描述">系统组成(简单描述)</h3>
<blockquote>
<p>对外服务</p>
</blockquote>
<p>搜索整体系统,对外提供服务包括</p>
<ol>
<li>基本搜索服务
用户输入query, 系统返回筛选并且排序后的结果, 在前端进行展现</li>
<li>下拉提示服务
用户在输入框输入query时, 下拉框根据输入提示搜索关键词, google/baidu的搜索框</li>
<li>相关搜索服务
在搜索结果页,根据用户所在的系统(客户端/移动端/网站等)以及关键词,提示搜索query相关的搜索</li>
<li>热门搜索
在某些业务中,或者前端,展示热门搜索关键词</li>
<li>关键词改写
对用户输入关键词进行改写, 以获取更好的搜索结果, 或者进行关键词纠错, 转换</li>
</ol>
<blockquote>
<p>缓存</p>
</blockquote>
<p>缓存在整个搜索系统中起到很关键的作用, 各个服务都需要使用缓存进行优化</p>
<p>系统使用memcached/redis分别进行处理. 整个搜索中用得最多的是下拉提示suggestion, 用户输入关键词整个过程中存在变动都会发起一次请求.</p>
<blockquote>
<p>业务(应用层+核心层)</p>
</blockquote>
<p>核心层, 提供单一职责, 灵活且性能足够的接口</p>
<p>应用层, 根据不同系统的业务需求进行编写, 调用核心层接口获取数据, 整合搜索结果, 并进行展示渲染</p>
<blockquote>
<p>元信息(数据元信息+排行信息等)</p>
</blockquote>
<p>业务本身的核心数据, 包含元信息, 元信息中只有少部分需要导入引擎, 建立索引 or 存储, 元信息中还可能包含排序相关的信息, 例如评分等</p>
<p>排行信息, 主要来自后端统计系统</p>
<blockquote>
<p>引擎</p>
</blockquote>
<p>对元信息, 进行分析并处理, 建立索引, 存储内容</p>
<p>并提供搜索, 可以决定排序规则</p>
<blockquote>
<p>日志系统</p>
</blockquote>
<p>负责记录各个服务的日志, 用于统计以及其他服务的数据挖掘</p>
<p>可以记录每次搜索的时间,用户,关键词,改写词,是否有结果,结果信息, 翻页信息等等</p>
<blockquote>
<p>算法模块</p>
</blockquote>
<p>对记录日志进行分析, 使用算法生成其他服务需要的数据</p>
<blockquote>
<p>报告系统</p>
</blockquote>
<p>对日志进行统计, 计算搜索pv/uv, 无结果率, 搜索关键词排行, 下拉提示点击率等等</p>
<p>用于关键性指标的统计, 方便针对性优化</p>
<hr>
<p>接下去, 分块简要说明下</p>
<h3 id="搜索服务-数据层">搜索服务-数据层</h3>
<p>数据存储跟各自业务有关系, 信息录入渠道主要是运营录入或者抓取导入等, 存储使用<code>mysql/postgresql</code>等数据库</p>
<p>rank data 主要是由日志系统统计出一些根据涉及排序相关的数据, 例如用户点击次数, 玩次, 评分等等, 会直接影响到结果排序</p>
<p>注意, 由于这些数据都会存在变更, 所以, 需要存储update_time, 用于引擎增量建立索引.</p>
<h3 id="搜索服务-引擎">搜索服务-引擎</h3>
<p>实现上, 使用的是开源的 <a href="http://lucene.apache.org/solr/">apache solr</a>, 版本4.5, 刚才看了下最新版到了4.8了.</p>
<p>曾经一度想自己去实现, 结果发现复杂化了, 系统设计中, 切忌把实现问题的手段当做问题本身去处理.</p>
<p>还有很多同类引擎, 可以去对比下.</p>
<p>选中solr的原因: 简单</p>
<ol>
<li>输入, 足够简单的数据提供方式, 通过配置文件定义数据库及sql等信息, 就可以建立元数据到引擎数据的关系, 且有接口可以方便地进行全量/增量更新</li>
<li>配置简单, 可以配置索引处理方式, 例如中文分词,拼音搜索等, 可以配置不同接口的排序, 可以配置缓存等. ps: 拼音搜索可以使用<code>EdgeNGram</code>索引处理实现.</li>
<li>输出, 足够强大的查询接口</li>
</ol>
<p>对于引擎, 很重要一块是搜索结果排序, <code>solr</code> 可以很方便地支持自定义排序, 可以依赖于输入数据中的排序字段, 进行公式计算, 得到最终的加权和, 用于决定排序. 这里的公式需要针对业务中影响排序的因素进行分析, 然后不断调整因素的权重, 得到最终的排序效果.</p>
<p>如果要进行一些其他处理, 可以在应用层或核心层进行额外处理.</p>
<h3 id="下拉提示服务">下拉提示服务</h3>
<p>前后做了两个版本, 一个版本基于<code>分词-统计-cache</code>实现的, 后面一个版本基于 <code>trie树-cache</code>实现.</p>
<p>元信息直接导出, 以游戏为例, 游戏名+图标+类型+玩次等信息</p>
<p>主要是针对游戏名进行处理:(原词+拼音+拼音首字母)</p>
<pre><code>植物大战僵尸 -&gt; [植物大战僵尸, zhiwudazhanjiangshi, zwdzjs]
</code></pre>
<p>然后, 在内存中建立前缀树. 这里使用的是<code>double-arry-trie</code>实现</p>
<p><code>double-array-trie</code>文章: <a href="http://en.wikipedia.org/wiki/Trie">What is Trie</a> | <a href="http://linux.thai.net/~thep/datrie/datrie.html">An Implementation of Double-Array Trie</a></p>
<p>用户输入query, 没发生一次变化, 发送请求到下拉提示服务, 首先会去命中缓存, 未命中, 进入trie树搜索前缀, 获取此前缀所有后缀, 即获取提示关键词集合, 排序获取权重最高的进行返回(是这个流程, 但实际上没那么简单, 要考虑性能).</p>
<p>如果不开缓存，实时计算的话，对cpu占用率非常高，每次都要搜索<code>trie</code>树，所以开启了memcached外部缓存.</p>
<p>开源了一份, 但并不是线上的实现, 而是优化版本, 但是一直没有机会上到线上看下效果, 有兴趣可以看下 <a href="https://github.com/wklken/suggestion">suggestion</a></p>
<h3 id="相关搜索服务">相关搜索服务</h3>
<p>目前做得比较简单, 使用同一个用户的搜索关键词链进行分析, 处理成
<code>[ 搜索关键词-后继搜索关键词]</code>, 并进行统计, 最终获取统计结果.</p>
<p>这个服务一直没有进行优化, 导致相关搜索的结果并不好, 存在很多bad case(推荐重复的内容/单字符推荐等).</p>
<p>可以基于算法进行重构.</p>
<h3 id="关键词改写">关键词改写</h3>
<p>关键词改写, 主要分成两类, 一类是输入关键字错误导致无结果(错别字/缺字/多字等), 另一类是输入关键字是业务上某些名称的别名, 系统内没有, 需要转换.</p>
<p>通过改写, 可以实现纠错以及转换的目的, 使用户能正确获取结果</p>
<p>关于纠错, 目前处理方式, 用户搜索关键词链, 处理成 <code>[无结果词 - 有结果词]</code>, 另外还有用户下拉提示点击 <code>[无结果输入词 - 有结果点击词]</code>, 然后进行统计, 根据一系列规则进行筛选, 获取改写列表.(目前是基于规则的, 优化空间还很大)</p>
<p>关于业务上的改写, 需要提供入口, 提供给运营人员针对一些术语进行改写, 例如<code>[gta -&gt; 侠盗猎车手]</code></p>
<p>这个服务比较简单粗暴, 计算完成后直接将键值对刷入缓存, 对外提供服务.</p>
<p>关键词改写需要进行持续的优化, 定期获取新的日志进行批量处理, 加入列表. 优化余地很大, 可以有效降低无结果率.</p>
<h3 id="统计">统计</h3>
<p>主要对每日的搜索日志进行统计, 得到两部分信息:</p>
<ol>
<li>报表数据: 不同平台不同渠道的每日pv/uv, 无结果率, 下拉提示点击率等</li>
<li>排行数据: 不同纬度下搜索排行, 用于反向作用于搜索引擎排序</li>
</ol>
<hr>
<h3 id="一些坑">一些坑</h3>
<ol>
<li>系统使用的<code>memcached</code>集群作为缓存, 遇到一些坑, 例如<code>key</code>最大长度250,   <code>key</code>不能包含空格和控制字符, 存储数据最大1M. 即, 默认对用户的输入不信任(看日志才知道有多少奇葩的搜索query). 切成redis或许会好一些.</li>
<li>关于备份. 由于业务初期流量一直不大, 所以除了应用层使用<code>nginx</code>做负载均衡外, 核心层和<code>solr</code>都使用单机实例. 带来的问题是, 虽然整体负载不高, 但是没有备份, 出现过一次<code>solr</code>引擎挂到导致搜索整体失效30分钟的故障, 后面对每个单机服务都进行了服务备份, 失效启用.</li>
<li>需要对整体系统进行监控, 使用<code>sentry</code>和<code>statsd</code>, 可以实时监测到流量变化以及程序错误.</li>
<li>日志很重要, 要针对自己需要了解的指标以及需要统计分析的字段, 设计尽可能完整的日志记录.</li>
</ol>
<hr>
<h3 id="一些感想">一些感想</h3>
<p>需要确认整体目标, 然后建立关键性指标, 实现基础方案, 上线, 并持续地关注数据, 分析日志以及bad case, 然后进行优化, 观察指标变化. 记得系统最初的召回率84%, 后来一步步提升到了92%. 这是一个长期的, 不断优化的过程.</p>
<p>很多东西, 都需要自己一步步去摸索和尝试.</p>
<p>当然, 这只是一个小型的搜索系统, 其中每一个模块都可以针对性地扩展和优化, 使用更好的算法, 达到更好的效果.</p>
<blockquote>
<p>It&rsquo;s simple, but it works, that&rsquo;s enough:)</p>
</blockquote>
<p>系统总是跟随业务逐渐成长变化的, 很可惜, 业务夭折, 这个系统可能失去了在这里继续进化的可能.</p>
<p>希望提供一些可供大家借鉴的方法. That&rsquo;s all.</p>
<hr>
<p>先这样吧</p>
<p>wklken</p>
<p>2014-06-09 于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>JavaScript学习补充</title>
			<link>https://wklken.me/posts/2014/05/25/learning-javascript.html</link>
			<pubDate>Sun, 25 May 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/05/25/learning-javascript.html</guid>
			<description>以下笔记全部摘录和整理(缩减)自互联网文章, 太多了已然没法找到对应作者, 在此感谢分享者&amp;gt;) null 和 undefined Undefined相当于一个变量并没有</description>
			<content type="html"><![CDATA[<p>以下笔记全部摘录和整理(缩减)自互联网文章, 太多了已然没法找到对应作者, 在此感谢分享者&gt;)</p>
<p><img src="/imgs/front/js-advance.jpg" alt="js-advance"></p>
<h3 id="null-和-undefined">null 和 undefined</h3>
<p>Undefined相当于一个变量并没有明确的被赋值(是否被赋值, 可能无心忽略, 逻辑问题)
JS的怪异之处就在于undefined真的是一个可以使用的值。</p>
<pre><code>&gt; var foo;
&gt; foo
undefined
同理，当缺失参数时 JavaScript 会分配一个 undefined：

&gt; function id(x) { return x }
&gt; id()
undefined

a = 1;
a !== undefined // true

a = undefined
var b
a === b //true
</code></pre>
<p>Null相当于变量被明确指定了没有值，而不是由于意外的原因被忽略掉了(赋值null, 正当逻辑)</p>
<ol>
<li>参与运算</li>
</ol>
<p>JS的null如果进入运算，真的会被解析成为0或false：</p>
<p>(1 + null) # 1
(1 * null) # 0
(1 * null) # Infinity</p>
<p>undefined进入运算，一律得到NaN：</p>
<p>(1 + undefined) # NaN
(1 * undefined) # NaN
(1 / undefined) # NaN</p>
<ol start="2">
<li>逻辑判断</li>
</ol>
<p>null和undefined逻辑判断时都认为是false。</p>
<p>只用一个判断，就可以同时检验这两项是否为真:</p>
<pre><code>//也会把 false, -0, +0, NaN 与 '' 当成“空值”
if (v) {
    // v 有值
} else {
    // v 没有值
}
</code></pre>
<p>但是如果碰到大坑==的时候</p>
<pre><code>var foo;
console.log(foo == null); // true
console.log(foo == undefined); // true
console.log(foo === null); // false
console.log(foo === undefined); // true
console.log(null == undefined); // true
</code></pre>
<p>好的做法, 一律使用<code>===</code></p>
<pre><code>判断一个量已定义且非空，只使用：if (a !== null &amp;&amp; a !== undefined)。
</code></pre>
<hr>
<h3 id="-和-">=== 和 ==</h3>
<p>1.==用来判断两个值是否相等</p>
<p>当两个值类型不同时，会发生自动转换，得到的结果非常不符合直觉，这可能不是你想要的结果。</p>
<pre><code>&quot;&quot; == &quot;0&quot; // false
0 == &quot;&quot; // true
0 == &quot;0&quot; // true
false == &quot;false&quot; // false
false == &quot;0&quot; // true
false == undefined // false
false == null // false
null == undefined // true
&quot; \t\r\n&quot; == 0 // true
</code></pre>
<p>2.===</p>
<p>类型+值比较</p>
<blockquote>
<p>&ldquo;如果两边的操作数具有相同的类型和值，===返回true，!==返回false。&quot;——《JavaScript：语言精粹》</p>
</blockquote>
<p>最佳实践:</p>
<pre><code>任何时候在比较操作中使用 === 和  !==
</code></pre>
<h3 id="json操作">json操作</h3>
<pre><code>var person = {name :'Saad', age : 26, department : {ID : 15, name : &quot;R&amp;D&quot;} };

var stringFromPerson = JSON.stringify(person);
/* stringFromPerson is equal to &quot;{&quot;name&quot;:&quot;Saad&quot;,&quot;age&quot;:26,&quot;department&quot;:{&quot;ID&quot;:15,&quot;name&quot;:&quot;R&amp;D&quot;}}&quot;   */

var personFromString = JSON.parse(stringFromPerson);
/* personFromString is equal to person object  */
</code></pre>
<p>to string</p>
<pre><code>var obj = {
    name: 'myObj'
};

JSON.stringify(obj);
</code></pre>
<hr>
<h3 id="函数对象及匿名函数">函数对象及匿名函数</h3>
<p>1.函数对象赋值</p>
<pre><code>var slice_func = [].slice
//slice_func()

var a = function() {
};
// a()

var a = {
    fun : function() {
    };
}
// a.fun()

someElement.addEventListener(&quot;click&quot;, function(e) {
    // I'm anonymous!
});
</code></pre>
<p>以及</p>
<pre><code>var f = function foo(){
    return typeof foo; // foo是在内部作用域内有效
};
// foo在外部用于是不可见的
typeof foo; // &quot;undefined&quot;
f(); // &quot;function&quot;
</code></pre>
<p>匿名函数</p>
<p>from</p>
<pre><code>var name = 'Chris';
var age = '34';
var status = 'single';
function createMember(){
// [...]
}
function getMemberDetails(){
// [...]
}
</code></pre>
<p>to</p>
<pre><code>var myApplication = function(){
var name = 'Chris';
var age = '34';
var status = 'single';
return{
createMember:function(){
// [...]
},
getMemberDetails:function(){
// [...]
}
}
}();
// myApplication.createMember() and
// myApplication.getMemberDetails() now works.
</code></pre>
<hr>
<h3 id="最佳实践">最佳实践</h3>
<p>1.定义多个变量时，省略var关键字，用逗号代替</p>
<pre><code>var someItem = 'some string';
var anotherItem = 'another string';
var oneMoreItem = 'one more string';
</code></pre>
<p>更好的做法</p>
<pre><code>var someItem = 'some string',
    anotherItem = 'another string',
    oneMoreItem = 'one more string';
</code></pre>
<p>2.谨记，不要省略分号, 不要省略花括号</p>
<p>省略分号,可能导致更大的,未知的,难以发现的问题</p>
<pre><code>var someItem = 'some string'
function doSomething() {
return 'something'
}
</code></pre>
<p>更好的做法</p>
<pre><code>var someItem = 'some string';
function doSomething() {
return 'something';
}
</code></pre>
<p>3.使用{}代替 new Ojbect()</p>
<p>在JavaScript中创建对象的方法有多种。可能是传统的方法是使用”new”加构造函数，像下面这样:</p>
<pre><code>var o = new Object();
o.name = 'Jeffrey';
o.lastName = 'Way';
o.someFunction = function() {
console.log(this.name);
}
</code></pre>
<p>更好的做法</p>
<pre><code>var o = {}; //空对象

var o = {
name: 'Jeffrey',
lastName = 'Way',
someFunction : function() {
    console.log(this.name);
}
};
</code></pre>
<blockquote>
<p>只要把多个全局变量都整理在一个名称空间下，拟将显著降低与其他应用程序、组件或类库之间产生糟糕的相互影响的可能性。——Douglas Crockford</p>
</blockquote>
<p>4.使用[]代替 new Array()</p>
<pre><code>var a = new Array();
a[0] = &quot;Joe&quot;;
a[1] = 'Plumber';
</code></pre>
<p>更好的做法：</p>
<pre><code>var a = ['Joe','Plumber'];
</code></pre>
<p>5.typeof判断</p>
<p>typeof一般只能返回如下几个结果：number,boolean,string,function,object,undefined</p>
<p>expr:</p>
<pre><code>typeof xx === ''
typeof xx !== ''
</code></pre>
<p>e.g.</p>
<pre><code>// Numbers
typeof 37 === 'number';
typeof 3.14 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // 尽管NaN是&quot;Not-A-Number&quot;的缩写,意思是&quot;不是一个数字&quot;

// Strings
typeof &quot;&quot; === 'string';
typeof &quot;bla&quot; === 'string';
typeof (typeof 1) === 'string'; // typeof返回的肯定是一个字符串

// Booleans
typeof true === 'boolean';
typeof false === 'boolean';

// Undefined
typeof undefined === 'undefined';
typeof blabla === 'undefined'; // 一个未定义的变量,或者一个定义了却未赋初值的变量

// Objects
typeof {a:1} === 'object';
typeof [1, 2, 4] === 'object'; // 使用Array.isArray或者Object.prototype.toString.call方法可以分辨出一个数组和真实的对象
typeof new Date() === 'object';

// Functions
typeof function(){} === 'function';
typeof Math.sin === 'function';

typeof null === 'object'; // 从JavaScript诞生以来,一直是这样的.
</code></pre>
<p>6.三元运算符 :强大且风骚</p>
<p>语法</p>
<pre><code>expression ? xxx : yyy
</code></pre>
<p>bad</p>
<pre><code>var direction;
if(x &lt; 200){
  direction = 1;
} else {
  direction = -1;
}
</code></pre>
<p>good</p>
<pre><code>var direction = x &lt; 200 ? 1 : -1;
</code></pre>
<p>7.使用逻辑 AND/OR 做条件判断</p>
<pre><code>var foo = 10;
foo == 10 &amp;&amp; doSomething(); // 等价于 if (foo == 10) doSomething();
foo == 5 || doSomething(); // 等价于 if (foo != 5) doSomething();

//默认值
a = b || 'default'
return b || c || d &gt; 1 ? 0 : 2
</code></pre>
<p>8.给一个变量赋值的时候不要忘记使用var关键字</p>
<p>给一个未定义的变量赋值会导致创建一个全局变量。要避免全局变量</p>
<p>9.自我调用的函数</p>
<p>自调用匿名函数（Self-Invoked Anonymous Function）或者即时调用函数表达式（IIFE-Immediately Invoked Function Expression)。这是一个在创建后立即自动执行的函数</p>
<pre><code>(function(){
    // some private code that will be executed automatically
})();

(function(a,b){
    var result = a+b;
    return result;
})(10,20)
</code></pre>
<p>10.避免使用 eval() 和 Function 构造函数</p>
<p>Eval=邪恶, 不仅大幅降低脚本的性能（译注：JIT编译器无法预知字符串内容，而无法预编译和优化），而且这也会带来巨大的安全风险，因为这样付给要执行的文本太高的权限，避而远之</p>
<p>使用 eval 和 Function 构造函数是非常昂贵的操作，因为每次他们都会调用脚本引擎将源代码转换成可执行代码。</p>
<pre><code>var func1 = new Function(functionCode);
var func2 = eval(functionCode);
</code></pre>
<p>11.避免使用 with()</p>
<p>使用 with() 会插入一个全局变量。因此，同名的变量会被覆盖值而引起不必要的麻烦</p>
<p>12.脚本放在页面的底部</p>
<p>记住——首要目标是让页面尽可能快的呈献给用户，脚本的夹在是阻塞的，脚本加载并执行完之前，浏览器不能继续渲染下面的内容。因此，用户将被迫等待更长时间</p>
<p>13.避免在For语句内声明变量</p>
<p>bad</p>
<pre><code>for(var i = 0; i &lt; someArray.length; i++) {
var container = document.getElementById('container');
container.innerHtml += 'my number: ' + i;
console.log(i);
}
</code></pre>
<p>good</p>
<pre><code>var container = document.getElementById('container');
for(var i = 0, len = someArray.length; i &lt; len;  i++) {
container.innerHtml += 'my number: ' + i;
console.log(i);
}
</code></pre>
<p>14.给代码添加注释</p>
<pre><code>// 循环数组，输出每项名字（译者注：这样的注释似乎有点多余吧）.
for(var i = 0, len = array.length; i &lt; len; i++) {
console.log(array[i]);
}
</code></pre>
<p>15.instanceof</p>
<p>instanceof 方法要求开发者明确地确认对象为某特定类型</p>
<pre><code>var oStringObject = new String(&quot;hello world&quot;);
console.log(oStringObject instanceof String);   // 输出 &quot;true&quot;

// 判断 foo 是否是 Foo 类的实例
function Foo(){}
var foo = new Foo();
console.log(foo instanceof Foo)//true

// 判断 foo 是否是 Foo 类的实例 , 并且是否是其父类型的实例
function Aoo(){}
function Foo(){}
Foo.prototype = new Aoo();//JavaScript 原型继承

var foo = new Foo();
console.log(foo instanceof Foo)//true
console.log(foo instanceof Aoo)//true
</code></pre>
<p>16.apply/call</p>
<pre><code>someFn.call(this, arg1, arg2, arg3);
someFn.apply(this, [arg1, arg2, arg3]);
</code></pre>
<p>apply</p>
<pre><code>Function.apply(obj,args)方法能接收两个参数

obj：这个对象将代替Function类里this对象
args：这个是数组，它将作为参数传给Function（args--&gt;arguments）
</code></pre>
<p>call</p>
<pre><code>Function.call(obj,[param1[,param2[,…[,paramN]]]])
obj：这个对象将代替Function类里this对象
params：这个是一个参数列表
</code></pre>
<p>使用哪个取决于参数的类型</p>
<h3 id="扩展阅读">扩展阅读</h3>
<p>书籍</p>
<p><a href="http://blog.jobbole.com/8087/">Limu：JavaScript的那些书</a>
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript">mozilla js文档</a></p>
<p>编程风格</p>
<p><a href="http://www.ruanyifeng.com/blog/2012/04/javascript_programming_style.html">Javascript编程风格</a></p>
<p>this</p>
<p><a href="http://blog.jobbole.com/12203/">完全理解关键字this</a> | <a href="http://blog.jobbole.com/39305/">详解JavaScript中的this</a> | <a href="http://blog.jobbole.com/54267/">解密 JavaScript 中的 this</a>
| <a href="http://blog.jobbole.com/67347/">JavaScript中this的工作原理以及注意事项</a></p>
<p>性能</p>
<p><a href="http://blog.jobbole.com/31951/">编写快速、高效的JavaScript代码</a></p>
<p><a href="http://blog.jobbole.com/37306/">Javascript执行效率小结</a></p>
<p><a href="http://blog.jobbole.com/47304/">JavaScript的性能优化：加载和执行</a></p>
<p>闭包</p>
<p><a href="http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html">学习Javascript闭包（Closure）</a></p>
<p>模块化编程</p>
<p><a href="http://www.ruanyifeng.com/blog/2012/10/javascript_module.html">Javascript模块化编程（一）：模块的写法</a></p>
<p><a href="http://blog.jobbole.com/40409/">深入研究JavaScript的Module模式</a></p>
<p><a href="http://blog.jobbole.com/66135/">为现代JavaScript开发做好准备</a></p>
<p>面向对象</p>
<p><a href="http://blog.jobbole.com/23563/">Javascript定义类（class）的三种方法</a></p>
<p><a href="http://www.oschina.net/question/100267_52409">JavaScript面向对象15分钟教程</a></p>
<p><a href="http://blog.jobbole.com/18191/">如何编写可维护的面向对象JavaScript代码</a></p>
<p><a href="http://blog.jobbole.com/31274/">拥抱原型面向对象编程</a></p>
<p><a href="http://coolshell.cn/articles/6441.html">Javascript 面向对象编程</a> | <a href="http://coolshell.cn/articles/6668.html">再谈javascript面向对象编程</a></p>
<p><a href="http://blog.jobbole.com/38614/">全面理解面向对象的 JavaScript</a></p>
<p><a href="http://blog.jobbole.com/19795/">JavaScript原型和继承</a> | <a href="http://blog.jobbole.com/66441/">JavaScript中的原型和继承</a></p>
<p>事件</p>
<p><a href="http://blog.jobbole.com/39446/">生动详细解释javascript的冒泡和捕获</a></p>
<p><a href="http://blog.jobbole.com/52430/">DOM事件简介</a></p>
<p><a href="http://chajn.org/project/javascript-events-responding-user/">你若触发，我就处理——浅谈JavaScript的事件响应</a></p>
<p>设计模式</p>
<p><a href="http://blog.jobbole.com/25537/">理解JavaScript中的设计模式</a></p>
<p><a href="http://blog.jobbole.com/29454/">常用的Javascript设计模式</a></p>
<p><a href="http://blog.jobbole.com/9648/">理解JavaScript原型</a></p>
<p>console</p>
<p><a href="http://blog.segmentfault.com/classicemi/1190000000481884">你真的了解console吗?</a></p>
<p><a href="http://blog.jobbole.com/60787/">通过console.table()做高级JavaScript调试</a></p>
<p>tools</p>
<p><a href="http://blog.jobbole.com/64771/">JavaScript基础工具清单</a></p>
<p><a href="http://blog.jobbole.com/60938/">给开发者提供的 35 款 JavaScript 图形图表库</a> + <a href="http://echarts.baidu.com/">百度图标库Echarts</a></p>
<p>其他</p>
<p><a href="http://blog.jobbole.com/60245/">Responsive Javascript 是什么？</a></p>
<p><a href="http://blog.jobbole.com/53487/">JavaScript 跨域总结与解决办法</a></p>
<p><a href="http://www.ruanyifeng.com/blog/2011/06/10_design_defects_in_javascript.html">Javascript的10个设计缺陷</a></p>
<p><a href="http://blog.jobbole.com/43649/">从零开始写JavaScript框架（一）</a> | <a href="http://blog.jobbole.com/43663/">从零开始写JavaScript框架（二</a></p>
<p><a href="http://blog.jobbole.com/52069/">JavaScript核心</a></p>
<p><a href="http://blog.jobbole.com/39571/">JavaScript必知必会+理解总结</a></p>
<p><a href="http://blog.jobbole.com/47296/">JavaScript变量作用域之殇</a></p>
<p><a href="http://blog.jobbole.com/66699/">JavaScript 的常见“陷阱”</a></p>
]]></content>
		</item>
		
		<item>
			<title>我的mac app列表 </title>
			<link>https://wklken.me/posts/2014/05/24/my-mac-app-list.html</link>
			<pubDate>Sat, 24 May 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/05/24/my-mac-app-list.html</guid>
			<description>我曾经说过, 买mac是2013年自己做的最正确的决定之一 转眼一年多过去了, mac为我节省的时间,带来的便捷,以及给自己工作和生活带来的变化是</description>
			<content type="html"><![CDATA[<hr>
<p>我曾经说过, 买mac是2013年自己做的最正确的决定之一</p>
<p>转眼一年多过去了, mac为我节省的时间,带来的便捷,以及给自己工作和生活带来的变化是没法估量的</p>
<p>整理下自己app列表, 瞅了下连带系统自带的一共125个app</p>
<p><img src="/imgs/resources/my-apps.jpeg" alt="myapps"></p>
<hr>
<h3 id="效率">效率</h3>
<blockquote>
<p>Alfred 2</p>
</blockquote>
<p>神器,不解释</p>
<blockquote>
<p>DEVONthink Pro</p>
</blockquote>
<p>资源素材收集整理分类, 知识管理</p>
<blockquote>
<p>OmniFocus Pro</p>
</blockquote>
<p>GTD</p>
<blockquote>
<p>Vitanmin-R 2</p>
</blockquote>
<p>番茄时间工作法</p>
<blockquote>
<p>BetterTouchTool</p>
</blockquote>
<p>触控板/鼠标功能扩展</p>
<blockquote>
<p>Keyboard Maestro</p>
</blockquote>
<p>键盘大师, 门槛较高</p>
<blockquote>
<p>aText</p>
</blockquote>
<p>关键字扩展 (准备尝试TextExpander)</p>
<blockquote>
<p>Moom</p>
</blockquote>
<p>窗口管理</p>
<blockquote>
<p>ClipMenu</p>
</blockquote>
<p>剪贴板</p>
<blockquote>
<p>PopClip</p>
</blockquote>
<p>类iphone选中工具,拥有很多给力的插件,例如打开选中下的链接</p>
<blockquote>
<p>1Password 4</p>
</blockquote>
<p>密码管理</p>
<blockquote>
<p>Bartender</p>
</blockquote>
<p>MenuBar管理</p>
<blockquote>
<p>HyperSwitch</p>
</blockquote>
<p>最好的切换工具(better than km or manico)</p>
<blockquote>
<p>Manico</p>
</blockquote>
<p>应用切换, 辅助</p>
<h3 id="编辑器">编辑器</h3>
<blockquote>
<p>MacVim</p>
</blockquote>
<p>编辑器之神,不解释, 配置见 <a href="https://github.com/wklken/k-vim">k-vim</a></p>
<blockquote>
<p>Mou</p>
</blockquote>
<p>markdown编辑</p>
<blockquote>
<p>haroopad</p>
</blockquote>
<p>markdown编辑</p>
<blockquote>
<p>Sublime Text</p>
</blockquote>
<p>源码查看</p>
<blockquote>
<p>Chocolat</p>
</blockquote>
<p>临时文件编辑</p>
<blockquote>
<p>Day One</p>
</blockquote>
<p>日记</p>
<blockquote>
<p>iA Writer</p>
</blockquote>
<p>写作工具,支持markdown</p>
<blockquote>
<p>Atom</p>
</blockquote>
<p>下一代编辑器,下了尝鲜</p>
<h3 id="开发">开发</h3>
<blockquote>
<p>iTerm</p>
</blockquote>
<p>终端利器</p>
<blockquote>
<p>Dash</p>
</blockquote>
<p>文档速查</p>
<blockquote>
<p>Xcode</p>
</blockquote>
<p>写app用</p>
<blockquote>
<p>CodeBox</p>
</blockquote>
<p>代码片段</p>
<blockquote>
<p>OhMyStar</p>
</blockquote>
<p>github star项目管理</p>
<blockquote>
<p>Gas Mask</p>
</blockquote>
<p>host快速切换</p>
<h3 id="浏览器及阅读">浏览器及阅读</h3>
<blockquote>
<p>Google Chrome</p>
</blockquote>
<p>一号浏览器</p>
<blockquote>
<p>Firefox</p>
</blockquote>
<p>二号浏览器</p>
<blockquote>
<p>Safari</p>
</blockquote>
<p>三号浏览器</p>
<blockquote>
<p>Pocket</p>
</blockquote>
<p>read it later</p>
<blockquote>
<p>ReadKit</p>
</blockquote>
<p>rss 订阅</p>
<blockquote>
<p>Kindle</p>
</blockquote>
<p>电子书</p>
<h3 id="社交">社交</h3>
<blockquote>
<p>QQ</p>
</blockquote>
<p>常年挂机</p>
<blockquote>
<p>RTX</p>
</blockquote>
<p>内部交流</p>
<blockquote>
<p>WeChat</p>
</blockquote>
<p>微信,妈蛋这货不保存聊天记录</p>
<blockquote>
<p>AliWangwang</p>
</blockquote>
<p>淘宝</p>
<h3 id="云">云</h3>
<blockquote>
<p>Dropbox</p>
</blockquote>
<p>重要文件</p>
<blockquote>
<p>Evernote</p>
</blockquote>
<p>云笔记</p>
<blockquote>
<p>百度云同步盘</p>
</blockquote>
<p>资源文件</p>
<h3 id="gtd">GTD</h3>
<blockquote>
<p>Wunderlist</p>
</blockquote>
<p>记录待办事项</p>
<blockquote>
<p>Fantastical</p>
</blockquote>
<p>日历管理</p>
<blockquote>
<p>Eggscellent</p>
</blockquote>
<p>番茄钟</p>
<h3 id="界面">界面</h3>
<blockquote>
<p>Flux</p>
</blockquote>
<p>色温调整</p>
<blockquote>
<p>爱壁纸HD</p>
</blockquote>
<p>壁纸管理</p>
<h3 id="影音">影音</h3>
<blockquote>
<p>MPlayerX</p>
</blockquote>
<p>播放器</p>
<blockquote>
<p>Xiami</p>
</blockquote>
<p>虾米</p>
<blockquote>
<p>diumoo</p>
</blockquote>
<p>豆瓣</p>
<blockquote>
<p>iTunes</p>
</blockquote>
<p>自带</p>
<h3 id="办公">办公</h3>
<blockquote>
<p>Keynote
Numbers
Pages</p>
</blockquote>
<p>三件套</p>
<blockquote>
<p>XMind</p>
</blockquote>
<p>脑图</p>
<blockquote>
<p>Airmail</p>
</blockquote>
<p>邮件</p>
<blockquote>
<p>Swift Publisher 3</p>
</blockquote>
<p>pages 增强</p>
<blockquote>
<p>Balsamiq Mockups</p>
</blockquote>
<p>原型图</p>
<h3 id="系统工具">系统工具</h3>
<blockquote>
<p>Parallels Desktop</p>
</blockquote>
<p>虚拟机</p>
<blockquote>
<p>Path Finder</p>
</blockquote>
<p>增强finder</p>
<blockquote>
<p>CleanMyMac 2</p>
</blockquote>
<p>垃圾清理</p>
<blockquote>
<p>Snip</p>
</blockquote>
<p>切图</p>
<blockquote>
<p>Folx 3 / Thunder</p>
</blockquote>
<p>下载</p>
<blockquote>
<p>Caffeine</p>
</blockquote>
<p>保持屏幕常亮</p>
<blockquote>
<p>Lock Screen Plus</p>
</blockquote>
<p>锁屏</p>
<blockquote>
<p>Keka</p>
</blockquote>
<p>压缩解压</p>
<blockquote>
<p>GoAgentX</p>
</blockquote>
<p>科学上网</p>
<blockquote>
<p>LICEcap</p>
</blockquote>
<p>录屏</p>
<blockquote>
<p>Better Rename 9</p>
</blockquote>
<p>批量文件重命名</p>
<blockquote>
<p>Gemini</p>
</blockquote>
<p>重复文件查找</p>
<blockquote>
<p>MacHider</p>
</blockquote>
<p>文件隐藏</p>
<blockquote>
<p>MagicanRest</p>
</blockquote>
<p>休息提醒</p>
<blockquote>
<p>smcFanControl</p>
</blockquote>
<p>自动控制风扇</p>
<h3 id="其他">其他</h3>
<blockquote>
<p>LIMBO</p>
</blockquote>
<p>一个很虐的游戏</p>
]]></content>
		</item>
		
		<item>
			<title>读书笔记--你就是极客!软件开发人员生存指南</title>
			<link>https://wklken.me/posts/2014/05/03/beinggeek.html</link>
			<pubDate>Sat, 03 May 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/05/03/beinggeek.html</guid>
			<description>BeingGeek:The Software Developer&amp;rsquo;s Carrer Handbook 五一三天,读完五本书&amp;hellip;..这本算是重读 写作风格类似&amp;lt;黑客与画家&amp;gt; 10分标准,打个7.5 个人职业生涯面临的</description>
			<content type="html"><![CDATA[<p>BeingGeek:The Software Developer&rsquo;s Carrer Handbook</p>
<p><img src="/imgs/books/being-geek.jpg" alt="封面"></p>
<p>五一三天,读完五本书&hellip;..这本算是重读</p>
<p>写作风格类似&lt;黑客与画家&gt;</p>
<p>10分标准,打个7.5</p>
<p>个人职业生涯面临的种种境遇和问题:跳槽,面试,管理,招聘,危机处理,沟通,合作,团队建设,演讲,绩效考核等,甚至还有一篇是专门写给我们背后的支持者的(例如女朋友,如果有的话&hellip;..).</p>
<p>书上关于 团队管理,沟通,换工作的几章,可以重点看下</p>
<p>里面包含了很多观点,隔一段时间来重看认识会有不同,摘录几点&hellip;..算是读书摘要吧</p>
<hr>
<p>阅读时,要不断重复3个问题(明确方向)</p>
<pre><code>1.我正在做什么?
2.我真正想做的是什么?
3.我在乎的是什么?我关心的是什么?
</code></pre>
<h3 id="致胜之道">致胜之道</h3>
<p>系统思维:</p>
<pre><code>我们寻找&quot;定义&quot;来了解
&quot;系统&quot;,以便找出
&quot;规则&quot;,这样我们才能
明白下一步该做什么
并最终获得胜利
</code></pre>
<p>只要投入足够的时间和精力,是可以完全地认识系统的</p>
<p>麻烦的&quot;人&quot;, 人总是会把事情搞砸,他们是系统故障的根源</p>
<p>提高胜率:为不可预测事件做好准备</p>
<p>只有自己,才能成就自己的事业.认为身边所有人都要对你的职业生涯负责的想法是错误的.</p>
<h3 id="三要素">三要素</h3>
<p>将职业发展与管理哲学归结为三要素</p>
<pre><code>1.技术方向
最接近代码的人最有资格为工作设定技术方向
你才是代码的拥有者,你需要关心它,每天都要
你是否主动地确定产品的技术方向?

2.保持成长
不进则退,不胜则亡,成长的基本单位是知识
增长代表一种战略.可以学到更多,完成更多的工作,获得晋升,改善自己,并承担更多责任.
你是否明白要做的哪些事情才能一直成长?

3.交付工作
言出必行,兑现承诺,重视自己的信誉
你是否能按时完成工作?兑现承诺?言出必行?
</code></pre>
<h3 id="工作之痒">工作之痒</h3>
<p>检验:我满意现在的工作么?你对自己的工作有多投入?你的动机是什么?</p>
<p>不要在一气之下换工作.没有什么比愤怒更能让人失去理智.</p>
<p>你需要以自信的立场来考虑换工作.不应该是为了逃避一个错误.而应该是去迎接一个新的机遇.</p>
<p>关于移交工作:工作永远是做不完的,从来没有哪个时刻是适合辞职离开的</p>
<h3 id="公司文化">公司文化</h3>
<p>如果想获得提拔,就要成功地满足某一群人的需求.</p>
<p>问题是,永远要想在管理着前面,</p>
<h3 id="沟通">沟通</h3>
<p>不要用借口来回答问题</p>
<p>每次开口,应该言之有物,在令人不安的沉默中,应该坚持这种观点,而不是把自己放在受害者的角度上寻求借口</p>
<h3 id="处理危机">处理危机</h3>
<p>几点:</p>
<pre><code>1.如果缺乏指引,人么会追随错误的导向

2.人们不停地讨论正在面对的危机,以互相提供群体治疗

3.每个人都想了解全部的真相
</code></pre>
<p>三场会议</p>
<pre><code>1.一对一面谈
2.部门会议
3.成果检查会议
</code></pre>
<h3 id="系统的游戏化">系统的游戏化</h3>
<p>让某些事情变得有趣,积极参与</p>
<pre><code>1.规则必须是明确的
2.规则必须是不可违反的
3.不到万不得已,不要拿金钱作为奖励手段
</code></pre>
<h3 id="后巷桥牌">后巷桥牌</h3>
<p>在工作中,应该和同事保持多近的距离?</p>
<p>彼此信任和尊重的团队会更加多产且高效.我们的目标不适合每个人都成为朋友,而是建立一组关系,这组关系中,我们互相相信对方是可靠地,诚实的,有能力的,有毅力的.</p>
<h3 id="任务列表">任务列表</h3>
<p>每天的任务事项:</p>
<pre><code>1.今天,必须今天完成的
2.稍后,不需要今天,稍后搞定即可
3.永不,我永远不会去完成这项任务
</code></pre>
<p>不需要等级结构,标签,不需要优先级,不需要设定日期.</p>
<p>保持极简</p>
<h3 id="涓滴列表">涓滴列表</h3>
<p>坚持长期投入零散的时间,最后能做到的就远远超乎自己的想象.</p>
<p>例如:人,写作,读书&hellip;.</p>
<p>充分利用零散时间</p>
<h3 id="工具的狂热法则">工具的狂热法则</h3>
<p>合适的工具将使生产力指数增长</p>
<p>狂热法则:</p>
<pre><code>1.貌似简单
2.不受工作场所限制
3.设计的目的都是免除重复动作
4.只做我让他们做的事情
5.只属于我自己
6.我的工具一直为自己的生存而战
</code></pre>
<h3 id="面对责难">面对责难</h3>
<pre><code>1.承认你搞砸了这个问题
2.承认&quot;我不知道&quot;
3.具体地解释你准备查明问题的步骤,并给自己定下最后期限.
</code></pre>
<h3 id="绩效考核">绩效考核</h3>
<p>描述:</p>
<pre><code>1.完成了什么工作
2.完成得如何
3.下一步需要做什么
</code></pre>
<h3 id="对机遇的完整步法">对机遇的完整步法</h3>
<pre><code>1.我打算去哪里?
2.我打算创造什么?
3.我打算如何创造出来?
</code></pre>
<h3 id="注意缺口">注意缺口</h3>
<p>每个人都是可替代的</p>
<h3 id="经验">经验</h3>
<p>你的经验和你努力拼搏的过去,这些给你做决定所以来的有用的,有价值的直觉</p>
<p>经验有半衰期</p>
<p>了解我的世界,预测下一步会发生什么?</p>
<pre><code>仅仅是现在做得很棒,并不意味着你是成功的
困难是有益的
冲突就是学习
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>后端不高兴——关于协作和沟通</title>
			<link>https://wklken.me/posts/2014/04/24/unhappy-about-cooperation-and-communication.html</link>
			<pubDate>Thu, 24 Apr 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/04/24/unhappy-about-cooperation-and-communication.html</guid>
			<description>================== 再过两月毕业三周年，回顾一下，突然想起了一些问题，顺手记录一下 选择后端的原因之一，代码写得好一些，然后改需求的时候，刷刷刷改几行代码发布，</description>
			<content type="html"><![CDATA[<p>==================</p>
<p>再过两月毕业三周年，回顾一下，突然想起了一些问题，顺手记录一下</p>
<p>选择后端的原因之一，代码写得好一些，然后改需求的时候，刷刷刷改几行代码发布，然后泡杯茶站在前端同学后面看他们苦逼地改页面:)</p>
<p><img src="/imgs/blabla/unhappyface.jpg" alt="unhappy"></p>
<p>工作那么久，逐渐变得“冷血”，要学会说no，学会排期，学会去“降低”一些人的期望甚至是“无情”扼杀，所以或许初次合作会给人一种不“友好”的印象。</p>
<p>但是在“友好”和“效率”，我选择了后者，记得当初很菜很菜的时候，我还是很“友好”的。(妈蛋，一天要处理N多问题，跟运营运维产品前端测试等等沟通，还得挤时间码代码测试发布上线，只能高效至上了)</p>
<p>不扯了，归正题，聊一些平时沟通合作遇到的问题</p>
<p>PS:这里后端偏指服务端开发，当然，问题普适于各个角色</p>
<hr>
<h3 id="1-问问题的方式">1. 问问题的方式</h3>
<p>之前在微博看见人说过的一个现象，再次提一下</p>
<p>假如有人找你咨询一个问题</p>
<pre><code>A: 在? (在么?/在不在?)
B: 额
A: xxx功能你知道么？
B: 知道
A: xxx有个功能有问题了，能帮看下么
B: 什么地方
A: xxxxxxxx
B: 算了你切个图过来吧
A: [图]
</code></pre>
<p>这个，首先，可能我在写代码，闪动弹窗对有轻微强迫症的人来说，必须要点开的啊（妈蛋），然后，我即时响应了，可能由于你有事什么的，过一会回答，但是这个等待过程中我又很那去投入一件事，很容易投入没几分钟被打断&hellip;这几句对话可能跨度是几分钟，甚至几个钟头（跑去吃了个饭回来看到消息给才给回复，哥都快忘了），其次，我真的很吝啬打那么多字&gt;_&lt;#</p>
<p>效率杀手之一</p>
<p>最有效率的其实是：</p>
<pre><code>A: 在吗?xxx功能你知道么?现在有问题了，xxx异常，访问链接 http://xxxxxxx，截图如下
   （给力一点的还会圈起来标注说明）
B: ok，稍等我看下
.....
B: 已修复，验证下
A: 好的
</code></pre>
<p>感受下吧</p>
<p>推荐一篇文章 <a href="http://macshuo.com/?p=367">如何提问</a> | 一本书 <a href="http://book.douban.com/subject/20428922/">学会提问</a></p>
<h3 id="2-搞明白这事谁的问题">2. 搞明白“这事谁的问题”</h3>
<p>当一个人发现一个问题的时候，总是很兴奋地，迫不及待想要证实</p>
<p>然后就会有如下</p>
<pre><code>A: xxx系统是不是挂了，我登陆不上去【有时候是页面差异，系统操作失败等】

B: 稍等，我看下
三分钟后
B: 后台正常，刚才重新走了一遍流程，没问题，[切图]
   你再试下，可能xxx问题
A: 我忘改xxx了/登陆超时了/xxx了/我忘记做xxx操作了
</code></pre>
<p>吐血的问题，很多时候，都是自己机器的问题，可能是host/网络/浏览器/系统登陆等等因素导致的，但是大多数人已发现问题，总是认为是别人的问题(系统的问题)，然后迫不及待，然后，我们要花费时间来排查各个可能的问题，跳转N多机器，检查N多服务，而且，相信我，这个过程不会很有趣，而且问题本身的种类很难导致很难将其自动化&hellip;.《论排查问题的复杂性》</p>
<p>当然，如果是可以自动化而你偏偏要每次人肉查而且乐在其中，那我没办法了。</p>
<p>后来就变成</p>
<pre><code>A: xxx系统是不是挂了，我点发布了但是前台没更新

B: 重做一遍，10分钟后没更新通知我 【够冷血吧】
</code></pre>
<p>这类问题次数少也就忍了，担心的是对应人员新人进来的时候，没有老员工带或者培训，那么系统开发者往往一次又一次成为“义务培训导师”&hellip;..</p>
<p>另外遇到问题也要忍住证实的欲望，先自己确认下</p>
<p>有一本书推荐《你的灯亮着么》</p>
<h3 id="3-suprise">3. Suprise</h3>
<p>后端最讨厌的是suprise，安排和做事情的节奏都会被打乱。</p>
<p>我的原则是，不接受&amp;unhappy，紧急的会去配合做，当然心情不会happy</p>
<p>方案设计，评审，开发，测试那么多环节都没发现，要上线前，需求变更或者xxx有问题，suprise</p>
<p>然后，上线时间有时候又是固定的，所以必须要配合处理</p>
<p>当然，作为一枚“有责任感”的后端，肯定都会全力配合处理，不高兴是一回事，把事情完美地搞完是一回事。</p>
<p>但这时候，往往会发现，时间不等人，所有人都盯着你，瞬间亚历山大，如果没搞好还很容易成为“责任人”，延期什么的很容易落到你头上，属于吃力不讨好的角色，前期按时完成，suprise却要背负所有，心里瞬间失衡(╯‵□′)╯︵┻━┻</p>
<p>问题是，这个suprise的来源. 才是问题所在，更多的应该反思这里，否则很容易造成后续合作困难。</p>
<p>另外，suprise很容易导致黑逻辑、补丁、牛皮糖、硬编码等等，将一块干净的自留地变成垃圾桶，而且破窗理论，所以，要控制。</p>
<h3 id="4-要明白一个道理">4. 要明白一个道理</h3>
<p>1个人1个月能干好一件事情，不代表30个人在1天能把这件事做了</p>
<p>很简单的一个道理，但是很容易被人忽视</p>
<h3 id="5-关于估时间">5. 关于估时间</h3>
<p>后端需要信任，虽然我们有时候估时间不大准，但是基本都能在少于估算的时间内完成，超过的情况并不多。</p>
<p>而且随着工作经验积累，估算时间会越来越准。(三小时就是三小时，额，上下误差几分钟)</p>
<p>一般问题过来，很简单顺手做了，复杂的会给个完成时间。</p>
<p>不要站在自己的角度去给后端的估时间，常见的理由是：“就简单加一个xxx”，“修改一下而已”等，你要知道有些系统并没有那么简单，你要的可能是一个现在能用的东西，但我们需要一个以后无论怎么改都好改而且能用的东西。（相信我，差异很大），同理，不要估工作量啊（2天的量估0.5天，要做完，那另外1.5天怎么破，哥做不到&gt;_&lt;）</p>
<p>记住，除非火烧眉毛，否则，能在承诺完成时间内搞定的，不要催。</p>
<p>我们需要的是冷静和清晰的思路。催促和打断于事无补。</p>
<h3 id="6-上帝同一时间内有且仅只有一个">6. 上帝，同一时间内有且仅只有一个</h3>
<p>所有人，都认为自己的需求优先级最高。</p>
<p>对后端来说，合作和沟通的每个人都是上帝。</p>
<p>但是要记住，上帝，同一时刻内只有一个。</p>
<p>所以，有了优先级这一说，会排期，一次只做一件事。</p>
<p>再给力的后端也不是超人，一次处理N件事情效率很低容易出错，非常不明智的</p>
<p>所以，要学会接受排期，除非排的时间不合理要去沟通。</p>
<p>你会发现，在截止日期到来的前一刻，后端小伙伴的东西已经搞完提供了。</p>
<h3 id="7-合理使用项目管理工具邮件im电话">7. 合理使用项目管理工具/邮件/IM/电话</h3>
<p>综合使用工具进行沟通</p>
<p>涉及项目跟踪通知等，请用项目管理工具(目前tower)</p>
<p>极重要事情，请邮件</p>
<p>极紧急事情，请当面，或者电话</p>
<p>其他，IM</p>
<p>小事，确认，疑问，突发奇想？灵机一动？等等，注意除非这个沟通有可能导致你必须要去等，否则不到万不得已不要杀过去打断一个程序员的思路。</p>
<p>打断是效率杀手，如果不用电话，那恕我只能定期去查，异步回复，所以就不要傻等。</p>
<h3 id="8-被开会">8. 被开会</h3>
<p>不重要的会议不要勾选抄送我，谢谢</p>
<p>冗长无聊相关性不大的会议允许早退，或者过了跟自己相关的部分允许早退</p>
<p>超紧急临时会议，没问题</p>
<p>其他临时会议，没提前发邀约提前通知的会议都是耍流氓，要知道本来用来码代码的两小时被突如其来的会议占用，打乱了计划安排，那么晚上就得花一个小时加班补回来【别问我为啥是一个小时】</p>
<h3 id="9-拿到承诺">9. 拿到承诺</h3>
<p>如果有一件事，你提给后端，不说明问题，优先级，截止时间，或者，压根你就扔给后端就不管了。（被动等待会等到海枯石烂的）</p>
<p>然后你期望他把事情给做了，把结果给你，或者将事情跟完。很多时候，这都是你的幻想。</p>
<p>如果我们没做出承诺，可能这件事情就被排后了，至于完成的时间，完全与取决于工作安排和工作量。</p>
<p>所以，一般提给后端的时候，可以多问一句，“什么时候能搞定”，拿到承诺，一般没问题了。</p>
<h3 id="10-当面沟通时提供上下文">10. 当面沟通时提供上下文</h3>
<p>遇到次数不多，但是还是会遇到，干活中都是有“状态”的</p>
<p>有时候在做一件事情，一半</p>
<p>然后例如这样</p>
<pre><code>A: 把xxx字段改成xxxx.... blablablabla.......

B: 啊？（脑补痴呆状）
</code></pre>
<p>或者这样</p>
<pre><code>A: 把xxx字段改成xxxx.... blablablabla.......

B: 不合适吧，xxxxxxx.......

A: 我在说xx问题

B: 我以为你说XXX问题
</code></pre>
<p>其实可以这样:</p>
<pre><code>A: 关于xx的问题，我们..........
</code></pre>
<hr>
<p>你要相信，大部分后端都是善良的孩子&hellip;&hellip;</p>
<p>而且大部分后端，一个人干N个人的活(N&gt;=2), (╯‵□′)╯︵┻━┻</p>
]]></content>
		</item>
		
		<item>
			<title>JavaScript一站式入门笔记</title>
			<link>https://wklken.me/posts/2014/04/20/javascript-base.html</link>
			<pubDate>Sun, 20 Apr 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/04/20/javascript-base.html</guid>
			<description>大二开始接触的javascript，到现在，五年过去了，中间断断续续用了一些，但是主要是搞后端的，没有正儿八经“学习”一下。 对其了解不成系统</description>
			<content type="html"><![CDATA[<p><img src="/imgs/front/javascript.png" alt="封面"></p>
<hr>
<p>大二开始接触的javascript，到现在，五年过去了，中间断断续续用了一些，但是主要是搞后端的，没有正儿八经“学习”一下。</p>
<p>对其了解不成系统，最近做了一个系统涉及前端框架，所以借此机会学习总结了下。</p>
<p>入门笔记，非初学者可以绕道哈:)</p>
<p>后续打算深入学习一下，顺带去玩玩node.js</p>
<hr>
<p>参考教程</p>
<p>入门: 【已完成】</p>
<ul>
<li><a href="http://gitbookio.github.io/javascript/">gitbook javascript教程</a> 【简单】</li>
<li><a href="http://www.w3school.com.cn/js/index.asp">w3cschool教程</a> 【简单，较全】</li>
<li><a href="https://learn.jquery.com/javascript-101/">Jquery:JavaScript101</a> 【质量高】</li>
<li><a href="http://www.learn-javascript-tutorial.com/">JavaScript Tutorial</a> 【全面，不错】</li>
</ul>
<p>进阶：【待阅读】</p>
<ul>
<li><a href="http://ejohn.org/apps/learn">Learning Advanced JavaScript</a></li>
</ul>
<p>其他资料</p>
<ul>
<li><a href="http://superherojs.com/">javascript资料大全-superherojs</a></li>
<li><a href="http://jstherightway.org/">javascript the right way</a></li>
</ul>
<p>书籍</p>
<ul>
<li>javascript 权威指南</li>
</ul>
<p>注意：笔记全部来自于以上资料</p>
<h2 id="目录">目录</h2>
<pre><code> 第一部分：简介(Getting Started)
  1.HTML/CSS/JavaScript
  2.JavaScript书写和位置
  3.简单语法
 第二部分：基本语法
  1.变量(varivalbes)
  2.操作符(operators)
  3.真值判断 - 比较
  4.控制流
    分支
    循环
 第二部分：函数
  1.Functions
  2.global functions
 第三部分：对象
  1.Array
  2.Object
 第四部分：内置对象
  1.字符串(string)
  2.math
  3.date
  4.boolean
 第五部分: DOM 操作
 第六部分：其他
  1.正则
  2.this关键字
  3.异常处理
  4.作用域
    Global Scope
    Local Scope
  5.闭包
  6.typeof(Testing Type)
  7.Timers
</code></pre>
<hr>
<h2 id="第一部分简介getting-started">第一部分：简介(Getting Started)</h2>
<h3 id="1htmlcssjavascript">1.HTML/CSS/JavaScript</h3>
<p>关系</p>
<pre><code>HTML is for Content
CSS is for Presentation
JavaScript is for Interactivity
</code></pre>
<h3 id="2javascript书写和位置">2.JavaScript书写和位置</h3>
<p>external,外部js文件引入</p>
<pre><code>&lt;!-- Code is written in a .js file, included via the script tag src attribute. --&gt;
&lt;script src=&quot;/path/to/example.js&quot;&gt;&lt;/script&gt;
</code></pre>
<p>inline,本页面</p>
<pre><code>&lt;!-- Embed code directly on a web page using script tags. --&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
alert( &quot;Hello World!&quot; );
&lt;/script&gt;
</code></pre>
<p>attribute</p>
<pre><code>&lt;!-- Inline code directly on HTML elements being clicked. --&gt;
&lt;a href=&quot;javascript:alert( 'Hello World' );&quot;&gt;Click Me!&lt;/a&gt;
&lt;button onClick=&quot;alert( 'Good Bye World' );&quot;&gt;Click Me Too!&lt;/button&gt;
</code></pre>
<p>BP:将<code>&lt;script&gt;</code>放到页面底部，<code>&lt;body&gt;</code>标签之前</p>
<pre><code>&lt;!doctype html&gt;
&lt;html&gt;
&lt;head&gt;&lt;/head&gt;
&lt;body&gt;

&lt;h1 id=&quot;hello-world&quot;&gt;Hello World&lt;/h1&gt;
&lt;script&gt;
// Moving the script to the bottom of the page will make sure the element exists.
var title = document.getElementById( &quot;hello-world&quot; );
console.log( title );
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<h3 id="3简单语法">3.简单语法</h3>
<p>code sample</p>
<pre><code>var hello = &quot;Hello&quot;;
var world = &quot;World&quot;;

// Message equals &quot;Hello World&quot;
var message = hello + &quot; &quot; + world;
</code></pre>
<p>分号 ;</p>
<pre><code>分号用于分隔 JavaScript 语句。
通常我们在每条可执行的语句结尾添加分号。
使用分号的另一用处是在一行中编写多条语句。
</code></pre>
<p>注释</p>
<pre><code>// 单行,This is a comment, it will be ignored by the interpreter
var a = &quot;this is a variable defined in a statement&quot;;

/*
多行
This is a multi-line comment,
it will be ignored by the interpreter
*/
var a = &quot;this is a variable defined in a statement&quot;;
</code></pre>
<p>对代码行进行折行  - 可以对js代码通过反斜杠，拆分换行</p>
<p>JavaScript 会忽略多余的空格。可以向脚本添加空格，来提高其可读性。(空格和折行无关紧要)</p>
<p>javaScript 中的所有事物都是对象：字符串、数字、数组、日期，等等。</p>
<hr>
<h2 id="第二部分基本语法">第二部分：基本语法</h2>
<h3 id="1变量varivalbes">1.变量(varivalbes)</h3>
<p>命名</p>
<pre><code>必须以字母开头, 也能以 $ 和 _ 符号开头（不过我们不推荐这么做）
可以包含数字
名称大小写敏感（y 和 Y 是不同的变量）
不能使用保留字
</code></pre>
<p>注意：变量命名不要使用保留字 <a href="https://learn.jquery.com/javascript-101/reserved-words/">Reserved Words</a></p>
<p>声明和赋值</p>
<pre><code>var age;
age = 26;
//or
var age = 26;

var age, height, weight, gender;
//or
var name=&quot;Gates&quot;, age=56, job=&quot;CEO&quot;;

//注意，只声明不赋值，其值为undefined
var x;
x === undefined; // true

//=================

// This works:
var test = 1;
var test2 = function() { ... };
var test3 = test2( test );

// And so does this:
var test4 = 1,
    test5 = function() { ... },
    test6 = test2( test );
</code></pre>
<p>变量类型 Types</p>
<pre><code>Numbers
    Float: a number, like 1.21323, 4, -33.5, 100004 or 0.123
    Integer: a number like 1, 12, -33, 140 but not 1.233
String: a line of text like &quot;boat&quot;, &quot;elephant&quot; or &quot;damn, you are tall!&quot;
Boolean: either true or false, but nothing else
    // Boolean values.
    var okay = true;
    var fail = false;
Arrays: a collection of values like: 1,2,3,4,'I am bored now'
Objects: a representation of a more complex object

null
    // Define a null value. 可用于清空变量
    var foo = null;
undefined, 表示变量不含值
    // Two ways to achieve an undefined value.
    var bar1 = undefined;
    var bar2;
</code></pre>
<h3 id="2操作符operators">2.操作符(operators)</h3>
<p>算术(Arithmetic Operators)</p>
<pre><code>Operator Description
+        Addition
-        Subtraction
*        Multiplication
/        Division
%        Modulus (remainder)
++       Increment by one
--       Decrement by one

Increment: Given a = 5
    c = a++, Results: c = 5 and a = 6
    c = ++a, Results: c = 6 and a = 6
Decrement: Given a = 5
    c = a--, Results: c = 5 and a = 4
    c = --a, Results: c = 4 and a = 4
</code></pre>
<p>注意</p>
<pre><code>减法(-)
将两边的操作数都转换为数字

加法(+)
1. 两边的操作数首先被转换成原始值.这里我们称之为A 和 B.
2. 如果有任意一个原始值是字符串,则把另一个也转换成字符串,执行A和B的连接操作并返回连接后的字符串.
3. 否则把A和B都转换为数字,返回两个数字的和

&lt; 同 +
</code></pre>
<p>赋值操作符(Assignment Operators)</p>
<pre><code>Operator    Description
=           Assignment, a = 3
+=          (a+=3 is the same as a=a+3)
-=          (a-=3 is the same as a=a-3)
*=          (a*=3 is the same as a=a*3)
/=          (a/=3 is the same as a=a/3)
%=          (a%=3 is the same as a=a%3)
</code></pre>
<p>字符串操作符(String Operators)</p>
<pre><code>Operator    Description
+           Concatenation (var greeting = &quot;Hello &quot; + firstname;)
+=          One step concatenation and assignment (var greeting = &quot;Hello &quot;; greeting += firstname;)

&quot;hello&quot;.concat(&quot; world&quot;)// &quot;hello world&quot;

注意区别
// Addition vs. Concatenation
var foo = 1;
var bar = &quot;2&quot;;
console.log( foo + bar ); // 12

// Coercing a string to act as a number.
var foo = 1;
var bar = &quot;2&quot;;

console.log( foo + Number(bar) ); // 3
</code></pre>
<p>三元运算(Ternary Operator)</p>
<pre><code>test ? expression1 : expression2

test
任何 Boolean 表达式。
expression1
如果 test 为 true，则返回表达式。 可能是逗号表达式。
expression2
如果 test 为 false，则返回表达式。 可以使用逗号表达式链接多个表达式。

e.g.
// Set foo to 1 if bar is true; otherwise, set foo to 0:
var foo = bar ? 1 : 0;
</code></pre>
<p>默认值(Default Operator)</p>
<pre><code>Operator    Description
||          Used to assign a default operator

var yourName = prompt(&quot;Your Name?&quot;,&quot;&quot;) || &quot;Stranger&quot;;
</code></pre>
<p>比较运算符(Comparison operators)</p>
<pre><code>==  Equals
!=  Doesn't equal
=== Strictly equals
!== Doesn't strictly equal
&gt;   Is greater than
&lt;   Is less than
&gt;=  Is greater than or equal to
&lt;=  Is less than or equal to

== / === 的区别
== 值相等， === 值相等，且类型相同

BP: 相等比较总是使用 === 和 !===

//=====================

e.g.
var foo = 1;
var bar = 0;
var baz = &quot;1&quot;;
var bim = 2;

foo == bar; // false
foo != bar; // true
foo == baz; // true; but note that the types are different

foo === baz;             // false
foo !== baz;             // true
foo === parseInt( baz ); // true

foo &gt; bim;  // false
bim &gt; baz;  // true
foo &lt;= baz; // true
</code></pre>
<h3 id="3真值判断---比较">3.真值判断 - 比较</h3>
<p>真值判断</p>
<pre><code>// Values that evaluate to false:

false
&quot;&quot; // An empty string.
NaN // JavaScript's &quot;not-a-number&quot; variable.
null
undefined // Be careful -- undefined can be redefined!
0 // The number zero.

// Values that evaluate to true:

true
// Everything else evaluates to true, some examples:
&quot;0&quot;
&quot;any string&quot;
[] // An empty array.
{} // An empty object.
1 // Any non-zero number.
</code></pre>
<p>逻辑运算</p>
<pre><code>//短路运算
&amp;&amp;  and (a == b &amp;&amp; c != d)
||  or (a == b || c != d)
!   not !(a == b || c != d)

--------------------

// Logical AND and OR operators

var foo = 1;
var bar = 0;
var baz = 2;

// returns 1, which is true
foo || bar;

// returns 1, which is true
bar || foo;

// returns 0, which is false
foo &amp;&amp; bar;

// returns 2, which is true
foo &amp;&amp; baz;

// returns 1, which is true
baz &amp;&amp; foo;

--------------------

if(x &gt; 10 &amp;&amp; x &lt; 20) {
    ...
}

if(country === 'England' || country === 'Germany') {
        ...
}


if ( (name === &quot;John&quot; || name === &quot;Jennifer&quot;) &amp;&amp; country === &quot;France&quot;)

--------------------

//其他用法
// Do something with foo if foo is truthy.
foo &amp;&amp; doSomething( foo );

// Set bar to baz if baz is truthy;
// otherwise, set it to the return value of createBar()
var bar = baz || createBar();
</code></pre>
<h3 id="4控制流">4.控制流</h3>
<h4 id="分支">分支</h4>
<p>if-eles</p>
<pre><code>if (conditions) {
    statements;
} else if (conditions) {
    statements;
} else {
    statements;
}
</code></pre>
<p>e.g.</p>
<pre><code>if (time&lt;10)
{
    x=&quot;Good morning&quot;;
} else if (time&lt;20)
{
    x=&quot;Good day&quot;;
} else
{
    x=&quot;Good evening&quot;;
}
</code></pre>
<p>switch</p>
<p>在判断情况大于2种的时候，使用 switch/case 更高效，而且更优雅（更易于组织代码）。但在判断的情况超过10种的时候不要使用 switch/case</p>
<pre><code>switch (expression) {
    case value :
        statements;
    case value :
        statements;
    default :
        statements;
}
</code></pre>
<p>e.g.</p>
<pre><code>//记得break
var quantity = 1;
switch (quantity) {
    case 1 :
        alert(&quot;quantity is 1&quot;);
        break;
    case 2 :
        alert(&quot;quantity is 2&quot;);
        break;
    default :
        alert(&quot;quantity is not 1 or 2&quot;);
}
</code></pre>
<h4 id="循环">循环</h4>
<p>for</p>
<pre><code>for (initialization; conditions; change) {
    statements;
}
</code></pre>
<p>e.g.</p>
<pre><code>for(var i = 0; i &lt; 10; i = i + 1){
    // do this code ten-times
}

var i = 0;
for(; i&lt;length; i+=2) {
}

var i=0,len=cars.length;
for (; i&lt;len; )
{
    document.write(cars[i] + &quot;&lt;br&gt;&quot;);
    i++;
}
</code></pre>
<p>for/in</p>
<p>谨慎使用for-in, 遍历一个对象中的成员（属性，方法），如果用来遍历数组的到的结果并不是预期中数组每项的值，方法神马的会被遍历出来</p>
<pre><code>for (var index in array) {
    statements;
    //array[index]
}

for (var key in object) {
    statements;
    //object[key]
}
for (var name in object) {
    if (object.hasOwnProperty(name)) {
        // do something with name
    }
}

e.g.
</code></pre>
<p>while</p>
<pre><code>while (conditions) {
    statements;
}
</code></pre>
<p>e.g.</p>
<pre><code>var i = 0, x = &quot;&quot;;
while (i &lt; 5) {
    x = x + &quot;The number is &quot; + i;
    i++;
}
</code></pre>
<p>do-while</p>
<pre><code>do {
    statements;
} while (conditions);
</code></pre>
<p>e.g.</p>
<pre><code>do {
    // Even though the condition evaluates to false
    // this loop's body will still execute once.
    alert( &quot;Hi there!&quot; );
} while ( false );
</code></pre>
<p>break/continue</p>
<pre><code>break 语句用于跳出循环。
continue 用于跳过循环中的一个迭代。

label:
    语句

break labelname;
continue labelname;
</code></pre>
<p>e.g.</p>
<pre><code>// Stopping a loop
for ( var i = 0; i &lt; 10; i++ ) {
    if ( something ) {
        break;
    }
}

// Skipping to the next iteration of a loop
for ( var i = 0; i &lt; 10; i++ ) {
    if ( something ) {
        continue;
    }
    // The following statement will only be executed
    // if the conditional &quot;something&quot; has not been met
    console.log( &quot;I have been reached&quot; );

}
</code></pre>
<hr>
<h2 id="第二部分函数">第二部分：函数</h2>
<h3 id="1functions">1.Functions</h3>
<p>声明</p>
<pre><code>// Function declaration.
function foo() {
    // Do something.
}

// Named function expression.
var foo = function() {
    // Do something.
};

function myFunction(var1,var2)
{
    //function statements go here
}
</code></pre>
<p>e.g.</p>
<pre><code>function double(x) {
    return 2 * x;
}

or

var double = function(x) {
    return 2 * x;
};
</code></pre>
<p>参数</p>
<pre><code>function changeBg(color){
    document.bgColor = color;
}
</code></pre>
<p>返回值</p>
<pre><code>function myFunction()
{
    var x=5;
    return x;
}
var myVar=myFunction();
</code></pre>
<p>e.g.</p>
<pre><code>// A simple function.
var greet = function( person, greeting ) {
    var text = greeting + &quot;, &quot; + person;
    console.log( text );
};

greet( &quot;Rebecca&quot;, &quot;Hello&quot; ); // &quot;Hello, Rebecca&quot;


// A function that returns a value.
var greet = function( person, greeting ) {
    var text = greeting + &quot;, &quot; + person;
    return text;
};

console.log( greet( &quot;Rebecca&quot;, &quot;Hello&quot; ) ); // &quot;Hello, Rebecca&quot;


// A function that returns another function.
var greet = function( person, greeting ) {
    var text = greeting + &quot;, &quot; + person;
    return function() {
        console.log( text );
    };
};

var greeting = greet( &quot;Rebecca&quot;, &quot;Hello&quot; );

greeting(); // &quot;Hello, Rebecca&quot;
</code></pre>
<p>Immediately-Invoked Function Expression (IIFE)</p>
<p>参阅文章 <a href="http://benalman.com/news/2010/11/immediately-invoked-function-expression/">IIFE</a></p>
<p>创建并立刻执行.This pattern is extremely useful for cases where you want to avoid polluting the global namespace with code – no variables declared inside of the function are visible outside of it.</p>
<pre><code>// An immediately-invoked function expression.

(function() {
    var foo = &quot;Hello world&quot;;
})();

console.log( foo ); // undefined!
</code></pre>
<p>方法作为参数</p>
<p>方法可以赋值给变量，且，可以作为函数参数传递</p>
<pre><code>// Passing an anonymous function as an argument.
var myFn = function( fn ) {
    var result = fn();
    console.log( result );
};

// Logs &quot;hello world&quot;
myFn( function() {
    return &quot;hello world&quot;;
});

// Passing a named function as an argument
var myFn = function( fn ) {
    var result = fn();
    console.log( result );
};

var myOtherFn = function() {
    return &quot;hello world&quot;;
};

myFn( myOtherFn ); // &quot;hello world&quot;
</code></pre>
<h3 id="2global-functions">2.global functions</h3>
<p>These &ldquo;global&rdquo; functions we have discussed above are actually methods of the window object, but as window is assumed if no object is referenced, we don&rsquo;t need to explicitly write window.parseFloat() or window.isNaN(). Also, some of these functions such as Number() and String() are really function constructors for creating new String and Number objects. For now, just remember that you can use these functions to ensure you are working with a String or Number.</p>
<ul>
<li>Number(object)</li>
</ul>
<p>一个数值包装器</p>
<p>Number的属性</p>
<pre><code>MAX_VALUE
MIN_VALUE
NEGATIVE_INFINITY
POSITIVE_INFINITY
</code></pre>
<p>方法</p>
<pre><code>toExponential ,  toExponential 方法以指数形式返回 数字的字符串表示
toFixed, 四舍五入
toPrecision
toString
valueOf, valueOf 方法返回调用它的对象类型的原始
</code></pre>
<p>转为数字，如果失败返回NaN(Not a Number)</p>
<pre><code>var strNum1 = &quot;1&quot;;
var strNum2 = &quot;2&quot;;
var strSum = strNum1 + strNum2; //returns 12
alert(strSum);

var intNum1 = Number(strNum1);
var intNum2 = Number(strNum2);
var intSum = intNum1 + intNum2; //returns 3
alert(intSum);

// 四舍五入一个数字，保留N位小数
var num =2.443242342;
num = num.toFixed(4);  // num will be equal to 2.4432
</code></pre>
<ul>
<li>Boolean(object)</li>
</ul>
<p>Boolean 是一个 代表 true 或 false 值的对象</p>
<p>Boolean 对象有多个值，这些值</p>
<pre><code>相当于 false 值（0、 -0、null 或 “” [一个空字串]），未定义的 (NaN)，当然还有 false。
所有其他布尔 值相当于 true 值
</code></pre>
<p><code>var myBoolean = true; if(myBoolean == true) { // If the condition evaluates to true } else { // If the condition evaluates to false }</code></p>
<ul>
<li>String(object)</li>
</ul>
<p>转为字符串</p>
<pre><code>var intNum1 = 1;
var intNum2 = 2;
var intSum = intNum1 + intNum2; //returns 3
alert(intSum);

var strNum1 = String(intNum1);
var strNum2 = String(intNum2);
var strSum = strNum1 + strNum2; //returns 12
alert(strSum);
</code></pre>
<p>具体见string 方法</p>
<p>isNaN(object)</p>
<p>判断是否为数字，或者是否可以被转为数字。如果是数字，return false，否则，return true;</p>
<pre><code>isNaN(4) //false
isNaN('4') //false

isNaN('hello') //true
isNaN(0/0) //true
</code></pre>
<p>parseFloat() and parseInt()</p>
<p>The parseFloat() 如果字符串以数字开头，将会将字符串开头数字部分转为数字。否则，返回NaN</p>
<p>The parseInt() 如果字符串以数字开头，将会将字符串开头数字部分转为数字。否则，返回NaN</p>
<p>parseInt(string, radix), radix=[2,36], radix不设or0, 根据string, “0x”16进制, &ldquo;0&quot;8进制</p>
<pre><code>var race = &quot;26.2 miles&quot;;
parseFloat(race); //26.2
parseInt(race);  //26

race = &quot;Marathon&quot;;
parseFloat(race); //NaN
parseInt(race); //NaN
</code></pre>
<hr>
<h2 id="第三部分对象">第三部分：对象</h2>
<h3 id="1array">1.Array</h3>
<p>声明</p>
<pre><code>//语法
new Array();
new Array(size);
new Array(element0, element1, ..., elementn);

// Creating an array with the constructor:
var myarray = new Array();

// Creating an array with the array literal syntax:
var bar = [];

========================

// A simple array with constructor.
var myArray1 = new Array( &quot;hello&quot;, &quot;world&quot; );

// Literal declaration, the preferred way.
var myArray2 = [ &quot;hello&quot;, &quot;world&quot; ];

========================

var bar = new Array( 100 );
alert( bar[ 0 ] ); // undefined
alert( bar.length ); // 100

var foo = [ 100 ];
alert( foo[ 0 ] ); // 100
alert( foo.length ); // 1

a = new Array(3) // [undefined × 3]
a[0] // undefined
</code></pre>
<p>赋值</p>
<pre><code>var myArray = [];

myArray[ 0 ] = &quot;hello&quot;;
myArray[ 1 ] = &quot;world&quot;;
myArray[ 3 ] = &quot;!&quot;;

//Missing indices will be filled with undefined.
console.log( myArray ); // [ &quot;hello&quot;, &quot;world&quot;, undefined, &quot;!&quot; ];
</code></pre>
<p>读取</p>
<pre><code>// Accessing array items by index
var myArray = [ &quot;hello&quot;, &quot;world&quot;, &quot;!&quot; ];
console.log( myArray[ 2 ] ); // &quot;!&quot;
</code></pre>
<p>属性-length</p>
<pre><code>// Length of an array
var myArray = [ &quot;hello&quot;, &quot;world&quot;, &quot;!&quot; ];
console.log( myArray.length ); // 3
</code></pre>
<p><a href="http://blog.jobbole.com/56712/">原生数组函数</a></p>
<p>遍历</p>
<pre><code>// For loops and arrays - a classic
var myArray = [ &quot;hello&quot;, &quot;world&quot;, &quot;!&quot; ];
for ( var i = 0; i &lt; myArray.length; i = i + 1 ) {
    console.log( myArray[ i ] );
}

// for/in
var x
var mycars = new Array()
mycars[0] = &quot;Saab&quot;
mycars[1] = &quot;Volvo&quot;

for (x in mycars)
{
    console.log(mycars[x]);
}
</code></pre>
<p>常用方法</p>
<p>方法-push/pop</p>
<pre><code>// Pushing and popping

var myArray = [];

myArray.push( 0 ); // [ 0 ]
myArray.push( 2 ); // [ 0 , 2 ]
myArray.push( 7 ); // [ 0 , 2 , 7 ]
b = myArray.pop();     // [ 0 , 2 ] , b=7
</code></pre>
<p>方法-concat</p>
<pre><code>var myArray = [ 2, 3, 4 ];
var myOtherArray = [ 5, 6, 7 ];
// 返回新数组
var wholeArray = myArray.concat( myOtherArray ); // [ 2, 3, 4, 5, 6, 7 ]

//附加（append）一个数组到另一个数组上
var array1 = [12 , &quot;foo&quot; , {name: &quot;Joe&quot;} , -2458];
var array2 = [&quot;Doe&quot; , 555 , 100];

Array.prototype.push.apply(array1, array2);
/* array1 will be equal to  [12 , &quot;foo&quot; , {name &quot;Joe&quot;} , -2458 , &quot;Doe&quot; , 555 , 100] */
</code></pre>
<p>方法-join</p>
<pre><code>// Joining elements
var myArray = [ &quot;hello&quot;, &quot;world&quot;, &quot;!&quot; ];

// The default separator is a comma. 默认逗号
console.log( myArray.join() );     // &quot;hello,world,!&quot;

// Any string can be used as separator...
console.log( myArray.join( &quot; &quot; ) );  // &quot;hello world !&quot;;
console.log( myArray.join( &quot;!!&quot; ) ); // &quot;hello!!world!!!&quot;;

// ...including an empty one.
console.log( myArray.join( &quot;&quot; ) );   // &quot;helloworld!&quot;

// 拼标签
&quot;&lt;tr&gt;&lt;td&gt;&quot; + tdcell.join('&lt;/td&gt;&lt;td&gt;') + &quot;&lt;/td&gt;&lt;/tr&gt;&quot;
</code></pre>
<p>方法-indexOf</p>
<pre><code>var fruits = [&quot;Banana&quot;, &quot;Orange&quot;, &quot;Apple&quot;, &quot;Mango&quot;];
var a = fruits.indexOf(&quot;Apple&quot;);
//2
</code></pre>
<p>方法-slice</p>
<pre><code>// Slicing
var myArray = [ 1, 2, 3, 4, 5, 6, 7, 8 ];
var newArray = myArray.slice( 3 );

console.log( myArray );  // [ 1, 2, 3, 4, 5, 6, 7, 8 ]
console.log( newArray ); // [ 4, 5, 6, 7, 8 ]
</code></pre>
<p>方法-splice</p>
<pre><code>myArray.splice( index, length, values, ... );
Index – The starting index.
Length – The number of elements to remove.
Values – The values to be inserted at the index position.

var myArray = [ 0, 7, 8, 5 ];
myArray.splice( 1, 2, 1, 2, 3, 4 ); //from index 1, cut 2 elements, and insert 1,2,3,4
console.log( myArray ); // [ 0, 1, 2, 3, 4, 5 ]
</code></pre>
<p>方法-sort</p>
<pre><code>// Sorting without comparing function.
var myArray = [ 3, 4, 6, 1 ];
myArray.sort(); // 1, 3, 4, 6

// Sorting with comparing function.
function descending( a, b ) {
    return b - a;
}
var myArray = [ 3, 4, 6, 1 ];
myArray.sort( descending ); // [ 6, 4, 3, 1 ]
</code></pre>
<p>方法-reverse</p>
<pre><code>var myArray = [ &quot;world&quot; , &quot;hello&quot; ];
myArray.reverse(); // [ &quot;hello&quot;, &quot;world&quot; ]
</code></pre>
<p>方法-shift</p>
<pre><code>// Queue with shift() and push()
var myArray = [];

myArray.push( 0 ); // [ 0 ]
myArray.push( 2 ); // [ 0 , 2 ]
myArray.push( 7 ); // [ 0 , 2 , 7 ]
b = myArray.shift();   // [ 2 , 7 ],  b=0
</code></pre>
<p>方法-unshift</p>
<pre><code>//Inserts an element at the first position of the array:
var myArray = [];

myArray.unshift( 0 ); // [ 0 ]
myArray.unshift( 2 ); // [ 2 , 0 ]
myArray.unshift( 7 ); // [ 7 , 2 , 0 ]
</code></pre>
<p>方法-forEach</p>
<pre><code>Element – The element itself.
Index – The index of this element in the array.
Array – The array itself.

// Native .forEach()
function printElement( elem ) {
    console.log( elem );
}

function printElementAndIndex( elem, index ) {
    console.log( &quot;Index &quot; + index + &quot;: &quot; + elem );
}

function negateElement( elem, index, array ) {
    array[ index ] = -elem;
}

myArray = [ 1, 2, 3, 4, 5 ];

// Prints all elements to the console
myArray.forEach( printElement );

// Prints &quot;Index 0: 1&quot;, &quot;Index 1: 2&quot;, &quot;Index 2: 3&quot;, ...
myArray.forEach( printElementAndIndex );

// myArray is now [ -1, -2, -3, -4, -5 ]
myArray.forEach( negateElement );
</code></pre>
<p>获取数字数组中最大最小值</p>
<pre><code>var  numbers = [5, 458 , 120 , -215 , 228 , 400 , 122205, -85411];
var maxInNumbers = Math.max.apply(Math, numbers);
var minInNumbers = Math.min.apply(Math, numbers);
</code></pre>
<p>清空一个数组</p>
<pre><code>var myArray = [12 , 222 , 1000 ];
myArray.length = 0; // myArray will be equal to [].

改变length可以进行数组截断或者扩增(扩增的项是undefined)
</code></pre>
<p>不要使用 delete 来删除一个数组中的项</p>
<pre><code>var items = [12, 548 ,'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' ,2154 , 119 ];
items.length; // return 11
delete items[3]; // return true
items.length; // return 11
/* items will be equal to [12, 548, &quot;a&quot;, undefined × 1, 5478, &quot;foo&quot;, 8852, undefined × 1, &quot;Doe&quot;, 2154,       119]   */

使用

var items = [12, 548 ,'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' ,2154 , 119 ];
items.length; // return 11
items.splice(3,1) ;
items.length; // return 10
/* items will be equal to [12, 548, &quot;a&quot;, 5478, &quot;foo&quot;, 8852, undefined × 1, &quot;Doe&quot;, 2154,       119]   */
</code></pre>
<h3 id="2object">2.Object</h3>
<p>Objects包含一个或多个键值对，键：任意字符串，值：数字，字符串，array，其他objects，函数等</p>
<p>声明</p>
<pre><code>// Creating an object with the constructor:
var person1 = new Object;
person1.firstName = &quot;John&quot;;
person1.lastName = &quot;Doe&quot;;

alert( person1.firstName + &quot; &quot; + person1.lastName );

// Creating an object with the object literal syntax:
var person2 = {
    firstName: &quot;Jane&quot;,
    lastName: &quot;Doe&quot;
};

alert( person2.firstName + &quot; &quot; + person2.lastName );

//包含方法
var myObject = {
    sayHello: function() {
        console.log( &quot;hello&quot; );
    },
    myName: &quot;Rebecca&quot;
};

myObject.sayHello(); // &quot;hello&quot;

console.log( myObject.myName ); // &quot;Rebecca&quot;
</code></pre>
<p>嵌套</p>
<pre><code>// As mentioned, objects can also have objects as a property.
var people = {};

people[ &quot;person1&quot; ] = person1;
people[ &quot;person2&quot; ] = person2;

alert( people[ &quot;person1&quot; ].firstName );
alert( people[ &quot;person2&quot; ].firstName );
</code></pre>
<p>获取属性：寻址方式</p>
<pre><code>name=person.lastname;
name=person[&quot;lastname&quot;];
</code></pre>
<p>删除属性</p>
<pre><code>b = { 'b1': 1, 'b2':2 }
delete b['b2']
//Object {b1: 1}
</code></pre>
<p>读取未定义的属性</p>
<pre><code>// Properties that have not been created are undefined.
var person = { name: &quot;John Doe&quot; };
alert( person.email ); // undefined
</code></pre>
<p>访问对象的方法</p>
<pre><code>objectName.methodName()
</code></pre>
<hr>
<h2 id="第四部分内置对象">第四部分：内置对象</h2>
<h3 id="1字符串string">1.字符串(string)</h3>
<p>字符串可以是引号中的任意文本</p>
<p>声明</p>
<pre><code>// Single quotes can be used
var str = 'Our lovely string';

// Double quotes as well
var otherStr = &quot;Another nice string&quot;;

//In Javascript, Strings can contain UTF-8 characters:
&quot;中文 español English हिन्दी العربية português বাংলা русский 日本語 ਪੰਜਾਬੀ 한국어&quot;;
</code></pre>
<p>连接字符串</p>
<pre><code>//规则是：如果把数字与字符串相加，结果将成为字符串
var bigStr = 'Hi ' + 'JS strings are nice ' + 'and ' + 'easy to add';
</code></pre>
<p>长度</p>
<pre><code>// Just use the property .length
var size = 'Our lovely string'.length;
</code></pre>
<p>常用方法</p>
<p>方法-trim</p>
<pre><code>var str = &quot;       Hello World!        &quot;;
alert(str.trim());
</code></pre>
<p>方法-match</p>
<pre><code>str.match(&quot;world&quot;)
</code></pre>
<p>方法-replace</p>
<p>语法</p>
<pre><code>StringObject.replace(searchValue,replaceValue)

StringObject:字符串
searchValue：字符串或正则表达式
replaceValue:字符串或者函数
</code></pre>
<p>replaceValue可以是字符串。如果字符串中有几个特定字符的话，会被转换为特定字符串</p>
<pre><code>$&amp;  与正则相匹配的字符串
$`  匹配字符串左边的字符
$’ 匹配字符串右边的字符
$1,$2,$,3,…,$n 匹配结果中对应的分组匹配结果
</code></pre>
<p>实例</p>
<pre><code>str.replace(/Microsoft/,&quot;W3School&quot;)

a = 'hello world'
&quot;hello world&quot;
a.replace(/l/, 'a')
&quot;healo world&quot;
a.replace(/l/g, 'a')
&quot;heaao worad&quot;

var sStr='讨论一下正则表达式中的replace的用法';
sStr.replace(/正则表达式/,'{$&amp;}');//讨论一下{正则表达式}中的replace的用法

'abc'.replace(/b/,&quot;$`&quot;);//aac
'abc'.replace(/b/,&quot;$'&quot;);//acc&quot;'`&quot;)

'nimojs@126.com'.replace(/(.+)(@)(.*)/,&quot;$2$1&quot;)//@nimojs
</code></pre>
<p>replaceValue 是函数</p>
<pre><code>'JAVASCRIPT'.replace(/[A-G]/g,function(){
    return arguments[0].toLowerCase();
})//JaVaScRIPT

function logArguments(){    
    console.log(arguments);//[&quot;nimojs@126.com&quot;, &quot;nimojs&quot;, &quot;@&quot;, &quot;126.com&quot;, 0, &quot;nimojs@126.com&quot;] 
    return '返回值会替换掉匹配到的目标'
}
console.log(
    'nimojs@126.com'.replace(/(.+)(@)(.*)/,logArguments)
)
</code></pre>
<p>方法-split(delimiter)</p>
<pre><code>var s = &quot;A,B,C,D&quot;;
var a = s.split(&quot;,&quot;);
document.write(a[2]);
//Returns C
</code></pre>
<p>方法-indexOf(substring,startPosition)</p>
<pre><code>str.indexOf(&quot;Hello&quot;)
// if not exists, return -1
code.indexOf('a') === -1

webucator.indexOf(&quot;cat&quot;);
//Returns 4

webucator.indexOf(&quot;cat&quot;, 5);
//Returns -1
</code></pre>
<p>方法-lastIndexOf(substring,endPosition)</p>
<pre><code>webucator.lastIndexOf(&quot;cat&quot;);
//Returns 4

 webucator.lastIndexOf(&quot;cat&quot;, 5);
 //Returns 4
</code></pre>
<p>方法-substr(startPosition,length)</p>
<pre><code>webucator.substr(4, 3);
//Returns cat

webucator.substr(4);
//Returns cator
</code></pre>
<p>方法-substring(startPosition,endPosition)</p>
<pre><code>webucator.substring(4, 7);
//Returns cat

webucator.substring(4);
//Returns cator
</code></pre>
<p>方法-slice(startPosition,endPosition)</p>
<pre><code>webucator.slice(4, 7);
//Returns cat

var str=&quot;Hello happy world!&quot;
str.slice(6)// happy world!
str.slice(6,11) //happy
</code></pre>
<p>方法-slice(startPosition,positionFromEnd)</p>
<pre><code>webucator.slice(4, -2);
//Returns cat
</code></pre>
<p>方法-toLowerCase()/toUpperCase()</p>
<pre><code>webucator.toLowerCase()
//Returns webucator

webucator.toUpperCase();
//Returns WEBUCATOR
</code></pre>
<p>方法-charAt(position)</p>
<pre><code>webucator.charAt(4)
//Returns c (the fifth character)
</code></pre>
<p>方法-charCodeAt(position)</p>
<pre><code>webucator.charCodeAt(4)
//Returns 99
</code></pre>
<p>方法-fromCharCode(characterCodes)</p>
<pre><code>String.fromCharCode(169)
//Returns ©
</code></pre>
<h3 id="2math">2.math</h3>
<p>Math（算数）对象的作用是：执行常见的算数任务</p>
<p>属性</p>
<pre><code>//Math.PI Pi (Π)
Math.PI;
//3.141592653589793

//Math.SQRT2  Square root of 2.
Math.SQRT2;
//1.4142135623730951
</code></pre>
<p>Math.abs(number)</p>
<pre><code>Math.abs(-12);
//Returns 12
</code></pre>
<p>Math.ceil(number)</p>
<pre><code>Math.ceil(5.4);
//Returns 6
</code></pre>
<p>Math.floor(number)</p>
<pre><code>Math.floor(5.6);
//Returns 5
</code></pre>
<p>Math.max(numbers)</p>
<pre><code>Math.max(2, 5, 9, 3);
//Returns 9
</code></pre>
<p>Math.min(numbers)</p>
<pre><code>Math.min(2, 5, 9, 3);
//Returns 2
</code></pre>
<p>Math.pow(number, power)</p>
<pre><code>Math.pow(2, 5);
//Returns 32
</code></pre>
<p>Math.round(number)</p>
<pre><code>Math.round(2.5);
//Returns 3
</code></pre>
<p>Math.random();</p>
<pre><code>//Returns random
//number from 0 to 1
Math.random()
var rndDec = Math.random();
</code></pre>
<p>Math.power()</p>
<h3 id="3date">3.date</h3>
<p>处理日期和时间</p>
<p>定义</p>
<pre><code>//Syntax: new Date();
var myDate=new Date()
&quot;从 1970/01/01 至今已过去 &quot; + myDate.getTime() + &quot; 毫秒&quot;

var now = new Date();
document.write(now);

//Syntax: new Date(&quot;month dd, yyyy hh:mm:ss);&quot;)
var redSoxWin = new Date(&quot;October 21, 2004 12:01:00&quot;);
document.write(redSoxWin);

//Syntax: new Date(yyyy, mm, dd, hh, mm, ss, ms);
redSoxWin = new Date(2004, 9, 21, 12, 01, 00, 00);
document.write(redSoxWin);

// 计数从0开始
var d = new Date(2012, 4, 15);        // 2012年5月15日
alert(d.getMonth());            // 结果为4
</code></pre>
<p>Date.parse 的返回结果不是一个Date对象，而是从1970-01-01午夜（GMT）到给定日期之间的毫秒数。可以用Date的构造函数将其转换为Date对象</p>
<pre><code>new Date(Date.parse(&quot;8/2/2012&quot;));    // 正确识别为2012年8月2日
new Date(Date.parse(&quot;2012-08-02&quot;));    // 正确识别为2012年8月2日
new Date(Date.parse(&quot;2012-8-2&quot;));    // 不能识别
</code></pre>
<p>方法</p>
<p>getDate()   Returns the day of the month (1-31).</p>
<pre><code>rightNow.getDate();
//Returns 14
</code></pre>
<p>getDay()    Returns the day of the week as a number (0-6, 0=Sunday, 6=Saturday).</p>
<pre><code>rightNow.getDay();
//Returns 4
</code></pre>
<p>getMonth()  Returns the month as a number (0-11, 0=January, 11=December).</p>
<pre><code>rightNow.getMonth();
//Returns 3
</code></pre>
<p>getFullYear()   Returns the four-digit year.</p>
<pre><code>rightNow.getFullYear();
//Returns 2011
</code></pre>
<p>getHours()  Returns the hour (0-23).</p>
<pre><code>rightNow.getHours();
//Returns 0
</code></pre>
<p>getMinutes()    Returns the minute (0-59).</p>
<pre><code>rightNow.getMinutes();
//Returns 23
</code></pre>
<p>getSeconds()    Returns the second (0-59).</p>
<pre><code>rightNow.getSeconds();
//Returns 54
</code></pre>
<p>getMilliseconds()   Returns the millisecond (0-999).</p>
<pre><code>rightNow.getMilliseconds();
//Returns 650
</code></pre>
<p>getTime()   Returns the number of milliseconds since midnight January 1, 1970.</p>
<pre><code>rightNow.getTime();
//Returns 1113452634650
</code></pre>
<p>getTimezoneOffset() Returns the time difference in minutes between the computer of user and GMT.</p>
<pre><code>rightNow.getTimezoneOffset();
//Returns 240
</code></pre>
<p>toLocaleString()    Returns the Date object as a string.</p>
<pre><code>rightNow.toLocaleString();
//Returns Thursday, April 14,
//2011 12:23:54 AM
</code></pre>
<p>toGMTString()   Returns the Date object as a string in GMT timezone.</p>
<pre><code>rightNow.toGMTString();
//Returns Thu, 14 Apr 2011
//04:23:54 UTC
</code></pre>
<h3 id="4boolean">4.boolean</h3>
<p>布尔（逻辑）只能有两个值：true 或 false</p>
<pre><code>var x=true
var y=false
</code></pre>
<p>对象</p>
<pre><code>var myBoolean=new Boolean()
</code></pre>
<p>所有的代码行均会创建初始值为 false 的 Boolean 对象。</p>
<pre><code>var myBoolean=new Boolean();
var myBoolean=new Boolean(0);
var myBoolean=new Boolean(null);
var myBoolean=new Boolean(&quot;&quot;);
var myBoolean=new Boolean(false);
var myBoolean=new Boolean(NaN);
</code></pre>
<p>所有的代码行均会创初始值为 true 的 Boolean 对象：</p>
<pre><code>var myBoolean=new Boolean(1);
var myBoolean=new Boolean(true);
var myBoolean=new Boolean(&quot;true&quot;);
var myBoolean=new Boolean(&quot;false&quot;);
var myBoolean=new Boolean(&quot;Bill Gates&quot;);
</code></pre>
<hr>
<h2 id="第五部分-dom操作">第五部分: DOM操作</h2>
<h3 id="dom">DOM</h3>
<p>当网页被加载时，浏览器会创建页面的文档对象模型（Document Object Model）。
HTML DOM 模型被构造为对象的树</p>
<p>HTML DOM 树</p>
<pre><code>JavaScript 能够改变页面中的所有 HTML 元素
JavaScript 能够改变页面中的所有 HTML 属性
JavaScript 能够改变页面中的所有 CSS 样式
JavaScript 能够对页面中的所有事件做出反应
</code></pre>
<p>通过id查找</p>
<pre><code>var x=document.getElementById(&quot;intro&quot;);
</code></pre>
<p>本例查找 id=&ldquo;main&rdquo; 的元素，然后查找 &ldquo;main&rdquo; 中的所有 <p> 元素：</p>
<pre><code>var x=document.getElementById(&quot;main&quot;);
var y=x.getElementsByTagName(&quot;p&quot;);
</code></pre>
<p>改变输出</p>
<pre><code>document.write(Date());
</code></pre>
<p>改变html</p>
<pre><code>document.getElementById(id).innerHTML=new HTML
</code></pre>
<p>改变html属性</p>
<pre><code>document.getElementById(id).attribute=new value
</code></pre>
<p>改变css</p>
<pre><code>document.getElementById(id).style.property=new style
</code></pre>
<p>HTML Event Handlers</p>
<pre><code>onblur ,onchange ,onclick ,ondblclick ,onfocus ,onkeydown ,onkeypress ,onkeyup ,onload ,onload ,onmousedown ,onmousemove ,onmouseout ,onmouseover ,onmouseup ,onreset ,onselect ,onsubmit ,onunload ,onunload
</code></pre>
<p>创建节点</p>
<pre><code>&lt;div id=&quot;div1&quot;&gt;
&lt;p id=&quot;p1&quot;&gt;这是一个段落&lt;/p&gt;
&lt;p id=&quot;p2&quot;&gt;这是另一个段落&lt;/p&gt;
&lt;/div&gt;

&lt;script&gt;
var para=document.createElement(&quot;p&quot;);
var node=document.createTextNode(&quot;这是新段落。&quot;);
para.appendChild(node);

var element=document.getElementById(&quot;div1&quot;);
element.appendChild(para);
&lt;/script&gt;
</code></pre>
<p>删除节点</p>
<pre><code>&lt;div id=&quot;div1&quot;&gt;
&lt;p id=&quot;p1&quot;&gt;这是一个段落。&lt;/p&gt;
&lt;p id=&quot;p2&quot;&gt;这是另一个段落。&lt;/p&gt;
&lt;/div&gt;

&lt;script&gt;
var parent=document.getElementById(&quot;div1&quot;);
var child=document.getElementById(&quot;p1&quot;);
parent.removeChild(child);
&lt;/script&gt;
</code></pre>
<hr>
<h2 id="第六部分其他">第六部分：其他</h2>
<h3 id="1正则">1.正则</h3>
<p>RegExp 对象用于存储检索模式。
通过 new 关键词来定义 RegExp 对象</p>
<pre><code>var patt1=new RegExp(&quot;e&quot;);
</code></pre>
<p>RegExp 对象的方法
RegExp 对象有 3 个方法：test()、exec() 以及 compile()。</p>
<pre><code>var patt1=new RegExp(&quot;e&quot;);
patt1.test(&quot;The best things in life are free&quot;); //true

patt1.exec(&quot;The best things in life are free&quot;); //[&quot;e&quot;]

var patt1=new RegExp(&quot;e&quot;,&quot;g&quot;);
do
{
	result=patt1.exec(&quot;The best things in life are free&quot;);
	document.write(result);
}while (result!=null)

compile() 方法用于改变 RegExp。
compile() 既可以改变检索模式，也可以添加或删除第二个参数

var patt1=new RegExp(&quot;e&quot;);
document.write(patt1.test(&quot;The best things in life are free&quot;));
patt1.compile(&quot;d&quot;);
document.write(patt1.test(&quot;The best things in life are free&quot;));
</code></pre>
<p><a href="http://blog.jobbole.com/33054/">JavaScript中的正则有几个不同于其他语言的地方</a></p>
<h3 id="2this关键字">2.this关键字</h3>
<p>&ldquo;this&quot;是一个特殊的关键字，用在方法中，指向调用该方法的对象。this的值
In JavaScript, as in most object-oriented programming languages, this is a special keyword that is used in methods to refer to the object on which a method is being invoked. The value of this is determined using a simple series of steps:</p>
<p>1.调用方式是Function.call() 或者 Function.apply(), this将会被赋值为传递给.call()/.apply()的第一个参数.如果第一个参数是null或者undefined,this会指向global object.</p>
<pre><code>// A function invoked using Function.call()
var myObject = {
    sayHello: function() {
        console.log( &quot;Hi! My name is &quot; + this.myName );
    },
    myName: &quot;Rebecca&quot;
};

var secondObject = {
    myName: &quot;Colin&quot;
};

myObject.sayHello();                    // &quot;Hi! My name is Rebecca&quot;
myObject.sayHello.call( secondObject ); // &quot;Hi! My name is Colin&quot;
</code></pre>
<p>2.如果函数调用使用的是Function.bind(), this 会被赋值为传递给.bind()的第一个参数</p>
<pre><code>// A function created using Function.bind()

var myName = &quot;the global object&quot;;
var sayHello = function() {
    console.log( &quot;Hi! My name is &quot; + this.myName );
};
var myObject = {
    myName: &quot;Rebecca&quot;
};
var myObjectHello = sayHello.bind( myObject );

sayHello();      // &quot;Hi! My name is the global object&quot;
myObjectHello(); // &quot;Hi! My name is Rebecca&quot;
</code></pre>
<p>3.如果函数是作为对象的一个方法被调用，this指向该对象</p>
<pre><code>// A function being attached to an object at runtime.

var myName = &quot;the global object&quot;;
var sayHello = function() {
    console.log( &quot;Hi! My name is &quot; + this.myName );
};
var myObject = {
    myName: &quot;Rebecca&quot;
};
var secondObject = {
    myName: &quot;Colin&quot;
};

myObject.sayHello = sayHello;
secondObject.sayHello = sayHello;

sayHello();              // &quot;Hi! My name is the global object&quot;
myObject.sayHello();     // &quot;Hi! My name is Rebecca&quot;
secondObject.sayHello(); // &quot;Hi! My name is Colin&quot;
</code></pre>
<p>当在一个大的命名空间内调用函数，可以通过给一个变量赋值减少代码量，但是，注意方法赋值和对象赋值，在调用时this的区别,可能会会导致bug.</p>
<pre><code>var myNamespace = {
    myObject: {
        sayHello: function() {
            console.log( &quot;Hi! My name is &quot; + this.myName );
        },
        myName: &quot;Rebecca&quot;
    }
};

var hello = myNamespace.myObject.sayHello;

hello(); // &quot;Hi! My name is undefined&quot;

or

var myNamespace = {
    myObject: {
        sayHello: function() {
            console.log( &quot;Hi! My name is &quot; + this.myName );
        },
        myName: &quot;Rebecca&quot;
    }
};

var obj = myNamespace.myObject;

obj.sayHello(); // &quot;Hi! My name is Rebecca&quot;
</code></pre>
<h3 id="3异常处理">3.异常处理</h3>
<p>语法</p>
<pre><code>try
{
    //在这里运行代码
}catch(err)
{
    //在这里处理错误
}
</code></pre>
<p>e.g.</p>
<pre><code>function message() {
    try {
        adddlert(&quot;Welcome guest!&quot;);
    }catch(err) {
        txt=&quot;There was an error on this page.\n\n&quot;;
        txt+=&quot;Error description: &quot; + err.message + &quot;\n\n&quot;;
        txt+=&quot;Click OK to continue.\n\n&quot;;
        alert(txt);
    }
}
</code></pre>
<p>throw 语句允许我们创建自定义错误。</p>
<pre><code>正确的技术术语是：创建或抛出异常（exception）。
如果把 throw 与 try 和 catch 一起使用，那么您能够控制程序流，并生成自定义的错误消息。
语法
throw exception

function myFunction() {
    try {
        var x=document.getElementById(&quot;demo&quot;).value;
        if(x==&quot;&quot;)    throw &quot;empty&quot;;
        if(isNaN(x)) throw &quot;not a number&quot;;
        if(x&gt;10)     throw &quot;too high&quot;;
        if(x&lt;5)      throw &quot;too low&quot;;
    } catch(err)
    {
        var y=document.getElementById(&quot;mess&quot;);
        y.innerHTML=&quot;Error: &quot; + err + &quot;.&quot;;
    }
}
</code></pre>
<h3 id="4作用域">4.作用域</h3>
<h4 id="global-scope">Global Scope</h4>
<p>如果一个变量或函数是全局的，可以在任意地方获取。对浏览器，全局作用域是window对象。</p>
<p>如果一个变量在函数外面定义，则这个变量是全局的</p>
<pre><code>var x = 9;

//anywhere
window.x or x
</code></pre>
<h4 id="local-scope">Local Scope</h4>
<p>在函数体中使用var定义的变量，是局部变量</p>
<pre><code>function myFunc() {
    var x = 5;
}

console.log( x ); // ReferenceError: x is not defined
</code></pre>
<p>注意如果不适用var进行定义，则该变量默认全局</p>
<pre><code>function myFunc() {
    x = 5;
}

console.log( x ); // 5
</code></pre>
<p>Immediately-Invoked Function Expressions(IIFE) 可以避免全局变量(立刻定义和调用),在jQuery库中可以看到</p>
<pre><code>(function() {
    var jQuery = { /* All my methods go here. */ };
    window.jQuery = jQuery;
})();
</code></pre>
<p>局部变量在整个函数内可见，所以对于函数嵌套,内层可以访问外层变量:</p>
<pre><code>function outer() {
    var x = 5;
    function inner() {
        console.log( x );
    }
    inner(); // 5
}
</code></pre>
<p>外层不能访问内层变量</p>
<pre><code>function outer() {
    var x = 5;
    function inner() {
        console.log( x );
        var y = 10;
    }
    inner(); // 5
    console.log( y ); // ReferenceError: y is not defined
}
</code></pre>
<p>在函数中声明变量，没有使用var，javascript将作为全局变量处理，会到全局域去寻找该变量是否定义，如果未预定义，将会在全局域中定义该变量，造成一些不可预期的结果。</p>
<pre><code>// Functions have access to variables defined in the same scope.

var foo = &quot;hello&quot;;

var sayHello = function() {
    console.log( foo );
};

sayHello(); // &quot;hello&quot;

console.log( foo ); // &quot;hello&quot;
</code></pre>
<p>全局变量和局部变量可以重名</p>
<pre><code>var foo = &quot;world&quot;;
var sayHello = function() {
    var foo = &quot;hello&quot;;
    console.log( foo );
};

sayHello(); // &quot;hello&quot;

console.log( foo ); // &quot;world&quot;
</code></pre>
<p>When, within a function, you reference a variable defined in an outer scope, that function can see changes to the variable&rsquo;s value after the function is defined.</p>
<pre><code>var myFunction = function() {
    var foo = &quot;hello&quot;;
    var myFn = function() {
        console.log( foo ); //指向该变量
    };
    foo = &quot;world&quot;; // change 变量对应的值变更了
    return myFn;
};

var f = myFunction();

f(); // &quot;world&quot;
</code></pre>
<p>一个复杂的例子演示：</p>
<pre><code>(function() {
    var baz = 1;
    var bim = function() {
        console.log( baz );
    };
    bar = function() {
        console.log( baz );
    };
})();

console.log( baz ); // baz is not defined outside of the function, ReferenceError

bar(); //  1, 外部可见，全局的

bim(); // ReferenceError: bim is not defined
</code></pre>
<p>局部变量会在函数运行以后被删除。</p>
<p>全局变量会在页面关闭后被删除。</p>
<h3 id="5闭包">5.闭包</h3>
<p>可以参考 <a href="https://learn.jquery.com/javascript-101/closures/">入口</a></p>
<p>简单例子</p>
<pre><code>var buildMultiplier = function(x) {
    return function(y) {
        return x * y;
    }
}

var double = buildMultiplier(2);
var triple = buildMultiplier(3);

double(3); // =&gt; 6
triple(3); // =&gt; 9
</code></pre>
<p>Closures are an extension of the concept of scope.</p>
<p>通过闭包，函数可以获取在函数定义位置作用域范围内的变量</p>
<p>闭包需要通过实例去理解。</p>
<p>下面这个例子，最终每个函数的i将会是函数循环结果的最后一个值</p>
<pre><code>// Each function executed within the loop will reference
// the last value stored in i (5).
// This won't behave as we want it to - every 100 milliseconds, 5 will alert
for ( var i = 0; i &lt; 5; i++ ) {
    setTimeout(function() {
        alert( i );
    }, i * 100 );
}
</code></pre>
<p>闭包可以避免这种情况，为每个循环建立独立的可见范围&ndash;将变量的每个独立值存储在其可见域内。</p>
<pre><code>// Using a closure to create a new private scope
// fix: “close” the value of i inside createFunction, so it won't change
var createFunction = function( i ) { //可见范围，i被保存，独立
    return function() {
        alert( i );
    };
};

for ( var i = 0; i &lt; 5; i++ ) {
    setTimeout( createFunction( i ), i * 100 );
}
</code></pre>
<p>闭包还通常和this关键字配合</p>
<pre><code>// Using a closure to access inner and outer object instances simultaneously.
var outerObj = {
    myName: &quot;outer&quot;,
    outerFunction: function() {

        // Provide a reference to outerObj through innerFunction's closure
        var self = this;
        var innerObj = {
            myName: &quot;inner&quot;,
            innerFunction: function() {
                console.log( self.myName, this.myName ); // &quot;outer inner&quot;
            }
        };

        innerObj.innerFunction();

        console.log( this.myName ); // &quot;outer&quot;
    }
};
</code></pre>
<h3 id="6typeoftesting-type">6.typeof(Testing Type)</h3>
<p>typeof关键之用于判断一个变量的类型</p>
<p>type checking</p>
<pre><code>// Testing the type of various variables.
var myFunction = function() {
    console.log( &quot;hello&quot; );
};
var myObject = {
    foo: &quot;bar&quot;
};
var myArray = [ &quot;a&quot;, &quot;b&quot;, &quot;c&quot; ];
var myString = &quot;hello&quot;;
var myNumber = 3;
var myRegExp = /(\w+)\s(\w+)/;

typeof myFunction; // &quot;function&quot;
typeof myObject;   // &quot;object&quot;
typeof myArray;    // &quot;object&quot; -- Careful!
typeof myString;   // &quot;string&quot;
typeof myNumber;   // &quot;number&quot;
typeof null;       // &quot;object&quot; -- Careful!
typeof undefined;  // &quot;undefined&quot;
typeof meh;        // &quot;undefined&quot; -- undefined variable.
typeof myRegExp;   // &quot;function&quot; or &quot;object&quot; depending on environment.


if ( myArray.push &amp;&amp; myArray.slice &amp;&amp; myArray.join ) {
    // probably an array (this is called &quot;duck typing&quot;)
}

if ( Object.prototype.toString.call( myArray ) === &quot;[object Array]&quot; ) {
    // Definitely an array!
    // This is widely considered as the most robust way
    // to determine if a specific value is an Array.
}
</code></pre>
<h3 id="7timers">7.Timers</h3>
<p>Timers are started and stopped with the following four methods of the window object:</p>
<pre><code>setTimeout(code_to_execute, wait_time_in_milliseconds)
clearTimeout(timer)

setInterval(code_to_execute, interval_in_milliseconds)
clearInterval(interval)
</code></pre>
<p>test</p>
<pre><code>&lt;script type=&quot;text/javascript&quot;&gt;
    var timer;
    function changeBg(color) {
        timer = setTimeout(function() { document.bgColor=color; }, 1000);
    }

    function stopTimer() {
        clearTimeout(timer);
    }
&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;button onclick=&quot;changeBg('red')&quot;&gt;Change Background to Red&lt;/button&gt;
&lt;button onclick=&quot;changeBg('white')&quot;&gt;Change Background to White&lt;/button&gt;
&lt;button onclick=&quot;stopTimer()&quot;&gt;Wait! Don't do it!&lt;/button&gt;
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Vim技巧补充</title>
			<link>https://wklken.me/posts/2014/04/13/vim-addition-skills.html</link>
			<pubDate>Sun, 13 Apr 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/04/13/vim-addition-skills.html</guid>
			<description>读了一些文章，重新理一理，大部分是平常操作中容易忽视的,很容易忘了其实还可以这么干 Text Object y/d/c + text-object-operation * iw …inner word * aw …a word * iW …inner WORD *</description>
			<content type="html"><![CDATA[<p>读了一些文章，重新理一理，大部分是平常操作中容易忽视的,很容易忘了其实还可以这么干</p>
<h3 id="text-object">Text Object</h3>
<p>y/d/c + text-object-operation</p>
<pre><code>    * iw …inner word
    * aw …a word
    * iW …inner WORD
    * aW …a WORD

    * is …inner sentence
    * as …a sentence

    * ip …inner paragraph
    * ap …a paragraph

    -------------------------

    * i( or i) …inner block
    * a( or a) …a block
    * i&lt; or i&gt; …inner block
    * a&lt; or i&gt; …a block
    * i{ or i} …inner block
    * a{ or a} …a block

    * i&quot; …inner block
    * a&quot; …a block
    * i` …inner block
    * a` …a block
</code></pre>
<h3 id="替换">替换</h3>
<p>命令格式</p>
<pre><code>:[range]s/pattern/string/[c,e,g,i]

range,范围， 1,7指从第一行到第七行  1,$第一行到最后一行
pattern,被替换字符串，可以用regexp来表示
string,替换成的字符串

c: confirm，每次替换前询问
e: 不提示error
g: globe, 不询问，整行替换[默认只替换第一个]
i: ignore,不区分大小写
</code></pre>
<p>e.g.</p>
<pre><code>:s/vivian/sky/
替换当前行第一个vivian为sky

:s/vivian/sky/g
替换当前行所有vivian为sky，g表示global

:n,$s/vivian/sky/
替换第n行开始到最后一行中每一行的第一个vivian为sky，n为数字

:.,$s/vivian/sky/g
替换当前行开始到最后一行中每一行所有vivian为sky

:%s/vivian/sky/g（等同于 :g/vivian/sky/g）
替换每一行中所有 vivian 为 sky

:s/vivian\//sky\//
替换当前行第一个vivian/为sky/，可以使用\作为转义符

:1,$s/^/some string/
在文件的第一行至最后一行的行首前插入some string

:%s/$/some string/g
在整个文件每一行的行尾添加some string

:%s/\s\+$//
去掉所有的行尾空格，“\s”表示空白字符（空格和制表符），“\+”对前面的字符匹配一次或多次（越多越好），“$”匹配行尾（使用“\$”表示单纯的“$”字符）
</code></pre>
<p>反向引用</p>
<pre><code>%s/\(aa\)/\1-/g
</code></pre>
<h3 id="修改">修改</h3>
<p>1.字符修改-大小写</p>
<pre><code>v选中, U切换大写，u切花小写
~      光标所在之处字符大小写呼唤
guw    光标下的单词变为小写
gUw    光标下的单词变为大写
</code></pre>
<p>2.字符互换</p>
<pre><code>xp  左右交换光标处两字符的位置
</code></pre>
<p>3.行-合并</p>
<pre><code>J   上下两行连起来
选中多行，然后 J    多行连起来
</code></pre>
<p>4.操作</p>
<p>命令格式</p>
<pre><code>c{motion}
删除并进入输入模式 c$  ct!
</code></pre>
<p>e.g.</p>
<pre><code>ciw  change inner word, 修改当前光标下单词
cis  change inner sentence, 修改一整个句子

#text-object
ci&quot;
ci(

cw   修改一个单词
cc   change the whole line,剪切光标所在行并进入插入模式

ctx  剪切光标至字符x，进入插入模式
</code></pre>
<h3 id="删除">删除</h3>
<pre><code>dG 删除至文件末尾
dgg 删除只文件开头
</code></pre>
<h3 id="查找和跳转">查找和跳转</h3>
<p>跳转到当前行某个位置</p>
<pre><code>fx  到第一个x
2fx 到第二个x

Fx  往回查找
</code></pre>
<p>跳转到某行</p>
<pre><code>50G 移动到 50行
:50
</code></pre>
<p>标记并跳转</p>
<pre><code>#对26个字母有效
ma 标记书签
'a 跳到书签
'.  最后一次编辑的地方
</code></pre>
<p>段落跳转</p>
<pre><code>{   上一段(以空白行分隔)
}   下一段(以空白行分隔)

) 下一个句子
( 上一个句子
</code></pre>
<h3 id="选中">选中</h3>
<p>操作</p>
<pre><code>v   按字符
V   按行
ctrl+v 按块
</code></pre>
<p>命令格式</p>
<pre><code>va&lt;object&gt; or vi&lt;object&gt;

object ：
    w 一个单词， W 一个以空格为分隔的单词，
    s 一个句子，
    p 一个段落。
    也可以是一个特别的字符：&quot;、 '、 )、 }、 ]。
注意，这里v换成d/y就成了删除，拷贝的命令
</code></pre>
<p>e.g.</p>
<pre><code>假设你有一个字符串
    (map (+) (&quot;foo&quot;)).

而光标键在第一个 o 的位置。
vi&quot; → 会选择 foo.
va&quot; → 会选择 &quot;foo&quot;.
vi) → 会选择 &quot;foo&quot;.
va) → 会选择(&quot;foo&quot;).
v2i) → 会选择 map (+) (&quot;foo&quot;)
v2a) → 会选择 (map (+) (&quot;foo&quot;))'&quot;
</code></pre>
<p>选中括号里的内容</p>
<pre><code>v% 光标在一侧括号
</code></pre>
<p>全选</p>
<pre><code>ggVG
</code></pre>
<h3 id="保存和退出">保存和退出</h3>
<pre><code>:e file 打开文件
ZQ  无条件退出
ZZ  存盘退出
</code></pre>
<p>保存部分内容</p>
<pre><code>:1,10 w a.txt
:1,10 w &gt;&gt; a.txt
</code></pre>
<p>另存为</p>
<pre><code>:saveas new_file
</code></pre>
<p>执行外部命令</p>
<pre><code>:!cmd
:r!cmd  指向外部命令的结果插入到当前
</code></pre>
<h3 id="关于vim学习的建议">关于vim学习的建议</h3>
<p>来自网上两篇文章</p>
<pre><code>更多的是技能而非知识
更多的是双手而非头脑
Vim技巧
不贪图一下子掌握
不断实践
不是由头脑来搜索该用哪条操作技巧
而是建立手指的自然反应
</code></pre>
<p>一定要阅读一下Bram Moolenaar(vim的作者)写的Seven habits of effective text editing(七个有效的文本编辑习惯)</p>
<p>七个习惯</p>
<pre><code>快速移动
不要两次键入同样的东西
错误修复
经常需要编辑不止一个文件
协同作业
文本是结构化的
养成习惯
</code></pre>
<h3 id="其他">其他</h3>
<p>大写的</p>
<pre><code>D 删除当前到行尾，不要用d$
Y 复制整行，建议改写成复制当前到行尾
C 修改当前到行尾
</code></pre>
<p>repeat:</p>
<pre><code>. → (小数点) 可以重复上一次的命令
N&lt;command&gt; → 重复某个命令N次
</code></pre>
<p>宏录制和使用</p>
<pre><code>待补充
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>读书笔记--高效能人士的七个习惯&#43;执行4原则</title>
			<link>https://wklken.me/posts/2014/03/30/highly-effective-and-execution.html</link>
			<pubDate>Sun, 30 Mar 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/03/30/highly-effective-and-execution.html</guid>
			<description>今天打算写的是，关于两本“成功学”的书 《高效能人士的七个习惯》和《高效能人士的执行4原则》 一直以来对“成功学”都不感冒，“虚”，“扯淡”，“</description>
			<content type="html"><![CDATA[<p>今天打算写的是，关于两本“成功学”的书</p>
<p>《高效能人士的七个习惯》和《高效能人士的执行4原则》</p>
<p><img src="/imgs/books/the-7-habits-of-highly-effective-people.jpg" alt="封面1">
<img src="/imgs/books/the-4-disciplines-of-execution.jpg" alt="封面2"></p>
<p>一直以来对“成功学”都不感冒，“虚”，“扯淡”，“毫无意义”，一直没搞明白为什么那么多人，会专门去花费大量金钱学习和研究这些东西</p>
<p>以前不感冒，当然，现在也是，一如既往</p>
<p>以上两本书，一本书是刚毕业那会部门老大推荐的，跳槽之后，新的老大也推了这本书，所以从书堆里翻出来，认认真真地看完了（很惊讶走了那么多地方书还能留着），后面一本是亚马逊kindle上捞到的。</p>
<p>这两本，算是成功学，也不算，很多东西讲到了点上，有些道理，认同，也会去遵循</p>
<p>值得一看，能从中能借鉴一些东西</p>
<hr>
<h2 id="高效能人士的七个习惯">高效能人士的七个习惯</h2>
<p><img src="/imgs/books/the-7-habits-of-highly-effective-people-concepts.jpg" alt="主要概念图1"></p>
<pre><code>习惯一：积极主动——个人愿景的原则
习惯二：以终为始——自我领导的原则
习惯三：要事第一——自我管理的原则
习惯四：双赢思维——人际领导的原则
习惯五: 知彼解己——移情沟通的原则
习惯六: 统合综效——创造性合作的原则
习惯七：不断更新——平衡的自我更新的原则
</code></pre>
<h4 id="习惯一积极主动个人愿景的原则">习惯一：积极主动——个人愿景的原则</h4>
<p>人性的本质是主动而非被动地——人类确实能主动努力以提升生命价值(主观能动性)</p>
<p>积极主动，不仅指行事的态度，还意味着为人一定要对自己的人生负责</p>
<p>积极主动的人专注于“影响圈”，专心做自己力所能及的事情，能量是积极的，是影响圈不断扩大</p>
<p>影响圈的核心就是，作出承诺与信守诺言的能力</p>
<p>其他习惯的基础！！！</p>
<h4 id="习惯二-以终为始自我领导的原则">习惯二： 以终为始——自我领导的原则</h4>
<p>以你的人生目标作为衡量一切的标准。由个人最重视的期许或价值观来决定一切</p>
<p>在做任何事情之前，都需要认清方向——定目标</p>
<p>你以什么为中心？你为谁而活？</p>
<p>以原则为中心！！！！</p>
<p>以永恒不变的原则作为生活重心，就能建立高效能的思维定势，也就能正确审视所有其他的生活中心。(一个人的思维定式能决定他的态度和行为)</p>
<p>原因：主动的选择，没有受到环境和他人的影响；最有效的选择，且长期可预料；基于原则所作出的选择，能提高自身的价值。</p>
<p>所以，撰写使命宣言并付诸实践(确定角色和目标)</p>
<p>书写个人使命宣言 —— 即人生哲学或基本信念
（个人宪法，基于正确原则的个人使命宣言也同样是评价一切的标准）</p>
<h4 id="习惯三要事第一自我管理的原则">习惯三：要事第一——自我管理的原则</h4>
<p>把最重要的事情放在第一位，先做重要的事情</p>
<p>矩阵： 重要/不重要  *  紧急/不紧急</p>
<p>关注重要不紧急的事情</p>
<p>重要不紧急的事情： 建立人际关系，撰写使命宣言，规划长期目标，防患于未然等等</p>
<p>在必要时，勇于说“不”</p>
<p>学会授权</p>
<h4 id="习惯四双赢思维人际领导的原则">习惯四：双赢思维——人际领导的原则</h4>
<p>双赢 - 不断地在人际交往中寻求双边利益</p>
<p>要明确意识到其重要性</p>
<p>基础：信任</p>
<p>双赢关系</p>
<p>双赢的精髓就是信用，即情感账户</p>
<p>双赢过程：</p>
<pre><code>1.要从对方的角度看问题，真正理解对方的想法
2.认清主要问题和顾虑（而非立场）
3.确定大家都能接受的结果
4.实现这种结果的各种可能途径
</code></pre>
<h4 id="习惯五-知彼解己移情沟通的原则">习惯五 知彼解己——移情沟通的原则</h4>
<p>首先寻求去了解对方，然后再争取让对方了解自己。（进行有效人际交流的关键）</p>
<p>你真的听懂了么？</p>
<p>移情聆听</p>
<p>以理解为目的的聆听，要求听者站在说话者的角度理解他们的思维模式和感受</p>
<p>不仅要耳到，还要眼到，心到。用眼睛去观察，用心灵去体会</p>
<p>有效的沟通：</p>
<pre><code>1.第一阶段，复述语句，至少能使人专心聆听
2.第二阶段，加入解释，纯用自己的词句表达，用左脑的逻辑思考去理解
3.第三阶段，深入个人的感觉，右脑发挥作用，开始体会对方的心情
4.即加以理解，又带有感情，左右脑并用
</code></pre>
<p>如果你真正爱一个人，那么花时间了解对方将有益于今后的坦诚相待</p>
<h4 id="习惯六-统合综效创造性合作的原则">习惯六 统合综效——创造性合作的原则</h4>
<p>统合综效的基本心态是——如果一位具有相当聪明才智的人跟我意见不同，那么对方的主张必定有我尚未体会的奥妙，值得加以理解</p>
<p>有分歧才有收获</p>
<p>统合综效就是整体大于部分之和</p>
<p>统合综效的精髓就是判断和尊重差异，取长补短</p>
<p>所谓统合综效的沟通，是指敞开胸怀，接纳一切奇怪的想法，同时也贡献自己的浅见</p>
<p>尊重差异，要尊重，不偏激</p>
<p>即使处于不利的境地，也不应该放弃追求统合综效。</p>
<h4 id="习惯七不断更新平衡的自我更新的原则">习惯七：不断更新——平衡的自我更新的原则</h4>
<p>拥有财富，并不代表经济独立，拥有创造财富的能力才真正可靠</p>
<p>自我提升和完善的四个层面：身体，精神，智力，社会/情感</p>
<p>不断更新，意味着要兼顾这四种要素</p>
<ul>
<li>身体层面：</li>
</ul>
<p>健康饮食、充足休息以及定期锻炼</p>
<pre><code>    耐力：源于有氧运动
    韧性：源于伸展运动
    力量：源于持久的肌肉运动
</code></pre>
<ul>
<li>精神层面：</li>
</ul>
<p>人的本质，核心，对价值体系的坚持，是生活中非常私人但是至关重要的领域</p>
<ul>
<li>智力层面：</li>
</ul>
<p>主要靠教育，借此不断学习知识，魔力心智，开阔视野</p>
<p>养成定期阅读优秀文学作品的习惯</p>
<p>另一种有效的方式是写作，不断记录自己的想法，经历深刻见解和学习心得</p>
<ul>
<li>社会、情感层面</li>
</ul>
<p>在与他人的日常交往中完成，还需要必要的练习</p>
<p>坚守原则，肯定自我，与人为善，相信人生不止输赢两种抉择</p>
<p>平衡，要把握平衡！！！！！</p>
<p>螺旋式上升，良性循环，学习-坚持-时间，并沿着螺旋式上升的路线不断提高实践的层次</p>
<h2 id="高效能人士的执行4原则">高效能人士的执行4原则</h2>
<p><img src="/imgs/books/the-4-disciplines-of-execution-concepts.jpg" alt="主要概念图2"></p>
<h4 id="原则1聚焦最重要的目标focus-on-the-wildly-important">原则1：聚焦最重要的目标(Focus on the Wildly Important)</h4>
<p>将你最好的精力集中到一两个重要的目标（能够使你得到革命性结果的事情）</p>
<p>日常事务（周而复始，要做但不是重点）-&gt;    &lt;- 新目标（聚集精力在此）</p>
<p>好主意的数量总会超出执行能力的范围（learn to say no）</p>
<p>（How）最重要的目标是能够给你的组织带来巨大变化的目标：如果其他方便都保持现状的话，改进哪一个会给我们带来巨大的收益？</p>
<p>两类：来源于日常范围之内的（改进提升）来源于日常范围之外的（创新）</p>
<p>注意：没有任何团队可以同时关注两个以上的最重要目标。你选择的局部战斗必须要为赢得整个战争服务。领导可以否决，但不能独断。所有最重要的目标必须要有完成的时限和标准（SMART原则）</p>
<h4 id="原则2关注引领性指标act-on-the-lead-measures">原则2：关注引领性指标(Act on the Lead Measures)</h4>
<p>引领性指标是指那些和达成最终目标关系最为紧密的事情</p>
<p>滞后性指标（不能改变些什么，已经发生了，更容易获得，更直观）可以告诉你是否完成了目标，而引领性指标（预见性，可控性）可以教会你怎样做才能去完成目标</p>
<p>引领性指标，指针对最终目标而制定的，可预见可控的阶段性目标（杠杆作用-&gt;高效益）
显著特征：预见性，意味着一旦某个引领性指标发生变化，你就可以根据这个推断出滞后性指标之后会有什么变化。可控的，可以被你的团队所影响，你们可以靠自己的力量使促使引领性指标发生变化</p>
<p>引领性指标的数据往往比滞后性指标数据更难以获取</p>
<h4 id="原则3坚持激励性计分表keep-a-compelling-scoreboard">原则3：坚持激励性计分表(Keep a Compelling Scoreboard)</h4>
<p>这是一条激励士气的原则</p>
<p>确保每个人都能随时获知自己的成绩，这样他们才能知道自己领先还是落后（当面对成绩时，人们的表现将会发生变化）</p>
<p>将引领性指标和滞后性指标-&gt;看得见摸得着的量化指标</p>
<p>一个伟大的团队，在任何时候都知道自己的进度是否成功</p>
<p>建立激励性的选手型记分表：1.它是否简单？2.它是否显而易见3.是否能同时展示引领性指标和滞后性指标4.能否一眼从记分表上看出是否胜利</p>
<h4 id="原则4建立规律的问责制create-a-cadence-of-accountability">原则4：建立规律的问责制(Create a Cadence of Accountability)</h4>
<p>建立有规律的责任机制，一个对过去表现和未来计划的有规律的周期性问责</p>
<p>最重要目标会议：每周，20-30分钟之内，有一套完整流程并快速进行，为工作确定每一周的节奏，从而带动最重要目标的不断进展。</p>
<p>两条原则：1.应该在每周的同一天的同一时间召开 2.永远不要把日常事务带到最重要目标会议中来</p>
<p>组成部分：</p>
<pre><code>1.问责：汇报工作计划的完成情况
2.回顾记分表：寻找成功和不足
3.计划：清除障碍，作出新计划
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Golang笔记-06-struct</title>
			<link>https://wklken.me/posts/2014/03/09/06-struct.html</link>
			<pubDate>Sun, 09 Mar 2014 06:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/03/09/06-struct.html</guid>
			<description>struct struct，一组字段的集合，类似其他语言的class 放弃了大量包括继承在内的面向对象特性，只保留了组合(composition)这个最基</description>
			<content type="html"><![CDATA[<h3 id="struct">struct</h3>
<p>struct，一组字段的集合，类似其他语言的class</p>
<p>放弃了大量包括继承在内的面向对象特性，只保留了组合(composition)这个最基础的特性</p>
<h4 id="1声明及初始化">1.声明及初始化</h4>
<pre><code>type person struct {
    name string
    age  int
}

//初始化

func main() {
    var P person

    P.name = &quot;tom&quot;
    P.age = 25
    fmt.Println(P.name)

    P1 := person{&quot;Tom1&quot;, 25}
    fmt.Println(P1.name)

    P2 := person{age: 24, name: &quot;Tom&quot;}
    fmt.Println(P2.name)
}
</code></pre>
<h4 id="2struct的匿名字段继承">2.struct的匿名字段(继承)</h4>
<pre><code>type Human struct {
    name string
    age int
    weight int
}

tyep Student struct {
    Human //匿名字段，默认Student包含了Human的所有字段
    speciality string
}

mark := Student(Human{&quot;mark&quot;, 25, 120}, &quot;Computer Science&quot;)

mark.name
mark.age
</code></pre>
<p>能够实现字段继承，当字段名重复的时候，优先取外层的,可以通过指定struct名还决定取哪个</p>
<pre><code>mark.Human = Human{&quot;a&quot;, 55, 220}
mark.Human.age -= 1
</code></pre>
<p>struct不仅可以使用struct作为匿名字段，自定义类型、内置类型都可以作为匿名字段,而且可以在相应字段上做函数操作</p>
<h4 id="3method">3.method</h4>
<pre><code>type Rect struct {
    x, y float64
    width, height float64
}

//method

Reciver 默认以值传递，而非引用传递，还可以是指针
指针作为Receiver会对实例对象的内容发生操作，而普通类型作为Receiver仅仅是以副本作为操作对象，而不对原实例对象发生操作

func (r ReciverType) funcName(params) (results) {

}

如果一个method的receiver是*T，调用时，可以传递一个T类型的实例变量V，而不必用&amp;V去调用这个method

func (r *Rect) Area() float64 {
    return r.width * r.height
}

func (b *Box) SetColor(c Color) {
    b.color = c
}
</code></pre>
<h3 id="4method继承和重写">4.method继承和重写</h3>
<p>采用组合的方式实现继承</p>
<pre><code>type Human struct {
    name string
}

type Student struct {
    Human
    School string
}

func (h *Human) SayHi() {
    fmt.Println(h.name)
}

//则Student和Employee的实例可以调用
func main() {
    h := Human{name: &quot;human&quot;}
    fmt.Print(h.name)
    h.SayHi()

    s := Student{Human{&quot;student&quot;}}
    s.SayHi()

}
</code></pre>
<p>还可以进行方法重写</p>
<pre><code>funct (e *Student) SayHi() {
    e.Human.SayHi()
    fmt.Println(e.School)
}
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>读书笔记——我编程，我快乐</title>
			<link>https://wklken.me/posts/2014/03/08/the-passionate-programmer.html</link>
			<pubDate>Sat, 08 Mar 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/03/08/the-passionate-programmer.html</guid>
			<description>这是读到的第四本和37signals有联系的书 春节在回家的动车上读完的(飞机火车阅读效率加成&amp;gt;_&amp;lt;)，有些收获，今天翻出来重读了</description>
			<content type="html"><![CDATA[<p>这是读到的第四本和37signals有联系的书</p>
<p>春节在回家的动车上读完的(飞机火车阅读效率加成&gt;_&lt;)，有些收获，今天翻出来重读了一次</p>
<p>一系列观点和方法论，很多还是值得借鉴和思考的</p>
<p>记录观点以及自己一两句话的总结整理</p>
<p>这本书还是值得一读的，建议入手</p>
<p>PS:年后买纸质书需三思再三思，开始多读电子书吧</p>
<p>简而言之:</p>
<pre><code>1.选好技术和领域: 通才和专家
2.跟上潮流
3.学会更好更高效地工作：目标，计划，沟通，协作，写作，执行力....很多（重点）
4.避免思维僵固
5.写博客
6.维护自己的开源项目
</code></pre>
<p><img src="/imgs/books/passionate-programmer.jpg" alt="封面"></p>
<hr>
<p>帮你成就更卓越、更有意义的人生，工作只是其中的一部分</p>
<p>如果生活的大部分时间都被工作占据着，那么热爱工作就是热爱生活</p>
<p>制定自己的计划</p>
<pre><code>1.选择市场——关注的技术和商业领域
2.投资——知识和技术
3.执行——更好地产出
4.市场
</code></pre>
<hr>
<h3 id="第一章-选择市场">第一章 选择市场</h3>
<h5 id="1-稳定成熟的技术还是未成熟的技术">1. 稳定成熟的技术还是未成熟的技术？</h5>
<p>前瞻性，业界</p>
<p>决定权在你自己手中</p>
<blockquote>
<p>无论做出哪种选择，最终的目的是，产生利润</p>
</blockquote>
<h5 id="2-供应和需求">2. 供应和需求</h5>
<p>供需关系导致的薪资变化</p>
<blockquote>
<p>不要再价格上竞争，你承受不起</p>
</blockquote>
<p>需要在能力上与之抗争,更高层面</p>
<blockquote>
<p>发现市场上的不平衡</p>
</blockquote>
<h5 id="3-只会编程是不够的">3. 只会编程是不够的</h5>
<p>必须要深入了解你所处的领域</p>
<p>你的行业经历应该成为你的重要才能</p>
<blockquote>
<p>仔细思考在哪个商业领域投入时间</p>
</blockquote>
<h5 id="4-做团队中最差的">4. 做团队中最差的</h5>
<p>和优秀的人一起工作</p>
<p>与一个团队合作时间长了，会对自身的能力产生持久的影响</p>
<p>你身边的人会对你产生很大的影响，明智地选择你的圈子</p>
<h5 id="5-在思维上投资">5. 在思维上投资</h5>
<blockquote>
<p>没人给过我机会&hellip;.?要学会抓住机遇</p>
</blockquote>
<h5 id="6-不要听从父母">6. 不要听从父母</h5>
<p>父母总不希望儿女去冒险</p>
<p>在职业道路上，需要一些有目的性的冒险。别让恐惧征服了你</p>
<h5 id="7-做一名通才">7. 做一名通才</h5>
<p>传说中的全栈？</p>
<blockquote>
<p>通才很少，所以珍贵</p>
</blockquote>
<p>IT职业分解</p>
<pre><code>- 职业阶梯的各层
- 平台和操作系统
- 代码和数据
- 系统和应用
- 业务和IT
</code></pre>
<p>学会适应以及胜任不同角色的价值所在</p>
<blockquote>
<p>你的技术应该超越技术平台</p>
</blockquote>
<h5 id="8-成为一名专家">8. 成为一名专家</h5>
<blockquote>
<p>很多人认为专攻某种技术就简单地意味着不知道其他技术</p>
</blockquote>
<p>很显然，这是错误的</p>
<p>能搞定工作中遇到80%可预见的问题，并且有足够的知识来应付另外还未出现的20%的问题</p>
<h5 id="9-切忌孤注一掷">9. 切忌孤注一掷</h5>
<blockquote>
<p>以特定技术厂商为中心的观点，缺乏远见</p>
</blockquote>
<p>的确如此，厂商悲催了，你承受不了代价</p>
<h5 id="10-热爱它不然就离开它">10. 热爱它，不然就离开它</h5>
<blockquote>
<p>工作，因为你无法停止工作</p>
</blockquote>
<hr>
<h3 id="第二章-在产品上投资">第二章 在产品上投资</h3>
<h5 id="11-学会钓鱼">11. 学会钓鱼</h5>
<blockquote>
<p>需要主动问，不要等着别人来告诉你</p>
</blockquote>
<p>要自己主动学习，自学能力</p>
<h5 id="12-学习行业是如何运转的">12. 学习行业是如何运转的</h5>
<blockquote>
<p>只有了解了一个行业后，你才能创造性地有所建树</p>
</blockquote>
<p>业务领域很重要</p>
<h5 id="13-寻找良师">13. 寻找良师</h5>
<blockquote>
<p>可以依赖别人，但需确保这个人是靠得住的</p>
</blockquote>
<p>减少试错成本</p>
<h5 id="14-做一名良师">14. 做一名良师</h5>
<blockquote>
<p>想要弄明白自己是不是真的懂得某一知识，那就把它讲给其他人听</p>
</blockquote>
<blockquote>
<p>做导师不会下岗</p>
</blockquote>
<h5 id="15-练习练习再练习">15. 练习、练习、再练习</h5>
<p>需要刻意练习</p>
<blockquote>
<p>在极限处练习</p>
</blockquote>
<h5 id="16-做事的方法">16. 做事的方法</h5>
<blockquote>
<p>想要拥有自己的步骤，那就执行它</p>
</blockquote>
<p>学习，实践某些方法论，优化工作流</p>
<h5 id="17-站在巨人的肩膀上">17. 站在巨人的肩膀上</h5>
<blockquote>
<p>从现有的程序中得到领悟</p>
</blockquote>
<p>学习其他程序猿是如何系统地解决某一特定问题的</p>
<blockquote>
<p>用现有的程序反思自己的程序</p>
</blockquote>
<p>需要有更开阔的眼界</p>
<h5 id="18-在工作中将自己自动化">18. 在工作中，将自己自动化</h5>
<p>找出消耗你时间的东西，自动化之</p>
<hr>
<h3 id="第三章-执行">第三章 执行</h3>
<h5 id="19-就是现在">19. 就是现在</h5>
<p>流程问题？拖延症等？</p>
<blockquote>
<p>就现在，我们能做些什么？</p>
</blockquote>
<p>帕金森定律： 工作会自动膨胀到占满所有可用的时间</p>
<p>开始行动，不要总是安于现状，要做推动者(活血这正是我目前缺乏的)</p>
<h5 id="20-读心术">20. 读心术</h5>
<p>仔细观察，倾听，了解所需要做的，所能改善的(前提，你了解的信息时正确的)</p>
<blockquote>
<p>读心术用得好，人们就会信任你</p>
</blockquote>
<h5 id="21-每日成绩">21. 每日成绩</h5>
<blockquote>
<p>每天都有可汇报的成绩</p>
</blockquote>
<p>记录目标，并记录下来</p>
<h5 id="22-别忘了你在为谁工作">22. 别忘了你在为谁工作</h5>
<p>确保你的目标和工作与你公司的目标一致</p>
<p>即使不愿意，但需要绝对的执行力</p>
<p>在结构良好的环境中，经历的目标，就是整个团队的目标，解决了经历的问题，就是解决了整个团队的问题</p>
<p>好的经理的职责应该是为团队设定优先级，确保团队具备完成工作的一切需要，保证团队保持干劲和工作效率，并出示团队最终顺利完成工作。</p>
<h5 id="23-安分守己">23. 安分守己</h5>
<blockquote>
<p>要有雄心，但不必路人皆知</p>
</blockquote>
<p>专注于现在的工作</p>
<p>做好当下正确的事情</p>
<h5 id="24-今天我能把工作做到多好">24. 今天我能把工作做到多好？</h5>
<blockquote>
<p>你能为工作增添多少乐趣</p>
</blockquote>
<p>发挥创造力来应对那些平凡的工作</p>
<h5 id="25-你的价值多少">25. 你的价值多少</h5>
<p>了解公司在你身上的开销和你的产出</p>
<blockquote>
<p>问自己&quot;今天实现自己的价值了么&quot;</p>
</blockquote>
<h5 id="26-一桶水中的鹅卵石">26. 一桶水中的鹅卵石</h5>
<p>如果你明天离开公司，对公司造成的影响与其他同事离开有没有区别？</p>
<p>永远不要高枕无忧</p>
<blockquote>
<p>小心，别让成功冲昏了头脑</p>
</blockquote>
<p>要学会让自己不可替代，要建立一种友好的工作关系</p>
<p>同时也要知道，每个人都不是不可替代的，清楚知道这一点，并努力工作</p>
<h5 id="27-爱上维护">27. 爱上维护</h5>
<p>人们都喜欢创造</p>
<blockquote>
<p>维护也可以成为自由和创造的沃土</p>
</blockquote>
<p>最小开支维持软件正常运行</p>
<p>可以设计更可见的改进</p>
<p>可以有机会和其他客户直接进行交流</p>
<p>最讽刺的是，项目工作其实就是维护，只要项目团队写下来的第一行编码。</p>
<h5 id="28-8小时激情燃烧">28. 8小时激情燃烧</h5>
<p>在工作上，更少的工作时间可以有更高的效率</p>
<p>创造力和工作质量</p>
<blockquote>
<p>做项目像是马拉松，而不是全速短跑</p>
</blockquote>
<p>减少工作时间，你的收获更多</p>
<h5 id="29-学习如何失败">29. 学习如何失败</h5>
<p>带着防御性错失进行编程是很重要的</p>
<blockquote>
<p>每个错误的音调离正确的音调不过一步之遥</p>
</blockquote>
<p>解决计数、沟通或者项目管理中的错误</p>
<pre><code>- 发现错误第一时间提出，不要企图隐瞒错误
- 接受批评
- 提供解决方法
- 寻求帮助
</code></pre>
<blockquote>
<p>充满压力的时候是赢得忠诚的最好时机</p>
</blockquote>
<h5 id="30-说不">30. 说&quot;不&quot;</h5>
<blockquote>
<p>为了避免失望而说“是”，就是在说谎</p>
</blockquote>
<p>这不是个好习惯</p>
<p>要勇于诚实</p>
<h5 id="31-不要恐慌">31. 不要恐慌</h5>
<blockquote>
<p>英雄从不恐慌</p>
</blockquote>
<p>恐慌会导致丧失判断力</p>
<p>遇到问题，分析情境，获取意见，换角度思考问题</p>
<h5 id="32-说出来行动展示">32. 说出来、行动、展示</h5>
<p>承诺 - 做计划</p>
<blockquote>
<p>状态报告可以帮助你推销自己</p>
</blockquote>
<p>做计划时要时刻谨记的是，出现在计划上的每一项工作必须是要与后续工作相关，要么被完成、推迟、去除或被替代</p>
<p>碰到问题，做出计划来解决问题，而不是抱怨</p>
<hr>
<h3 id="第四章-推销不仅仅是迎合">第四章 推销,不仅仅是迎合</h3>
<p>表面上看，宣传自己很简单</p>
<p>你的目标有两个</p>
<pre><code>- 让别人知道你的存在
- 以及让他们知道，当他们碰到难题时，你是那个可以解决问题的人
</code></pre>
<h5 id="33-不要忽视感觉">33. 不要忽视感觉</h5>
<p>如果你非常出色，但并没有人知道，那你真的优秀么？谁会在意？没人会在意(很残酷，但是是对的)</p>
<blockquote>
<p>绩效考核永远不会是客观的(很不幸，这句也是对的)</p>
</blockquote>
<p>任何地方对你的评价都是主观的，意味着对你做出的评价总是基于别人对你的感觉</p>
<h5 id="34-探险向导">34. 探险向导</h5>
<blockquote>
<p>客户害怕您</p>
</blockquote>
<p>让人们了解到你的沟通能力是非常重要的</p>
<h5 id="35-学会沟通善与写作">35. 学会沟通，善与写作</h5>
<p>文字表达能力非常重要</p>
<blockquote>
<p>你自己就是你需要解释的内容</p>
</blockquote>
<h5 id="36-到场">36. 到场</h5>
<p>能够与上司和客户面对面地沟通是你的优势，不要浪费这个机会</p>
<p>面对面沟通是必要的</p>
<blockquote>
<p>了解你的同事</p>
</blockquote>
<h5 id="37-适当的语言">37. 适当的语言</h5>
<blockquote>
<p>请用行业术语推销你的成就</p>
</blockquote>
<h5 id="38-改变世界">38. 改变世界</h5>
<blockquote>
<p>带着任务去上班，并确保别人知道你的任务</p>
</blockquote>
<p>如果你不知道自己要做的改变是什么，那你就没有在做任何改变</p>
<p>如果你没有主动让自己获得承认，那你就还没有获得承认</p>
<h5 id="39-让人们听到你的声音">39. 让人们听到你的声音</h5>
<p>暮光放的更远一些，不要把自己局限在某一特定公司中的程序员</p>
<p>博客文章代码</p>
<h5 id="40-创建自己的商标">40. 创建自己的商标</h5>
<blockquote>
<p>你的名字就是你的商标</p>
</blockquote>
<blockquote>
<p>Google永远不会忘记</p>
</blockquote>
<h5 id="41-发布你编写的程序">41. 发布你编写的程序</h5>
<p>开源</p>
<blockquote>
<p>人人都能使用Rails,但很少有人能开发出Rails</p>
</blockquote>
<h5 id="42-变为卓越的能力">42. 变为卓越的能力</h5>
<p>卓越的意思是值得被关注</p>
<blockquote>
<p>展示或者让它死亡</p>
</blockquote>
<h5 id="43-建立关系">43. 建立关系</h5>
<blockquote>
<p>恐惧感是我们无法接近专业人士</p>
</blockquote>
<hr>
<h3 id="第五章-保持技术领先">第五章 保持技术领先</h3>
<h5 id="44-已经过时的技术">44. 已经过时的技术</h5>
<p>需要想想，哪些为何过时，哪些又不会？</p>
<blockquote>
<p>你引以为傲的新技术已经过时了</p>
</blockquote>
<p>时间就是一切，学习之前要先动动脑子</p>
<p>这就像是赌博，但是如果你不参加，就一定会输(&hellip;.)</p>
<p>向前看，清楚地知道你的技术发展方向</p>
<h5 id="45-你已经失去工作了">45. 你已经失去工作了</h5>
<blockquote>
<p>你不是你的工作</p>
</blockquote>
<p>任何事情都是处于变化之中</p>
<p>永远不要把自己当成一名程序员（记住这一点）</p>
<p>不要给自己设限</p>
<h5 id="46-没有终点的道路">46. 没有终点的道路</h5>
<p>职业生涯，最重要的部分不是晋升或者加薪，而是想这些发展方向努力工作的过程，或者，更重要的是，你抛开着一切忘我工作的过程</p>
<blockquote>
<p>不要关注结果，需要关注做事情的过程</p>
</blockquote>
<p>（但是考核的时候，结果为导向?）</p>
<p>都有个度的问题</p>
<h5 id="47-给自己一份蓝图">47. 给自己一份蓝图</h5>
<p>不要原地踏步</p>
<p>个人产品路线蓝图是用来判断你是否在不断向前发展的依据</p>
<h5 id="48-要注意观察市场变化">48. 要注意观察市场变化</h5>
<blockquote>
<p>留意那些技术达人</p>
</blockquote>
<p>防止被淘汰哈</p>
<h5 id="49-镜子里的胖子">49. 镜子里的胖子</h5>
<p>自身很那去观察注意自己的职业发展</p>
<p>找一个可信赖的第三方，是衡量进步的简单方法</p>
<blockquote>
<p>开发员们，要自我反省</p>
</blockquote>
<p>评价系统！</p>
<h5 id="50-南印度捉猴陷阱">50. 南印度捉猴陷阱</h5>
<p>价值僵固：当你过于坚信某事的价值时，就会无法客观地来评判它</p>
<blockquote>
<p>价值僵固是你脆弱</p>
</blockquote>
<p>语言之争，编辑器之战等等</p>
<h5 id="51-避免瀑布型职业计划">51. 避免瀑布型职业计划</h5>
<p>职业改变不仅是有可能的，并且是非常必要的</p>
<p>从实践中学习，不断改变你的目标</p>
<h5 id="52-每天都有进步">52. 每天都有进步</h5>
<p>每一个改变之后，你可能无法看到整体发生明显的不同</p>
<p>量变引起质变</p>
<p>即使一点小进步，你也应该感到高兴</p>
<p>人，总是不断在进步和成长的，多读些书，多学写一些东西</p>
<h5 id="53-独立">53. 独立</h5>
<p>独立不是件简单的事情，这把你所有的技术作为一个专业来测试</p>
<p>把它当做是个人发展项目</p>
<p>好奇，是一种优点</p>
]]></content>
		</item>
		
		<item>
			<title>读书笔记——写给大家看的设计书</title>
			<link>https://wklken.me/posts/2014/03/02/the-non-designers-design-book.html</link>
			<pubDate>Sun, 02 Mar 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/03/02/the-non-designers-design-book.html</guid>
			<description>对设计，知之甚少，战5渣 读到这本书，感觉蛮有收获的，起码会有一个基本的设计意识和概念，有些作用 这本书主要围绕设计的四个基本原则展开说明，有详</description>
			<content type="html"><![CDATA[<p><img src="/imgs/books/design-book.jpg" alt="cover"></p>
<p>对设计，知之甚少，战5渣</p>
<p>读到这本书，感觉蛮有收获的，起码会有一个基本的设计意识和概念，有些作用</p>
<p>这本书主要围绕设计的四个基本原则展开说明，有详细的说明和对比案例</p>
<hr>
<h3 id="设计原则">设计原则</h3>
<p>得到优秀的设计</p>
<pre><code>1.学习四大基本原则
2.认识到自己没有运用这些原则
3.应用基本原则
</code></pre>
<h4 id="1亲密性">1.亲密性</h4>
<pre><code>将相关项目组织在一起：移动这些项，使它们的物理位置互相靠近。
相关的项将被看做凝聚为一体的一个组，而不再是一堆彼此无关的片段

亲密性意味着存在关联
</code></pre>
<p>注意，人们查看时的视觉感受，顺序(视线如何移动的，起点-路径-结束)、停留、重点。将有很近的亲密性的多个项目放一起，成为一个视觉单元</p>
<pre><code>如果某些元素在理解上存在关联，或者相互之间存在某种关系，那么这些元素在视觉上也应当有关联
</code></pre>
<p>亲密性也同时意味着，对非亲密项进行隔离（利用空白、线、图等）,使孤立</p>
<pre><code>位置是否靠近可以体现出元素之间是否存在关系
</code></pre>
<p>需要对所有元素有一个分类组织的过程</p>
<pre><code>亲密性的根本目的是视线组织性(条理性)

条理，意味着更容易被阅读和记住
</code></pre>
<p>要避免的问题</p>
<pre><code>不要仅仅因为有空白就把元素放在角落或者中间
避免一个页面上有太多孤立的元素
不要再元素之间留出相同大小的空白，除非各组同属于一个子集
不属于同一组的元素之间一定不要建立关系
</code></pre>
<h4 id="2对齐">2.对齐</h4>
<pre><code>任何元素都不能在页面上随意安放，每一项都应当与页面上的某个内容存在某种视觉联系

对齐-更内聚的单元，虽然其物理位置可能是彼此分离的

建立一种看不见的关联

应该找到一条明确的对齐线，并用它来对齐(皆为线)
</code></pre>
<p>左对齐和右对齐看起来外观更为清晰,效果更分明，而居中对齐不是，所以，尽量避免，多留意居中对齐的效果是否是你想要表达的。当然，慎用两端对齐</p>
<p>勇敢一些，不要畏缩</p>
<p>在得到更多培训之前，一定要坚持一个原则：页面上只是用一种文本对齐（所有文本都左对齐、右对齐或者居中）,避免混合使用多种对齐方式</p>
<p>如果使用两种，需要懂得，聚聚项目之间的布局关系</p>
<p>根本目的</p>
<pre><code>是页面统一而且有条理

统一性：页面上所有元素看上去统一、有联系而且彼此相关，需要在各个单独的元素之间存在某种视觉纽带
</code></pre>
<p>要特别注意元素放在哪里。应当总能在页面上找出与之对齐的元素，尽管这两个对象的物理位置可能相距很远</p>
<h4 id="3重复">3.重复</h4>
<pre><code>设计的某些方面需要在整个作品中重复

读者看到的任何方面都可以作为重复元素：字体，线，项目符号，颜色，设计要素，某种格式，空间关系

重复有助于组织信息:利于将设计中单独的部分统一起来，可以帮助读者浏览各个页面
</code></pre>
<p>重复的最大好处是是各项看起来同属一组,虽然元素看起来都不完全相同.变换成一致的外观</p>
<p>重复，将各个部分连在一起，从而同意并增强整个作品，否者这些部分只是彼此孤立的单元</p>
<p>根本目的：</p>
<pre><code>统一，并增强视觉效果
</code></pre>
<p>如何实现</p>
<pre><code>可以认为是保持一致性：需要把现有的一致性更向前推进一步
</code></pre>
<p>要避免的问题</p>
<pre><code>避免太多地重复一个元素，重复太多会让人讨厌.要注意对比的价值
</code></pre>
<h4 id="4对比">4.对比</h4>
<pre><code>如果两个项不完全相同，就应当使之不同，而且应当是截然不同

要想实现有效地对比，对比就必须强烈！千万不要畏畏缩缩
</code></pre>
<p>设计页面很少只是用某一种原则</p>
<p>在页面上增加对比能吸引眼球，我们的眼睛喜欢看到对比的事物</p>
<p>根本目的</p>
<pre><code>增强页面效果：更有意思，更好的可读性
有效于信息的组织
</code></pre>
<p>如何实现</p>
<pre><code>最简单方法是实现字体对比，也可以利用线宽、颜色、大小、空间、元素之间间隔、材质等形成对比
重要的是：对比一定要强烈
</code></pre>
<p>避免的问题</p>
<pre><code>不要犹豫，如果想形成对比，就加大力度
</code></pre>
<p>提高视觉敏感度</p>
<p>从好的设计中获得理念，寻找灵感</p>
<h4 id="5设计过程">5.设计过程</h4>
<pre><code>1.从中心点开始
确定希望读者最新看什么。除非你已经决定要建立一个非常协调的设计，否则就应该创建一个有强烈对比的中心店

2.将信息分组
按逻辑进行你分组，确定这些组之间的关系。通过组之间靠近与否来显示这些关系
在页面上组织文本和图片时，要建立并维护明确的对齐
创建重复，或者找出可以重复的项，建立重复
除非你已经决定要建立一个非常协调的设计，否则就应该创建强烈的对比
</code></pre>
<hr>
<p>书中还有关于颜色选择、字体设计的部分章节，需要可以自查</p>
<p>总的来说，很好的一本书，推荐</p>
<p>wklken</p>
<p>2014-03-02 于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>Golang笔记-05-函数</title>
			<link>https://wklken.me/posts/2014/03/02/05-func.html</link>
			<pubDate>Sun, 02 Mar 2014 05:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/03/02/05-func.html</guid>
			<description>函数 Go语言里面的核心设计，通过关键字func来声明 func funcName(input type1, input2 type2) (output1 type1, output2 type2) { //logical code return value1, value2 } 基本语法 1.语法 //一般函数 func func_name(a int) { println(a) } //多参数，无返</description>
			<content type="html"><![CDATA[<h3 id="函数">函数</h3>
<p>Go语言里面的核心设计，通过关键字func来声明</p>
<pre><code>func funcName(input type1, input2 type2) (output1 type1, output2 type2) {
    //logical code
    return value1, value2
}
</code></pre>
<h3 id="基本语法">基本语法</h3>
<p>1.语法</p>
<pre><code>//一般函数
func func_name(a int) {
    println(a)
}

//多参数，无返回值
func func_name(a, b int, c string) {
    println(a, b, c)
}

//单个返回值
func func_name(a, b int) int { //同类型，可以省略  a, b int
    return a + b
}

//多个返回值
func func_name(a, b int) (c int, err error) {  //返回值还可以是   (int, error)
    return a+b, nil
}

func SumAndProduct(A, B int) (int, int) {
    return A+B, A*B
}
</code></pre>
<p>2.说明：</p>
<pre><code>关键字func声明
可以有一个或多个参数，每个参数后面带有类型,通过&quot;,&quot;分隔函数可以返回多个值
返回值声明，可以只声明类型
    如果没有返回值，可以省略最后的返回信息
    如果有返回值，必须在外层添加return


Go函数不支持嵌套(nested),重载（overload）和默认参数(default parameters)
支持：
    1.无需声明原型
    2.不定长度变参
    3.多返回值
    4.命名返回值参数
    5.匿名函数
    6.闭包

注意：
    函数使用func开头，左大括号不能另起一行
</code></pre>
<p>小写字母开头的函数指在本包内可见，大写字母开头的函数才能被其他包调用</p>
<h3 id="多返回值及命名返回参数">多返回值及命名返回参数</h3>
<p>可以像python那样返回多个结果，只是非tuple</p>
<p>对于不想要的返回值，可以扔垃圾桶_</p>
<p>如果用命名返回参数，return语句可以为空。return 不为空，返回值顺序是return的顺序而非在函数头声明的顺序</p>
<pre><code>package main

func change(a, b int) (x, y int) {
    x = a + 100
    y = b + 100

    return   //101, 102
    //return x, y  //同上
    //return y, x  //102, 101
}

func main(){
    a := 1
    b := 2
    c, d := change(a, b)
    println(c, d)
</code></pre>
<p>如果命名返回参数被代码块中的同名变量覆盖了，就必须使用显式return返回结果</p>
<p>不需要强制命名返回值，但是命名后的返回值可以让代码更加清晰，可读性更强</p>
<h3 id="参数传递传值与传指针">参数传递:传值与传指针</h3>
<p>指针, Go保留指针，用&quot;.&ldquo;而非&rdquo;-&gt;&ldquo;操作指针目标对象成员
操作符</p>
<pre><code>&amp; 取变量地址
* 通过指针间接访问目标函数

func add1(a int) int {
    a = a + 1
    return a
}

x := 3
x1 := add1(x)
x //3
x1 //4
传值，x1的值没有改变

func add2(a *int) int {
    *a = *a + 1
    return *a
}
x := 3
x1 := add2(&amp;x)
x // 4
x1 // 4
</code></pre>
<p>传指针多个函数能操作同一个对象</p>
<p>传指针比较轻量级(8byte)，只是传内存地址，我饿们可以用指针来传递体积大的结构体</p>
<p>Go语言中，string,slice,map这三种类型的实现机制类似指针，所以可以直接传递，而不用取地址后传指针</p>
<p>注意，若函数需要改变 slice长度，仍需要取地址传指针</p>
<h3 id="参数传递可变参数">参数传递:可变参数</h3>
<p>变参本质上就是一个slice，且必须是最后一个形参</p>
<p>将slice传递给变参函数时，注意用…展开，否则会被当做dang单个参数处理，和python类似</p>
<pre><code>package main

func sum(s string, args ...int)  {
    var x int
    for _, n := range args {
        x += n
    }
    println(s, x)
}
func main(){
   sum(&quot;1+2+3=&quot;, 1, 2, 3)

   x := []int{0,1,2,3,4}
   sum(&quot;0+1+2+3=&quot;, x[:4]...)
}

...type类型只能作为函数的参数类型存在，并且是最后一个参数
本质上是一个数组切片，即[]type
</code></pre>
<p>任意类型的不定参数</p>
<pre><code>func Printf(format string, args ...interface{}) {
}
</code></pre>
<h3 id="匿名函数">匿名函数</h3>
<pre><code>f := func(x,y int) int {
    return x + y
}
</code></pre>
<h3 id="函数作为值类型">函数作为值、类型</h3>
<p>在Go语言中，函数也是一种变量，可以通过type来定义它,它的类型就是所有拥有相同的参数，相同的返回值</p>
<pre><code>语法
type typeName func (input1 inputType1, input2 inputType2 [, ....]) (result1 resultType1 [,....])
</code></pre>
<p>用法e.g.1</p>
<pre><code>type testInt func(int) bool //声明了一个函数类型

func filter(slice []int, f testInt) []int {
    var result []int
    for _, value := range slice {
        if f(value) {
            result = append(result, value)
        }
    }
}

func isOdd(integer int) bool {
    if integer % 2 == 0 {
        return false
    }
    return true
}

filter(a, isOdd)
</code></pre>
<p>这种用法，在写接口的时候非常有用</p>
<p>用法e.g.2</p>
<p>可以定义函数类型，也可以将函数作为值进行传递(默认值nil)</p>
<pre><code>package main

//定义函数类型callback
type callback func(s string)

//定义一个函数，可以接收另一个函数作为参数
// sum为参数名称， func(int, int) int为参数类型
func test(a, b int, sum func(int, int) int)  {
    println( sum(a,b) )
}

func main(){
    //演示1
    var cb callback
    cb = func(s string) {
        println(s)
    }
    cb(&quot;hello world&quot;)

    //演示2
    test(1, 2, func(a, b int) int {return a + b})
}
</code></pre>
<p>结果：</p>
<pre><code>hello world
3
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Golang笔记-04-array/slice/map</title>
			<link>https://wklken.me/posts/2014/03/02/04-data-structure.html</link>
			<pubDate>Sun, 02 Mar 2014 04:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/03/02/04-data-structure.html</guid>
			<description>一.Array 在Go语言中，数组是一个值类型(value type) 所有的值类型变量在赋值和作为参数传递时都将产生一个复制动作 如果作为函数的参数类型，</description>
			<content type="html"><![CDATA[<h3 id="一array">一.Array</h3>
<p>在Go语言中，数组是一个值类型(value type)</p>
<p>所有的值类型变量在赋值和作为参数传递时都将产生一个复制动作</p>
<p>如果作为函数的参数类型，则在函数调用时参数发生数据复制，在函数体中无法修改传入数组的内容</p>
<p>数组相等用 = != 比较，不能用 &lt; &gt;</p>
<h4 id="1声明赋值">1.声明&amp;赋值</h4>
<p>初始化</p>
<pre><code>语法
var VarName [n]type     // n&gt;=0

e.g.
var a [5]int //[0 0 0 0 0]
var c [2][3]int //二维

var b int = [5]int{1,2,3,4,5} //声明并初始化

a := [3]int{1,2,3}
b := [10]int{1,2,3} //前三个元素，其他为0
c := [20]int{19:1} //第20个元素初始化为1，其他默认0
d := [...]int{4,5,6} //自动计算长度
e := [...]int{0:1, 1:2, 19:3} //自动推断


二维数组
doubleArray := [2][4]int{[4]int{1,2,3,4}, [4]int{5,6,7,8}}
easyArray := [2][4]int{{1,2,3,4}, {1,2,3,4}}
多维 [...][n] 前者可推断，但是后者必须显示赋值
</code></pre>
<p>数组的长度是该数组类型的一个内置常量</p>
<pre><code>arrLength := len(arr)
</code></pre>
<p>注意，数组长度也是类型的一部分，因此不同长度数组为不同类型(内置常量)</p>
<p>即[3]int和[4]int是不同类型，并且数组不能改变长度</p>
<p>数组之间的赋值是值的赋值，即当把一个数组作为参数传入函数的时候，传入的其实是该数组的副本(一次复制操作)，而不是它的指针，如果要传入指针，使用slice</p>
<h4 id="2元素访问">2.元素访问</h4>
<pre><code>for i:=0; i &lt; len(array); i++ {
    fmt.Println(i, array[i])
}

for i, v := range array {
    fmt.Println(i, v)
}
</code></pre>
<p>可以用new创建数组</p>
<pre><code>p := new([10]int)
返回一个指向数组的指针
</code></pre>
<p>注意区分</p>
<pre><code>指向数组的指针
a := [100]int{}
var p *[100]int = &amp;a

指针数组
x, y = 1, 2
a := [...]*int{&amp;x, &amp;y}
</code></pre>
<h3 id="二slice">二.Slice</h3>
<p>数组切片就像一个指向数组的指针，但更复杂，实际上它拥有自己的数据结构，而不仅仅是指针(指向原生数组的指针 + 数组切片中元素个数 + 数组切片已分配的存储空间)</p>
<p>一个引用类型，总是指向一个底层array，声明可以向array一样，只是不需要长度</p>
<p>slice就像一个结构体，包含三个元素</p>
<pre><code>一个指针，指向数组中slice指定的开始位置
长度，即slice的长度
最大长度，也就是slice开始位置到数组的最后位置的长度
</code></pre>
<h4 id="1声明赋值-1">1.声明&amp;赋值</h4>
<p>通过array创建</p>
<pre><code>var myArray [10]int = [10]int{1,2,3,4,5,6,7,8,9,10}
var mySlice []int = myArray[:5]

a := [5]int{1,2,3,4,5}
b := a[2:4]
b := a[:4]
b := a[2:]

从数组或已存在的slice再次声明
var ar [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}

var a, b []byte
a = ar[2:5]
b = ar[3:5]
</code></pre>
<p>直接创建</p>
<pre><code>myslice1 := make([]int, 5)
myslice2 := make([]int, 5, 10) //初始个数5，预留10个元素的存储空间
myslice3 := []int{1,2,3,4,5}
</code></pre>
<h4 id="2元素访问-1">2.元素访问</h4>
<pre><code>for i:=0; i&lt;len(mySlice); i++ {
    fmt.Println(i, mySlice[i])
}

for i, v := range mySlice {
    fmt.Println(i, v)
}
</code></pre>
<h4 id="3其他操作">3.其他操作</h4>
<p>大小和容量</p>
<pre><code>len获取slice的长度
cap获取slice的最大容量
</code></pre>
<p>动态增减元素</p>
<pre><code>append想slice里面追加一个或者多个元素，然后返回一个和slice一样类型的slice

//append
mySlice = append(mySlice, 1, 2, 3) //增加三个元素
mySlice = append(mySlice, mySlice2) //增加另一个

注意，append会改变slice所引用的数组的内容，从而影响到引用统一数组的其他slice，
     但当slice中没有剩余空间，此时动态分配新的数组空间返回的slice数组指针将指向这个空间，
     而原数组的内容将保持不变，其他引用此数组的slice不受影响(坑，可能引入bug)
</code></pre>
<p>内容复制</p>
<pre><code>copy，从源slice的src中复制到目标dst，并且返回复制元素的个数
copy(dst, source) //会按短的个数复制

slice1 := []int{1,2,3,4,5}
slice2 := []int{5,4,3}

copy(slice2, slice1) //复制slice1前三个 1 -&gt; 2
copy(slice1, slice2) //复制slice2的前三个 2 -&gt; 1
</code></pre>
<p>切片</p>
<pre><code>默认开始位置0，ar[:n]等价于ar[0:n]
第二个序列默认是数组长度 ar[n:] 等价于 ar[n:len(ar)]
从一个数组直接获取slice，可以是ar[:]
</code></pre>
<p>slice是引用类型，所以当改变其中元素的时候，其他的所有引用都会改变</p>
<pre><code>aSlice = array[3:7]
bslice = aSlice[:3]
</code></pre>
<h3 id="三map">三.Map</h3>
<p>Python中字典的概念</p>
<p>map是无序的,长度不固定，内置的len可以用于map,可以方便的修改</p>
<h4 id="1声明赋值-2">1.声明&amp;赋值</h4>
<pre><code>map[keyType]valueType

var m map[string] PersonInfo
m = make(map[string] personInfo[, 100])

var numbers map[string]int
or
numbers := make(map[string]int)
numbers[&quot;one&quot;] = 1
</code></pre>
<p>初始化一个字典</p>
<h4 id="2元素访问-2">2.元素访问</h4>
<pre><code>rating := map[string]float32 {&quot;c&quot;:5, &quot;Go&quot;:4.5}

csharpRating, ok := rating[&quot;C#&quot;]
if ok {
    fmt.Println(&quot;get the value&quot;)
} else{
    fmt.Println(&quot;error&quot;)
}
</code></pre>
<h4 id="3基本操作">3.基本操作</h4>
<p>赋值</p>
<pre><code>m[&quot;1234&quot;] = PersonInfo{}
</code></pre>
<p>删除</p>
<pre><code>delete(m, &quot;1234&quot;)
</code></pre>
<h3 id="四其他">四.其他</h3>
<p>make和new操作</p>
<pre><code>make用于内建类型(map,slice,channel) 的内存分配。

new用于各种类型的内存分配
</code></pre>
<p>new本质上和其他语言中同名函数一样, new(T)分配了零值填充的T类型的内存空间，并返回其地址，即一个*T类型的值
即，返回一个指针，指向新分配的类型T的零值</p>
<p>make(T, args)，只能创建slice,map,channel，并返回一个有初始值（非零值）的T类型，而不是*T。
本质来讲，导致这三个类型有所不同的原因是，指向数据结构的引用在使用前必须被初始化</p>
]]></content>
		</item>
		
		<item>
			<title>Golang笔记-03-控制流</title>
			<link>https://wklken.me/posts/2014/03/02/03-control-flow.html</link>
			<pubDate>Sun, 02 Mar 2014 03:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/03/02/03-control-flow.html</guid>
			<description>控制结构分为： 条件+选择+循环 IF 1.说明 条件表达式没有括号 支持一个初始化表达式（可以是多变量初始化语句） 初始化语句中定义的都是只能在bloc</description>
			<content type="html"><![CDATA[<p>控制结构分为： 条件+选择+循环</p>
<h3 id="if">IF</h3>
<p>1.说明</p>
<pre><code>条件表达式没有括号
支持一个初始化表达式（可以是多变量初始化语句）
初始化语句中定义的都是只能在block级别中使用的局部变量，不能在block之外使用
左大括号必须和条件语句在同一行(必须与if/else在同一行)
go没有三元运算符

if判断语句条件不需要括号
在判断语句里卖弄允许声明一个变量，其作用域只在逻辑块内，其他地方不起作用
花括号一定存在，且必须与if/else在同一行
</code></pre>
<p>2.语法</p>
<pre><code>//基本
if a &gt; 0 {  //无括号
     dosomething()
} else if a == 0 { //必须用花括号
     doothertings()
} else {
     donothing()
}

//单行模式
if a &gt; 0 { a += 100 } else { a -= 100 }
</code></pre>
<p>3.示例</p>
<pre><code>package main

func main(){
    a := 10

    if a &gt; 0 {
        a += 100
    } else if a == 0 {
        a = 0
    } else {
        a -= 100
    }
    println(a)

    b := 101
    if b &gt; 0 { b += 100 } else { b -= 100 }
    println(b)
}

//支持一个初始化语句
if a:=1; a&lt;10 { //允许在条件之前执行一个简单语句，由此语句定义的变量作用域仅在if/else范围内
    return a
}
if a, b := 1,2; a+b == 10 {
}

if x := computedValue(); x &gt; 10 {
} else {
}
</code></pre>
<p>4.结果</p>
<pre><code>110
201
</code></pre>
<p>注意，在有返回值的函数中，不允许将“最终的”return语句放到if &hellip; else &hellip; 结构中，否则编译失败</p>
<pre><code>func example(x int) int {
    if x == 0 {
        return 5
    } else {
        return x
    }
}
</code></pre>
<h3 id="for">FOR</h3>
<p>for是go的&quot;while&quot;, 只支持for关键字.有三种形式</p>
<p>1.语法</p>
<pre><code>for init; condition; post {
    //init不支持逗号，只能平行赋值
    //condition每次循环开始都会检查，不建议在里面使用函数，建议用计算好的变量/常量代替
    //post后面必须跟花括号,每轮循环结束的时候调用
}

for i:=0; i&lt;10; i++ {
}

-----------------------------

for condition {
    dosomething()
}

i:=1
for i&lt;10 {
}

-----------------------------

for { //无限循环
    dosomething()
}

for {
    a++
    if a &gt; 10 {
        break
    }
}

-----------------------------
</code></pre>
<p>2.说明</p>
<pre><code>a. 初始化和步进表达式可以使多个值：必须使用平行赋值
    i, j:=0,len(a)-1; i&lt;j; i,j=i+1,j-1
b.每次循环都会重新检查条件表达式
</code></pre>
<p>3.示例</p>
<pre><code>package main

func main(){
    ss := &quot;abcd&quot;
    for i:=0; i&lt;len(ss); i++ {
        println(ss[i])
    }
}
</code></pre>
<p>得到：</p>
<pre><code>97
98
99
100
</code></pre>
<p>4.配合range</p>
<p>for循环和保留字range一起使用，完成迭代器iterator操作</p>
<p>string, array, slice, map, channel都可以用range进行迭代器操作</p>
<p>range返回序号和值，若是不想要，可以用_</p>
<pre><code>  for i, c := range &quot;abc&quot; {
      fmt.Printf(&quot;s[%d] = %c\n&quot;, i, c)
  }
</code></pre>
<p>得到</p>
<pre><code>  s[0] = a
  s[1] = b
  s[2] = c
</code></pre>
<h3 id="switch">SWITCH</h3>
<p>python里面没有</p>
<p>1.语法</p>
<p>支持初始化表达式</p>
<pre><code>switch a:=5; a { //默认break，匹配成功后不会自动向下执行其他case,而是跳出整个switch
    case 0:         //普通
        println(a)
    case 1, 2:      //多个分支，逗号分隔
        println(a)
    case 100:       //什么都不做
    case 5:
        println(a)
        fallthrough   //进入后面的case 进行处理，而不是跳出block
    default:
        println(a)    //默认
}
注意，不需要break来明确退出一个case，一旦条件符合，自动终止,除非使用fallthough

--------------

可以不带表达式
switch sExpr {
        case expr1: //sExpr和expr1必须类型一致,不限制为常量或者证书，可以用任何类型或表达式
            ...
}

switch {
    case 0 &lt;= Num &amp;&amp; Num &lt;= 3:
        fmt.Printf(&quot;3&quot;)
}
</code></pre>
<p>几种形式:</p>
<pre><code>a := 1
switch a {
    case 0:
        ...
}

a := 1
switch {
    case a&gt;=0:
        ....
    case a&gt;1:
        ....
}

switch a:=1; {
    case a&gt;=0:
        ...
    case a&gt;1:
        ...
}
</code></pre>
<p>2.替代if&hellip;else if…else…</p>
<p>不指定switch 条件表达式(没有条件表达式，在case语句中有)，或者直接为true</p>
<pre><code>  a := 5
  switch {     //两句可合并位 switch a:=5; {
      case a &gt; 1:
          println(&quot;a&quot;)
      case a &gt; 2:
          println(&quot;b&quot;)
      default:
          println(&quot;c&quot;)
  }
</code></pre>
<h3 id="goto-break-continue">goto break continue</h3>
<p>均可配合标签使用(标签区分大小写，若声明了没有使用会导致编译错误)</p>
<pre><code>break/continue可配合标签用于多重循环跳出
goto是调整执行位置，与其他两个执行结果并不相同
</code></pre>
<p>1.goto</p>
<pre><code>支持函数内部goto跳转

请善用

必须跳转到当前函数内定义的标签,标签名大小写敏感

func myFunc() {
    i := 0
Here:
    println(i)
    i++
    goto Here
}
</code></pre>
<p>2.continue</p>
<pre><code>进入下一次循环
</code></pre>
<p>3.break</p>
<pre><code>终止循环

for index := 10; index &gt; 0; index -- {
    if index == 5 {
        break //continue
    }
}
</code></pre>
<p>示例</p>
<pre><code>package main
func main(){
    a := 1
    LABEL1:
        println(&quot;inc a=&quot;, a)
        a += 1

    LABEL2:
        //println(&quot;here&quot;)

    for ; a &lt; 6; {
        println(a)
        if a == 3 {
            a += 1
            continue LABEL2
        }
        if a == 5 {
            break
        }
        goto LABEL1
    }
}
</code></pre>
<p>结果:</p>
<pre><code>inc a= 1
2
inc a= 2
3
4
inc a= 4
5
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Golang笔记-02-类型、变量、常量</title>
			<link>https://wklken.me/posts/2014/03/02/02-type-var-const.html</link>
			<pubDate>Sun, 02 Mar 2014 02:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/03/02/02-type-var-const.html</guid>
			<description>基本类型 1.基本类型列表 类型 长度 说明 bool 1 true/false,默认false, 不能把非0值当做true(不用数字代表true/false) byte</description>
			<content type="html"><![CDATA[<h3 id="基本类型">基本类型</h3>
<p>1.基本类型列表</p>
<pre><code>类型        长度     说明
bool         1      true/false,默认false, 不能把非0值当做true(不用数字代表true/false)
byte         1      uint8 别名
rune         4      int32别名。 代表一个unicode code point
int/unit            一来所运行的平台，32bit/64bit
int8/uint8   1     -128 ~ 127; 0 ~ 255
int16/uint16 2     -32768 ~ 32767; 0 ~ 65535
int32/uint32 4     -21亿 ~ 21亿， 0 ~ 42亿
int64/uint64 8

float32      4     精确到7位小数,相当于c的float
float64      8     精确到15位小数,相当于c的double

complex64    8
complex128   16

uintptr            足够保存指针的32位、64位整数,指针(可以存指针的整数型)
array              值类型,数组
struct             值类型,结构体
string             值类型,字符串类型，常用
slice              引用类型,切片
map                引用类型,字典
channel            引用类型,通道
interface          接口类型,接口
function           函数类型,函数
</code></pre>
<p>2.类型转换</p>
<p>不支持隐式类型转换，必须进行显式类型转换</p>
<p>转换只发生在两种互相兼容的类型之间: 各类int不允许相互赋值或操作，不然会在编译时报错</p>
<pre><code>&lt;type&gt;(expression)
</code></pre>
<p>示例</p>
<pre><code>package main
import &quot;fmt&quot;

func main(){
    a := 0x1234
    b := 1234.56
    c := 256

    fmt.Printf(&quot;%x\n&quot;, uint8(a))
    fmt.Printf(&quot;%d\n&quot;, int(b))
    fmt.Printf(&quot;%f\n&quot;, float64(c))
}
</code></pre>
<p>结果</p>
<pre><code>34
1234
256.000000
</code></pre>
<p>3.类型别名</p>
<pre><code>type t_str string
var b t_str = &quot;a str&quot;
</code></pre>
<p>4.类型默认值</p>
<p>声明不赋值，类型零值，非空值,而是声明后的默认值</p>
<pre><code>bool: false
integers: 0
floats: 0.0
string: &quot;&quot;
pointers,functions,interfaces,slices,channels,maps: nil
</code></pre>
<h3 id="保留字">保留字</h3>
<pre><code>break      case   chan     const        continue
default    defer  else     fallthrough  for
func       go     goto     if           import
interface  map    package  range        return
select     struct switch   type         var
</code></pre>
<h3 id="变量">变量</h3>
<p>1.变量声明</p>
<pre><code>//第一种，指定变量类型，声明后若不赋值，使用默认值
var v_name v_type
v_name = value

//第二种，根据值自行判定变量类型
var v_name = value

//第三种，省略var, 注意 :=左侧的变量不应该是已经声明过的，否则会导致编译错误.
v_name := value

e.g.
var a int = 10
var b = 10
c : = 10
</code></pre>
<p>示例：</p>
<pre><code>package main
var a = 1234
var b string = &quot;hello&quot;
var c bool

func main(){
    println(a, b, c)
}
</code></pre>
<p>结果：</p>
<pre><code>1234 hello false
</code></pre>
<p>2.多变量声明：</p>
<pre><code>//类型相同多个变量, 非全局变量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3

var vname1, vname2, vname3 = v1, v2, v3 //和python很像,不需要显示声明类型，自动推断

vname1, vname2, vname3 := v1, v2, v3 //出现在:=左侧的变量不应该是已经被声明过的，否则会导致编译错误


//类型不同多个变量, 全局变量, 局部变量不能使用这种方式
var (
    vname1 v_type1
    vname2 v_type2
)
</code></pre>
<p>示例：</p>
<pre><code>package main

var x, y int
var (  //这种只能出现在全局变量中，函数体内不支持
    a int
    b bool
)

var c, d int = 1, 2
var e, f = 123, &quot;hello&quot;

//这种不带声明格式的只能在函数体中出现
//g, h := 123, &quot;hello&quot;

func main(){
    g, h := 123, &quot;hello&quot;
    println(x, y, a, b, c, d, e, f, g, h)
}
</code></pre>
<p>结果：</p>
<pre><code>0 0 0 false 1 2 123 hello 123 hello
</code></pre>
<p>注意：</p>
<pre><code>A.多变量赋值时，将先行计算所有左侧变量的值，再进行赋值

    i := 0
    i, l[i] = 1, 2
    //get i = 1， l[0] = 2


    sc[0], sc[0] = 1, 2
    //get sc[0] = 2

B.垃圾桶_

    func test()(int, string) {
        return 123, &quot;abc&quot;
    }

    a, _ := test()

C.已声明但是没有使用的变量会在编译阶段报错，较Python 更为严格
</code></pre>
<h3 id="常量">常量</h3>
<p>常量可以是字符，字符串，布尔或数字</p>
<p>常量赋值是编译期的行为</p>
<p>1.常量声明</p>
<pre><code>在编译阶段就能确定下来的值,在运行时无法改变该值
常量可以定义为数值、布尔值或字符串等类型

const constantName = value
const Pi float32 = 3.1415926

const c_name [type] = value
const c_name1, c_name2 = value1, value2
const (
    c_name1 = vluae1
    c_name2 = value2
)

=右侧，必须为常量或常量表达式，如果使用到了函数，必须为内置函数（编译期行为）

const i = 10000
</code></pre>
<p>说明：</p>
<pre><code>A.常量必须是编译期能确定的Number(char/integer/float/complex)、String和bool

B.在定义常量数组时，如果不提供初始化值，则表示与上行常量类型，值，完全相同

    const (
        a = &quot;abc&quot;
        b
    )
    //则 b = &quot;abc&quot;

C.常量可以用len(), cap(), unsafe.Sizeof()常量计算表达式的值.  常量表达式中，函数必须是内置函数，否则编译不过

    package main

    import &quot;unsafe&quot;
    const (
        a = &quot;abc&quot;
        b = len(a)
        c = unsafe.Sizeof(a)
    )

    func main(){
        println(a, b, c)
    }


结果：    abc 3 16
</code></pre>
<p>###枚举</p>
<p>iota，特殊常量，可以认为是一个可以被编译器修改的常量</p>
<p>在每一个const关键字出现时，被重置为0，然后再下一个const出现之前，每出现一次iota，其所代表的数字会自动增加1</p>
<p>不提供初始值，则表示使用上一行的表达式</p>
<p>1.声明：</p>
<p>iota生成从0开始的自动增长枚举值，意味着，多一个枚举值，iota+=1，无论是否使用</p>
<p>基本语法</p>
<pre><code>const (
    a = 1
    b = 2
)

const (
    a = iota //0
    b  //1
    c  //2
)

const (
    _ = iota
    a    //1
    b    //2
)
</code></pre>
<p>iota用法</p>
<pre><code>func main() {
    const (
            a = iota  //0
            b   //1
            c   //2
            d = &quot;ha&quot;  //独立值，iota += 1
            e    //&quot;ha&quot;   iota += 1
            f = 100    //iota +=1
            g     //100  iota +=1
            h = iota  //7,恢复计数
            i      //8
    )

}

const (
    x = iota // 0
    y = iota // 1
    z = iota // 2
    w //省略，默认和前面一样字面值   w = iota, 即3
)
const v = iota //遇到const关键字，iota重置
</code></pre>
<p>注意: 每行的变量数必须一致
const (
A, B = iota, iota
C, D
E, F
)</p>
<pre><code>func main() {
    println(A,B,C,D,E,F)
}

//结果： 0 0 1 1 2 2   【各自增长】
</code></pre>
<h3 id="运算符">运算符</h3>
<p>Go运算符全部是从左到右结合的</p>
<p>不支持运算符重载</p>
<pre><code>优先级    运算符                        说明
  高   * / % &lt;&lt; &gt;&gt; &amp; &amp;^(AND NOT)
       + - ! ^
       == != &lt; &lt;= &gt; &gt;=
       &lt;-                             channel运算符
       &amp;&amp;
  低   ||
</code></pre>
<p>在go中，++ &ndash;为语句，而非表达式</p>
<pre><code>package main

func main(){
    i := 1
    i ++
    println(i)

    b := i
    println(b)

    //syntax error: unexpected ++, expecting semicolon or newline or }
    //c := i++
    //意味着, ++/--不能出现在等号右侧
}
</code></pre>
<h3 id="指针">指针</h3>
<p>Go保留了指针， *T表示T对应的指针类型</p>
<p>如果包含包名， 则应该是 *<package>.T</p>
<p>代表指针类型的符号 &lsquo;*&rsquo; 总是和类型放在一起，而不是紧挨着变量名</p>
<p>同样支持指针的指针**T</p>
<p>1.声明</p>
<pre><code>var a, b *int
</code></pre>
<p>2.说明</p>
<pre><code>操作符&amp;取变量地址，用*透过指针变量间接访问目标对象
默认值是nil,没有NULL常量
不支持指针运算，不支持‘-&gt;'预算福，直接'.'选择符操作指针目标对象成员
可以在unsafe.Pointer和任意类型指针间进行转换
可以将unsafe.Pointer转换为uintptr,然后变相做指针运算，uintptr可以转换为整数
</code></pre>
<p>3.示例</p>
<pre><code>package main
import &quot;fmt&quot;

type User struct {
    Id int
    Name string
}
func main(){
    i := 100
    var p *int = &amp;i  //取地址

    println(*p)   //取值


    up := &amp;User{1, &quot;Jack&quot;}
    up.Id = 100  //直接取只针对想成员
    fmt.Println(up)

    u2 := *up  //拷贝对象
    u2.Name = &quot;Tom&quot;
    fmt.Println(up, u2)
}
</code></pre>
<p>4.结果：</p>
<pre><code>100
&amp;{100 Jack}
&amp;{100 Jack} {100 Tom}
</code></pre>
<h3 id="分组声明">分组声明</h3>
<pre><code>import (
    &quot;fmt&quot;
    &quot;os&quot;
)

const (
    i = 100  //首行必须有常量表达式
    pi = 3.1415
)

var (  //全局变量可用，函数体内不支持
    i int
    pi float32
)
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Golang笔记-01-简介</title>
			<link>https://wklken.me/posts/2014/03/02/01-intro.html</link>
			<pubDate>Sun, 02 Mar 2014 01:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/03/02/01-intro.html</guid>
			<description>Go语言最主要的特性 自动垃圾回收 更丰富的内置类型 函数多返回值 错误处理 匿名函数和闭包 类型和接口 并发编程 反射 语言交互性 高性能/高效开发 安装 安装说</description>
			<content type="html"><![CDATA[<h3 id="go语言最主要的特性">Go语言最主要的特性</h3>
<pre><code>自动垃圾回收
更丰富的内置类型
函数多返回值
错误处理
匿名函数和闭包
类型和接口
并发编程
反射
语言交互性

高性能/高效开发
</code></pre>
<h3 id="安装">安装</h3>
<p>安装说明 <a href="http://golang.org/doc/install">地址</a></p>
<p>包下载 <a href="https://code.google.com/p/go/downloads/list">地址</a></p>
<p>确认是否安装成功</p>
<pre><code>go version //查看版本
</code></pre>
<h3 id="环境变量设置">环境变量设置</h3>
<h3 id="整体目录结构">整体目录结构</h3>
<p>通过package组织，只有package名称为main的可以包含main函数</p>
<p>一个程序有且仅有一个main包</p>
<p>通过import 关键字导入其他非main包</p>
<pre><code>bin/
    |- mathapp
pkg/
    |- 平台名
        |- xxx.a
src/
    |- mathapp
        |- main.go
</code></pre>
<h3 id="helloworld">Helloworld</h3>
<pre><code>package main //声明文件的package

import {
    &quot;fmt&quot; //import 包，不能包含没有用到的包，否则而编译错误
}
func main() { //入口函数, 无参数无返回值
    fmt.Println(&quot;hello world&quot;)
}

//运行
$go run hello.go

$go build hello.go
$./hello
</code></pre>
<h3 id="go命令">go命令</h3>
<p>用命令行查看</p>
<pre><code>go help

go build 编译
go clean 移除当前源码包里面的编译生成文件
go fmt 格式化代码
go get 动态获取远程代码包
go install 生成结果文件，并将编译好的结果一到$GOPATH/pkg或者$GOPATH/bin
go test 运行测试用的可执行文件
go doc   godoc -http=:8080 查看文档

go fix 修复以前老版本代码到新版本
go version查看当前版本
go env 查看当前go的环境变量
go list 列出当前所有安装package
go run 编译并运行go语言程序
</code></pre>
<h3 id="调试">调试</h3>
<p>使用gdb进行调试, go语言内部已经内置了</p>
<pre><code>list
break
delete
backtrace
info
print
whatis
next
continue
set variable
</code></pre>
<h3 id="编辑器设置">编辑器设置</h3>
<p>vim</p>
<h2 id="其他补充">其他补充</h2>
<p>注释</p>
<pre><code>//单行
/* ----- */ 多行
</code></pre>
<p>import 多个包</p>
<pre><code>import (
    &quot;fmt&quot;
    &quot;os&quot;
)
</code></pre>
<p>调用包里地函数</p>
<pre><code>&lt;packageName&gt;.&lt;Function&gt;
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Golang笔记-00-说明及资源</title>
			<link>https://wklken.me/posts/2014/02/23/golang-base-intro.html</link>
			<pubDate>Sun, 23 Feb 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/02/23/golang-base-intro.html</guid>
			<description>这份笔记是在学习golang过程中，汇集几本书以及网络视频、博客等资料汇总而成的，记录，方便查询 已经基本完成，逐步发上来 涉及内容 说明-资源 done</description>
			<content type="html"><![CDATA[<p><img src="/imgs/golang/golang.png" alt="golang"></p>
<p>这份笔记是在学习golang过程中，汇集几本书以及网络视频、博客等资料汇总而成的，记录，方便查询</p>
<p>已经基本完成，逐步发上来</p>
<h4 id="涉及内容">涉及内容</h4>
<pre><code>说明-资源 done
基础知识 done
类型 - 常量 - 变量 - 运算符 done
控制流 done
数据结构：数组，切片，map done
函数 done
结构struct
接口
字符串
文件
异常处理
数学计算
并发
反射
正则
数据库
时间日期
配置文件
日志logging
json/xml文件处理
测试
项目及项目结构
</code></pre>
<h4 id="资源">资源</h4>
<p>书籍：
<a href="https://github.com/astaxie/build-web-application-with-golang">Go web编程</a>|
<a href="https://github.com/Unknwon/the-way-to-go_ZH_CN">Go 入门指南(The Way to Go)</a>
<a href="https://github.com/astaxie/Go-in-Action">Go 实战开发</a></p>
<p>视频:
<a href="https://github.com/Unknwon/go-fundamental-programming">Go编程基础</a>|
<a href="https://github.com/Unknwon/go-web-foundation">Go Web基础</a>|
<a href="https://github.com/Unknwon/go-rock-libraries-showcases">Go名库讲解</a></p>
<p>网站：
论坛<a href="http://golangtc.com/">Golang中国</a>|
博客<a href="http://blog.go-china.org/">Golang中国</a>|
<a href="https://gobyexample.com/">Go by Example</a></p>
<p>工具网站:
API速查 <a href="https://gowalker.org/">gowalker</a>|
在线编译 <a href="http://gobuild.io/">gobuild</a></p>
<p>web框架:
<a href="https://github.com/astaxie/beego">beego</a>|
<a href="https://github.com/hoisie/web">web.go-类web.py</a>|
<a href="https://github.com/codegangsta/martini">Martini-类flask</a>|
<a href="https://github.com/robfig/revel">revel</a>|
<a href="https://github.com/hoisie/web">web</a></p>
<p>To be continue&hellip;..</p>
]]></content>
		</item>
		
		<item>
			<title>读书笔记——追随你的心，用思想改变世界</title>
			<link>https://wklken.me/posts/2014/01/19/follow-your-heart.html</link>
			<pubDate>Sun, 19 Jan 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/01/19/follow-your-heart.html</guid>
			<description>乔布斯的十大演讲 很喜欢封面的设计，这本书的纸质也很赞，摸起来蛮有质感的，书买了应该有两年了，一直带着，但是没怎么看 今天又重新翻出来，书的边缘</description>
			<content type="html"><![CDATA[<p>乔布斯的十大演讲</p>
<p><img src="/imgs/books/follow-your-heart.jpg" alt="封面"></p>
<p>很喜欢封面的设计，这本书的纸质也很赞，摸起来蛮有质感的，书买了应该有两年了，一直带着，但是没怎么看</p>
<p>今天又重新翻出来，书的边缘，已经有了岁月的痕迹，微微泛黄</p>
<p>封面是乔帮主，还有那句经典的&quot;Follow your heart&quot;</p>
<p>曾几何时，对帮主不是很了解，即使是帮主走的那年，12年十月，看了乔布斯传，对apple有了基本的认识</p>
<p>12年后，逐渐的，变成了果粉，没有刻意去追求什么，只是，apple的产品，逐一进入的我的生活</p>
<p>我曾经说过，买macbook是2013年最正确的决定之一，然后，运气爆表年会中了一个ipad air，变成了今年最幸运的事情之一。用了一天，下了多看,kindle,知乎，网易公开课，还有google浏览器，微博，pocket,evernote。以后不用抱着电脑累了，相信会为我的生活带来些什么</p>
<hr>
<p>整本书，看看第一章第二章即可</p>
<p>第一章，语录，可以反复读，即使读不懂，随着时间，阅历丰富，有些东西会逐渐懂得。</p>
<p>第二章，斯坦福的演讲，看看不错, <a href="http://v.163.com/special/opencourse/jobs.html">入口</a></p>
<p>后面的，大部分是发布会，以及采访，可以直接看视频，看文字没啥特别的感觉，感觉编辑纯粹是凑字数</p>
<p>另外，还有一个遗失的访谈，蛮长的，一个多小时，但是不得不说，值得一看，<a href="http://v.163.com/movie/2013/5/N/R/M8TBJIK7D_M8TBLIINR.html">入口</a></p>
<hr>
<p>以下，是一些感受</p>
<blockquote>
<p>我活着，就是为了改变世界</p>
</blockquote>
<p>我只能说，帮主做到了。</p>
<p>很多时候，我都在思考，为什么活着，这个是个哲学问题，又不是，很难想明白的</p>
<p>每个工程师，都有改变世界的梦想</p>
<p>这么多年，有些端倪了，活着，对我而言，更像是：“创造价值”，让我，以及我所能影响到的人，生活更加美好些</p>
<blockquote>
<p>领袖和跟风者的区别就在于创新</p>
</blockquote>
<p>有所感受，不敢妄加评论，起码我所接触到的中国互联网环境还太狭窄</p>
<blockquote>
<p>人要么是天才，要么是笨蛋</p>
</blockquote>
<p>有些偏激，但是蛮有道理的</p>
<blockquote>
<p>热忱是胜利的秘诀</p>
</blockquote>
<p>对所做的事情，需要有热情</p>
<blockquote>
<p>佛教中有一句话：初学者心态。拥有初学者心态是件了不起的事情</p>
</blockquote>
<p>每隔一段时间，都需要倒掉，空杯心态，去接触新的东西，去重新认识已有的东西</p>
<blockquote>
<p>求知若渴，虚怀若谷</p>
</blockquote>
<p>前者是现在必须努力达到的目标，后者，是一生需要学习和感悟的</p>
<blockquote>
<p>每个人的时间有限，所以你不要为别人活着。不要让自己的内心被别人的意见左右，不要活在他人的观念里，更不要被平庸的教条限制。最重要的是，坚定地寻找和倾听自己心灵发出的声音，那才是你的真实想法。要相信你的直觉和心灵。除此之外，其他的一切都是次要的。</p>
</blockquote>
<p>我觉得，每个人都有必要自己读一读这段话，从小到大，我们的思想受到了太多束缚和限制，所以导致很多人，终其一生，都为了别人而活，活在别人的观念里</p>
<blockquote>
<p>互联网创业不在于有多少人开始创办公司，而在于有多少人能坚持到底</p>
</blockquote>
<p>坚持很重要</p>
<blockquote>
<p>没有经验，你就永远不可能知道经验的价值，或者永远不可能知道如何正确地保有经验所创造的财富</p>
</blockquote>
<p>经验很重要，见过多年只有一年经验的人，也见过一年有多年经验的人。刚毕业那会，对所谓的“经验”很不屑，两年多过来，有了蛮多认识的。经验，很重要，而且通过书本什么的很那学来的，实践出真知。经验，应该可以作为“聪明”和“智慧”的分界线吧。and，经验是需要积累的，不断持续的积累，有意识地积累。帮主也提到，过去的一切，点滴，即使再小，在未来的某个时刻，也会起到作用，所有的事情和经历，都是关联的。all connected!</p>
<blockquote>
<p>成为海盗吧，成为海盗比加入海军更有意思</p>
</blockquote>
<p>海军的优势，或许是“纪律”，但是劣势，或许也是“纪律”，向往自由的人，成为“海盗”更为合适。我的征途是星辰大海</p>
<blockquote>
<p>你若想将未来的点联系在一起，那你就只能先将过去的点联系起来。你得相信你的未来和你的过去是联系着的。你必须要相信直觉、命运、生活等这些东西。这个方法从来没有让我失望过，它反而让我的生活发生了奇妙的变化</p>
</blockquote>
<p>很多事情，并非一蹴而就，或许在外人看来是，但是背后，都以一段很长的过去。我们的现在，就是未来的过去，未来想要变成什么得到什么，和现在是相关的。</p>
<p>另外，我开始相信直觉。</p>
<blockquote>
<p>你的工作将会占据你的大部分时间，做你自己认为最了不起的工作时唯一让你真正满意的方法。而做好你最满意的工作的唯一方法就是热爱你所做的事情。因此，继续找你满意的工作，直到找到它，不要气馁</p>
</blockquote>
<p>非常赞同，在懵懂中度过了大学四年，花了接近两年，看是有点懂了。做自己喜欢的事情。决定并不难，难的是，如何真正知道自己喜欢什么。</p>
<blockquote>
<p>记住一点，就是每个人都会死去。这是我遇到的最重要的决策工具，很多重大的人生选择都是在它的帮助下完成的。外部所有的骄傲、期望、对失败或尴尬的恐惧，在死亡面前，都会消失殆尽，只留下真正重要的东西</p>
</blockquote>
<p>有点感觉，但是还做不到，可能，还是自己太年轻了</p>
<blockquote>
<p>热爱你所做的事是做伟大工作的最佳方法。如果你还没有找到你想要的工作，继续找。不要停下来。只要你全力以赴，你知道你一定会找到它。</p>
</blockquote>
<p>坚持</p>
<blockquote>
<p>人生苦短，你总有一天会离开人世。一个人没有机会去做那么多事，所以每个人都应该极其优秀才行。因为这就是我们的生活。</p>
</blockquote>
<p>做选择的时候，多想想这个问题</p>
<hr>
<p>2014-01-19</p>
<p>wklken</p>
]]></content>
		</item>
		
		<item>
			<title>如何书写shell脚本</title>
			<link>https://wklken.me/posts/2014/01/12/shell-script-base.html</link>
			<pubDate>Sun, 12 Jan 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/01/12/shell-script-base.html</guid>
			<description>关于Linux基础命令，可以查看另一篇博文 Linux Shell脚本攻略笔记 以下内容，主要是，了解书写shell脚本所需要的大部分知识,主要内容来自于</description>
			<content type="html"><![CDATA[<p>关于Linux基础命令，可以查看另一篇博文 <a href="https://wklken.me/posts/2013/07/04/note-of-linux-shell-scripting-cookbook.html">Linux Shell脚本攻略笔记</a></p>
<p>以下内容，主要是，了解书写shell脚本所需要的大部分知识,主要内容来自于书籍和网络</p>
<p>目的是，能快速书写出需要的shell脚本</p>
<p>开始</p>
<p>version 0.1 2014-01-12 基本内容, 完成度30%</p>
<hr>
<p>资源</p>
<p><a href="http://google-styleguide.googlecode.com/svn/trunk/shell.xml">google shell style guide</a></p>
<p><a href="https://wklken.me/posts/2013/07/04/note-of-linux-shell-scripting-cookbook.html">Linux Shell脚本攻略笔记</a></p>
<p><a href="http://blog.jobbole.com/48717/">Linux Shell编程实战技巧</a></p>
<p><a href="http://kodango.com/bash-pitfalls-part-1">Bash编程易犯的错误 1234</a></p>
<p><a href="http://blog.jobbole.com/16604/">关于shell脚本编程的10个最佳实践</a></p>
<p><a href="http://bash.cumulonim.biz/BashPitfalls.html">Bash Pitfalls</a></p>
<hr>
<h2 id="第一部分-一些概念">第一部分 一些概念</h2>
<p>标准IO</p>
<pre><code>文件描述符
0 标准输入 默认键盘
1 标准输出 默认终端
2 标准错误 默认终端
</code></pre>
<p>重定向</p>
<pre><code>&gt; 输出重定向
&gt;&gt; 追加到输出重定向
&lt; 输入重定向
&lt;&lt; 追加到输入重定向

ls -l &gt; /tmp/a

cmd &gt;/dev/null 2&gt;&amp;1 #输出到垃圾桶
</code></pre>
<p>管道</p>
<pre><code>前后连接两个命令

ls -l | grep test
</code></pre>
<p>引号</p>
<pre><code>双引号：可以除了字符$`\外地任何字符或字符串
单引号：忽略任何引用值，将引号里的所有字符作为一个字符串 $var 不能被解析
反引号：设置系统命令输出到变量
</code></pre>
<p>shell脚本识别三种基本命令：内建命令，shell函数和外部命令</p>
<p>基本的命令查找:shell会沿着查找路径$PATH来寻找命令</p>
<pre><code>echo $PATH

可以在.profile文件中修改
export PATH=$PATH:$HOME/bin
</code></pre>
<p>and/or</p>
<pre><code>expression1 &amp;&amp; expression2 &amp;&amp; expression3
只有前面一条命令执行成功，才执行下一条
expression1执行成功，才执行expression2
串联的

expression1 || expression2 || expression3
执行命令，直到有一条成功为止
</code></pre>
<hr>
<h2 id="第二部分-shell脚本">第二部分 shell脚本</h2>
<p>首行声明使用bash(声明脚本执行解释器)</p>
<pre><code>#!/bin/bash

# do something

exit 0/n
</code></pre>
<p>运行</p>
<pre><code>sh xx.sh
bash xx.sh #大部分情况下两个一样，某些命令只有bash有，只能用这个

or

chmod u+x xx.sh
./xx.sh
</code></pre>
<p>调试</p>
<pre><code>#查看运行时，每个命令回显，执行之后回显
sh -x xx.sh

#执行之前回显
sh -v xx.sh

#检查语法错误，不执行
sh -n xx.sh

#如果使用了未定义的变量，给出错误信息
sh -u xx.sh

#调试部分脚本
echo &quot;Hello $USER,&quot;
set -x
echo &quot;Today is $(date %Y-%m-%d)&quot;
set +x
</code></pre>
<p>判断执行结果</p>
<pre><code>N=$?  #0 &lt;= N &lt;= 255

0 无错误，正常执行结束
非0 异常
    1-125命令不成功退出
    126命令成功，但文件无法执行
    127命令找不到
    &gt;128命令因收到信号而死亡
</code></pre>
<p>获取目录名和文件名</p>
<pre><code># To find base directory
APP_ROOT=`dirname &quot;$0&quot;`

# To find the file name
filename=`basename &quot;$filepath&quot;`

# To find the file name without extension
filename=`basename &quot;$filepath&quot; .html`

e.g.
BASEDIR=$(dirname $0)
cd $BASEDIR
CURRENT_DIR=`pwd`
</code></pre>
<p>日期</p>
<pre><code>TODAY=`date +%Y%m%d`
DAY_1_AGO=`date -d &quot;$TODAY 1 days ago&quot; +%Y%m%d`

常用接受日期/使用默认日期处理

if [ -n &quot;$1&quot; ]
then
    TODAY=&quot;$1&quot;
else
    TODAY=`date +%Y%m%d`
fi
</code></pre>
<p>crontab调度</p>
<pre><code>查看
crontab -l
编辑
crontab -e

格式
* * * * * command_path

字段      含义     范围
1        分钟         0-59
2        小时         0-23
3        日期         1-31
4        月份         1-12
5        星期几，0=周日   0-6
6        具体命令,可以是调用脚本

*任意时刻
n1,n2  分割，n1和n2
*/n  每隔n单位
n1-n2   时段，一个时段内

0 */2 * * * sh run.sh     每隔两小时
20 7 * * * sh run.sh 每天7:20
0 1,5 * * * sh run.sh 每天1点和5点
* * * * * sh run.sh 每分钟执行一次
</code></pre>
<hr>
<h2 id="第三部分-变量">第三部分 变量</h2>
<h4 id="1变量赋值">1.变量赋值</h4>
<pre><code>varname=&quot;value&quot;
varname=`expression`

注意，等号两边必须不能包含空格
</code></pre>
<h4 id="2分类">2.分类</h4>
<pre><code>四种变量：环境变量、本地变量、位置变量、特定变量参数

环境变量可作用于所有子进程
本地变量在用户现在的shell 生命期的脚本中使用，仅存在于当前进程
位置变量：作为程序参数
特定变量：特殊作用
</code></pre>
<h4 id="3环境变量">3.环境变量</h4>
<pre><code>设置
MYVAR=&quot;test&quot;
expirt MYVAR
or
export MYVAR=&quot;test&quot;

只读
MYVAR=&quot;test&quot;
readonly MYVAR
or
readonly MYVAR=&quot;test&quot;

显示
export -p
env #查看所有环境变量
$MYVAR #获取

消除
unset MYVAR
</code></pre>
<h4 id="4本地变量">4.本地变量</h4>
<pre><code>设置
LOCAL_VAR=&quot;test&quot;
or
LOCAL_VAR=&quot;test&quot;
readonly LOCAL_VAR #设置只读

还可以使用declare命令定义
</code></pre>
<h4 id="5位置变量">5.位置变量</h4>
<pre><code>$0 脚本名称
$# 传递到脚本参数个数
$$ shell脚本运行当前进程ID
$? 退出状态
$N N&gt;=1，第n个参数
</code></pre>
<h4 id="6字符串处理">6.字符串处理</h4>
<p>长度</p>
<pre><code>${#VARIABLE_NAME} 可以给出字符串的长度。

if [ ${#authy_api_key} != 32 ]
then
    return $FAIL
fi
</code></pre>
<p>拼接字符串</p>
<pre><code>echo &quot;$x$y&quot;
</code></pre>
<p>字符串切片</p>
<pre><code>${变量名:起始:长度}得到子字符串

$ test='I love china'
$ echo ${test:5}
e china
$ echo ${test:5:10}
e china

str=&quot;hello world&quot;
echo ${str:6}  # ${var:offset:length}
</code></pre>
<p>字符串替换</p>
<pre><code>${变量/查找/替换值} 一个“/”表示替换第一个，”//”表示替换所有,当查找中出现了：”/”请加转义符”\/”表示
echo ${str/foo/bar} #首个
echo ${str//foo/bar} #所有
</code></pre>
<p>正则匹配</p>
<pre><code>if [[ $str =~ [0-9]+\.[0-9]+ ]]
</code></pre>
<h4 id="7数值处理">7.数值处理</h4>
<p>自增</p>
<pre><code>a=1
a=`expr a + 1`

or

a=1
let a++
let a+=2
</code></pre>
<p>let</p>
<pre><code>no1=4
no2=5
let result=no1+no2
</code></pre>
<p>expr</p>
<pre><code>result=`expr 3 + 4`
result=$(expr $no1 + 5)
</code></pre>
<p>其他</p>
<pre><code>result=$[ no1 + no2 ]
result=$[ $no + 5 ]

result=$(( no1 + 5 ))
</code></pre>
<p>浮点数</p>
<pre><code>echo &quot;4 * 0.56&quot; | bc
设定精度
echo &quot;scale=2;3/8&quot; | bc
进制转换
echo &quot;obase=2;100&quot; | bc
平方
echo &quot;sqrt(100)&quot; | bc
</code></pre>
<p>数组和map</p>
<hr>
<h2 id="第四部分-控制流">第四部分 控制流</h2>
<h4 id="1条件测试">1.条件测试</h4>
<p>语法</p>
<pre><code>test condition
[ condition ] #注意两边加空格

$? #获取判断结果，0表示condition=true
</code></pre>
<p>条件测试中的逻辑</p>
<pre><code>-a 与
-o 或
!  非
&amp;&amp;
||

if [ -n &quot;$str&quot; -a -f &quot;$file&quot; ]
if [ -n &quot;$str&quot; ] &amp;&amp; [ -f &quot;$file&quot; ]
</code></pre>
<p>字符串测试</p>
<pre><code>=   两字符串相等
!=  两字符串不等
-z  空串 [zero]
-n  非空串 [nozero]

[ -z &quot;$EDITOR&quot; ]
[ &quot;$EDITOR&quot; = &quot;vi&quot; ]
</code></pre>
<p>数值测试</p>
<pre><code>-eq  数值相等（equal）
-ne  不等（not equal）
-gt  A&gt;B（greater than）
-lt  A&lt;B（less than）
-le  A&lt;=B（less、equal）
-ge  A&gt;=B（greater、equal）

N=130
[ &quot;$N&quot; -eq 130 ]
</code></pre>
<p>文件测试</p>
<pre><code>-d目录
-f 普通文件（Regular file）

-e 文件存在
-z 文件长度=0
-s 文件长度大于0，非空

-b 块专用文件
-c 字符专用文件
-L 符号链接

-r Readable（文件、目录可读）
-w Writable（文件、目录可写）
-x Executable（文件可执行、目录可浏览）

-g 如果文件的set-group-id位被设置则结果为真
-u 文件有suid位设置
</code></pre>
<h4 id="2分支if-elsecase">2.分支if-else/case</h4>
<p>if-else语法</p>
<pre><code>if condition1
then
	//do thing a
elif condition2
then
	//do thing b
else
	//do thing c
fi

or

if condition; then
# do something
fi
</code></pre>
<p>case语法</p>
<pre><code>case $VAR in
	1)
		echo &quot;abc&quot;
		;;
	2|3|4)
		echo &quot;def&quot;
		;;
	*)
		echo &quot;last&quot;
		;;
esac
</code></pre>
<h4 id="3循环forwhileuntil">3.循环for/while/until</h4>
<p>for语法</p>
<pre><code>for VARIABLE in 1 2 3 4 5 .. N
do
    //commands
done

for OUTPUT in $(Linux-Or-Unix-Command-Here)
do
    //commands on $OUTPUT
done

#bash
for (( EXP1; EXP2; EXP3 ))
do
    //commands
done

例子

for i in 1 2 3 4 5; do
	echo $i
done

for i in `seq 1 5`; do
	echo $i
done

#!/bin/bash
echo &quot;Bash version&quot;
for i in $(seq 1 2 20)
do
   echo &quot;Welcome $i times&quot;
done

for i in {1..5}; do
	echo $i
done

#!/bin/bash
echo &quot;Bash version&quot;
for i in {0..10..2}
do
    echo &quot;Welcome $i times&quot;
done

for ((i=1; i&lt;=10; i++)); do
	echo $i
done

#无限循环
#!/bin/bash
for (( ; ; ))
do
   echo &quot;infinite loops [ hit CTRL+C to stop]&quot;
done
</code></pre>
<p>while</p>
<pre><code>while condition
do
	//do something
done

COUNTER=0
while [ $COUNTER -lt 5 ]
do
	COUNTER=`expr $COUNTER + 1`
	echo $COUNTER
done


无限循环
while [ 1 ]
do
	//
done
</code></pre>
<p>until</p>
<pre><code>#执行命令，直到条件为真，至少执行一次，可以用来做监控，condition每次都回去检查
until condition
do
	//do something
done
</code></pre>
<p>break/continue</p>
<pre><code>break
允许跳出循环，通常在进行一些列处理后退出循环或case语句
若多重循环，可指定跳出的循环个数，如跳出两重循环  break 2

continue
不会跳出循环，只是跳过此循环步
命令是程序在本循体内忽略下面的语句,从循环头开始执行
</code></pre>
<hr>
<h2 id="第五部分-函数">第五部分 函数</h2>
<h4 id="1函数定义">1.函数定义</h4>
<pre><code>function func_name() {

}

func_name() {
	//do some thing
}
</code></pre>
<p>注意</p>
<pre><code>函数名，在脚本中必须唯一
函数必须，先定义，后使用
</code></pre>
<p>return</p>
<pre><code>function equal() {
    return 1
}

equal
echo $? #got 1
</code></pre>
<h3 id="2参数传递">2.参数传递</h3>
<pre><code>#位置参数
function copyfile() {
	cp $1 $2
	return $?
}

调用

copyfile /tmp/a /tmp/b
or获取返回值
result=`copyfile /tmp/a /tmp/b`
</code></pre>
<p>位置参数</p>
<pre><code>$1 - $9，当参数超过10个时，需要使用${10}
$# 参数个数
$* 将所有参数视为一个字符串=&quot;$1 $2 ...&quot;
$@ 将所有参数视为个体=&quot;$1&quot; &quot;$2&quot; &quot;$3&quot;
</code></pre>
<h3 id="3返回值和退出状态">3.返回值和退出状态</h3>
<pre><code>#返回值
function func_a() {
	return 1
}

result=`func_a`
if [ result != 0 ]
then
	echo &quot;Error&quot;
fi

#退出状态
function func_b() {
	//do something
}

func_b
if [ $? -eq 0 ]
then
	echo &quot;Success&quot;
else
    echo &quot;Error&quot;
fi

#更简洁
if func_b; then
    echo &quot;Success&quot;
else
    echo &quot;Error&quot;
fi

func_b &amp;&amp; echo &quot;Success&quot; || echo &quot;Error&quot;
</code></pre>
<h2 id="第四部分-高级">第四部分 高级</h2>
<p>bash中参数展开-展开运算符</p>
<pre><code>${varname:-word} 如果变量未定义，返回默认值.  ${noexist:-0}返回0
${varname:=word} 如果变量未定义，设置变量为默认值  ${noexists:=0}; echo ${noexists}; 得到0

${varname:?message} 若未定义，显示varname:message并退出当前的命令或脚本
${varname:+word} 若存在且非null，返回word，否则返回null
</code></pre>
<p>模式匹配</p>
<pre><code>${variable##pattern}
${variable%pattern}
</code></pre>
<h2 id="第五部分-其他">第五部分 其他</h2>
<p>读文件</p>
<pre><code>while read -r line; do
    echo $line
done &lt; file

保留首尾字符
while IFS= reaad -r line; do
    echo $line
done
</code></pre>
<p>一些内置命令</p>
<pre><code>:
空命令，类似python的pass

.
相当于source

\
用于跨行命令

echo
输出，类似println

exec

exit n
脚本以n作为退出码退出

export
设置或显示环境变量

expr
简单计算
x=`expr $x + 1`
x=$(expr $x + 1)

let
d=111
let d=$d+1; echo $d
112

printf
格式化输出

return
函数返回

set

shift
所有参数变量左移一个位置

unset
从环境变量中删除变量或函数
</code></pre>
<p>BP:</p>
<pre><code>使用$() 代替反引号``
$(()) 代替expr运算符
</code></pre>
<p>bash</p>
<pre><code>GNU Bash 主页
http://www.gnu.org/software/bash/
GNU Bash 手册
http://www.gnu.org/software/bash/manual/
</code></pre>
<p>更多的特性</p>
<pre><code>$((3 + 4))          而不需要 expr 3 + 4, 算术展开
/usr/{bin,local/bin}  而不需要 /usr/bin /usr/local/bin
${str/src/dst}       而不需要 echo $str | sed ”s/$src/$dst/”
</code></pre>
<p>更方便的语法</p>
<pre><code>for (( expr1; expr2; expr3 )); do
        commands
done
for (( i = 0; i &lt; 100; i++ )); do … done
echo a{b,c,d}e  ==&gt; abe ace ade
</code></pre>
<p>表达式求值</p>
<pre><code>$[]    []$中间可以加表达式  eg: echo $[$a+$b]
$(())   (())中间可以加表达式。Eg： total=$(($a*$b))
</code></pre>
<p>正则</p>
<pre><code>bash的正则表达式
str='hello, world'
if [[ $str =~ '\s+world$' ]]; then
    echo match!
fi
if echo &quot;$str&quot; | grep -E '[ ]+world$'; then
    echo match!
fi
</code></pre>
<p>获取软连接指向的真实文件名</p>
<pre><code>#注:有些系统没有这个命令
readlink /usr/bin/python
</code></pre>
<p>增加debug</p>
<pre><code>function debug() {
    if [[ $DEBUG ]]
    then
        echo &quot;&gt;&gt;&gt; $*&quot;
    fi
}

# For any debug message
debug &quot;Trying to find config file&quot;

还有来自于一些很酷的Geeks的单行debug函数：

function debug() { ((DEBUG)) &amp;&amp; echo &quot;&gt;&gt;&gt; $*&quot;; }
function debug() { [ &quot;$DEBUG&quot; ] &amp;&amp; echo &quot;&gt;&gt;&gt; $*&quot;; }
</code></pre>
<p>将执行日志全部写到某个文件</p>
<pre><code>exec &gt;&gt;&quot;$LOGPATH&quot;/xx.log.$TODAY 2&gt;&amp;1
#begin of code
</code></pre>
<hr>
]]></content>
		</item>
		
		<item>
			<title>读书笔记-程序员的思维训练</title>
			<link>https://wklken.me/posts/2014/01/05/pragmatic-thinking-and-learning.html</link>
			<pubDate>Sun, 05 Jan 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/01/05/pragmatic-thinking-and-learning.html</guid>
			<description>================ &amp;laquo;程序员的思维修炼——开发认知潜能的九堂课&amp;raquo; 不是教你学习如何写代码之类的，而是，如何使用大脑，培养习惯，训练自己的</description>
			<content type="html"><![CDATA[<p>================</p>
<p>&laquo;程序员的思维修炼——开发认知潜能的九堂课&raquo;</p>
<p>不是教你学习如何写代码之类的，而是，如何使用大脑，培养习惯，训练自己的思维</p>
<p>当然，不仅限于程序员，我觉得对大多数人都有适用性</p>
<p>方法论，有点类似程序员的成功学的味道，选择性阅读和实践</p>
<p>以下是摘录</p>
<hr>
<p>诀窍汇总:</p>
<pre><code>诀窍一：始终关注情境
诀窍二：新手使用规则，专家使用直觉
诀窍三：知道你不知道什么
诀窍四：通过观察和模仿来学习
诀窍五：保持实践以维持专家水平
诀窍六：如果你需要创造力，直觉或者独创技能，避免使用形式方法
诀窍七：学习如何学习的技能
诀窍八：捕获所有的想法以从中获益更多
诀窍九：综合学习与分析学习并重
诀窍十：争取好的设计，它真的很有效
诀窍十一：重新连线大脑，坚信这一点并不断实践
诀窍十二：增加感官体验以促进大脑的使用
诀窍十三：R型开路，L型紧跟
诀窍十四：使用隐喻作为L型和R型相融之处
诀窍十五：培养幽默感以建立更强大的隐喻
诀窍十六：离开键盘去解决难题
诀窍十七：改变解决问题的角度
诀窍十八：推迟下结论
诀窍十九：适应不确定性
诀窍二十：信任记录而不是记忆，每一次思维的输出都是一次输入
诀窍二十一：从多个角度看待问题
诀窍二十二：尊重不同人的不同性格
诀窍二十三：想高级动物一样行动，请做深呼吸，而不要张口嘶鸣
诀窍二十四：相信直觉，但是要验证
诀窍二十五：建立SMART任务实现你的目标
诀窍二十六：对主动学习的投资做好计划
诀窍二十七：发现你的最佳学习方式
诀窍二十八：组织学习小组学习和辅导
诀窍二十九：主动阅读
诀窍三十：同时用R型和L型做笔记
诀窍三十一：写文档的过程比文档本身更重要
诀窍三十二：观察、实践、教学
诀窍三十三：为了更好地学习，请更好地玩
诀窍三十四：从相似点中学习，从差异中忘却
诀窍三十五：在你的环境中安全地探索、创造和应用
诀窍三十六：观察，不做判断，然后行动
诀窍三十七：允许失败，你会走向成功
诀窍三十八：让大脑为成功形成惯例
诀窍三十九：学会集中注意力
诀窍四十：挤出思维时间
诀窍四十一：使用wiki来管理信息和知识
诀窍四十二：指定交流规则来管理干扰
诀窍四十三：少发送邮件，你就会少收到邮件
诀窍四十四：为邮件通信选择你自己的进度
诀窍四十五：屏蔽中断来保持注意力
诀窍四十六：使用多台显示器来避免情境切换
诀窍四十七：优化你的个人工作流以达到最大化情境
诀窍四十八：抓住方向盘，你不能自动驾驶
</code></pre>
<hr>
<p>###第一章 绪论</p>
<p>认知科学，神经学，学习和行为理论</p>
<p>对大脑进行重新设计和重新连线——更高效的工作</p>
<p>程序设计其实就是解决问题，需要发明，创造和灵感</p>
<p>软件开发失败——我们自己的错误造成的，我们自身往往增加了程序设计的难度</p>
<blockquote>
<p>软件是在头脑中创造的</p>
</blockquote>
<p>最重要的两项技能</p>
<pre><code>沟通能力:简单，有效沟通的重要性
学习和思考能力: 学习能力，批判性的思考能力和创造力——完全取决于你自己
</code></pre>
<p>每个人都是不同的，尝试执行一些建议，在判断哪些对你有用</p>
<p>随着不断成长和适应，人么需要改变自己的习惯和方法</p>
<blockquote>
<p>切忌随波逐流</p>
</blockquote>
<blockquote>
<p>一切都是互相关联的</p>
</blockquote>
<p>没有什么事物是孤立存在的，一切都是系统和更大的情境的一部分</p>
<blockquote>
<p>有些东西是基础的，各领域相通的</p>
</blockquote>
<p>瑜伽和冥想</p>
<hr>
<p>###第二章 从新手到专家的历程</p>
<blockquote>
<p>制造问题的思维方式无法用来解决问题 ——爱因斯坦</p>
</blockquote>
<p>解释观察到的现象</p>
<pre><code>事件理论:可以被测量，验证或证明
构建理论：无形的抽象，无法被证明.通过它的用处来很亮的，无法判断准确与否
</code></pre>
<p>新手和专家</p>
<pre><code>专家——使工作看起来更轻松
通常很难把他们的行为恰如其分的解释清楚，如此熟练以至于已经变成无意识的。大量经验都是通过大脑而非语言、无意识区存储的，难以观察表述
</code></pre>
<blockquote>
<p>清晰表述专业技能十分困难</p>
</blockquote>
<p>新手和专家的根本区别，是他们看待世界的方式不同，反应也不同——不只是知道更多或者获得了技术，而且在如何认识世界，解决问题和形成思维模型等方面体验到的根本区别</p>
<blockquote>
<p>德雷福斯模型针对每项技能</p>
</blockquote>
<p>德雷福斯模型的5个阶段</p>
<pre><code>1.新手：经验很少或者根本没有经验（通过实施这项技术促进了思维的改变）
        新手需要指令清单
        可以通过与情境无关的规则。但是，规则只能让你启程，不会让你走的更远
2.高级新手：能够多少拜托固定规则，可以独立尝试任务，但是仍难以解决问题
            高级新手想要快速获取信息，但是不追根究底
            能够根据过去经验，逐步在正确的情境里采纳建议，但是比较吃力——开始形成一些总体原则
            高级新手不需要全局思维
3.胜任者：建立问题域的概念模型，并有效地使用它们，可以独立地解决自己遇到的问题
          胜任者能够解决问题
          有主动性、足智多谋
          还没有足够的能力反思和自我纠正
4.精通者：需要全局思维，围绕这个技术，寻找并想了解更大的概念框架
          能够纠正以往不好的工作表现，会烦死以前是如何做的，并修改其做法，期望下一次表现得更好——自我改进
          会学习他人经验
          有足够的经验，知道下一步会发生什么
          可以有效地运用软件模式
          更像初级的专家，而不是高级的胜任者
5.专家：各个领域知识和信息的主要来源，总是不断地寻找更好的方法和方式去做事
        根据直觉做事，不需要理由
</code></pre>
<blockquote>
<p>规则断送专家</p>
</blockquote>
<p>直觉是专家的工具，但公司往往轻视它，错误地认为，不科学或不可重复</p>
<p>敏捷开发</p>
<p>开发人员之间存在20：1-40：1的生产力差异</p>
<p>企业的压力和所谓的公平，伤害了新手和专家</p>
<p>新手到专家的变化</p>
<pre><code>从依赖规则想依赖直觉转变
观念变化，问题已不再是一个相关度等同的所有单元的集合体，而是一个完整的独特的整体，其中只有某些单元是相关的
从问题的旁观者转变为问题涉及的系统本身
</code></pre>
<blockquote>
<p>大多数人都是高级新手</p>
</blockquote>
<p>专家 != 老师</p>
<blockquote>
<p>直觉和模式匹配能力超载了显性知识</p>
</blockquote>
<p>十年成就专家？</p>
<pre><code>大约十年的努力，需要辛勤工作
-需要一个明确定义的任务
-任务需要有适当的难度——有挑战性但可行
-任务环境可以提供大量反馈，以便于你采取行动
-提供重复犯错和纠正错误的机会

一旦你成为某个领域的专家，在别的领域成为专家就变得更容易
</code></pre>
<p>没有实践就没有技能,而且没有什么东西可以替代实践</p>
<p>模仿-吸收-创新</p>
<p>在实践中保持技能</p>
<blockquote>
<p>优胜者不会帮扶失败者</p>
</blockquote>
<p>编程专家必须持续编程，并找到一个有意义有价值的职业生涯</p>
<p>警惕工具陷阱——规则无法告诉你在某种情况下应该采取的最合适的行动,不要屈服于工具或者模型的虚假权威。没有什么可以替代思考</p>
<blockquote>
<p>警惕非情境化的客观性</p>
</blockquote>
<blockquote>
<p>一种规格并非处处适用</p>
</blockquote>
<hr>
<p>###第三章 认识你的大脑</p>
<p>L模式，线性模式:细致工作并实现目标</p>
<pre><code>语言能力
分析能力
符号能力
抽象能力
时间能力
推理能力
数字能力
逻辑能力
线性思维能力
</code></pre>
<p>R模式，富模式:直觉、问题解决和创造性,对于复杂的问题，不受直接意识控制。异步的，可以作为后台进程运行</p>
<pre><code>非语言
非理性
综合
空间性
具体
直觉
分析
全面

能够提供直觉（专家必要）
</code></pre>
<p>随时记录想法(24*7)</p>
<p>钢笔、记事本、索引卡片、PDA、语音设备、</p>
<p>每个人都有好点子，点子价值并不高。拥有想法-跟踪想法-付诸行动-成功实现</p>
<p>设计胜于功能——商品化意味着美学品味的竞争</p>
<p>神经可塑性，意味着你能够学习的最大容量或者你可以获得的基恩能够数量不是固定的,没有上限，只要你相信这一点</p>
<p>多编码，深思熟虑，专注实践</p>
<hr>
<p>###第四章 利用右脑</p>
<p>启动感官输入——使用多感官技术，通过增强触觉获得成功  积木等</p>
<blockquote>
<p>利用多感官反馈</p>
</blockquote>
<blockquote>
<p>刺激你的大脑</p>
</blockquote>
<p>用右脑画画——学习绘画</p>
<p>激活右脑：听音乐、绘画、静思、慢跑、针线活、攀岩等等 建立L-R型互相转换</p>
<p>需要用R型打头阵，然后转到L型去“生产”出来</p>
<p>酒醉写作，酒醒修改——顺其自然，不要做完美主义者，如果想法过早的收到束缚，那么创造力就会被扼杀</p>
<p>结对编程——一个L型，一个R型</p>
<p>L型和R型在隐喻（创建类比的过程）相同</p>
<pre><code>隐喻，一中激发创造力的强大技术
语言和意向共同的基础，实在左右半脑之间，在潜意识和意识之间游弋的途径
</code></pre>
<p>幽默能力都来自于发掘或者扩展常规之外的关系，真正突破思维界限</p>
<p>收获R型线索</p>
<pre><code>你已经知道——一切输入都会被存储
许多想法无法用语言表述
利用图像流
利用自由日记
晨写技术——坚持天天写，限定页数，各类，不用审查，倾倒想法
自由写技术
利用散步——非目标驱动思维，把一切写到某个地方，不要试图思考，记住它，简单记住，让事实和问题自由地浸泡
</code></pre>
<p>收获模式</p>
<blockquote>
<p>代码，一次编写，多次阅读</p>
</blockquote>
<p>换种思路，角度——逆向思维，夸大想法，组合完全异类的想法</p>
<hr>
<p>###第五章 调试你的大脑</p>
<blockquote>
<p>直觉是伟大的，除了当它不伟大的时候</p>
</blockquote>
<p>四大问题 (读一读：批判性思维/决策与判断)</p>
<pre><code>认知偏见——如何被误导：思维定势，基本归因错误，自私的偏见，需要定论，认可上的偏见
时代影响
个性倾向
硬件故障
</code></pre>
<p>推迟下结论——通过明确的概率进行猜想</p>
<p>记忆是靠不住的</p>
<p>认清时代影响——当你强烈的支持或赞成一个观点时，你的论据是逻辑性的还是情感作怪？</p>
<p>了解个性倾向(MBTI性格测评)</p>
<pre><code>E/I 外向内向
S/N 感觉直觉
T/F 思考情感
J/P 判断知觉

INTJ 内向 - 直觉 - 思考 - 判断
</code></pre>
<hr>
<p>###第六章 主动学习</p>
<p>学习：</p>
<pre><code>学习不是强加于你的，而是需要你主动做的事情
仅仅掌握知识，而不去实践，没有用
随机的方法，没有目标和反馈，往往会导致随机的结果
</code></pre>
<blockquote>
<p>目标任务使你更靠近目标</p>
</blockquote>
<p>SMART目标</p>
<pre><code>具体，可度量，可实现，相关的，时间可控的
相关的：兴趣，热情
</code></pre>
<p>建立一个务实的投资计划</p>
<p>把技术和才干看做一个只是投资组合</p>
<pre><code>制定具体计划
    现在（你的下一步行动）
    明年的目标
    五年后的目标
多样化
    不要把所有的鸡蛋放到同一个篮子里：更好地组合语言，环境，技术，行业和非技术领域（管理、公共演讲、人类学、音乐、艺术等）
主动投资
    需要客观地按天来评估你的计划，判断运作情况
定期投资
    成本平均法——养成一种习惯
</code></pre>
<p>使用你的原生学习模式</p>
<pre><code>视觉型
听觉型
动觉型
</code></pre>
<p>多元智力</p>
<pre><code>身体-动觉：体育、舞蹈、DIY项目、木工、工艺、烹饪
语言：口头辩论，讲故事、阅读和写作
逻辑-数学：数学，数字，科学，分类学，几何
视觉-空间：图标图解，素描，绘画等
音乐：演奏，识别声音，节奏，模式，记忆标语，诗文
人际：感情共鸣、感觉、意图、他人的鼓励
自我认知：自我反省、了解内心世界、梦，和他人的关系
</code></pre>
<p>一起工作一起学习</p>
<pre><code>寻求建议
选择一项提议或者一个负责人
买书
安排午餐会议
</code></pre>
<p>使用增强的学习方法</p>
<pre><code>主动阅读和总结书面材料的更好方式
使用思维导图探索和发现模式和关系
以教代学
</code></pre>
<p>SQ3R主动阅读</p>
<pre><code>调查(Survey)：扫描每章目录和总结，得出总的看法
问题(Question)：记录所有问题
阅读(Read)：阅读全部内容
复述(Recite)：总结，笔记，用自己的话描述
回顾(Review)：重读，扩展笔记，与同事讨论
</code></pre>
<p>使用思维导图</p>
<hr>
<p>###第七章 积累经验</p>
<p>积累经验是学习和成长的关键</p>
<pre><code>通过构造来学习，而不是通过学习来构造
更好的利用反馈，让失败也变得有意义
让大脑提前为成功构建神经网络
</code></pre>
<p>真正的学习——对你有用的学习——来自实践和认知，而不是外部的教学活动或者死记硬背</p>
<p>玩耍的意义Play</p>
<pre><code>非目的性的探索，不仅仅接受信息，而且亲自探索和构建思维模型，犯错，吸取经验
引入一种新奇的感觉，乐趣
</code></pre>
<p>用一种好玩的方式学习新资料或者解决问题，可以让这个过程变得更让人销售，也让学习变得容易</p>
<p>利用现有的知识</p>
<pre><code>未知量是什么-已知量是什么-条件是什么？
</code></pre>
<p>正确对待实践中的失败——失败是成功的关键，但不是任意的失败，需要管理失败</p>
<p>一种高效有益的学习环境应该允许你安全地做三件事：探索、创造、应用</p>
<p>建立探索环境</p>
<pre><code>自由实验-能够原路返回-重现任意时刻的工作产品-能够证实进展
</code></pre>
<p>了解内在诀窍</p>
<pre><code>通过探索可以学得更好，而不是指令
</code></pre>
<p>培养情境反馈</p>
<pre><code>意识
不要把精力放在纠正一个个细节上，只需要具有意识
不要想着来纠正，但是在出错的时候要知道，然后再采取行动纠正
</code></pre>
<p>压力扼杀认知——当面对时间压力时，人最没有创造力.面对压力时，我们要放松</p>
<p>允许失败——允许失败会促进成功.一旦允许失败，你就不会失败</p>
<p>想象超越感官——从大脑中获取经验</p>
<p>利用大脑模拟成功</p>
<hr>
<p>###第八章 控制注意力</p>
<p>信息过载，需要更好地管理你的思维</p>
<pre><code>增强注意力
管理你的知识
优化当前情境
</code></pre>
<p>放松的，集中的注意力</p>
<p>如何冥想</p>
<pre><code>沉浸到一种宽松的思维中，可以意识到自己和你的情境，不用做出任何判断和回应
——内观冥想：意识到某些事情，但是没有额外的思考

寻找一个安静的地方，拜托干扰或中断
舒适，清醒地坐着，挺直背
闭上眼睛，将注意力集中在呼吸
注意呼吸节奏，吸气的长度和质量，吸气后屏气的短暂间歇，呼气的质量，呼气后屏气的短暂间歇。不要试图去改变它，只是感受
将思维集中于呼吸，不要说或，不要描述任何想法，不要和自己交谈
每当注意力游荡开，摒弃想法，注意力回到呼吸上
</code></pre>
<p>内心的平静与强大</p>
<p>通过分散注意力来集中注意力</p>
<pre><code>你必须有意识地忘掉问题，让问题在思维中浸泡一会
</code></pre>
<p>不做某些事</p>
<p>管理你的知识：</p>
<pre><code>有一个外部的系统，有效地记录、管理等，可以快速获取
</code></pre>
<p>优化当前情境</p>
<pre><code>多任务处理将耗费你百分之二十至四十的生产力
</code></pre>
<p>避免分心</p>
<p>有效地组织和处理任务</p>
<pre><code>仅扫描输入队列一次
顺序地处理每组工作
不要再头脑中保留清单
</code></pre>
<p>积极主动地去管理干扰</p>
<pre><code>制定项目交流的规则
放缓电子邮件，做到异步定时而非实时: 设定邮件通知-加大检查邮件间隔 - 注意设定期望答复的速度和电子邮件数量
</code></pre>
<p>保持情境的一个办法是，提高进出情境的物力成本，有助于提醒你隐藏的精神成本</p>
<p>启动可屏蔽中断</p>
<p>保存情境堆栈：出栈时记录关键词等，方便回来</p>
<p>保持任务注意力</p>
<p>如何保持注意力：</p>
<pre><code>1.学会安慰喋喋不休的L型思维
2.主动在前进中思考和增强思想，即使是不成熟的
3.明确情境切换的昂贵代价，尽可能地避免
</code></pre>
<hr>
<p>###第九章 超越专家</p>
<p>有效地改变</p>
<pre><code>制定计划
“不作为”是敌人，而“错误”不是
给新习惯适应的时间
信念是真实的
采取步步为营的细小步骤
</code></pre>
<p>明天上午做什么</p>
<pre><code>不管你能做什么，或者期望自己能做什么，现在就开始做。勇敢可以给人智慧，力量和神奇。现在就开始做吧——歌德

一个笔记本，涂鸦，思维导图，几比几
开始写博客，为读过的书写书评
让散步成为你每天生活的一部分
再拿一个显示器,开始使用虚拟桌面
</code></pre>
<p>认识你自己，人士当前时刻，认识你所处的情境</p>
<p>自由的代价是永远提高警惕。这也是意识的代价</p>
<hr>
]]></content>
		</item>
		
		<item>
			<title>2013年终总结</title>
			<link>https://wklken.me/posts/2014/01/01/summary-08-2013end-2014begin.html</link>
			<pubDate>Wed, 01 Jan 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/01/01/summary-08-2013end-2014begin.html</guid>
			<description>每年写总结，总是一拖在拖，早几天就在想，今年做了些什么 目测这篇总结要从今年写到明年：） 好了，开始，依旧很杂，仅供看看&amp;hellip;&amp;hel</description>
			<content type="html"><![CDATA[<p>每年写总结，总是一拖在拖，早几天就在想，今年做了些什么</p>
<p>目测这篇总结要从今年写到明年：）</p>
<p>好了，开始，依旧很杂，仅供看看&hellip;&hellip;</p>
<hr>
<h3 id="脚步">脚步</h3>
<p>今年，去过两次海南（一次骑行一次公司旅游），一趟厦门（离老家一两小时的地方第一次去竟然是——从深圳杀过去的）.</p>
<p>去海南，不在计划内的，突然想去，就去了&hellip;&hellip;一路上都是大家在等我，胃出问题了，体能又不行。一路经历蛮多的，行走路上，看到异地，那里的人们是怎么生活的，感受一下。话说看海看到审美疲劳&hellip;&hellip;.</p>
<p>不记得太多，最深刻，莫过于最后一天到三亚，找青旅的路上，一个人，手机没电了，买了张地图，误打误撞骑到了鹿回头岭上，半山顶的一个平台，骑不动了把车停了躺在台阶上，看日落，对面是大海，有小岛，有轮船，夕阳，阳光透过云朵，照到海上，斑驳，海面，灿烂，摇曳，很美，很宁静&hellip;..</p>
<p>这一趟，不虚此行</p>
<p>很遗憾，手机没电，各种没电，这幅场景，只能在脑海了</p>
<p>五一那趟去海南，晒得很黑，一路行来，不记得多少公里，深圳汽车到海口，骑车到三亚，再高铁回海口飞回深圳.一路行来没去什么景点，没看到什么名胜</p>
<p>想到一些东西，学到一些东西，有些感受，足以，走了多远，并不是很重要吧</p>
<p>后来去的那一趟，玉带滩，天涯海角，非诚勿扰拍摄地等等，该看的都看回来了，反而没什么感觉，不过大巴走的路线和骑行有一段重复，想着当初爬那坡爬到要挂的心情&hellip;&hellip;感受颇多，对了，有个地方叫做长坡镇，坡真的很长，真的&hellip;.</p>
<p>厦门之旅，太赶，权当回家乡了，鼓浪屿什么的，时间有限，满景点都是人，感觉并不如何</p>
<p>今年虽然没满万里，勉强七八千里还是有的，额，大部分还是交通工具.</p>
<p>庆幸的是，高铁开了，以后回家4小时，票价150，幸甚，以后回家不愁了</p>
<p>行走，并不一定为了什么，不是公里数，不是任何数，带着问题或者不带着问题，拍照或者不拍照，到一个地方，有一些经历，有一些感受，最好再想清楚一两件事，也就足够了吧</p>
<p>2014，继续行走</p>
<hr>
<h3 id="工作">工作</h3>
<p>去年从杭州离职到深圳，整整一年又两个月。</p>
<blockquote>
<p>为什么从豆瓣离职：作为一个程序员，不加班我感到很空虚&hellip;.
&ndash; 知乎</p>
</blockquote>
<p>刨掉前两个月，2013一整年，好像没加过几次班，单手可以数&hellip;&hellip;一方面公司不提倡，提供了好的氛围，另一方面，更高效更懂得时间管理执行力更高了（自夸嫌疑，^_^，工作两年多了，好歹有点进步）</p>
<p>当然，一方面今年没有涉及到特别赶的项目</p>
<p>其实，真心觉得，懂得管理好自己，学会规划，时间的安排，足够强大的执行力，足够强的沟通，那么，每天八小时，足够了。</p>
<p>足够高效代表足够的产出，不需要额外通过加班弥补产出的不足。时间多，可以投入提升产能，良性循环</p>
<p>加班可能陷入恶性循环，好吧.</p>
<p>不扯了，今年工作上算是像个“有经验的”工程师了，项目各个流程，时间规划，执行，感觉比一年前的我进步许多，原来可能只知道怎么做，现在趋向于，怎么做的更好。</p>
<p>年中的时候，需要作为类似“组长”角色的角色，带着大家干活，其实感觉半年多下来，做的并不怎么好，只是做一些日常，提供必要的帮助和协调，跟进和处理。各种不完美，拥抱变化，尝试去学习，去处理。也在慢慢进步吧，虽然挺慢的，逐渐学习很多东西&hellip;&hellip;</p>
<p>今天还在翻tower，查看今年自己干了什么，发现除了将整体搜索重构，写了个识别程序，其他的项目好像都有打酱油的嫌疑，或许做的不错，感觉不在兴趣点上吧，起码成就感没有爆.今年真没干嘛，更多的是各种需求的支持和处理。</p>
<p>也要反思一下，自己更喜欢做什么。做的事情如果恰好和兴趣匹配，那么项目绝对可以做到超出期望，匹配度一般的话，满足期望&hellip;&hellip;.不匹配的话，3.25&hellip;..</p>
<p>现在上班，要打卡，虽然是十点上班，但是总觉得打卡对于工程师而言，蛮不对味的，虽然现在习惯了&hellip;.
还有一个，要绩效，填那些东西对目标感比较强的人来说，浪费太多时间，年底上了个系统，已经很方便客观了。明白一个道理，好的工具，对不需要的人（或者认为自己不需要），再好也是浪费时间，对需要的人，绝对是提升产出产能的利器.</p>
<p>今年工作，有亮点，有槽点，一年过来，算是蛮顺利的，给自己勉强80优秀，再接再厉，希望明年正正经经做几件自我认同的事情出来</p>
<hr>
<h3 id="读书">读书</h3>
<p>既然不加班，那么多时间怎么花，这是个问题，尤其哥还单身&hellip;&gt;_&lt;#</p>
<p>还是在保持每月买几本书的节奏，然后当当京东偶尔满400-200的活动手贱忍不住会参加，然后，年第一盘算，加上10来本兑换的，一共90+本</p>
<p>买书如山倒，读书如抽丝，哎</p>
<p>仔细算下来，读过的有20+本（我指的读过DONE是指，两遍以上，有笔记总结的，技术类的我会给“它”脱水），看多的应该也有20+本（看过，一遍），总共应该占了50%以上，应该算还行的程度，剩余50%，只能排到2014了</p>
<p>最近看到码农周刊和知乎的 2013书单，又要下手，不过打算开始电子版了，非技术类的，都电子版，纸质版的搬家绝对是个悲剧&hellip;&hellip;</p>
<p>没什么事，不知道做什么，就读书吧，总会有收获的，或多或少</p>
<p>怎么挑书，微博，知乎，别人推荐的，热门的一般都不会差，可以一读（当然，远离大部分成功学的书，只有很少一部分是可读的）</p>
<p>为什么要读？理由有很多，知识改变命运，这句话很老，但并不可笑.</p>
<p>怎么读？貌似不需要记住吧？有印象，收获一些观点，一些感悟，对自己有帮助，足够了吧？</p>
<p>反正，我现在工作中用到的，90%以上，纯属毕业到现在自学&hellip;..主要途径还是读书</p>
<p>知乎上有一个话题，什么东西大大提升你的幸福感，排前有一个:kindle</p>
<p>总之，今年读书勉强60及格，后续买书走电子版，入手需三思再三思&hellip;..</p>
<p>附，<a href="http://www.wklken.me/pages/books.html">我的书单</a></p>
<hr>
<h3 id="博客项目">博客&amp;项目</h3>
<p>写博客，主要为了积累和分享</p>
<p>博客年初过来深圳后，就不再csdn上更新了，迁移，期间自己去写了一个博客程序（重复造轮），后来又废弃了（囧），后来终于找到满意的，修改了主题，就是现在这个博客，pelican静态化到美帝digitalocean服务器，然后就可以通过vim直接写，不需要考虑格式啊排版乱七八糟的东西，一键维护到github，自动更新到服务器，刷新文章列表.</p>
<p>博文，40+篇，不算高产，有一段时间迷上翻译，所以有一部分翻译文章，刚开始发了一些笔记总结，还有很多初稿没更新上来，很多东西，只有感觉了可以，才会发（即使这样，质量和深度也就一般，需要继续修炼）</p>
<p>有几篇类似读书笔记的东西，大部分笔记还在evernote里存着，后续整理发出</p>
<p>继续坚持写吧，每每回头看，总会有感受</p>
<p>关于项目</p>
<p>今年没搞出什么东西来，哎</p>
<p>现在自己维护的项目列表  <a href="http://www.wklken.me/pages/projects.html">github</a></p>
<p><a href="https://github.com/wklken/k-vim">k-vim</a> 这个配置，蛮多人用的，打算2014再更新一个版本，让更多pythonista和vim党用起来.</p>
<p><a href="https://github.com/wklken/stackoverflow-py-top-qa">stackoverflow-py-top-qa</a>那一两个月，开始翻译，翻译了一百多个问题，你问我怎么搞出来的，我也不知道，一天翻译几个一天翻译几个，就那么多了，还欠着100+问题，2014春节争取搞完，不能再欠了</p>
<p>其他，真没什么了，写过三个程序，都夭折了，前几天给自己新建了几个，争取2014完成两个，争取也加入一些自己感兴趣的项目</p>
<p>博客，80分，项目60，都不是很满意，2014希望有改变吧</p>
<hr>
<h3 id="生活">生活</h3>
<p>在地铁站旁边的小区住，现在也一年多了，房间虽小，但是有一个书柜，靠近街道有点吵，但是胜在能晒到太阳。</p>
<p>上半年把蒸功夫和麦当劳通关N次，终于疲倦了，开始自己DIY，目前会煮面，会煮稀饭，会做个咖喱饭，勉强喂饱自己，不容易啊&hellip;&hellip;.学会做饭使用一个必须的生存技能，饿，没错。成为一个厨子的道路还需要经历一段漫长(并且黑暗)的时间&hellip;&hellip;</p>
<p>周末，基本睡半天，下午做窗户边晒太阳看书，晚上写代码到深夜，周天早出去溜溜，步行个七八公里，到海边或者绿道公园走走，权当锻炼身体了&hellip;&hellip;没车也就远离骑行了</p>
<p>蛮充实的，也蛮单调的，真的很单调</p>
<p>发现我正在从一般宅迈向终极宅的道路上大步踏去，o(╯□╰)o</p>
<p>生活，40分，哎，这次真的不及格</p>
<hr>
<h3 id="其他">其他</h3>
<p>去年还在想，更快的从windows转到ubuntu，今年年初果断皈依mac了&hellip;&hellip;计划赶不上变化</p>
<p>macbook给我带来的是最最最根本的，提升？额，可以用这个词，直接废弃公司的电脑了，每天上下班背着电脑，虽然不怎么方便，但是一整年下来mac为我节约的时间不是按几个小时算的&hellip;&hellip;今年做的最正确的事之一</p>
<p>今年的唯一变化是不上网易了，负能量太多，只能刷刷知乎了&hellip;..依旧刷微博，只是频率低了很多</p>
<p>不再读小说，自从上次读完平凡的世界，现在唯一只追一本小说了，现在的小说带来的快乐和花费的时间完全不成比例，干脆戒了</p>
<p>今年也错过了很多事情，哎</p>
<p>很多目标没有达到，很多事情没有做成</p>
<hr>
<h3 id="扯点其他的">扯点其他的</h3>
<p>几点</p>
<p>Live your life——过自己真正想要的生活，不是别人的，家人的或者别人眼里的，别人想要的，别人要求的生活，不管世俗不世俗，起码要想清楚，我想要的是什么，我想要过的生活又是什么。我也还没怎么明白，在思考，很容易知道自己不喜欢什么，但是很难搞明白自己想要什么</p>
<p>做喜欢的事情——很重要，或许要花很长一段时间才能明白.我大体想明白了</p>
<p>做点事情——要做事情，不管喜不喜欢，不可能所有的事情都是自己喜欢的，生活没有那么完美，不喜欢的，一样要做好，有句话叫做：做好当下你认为正确的事情。我一直在坚守的原则之一</p>
<p>创造价值——总是要创造一些东西的，生活的意义</p>
<p>每天进步一点点——哪怕真的只有一点点.例如背单词&hellip;..</p>
<p>有一些事情注定我当下想不明白，性格的缺陷导致容易优柔寡断，哎，奈何，奈何。只能尽力克服了</p>
<p>好了，鸡汤完了</p>
<hr>
<p>2013已远去，写到一般（好多错字有木有&hellip;.）的时候，不知不觉，已经迈入2014了</p>
<p>从今天开始的很长一段时间，要去纠正时间签名老是签成2013&hellip;&hellip;</p>
<p>2014，新的开始和起点，也意味着新的计划(去年的计划貌似有一些没实现，具体是什么我也忘了&hellip;.)</p>
<p>计划永远赶不上变化的，但是我们依旧要计划，总比没有强:)</p>
<p>干嘛去？</p>
<p>做几个自己满意的项目(工作相关)</p>
<p>继续读书(先清库存，再入新书)</p>
<p>继续搞一些自己想写的代码(路漫漫)</p>
<p>到某个地方去旅行（不是旅游）</p>
<p>回学校一趟</p>
<p>&hellip;&hellip;&hellip;</p>
<p>暂时没想好,想好了就去做，这就够了吧</p>
<hr>
<p>all right!</p>
<p>再见，2013</p>
<p>你好，2014</p>
<hr>
<p>wklken</p>
<p>2014-01-01(刚才又签成2013了&hellip;..(╯‵□′)╯︵┻━┻)</p>
<p>于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>电影人生</title>
			<link>https://wklken.me/posts/2014/01/01/movies.html</link>
			<pubDate>Wed, 01 Jan 2014 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2014/01/01/movies.html</guid>
			<description>每一部电影，都是一种人生，感悟 每周五是我的电影之夜，以后有了自己小窝之后一定要搞个影院 会记录一些看过的，个人喜欢的电影 每部电影，都能看到什么</description>
			<content type="html"><![CDATA[<blockquote>
<p>每一部电影，都是一种人生，感悟</p>
</blockquote>
<p>每周五是我的电影之夜，以后有了自己小窝之后一定要搞个影院</p>
<p>会记录一些看过的，个人喜欢的电影</p>
<p>每部电影，都能看到什么，获得些什么</p>
<hr>
<p>无人区</p>
<p>饥饿游戏1 饥饿游戏2</p>
<p>地心引力</p>
<hr>
<p>时空恋旅人</p>
<p>意外的恋爱时光</p>
<p>一座城池</p>
<hr>
<p>和summer的500天/好好先生</p>
<p>黄铜茶壶</p>
<p>光的棍</p>
<p>哈利波特4-7(Luna)</p>
]]></content>
		</item>
		
		<item>
			<title>[翻译]Python中staticmethod和classmethod的差异</title>
			<link>https://wklken.me/posts/2013/12/22/difference-between-staticmethod-and-classmethod-in-python.html</link>
			<pubDate>Sun, 22 Dec 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/12/22/difference-between-staticmethod-and-classmethod-in-python.html</guid>
			<description>原文地址 入口 很短，顺手一翻 Class vs static methods in Python 这篇文章试图解释：什么事staticmethod/classmethod,并且这两者之间的差异. sta</description>
			<content type="html"><![CDATA[<p>原文地址 <a href="http://www.pythoncentral.io/difference-between-staticmethod-and-classmethod-in-python/">入口</a></p>
<p>很短，顺手一翻</p>
<hr>
<h3 id="class-vs-static-methods-in-python">Class vs static methods in Python</h3>
<p>这篇文章试图解释：什么事staticmethod/classmethod,并且这两者之间的差异.</p>
<p>staticmethod和classmethod均被作为装饰器，用作定义一个函数为&quot;staticmethod&quot;还是&quot;classmethod&quot;</p>
<p>如果想要了解Python装饰器的基础，可以看 <a href="http://www.pythoncentral.io/python-decorators-overview/">这篇文章</a></p>
<h3 id="simple-static-and-class-methods">Simple, static and class methods</h3>
<p>类中最常用到的方法是 实例方法(instance methods), 即，实例对象作为第一个参数传递给函数</p>
<p>例如，下面是一个基本的实例方法</p>
<pre><code>:::python
class Kls(object):
    def __init__(self, data):
        self.data = data

    def printd(self):
        print(self.data)

ik1 = Kls('arun')
ik2 = Kls('seema')

ik1.printd()
ik2.printd()
</code></pre>
<p>得到的输出:</p>
<pre><code>arun
seema
</code></pre>
<p>调用关系图:</p>
<p><img src="/imgs/translate/trans-classmethod-staticmethod-1.png" alt="translate1"></p>
<p>查看代码和图解:</p>
<pre><code>1/2 参数传递给函数
3   self参数指向实例本身 
4   我们不需要显式提供实例，解释器本身会处理
</code></pre>
<p>假如我们想仅实现类之间交互而不是通过实例？我们可以在类之外建立一个简单的函数来实现这个功能，但是将会使代码扩散到类之外，这个可能对未来代码维护带来问题。</p>
<p>例如：</p>
<pre><code>def get_no_of_instances(cls_obj):
    return cls_obj.no_inst

class Kls(object):
    no_inst = 0

    def __init__(self):
        Kls.no_inst = Kls.no_inst + 1

ik1 = Kls()
ik2 = Kls()

print(get_no_of_instances(Kls))
</code></pre>
<p>结果:</p>
<p>2</p>
<h3 id="the-python-classmethod">The Python @classmethod</h3>
<p>现在我们要做的是在类里创建一个函数，这个函数参数是类对象而不是实例对象.</p>
<p>在上面那个实现中，如果要实现不获取实例,需要修改如下:</p>
<pre><code>def iget_no_of_instance(ins_obj):
    return ins_obj.__class__.no_inst

class Kls(object):
    no_inst = 0

    def __init__(self):
        Kls.no_inst = Kls.no_inst + 1

ik1 = Kls()
ik2 = Kls()
print iget_no_of_instance(ik1)

结果
2
</code></pre>
<p>可以使用Python2.2引入的新特性，使用@classmethod在类代码中创建一个函数</p>
<pre><code>class Kls(object):
    no_inst = 0

    def __init__(self):
        Kls.no_inst = Kls.no_inst + 1

    @classmethod
    def get_no_of_instance(cls_obj):
        return cls_obj.no_inst

ik1 = Kls()
ik2 = Kls()

print ik1.get_no_of_instance()
print Kls.get_no_of_instance()
</code></pre>
<p>We get the following output:</p>
<pre><code>2
2
</code></pre>
<h3 id="the-python-staticmethod">The Python @staticmethod</h3>
<p>通常，有很多情况下一些函数与类相关，但不需要任何类或实例变量就可以实现一些功能.</p>
<p>比如设置环境变量，修改另一个类的属性等等.这种情况下，我们也可以使用一个函数，一样会将代码扩散到类之外（难以维护）</p>
<p>下面是一个例子:</p>
<pre><code>IND = 'ON'

def checkind():
    return (IND == 'ON')

class Kls(object):
    def __init__(self,data):
        self.data = data

    def do_reset(self):
        if checkind():
            print('Reset done for:', self.data)

    def set_db(self):
        if checkind():
            self.db = 'new db connection'
            print('DB connection made for:',self.data)

ik1 = Kls(12)
ik1.do_reset()
ik1.set_db()
</code></pre>
<p>结果:</p>
<pre><code>Reset done for: 12
DB connection made for: 12
</code></pre>
<p>现在我们使用@staticmethod, 我们可以将所有代码放到类中</p>
<pre><code>IND = 'ON'

class Kls(object):
    def __init__(self, data):
        self.data = data

    @staticmethod
    def checkind():
        return (IND == 'ON')

    def do_reset(self):
        if self.checkind():
            print('Reset done for:', self.data)

    def set_db(self):
        if self.checkind():
            self.db = 'New db connection'
        print('DB connection made for: ', self.data)

ik1 = Kls(12)
ik1.do_reset()
ik1.set_db()
</code></pre>
<p>得到的结果:</p>
<pre><code>Reset done for: 12
DB connection made for: 12
</code></pre>
<h3 id="how-staticmethod-and-classmethod-are-different">How @staticmethod and @classmethod are different</h3>
<pre><code>class Kls(object):
    def __init__(self, data):
        self.data = data

    def printd(self):
        print(self.data)

    @staticmethod
    def smethod(*arg):
        print('Static:', arg)

    @classmethod
    def cmethod(*arg):
        print('Class:', arg)
</code></pre>
<p>调用</p>
<pre><code>&gt;&gt;&gt; ik = Kls(23)
&gt;&gt;&gt; ik.printd()
23
&gt;&gt;&gt; ik.smethod()
Static: ()
&gt;&gt;&gt; ik.cmethod()
Class: (&lt;class '__main__.Kls'&gt;,)
&gt;&gt;&gt; Kls.printd()
TypeError: unbound method printd() must be called with Kls instance as first argument (got nothing instead)
&gt;&gt;&gt; Kls.smethod()
Static: ()
&gt;&gt;&gt; Kls.cmethod()
Class: (&lt;class '__main__.Kls'&gt;,)
</code></pre>
<p>图解</p>
<p><img src="/imgs/translate/trans-classmethod-staticmethod-2.png" alt="translate2"></p>
]]></content>
		</item>
		
		<item>
			<title>[翻译]Python中如何使用*args和**kwargs</title>
			<link>https://wklken.me/posts/2013/12/21/how-to-use-args-and-kwargs-in-python.html</link>
			<pubDate>Sat, 21 Dec 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/12/21/how-to-use-args-and-kwargs-in-python.html</guid>
			<description>不知道有没有人翻译了，看到了，很短，顺手一翻 原文地址 入口 或者可以叫做，在Python中如何使用可变长参数列表 函数定义 这是一种特殊的语法，在函</description>
			<content type="html"><![CDATA[<p>不知道有没有人翻译了，看到了，很短，顺手一翻</p>
<p>原文地址 <a href="http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/">入口</a></p>
<hr>
<p>或者可以叫做，在Python中如何使用可变长参数列表</p>
<h3 id="函数定义">函数定义</h3>
<p>这是一种特殊的语法，在函数定义中使用*args和**kwargs传递可变长参数.  *args用作传递非命名键值可变长参数列表（位置参数）; **kwargs用作传递键值可变长参数列表</p>
<p>下面的例子传递一个位置参数以及两个可变长参数</p>
<pre><code>def test_var_args(farg, *args):
    print &quot;formal arg:&quot;, farg
    for arg in args:
        print &quot;another arg:&quot;, arg

test_var_args(1, &quot;two&quot;, 3)
</code></pre>
<p>结果:</p>
<pre><code>formal arg: 1
another arg: two
another arg: 3
</code></pre>
<p>这里有一个键值的例子，传递一个位置参数和两个键值参数</p>
<pre><code>def test_var_kwargs(farg, **kwargs):
    print &quot;formal arg:&quot;, farg
    for key in kwargs:
        print &quot;another keyword arg: %s: %s&quot; % (key, kwargs[key])

test_var_kwargs(farg=1, myarg2=&quot;two&quot;, myarg3=3)
</code></pre>
<p>结果:</p>
<pre><code>formal arg: 1
another keyword arg: myarg2: two
another keyword arg: myarg3: 3
</code></pre>
<h3 id="函数调用">函数调用</h3>
<p>这种语法不仅在函数定义中可以使用，在调用函数是也会出现</p>
<p>(相当于extract package的效果)</p>
<p>在调用函数时，使用*args和**kwargs</p>
<pre><code>def test_var_args_call(arg1, arg2, arg3):
    print &quot;arg1:&quot;, arg1
    print &quot;arg2:&quot;, arg2
    print &quot;arg3:&quot;, arg3

args = (&quot;two&quot;, 3)
test_var_args_call(1, *args)
</code></pre>
<p>结果:</p>
<pre><code>arg1: 1
arg2: two
arg3: 3
</code></pre>
<p>**kwargs</p>
<pre><code>def test_var_args_call(arg1, arg2, arg3):
    print &quot;arg1:&quot;, arg1
    print &quot;arg2:&quot;, arg2
    print &quot;arg3:&quot;, arg3

kwargs = {&quot;arg3&quot;: 3, &quot;arg2&quot;: &quot;two&quot;}
test_var_args_call(1, **kwargs)
</code></pre>
<p>结果:</p>
<pre><code>arg1: 1
arg2: two
arg3: 3
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Python招聘需求与技能体系</title>
			<link>https://wklken.me/posts/2013/12/21/python-jd.html</link>
			<pubDate>Sat, 21 Dec 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/12/21/python-jd.html</guid>
			<description>目前国内的招聘Python，基本都是偏向web后台开发，偶有高大上的数据挖掘&amp;amp;机器学习 这是之前(2012年)找工作整理的一些JD，在</description>
			<content type="html"><![CDATA[<p>目前国内的招聘Python，基本都是偏向web后台开发，偶有高大上的数据挖掘&amp;机器学习</p>
<p>这是之前(2012年)找工作整理的一些JD，在梳理几年来的笔记，顺带理一理</p>
<p>可以以此建立自己的技能体系</p>
<hr>
<h3 id="第一部分-一些要求">第一部分: 一些要求</h3>
<p>1.学历</p>
<pre><code>格式： 计算机及相关专业本科及以上学历.....
</code></pre>
<p>不解释，不说明</p>
<p>毕业后很少再看学历了，更多的是看能力吧（我会告诉你第一年周围都是硕士博士海龟么，俺学历最低，自卑啊）</p>
<p>见过数学系、物理系、信息自动化系的跑过来当码农，唯一遗憾的是还没有见到中文系的&hellip;..</p>
<p>2.经验</p>
<pre><code>格式：拥有X年以上经验，至少独立负责过X个项目
</code></pre>
<p>很蛋疼的东西，见过一年经验用N年的，也见过一年拥有N年经验的</p>
<p>受环境影响，但更多的是取决于个人</p>
<p>听过一句话：人和人的唯一区别，就是有木有主观能动性（略有些偏激，但是有道理）</p>
<p>3.基础素质</p>
<pre><code>格式：具有良好的XXXX

编码规范/风格
文档书写习惯
沟通与表达能力，逻辑思维清晰
团队合作
动手能力/独立工作能力
进取心，求知欲，工作热情
善于学习，乐于分享，快速学习能力
能承受较大的工作压力
执行力
责任感
英文阅读能力
创新精神
对新技术敏感
独立分析、设计、解决问题的能力
</code></pre>
<p>这些都相对比较“虚”，不怎么好考察，面试+试用可以相对客观了解是否符合</p>
<p>个人认为，责任感+执行力，这两点足够了，其他一般不会差。</p>
<p>自我驱动的人，永远会get things done.所以会不断去弥补自己的短板，其他对应能力即使不足，也能很快弥补</p>
<p>4.加分项</p>
<pre><code>格式：XXXX优先

使用*nix系统
vim/Emacs编辑器
对开源技术有强烈兴趣和爱好，参与提交bug/patch
各种技术/经验(前端/hadoop/机器学习/数据挖掘/函数式编程)
</code></pre>
<p>为毛不是osx系统(╯‵□′)╯︵┻━┻</p>
<p>从一些侧面，查看一个人的特质吧</p>
<p>5.提供</p>
<pre><code>格式: XXXX账号/地址

博客地址
github/bitbucket
stackoverflow
知乎
微博
</code></pre>
<p>搞技术的，很有必要建立自己的领地，搞一些东西</p>
<p>分享，更重要的是积累</p>
<hr>
<h3 id="第二部分干货">第二部分：干货</h3>
<p>关于Python后端开发要求</p>
<p>1.对Python有兴趣，熟悉Python(标准库)</p>
<p>最好阅读过源码</p>
<p>了解Python的优化(熟悉pypy更佳)</p>
<p>2.至少至少一门语言（不说“精通”）</p>
<p>起码熟悉其他基本语言</p>
<pre><code>C/C++  Lisp Haskell Scheme golang erlang Java R Ruby Node.js PHP Perl Lua

我选了Java(曾经擅长)/Golang/Lisp/Ruby/C/C++
</code></pre>
<p>3.数据结构和算法</p>
<p>数据结构和算法基础扎实</p>
<p>4.Python框架</p>
<pre><code>Django/Tornado/Flask/Gevent/Web.py/Bottle/Celery/Twisted/NumPy
</code></pre>
<p>5.熟悉Linux</p>
<pre><code>基本操作和命令
会Shell
版本Git/Svn
部署相关: Nginx/Gunicorn/Fabric/Virtualenv
</code></pre>
<p>6.数据库</p>
<pre><code>熟悉Mysql等关系数据库使用
熟悉数据库设计
熟悉数据库调优/优化
有NoSQL使用经验 Redis/MongoDB等
</code></pre>
<p>7.后端技术相关</p>
<pre><code>Redis
Memcached
RabbitMQ/ZeroMQ
</code></pre>
<p>8.网络编程基础</p>
<pre><code>熟悉tcp/ip协议，熟悉网络编程
了解常见的网络模型
多线程
</code></pre>
<p>9.前端相关</p>
<pre><code>熟悉Web开发相关知识
熟悉HTML/CSS/Javascript/JQuery
熟悉AngularJS
</code></pre>
<p>10.其他</p>
<pre><code>数据抓取，爬虫
beautifulsoup/scrapy

机器学习/数据挖掘/自然语言处理（推荐算法）

TDD

高并发系统
大容量存储系统
消息系统

Linux系统编程/网络编程
</code></pre>
<p>基于此，去构建自己的技能体系，然后针对各项逐渐深入</p>
<p>就这些，骚年，努力练级去吧</p>
<hr>
<p>wklken</p>
<p>2013-12-21</p>
]]></content>
		</item>
		
		<item>
			<title>Python模板-Jinja2</title>
			<link>https://wklken.me/posts/2013/12/21/python-template-jinja2.html</link>
			<pubDate>Sat, 21 Dec 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/12/21/python-template-jinja2.html</guid>
			<description>Jinja2的一些使用摘要 Jinja2作为flask的默认页面模板，感觉蛮强大的，可适用于各种模板类相关的项目 特别是模板继承，写页面很舒服 资</description>
			<content type="html"><![CDATA[<p><img src="/imgs/python/jinja.png" alt="jinja"></p>
<p>Jinja2的一些使用摘要</p>
<p>Jinja2作为flask的默认页面模板，感觉蛮强大的，可适用于各种模板类相关的项目</p>
<p>特别是模板继承，写页面很舒服</p>
<hr>
<h3 id="资源">资源</h3>
<p>文档 <a href="http://jinja.pocoo.org/docs/">http://jinja.pocoo.org/docs/</a></p>
<h3 id="安装">安装</h3>
<pre><code>sudo easy_install jinja2
sudo pip install jinja2
</code></pre>
<h3 id="终端版helloworld">终端版helloworld</h3>
<pre><code>&gt;&gt;&gt; from jinja2 import Template
&gt;&gt;&gt; template = Template('Hello {{ name }}!')
&gt;&gt;&gt; template.render(name='World')
u'Hello World!'
</code></pre>
<h3 id="和">{%和{{</h3>
<pre><code>&lt;body&gt;
    &lt;ul id=&quot;navigation&quot;&gt;
    {% for item in navigation %}
        &lt;li&gt;&lt;a href=&quot;{{ item.href }}&quot;&gt;{{ item.caption }}&lt;/a&gt;&lt;/li&gt;
    {% endfor %}
    &lt;/ul&gt;

    &lt;h1&gt;My Webpage&lt;/h1&gt;
    {{ a_variable }}
&lt;/body&gt;
</code></pre>
<p>{% 用于执行语句</p>
<p>{{ 输出语句结果到模板</p>
<p>{{}}中的运算</p>
<pre><code>{{ 1 + 1 }}
{{ 3 - 2 }}
{{ 1 / 2 }} is {{ 0.5 }}.
{{ 20 // 7 }} is 2
{{ 11 % 7 }} is 4
{{ 2 * 2 }} would return 4
{{ '=' * 80 }}
{{ 2**3 }}
</code></pre>
<h3 id="变量">变量</h3>
<pre><code>变量输出 - 获取属性的方法，都可以
{{ foo.bar }}
# 查属性 - 查子元素 - 没有报错

{{ foo['bar'] }}
# 查子元素 - 查属性 - 没有报错

变量赋值
{% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
{% set key, value = call_something() %}


# set and use it later
{% set mybool = [False] %}
{% set _ = mybool.append(not mybool.pop()) %}
</code></pre>
<h3 id="注释">注释</h3>
<pre><code>{# note: disabled template because we no longer use this
    {% for user in users %}
        ...
    {% endfor %}
#}
</code></pre>
<h3 id="空格控制">空格控制</h3>
<pre><code>&lt;div&gt;
    {% if True %}
        yay
    {% endif %}
&lt;/div&gt;
</code></pre>
<p>默认，会执行 trim_blocks and lstrip_blocks</p>
<p>移除空格</p>
<pre><code>{% for item in seq -%}
    {{ item }}
{%- endfor %}

seq = 1 to 9
得到 123456789
</code></pre>
<h3 id="escaping">Escaping</h3>
<p>简单的</p>
<pre><code>{{ '{{' }}
</code></pre>
<p>块</p>
<pre><code>{% raw %}
    &lt;ul&gt;
    {% for item in seq %}
        &lt;li&gt;{{ item }}&lt;/li&gt;
    {% endfor %}
    &lt;/ul&gt;
{% endraw %}
</code></pre>
<h3 id="if分支判断">if分支判断</h3>
<pre><code>{% if loop.index is divisibleby 3 %}
{% if loop.index is divisibleby(3) %}


{% if kenny.sick %}
    Kenny is sick.
{% elif kenny.dead %}
    You killed Kenny!  You bastard!!!
{% else %}
    Kenny looks okay --- so far
{% endif %}
</code></pre>
<p>可用的条件操作符</p>
<pre><code>==  Compares two objects for equality.
!=  Compares two objects for inequality.
&gt;   true if the left hand side is greater than the right hand side.
&gt;=  true if the left hand side is greater or equal to the right hand side.
&lt;   true if the left hand side is lower than the right hand side.
&lt;=  true if the left hand side is lower or equal to the right hand side.
</code></pre>
<p>逻辑连接符</p>
<pre><code>and     Return true if the left and the right operand is true.
or      Return true if the left or the right operand is true.
not     negate a statement (see below).
(expr)  group an expression.
</code></pre>
<p>内置测试列表 <a href="http://jinja.pocoo.org/docs/templates/#builtin-tests">http://jinja.pocoo.org/docs/templates/#builtin-tests</a></p>
<pre><code>callable(object)
defined(value)
        {% if variable is defined %}
            value of variable: {{ variable }}
        {% else %}
            variable is not defined
        {% endif %}
divisibleby(value, num)
escaped(value)
even(value)
iterable(value)
lower(value)
mapping(value)
none(value)
number(value)
odd(value)
sameas(value, other)
    {% if foo.attribute is sameas false %}
        the foo attribute really is the `False` singleton
    {% endif %}
sequence(value)
string(value)
undefined(value)
upper(value)
</code></pre>
<h3 id="for循环">for循环</h3>
<pre><code>&lt;h1&gt;Members&lt;/h1&gt;
&lt;ul&gt;
{% for user in users %}
&lt;li&gt;{{ user.username|e }}&lt;/li&gt;
{% endfor %}
&lt;/ul&gt;

&lt;ul&gt;
{% for user in users %}
    &lt;li&gt;{{ user.username|e }}&lt;/li&gt;
{% else %}
    &lt;li&gt;&lt;em&gt;no users found&lt;/em&gt;&lt;/li&gt;
{% endfor %}
&lt;/ul&gt;
</code></pre>
<p>循环体中可用变量</p>
<pre><code>Variable        Description
loop.index      The current iteration of the loop. (1 indexed)
loop.index0     The current iteration of the loop. (0 indexed)
loop.revindex   The number of iterations from the end of the loop (1 indexed)
loop.revindex0  The number of iterations from the end of the loop (0 indexed)
loop.first      True if first iteration.
loop.last       True if last iteration.
loop.length     The number of items in the sequence.
loop.cycle      A helper function to cycle between a list of sequences. See the explanation below.
loop.depth      Indicates how deep in deep in a recursive loop the rendering currently is. Starts at level 1
loop.depth0     Indicates how deep in deep in a recursive loop the rendering currently is. Starts at level 0
</code></pre>
<h3 id="filters">filters</h3>
<pre><code>{{ name|striptags|title }}
</code></pre>
<p>一系列方法
内置filter列表 <a href="http://jinja.pocoo.org/docs/templates/#builtin-filters">http://jinja.pocoo.org/docs/templates/#builtin-filters</a></p>
<pre><code>abs(number)
attr(obj, name)   foo|attr(&quot;bar&quot;) works like foo[&quot;bar&quot;]
                  just that always an attribute is returned and items are not looked up
batch(value, linecount, fill_with=None)
capitalize(s)
center(value, width=80)
default(value, default_value=u'', boolean=False)
                {{ my_variable|default('my_variable is not defined') }}
                {{ ''|default('the string was empty', true) }} 当变量false是，用default替换
dictsort(value, case_sensitive=False, by='key')
escape(s)
filesizeformat(value, binary=False)
first(seq)  Return the first item
float(value, default=0.0)
forceescape(value) Enforce HTML escaping
format(value, *args, **kwargs)
                {{ &quot;%s - %s&quot;|format(&quot;Hello?&quot;, &quot;Foo!&quot;) }}
groupby(value, attribute)
indent(s, width=4, indentfirst=False)
                {{ mytext|indent(2, true) }}
int(value, default=0)
join(value, d=u'', attribute=None)
                {{ [1, 2, 3]|join('|') }}
last(seq)
length(object)
list(value)
lower(s)
map()
        {{ users|map(attribute='username')|join(', ') }}
pprint(value, verbose=False)
random(seq)
reject() Filters a sequence of objects by appying a test
        {{ numbers|reject(&quot;odd&quot;) }}
rejectattr()
        {{ users|rejectattr(&quot;is_active&quot;) }}
        {{ users|rejectattr(&quot;email&quot;, &quot;none&quot;) }}
replace(s, old, new, count=None)
        {{ &quot;aaaaargh&quot;|replace(&quot;a&quot;, &quot;d'oh, &quot;, 2) }}
reverse(value)
round(value, precision=0, method='common')
        {{ 42.55|round }} 43
        {{ 42.55|round(1, 'floor') }} 42.5
safe(value)
        automatic escaping enabled this variable will not be escaped
select()
        {{ numbers|select(&quot;odd&quot;) }}
selectattr()
        {{ users|selectattr(&quot;is_active&quot;) }}
        {{ users|selectattr(&quot;email&quot;, &quot;none&quot;) }}
slice(value, slices, fill_with=None)
sort(value, reverse=False, case_sensitive=False, attribute=None)
        {% for item in iterable|sort %}
        {% for item in iterable|sort(attribute='date') %}

string(object)
        x|string()
striptags(value)
sum(iterable, attribute=None, start=0)
        Total: {{ items|sum(attribute='price') }}
title(s)
trim(value)
truncate(s, length=255, killwords=False, end='...')
        {{ &quot;foo bar&quot;|truncate(5) }}   &quot;foo ...&quot;
        {{ &quot;foo bar&quot;|truncate(5, True) }}   &quot;foo b...&quot;
upper(s)
urlencode(value)
urlize(value, trim_url_limit=None, nofollow=False)
wordcount(s)
wordwrap(s, width=79, break_long_words=True, wrapstring=None)
xmlattr(d, autospace=True)
</code></pre>
<h3 id="macros宏">Macros宏</h3>
<p>创建</p>
<pre><code>{% macro input(name, value='', type='text', size=20) -%}
    &lt;input type=&quot;{{ type }}&quot; name=&quot;{{ name }}&quot; value=&quot;{{
        value|e }}&quot; size=&quot;{{ size }}&quot;&gt;
{%- endmacro %}
</code></pre>
<p>使用</p>
<pre><code>&lt;p&gt;{{ input('username') }}&lt;/p&gt;
&lt;p&gt;{{ input('password', type='password') }}&lt;/p&gt;
</code></pre>
<p>可以在其他模板使用，需要import</p>
<p>定义</p>
<pre><code>{% macro input(name, value='', type='text') -%}
    &lt;input type=&quot;{{ type }}&quot; value=&quot;{{ value|e }}&quot; name=&quot;{{ name }}&quot;&gt;
{%- endmacro %}

{%- macro textarea(name, value='', rows=10, cols=40) -%}
    &lt;textarea name=&quot;{{ name }}&quot; rows=&quot;{{ rows }}&quot; cols=&quot;{{ cols
        }}&quot;&gt;{{ value|e }}&lt;/textarea&gt;
{%- endmacro %}
</code></pre>
<p>使用</p>
<pre><code>{% import 'forms.html' as forms %}
&lt;dl&gt;
    &lt;dt&gt;Username&lt;/dt&gt;
    &lt;dd&gt;{{ forms.input('username') }}&lt;/dd&gt;
    &lt;dt&gt;Password&lt;/dt&gt;
    &lt;dd&gt;{{ forms.input('password', type='password') }}&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;{{ forms.textarea('comment') }}&lt;/p&gt;
</code></pre>
<p>或者是</p>
<pre><code>{% from 'forms.html' import input as input_field, textarea %}
</code></pre>
<p>宏与宏之间的交互</p>
<pre><code>{% macro render_dialog(title, class='dialog') -%}
    &lt;div class=&quot;{{ class }}&quot;&gt;
        &lt;h2&gt;{{ title }}&lt;/h2&gt;
        &lt;div class=&quot;contents&quot;&gt;
            {{ caller() }}
        &lt;/div&gt;
    &lt;/div&gt;
{%- endmacro %}

{% call render_dialog('Hello World') %}
    This is a simple dialog rendered by using a macro and
    a call block.
{% endcall %}
</code></pre>
<h3 id="模板继承">模板继承</h3>
<p>父模板</p>
<pre><code>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot;&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
&lt;head&gt;
    {% block head %}
    &lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot; /&gt;
    &lt;title&gt;{% block title %}{% endblock %} - My Webpage&lt;/title&gt;
    {% endblock %}
&lt;/head&gt;
&lt;body&gt;
    &lt;div id=&quot;content&quot;&gt;{% block content %}{% endblock %}&lt;/div&gt;
    &lt;div id=&quot;footer&quot;&gt;
        {% block footer %}
        &amp;copy; Copyright 2008 by &lt;a href=&quot;http://domain.invalid/&quot;&gt;you&lt;/a&gt;.
        {% endblock %}
    &lt;/div&gt;
&lt;/body&gt;
</code></pre>
<p>子模板</p>
<pre><code>{% extends &quot;base.html&quot; %}
{% block title %}Index{% endblock %}
{% block head %}
    {{ super() }}
    &lt;style type=&quot;text/css&quot;&gt;
        .important { color: #336699; }
    &lt;/style&gt;
{% endblock %}
{% block content %}
    &lt;h1&gt;Index&lt;/h1&gt;
    &lt;p class=&quot;important&quot;&gt;
    Welcome on my awesome homepage.
    &lt;/p&gt;
{% endblock %}
</code></pre>
<p>注意，可以有更好的可读性</p>
<pre><code>{% block sidebar %}
    {% block inner_sidebar %}
        ...
    {% endblock inner_sidebar %}
{% endblock sidebar %}
</code></pre>
<h3 id="模板包含include">模板包含include</h3>
<pre><code>{% include 'header.html' %}
    Body
{% include 'footer.html' %}

#jinja2.2
{% include &quot;sidebar.html&quot; ignore missing %}
{% include &quot;sidebar.html&quot; ignore missing with context %}
{% include &quot;sidebar.html&quot; ignore missing without context %}
</code></pre>
<h3 id="其他">其他</h3>
<p>1.一些列表方法</p>
<p><a href="http://jinja.pocoo.org/docs/templates/#list-of-global-functions">http://jinja.pocoo.org/docs/templates/#list-of-global-functions</a></p>
<p>2.其他操作符</p>
<pre><code>in      Perform sequence / mapping containment test. Returns true if the left operand is contained in the right. {{ 1 in [1, 2, 3] }} would for example return true.
is      Performs a test.
|       Applies a filter.
~       Converts all operands into strings and concatenates them. {{ &quot;Hello &quot; ~ name ~ &quot;!&quot; }} would return (assuming name is 'John') Hello John!.
()      Call a callable: {{ post.render() }}. Inside of the parentheses you can use positional arguments and keyword arguments like in python: {{ post.render(user, full=true) }}.
. / []  Get an attribute of an object. (See Variables)
</code></pre>
<p>3.range, 可以正常使用range</p>
<pre><code>{% for n in range(n) %}
    {{n}}
{% endfor %}
</code></pre>
<hr>
<p>The end. 未完待续</p>
<p>wklken</p>
<p><a href="http://www.wklken.me">http://www.wklken.me</a></p>
<p>2013-12-21</p>
]]></content>
		</item>
		
		<item>
			<title>Python模板-Mako</title>
			<link>https://wklken.me/posts/2013/12/14/python-template-mako.html</link>
			<pubDate>Sat, 14 Dec 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/12/14/python-template-mako.html</guid>
			<description>一直使用Jinja2，前段时间听说mako，一试 大同小异，天下模板都差不多 要写代码测试，文档先行 资源 官网 http://www.makotemplates.org/ 文档 http://docs.makotemplates.org/en/latest/ 文档翻译 Mako模板入门 http://help.42qu.com/code/mako.html 安</description>
			<content type="html"><![CDATA[<p><img src="/imgs/python/mako.png" alt="mako"></p>
<p>一直使用Jinja2，前段时间听说mako，一试</p>
<p>大同小异，天下模板都差不多</p>
<p>要写代码测试，文档先行</p>
<hr>
<h3 id="资源">资源</h3>
<p>官网 <a href="http://www.makotemplates.org/">http://www.makotemplates.org/</a></p>
<p>文档 <a href="http://docs.makotemplates.org/en/latest/">http://docs.makotemplates.org/en/latest/</a></p>
<p>文档翻译 Mako模板入门 <a href="http://help.42qu.com/code/mako.html">http://help.42qu.com/code/mako.html</a></p>
<h3 id="安装">安装</h3>
<pre><code>pip install mako
</code></pre>
<h3 id="helloworld">HelloWorld</h3>
<pre><code>from mako.template import Template

mytemplate = Template(&quot;hello world!&quot;)
print mytemplate.render()

-------------------------

from mako.template import Template
print Template(&quot;hello ${data}!&quot;).render(data=&quot;world&quot;)
</code></pre>
<h3 id="语法">语法</h3>
<pre><code>输出变量 ${x}

数学计算 ${1+1}
the contents within the ${} tag are evaluated by Python directly, so full expressions are OK

filter
${&quot;test&quot;|u}
${&quot;test&quot;|u,trim}
内置filter列表
    u : URL escaping, provided by urllib.quote_plus(string.encode('utf-8'))
    h : HTML escaping, provided by markupsafe.escape(string)
    x : XML escaping
    trim : whitespace trimming, provided by string.strip()
    entity : produces HTML entity references for applicable strings, derived from htmlentitydefs
    unicode (str on Python 3): produces a Python unicode string (this function is applied by default)
    decode.&lt;some encoding&gt; : decode input into a Python unicode with the specified encoding
    n : disable all default filtering; only filters specified in the local expression tag will be applied.

分支
% if x == 5:
    abcd
% endif

循环
% for a in ['1', '2', '3']:
    % if a == '1':
      abc
    % elif a == '2':
      def
    % else:
      gh
    % endif
$ endfor

Python语法
this is a template
&lt;%
    x = db.get_resource('foo')
    y = [z.element for z in x if x.frobnizzle==5]
%&gt;
% for elem in y:
    element: ${elem}
% endfor

换行

加 / 强制不换行


设置变量
% for item in ('apple', 'banana'):
    &lt;%
        isBanana = False
    %&gt;
    % if item == 'banana':
    &lt;%
        isBanana = True
    %&gt;
    %endif
    % if isBanana:
        &lt;span&gt; Bought a banana&lt;/span&gt;
    %endif
%endfor
</code></pre>
<h3 id="注释">注释</h3>
<pre><code>## 这是一个注释.
...text ...

多行
&lt;%doc&gt;
这里是注释
更多注释
&lt;/%doc&gt;
</code></pre>
<h3 id="模块级别语句">模块级别语句</h3>
<p>&lt;% %&gt; 的一个变体是 &lt;%! %&gt;，代表模块级别的代码块。其中的代码会在模板的模块级别执行，而不是在模板的 rendering 函数中。</p>
<pre><code>&lt;%!
import mylib
import re

def filter(text):
    return re.sub(r'^@', '', text)
%&gt;
</code></pre>
<h3 id="标签">标签</h3>
<pre><code>定义了当前模板的总体特性，包括缓存参数，以及模板被调用时期待的参数列表（非必须）
&lt;%page args=&quot;x, y, z='default'&quot;/&gt;
&lt;%page cached=&quot;True&quot; cache_type=&quot;memory&quot;/&gt;


&lt;%include file=&quot;header.html&quot;/&gt;
hello world
&lt;%include file=&quot;footer.html&quot;/&gt;

%def 标签用于定义包含一系列内容的一个 Python 函数，此函数在当前模板的其他某个地方被调用到
&lt;%def name=&quot;myfunc(x)&quot;&gt;
this is myfunc, x is ${x}
&lt;/%def&gt;
${myfunc(7)}

&lt;%block filter=&quot;h&quot;&gt;
some &lt;html&gt; stuff.
&lt;/%block&gt;
&lt;%block name=&quot;header&quot;&gt;
    &lt;h2&gt;&lt;%block name=&quot;title&quot;/&gt;&lt;/h2&gt;
&lt;/%block&gt;

Mako 中的 %namespace 等价于 Python 里的 import 语句。它允许访问其他模板文件的所有 rendering 函数和元数据
&lt;%namespace file=&quot;functions.html&quot; import=&quot;*&quot;/&gt;

&lt;%inherit file=&quot;base.html&quot;/&gt;

处理多行注释：
&lt;%doc&gt;
    these are comments
    more comments
&lt;/%doc&gt;

该标签使得 Mako 的词法器对模板指令的常规解析动作停止，并以纯文本的形式返回其整个内容部分
&lt;%text filter=&quot;h&quot;&gt;
heres some fake mako ${syntax}
&lt;%def name=&quot;x()&quot;&gt;${x}&lt;/%def&gt;
&lt;/%text&gt;
</code></pre>
<p>有时你想中途停止执行一个模板或者 &lt;%def&gt; 方法，只返回已经收集到的文本信息，可以通过在 Python 代码块中使用 return 语句来完成</p>
<pre><code>% if not len(records):
    No records found.
    &lt;% return %&gt;
% endif
</code></pre>
<h3 id="文件template">文件template</h3>
<p>为提高性能，从文件中加载的 Template, 可以将它产生的模块的源代码以普通 python 模块文件的形式(.py)，</p>
<p>缓存到文件系统中。只要加一个参数 module_directory 即可做到这一点：</p>
<pre><code>from mako.template import Template

mytemplate = Template(filename='/docs/mytmpl.txt', module_directory='/tmp/mako_modules')
print mytemplate.render()
</code></pre>
<p>当上述代码被 render 的时候，会创建文件 /tmp/mako_modules/docs/mytmpl.txt.py.</p>
<p>下一次 Template 对象被用同样参数调用的时候，就会直接重用该模块文件。</p>
<h3 id="文件templatelookup">文件TemplateLookup</h3>
<pre><code>#有一个对 header.txt 文件的包含引用。而从何处去查找 header.txt, 则由 TemplateLookup 指明，是 &quot;/docs&quot; 目录
from mako.template import Template
from mako.lookup import TemplateLookup

mylookup = TemplateLookup(directories=['/docs'])
mytemplate = Template(&quot;&quot;&quot;&lt;%include file=&quot;header.txt&quot;/&gt; hello world!&quot;&quot;&quot;, lookup=mylookup)


--------------

#可以直接通过 TemplateLookup 来获取模板对象，利用 TemplateLookup 的 get_template 方法，
#并传递模板的 URI 作为参数
mylookup = TemplateLookup(directories=['/docs'], output_encoding='utf-8', encoding_errors='replace')
mytemplate = mylookup.get_template(&quot;foo.txt&quot;)
print mytemplate.render()

-------------
参数
mylookup = TemplateLookup(directories=['/docs'], output_encoding='utf-8', encoding_errors='replace', , collection_size=500)
TemplateLookup 同时也会在内存中缓存一组模板，所以并不是每一次请求都会导致模板的重新编译和模块重新加载。默认 TemplateLookup 的大小没有限制，但你可以通过 collection_size 参数来限制它
以上的 lookup 会持续加载模板到内存中，直到达到 500 的时候，它就会清除掉一定比例的模板缓存项，根据“最近最少访问”原则

另一个 TemplateLookup 相关的标志是  filesystem_checks. 默认为 True,
每一次 get_template() 方法返回模板后，原始的模板文件的 revision time 会和上次加载模板的时间做对比，
如果文件更新，则会加载其内容，并重新编译该模板。
在生产环境下，设置 filesystem_checks 为 False 可以带来一定的性能提升（和具体的文件系统有关）
</code></pre>
<h3 id="自己创建context">自己创建context</h3>
<pre><code>from mako.template import Template
from mako.runtime import Context
from StringIO import StringIO

mytemplate = Template(&quot;hello, ${name}!&quot;)
buf = StringIO()
ctx = Context(buf, name=&quot;jack&quot;)
mytemplate.render_context(ctx)
print buf.getvalue()
</code></pre>
<h3 id="其他">其他</h3>
<p>1.解决mako中文乱码问题</p>
<pre><code>TemplateLookup(... , output_encoding='utf-8', ...)
Template(..., input_encoding='utf-8')
又在mako的模板文件的首行添加
## -*- encoding:utf8 -*-
</code></pre>
<hr>
<p>The end. 未完待续</p>
<p>wklken</p>
<p><a href="http://www.wklken.me">http://www.wklken.me</a></p>
<p>2013-12-14</p>
]]></content>
		</item>
		
		<item>
			<title>Git操作小结</title>
			<link>https://wklken.me/posts/2013/12/01/git-base.html</link>
			<pubDate>Sun, 01 Dec 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/12/01/git-base.html</guid>
			<description>git操作小结，资料来源于网络 教程资源 在线教程(重点推荐) TryGit LearnGitBranchin 廖雪峰git教程 githug (通关游戏，攻略) Git Tutorials Git Immersion 资源 Git 参考手册 Pro git Git Pocket Guide git自下</description>
			<content type="html"><![CDATA[<p>git操作小结，资料来源于网络</p>
<h3 id="教程资源">教程资源</h3>
<blockquote>
<p>在线教程(重点推荐)</p>
</blockquote>
<p><a href="http://try.github.io/levels/1/challenges/1">TryGit</a></p>
<p><a href="http://pcottle.github.io/learnGitBranching/">LearnGitBranchin</a></p>
<p><a href="https://www.gitbook.io/book/lvwzhen/Git-Tutorial">廖雪峰git教程</a></p>
<p><a href="https://github.com/Gazler/githug">githug</a> (通关游戏，<a href="http://fancyoung.com/blog/githug-cheat-sheet/">攻略</a>)</p>
<p><a href="https://www.atlassian.com/git/tutorial/git-basics">Git Tutorials</a></p>
<p><a href="http://gitimmersion.com/">Git Immersion</a></p>
<blockquote>
<p>资源</p>
</blockquote>
<p><a href="http://gitref.org/zh/basic/">Git 参考手册</a></p>
<p><a href="http://git-scm.com/book/zh">Pro git</a></p>
<p><a href="http://chimera.labs.oreilly.com/books/1230000000561/index.html">Git Pocket Guide</a></p>
<p><a href="http://ikandou.com/io/book/48272048/">git自下而上方法-爱看豆</a></p>
<blockquote>
<p>文章</p>
</blockquote>
<p><a href="http://blog.jobbole.com/50603/">写给Git初学者的7个建议</a></p>
<p><a href="http://rogerdudler.github.io/git-guide/index.zh.html">git简易指南</a></p>
<p><a href="http://marklodato.github.io/visual-git-guide/index-zh-cn.html">图解git</a></p>
<p><a href="http://youngsterxyf.github.io/2013/09/28/learning-git-internals-by-example/">通过示例学习git构造</a></p>
<p><a href="http://www.ruanyifeng.com/blog/2012/07/git.html">git分支管理策略</a></p>
<p><a href="http://luolei.org/2013/09/git-config-advanced/">Git log diff config高级进阶</a></p>
<p><a href="http://www.oschina.net/translate/10-useful-advanced-git-commands">10个很有用的高级git命令</a></p>
<p><a href="http://ux.etao.com/posts/711">高富帅们的git技巧</a></p>
<p><a href="http://blog.jobbole.com/43288/">一些实用的GitHub模式</a></p>
<h3 id="git-config">git config</h3>
<p>git配置文件~/.gitconfig</p>
<pre><code>#查看帮助
git config --help

git config --global user.name &quot;Your Name Here&quot;

其他常用config
git config --global user.name &quot;robbin&quot;
git config --global user.email &quot;fankai#gmail.com&quot;
git config --global color.ui true

#alias
git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.br branch

git config --global core.editor &quot;mate -w&quot;    # 设置Editor使用textmate
git config -1 #列举所有配置
</code></pre>
<h3 id="仓库基本操作">仓库基本操作</h3>
<p>1.检出一个项目</p>
<pre><code>git clone path-to-git-repository
</code></pre>
<p>2.更新本地仓库到最新改动</p>
<pre><code>git pull
</code></pre>
<p>2.将现有项目推送到远程</p>
<pre><code>cd test   #跳到要提交的目录下
git init  #初始化git
git add . #将所有文件加入到索引
git commit #提交到HEAD
git remote add origin &lt;server&gt; #增加到remote
git push origin master #推送过去! DONE
</code></pre>
<p>3.丢弃本地所有改动与提交，获取服务器上最新版本并将主干分支指向它</p>
<pre><code>git fetch origin
git reset --hard origin/master
</code></pre>
<h3 id="基本操作">基本操作</h3>
<p><img src="/imgs/git-base/git-flow-structure.png" alt="git-flow-structure"></p>
<p>1.添加(CurrentDir -&gt; Stage)</p>
<pre><code>git add file_name
git add *
</code></pre>
<p>3.删除(CurrentDir -&gt; Stage)</p>
<pre><code>git rm file_name  #从版本控制中删除，并删除磁盘上的文件
git rm --cached file_name #不删除磁盘上的
</code></pre>
<p>3.取消add/rm(Stage -&gt; CurrentDir)</p>
<pre><code>git reset HEAD file_name
</code></pre>
<p>2.提交(Stage -&gt; HEAD)</p>
<pre><code>git commit file_name -m '提交信息'
</code></pre>
<p>4.推送改动(HEAD -&gt; Remote)</p>
<pre><code>git push origin master
</code></pre>
<p>4.diff</p>
<pre><code>git diff  #diff CurrentDir Stage, 查看有哪些需要add
git diff --cached/--staged     #哪些需要commit
</code></pre>
<p>5.log</p>
<pre><code>git log  #查看日志
</code></pre>
<p>7.替换掉本地改动(Stage -&gt; CurrentDir)</p>
<pre><code>git checkout -- &lt;filename&gt;
</code></pre>
<p>8.mv</p>
<pre><code>git mv old_name new_name #重命名
</code></pre>
<p>BEGIN: add -&gt; commit -&gt; push -&gt; DONE</p>
<p>文件变化状态图</p>
<p><img src="/imgs/git-base/git-file-status-lifecycle.png" alt="git-file-status-lifecycle"></p>
<h3 id="分支操作">分支操作</h3>
<p><img src="/imgs/git-base/git-branch.png" alt="git-branch"></p>
<p>1.创建分支</p>
<pre><code>git branch new_br_name #创建
git branch             #查看当前有的分支

git checkout new_br_name #切换到新分支

git checkout -b new_br_name #创建并切过去
</code></pre>
<p>2.远程分支</p>
<pre><code>git push origin new_br_name  #推送branch到远端维护起来

#删除
git push origin : &lt;new_br_name&gt;
or
git push origin --delete &lt;new_br_name&gt;
</code></pre>
<p>3.合并分支(merge)</p>
<pre><code>git branch  #查看当前分支
git checkout master #切换回主干
git merge new_br_name  #合并new_br_name分支到主干, 自动合并

1.没冲突(no conflicts) -&gt; fine -&gt; commit -&gt; push to remote

2.有冲突(conflicts)
git diff   #查看当前哪些文件有冲突, 标识 unmerged
vim xxx    #手动编辑解决冲突

    冲突文件中标识
    &lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD:file.txt
    Hello world  #当前branch的
    =======
    Goodbye      #要合并branch的
    &gt;&gt;&gt;&gt;&gt;&gt;&gt; 77976da35a11db4580b80ae27e8d65caf5208086:file.txt

git add xxx #加入

-&gt; 解决所有冲突之后 -&gt; commit -&gt; push to remote
</code></pre>
<p>3.删除无用分支</p>
<pre><code>git branch -d new_br_name  #只能删除已经被当前分支合并的分支
git branch -D new_br_name  #强删
</code></pre>
<p>4.撤销一个合并</p>
<pre><code>git reset --hard HEAD
</code></pre>
<h3 id="tag操作">tag操作</h3>
<p>BP:在发布之前，创建标签</p>
<p>1.创建删除</p>
<pre><code>创建
git tag tag_name &lt;commit ID&gt; #commit ID可以通过git log来查看

删除
git tag -d tag_name
</code></pre>
<p>3.远程tag</p>
<pre><code>推送所有tags
git push --tags

删除
git push origin --delete tag &lt;tag_name&gt;
</code></pre>
<h3 id="忽略某些文件">忽略某些文件</h3>
<p>项目中那些不需要的文件(untracked),可以忽略</p>
<p>顶层工作目录中添加一个叫&quot;.gitignore&quot;的文件 <a href="https://www.kernel.org/pub/software/scm/git/docs/gitignore.html">语法文档</a></p>
<pre><code># 以'#' 开始的行，被视为注释.
# 忽略掉所有文件名是 foo.txt 的文件.
foo.txt
# 忽略所有生成的 html 文件,
*.html
# foo.html是手工维护的，所以例外.
!foo.html
#  忽略所有.o 和 .a文件.
*.[oa]
</code></pre>
<h3 id="常见问题">常见问题</h3>
<p>1.如何让git能处理汉字文件名</p>
<p>git默认quote任何非ascii文件名字符,想要支持非</p>
<pre><code>git config --global core.quotepath false

或者在$HOME/.gitconfig 配置
[core]
    quotepath = false
</code></pre>
<p>2.git add -A 和 git add .的区别</p>
<pre><code>&quot;git add -A&quot;  = &quot;git add .; git add -u&quot;.

- git add -A stages All
- git add . stages new and modified, without deleted
- git add -u stages modified and deleted, without new
</code></pre>
<p>3.怎么配置git结果显示颜色</p>
<pre><code>[color]
#开启着色功能
    status = auto
    diff = auto
    branch = auto
    interactive = auto
</code></pre>
<p>4.获得帮助</p>
<pre><code>git help &lt;command&gt;  # 显示command的help
</code></pre>
<h3 id="其他">其他</h3>
<p>1.<a href="http://luolei.org/2013/08/better-git-log/">更好的git log</a></p>
<pre><code>git config --global alias.lg &quot;log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset' --abbrev-commit&quot;
#然后使用 git lg
</code></pre>
<p>2.自己搭建代码库</p>
<p>类似github的应用，gitlab(google之)</p>
<p>3.git-flow</p>
<p><a href="http://danielkummer.github.io/git-flow-cheatsheet/index.zh_CN.html">git-flow备忘清单</a></p>
<p>4.基于git的wiki</p>
<p>gollum(google之)</p>
<hr>
<p>wklken</p>
<p>2013-12-01</p>
]]></content>
		</item>
		
		<item>
			<title>[摘要]Python 最佳实践指南</title>
			<link>https://wklken.me/posts/2013/11/25/summary-of-the-hitchhikers-guide-2-python.html</link>
			<pubDate>Mon, 25 Nov 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/11/25/summary-of-the-hitchhikers-guide-2-python.html</guid>
			<description>文档地址 The Hitchhiker&amp;rsquo;s Guide to Python! 这份文档 目标对象：入门后，有一定基础的Pythonista 关键词：最佳实践，Pythonic，各类工具介绍 粗粗粗略地过了一</description>
			<content type="html"><![CDATA[<p>文档地址 <a href="http://docs.python-guide.org/en/latest/">The Hitchhiker&rsquo;s Guide to Python!</a></p>
<p>这份文档</p>
<pre><code>目标对象：入门后，有一定基础的Pythonista
关键词：最佳实践，Pythonic，各类工具介绍
</code></pre>
<p>粗粗粗略地过了一遍，大体捞了一些东西出来，大段大段英文太费眼了，回头细读在更新进来</p>
<p>浓缩版，20分钟可大体过完，然后根据自己需要去看详细的吧</p>
<p>整体内容还是很不错的，建议细读英文</p>
<p>PS:文档含有巨量的TODO(没写空白着待补充的)，不过但从目录上来看还是很强大滴，相信完善后，会成为一份很牛逼的指南(难度比官方指南高一点点)</p>
<hr>
<h1 id="第零部分-getting-started">第零部分 Getting Started</h1>
<p><a href="http://docs.python-guide.org/en/latest/#getting-started">链接</a></p>
<p>不解释，不翻译，自个看&hellip;.真的没啥(每本入门书籍第一章&hellip;)</p>
<hr>
<h1 id="第一部分-writing-great-code">第一部分 Writing Great Code</h1>
<h3 id="structuring-your-project">Structuring Your Project</h3>
<p><a href="http://docs.python-guide.org/en/latest/writing/structure/#structuring-your-project">链接</a></p>
<p>import 最佳实践</p>
<p>Very bad</p>
<pre><code>[...]
from modu import *
[...]
x = sqrt(4)  # Is sqrt part of modu? A builtin? Defined above?
</code></pre>
<p>Better</p>
<pre><code>from modu import sqrt
[...]
x = sqrt(4)  # sqrt may be part of modu, if not redefined in between
</code></pre>
<p>Best</p>
<pre><code>import modu
[...]
x = modu.sqrt(4)  # sqrt is visibly part of modu's namespace
</code></pre>
<p>Python中关于OOP的 <a href="http://docs.python-guide.org/en/latest/writing/structure/#object-oriented-programming">观点</a></p>
<p>Decorators</p>
<pre><code>def foo():
    # do something

def decorator(func):
    # manipulate func
    return func

foo = decorator(foo)  # Manually decorate

@decorator
def bar():
    # Do something
# bar() is decorated
</code></pre>
<p>动态类型(Dynamic typing)</p>
<p>Avoid using the same variable name for different things.</p>
<p>Bad</p>
<pre><code>a = 1
a = 'a string'
def a():
    pass  # Do something
</code></pre>
<p>Good</p>
<pre><code>count = 1
msg = 'a string'
def func():
    pass  # Do something
</code></pre>
<p>It is better to use different names even for things that are related, when they have a different type:</p>
<pre><code>Bad

items = 'a b c d'  # This is a string...
items = items.split(' ')  # ...becoming a list
items = set(items)  # ...and then a set
</code></pre>
<p>可变和不可变类型(Mutable and immutable types)</p>
<p>字符串拼接最佳实践</p>
<p>Bad</p>
<pre><code># create a concatenated string from 0 to 19 (e.g. &quot;012..1819&quot;)
nums = &quot;&quot;
for n in range(20):
  nums += str(n)   # slow and inefficient
print nums
</code></pre>
<p>Good</p>
<pre><code># create a concatenated string from 0 to 19 (e.g. &quot;012..1819&quot;)
nums = []
for n in range(20):
  nums.append(str(n))
print &quot;&quot;.join(nums)  # much more efficient
</code></pre>
<p>Best</p>
<pre><code># create a concatenated string from 0 to 19 (e.g. &quot;012..1819&quot;)
nums = [str(n) for n in range(20)]
print &quot;&quot;.join(nums)
</code></pre>
<p>join() is not always best
创建新字符串和修改原有字符串</p>
<pre><code>foo = 'foo'
bar = 'bar'

foobar = foo + bar  # This is good
foo += 'ooo'  # This is bad, instead you should do:
foo = ''.join([foo, 'ooo'])
</code></pre>
<p>字符串格式化</p>
<pre><code>foo = 'foo'
bar = 'bar'

foobar = '%s%s' % (foo, bar) # It is OK
foobar = '{0}{1}'.format(foo, bar) # It is better
foobar = '{foo}{bar}'.format(foo=foo, bar=bar) # It is best
</code></pre>
<h3 id="code-style">Code Style</h3>
<p><a href="http://docs.python-guide.org/en/latest/writing/style/#code-style">链接</a></p>
<h4 id="一般概念general-concepts">一般概念(General concepts)</h4>
<p>明确的代码</p>
<p>Bad</p>
<pre><code>def make_complex(*args):
    x, y = args
    return dict(**locals())
</code></pre>
<p>Good</p>
<pre><code>def make_complex(x, y):
    return {'x': x, 'y': y}
</code></pre>
<p>每行一个声明</p>
<p>Bad</p>
<pre><code>print 'one'; print 'two'

if x == 1: print 'one'

if &lt;complex comparison&gt; and &lt;other complex comparison&gt;:
    # do something
</code></pre>
<p>Good</p>
<pre><code>print 'one'
print 'two'

if x == 1:
    print 'one'

cond1 = &lt;complex comparison&gt;
cond2 = &lt;other complex comparison&gt;
if cond1 and cond2:
    # do something
</code></pre>
<p>函数参数</p>
<pre><code>#不解释了
位置参数，默认参数，*args, **args
</code></pre>
<p>Avoid the magical wand(这个肿么翻&hellip;)</p>
<pre><code>原因：
Python comes with a very rich set of hooks and tools allowing to do almost any kind of tricky tricks

建议：
it is always better to use the most straightforward way to achieve your goal

感受一下:
We consider that a Python developer should know about these nearly infinite possibilities,
because it grows the confidence that no hard-wall will be on the way.
However, knowing how to use them and particularly when not to use them is the most important.

Like a Kungfu master, a Pythonista knows how to kill with a single finger, and never to actually do it.

其实就是告诉你，骚年，这玩意你要去学习去了解去掌握，目的是增强实力保持自信，但是不要去用啊
(说的原子弹吧....)
</code></pre>
<h4 id="方言idioms">方言(Idioms)</h4>
<p>Idiomatic Python code is often referred to as being Pythonic.</p>
<p>列举了一些：</p>
<p>Unpacking</p>
<pre><code>for index, item in enumerate(some_list):
    # do something with index and item

a, b = b, a

a, (b, c) = 1, (2, 3)
</code></pre>
<p>忽略接收变量,这里用的是两个下划线，原因
<a href="http://docs.python-guide.org/en/latest/writing/style/#create-an-ignored-variable">http://docs.python-guide.org/en/latest/writing/style/#create-an-ignored-variable</a></p>
<pre><code>filename = 'foobar.txt'
basename, __, ext = filename.rpartition('.')
</code></pre>
<p>同一个元素创建一个长度为N的列表</p>
<pre><code>four_nones = [None] * 4
</code></pre>
<p>创建一个长度N的嵌套列表</p>
<pre><code>four_lists = [[] for __ in xrange(4)]
</code></pre>
<p>由列表拼接字符串</p>
<pre><code>letters = ['s', 'p', 'a', 'm']
word = ''.join(letters)
</code></pre>
<p>快速查找</p>
<pre><code>d = {'s': [], 'p': [], 'a': [], 'm': []}
l = ['s', 'p', 'a', 'm']

def lookup_dict(d): #O(1)
    return 's' in d

def lookup_list(l): #O(n)
    return 's' in l
</code></pre>
<p>Zen of Python</p>
<pre><code>import this
</code></pre>
<p>PEP8</p>
<pre><code>$ pip install pep8
$ pep8 optparse.py
optparse.py:69:11: E401 multiple imports on one line
optparse.py:77:1: E302 expected 2 blank lines, found 1
</code></pre>
<h3 id="惯例conventions">惯例(Conventions)</h3>
<p>判断值是否等于常量</p>
<p>Bad:</p>
<pre><code>if attr == True:
    print 'True!'

if attr == None:
    print 'attr is None!'
</code></pre>
<p>Good:</p>
<pre><code># Just check the value
if attr:
    print 'attr is truthy!'

# or check for the opposite
if not attr:
    print 'attr is falsey!'

# or, since None is considered false, explicitly check for it
if attr is None:
    print 'attr is None!'
</code></pre>
<p>获取字典元素</p>
<p>Bad:</p>
<pre><code>d = {'hello': 'world'}
if d.has_key('hello'):
    print d['hello']    # prints 'world'
else:
    print 'default_value'
</code></pre>
<p>Good:</p>
<pre><code>d = {'hello': 'world'}

print d.get('hello', 'default_value') # prints 'world'
print d.get('thingy', 'default_value') # prints 'default_value'

# Or:
if 'hello' in d:
    print d['hello']
</code></pre>
<p>快捷列表操作</p>
<p>Bad:</p>
<pre><code># Filter elements greater than 4
a = [3, 4, 5]
b = []
for i in a:
    if i &gt; 4:
        b.append(i)
</code></pre>
<p>Good:</p>
<pre><code>a = [3, 4, 5]
b = [i for i in a if i &gt; 4]
b = filter(lambda x: x &gt; 4, a)
</code></pre>
<p>Bad:</p>
<pre><code># Add three to all list members.
a = [3, 4, 5]
for i in range(len(a)):
    a[i] += 3
</code></pre>
<p>Good:</p>
<pre><code>a = [3, 4, 5]
a = [i + 3 for i in a]
# Or:
a = map(lambda i: i + 3, a)
</code></pre>
<p>使用enumerate</p>
<pre><code>for i, item in enumerate(a):
    print i, item
# prints
# 0 3
# 1 4
# 2 5
</code></pre>
<p>读文件</p>
<p>Bad:</p>
<pre><code>f = open('file.txt')
a = f.read()
print a
f.close()
</code></pre>
<p>Good:</p>
<pre><code>with open('file.txt') as f:
    for line in f:
        print line
</code></pre>
<p>超长的行</p>
<p>Bad:</p>
<pre><code>my_very_big_string = &quot;&quot;&quot;For a long time I used to go to bed early. Sometimes, \
    when I had put out my candle, my eyes would close so quickly that I had not even \
    time to say “I’m going to sleep.”&quot;&quot;&quot;

from some.deep.module.inside.a.module import a_nice_function, another_nice_function, \
    yet_another_nice_function
</code></pre>
<p>Good:</p>
<pre><code>#受教了....
my_very_big_string = (
    &quot;For a long time I used to go to bed early. Sometimes, &quot;
    &quot;when I had put out my candle, my eyes would close so quickly &quot;
    &quot;that I had not even time to say “I’m going to sleep.”&quot;
)

from some.deep.module.inside.a.module import (
    a_nice_function, another_nice_function, yet_another_nice_function)
</code></pre>
<h3 id="reading-great-code">Reading Great Code</h3>
<p><a href="http://docs.python-guide.org/en/latest/writing/reading/#reading-great-code">链接</a></p>
<p>感受下：The number one thing that Python programmers do is read code.</p>
<p>再感受一把：One of the secrets of becoming a great Python programmer is to read, understand, and comprehend excellent code.</p>
<p>几个推荐阅读源代码项目</p>
<p><a href="https://github.com/gleitz/howdoi">Howdoi</a></p>
<p><a href="https://github.com/mitsuhiko/flask">Flask</a></p>
<p><a href="https://github.com/mitsuhiko/werkzeug">Werkzeug</a></p>
<p><a href="https://github.com/kennethreitz/requests">Requests</a></p>
<p><a href="https://github.com/kennethreitz/tablib">Tablib</a></p>
<h3 id="文档documentation">文档(Documentation)</h3>
<p><a href="http://docs.python-guide.org/en/latest/writing/documentation/#documentation">链接</a></p>
<p>感受一下：Readability is a primary focus for Python developers, in both project and code documentation.</p>
<p>具体还是读原文吧</p>
<p>项目文档组成</p>
<pre><code>1.A README file
  at the root directory should give general information to the users and the maintainers.

  reStructuredText 或 Markdown
2.An INSTALL file
  is less necessary with python

  setup.py

3.A LICENSE file
  should always be present and specify the license under which the software is made available to the public
4.A TODO file or a TODO section in README
  should list the planned development for the code.
5.A CHANGELOG file or section in README
  should compile a short overview of the changes in the code base for the latest versions.
</code></pre>
<p>几种文档工具</p>
<pre><code>Sphinx(听说最强大....)
reStructuredText
Markdown(俺的最爱...)
</code></pre>
<p>代码文档建议</p>
<p>Comments clarify code and begin with a hash (#).</p>
<p>In Python, docstrings describe modules, classes, and functions:</p>
<pre><code>def square_and_rooter(x):
    &quot;&quot;&quot;Returns the square root of self times self.&quot;&quot;&quot;
</code></pre>
<p>注解代码块</p>
<p>Do not use triple-quote strings to comment code.</p>
<p>This is not a good practice, because line-oriented command-line tools such as grep will not be aware that the commented code is inactive.</p>
<p>It is better to add hashes at the proper indentation level for every commented line.</p>
<p>最佳实践:</p>
<pre><code>不用三引号注解代码块
每一行加#来注释
</code></pre>
<h3 id="测试你的代码testing-your-code">测试你的代码(Testing Your Code)</h3>
<p><a href="http://docs.python-guide.org/en/latest/writing/tests/#testing-your-code">链接</a></p>
<p>测试一些通用原则</p>
<pre><code>1.A testing unit should focus on one tiny bit of functionality and prove it correct.
2.Each test unit must be fully independent
3.Try hard to make tests that run fast
4.Learn your tools and learn how to run a single test or a test case
5.Always run the full test suite before a coding session, and run it again after.
6.It is a good idea to implement a hook that runs all tests before pushing code to a shared repository.
7.If you are in the middle of a development session and have to interrupt your work,
  it is a good idea to write a broken unit test about what you want to develop next.
8.The first step when you are debugging your code is to write a new test pinpointing the bug.
9.Use long and descriptive names for testing functions
10.When something goes wrong or has to be changed, and if your code has a good set of tests,
  you or other maintainers will rely largely on the testing suite to fix the problem or modify a given behavior.
11.Another use of the testing code is as an introduction to new developers.
</code></pre>
<p>单元测试(Unittest)</p>
<p>Python内置模块, <a href="http://docs.python.org/library/unittest.html">文档</a></p>
<pre><code>import unittest

def fun(x):
    return x + 1

class MyTest(unittest.TestCase):
    def test(self):
        self.assertEqual(fun(3), 4)
</code></pre>
<p>文档测试(Doctest)</p>
<p>非精细case，只验证主体功能可用</p>
<pre><code>def square(x):
    &quot;&quot;&quot;Squares x.

    &gt;&gt;&gt; square(2)
    4
    &gt;&gt;&gt; square(-2)
    4
    &quot;&quot;&quot;

    return x * x

if __name__ == '__main__':
    import doctest
    doctest.testmod()
</code></pre>
<p>相关工具</p>
<p><a href="http://pytest.org/latest/">py.text</a>
$ pip install pytest</p>
<p><a href="http://readthedocs.org/docs/nose/en/latest/">Nose</a> unittest的扩展
$ pip install nose</p>
<p><a href="http://testrun.org/tox/latest/">tox</a>
$ pip install tox</p>
<p><a href="http://pypi.python.org/pypi/unittest2">Unittest2</a>
$ pip install unittest2</p>
<p><a href="http://www.voidspace.org.uk/python/mock/">mock</a>
$ pip install mock</p>
<h3 id="common-gotchas不懂怎么翻-">Common Gotchas(不懂怎么翻&hellip;╮(╯▽╰)╭ )</h3>
<p>一些新手可能疑惑的例子</p>
<p>两个例子</p>
<p>1.可变默认参数</p>
<p>What You Wrote</p>
<pre><code>def append_to(element, to=[]):
    to.append(element)
    return to
</code></pre>
<p>What You Might Have Expected to Happen</p>
<pre><code>my_list = append_to(12)
print my_list

my_other_list = append_to(42)
print my_other_list

[12]
[42]
</code></pre>
<p>What Does Happen</p>
<pre><code>[12]
[12, 42]
</code></pre>
<p>What You Should Do Instead</p>
<pre><code>def append_to(element, to=None):
    if to is None:
        to = []
    to.append(element)
    return to
</code></pre>
<p>Python默认参数在函数定义处执行一次，而不是每次函数调用时执行。</p>
<p>2.Late Binding Closures(又一个，延迟绑定闭包?)</p>
<p>What You Wrote</p>
<pre><code>def create_multipliers():
    return [lambda x : i * x for i in range(5)] #
</code></pre>
<p>What You Might Have Expected to Happen</p>
<pre><code>for multiplier in create_multipliers():
    print multiplier(2) # 任意一个返回的函数被调用时，内部循环i=4

0
2
4
6
8
</code></pre>
<p>What Does Happen</p>
<pre><code>8
8
8
8
8
</code></pre>
<p>What You Should Do Instead</p>
<pre><code>from functools import partial
from operator import mul

def create_multipliers():
    return [partial(mul, i) for i in range(5)]
</code></pre>
<p>Python的闭包是延时绑定</p>
<h3 id="选择证书choosing-a-license">选择证书(Choosing a License)</h3>
<p><a href="http://docs.python-guide.org/en/latest/writing/license/#choosing-a-license">链接</a></p>
<p>开源证书 <a href="http://opensource.org/licenses/alphabetical">列表</a></p>
<p>证书选择器 <a href="http://three.org/openart/license_chooser/">入口</a></p>
<hr>
<h1 id="第二部分-scenario-guide">第二部分 Scenario Guide</h1>
<p>都是介绍性质的，类似工具目录,而且大部分是空的，目前没详细信息</p>
<p>要了解具体，goole相关关键词吧</p>
<p>具体自己翻吧 <a href="http://docs.python-guide.org/en/latest/#scenario-guide">位置</a></p>
<p>目录：</p>
<h3 id="network-applications">Network Applications</h3>
<p>Http:</p>
<pre><code>Requests
</code></pre>
<p>Distributed Systems</p>
<pre><code>ZeroMQ
RabbitMQ
</code></pre>
<h3 id="web-applications">Web Applications</h3>
<p>Context</p>
<pre><code>WSGI
</code></pre>
<p>Frameworks</p>
<pre><code>Django
Flask
Werkzeug
Tornado
Pyramid
</code></pre>
<p>Web Servers</p>
<pre><code>Nginx
</code></pre>
<p>WSGI Servers</p>
<pre><code>Gunicorn
</code></pre>
<p>Hosting</p>
<pre><code>PasS (Platform as a service)
Heroku
DotCloud
Gondor
</code></pre>
<p>Templating</p>
<pre><code>Jinja2
</code></pre>
<h3 id="html-scraping">HTML Scraping</h3>
<pre><code>lxml
Requests
</code></pre>
<h3 id="command-line-applications">Command Line Applications</h3>
<pre><code>Clint
docopt
</code></pre>
<h3 id="gui-applications">GUI Applications</h3>
<pre><code>Qt
Cocoa
wxPython
GTk
Tk
Kivy
PyjamasDesktop (pyjs Desktop)
Camelot
</code></pre>
<h3 id="databases">Databases</h3>
<pre><code>DB-API
SQLAlchemy
Django ORM
</code></pre>
<h3 id="networking">Networking</h3>
<pre><code>Twisted
PyZMQ
gevent
</code></pre>
<h3 id="systems-administration">Systems Administration</h3>
<pre><code>Fabric
Salt
Psutil
Chef
Puppet
Blueprint
Buildout
</code></pre>
<h3 id="continuous-integration">Continuous Integration</h3>
<pre><code>Jenkins
Buildbot
Mule?
Tox
Travis-CI
</code></pre>
<h3 id="speed">Speed</h3>
<p>C Extensions</p>
<pre><code>GIL
Cython
Pyrex
Shedskin
Numba
</code></pre>
<p>Threading</p>
<pre><code>Threading
Multiprocessing
</code></pre>
<p>###Scientific Applications</p>
<p>Tools</p>
<pre><code>IPython
</code></pre>
<p>Libraries</p>
<pre><code>NumPy
Numba
SciPy
Matplotlib
Pandas
Rpy2
PsychoPy
</code></pre>
<h3 id="image-manipulation">Image Manipulation</h3>
<pre><code>Python Imaging Library(PIL)
</code></pre>
<h3 id="xml-parsing">XML parsing</h3>
<pre><code>untangle
xmltodict
</code></pre>
<hr>
<p>有需要自取&hellip;..</p>
<h1 id="第三部分-shipping-great-code">第三部分 Shipping Great Code</h1>
<p><a href="http://docs.python-guide.org/en/latest/#shipping-great-code">http://docs.python-guide.org/en/latest/#shipping-great-code</a></p>
<h1 id="第四部分-development-environment">第四部分 Development Environment</h1>
<p><a href="http://docs.python-guide.org/en/latest/#development-environment">http://docs.python-guide.org/en/latest/#development-environment</a></p>
<h1 id="第五部分-additional-notes">第五部分 Additional Notes</h1>
<p><a href="http://docs.python-guide.org/en/latest/#additional-notes">http://docs.python-guide.org/en/latest/#additional-notes</a></p>
<hr>
<p>The end!</p>
<p>2013-11-25</p>
<p>wklken</p>
]]></content>
		</item>
		
		<item>
			<title>读书笔记-程序员的职业素养</title>
			<link>https://wklken.me/posts/2013/11/24/the-clean-coder.html</link>
			<pubDate>Sun, 24 Nov 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/11/24/the-clean-coder.html</guid>
			<description>作者：Robert C. Martin 一本“方法论”的书，还是可以借鉴很多东西的,做了摘录 讲了很多东西，涉及这个职业的方方面面 目标是，成为“专业人士” 每一节</description>
			<content type="html"><![CDATA[<p>作者：Robert C. Martin</p>
<p>一本“方法论”的书，还是可以借鉴很多东西的,做了摘录</p>
<p>讲了很多东西，涉及这个职业的方方面面</p>
<p>目标是，成为“专业人士”</p>
<p>每一节的标题还是有点作用的，摘录之</p>
<hr>
<h3 id="序">序</h3>
<p>需要的不是“经历丰富的人”而是“有职业素养的人”</p>
<p>相比问题本身，解决问题的方式、步骤以及反思深度都体现出一个人的职业素养</p>
<p>职业素养: 它体现了能力和素质，又强调了持续的积累和养成</p>
<p>技术人员需要如何改变才能被视为专业人士呢？</p>
<h3 id="前言">前言</h3>
<p>专业，要变得有影响力，有说服力</p>
<h3 id="引言">引言</h3>
<p>尝试定义专业程序员，成为真正专业的程序员，需要什么样的态度、原则、行动 (本书主旨, note about that)</p>
<h3 id="第一章-专业主义">第一章 专业主义</h3>
<p>1.1 清楚你要什么</p>
<p>&ldquo;专业主义&rdquo;，不但象征着荣誉与骄傲，而且明确意味着责任与义务</p>
<p>&ldquo;专业主义&quot;就意味着担当责任</p>
<p>1.2 担当责任</p>
<p>尽职尽责</p>
<p>1.3 首先，不行损害之事</p>
<p>1.3.1 不要破坏软件的功能</p>
<p>要做得专业，就不能留下bug</p>
<p>要对自己的不完美负责</p>
<p>所谓专业人士，就是能对自己犯下的错误负责的人，哪怕那些错误实际上是在所难免的</p>
<p>你有责任让失误率无限接近0</p>
<pre><code>1.让QA找不出任何问题(每次 QA 找出问题时, 更糟糕的是用户找出问题时, 你都该震惊羞愧, 并决心以此为戒)
2.要确信代码正常运行:
  如何确保-测试，一遍遍测试.自动化测试
  要求: 你写的每一行代码都要测试，完毕
3.自动化QA
</code></pre>
<p>1.3.2 不要破坏结构</p>
<p>聪明人不会为了发布新功能而破坏结构</p>
<p>所有软件项目的根本指导原则是，软件要易于修改</p>
<p>如果你希望自己的软件灵活可变，那就应该时常修改它: 要证明易于修改，唯一办法就是做些实际的修改</p>
<p>&ldquo;无情重构&rdquo;，每次读、修改代码，就要比原来更简洁</p>
<p>不要害怕修改代码，（有一套完整测试，你就根本不会害怕）</p>
<p>1.4 职业道德</p>
<p>职业发展是你自己的事(雇主没有义务确保你在职场能够立于不败之地, 也没有义务培训你)</p>
<p>将自己的职业发展寄希望于雇主的软件开发人员将会很惨</p>
<p>&ldquo;术业有专攻&rdquo;,需要投入时间去追求</p>
<p>1.4.1 了解你的领域</p>
<p>每个专业软件开发人员必须精通的事项(感觉有些可借鉴，并非全部)</p>
<pre><code>1.设计模式
2.设计原则。必须了解SOLID原则，而且要深刻理解组件设计原则
3.方法。必须了解XP/Scrum/精益/看板/瀑布/结构化分析/结构化设计
4.实践。TDD、OOP、结构化编程、持续集成和结对编程
5.工件、UML/DFD/结构图/Petri网络图/状态迁移图表、流程图和决策表
</code></pre>
<p>1.4.2 坚持学习</p>
<p>读书，看相关文章，关注博客和微博，参加技术大会，访问用户群，多参与读书与学习小组</p>
<p>不懂就学，不要畏难</p>
<p>1.4.2 练习</p>
<p>练习，指的是在日常工作之余专门练习技能，以期自我提升</p>
<p>解决一些简单的编程问题, 把它当做热身练习或静心过程</p>
<p>1.4.4 合作</p>
<p>学习的第二个最佳方法是与他人合作.</p>
<p>1.4.5 辅导</p>
<p>教学相长, 传道授业的同时, 导师也会从中受益</p>
<p>1.4.6 了解业务领域</p>
<p>对新领域有所了解, 未必需要成为该领域的专家, 但是仍然需要勤勉, 付出相当的努力来仍是业务领域.</p>
<p>1.4.7 与雇主/客户保持一致</p>
<p>必需弄明白雇主的真正问题, 站在其角度思考.</p>
<p>1.4.8 谦逊</p>
<p>专业人士都清楚自己的自负，不会故作谦逊</p>
<h3 id="第二章-说不">第二章 说不</h3>
<p>专业人士敢于说明真相而不屈从于权势。专业人士有勇气对他们的经理说“不”</p>
<p>2.1 对抗角色</p>
<p>不靠谱的承诺是失职</p>
<p>说不，然后找到双方都能接受的解决方案</p>
<p>有时候需要提供必要细节, 解释说服, 但有时候提供太多细节会导致更多的微观管理</p>
<p>2.2 高风险时刻</p>
<p>最要说“不”的时那些高风险的关键时刻</p>
<p>2.3 要有团队精神</p>
<p>恪尽职守，关心队友，提供帮助，最大可能做到尽职尽责</p>
<p>有团队精神的人不会总是说“是”</p>
<p>2.3.1 试试看</p>
<p>没有“试试看”这回事</p>
<p>许诺“尝试”，就意味着你承认自己之前未尽全力，承认自己还有余力可施，意味着你只要再加把劲还是可以达成目标的</p>
<p>本质上，承诺“尝试”是一种不诚实的表现</p>
<p>2.3.2 消极对抗</p>
<p>直接交流沟通，而不是消极对抗</p>
<p>2.4 说“是”的成本</p>
<p>运作良好的团队的经理和开发人员, 会相互协商, 直至达成共同认可的行动方案.</p>
<p>有时候，获取正确决策的唯一途径，便是勇敢无畏的说出“不”字。</p>
<p>2.5 如何写出好代码</p>
<p>&ldquo;客户所要的任何一项功能, 一旦写起来, 总是远比它开始时所说的药复杂许多&hellip;.&rdquo;</p>
<p>专业人士常常成为英雄，但这样的荣誉并非他们所刻意追求的</p>
<p>成为英雄与“解决问题”的诱惑诚然巨大，只是我们要明白，委屈专业原则以求全，并非问题的解决之道。舍弃这些原则，只会制造出更多的麻烦</p>
<p>坚守专业原则(问题: 哪些是专业原则?)</p>
<p>要学会说&quot;不&rdquo;</p>
<h3 id="第三章-说是">第三章 说“是”</h3>
<p>3.1 承诺用语</p>
<p>做出承诺，包含三个步骤</p>
<pre><code>口头上说自己将会去做
心里认真对待做出的承诺
真正付诸行动
</code></pre>
<p>当我们承诺某事时，必须认证对待承诺</p>
<p>3.1.1 识别“缺乏承诺”的征兆</p>
<p>一类词语</p>
<pre><code>需要、应当
希望、但愿
让我们（不是让我）
</code></pre>
<p>3.1.2 真正的承诺听起来是怎样的</p>
<p>你，你自己，始终能掌控某些事情，也就是说，总有些事是你可以承诺做到的</p>
<pre><code>我将在....之前...
</code></pre>
<p>你对自己将会做某件事做了清晰的事实陈述，而且明确了完成期限</p>
<p>之所以没成功</p>
<pre><code>1.是因为我寄希望于某某去做这件事
你只能承诺自己能完全掌控的事
最终目标依赖与他人，那么就应该采取些具体行动，接近最终目标

2.是因为我不大确信是否真能完成得了
即使目标无法完成，你仍能全力前进，离目标更近些3

3.是因为有些时候我真的无能为力
如果你无法兑现承诺，最重要的就是，尽早向你的承诺对象发出预警，越快越好，越早越好
</code></pre>
<p>3.2  学习如何说“是”</p>
<p>3.2.1 “试试”的另一面</p>
<p>试试尽力xxxx</p>
<p>3.2.2 坚守原则</p>
<p>如果是专业人员，就不会放弃底线。</p>
<p>写测试/重构/回归&hellip;.打破这些纪律和原则, 必然会拖慢进度</p>
<p>专业人士对自己的能力极限了如指掌, 他们十分清楚自己还能保持效率加班多长时间, 也非常明白要付出的代价.</p>
<h3 id="第四章-编码">第四章 编码</h3>
<p>具备&quot;出错感知力&quot;, 说明你已经能够非常迅速地获得反馈, 能够更为快速地从错误中学习.</p>
<p>要精熟掌握每项技艺, 关键都是要具备&quot;信心&quot;和&quot;出错感知&quot;能力</p>
<p>4.1 做好准备</p>
<p>编码是一项智力活动</p>
<pre><code>1.代码必须能够正常工作
2.代码必须能够帮你解决客户提出的问题
3.代码必须能和现有系统结合得天衣无缝
4.其他程序员必须能读懂你的代码
</code></pre>
<p>如果感到疲劳或者心烦意乱, 千万不要写代码. 要找到一种方法来消除干扰, 让心绪平静下来.</p>
<p>4.1.1 凌晨3点写出的代码</p>
<p>疲劳的时候，千万不要写代码</p>
<p>要确保自己已经将睡眠、健康和生活方式调整到最佳状况，这样才能在每天的8个小时里全力以赴</p>
<p>4.1.2 焦虑时写下的代码</p>
<p>专业开发人员善于合理分配个人时间，以确保工作时间段中尽可能富有成效。</p>
<p>在家中时，应该专门安排时间解决焦虑，这样就不会把焦虑情绪带到办公室里</p>
<p>4.2 流态区</p>
<p>意识高度专注，但思维视野收拢到狭窄的状态</p>
<p>避免进入流态区！并非真的极为高效，也绝非毫无错误</p>
<p>流态区写的代码可能会快些，但是后面你将不得不更多的回头重新审视这些代码</p>
<p>切换思维、结对编程等</p>
<p>ps: 为何我反而喜欢这种流态</p>
<p>4.2.1 音乐</p>
<p>对不同人，音乐帮助不一样，不一定有助于编码</p>
<p>4.2.2 中断</p>
<p>礼貌地回应中断</p>
<p>当然，要想办法减少中断</p>
<p>4.2.3 阻塞</p>
<p>不要干坐，找一些其他事情干</p>
<p>或者，结对编程</p>
<p>另一种方法：创造性输出依赖于创造性输入，增加自己知识体系的广度</p>
<p>4.4 调试</p>
<p>TDD?</p>
<p>衡量你是否是一个专业人士的重要方面，能否将调试时间尽量降到最低!</p>
<p>绝对的零调试时间是一个理想化的目标, 无法达到, 但要将之作为努力方向</p>
<p>4.5 保持节奏</p>
<p>软件开发是一场马拉松, 而不是短跑冲刺</p>
<p>4.5.1 知道何时应该离开一会</p>
<p>阻塞，疲倦等，让自己保持好节奏</p>
<p>当碰到困难而受阻时, 当你感到疲倦时, 就离开一会儿, 让富有创造力的潜意识接管问题.</p>
<p>4.6 进度延迟</p>
<p>管理延迟的秘诀, 便是早期检测和保持透明.</p>
<p>三个考虑到多种因素的期限：乐观预估，标称预估，悲观预估</p>
<p>尽量严守这三个时间点.</p>
<p>4.6.1 期望</p>
<p>调整和确认期望</p>
<p>除非另有后背元, 否则不要轻易松口退步, 不要让其他任何人对此抱有期望.</p>
<p>(自身承诺, 个人信誉)</p>
<p>4.6.2 盲目冲刺</p>
<p>坚持维持你的估算</p>
<p>不要经受不住诱惑盲目冲刺</p>
<p>不要让其他人抱有不实际的期望.</p>
<p>4.6.3 加班加点</p>
<p>不应该采用额外加班加点工作的方案，除非以下三个条件都能满足：</p>
<pre><code>1.你个人能挤出时间
2.短期加班，最多加班两周
3.你的老板要有后备预案，以防止万一加班措施失败了(最为关键)
</code></pre>
<p>4.6.4 交付失误</p>
<p>最糟糕：明知道没有完成任务却宣称已经完成</p>
<p>4.6.5 定义完成</p>
<p>创建一个确切定义的“完成”标准</p>
<p>4.7 帮助</p>
<p>4.7.1 帮助他人</p>
<p>清楚状态，腾出时间</p>
<p>作为专业人士，要以能够随时帮助别人为荣</p>
<p>4.7.2 接受他人的帮助</p>
<p>要以乐于接受别人的帮助为荣</p>
<p>同时要学会如何请求帮助</p>
<p>4.7.3 辅导</p>
<h3 id="第五章-测试驱动开发">第五章 测试驱动开发</h3>
<p>TDD,测试驱动开发，先写测试的编程</p>
<p>5.1 此事已有定论</p>
<p>TDD绝不仅仅是一种用于缩短编码周期的简单技巧</p>
<p>此事已有定论：TDD是确切可行，并且，每个开发者都要适应和掌握TDD</p>
<p>5.2 TDD的三项法则(似乎有些过了)</p>
<pre><code>1.在编写好失败单元测试之前，不要编写任何产品代码
2.只要有一个单元测试失败了，就不要再写测试代码；无法通过编译也是一种失败情况
3.产品代码恰好能够让当前失败的单元测试成功通过即可，不要多写
</code></pre>
<p>5.3 TDD的优势</p>
<p>5.3.1 确定性</p>
<p>代码有任何修改，都必须运行手头有的全部测试</p>
<p>确定状态</p>
<p>5.3.2 缺陷注入率</p>
<p>TDD能够显著降低缺陷</p>
<p>5.3.3 勇气</p>
<p>拥有一套值得信赖的测试，便可完全打消对修改代码的全部恐惧。</p>
<p>放手整理，代码变得更具有可塑性，可以安全地将之雕琢为简单而满意的结构</p>
<p>5.3.4 文档</p>
<p>单元测试即文档</p>
<p>5.3.5 设计</p>
<p>测试先行，会迫使你去考虑什么是好的设计</p>
<p>事后测试只是一种防守，而先行测试是一种进攻</p>
<p>5.3.6 专业人士的选择</p>
<p>TDD是专业人士的选择</p>
<p>它是一项能够提升代码确定性, 给程序员孤立, 降低代码缺陷率, 优化文档和设计的原则.</p>
<p>5.4 TDD的局限</p>
<p>TDD并非万能</p>
<p>某些场合显得不切实际或不合适</p>
<h3 id="第六章-练习">第六章 练习</h3>
<p>专业人士都需要借助专门的训练提升自己的技能</p>
<p>6.1 引子</p>
<p>6.1.1 10的22次方</p>
<p>现在我们有更好的工具，更好的语言，但是，语句的本质并没有随时间而改变</p>
<p>6.1.2 转变</p>
<p>工作方式已经截然不同</p>
<p>任何事情，要做得快，都离不开练习</p>
<p>无论搏斗还是编程, 速度都来源于练习.</p>
<p>6.2 编程柔道场</p>
<p>需要找一些东西，来做实际的练习</p>
<p>6.3 自身经验的扩展</p>
<p>会受限，即所解决问题的种类比较单一</p>
<p>所以，要自己扩展</p>
<pre><code>1.开源，提升技能的最好方式
2.自己规划，不要局限在公司的语言和平台
</code></pre>
<p>6.4 结论</p>
<p>无论如何，专业人士都需要练习</p>
<p>保持自己的技能不落伍是自己的责任, 而不是雇主的责任</p>
<p>联系的时候你是赚不到钱的, 但是联系之后, 你会获得回报, 而且是丰厚的回报.</p>
<h3 id="第七章-验收测试">第七章 验收测试</h3>
<p>专业开发人员既要做好开发, 也要做好沟通.</p>
<p>7.1 需求的沟通</p>
<p>7.1.1 过早精细化</p>
<p>陷阱：过早精细化</p>
<pre><code>1.不确定原则
需求完成得越精细，就越容易被忽视, 系统因此也谈不上完工
观察者效应/不确定原则：每次你想业务方提供一个功能，他们获取比之前更多的信息，反过来影响他们对整个系统的看法
2.预估焦虑
所谓的预估，预估整个系统，对需求进行精确评估
其实：即便拥有全面准确的信息，评估通常也会存在很大的变数
     不确定原则，需求是一定会变化的，追求的那种精确是徒劳的
</code></pre>
<p>7.1.2 迟来的模糊性</p>
<p>推迟过早精细化的另一个问题，迟来的模糊性</p>
<p>需求的模糊，带来分歧或争论</p>
<p>寻找各方都同意的关于需求的表述, 而不是去解决争端</p>
<p>7.2 验收测试</p>
<p>业务方与开发方合作编写的测试，其目的在于确定需求已经完成</p>
<p>7.2.1 “完成”的定义</p>
<p>完成，就是完成</p>
<p>完成意味着，所有代码都写完了，所有测试都通过了，QA和需求方已经认可。这，才是完成</p>
<p>7.2.2 沟通</p>
<p>验收测试的目的是沟通，澄清，精确化</p>
<p>7.2.3 自动化</p>
<p>手工测试成本太高，相比手动测试，自动化测试的成本非常低</p>
<p>7.2.4 额外工作</p>
<p>不要把测试看做额外工作，而应当看成节省时间和金钱的办法</p>
<p>7.2.5 验收测试什么时候写，由谁来写</p>
<p>理想状态下：业务方和QA协作编写，程序员检查是否有矛盾和冲突</p>
<p>只需要确保测试者和开发者不是同一人</p>
<p>7.2.6 开发人员的角色</p>
<p>开发人员有责任把验收测试与系统联系起来，然后让这些测试通过</p>
<p>7.2.7 测试的协商与被动推进</p>
<p>身为专业的开发人员，与编写测试的人协商并改进测试是你的职责，绝不能被动接受测试</p>
<p>请记住, 身为专业开发人员, 你的职责是协助团队开发出最棒的软件. 也就是说, 每个人都需要关心错误和疏忽, 并协力改正.</p>
<p>7.2.8 验收测试和单元测试</p>
<p>单元测试是程序员写给程序员的</p>
<p>验收测试是业务方写给业务方的</p>
<p>7.2.9 图形界面及其他复杂因素</p>
<p>恰当地测试</p>
<p>尽可能减少GUI测试</p>
<p>7.2.10 持续集成</p>
<p>保持持续集成系统的时刻运行</p>
<p>7.3 结论</p>
<p>要解决开发方和业务方沟通问题，有效的办法就是，编写自动化的验收测试</p>
<h3 id="第八章-测试策略">第八章 测试策略</h3>
<p>每个专业的开发团队都需要一套好的测试策略</p>
<p>8.1 QA应该找不到任何错误</p>
<p>QA在团队中扮演需求规约定义者和特性描述者</p>
<p>8.2 自动化测试金字塔</p>
<pre><code>5%   人工探索式测试
10%  系统测试
20%  集成测试
50%  组件测试
100% 单元测试
</code></pre>
<p>8.2.1 单元测试</p>
<p>在最低层次上定义系统</p>
<p>单元测试是可行的</p>
<p>单元测试可以做到90%以上的覆盖率</p>
<p>开发人员</p>
<p>8.2.2 组件测试</p>
<p>验收测试的一种，针对系统的各个组件编写的</p>
<p>QA和业务人员</p>
<p>8.2.3 集成测试</p>
<p>只能对那些组件很多的较大型系统才有意义</p>
<p>测试组件装配到一起是否协调,是装配测试</p>
<p>系统架构师或者主设计师</p>
<p>8.2.4 系统测试</p>
<p>针对真个击沉完毕的系统来运行的自动化测试,是最终的集成测试</p>
<p>测试系统是否正确组装完毕，以及系统各个组件之间是否能正常交互</p>
<p>系统架构师和技术负责人来编写.</p>
<p>8.2.5 人工探索性测试</p>
<p>人工，对系统进行深入研究和探索</p>
<p>8.3 结论</p>
<p>开发团队要和QA紧密配合，创建有单元测试，组件测试，集成测试，系统测试和探索式测试构成的测试体系</p>
<h3 id="第九章-时间管理">第九章 时间管理</h3>
<p>9.1 会议</p>
<p>关于会议，有两条真理</p>
<pre><code>1.会议是必须的
2.会议浪费了大量的时间
</code></pre>
<p>专业开发人员同样清楚会议额的高昂成本。所以，如果会议没有现实且显著的成效，他们会主动拒绝</p>
<p>9.1.1 拒绝</p>
<p>邀请你参加会议的人并不负责管理你的时间，为时间负责的人只有你</p>
<p>理智地使用时间，谨慎选择，应当参加哪些会议，礼貌拒绝哪些会议</p>
<p>领导的最重要责任之一, 就是帮你从某些会议脱身. 好的领导一定会主动维护你拒绝出席的决定, 因为她和你一样关心你的时间.</p>
<p>9.1.2 离席</p>
<p>会议并不总按计划进行的</p>
<p>如果会议然人厌烦，就离席(想个办法礼貌地退出来)</p>
<p>9.1.3 确定议程与目标</p>
<p>会议应当有清晰的议程，确定每个议题所花的时间，以及明确的目标</p>
<p>9.1.4 立会</p>
<p>站立会议</p>
<pre><code>1.我昨天做了什么
2.今天打算做什么
3.我遇到了什么问题
</code></pre>
<p>每个人发言不超过1分钟</p>
<p>9.1.5 迭代计划会议</p>
<p>会议的节奏应该很快，简明扼要地讨论各个候选人物，然后决定是选择还是放弃</p>
<p>会议是每轮迭代时间的5%以内</p>
<p>9.1.6 迭代回顾和DEMO展示</p>
<p>在迭代的末尾召开</p>
<p>9.1.7 争论/反对</p>
<p>凡事不能再5分钟内解决的争论，都不能靠辩说解决</p>
<p>用数据说话</p>
<p>如果你同意了, 就必须拿出行动来.</p>
<p>9.2 注意力点数</p>
<p>编程是需要持续投入精力和注意力的智力活动</p>
<p>注意力点数会随时间流逝而减少.</p>
<p>9.2.1 睡眠</p>
<p>保证睡眠，好好睡上7小时</p>
<p>9.2.2 咖啡因</p>
<p>适度</p>
<p>9.2.3 恢复</p>
<p>在注意力不集中的时候，无法控制注意力，可以想办法花30到60分钟恢复</p>
<p>9.2.4 肌肉注意力</p>
<p>肌肉注意力有助于改善心智注意力</p>
<p>定期训练肌肉注意力</p>
<p>9.2.5 输入与输出</p>
<p>平衡输入与输出</p>
<p>9.3 时间拆分和番茄工作法</p>
<p>25分钟高效工作+5分钟休息，每4个番茄钟休息30分钟</p>
<p>25分钟内，可以拒绝任何干扰</p>
<p>9.4 要避免的行为</p>
<p>优先级错乱：提高某个任务优先级来借口推迟真正急迫的任务</p>
<p>专业开发人员会评估每个人物的优先级，排除个人喜好和需求，按照真实的紧急程度来执行任务</p>
<p>9.5 死胡同</p>
<p>慎重的态度和积累的经验可以帮你避免某些死胡同，但无法避免所有</p>
<p>在走入死胡同时，要迅速意识到，并有足够的勇气走回头路</p>
<p>坑法则，The Rule of Holes：如果你掉进坑里，别挖</p>
<p>9.6 泥潭</p>
<p>泥潭会减慢你的速度，但不会让你彻底停下来</p>
<p>泥潭不容易被发现</p>
<p>发现自己深处泥潭还要固执前进，是最严重的优先级错乱</p>
<p>9.7 结论</p>
<p>专业的开发人员会用心管理自己的时间和注意力</p>
<h3 id="第十章-预估">第十章 预估</h3>
<p>预估是软件开发人员面对的最简单、也是最可怕的活动之一</p>
<p>10.1 什么是预估</p>
<p>问题在于，不同人不同看法，业务方认为是承诺，开发方认为是猜测. 两者相差迥异</p>
<p>10.1.1 承诺</p>
<p>承诺是必须做到的。</p>
<p>如果你承诺在某天做成某事, 就必须按时完成.</p>
<p>专业开发人员不随便承诺，除非他们确切知道可以完成</p>
<p>如果被要求承诺做自己不确定的事情, 那么就应当坚决拒绝.</p>
<p>10.1.2 预估</p>
<p>预估是一种猜测。它不包含任何承诺的色彩，他不需要做任何约定</p>
<p>大多数软件开发人员不擅长预估。</p>
<p>预估不是个定数，预估的结果是一种概率分布</p>
<p>10.1.3 暗示性承诺</p>
<p>专业开发人员能够清楚区分预估和承诺，只有在确切知道可以完成的情况下，他们才会给出承诺</p>
<p>另外，需要小心避免给出暗示性的承诺</p>
<p>10.2 PERT</p>
<p>计划评审技术</p>
<p>三元分析法</p>
<pre><code>O，乐观估计
N，标称估计 
P，悲观估计

u = (O+4N+P)/6
u是任务期望完成时间
</code></pre>
<p>10.3 预估任务</p>
<p>德尔菲法：一组人集合起来，讨论某项任务，预估完成时间，然后重复“讨论-预估”的过程，直到意见统一</p>
<p>10.4 大数定理</p>
<p>把大任务切分成许多小任务，分开预估再加总，结果会比单独平谷大人物要准确很多</p>
<p>10.5 结论</p>
<p>懂得如何为业务人员提供可信的预估结果，以便做出计划</p>
<p>如果做不到, 或者不确定能做到, 专业开发人员不会给出承诺.</p>
<h3 id="第十一章-压力">第十一章 压力</h3>
<p>即使有压力，专业开发人员也会冷静果断。尽管压力不断增大，他依然会坚守所受的训练和纪律，他知道这是他赖以战胜有最后期限和承诺所带来压力感的最好方法</p>
<p>11.1 避免压力</p>
<p>在压力下保持冷静的最好方式，便是规避会导致压力的处境</p>
<p>规避的方式也许无法完全减除压力, 但是可以大大降低压力并缩短高压力期的时间.</p>
<p>11.1.1 承诺</p>
<p>我们要做的就是使风险定量化并将他们陈述给业务方, 这样他们就能做好相应的准备.</p>
<p>避免不切实际的承诺</p>
<p>11.1.2 保持整洁</p>
<p>快速前进确保最后期限的方法，便是保持整洁</p>
<p>让系统、代码和设计尽可能简洁，就可以避免压力</p>
<p>要尽力保持输出成功整洁干净</p>
<p>11.1.3 危机中的纪律</p>
<p>选择那些你在危急时刻依然会遵循的纪律原则，并且在所有工作中都遵守这些纪律。</p>
<p>遵守这些纪律原则是避免陷入危机的最好途径</p>
<p>11.2 应对压力</p>
<p>11.2.1 不要惊慌失措</p>
<p>放松下来，对问题深思熟虑</p>
<p>努力寻找可以带来最好结果的路径，然后沿着那条路径以合理稳定的节奏前进</p>
<p>11.2.2 沟通</p>
<p>让你的团队和主管知道你正深陷困境之中</p>
<p>11.2.3 依靠你的纪律原则</p>
<p>当事情十分困难时，要坚信你的纪律原则</p>
<p>11.2.4 寻求帮助</p>
<p>11.3 总结</p>
<p>应对压力的诀窍在于, 能回避压力时尽可能地回避, 当无法回避时则勇敢直面压力.</p>
<h3 id="第十二章-协作">第十二章 协作</h3>
<p>大多数软件都是有团队开发出来的</p>
<p>单打独斗与有利于团队之外都是不专业的表现.</p>
<p>12.1 程序员与人</p>
<p>12.1.1 程序员与雇主</p>
<p>专业程序员的首要职责是满足雇主的需求</p>
<p>专业程序员会花时间去理解业务</p>
<p>12.1.2 程序员与程序员</p>
<pre><code>1.代码个体所有
不正常团队的糟糕症状
2.协作性的代码共有权
共有, 每个人都可以做出合适的修改
3.结对
</code></pre>
<p>12.2 小脑</p>
<p>专业人士会共同工作</p>
<p>有些时候，单独工作是正确的。但是一般来说，和他人紧密协作，在大部分时间段中结对工作，是最好的做法</p>
<p>12.3 结论</p>
<p>一定要学会交流，和人们交流</p>
<h3 id="第十三章-团队与项目">第十三章 团队与项目</h3>
<p>13.1 只是简单混合吗？</p>
<p>13.1.1 有凝聚力的团队</p>
<p>形成团队是需要时间的.</p>
<p>团队成员首先需要建立关系。需要学习如何写作，需要了解彼此的批号，强项，弱项，最终，才能凝聚成团队</p>
<pre><code>1.发酵期 
成员克服个体差异，默契配合，彼此信任，形成真正有凝聚力的团队，是需要一定时间的

2.团队和项目，何者为先
把项目分配给形成凝聚力的团队，而不是围绕项目来组件团队
</code></pre>
<p>有凝聚力的团队确实有些神奇之处, 他们能够一起创造奇迹.</p>
<p>13.1.2 如何管理有凝聚力的团队</p>
<p>每个团队都有自己的速度。团队的速度，即是在一定时间段内团队能够完成的工作量</p>
<p>13.1.3 项目承包人的困境</p>
<p>13.2 结论</p>
<p>团队比项目更难构建. 因此, 组建稳健的团队, 让团队在一个有一个项目中整体移动共同工作是较好的做法.</p>
<h3 id="第十四章-辅导学徒期与技艺">第十四章 辅导、学徒期与技艺</h3>
<p>14.1 失败的学位教育</p>
<p>14.2 辅导</p>
<p>14.3 学徒期</p>
<p>14.3.1 软件学徒期</p>
<pre><code>1.大师
2.熟练工
3.学徒、实习生
</code></pre>
<p>14.3.2 现实情况</p>
<p>程序员的水平是否能够提升和最终是否能够得到职位晋升，全视乎程序员自己的表现</p>
<p>14.4 技艺</p>
<p>技艺是工匠所持的精神状态</p>
<hr>
]]></content>
		</item>
		
		<item>
			<title>Nginx基础笔记</title>
			<link>https://wklken.me/posts/2013/11/23/nginx-base.html</link>
			<pubDate>Sat, 23 Nov 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/11/23/nginx-base.html</guid>
			<description>nginx小结 资源 资源 Nginx 官网 Nginx 官方下载地址 Nginx最佳实践配置项目 地址 Nginx Configuration wiki 教程 agentzh的Nginx教程 链接 Nginx开发从入门到精</description>
			<content type="html"><![CDATA[<p>nginx小结</p>
<h3 id="资源">资源</h3>
<blockquote>
<p>资源</p>
</blockquote>
<p>Nginx <a href="http://nginx.org/">官网</a></p>
<p>Nginx <a href="http://nginx.org/en/download.html">官方下载地址</a></p>
<p>Nginx最佳实践配置项目 <a href="https://github.com/Umkus/nginx-boilerplate">地址</a></p>
<p>Nginx Configuration <a href="http://wiki.nginx.org/Configuration">wiki</a></p>
<blockquote>
<p>教程</p>
</blockquote>
<p>agentzh的Nginx教程 <a href="http://openresty.org/download/agentzh-nginx-tutorials-zhcn.html">链接</a></p>
<p>Nginx开发从入门到精通 <a href="http://tengine.taobao.org/book/index.html">入口</a></p>
<blockquote>
<p>文章</p>
</blockquote>
<p>Nginx战斗准备-优化指南 <a href="http://www.oschina.net/translate/nginx-setup">入口</a></p>
<p>Nginx模块开发入门 <a href="http://blog.codinglabs.org/articles/intro-of-nginx-module-development.html">入口</a></p>
<p>解析Nginx负载均衡 <a href="http://stblog.baidu-tech.com/?p=2027">入口</a></p>
<p>Nginx Rewrite研究笔记 <a href="http://blog.cafeneko.info/2010/10/nginx_rewrite_note/">入口</a></p>
<h2 id="安装">安装</h2>
<h3 id="ubuntu下">ubuntu下</h3>
<pre><code>sudo apt-get install nginx

启动
sudo /etc/init.d/nginx start       #通过init.d下的启动文件启动。
sudo service nginx start#通过ubuntu的服务管理器启动

配置文件位置
/etc/nginx/nginx.conf
</code></pre>
<h3 id="编译安装">编译安装</h3>
<p>1.先决条件</p>
<pre><code>1.gcc
apt-get install gcc
2.pcre(Perl Compatible Regular Expression)
apt-get install libpcre3 libpcre3-dev
3.zlib
apt-get install zliblg zliblg-dev
4.openssl
apt-get install openssl opensll-dev

#如果非apt，可以使用下载包手动编译安装的方式处理
</code></pre>
<p>2.下载包</p>
<pre><code>www.nginx.net 下载稳定版
wget http://nginx.org/download/nginx-1.4.4.tar.gz
</code></pre>
<p>3.解压安装</p>
<pre><code>tar -xzvf nginx-1.4.4.tar.gz
#默认，安装目录/usr/local/nginx
./configure
make
make install

#配置
./configure --conf-path=/etc/nginx/nginx.conf

可以配置一些其他选项

安装后查看下目录下的Configuration summary
</code></pre>
<p>4.init脚本</p>
<pre><code>需要给nginx建立一个init脚本
从网上捞一个，放入/etc/init.d/nginx
</code></pre>
<p>推荐编译配置</p>
<pre><code>1.使用不同prefix，方便指定不同版本,也便于升级
./configure --prefix=/usr/local/nginx-1.4.4
</code></pre>
<h2 id="基本操作">基本操作</h2>
<pre><code>查看帮助
/usr/local/nginx/sbin/nginx -h

立即停止进程(TERM信号)
/usr/local/nginx/sbin/nginx -s stop

温和停止进程(QUIT信号)
/usr/local/nginx/sbin/nginx -s quit

重加载
/etc/init.d/nginx reload #有init脚本情况下
/usr/local/nginx/sbin/nginx -s reload #原生

检测配置文件是否正确
/usr/local/nginx/sbin/nginx -t #生产路径下的
/usr/local/nginx/sbin/nginx -t -c /home/ken/tmp/test.conf #可以测试某个临时文件
</code></pre>
<h2 id="http基本配置">HTTP基本配置</h2>
<h3 id="配置说明">配置说明</h3>
<pre><code>注释，#
每条指令总是以分好结束(;)
配置继承：在一个区块中嵌套其他区段，那么被嵌套的区段会继承其父区段的设置
字符串，可以没有引号，但是如果存在特殊字符（空格，分号，花括号）需要用引号引起
单位：大小(k/K m/M) 时间值(ms/s/m/h/d/w/M/y 默认s)
模块提供各种变量值，可以进行读取和赋值（每个模块提供变量列表需要自己去查）
</code></pre>
<h3 id="配置文件目录结构">配置文件目录结构</h3>
<pre><code>/usr/local/nginx/conf/

- mime.types 一个文件扩展列表，它们与MIME类型关联
- fastcgi.conf 与FastCGI相关的配置文件
- proxy.conf 与Proxy相关的配置文件
- nginx.conf 应用程序的基本配置文件
- sites/
    |- a.conf #允许给每个单独网站建立一个配置文件
    |- b.conf
    |- dir/
        |- c.conf

需要在nginx.conf中使用包含命令
include sites/*.conf;
include sites/*/*.conf;
</code></pre>
<h3 id="配置文件结构">配置文件结构</h3>
<pre><code>http { #嵌入配置文件的根部， 一个http里可以配置多个server

    server { #声明一个站点
        server_name www.website.com; #监听的主机名
        listen 80; #监听套接字所使用的ip地址和端口号

        error_page 404 /not_found.html;
        error_page 500 501 502 503 504 /server_error.html;

        index index.html;

        root /var/www/website/com/html; #定义文档的根目录

        #location, 通过制定的模式与客户端请求的URI相匹配
        location / { #网站的特定位置
        }
        location /admin/ { #网站的特定位置 #
            alias /var/www/locked/; #只能放在 location区段中，为指定路径提供别名
        }

        #操作符,匹配时跟定义顺序无关
        location = /abcd { #精确匹配，不能用正则
        }
        location /abc/ { #url必须以指定模式开始，不能用正则
        }
        location ^~ /abcd$ { #吴标致行为，URI定位必须以指定模式开始，如果匹配，停止搜索其他模式
        }
        location ~ ^/abcd$ { #正则匹配，区分大小写
        }
        location ~* ^/abcd$ { #正则匹配，不区分大小写
        }
        location @test  { #定义location区段名，客户端不能访问，内部产生的请求可以,例如try_files或error_page
        }
    }
}
</code></pre>
<h2 id="模块">模块</h2>
<h3 id="模块化">模块化</h3>
<p>nginx真正的魅力在于它的模块，整个应用程序建立在一个模块化系统之上，在编译时，可以对每一个模块进行启用或者禁用</p>
<h3 id="index模块">index模块</h3>
<p>定义往回走哪index页</p>
<pre><code>index index.php index.html /data/website/index.html;
#可以指定多个，但是ngxin提供第一个找到的文件
</code></pre>
<h3 id="log模块">Log模块</h3>
<pre><code>access_log /file/path;
error_log /file/path error;  #level: debug/info/notice/warn/error/crit
</code></pre>
<p>日志格式</p>
<pre><code>log_format main '$remote_addr - $remote_user [$time_local] &quot;$request&quot; '
'$status $body_bytes_sent &quot;$http_referer&quot; '
'&quot;$http_user_agent&quot; $http_x_forwarded_for';

access_log /var/log/test.log main;
</code></pre>
<h3 id="real-ip模块">Real IP模块</h3>
<p>默认编译nginx不包含这个模块</p>
<p>当通过nginx将用户请求进行转发时，接收请求的应用要拿到用户的真实IP(经转发拿到的是服务器的IP)</p>
<pre><code>real_ip_header X-Forwarded-For;
</code></pre>
<h3 id="access模块">Access模块</h3>
<p>可以禁用ip段</p>
<p>语法</p>
<pre><code>#如果规则之间有冲突，会以最前面匹配的规则为准
deny IP;
deny subnet;
allow IP;
allow subnet;
# block all ips
deny    all;
# allow all ips
allow    all;
</code></pre>
<p>配置一个blockips.conf,然后在nginx.conf中include</p>
<p>e.g</p>
<pre><code>location {
    allow 127.0.0.1; #允许本地ip 注意顺序，allow要放在前面
    deny all; #禁止其他ip
}
</code></pre>
<h3 id="rewrite模块">Rewrite模块</h3>
<p>作用：执行URL重定向,允许你去掉带有恶意的URL，包含多个参数（修改）</p>
<p>利用正则的匹配，分组和引用，达到目的</p>
<p>break/return/set等</p>
<pre><code>if (-f $uri) {
    break
}
if ($uri ~ ^/admin/){
    return 403;
}
if ($uri ~ ^/search/(.*)$) {
    set $query $1;
    rewrite ^ /search.php?q=$query?;
}
</code></pre>
<p>例子</p>
<pre><code>A:http://website.com/search/some-search-keywords
B:http://website.com/search.php?q=some-search-keywords
rewrite ^/search/(.*)$ /search.php?q=$1?;

A:http://website.com/user/31/James
B:http://website.com/user.php?id=31&amp;name=James
rewrite ^/user/([0-9]+)/(.+)$ /user.php?id=$1&amp;name=$2?;

A:http://website.com/index.php/param1/param2/param3
B:http://website.com/index.php/?p1=param1&amp;p2=param2&amp;p3=param3
rewrite ^/index.php/(.*)/(.*)/(.*)$ /index.php?p1=$1&amp;p2=$2&amp;p3=$3?;
</code></pre>
<p>rewrite语法</p>
<pre><code>rewrite A B option;
options:
        last :表示完成rewrite
        break:本规则匹配完成后，终止匹配，不再匹配后面的规则
        redirect:返回302临时重定向，地址栏会显示跳转后的地址
        permanent:返回301永久重定向，地址栏会显示跳转后的地址
</code></pre>
<h3 id="proxy模块">Proxy模块</h3>
<p>默认模块，允许你讲客户端的HTTP请求转到后端服务器</p>
<pre><code>location / {
    proxy_pass_header Server;  #该指令强制一些被忽略的头传递到客户端
    proxy_redirect off; #允许改写出现在HTTP头却被后端服务器触发重定向的URL,对相应本身不做任何处理
    proxy_set_header Host $http_host; #允许你重新定义代理header值再转到后端服务器.目标服务器可以看到客户端的原始主机名
    proxy_set_header X-Real-IP $remote_addr; #目标服务器可以看到客户端的真实ip，而不是转发服务器的ip
    proxy_set_header X-Scheme $scheme;
    proxy_pass http://localhost:8080;
}
</code></pre>
<h3 id="upstream模块">upstream模块</h3>
<pre><code>upstream up_name {
    server 192.168.0.1:9000 weight=5; #权重
    server 192.168.0.2:9000 weight=5 max_fails=5 fail_timeout=60s; #在60s内，其错误通信超过5次,认为该服务失效
    server 192.168.0.3:9000 down; #服务标记为离线，不再使用
    server 192.168.0.4:9000 backup; #备份服务器，其他全部宕机了才启用
}
</code></pre>
<h2 id="其他">其他</h2>
<h3 id="配置静态化目录">配置静态化目录</h3>
<pre><code>    location /static/
    {
        root /var/www/app/;
        autoindex off;
    }
</code></pre>
<h3 id="负载均衡">负载均衡</h3>
<pre><code>http {
    include mime.types;
    default_type application/octet-stream;

    keepalive_timeout 120;

    tcp_nodelay on;

    upstream up_localhost {
        server 127.0.0.1:8000 weight=5;
        server 127.0.0.1:8001 weight=10;
    }

    server {
        listen 80;

        server_name localhost;

        location /{
            proxy_pass http://up_localhost;
            proxy_set_header Host $host;
            proxy_set_header X-Real_IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }

}
</code></pre>
<h3 id="控制页面缓存">控制页面缓存</h3>
<pre><code>location ~ \.(htm|html|gif|jpg|jpeg|png|bmp|ico|css|js|txt)$ {
    root /opt/webapp;
    expires 24h;
}

expires 1 January, 1970, 00:00:01 GMT;
expires 60s;
expires 30m;
expires 24h;
expires 1d;
expires max;
expires off;
</code></pre>
<h3 id="nginx的内置变量">nginx的内置变量</h3>
<pre><code>$arg_PARAMETER 这个变量包含在查询字符串时GET请求PARAMETER的值。
$args 这个变量等于请求行中的参数。
$binary_remote_addr 二进制码形式的客户端地址。
$body_bytes_sent
$content_length 请求头中的Content-length字段。
$content_type 请求头中的Content-Type字段。
$cookie_COOKIE cookie COOKIE的值。
$document_root 当前请求在root指令中指定的值。
$document_uri 与$uri相同。
$host 请求中的主机头字段，如果请求中的主机头不可用，则为服务器处理请求的服务器名称。
$is_args 如果$args设置，值为&quot;?&quot;，否则为&quot;&quot;。
$limit_rate 这个变量可以限制连接速率。
$nginx_version 当前运行的nginx版本号。
$query_string 与$args相同。
$remote_addr 客户端的IP地址。
$remote_port 客户端的端口。
$remote_user 已经经过Auth Basic Module验证的用户名。
$request_filename 当前连接请求的文件路径，由root或alias指令与URI请求生成。
$request_body 这个变量（0.7.58+）包含请求的主要信息。在使用proxy_pass或fastcgi_pass指令的location中比较有意义。
$request_body_file 客户端请求主体信息的临时文件名。
$request_completion 请求完成
$request_method 这个变量是客户端请求的动作，通常为GET或POST。包括0.8.20及之前的版本中，这个变量总为main request中的动作，如果当前请求是一个子请求，并不使用这个当前请求的动作。
$request_uri 这个变量等于包含一些客户端请求参数的原始URI，它无法修改，请查看$uri更改或重写URI。
$schemeHTTP 方法（如http，https）。按需使用，例：
rewrite ^(.+)$ $scheme://example.com$1 redirect;
$server_addr 服务器地址，在完成一次系统调用后可以确定这个值，如果要绕开系统调用，则必须在listen中指定地址并且使用bind参数。
$server_name 服务器名称。
$server_port 请求到达服务器的端口号。
$server_protocol 请求使用的协议，通常是HTTP/1.0或HTTP/1.1。
$uri 请求中的当前URI(不带请求参数，参数位于$args)，可以不同于浏览器传递的$request_uri的值，它可以通过内部重定向，或者使用index指令进行修改。
</code></pre>
<h3 id="配置shtml访问">配置shtml访问</h3>
<p><a href="http://nginx.org/en/docs/http/ngx_http_ssi_module.html">文档</a></p>
<pre tabindex="0"><code>location  ~ ^/static {
    ....
    ssi on;
    ssi_client_errors on;
    ssi_types text/shtml;
}
</code></pre><h3 id="auth-basic配置">auth basic配置</h3>
<p>首先, 安装<code>htpasswd</code>, 生成密码</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">htpasswd -c paas_admin username
</span></span><span class="line"><span class="cl">password: password
</span></span></code></pre></div><p>生成文件后, 拷贝到nginx目录下</p>
<p>配置</p>
<pre tabindex="0"><code>location / {
    auth_basic &#34;Restricted&#34;;
    auth_basic_user_file /usr/local/nginx/conf/kibana.htpasswd;
    root  /data/BKLogTool/kibana;
    index  index.html  index.htm;
}
</code></pre><p>reload nginx</p>
]]></content>
		</item>
		
		<item>
			<title>读书笔记-拖拉一点也无妨</title>
			<link>https://wklken.me/posts/2013/11/17/the-art-of-procrastination.html</link>
			<pubDate>Sun, 17 Nov 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/11/17/the-art-of-procrastination.html</guid>
			<description>mac搞坏了，折腾一宿没搞定，试过N种方法，历时8个小时，结果把硬盘给装没了 &amp;gt;-&amp;lt; 最后无奈，搞去苹果店修理，中间等了三个小时 带上了nook，到公</description>
			<content type="html"><![CDATA[<p>mac搞坏了，折腾一宿没搞定，试过N种方法，历时8个小时，结果把硬盘给装没了 &gt;-&lt;</p>
<p>最后无奈，搞去苹果店修理，中间等了三个小时</p>
<p>带上了nook，到公园里，坐树下，看了两本书</p>
<p>&laquo;facebook效应&gt;&gt;和这本 &laquo;拖拉一点也无妨&gt;&gt;</p>
<p><img src="/imgs/books/the-art-of-procrastination.jpg" alt="procrastination"></p>
<p>只有一百来页</p>
<p>作为一个完美主义者，有轻微强迫症和轻微拖延症</p>
<p>感觉有些地方还是很有道理</p>
<p>以下为一些核心的观点</p>
<blockquote>
<p>后天能做的事情，就别赶着明天做了——马克吐温</p>
</blockquote>
<hr>
<blockquote>
<p>结构化拖延法</p>
</blockquote>
<p>每个拖延人士，都会把必须要做的重要事情往后拖——结构化拖延法则正是一门关于如何利用这一消极特性、让它为你服务的艺术</p>
<p>观点：爱拖延的任人极少什么都不做,只是逃避去做重要的事情(拖延，并不等于什么都不做)</p>
<p>结构化拖延法：</p>
<pre><code>利用这种心态，给必须完成的任务梳理一个结构
将想要做的事情按重要性列个清单，写下来，最紧急最重要的排在前面,将另外一些重要的值得做的事情排在后面

于是，完成这些任务，就会变成避免去做清单最上方任务的一种手段

-&gt; 拖延的人变成有用的人
</code></pre>
<p>你需要的是，一个排序得当的任务结构</p>
<p>清单最开头几件事：1）它看似有明确的截止日期（但实际上没有），2）它看似重要得不得了（实际上不是）</p>
<p>实际上是一种自我欺骗，但是有用：用一种性格缺陷去抵消另一种性格缺陷</p>
<p>最终由于没有做某些事，从而做成了不少别的事</p>
<blockquote>
<p>减少承诺？</p>
</blockquote>
<p>拖延者常常错误的做法——尽量少想别人做出承诺，以为事情越少就能改掉拖延的毛病</p>
<p>错误的，即使再少，也会拖延</p>
<p>所以，勇于去做承诺</p>
<blockquote>
<p>拖延与完美主义</p>
</blockquote>
<p>完美主义导致了拖延: 为何会导致拖延？因为做到完美不容易！</p>
<p>要接受一个事实：没有完美，我们从来没有干过完美的事情，就连接近完美的事情也没有(停留在脑海里的幻想层面的东西,而非真实的状况,是自我的幻想)</p>
<p>观点：对于一件无需做到完美的任务，就不去苛求完美（关于完美主义的幻想，浪费时间，影响情绪）。烂的开始是成功的一半，不要过于苛求完美</p>
<p>验伤选择:</p>
<pre><code>根据紧急程度进行分类排列
对于大多数事情，要做的时候，心里有数：做到“蛮好”就可以了，或许比“蛮好”再好一点，但是犯不着追求完美
养成习惯，做事之前逼自己分析一下，看看“不那么完美”的代价有多大，做到“完美无缺”有多大意义

很多事情，没那么完美也就足够了，不要等到任务过期,现在就开始做
</code></pre>
<p>刚刚好才是真的好</p>
<blockquote>
<p>待办事项清单</p>
</blockquote>
<p>当日待办事项清单，列好，大任务拆分成小任务，每完成一个鼓励自己一下</p>
<p>宏大的令人望而却步的大任务拆分成小的，没有那么吓人的任务</p>
<p>把令人分心的事情也写下来，提醒自己不要去做</p>
<blockquote>
<p>放点音乐</p>
</blockquote>
<p>音乐和情绪之间存在着直接的关联</p>
<p>找到合适自己节奏的音乐(轻快的歌,极富感染力欢乐的歌)</p>
<p>可以选择合适长短的音乐，开始做事，直到音乐结束</p>
<blockquote>
<p>电脑与拖延</p>
</blockquote>
<p>从某些方面，电脑是拖延者们的恩物,也是个祸害，因为人们太容易把时间浪费在毫无价值，又与手边任务毫不相干的事情上</p>
<p>找到最好的方法，使用电脑——处理邮件(分类，标记，存档)的方式，处理IM，处理社交网络等等,学会如何控制，防止上网迷路</p>
<blockquote>
<p>“平摊型“人士的呼吁</p>
</blockquote>
<p>把手上所做的事情平摊在眼前，根据当前情境决定处理哪件，轮转地处理问题</p>
<p>叠放起来，或许也就意味着，不会再次打开查看</p>
<p>好吧，我是“叠放型”人士</p>
<blockquote>
<p>与敌人合作？</p>
</blockquote>
<p>和非拖延人士合作，去做一大堆相对没那么重要的事情，非拖延人士绝对不会抽时间做的</p>
<p>学会如何与非拖延人士合作</p>
<blockquote>
<p>额外福利</p>
</blockquote>
<p>结构化拖延者的额外福利：有时候，排在清单项顶头的重要事项会自动消失</p>
<p>有些事情放一放，到最后可能就不需要去做了, 切勿把那些在明天到来之前可能会消失不见的事情在今天就做掉</p>
<p>拖延是缺点，但并非是最糟糕的缺点，有时候，缺点也有额外福利</p>
<blockquote>
<p>你看拖延不顺眼？</p>
</blockquote>
<p>最让人讨厌的拖延行为，往往是那些为了证明你不受他人控制的举动</p>
<p>结构化拖延是一码事，故意做给其他人看，让他知道你不受他辖制又是另一回事</p>
<blockquote>
<p>从哲学的角度替拖延说两句</p>
</blockquote>
<p>Nothing</p>
<hr>
<p>wklken</p>
<p>2013-11-17 create
2014-02-16 update</p>
<p>于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>Redis基础笔记</title>
			<link>https://wklken.me/posts/2013/10/19/redis-base.html</link>
			<pubDate>Sat, 19 Oct 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/10/19/redis-base.html</guid>
			<description>资源链接 推荐书籍:《Redis入门指南》 资源列表: redis命令速查command | CMD索引-中文 | CMD树-中文 redis源码githu</description>
			<content type="html"><![CDATA[<h2 id="资源链接">资源链接</h2>
<p>推荐书籍:《Redis入门指南》</p>
<blockquote>
<p>资源列表:</p>
</blockquote>
<p>redis命令速查<a href="http://redis.io/commands">command</a> |
<a href="http://redis.cn/commands.html">CMD索引-中文</a> |
<a href="http://redis.readthedocs.org/en/latest/">CMD树-中文</a></p>
<p>redis源码<a href="https://github.com/antirez/redis">github</a></p>
<p>下载地址<a href="http://redis.io/download">redis.io</a></p>
<p>The Little Redis book <a href="https://github.com/JasonLai256/the-little-redis-book/blob/master/cn/redis.md">入口</a></p>
<p>redis资料概要 @江南白衣 <a href="https://github.com/springside/springside4/wiki/redis">gist</a></p>
<p>redis资料汇总专题 <a href="http://blog.nosqlfan.com/html/3537.html">nosqlfan</a> (这个站有很多文章)</p>
<p>redis的设计与实现 <a href="http://www.redisbook.com/en/latest/">文档</a></p>
<p>redis2.6带注释源码 <a href="https://github.com/huangz1990/annotated_redis_source">github</a></p>
<p>redis 各种语言clients<a href="http://redis.io/clients">clients</a></p>
<p>python redis cli <a href="https://github.com/andymccurdy/redis-py">redis-py</a></p>
<p>网络文章汇总 <a href="http://www.redis.cn/article.html">入口</a></p>
<blockquote>
<p>文章列表：</p>
</blockquote>
<p>十五分钟介绍Redis数据结构  <a href="http://blog.nosqlfan.com/html/3202.html?ref=rediszt">入口</a></p>
<p>redis的订阅与发布 <a href="http://www.redisbook.com/en/latest/feature/pubsub.html">入口</a></p>
<p>redis之七种武器 <a href="http://blog.nosqlfan.com/html/2942.html?ref=rediszt">入口</a></p>
<p>使用Redis的五个注意事项 <a href="http://blog.nosqlfan.com/html/3705.html">入口</a></p>
<p>redis源码分析系列文章 <a href="http://blog.nosqlfan.com/html/2949.html?ref=rediszt">入口</a></p>
<p>Largest Redis Clusters Ever <a href="http://www.xdata.me/?p=301">入口</a> (使用场景)</p>
<p>成人网站YouPorn使用Redis之经验谈 <a href="http://blog.jobbole.com/44629/">入口</a></p>
<blockquote>
<p>相关项目</p>
</blockquote>
<p>Redis监控web工具 <a href="https://github.com/steelThread/redmon">redmon</a></p>
<h2 id="简介">简介</h2>
<h3 id="简介-1">简介</h3>
<p>Redis(REmote DIctionary Server),远程字典服务器，以字典结构存储数据，允许通过TCP协议读取字典中内容. 高性能键值对数据库</p>
<p>作用：</p>
<pre><code>1.缓存系统： 可以为每个键设置TTL(Time To Live),生存时间到期后键会自动被删除
             可限定数据占用最大内存空间，数据大道空间限制后自动按照一定规则淘汰不需要键
2.任务队列： redis列表类型可以用来实现队列, 支持阻塞式读取，很容易用作高性能队列，还支持“发布/订阅“消息的模式
</code></pre>
<p>其他:</p>
<pre><code>1.Redis中, 所有数据都存储在内存中, 但提供了持久化支持, 内存中数据可以异步写入硬盘, 不影响现有服务
2.与memcached对比, redis单线程模型, memcached支持多线程. 但redis支持高级数据类型和持久化
</code></pre>
<h3 id="安装">安装</h3>
<p>安装</p>
<pre><code>1.wget http://download.redis.io/releases/redis-2.6.16.tar.gz
2.tar -xzvf redis-2.6.16.tar.gz
3.cd redis-2.6.16
4.make
5.make install
</code></pre>
<p>osx</p>
<pre><code>brew install redis
</code></pre>
<p>安装后，在/usr/local/bin下面有</p>
<pre><code>redis-server     服务器, 重点
redis-cli        命令行客户端, 重点

redis-benchmark  性能测试工具
redis-check-aof  AOF文件修复工具
redis-check-dump RDB文件检查工具
</code></pre>
<p>启动</p>
<p>直接启动</p>
<pre><code>redis-server #默认6379
redis-server --port 6380
# 启动命令中配置将覆盖配置文件中同名参数
redis-server /path/to/redis.conf --loglevel warning
</code></pre>
<p>通过初始化脚本运行(生产环境)</p>
<pre><code>在安装包目录redis-2.6.16/utils/redis_init_script

1.将脚本复制到/etc/init.d目录下，文件名为 redis_端口号, 修改文件第六行 REDISPORT为相同端口号
2.建立目录
    /etc/redis           存放配置文件
    /var/redis/端口号    存放持久化文件
3.修改配置文件
    将配置文件redis-2.6.16/redis.conf 复制到/etc/redis目录下，以端口号命名 e.g.  6379.conf
    修改配置文件中部分参数
        daemonize   yes                         使redis以守护进程模式运行
        pidfile     /var/run/redis_端口号.pid   设置redis的PID文件按位置
        port        端口号                      设置监听的端口号
        dir         /var/redis/端口号           设置持久化文件存放位置
</code></pre>
<p>停止</p>
<pre><code>redis-cli SHUTDOWN
//server断开所有客户端连接, 根据配置执行持久化, 最后退出
</code></pre>
<p>Redis命令行客户端</p>
<pre><code>发送命令：
    redis-cli -h 127.0.0.1 -p 6379
    redis-cli PING
    redis-cli
命令返回值
    状态回复，e.g.  &gt;PING
    错误恢复, e.g.  &gt;ECMD
    整数回复, e.g.  &gt;INCR foo
    字符串回复，e.g. &gt;GET foo   &gt;GET notexists`
    多行字符串回复，e.g. &gt;KEYS *
</code></pre>
<h2 id="五种数据类型及相应命令">五种数据类型及相应命令</h2>
<p>基础命令(redis-cli)</p>
<ul>
<li>返回符合规则的键名列表</li>
</ul>
<p>pattern支持通配符, ? * [] 等, 会遍历所有键, 当键的数量较多时影响性能</p>
<pre><code>&gt;KEYS pattern
</code></pre>
<p>e.g.</p>
<pre><code>&gt;SET bar 1
&gt;KEYS *
</code></pre>
<ul>
<li>判断一个键是否存在</li>
</ul>
<p>返回，0不存在，1存在</p>
<pre><code>&gt;EXISTS key
</code></pre>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; EXISTS bar
(integer) 1
127.0.0.1:6379&gt; EXISTS foo
(integer) 0
</code></pre>
<ul>
<li>删除键</li>
</ul>
<p>可以删除多个键, 返回删除的个数</p>
<pre><code>&gt;DEL key [key ...]
</code></pre>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; DEL bar
(integer) 1
127.0.0.1:6379&gt; DEL bar
(integer) 0
</code></pre>
<ul>
<li>获取键值的数据类型</li>
</ul>
<p>返回string,hash(散列),list(列表),set(集合),zset(有序集合)</p>
<pre><code>&gt;TYPE key
</code></pre>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; SET bar 1
OK
127.0.0.1:6379&gt; TYPE bar
string
</code></pre>
<h3 id="1-字符串类型">1. 字符串类型</h3>
<p>最基本类型, 能存储任何形式/编码字符串, 包括二进制. 允许存储最大容量是512M</p>
<ul>
<li>
<p>赋值和取值命令</p>
<pre><code>  &gt;SET key value
  &gt;GET key
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; SET hi &quot;hello world&quot;
OK
127.0.0.1:6379&gt; GET hi
&quot;hello world&quot;
127.0.0.1:6379&gt; GET abc
(nil)
</code></pre>
<ul>
<li>
<p>递增数字(当存储的字符串是整数形式时)</p>
<pre><code>  当操作键不存在时，默认值0，第一次递增后结果1，当键值不是整数时，报错
  &gt;INCR key
  原子操作，可用于类似访问量统计, 自增id

  &gt;INCRBY key increment
  &gt;INCRBY bar 2
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; INCR abc
(integer) 1
127.0.0.1:6379&gt; GET abc
&quot;1&quot;
127.0.0.1:6379&gt; INCRBY abc 2
(integer) 3
127.0.0.1:6379&gt; GET abc
&quot;3&quot;
</code></pre>
<ul>
<li>
<p>减少数字</p>
<pre><code>  &gt;DECR key
  &gt;DECRBY key decrement
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; DECR abc
(integer) 2
127.0.0.1:6379&gt; DECRBY abc 2
(integer) 0
</code></pre>
<ul>
<li>
<p>浮点数</p>
<pre><code>  &gt;INCRBYFLOAT key increment
  &gt;INCRBYFLOAT bar 2.7
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; GET bar
&quot;1&quot;
127.0.0.1:6379&gt; INCRBYFLOAT bar 1.5
&quot;2.5&quot;
</code></pre>
<ul>
<li>
<p>向尾部追加</p>
<pre><code>  返回追加字符串长度

  &gt;APPEND key value
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>&gt;SET key hello
&gt;APPEND key &quot;world&quot;
</code></pre>
<ul>
<li>
<p>获取字符串长度</p>
<pre><code>  &gt;STRLEN key
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; GET hi
&quot;hello world&quot;
127.0.0.1:6379&gt; STRLEN hi
(integer) 11
</code></pre>
<ul>
<li>
<p>同时设置，获取多个键值</p>
<pre><code>  &gt;MGET key [key ...]
  &gt;MSET key value [key value ...]
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; MSET k1 v1 k2 v2
OK
127.0.0.1:6379&gt; GET k1
&quot;v1&quot;
127.0.0.1:6379&gt; MGET k1 k2
1) &quot;v1&quot;
2) &quot;v2&quot;
</code></pre>
<ul>
<li>
<p>位操作</p>
<pre><code>  &gt;GETBIT key offset
  &gt;SETBIT key offset value

  &gt;BITCOUNT key [start] [end] #获取值为1的二进制位个数

  &gt;BITOP operation destkey key [key ...] #对多个字符串类型键进行位运算, 并将结果存储到destkey
  e.g. BITOP OR res fol fo2
      GET res
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>a  97 01100001

127.0.0.1:6379&gt; SET k 'a'
OK
127.0.0.1:6379&gt; GET k
&quot;a&quot;

127.0.0.1:6379&gt; GETBIT k 0
(integer) 0
127.0.0.1:6379&gt; GETBIT k 1
(integer) 1

127.0.0.1:6379&gt; BITCOUNT k
(integer) 3
</code></pre>
<p>BP: redis对键的命名，  对象类型:对象id:对象属性</p>
<h3 id="2-散列类型">2. 散列类型</h3>
<p>一种字典结构，其存储了字段(field)和字段值映射，但字段值只能是字符串，不支持其他数据类型(即散列类型不支持数据类型嵌套)</p>
<p>散列类型适合存储对象</p>
<pre><code>对象类型:id - 对象属性 - 对象属性值
</code></pre>
<ul>
<li>
<p>基本命令</p>
<pre><code>  &gt;HSET key field value
  &gt;HGET key field
  #HSET不区分插入更新

  &gt;HMSET key field value [field value ...]
  &gt;HMGET key field [field ...]

  &gt;HGETALL key
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; HSET car name bmw
(integer) 1
127.0.0.1:6379&gt; HGET car name
&quot;bmw&quot;

127.0.0.1:6379&gt; HMSET car price 100 score 50
OK
127.0.0.1:6379&gt; HMGET car name price score
1) &quot;bmw&quot;
2) &quot;100&quot;
3) &quot;50&quot;

127.0.0.1:6379&gt; HGETALL car
1) &quot;name&quot;
2) &quot;bmw&quot;
3) &quot;price&quot;
4) &quot;100&quot;
5) &quot;score&quot;
6) &quot;50&quot;
</code></pre>
<ul>
<li>
<p>判断字段是否存在</p>
<pre><code>  #存在返回1，否则返回0
  &gt;HEXISTS key field
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; HEXISTS car name
(integer) 1
127.0.0.1:6379&gt; HEXISTS car model
(integer) 0
</code></pre>
<ul>
<li>
<p>当字段不存在时赋值</p>
<pre><code>  #已存在不进行任何操作, 不存在赋值
  &gt;HSETNX key field value
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; HSETNX car name  abc
(integer) 0
127.0.0.1:6379&gt; HGET car name
&quot;bmw&quot;

127.0.0.1:6379&gt; HEXISTS car model
(integer) 0
127.0.0.1:6379&gt; HSETNX car model 1
(integer) 1
127.0.0.1:6379&gt; HGET car model
&quot;1&quot;
</code></pre>
<ul>
<li>
<p>增加数字</p>
<pre><code>  &gt;HINCRBY key field increment
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>&gt;HINCRBY person score 60
</code></pre>
<ul>
<li>
<p>删除字段</p>
<pre><code>  &gt;HDEL key field [field ...]
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>HDEL car price
</code></pre>
<ul>
<li>
<p>只获取字段名或字段值</p>
<pre><code>  &gt;HKEYS key
  &gt;HVALS key
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; HKEYS car
1) &quot;name&quot;
2) &quot;price&quot;
3) &quot;score&quot;
4) &quot;model&quot;
127.0.0.1:6379&gt; HVALS car
1) &quot;bmw&quot;
2) &quot;100&quot;
3) &quot;50&quot;
4) &quot;1&quot;
</code></pre>
<ul>
<li>
<p>获取字段数量</p>
<pre><code>  &gt;HLEN key
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; HLEN car
(integer) 4
</code></pre>
<h3 id="3-列表类型">3. 列表类型</h3>
<p>List, 可以存储一个有序的字符串列表, 列表内元素非唯一性，可以向两端加入元素，或者获得列表的某一个片段</p>
<p>内部使用双向链表实现,两端添加元素复杂度O(1), 通过索引访问的速度较慢</p>
<p>可以用在分页, 新鲜事, 记录日志等</p>
<ul>
<li>
<p>向列表两端增加元素(可同时增加多个)</p>
<pre><code>  &gt;LPUSH key value [value ...]
  &gt;RPUSH key value [value ...]
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; LPUSH num 1 2 3
(integer) 3
127.0.0.1:6379&gt; RPUSH num 0
(integer) 4
# 3 2 1 0
</code></pre>
<ul>
<li>从列表两端弹出元素</li>
</ul>
<p>先移除一个元素，然后返回之</p>
<pre><code>&gt;LPOP key
&gt;RPOP key
</code></pre>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; LPOP num
&quot;3&quot;
127.0.0.1:6379&gt; RPOP num
&quot;0&quot;
</code></pre>
<ul>
<li>
<p>获取元素个数</p>
<pre><code>  &gt;LLEN key
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; LLEN num
(integer) 2
</code></pre>
<ul>
<li>
<p>获取片段(同python切片，-1表最后一个, stop超出返回最右边元素, start大于stop返回空)</p>
<pre><code>  &gt;LRANGE key start stop
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; LPUSH test a b c d e f g
(integer) 7
127.0.0.1:6379&gt; LRANGE test 0 2
1) &quot;g&quot;
2) &quot;f&quot;
3) &quot;e&quot;
</code></pre>
<ul>
<li>
<p>删除列表中指定值的元素</p>
<pre><code>  &gt;LREM key count value

  删除列表中前count个值为value的元素，返回值为实际删除元素的个数
              count = 0 所有
                    &gt; 0 从左边开始删count个
                    &lt; 0 从右边开始删|count|个
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; LRANGE test 0 2
1) &quot;g&quot;
2) &quot;f&quot;
3) &quot;e&quot;
127.0.0.1:6379&gt; LREM test 0 f
(integer) 1
127.0.0.1:6379&gt; LRANGE test 0 2
1) &quot;g&quot;
2) &quot;e&quot;
3) &quot;d&quot;
</code></pre>
<ul>
<li>
<p>设置指定索引元素值</p>
<pre><code>  &gt;LINDEX key index
  &gt;LSET key index value
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; LPUSH lt 3 2 1
(integer) 3
127.0.0.1:6379&gt; LINDEX lt 0
&quot;1&quot;

127.0.0.1:6379&gt; LSET lt 0 -1
OK
127.0.0.1:6379&gt; LINDEX lt 0
&quot;-1&quot;
</code></pre>
<ul>
<li>
<p>只保留列表指定片段</p>
<pre><code>  删除指定索引范围之外的所有元素
  &gt;LTRIM key start end
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; LRANGE la 0 99
1) &quot;6&quot;
2) &quot;5&quot;
3) &quot;4&quot;
4) &quot;3&quot;
5) &quot;2&quot;
6) &quot;1&quot;
127.0.0.1:6379&gt; LTRIM la 0 2
OK
127.0.0.1:6379&gt; LRANGE la 0 99
1) &quot;6&quot;
2) &quot;5&quot;
3) &quot;4&quot;
</code></pre>
<ul>
<li>
<p>向列表中插入元素</p>
<pre><code>  &gt;LINSERT key BEFORE|AFTER pivot value
  从左到右查找值为pivot的元素，然后根据BEFORE/AFTER决定将value插入该元素前面还是后面
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; LRANGE la 0 99
1) &quot;6&quot;
2) &quot;5&quot;
3) &quot;4&quot;
127.0.0.1:6379&gt; LINSERT la AFTER 5 3
(integer) 4
127.0.0.1:6379&gt; LRANGE la 0 99
1) &quot;6&quot;
2) &quot;5&quot;
3) &quot;3&quot;
4) &quot;4&quot;
</code></pre>
<p>将元素从一个列表转到另一个列表</p>
<pre><code>RPOPLPUSH source destination
#RPOP，然后LPUSH，返回每个元素值，e.g.循环测试网址的可用性
</code></pre>
<h3 id="4-集合类型">4. 集合类型</h3>
<p>无序，无重复(唯一)，可以存储最多2^32 - 1 个字符串</p>
<ul>
<li>增加删除元素</li>
</ul>
<p>返回操作成功的个数</p>
<pre><code>    &gt;SADD key member [member ...]
    &gt;SREM key member [member ...]
</code></pre>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; SADD letters a
(integer) 1
127.0.0.1:6379&gt; SADD letters a b c
(integer) 2
</code></pre>
<ul>
<li>
<p>获得集合中的所有元素</p>
<pre><code>  &gt;SMEMBERS key
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; SMEMBERS letters
1) &quot;c&quot;
2) &quot;a&quot;
3) &quot;b&quot;
</code></pre>
<ul>
<li>
<p>判断元素是否在集合中(复杂度O(1))</p>
<pre><code>  &gt;SISMEMBER key member
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; SISMEMBER letters a
(integer) 1
127.0.0.1:6379&gt; SISMEMBER letters d
(integer) 0
</code></pre>
<ul>
<li>
<p>集合间运算</p>
<pre><code>  # 差集
  &gt;SDIFF key [key ...]
  # 交集
  &gt;SINTER key [key ...]
  # 并集
  &gt;SUNION key [key ...]
</code></pre>
</li>
<li>
<p>进行集合运算并将结果存储</p>
<pre><code>  &gt;SDIFFSTORE destination_key key [key ...]
  &gt;SINTERSTORE destination_key key [key ...]
  &gt;SUNIONSTORE destination_key key [key ...]
</code></pre>
</li>
<li>
<p>获得集合中元素个数</p>
<pre><code>  &gt;SCARD key
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; SCARD letters
(integer) 3
</code></pre>
<ul>
<li>
<p>随机获取集合中元素</p>
<pre><code>  &gt;SRANDMEMBER key [count]
  count，正数，获取count个不重复的元素
  count, 负数，获取|count|个，可能重复
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; SRANDMEMBER letters
&quot;b&quot;
127.0.0.1:6379&gt; SRANDMEMBER letters 2
1) &quot;a&quot;
2) &quot;c&quot;

127.0.0.1:6379&gt; SRANDMEMBER letters -2
1) &quot;b&quot;
2) &quot;b&quot;
</code></pre>
<ul>
<li>
<p>弹出一个元素</p>
<pre><code>  &gt;SPOP key
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; SPOP letters
&quot;c&quot;
127.0.0.1:6379&gt; SCARD letters
(integer) 2
</code></pre>
<h3 id="5-有序集合">5. 有序集合</h3>
<p>sorted set,集合中每个元素都关联一个分数(不同元素分数可以相同)，可以根据分数进行排序(最高/最低N个)，进行有序相关的操作(分数可以相同)</p>
<p>有序集合使用散列表和跳跃表实现, 读取复杂度更低, 更耗费内存</p>
<p>按点击量排序，按时间排序等等，时间轴操作</p>
<ul>
<li>
<p>增加元素</p>
<pre><code>  &gt;ZADD key score member [score member]
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; ZADD scoreboard 89 tom 67 peter 100 david
(integer) 3
127.0.0.1:6379&gt; ZADD scoreboard 70 peter
(integer) 0
</code></pre>
<ul>
<li>
<p>获取元素分数</p>
<pre><code>  &gt;ZSCORE key member
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; ZSCORE scoreboard peter
&quot;70&quot;
</code></pre>
<ul>
<li>获取排名在某个范围内的元素列表</li>
</ul>
<p>分数相同，按字典序排,中文的话，取决于编码方式</p>
<pre><code>#分数从小到大排，返回索引从 start-stop之间的所有元素，包含两端元素, WITHSCORES同时获得元素分数
&gt;ZRANGE key start stop [WITHSCORES]
&gt;ZREVRANGE key start stop [WITHSCORES]
</code></pre>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; ZRANGE scoreboard 0 2
1) &quot;peter&quot;
2) &quot;tom&quot;
3) &quot;david&quot;
127.0.0.1:6379&gt; ZRANGE scoreboard 0 2 WITHSCORES
1) &quot;peter&quot;
2) &quot;70&quot;
3) &quot;tom&quot;
4) &quot;89&quot;
5) &quot;david&quot;
6) &quot;100&quot;

127.0.0.1:6379&gt; ZREVRANGE scoreboard 0 2 WITHSCORES
1) &quot;david&quot;
2) &quot;100&quot;
3) &quot;tom&quot;
4) &quot;89&quot;
5) &quot;peter&quot;
6) &quot;70&quot;
</code></pre>
<ul>
<li>
<p>获取指定分数范围的元素</p>
<pre><code>  &gt;ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; ZRANGEBYSCORE scoreboard 80 100 WITHSCORES
1) &quot;tom&quot;
2) &quot;89&quot;
3) &quot;david&quot;
4) &quot;100&quot;
</code></pre>
<p>希望不包含端点值</p>
<pre><code>&gt;ZRANGEBYSCORE scoreboard 80 (100
正负无穷大 +inf -inf
</code></pre>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; ZRANGEBYSCORE scoreboard 80 (100 WITHSCORES
1) &quot;tom&quot;
2) &quot;89&quot;

127.0.0.1:6379&gt; ZRANGEBYSCORE scoreboard 80 +inf WITHSCORES
1) &quot;tom&quot;
2) &quot;89&quot;
3) &quot;david&quot;
4) &quot;100&quot;

127.0.0.1:6379&gt; ZRANGEBYSCORE scoreboard 80 +inf WITHSCORES LIMIT 0 1
1) &quot;tom&quot;
2) &quot;89&quot;
</code></pre>
<ul>
<li>
<p>增加某个元素分数</p>
<pre><code>  &gt;ZINCRBY key increment member
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; ZSCORE scoreboard tom
&quot;89&quot;
127.0.0.1:6379&gt; ZINCRBY scoreboard 2 tom
&quot;91&quot;
127.0.0.1:6379&gt; ZSCORE scoreboard tom
&quot;91&quot;
</code></pre>
<ul>
<li>
<p>获得集合中元素数量</p>
<pre><code>  &gt;ZCARD key
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; ZCARD scoreboard
(integer) 3
</code></pre>
<ul>
<li>
<p>获得指定分数范围内的元素个数</p>
<pre><code>  &gt;ZCOUNT key min max
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; ZCOUNT scoreboard 80 100
(integer) 2
</code></pre>
<ul>
<li>
<p>删除一个或多个元素</p>
<pre><code>  &gt;ZREM key member [member ...]
</code></pre>
</li>
<li>
<p>按照排名范围删除元素</p>
<pre><code>  &gt;ZREMRANGEBYRANK key start stop
</code></pre>
</li>
<li>
<p>按照分数范围删除</p>
<pre><code>  &gt;ZREMRANGEBYSCORE key min max
</code></pre>
</li>
<li>
<p>获得元素排名</p>
<pre><code>  &gt;ZRANK key member #从小到大
  &gt;ZREVRANK key member #相反
</code></pre>
</li>
</ul>
<p>e.g.</p>
<pre><code>127.0.0.1:6379&gt; ZRANK scoreboard tom
(integer) 1
</code></pre>
<ul>
<li>
<p>计算有序集合的交集</p>
<pre><code>  &gt;ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

  #返回值为destination中元素个数
  #AGGREGATE sum默认值，destination键中元素分数是每个参与计算的集合中该元素分数的和
          min取最小值
          max取最大值
  #WEIGHTS 设置每个集合的权重，每个集合在参与计算时分数会被乘以权重
</code></pre>
</li>
</ul>
<h2 id="其他">其他</h2>
<h3 id="事务">事务</h3>
<p>Redis中事务是一组命令的集合, 一个事务中的命令要么都执行, 要么都不执行</p>
<pre><code>&gt; MULTI
&gt; SADD k1 v1
&gt; SADD k2 v2
&gt; EXEC
</code></pre>
<p>注意, 不支持回滚功能</p>
<h3 id="sort">SORT</h3>
<p>可以对列表/集合/有序集合进行排序</p>
<p>最强大最复杂, 用不好可能成为性能瓶颈 O(n + mlogm) n为排序个数, m为返回个数</p>
<pre><code>&gt;SORT key #从小到大
&gt;SORT key DESC #从大到小
</code></pre>
<p>SORTBY</p>
<pre><code>127.0.0.1:6379&gt; LPUSH sortbylist  2 1 3
(integer) 3
127.0.0.1:6379&gt; SET itemscore:1 50
OK
127.0.0.1:6379&gt; SET itemscore:2 100
OK
127.0.0.1:6379&gt; SET itemscore:3 -10
OK
127.0.0.1:6379&gt; SORT sortbylist BY itemscore:* DESC
1) &quot;2&quot;
2) &quot;1&quot;
3) &quot;3&quot;
</code></pre>
<p>SORTBY GET</p>
<pre><code>127.0.0.1:6379&gt; SORT sortbylist BY itemscore:* DESC GET POST:*-&gt;title GET POST:*-&gt;time
</code></pre>
<p>SOTRBY GET STORE</p>
<pre><code>127.0.0.1:6379&gt; SORT sortbylist BY itemscore:* DESC GET POST:*-&gt;title GET POST:*-&gt;time STORE new_key
</code></pre>
<h3 id="生存时间">生存时间</h3>
<p>TTL, time to live</p>
<p>时效数据，过一定时间删除这些数据</p>
<pre><code>#设置
&gt;EXPIRE key seconds
1表示设置成功, 0表键不存在或设置失败

#查询
&gt;TTL key
键不存在返回-1 or 没有设置生存时间

#去除时效
&gt;PERSIST key

#SET/GETSET为键赋值会同时清除键的生存时间
</code></pre>
<h3 id="任务队列">任务队列</h3>
<p>一般队列</p>
<pre><code>生产者 LPUSH
消费者 RPOP

BRPOP 和RPOP类似，但是当列表中没有元素时，BRPOP会一直阻塞住链接，直到有新元素加入
</code></pre>
<p>优先队列</p>
<pre><code>BLPOP key [key ...] timeout,同时检测多个键，如果所有键都没有元素则阻塞，如果其中有一个键有元素，则从该键中弹出元素
如果都有，则从左到右的顺序取第一个键中的一个元素

BLPOP queue:1 queue:2 queue:3 0
</code></pre>
<h3 id="发布订阅模式">发布/订阅模式</h3>
<p>进程间消息传递</p>
<p>订阅者：订阅者可以订阅一个或多个频道</p>
<pre><code>&gt;SUBSCRIBE channel1
</code></pre>
<p>发布者：可以向指定的频道发送消息，所有订阅此频道的订阅者都会受到此消息</p>
<pre><code>&gt;PUBLISH channel1 helloworld
</code></pre>
<h3 id="python中使用redis">Python中使用Redis</h3>
<p>官方推荐<a href="https://github.com/andymccurdy/redis-py">redis-py</a></p>
<p>安装</p>
<pre><code>sudo pip install redis
sudo easy_install redis
</code></pre>
<p>使用</p>
<pre><code>redis-py提供两个类Redis和StrictRedis用于实现Redis的命令，
StrictRedis用于实现大部分官方的命令，并使用官方的语法和命令（比如，SET命令对应与StrictRedis.set方法）
Redis是StrictRedis的子类，用于向后兼容旧版本的redis-py

&gt;&gt;&gt; import redis
&gt;&gt;&gt; r = redis.StrictRedis(host='localhost', port=6379, db=0)
&gt;&gt;&gt; r.set('foo', 'bar')
True
&gt;&gt;&gt; r.get('foo')
'bar'
</code></pre>
<p>connection pool</p>
<pre><code>管理对一个redis server的所有连接，避免每次建立、释放连接的开销。
默认，每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池，然后作为参数Redis，这样就可以实现多个Redis实例共享一个连接池。

pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool)
</code></pre>
<p>pipeline机制</p>
<pre><code>可以在一次请求中执行多个命令，这样避免了多次的往返时延
当一组命令中每条命令都不依赖于之前的执行结果, 可以使用

pipe = r.pipeline()
pipe.set('one', 'first')
pipe.set('two', 'second')
pipe.execute()

pipeline中的操作是原子的，要改变这种方式，可以传入transaction=False

pipe = r.pipeline(transaction=False)
</code></pre>
<h3 id="实际实例">实际实例</h3>
<p>什么应用，都用什么方式处理的</p>
<p>1.一般的缓存</p>
<p>用字符串类型足矣,
e.g.注册时得用户名冲突,在线用户</p>
<pre><code>&gt;SET key value
&gt;GET
</code></pre>
<p>一些缓存场景</p>
<pre><code>存储会话缓存(Session Cache), 利用持久化, 保存一些信息, 例如购物车
全页缓存(FPC)
</code></pre>
<p>2.计数,访问量统计，自增id等</p>
<pre><code>&gt;INCR key
</code></pre>
<p>3.存储对象实例</p>
<pre><code>用散列
&gt;HSET key field value
&gt;HGET key field
</code></pre>
<p>4.存列表，队列相关</p>
<p>作为队列使用</p>
<p>文章分类列表，评论列表等</p>
<pre><code>用列表
&gt;LPUSH key value
&gt;RPUSH key value

&gt;LPOP key
&gt;RPOP key
</code></pre>
<p>5.集合相关的</p>
<p>标签云等</p>
<pre><code>&gt;SADD key member
&gt;SREM key member
</code></pre>
<p>6.排序相关</p>
<p>排行榜</p>
<p>访问量排序,点击量等</p>
<pre><code>用有序结合
&gt;ZADD key score member
</code></pre>
<p>7.访问频率控制</p>
<p>设置key的失效时间
用 INCR
访问时检查次数, 若超过阈值, 走限制逻辑</p>
<p>or 记录次数, 超过阈值, 检查与最早一个相差是不是1分钟, 是, 走限制逻辑, 不是, 现有时间加入列表, 同时删除最早元素</p>
<p>8.发布/订阅</p>
<p>会用到的</p>
<h3 id="管理">管理</h3>
<p>重启后数据不丢失, 两种方式, 可单独使用或者结合使用</p>
<p>持久化：</p>
<p>RDB</p>
<pre><code>快照,符合一定条件时，将内存中的所有数据进行快照并存储到硬盘上
快照的条件可以在配置文件中配置, 两个参数: 时间和改动的键的个数
Redis默认采用的持久化方式

过程
1. Redis使用fork函数复制一份当前进程(父进程)的副本(子进程) (存的是fork时刻的数据)
   写时复制copy-on-write 开始时父子共享同一内存数据, 当父进程修改某片数据, 操作系统复制一份以保证子进程数据不受影响
2. 父进程继续接收命令, 子进程开始将内存中数据写入硬盘中临时文件
3. 写入结束后, 替换旧的RDB文件

任意时刻rdb文件都是完整地, 可以用于备份

可以手动发SAVE / BGSAVE 让redis执行快照(前者由主进程进行快照操作,阻塞其他请求, 后者fork子进程)
</code></pre>
<p>AOF</p>
<pre><code>每次执行一条会修改Redis中数据的命令，Redis会将该命令写到硬盘中的AOF文件
开启, 设置 appendonly yes
默认文件名 appendonly.aof 可以通过appendfilename设置

纯文本文件, 每当达到一定条件时可以进行重写
auto-aof-rewrite-percentage 100 #超过上一次百分比
auto-aof-rewrite-min-size 64mb #允许重写的最小aof文件大小

默认30s, 执行的命令同步到aof
可以配置
appendfsync everysec # 每秒一次
</code></pre>
<p>Redis可以配置主从数据库</p>
<p>redis-server &ndash;port 6380 &ndash;slaveof 127.0.0.1 6379</p>
<p>复制原理: 从数据库启动, 向主库发SYNC, 主库后台开始保存快照(RDB), 并将保存期间的命令缓存起来. 快照完成后, 将快照文件和缓存的命令发送给从库,
从库收到后载入快照文件并执行命令. 不支持断点续传</p>
<p>读写分离: 主库禁用持久化, 从库启用. 从库崩溃, 重启自动更新. 主库崩溃, 从库提升为主库再修复</p>
<p>常用查看命令</p>
<p>telnet连接</p>
<pre><code>&gt;telnet 127.0.0.1 6379
</code></pre>
<p>设定最大可用内存</p>
<p>如果服务器内存有限, 大量使用缓存且生存时间设置过长会导致Redis占满内存. or 为了防止占用内存过大而将生存时间设太短导致命中率过低</p>
<p>可以限制redis使用的最大内存, 按照一定规则淘汰不需要的键</p>
<pre><code>配置文件 
maxmemory 限制最大可用内存大小(单位字节)
maxmemory-policy 超过限制时的删除策略，一直删除直到小于指定内存

volatile-lru  使用LRU算法删除一个键，只对设置了生存时间的
allkeys-lru   使用LRU算法删除一个键
volatile-random 随机，只对设置了生存时间的
allkeys-random
volatile-ttl    删除生存时间最近的
noeviction      不删除键，返回错误
</code></pre>
<p>耗时命令日志</p>
<pre><code>&gt; SLOWLOG GET
</code></pre>
<h3 id="其他-1">其他</h3>
<p>批量删除</p>
<pre><code>#删除 /test/*开始的
./redis-cli -a password -n 0 keys &quot;/test/*&quot; | xargs ./redis-cli -a password -n 0 del
</code></pre>
<p>精简键名和键值, 最直观的减少内存占用的方式</p>
<hr>
<p>The end! To be continue &hellip;.</p>
]]></content>
		</item>
		
		<item>
			<title>博客程序TODO列表</title>
			<link>https://wklken.me/posts/2013/10/15/blog-todo-list.html</link>
			<pubDate>Tue, 15 Oct 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/10/15/blog-todo-list.html</guid>
			<description>blog使用的是pelican，主题是拿开源的进行自己修改的，具体见底部 之前做的时候，列了一份修改的计划，种种原因，只完成了部分 记下来，后续</description>
			<content type="html"><![CDATA[<p>blog使用的是pelican，主题是拿开源的进行自己修改的，具体见底部</p>
<p>之前做的时候，列了一份修改的计划，种种原因，只完成了部分</p>
<p>记下来，后续抽空修改吧</p>
<p>需要学习html/css/js基础知识</p>
<p>==================================</p>
<p>update: 2014-05-11</p>
<p>新增主题,取名luna, 类似iawrite专注模式</p>
<pre><code>1. 文章系列
2. markdown nav 侧边栏
3. 更漂亮的翻页
4. 分类页
5. archive页   区分年份, 加timeline
6. aboutme页面,使用imporess.js
7. tags标签展示
8. 分享到?
</code></pre>
<p>现在更倾向于,极简, 阅读和用户体验更好,需要背景色和字体选择更好</p>
<p>==================================</p>
<blockquote>
<p>TODO List</p>
</blockquote>
<pre><code>1.分类页，优化展示
2.专题页，优化展示
3.首页，文章列表的展示，方式上的修改
4.风格，更喜欢蓝色水晶
5.markdown超长不换行的处理
6.代码高亮: python/shell及其他 [DONE 20131021]
7.markdown标签浮动图层导航
8.csdn剩余博文迁移和优化
9.原有个人blog文章整理，修改
10.SEO入口，变成唯一入口，优化之，其他的下掉
11.整体风格统一，优化阅读体验 [DONE 20140601]
12.加入微博
13.书单的处理，要有图片
14.个人页的处理，个人简历，图
15.上一篇，下一篇 [DONE]
16.友链
17.近期文章
18,查看更多
19.资源页面 某些系列的入口
20.时间线
21.分享 [DONE 2014-11-09]
22.搜索入口 [DONE 2014-11-09]
</code></pre>
<p>主要困难还是审美不行啊，修改前端对我来说是蛮大的挑战</p>
<p>尝试处理下</p>
<hr>
<p>update 20140617</p>
<p>修改使用新主题, 编程focus模式, 极简主义</p>
<p>the end</p>
<p>wklken</p>
<p>2013-10-15</p>
]]></content>
		</item>
		
		<item>
			<title>搜索下拉提示框实现(python/golang)</title>
			<link>https://wklken.me/posts/2013/10/13/search-suggestion.html</link>
			<pubDate>Sun, 13 Oct 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/10/13/search-suggestion.html</guid>
			<description>在百度，google，taobao.com的搜索框，输入某个关键字时，会存在下拉提示，提示关键字列表 国庆的时候，想了下如何实现这个功能 用py</description>
			<content type="html"><![CDATA[<p>在百度，google，taobao.com的搜索框，输入某个关键字时，会存在下拉提示，提示关键字列表</p>
<p>国庆的时候，想了下如何实现这个功能</p>
<p>用python和golang搞了个基本版本，国庆后直接上到线上了</p>
<p>项目地址:</p>
<pre><code>https://github.com/wklken/suggestion
</code></pre>
<p>Demo地址：(30w关键字，每天约三百万次请求)</p>
<pre><code>http://s.kuaiwan.com/
</code></pre>
<p>基本机制：离线给出关键字和权重，用trie数据结构，逐一加入，建立一棵树，请求进来时，遍历树，获取节点，排序返回</p>
<p>2013-10-13</p>
<p>wklken</p>
]]></content>
		</item>
		
		<item>
			<title>小记-搭建discourse</title>
			<link>https://wklken.me/posts/2013/09/14/discourse.html</link>
			<pubDate>Sat, 14 Sep 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/09/14/discourse.html</guid>
			<description>早上，花了点时间，在刚买的digital ocean vps上尝试搭建了下discourse 网上的教程N多，最终决定使用官方的搭建 教程 对我这个ruby盲</description>
			<content type="html"><![CDATA[<p>早上，花了点时间，在刚买的digital ocean vps上尝试搭建了下<a href="http://www.discourse.org/">discourse</a></p>
<p>网上的教程N多，最终决定使用官方的搭建 <a href="https://github.com/discourse/discourse/blob/master/docs/INSTALL-ubuntu.md">教程</a></p>
<p>对我这个ruby盲来说，第一次接触ruby环境，第一个感觉，这安装流程也太TM多了吧，要配的东西老多了，但另一方面，
也发现，python的环境配置相关的东西，体验上和ruby比起来还是有差距的,</p>
<p>一个半小时，轻轻松松，也有点莫名其妙，搭建完了</p>
<p>后端ruby resultful API，我在思考，貌似用Python也可以实现，就是不知道迁移这玩意儿耗时几何，</p>
<p>研究研究，可以的话用python+flask实现一把</p>
<p>好了，哥的博客有了discourse，地址 <a href="http://bbs.wklken.me/">http://bbs.wklken.me</a></p>
<p>后续捣鼓下，研究下ruby</p>
<p>2013-09-14</p>
<p>wklken</p>
]]></content>
		</item>
		
		<item>
			<title>Flask使用小结</title>
			<link>https://wklken.me/posts/2013/09/09/python-framework-flask.html</link>
			<pubDate>Mon, 09 Sep 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/09/09/python-framework-flask.html</guid>
			<description>flask 使用的一些整理 资源 Flask 文档|英文| expore flask| 快速教材| flask-admin| Flask-DebugToolbar| Flask-Login| Flask-Cache| flask-sqlalchemy| flask-security| Flask-mako| Flask-Genshi| WTForms Flask Extensions 最简单的hello world #!/usr/bin/env python # encoding: utf-8 from flask import Flask app = Flask(__name__) @app.route(&#39;/&#39;) def index(): return &#39;hello world&#39; if __name__ == &#39;__main__&#39;: app.run(debug=True) #app.run(host=&#39;127.0.0.1&#39;, port=8000)</description>
			<content type="html"><![CDATA[<p><img src="/imgs/python/flask.png" alt="flask"></p>
<p>flask 使用的一些整理</p>
<h3 id="资源">资源</h3>
<p>Flask  <a href="https://dormousehole.readthedocs.org/en/latest/">文档</a>|<a href="http://flask.pocoo.org/docs/">英文</a>|
<a href="http://exploreflask.com/">expore flask</a>|
<a href="http://www.oschina.net/translate/the-flask-mega-tutorial-part-i-hello-world">快速教材</a>|
<a href="http://flask-admin.readthedocs.org/en/latest/">flask-admin</a>|
<a href="http://flask-debugtoolbar.readthedocs.org/en/latest/">Flask-DebugToolbar</a>|
<a href="http://flask-login.readthedocs.org/en/latest/">Flask-Login</a>|
<a href="http://pythonhosted.org/Flask-Cache/">Flask-Cache</a>|
<a href="http://pythonhosted.org/Flask-SQLAlchemy/">flask-sqlalchemy</a>|
<a href="http://pythonhosted.org/Flask-Security/">flask-security</a>|
<a href="http://pythonhosted.org/Flask-Mako/">Flask-mako</a>|
<a href="http://pythonhosted.org/Flask-Genshi/">Flask-Genshi</a>|
<a href="http://wtforms.simplecodes.com/docs/1.0.1/index.html">WTForms</a></p>
<p><a href="http://flask.pocoo.org/extensions/">Flask Extensions</a></p>
<hr>
<h3 id="最简单的hello-world">最简单的hello world</h3>
<pre><code>#!/usr/bin/env python
# encoding: utf-8

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return 'hello world'

if __name__ == '__main__':
    app.run(debug=True)
    #app.run(host='127.0.0.1', port=8000)
</code></pre>
<p>之后，访问http://localhost:5000</p>
<h3 id="支持postget提交">支持post/get提交</h3>
<pre><code>:::python
@app.route('/', methods=['GET', 'POST'])
</code></pre>
<h3 id="多个url指向">多个url指向</h3>
<pre><code>:::python
@app.route('/')
@app.route('/index')
</code></pre>
<h3 id="不管postget使用统一的接收">不管post/get使用统一的接收</h3>
<pre><code>:::python
from flask import request
args = request.args if request.method == 'GET' else request.form
a = args.get('a', 'default')
</code></pre>
<h3 id="处理json请求">处理json请求</h3>
<p>request的header中</p>
<pre><code>&quot;Content-Type&quot;: &quot;application/json&quot;
</code></pre>
<p>处理时:</p>
<pre><code>data = request.get_json(silent=False)
</code></pre>
<h3 id="获取post提交中的checkbox">获取post提交中的checkbox</h3>
<pre><code>{%for page in pages %}
&lt;tr&gt;&lt;td&gt;&lt;input type=checkbox name=do_delete value=&quot;{{ page['id'] }}&quot;&gt;&lt;/td&gt;&lt;td&gt;
{%endfor%}

page_ids = request.form.getlist(&quot;do_delete&quot;)
</code></pre>
<h3 id="使用url中的参数">使用url中的参数</h3>
<pre><code>:::python
@app.route('/query/&lt;qid&gt;/')
def query(qid):
    pass
</code></pre>
<h3 id="在request开始结束dosomething">在request开始结束dosomething</h3>
<p>一般可以处理数据库连接等等</p>
<pre><code>:::python
from flask import g

app = .....

@app.before_request
def before_request():
    g.session = create_session()

@app.teardown_request
def teardown_request(exception):
    g.session.close()
</code></pre>
<h3 id="注册jinja2模板中使用的过滤器">注册Jinja2模板中使用的过滤器</h3>
<pre><code>:::python
@app.template_filter('reverse')
def reverse_filter(s):
    return s[::-1]
</code></pre>
<p>或者</p>
<pre><code>:::python
def reverse_filter(s):
    return s[::-1]
app.jinja_env.filters['reverse'] = reverse_filter
</code></pre>
<p>可以这么用</p>
<pre><code>:::python
def a():...
def b():...

FIL = {'a': a, 'b':b}
app.jinja_env.filters.update(FIL)
</code></pre>
<h3 id="注册jinja2模板中使用的全局变量">注册Jinja2模板中使用的全局变量</h3>
<pre><code>:::python
JINJA2_GLOBALS = {'MEDIA_PREFIX': '/media/'}
app.jinja_env.globals.update(JINJA2_GLOBALS)
</code></pre>
<h3 id="定义应用使用的template和static目录">定义应用使用的template和static目录</h3>
<pre><code>:::python
app = Flask(__name__, template_folder=settings.TEMPLATE_FOLDER, static_folder = settings.STATIC_PATH)
</code></pre>
<h3 id="使用blueprint">使用Blueprint</h3>
<pre><code>:::python
from flask import Blueprint
bp_test = Blueprint('test', __name__)
#bp_test = Blueprint('test', __name__, url_prefix='/abc')

@bp_test.route('/')

--------
from xxx import bp_test

app = Flask(__name__)
app.register_blueprint(bp_test)
</code></pre>
<p>实例:</p>
<pre><code>bp_video = Blueprint('video', __name__, url_prefix='/kw_news/video')
@bp_video.route('/search/category/', methods=['POST', 'GET'])
#注意这种情况下Blueprint中url_prefix不能以 '/' 结尾, 否则404
</code></pre>
<h3 id="使用session">使用session</h3>
<p>包装cookie实现的，没有session id</p>
<pre><code>:::python
app.secret_key = 'PS#yio`%_!((f_or(%)))s'

然后
from flask import session

session['somekey'] = 1
session.pop('logged_in', None)

session.clear()

#过期时间,通过cookie实现的
from datetime import timedelta
session.permanent = True
app.permanent_session_lifetime = timedelta(minutes=5)
</code></pre>
<h3 id="反向路由">反向路由</h3>
<pre><code>:::python
from flask import url_for, render_template

@app.route(&quot;/&quot;)
def home():
    login_uri = url_for(&quot;login&quot;, next=url_for(&quot;home&quot;))
    return render_template(&quot;home.html&quot;, **locals())
</code></pre>
<h3 id="上传文件">上传文件</h3>
<pre><code>:::html
&lt;form action=&quot;/image/upload/&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;
&lt;input type=&quot;file&quot; name=&quot;upload&quot; /&gt;
</code></pre>
<p>接收</p>
<pre><code>:::python
f = request.files.get('upload')
img_data = f.read()
</code></pre>
<h3 id="直接返回某个文件">直接返回某个文件</h3>
<pre><code>:::python
return send_file(settings.TEMPLATE_FOLDER + 'tweet/tweet_list.html')
</code></pre>
<h3 id="请求重定向">请求重定向</h3>
<p><a href="http://flask.pocoo.org/docs/api/#flask.redirect">文档</a></p>
<p>flask.redirect(location, code=302)
the redirect status code. defaults to 302.Supported codes are 301, 302, 303, 305, and 307. 300 is not supported.</p>
<pre><code>:::python
@app.route('/')
def hello():
    return redirect(url_for('foo'))

@app.route('/foo')
def foo():
    return'Hello Foo!'
</code></pre>
<h3 id="获取用户真实ip">获取用户真实ip</h3>
<p>从request.headers获取</p>
<pre><code>:::python
real_ip = request.headers.get('X-Real-Ip', request.remote_addr)
</code></pre>
<p>或者, 使用werkzeug的middleware <a href="http://werkzeug.pocoo.org/docs/0.9/contrib/fixers/">文档</a></p>
<pre><code>from werkzeug.contrib.fixers import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app)
</code></pre>
<h3 id="return-json--jsonp">return json &amp; jsonp</h3>
<pre><code>:::python
import json
from flask import jsonify, Response, json

data = [] # or others
return jsonify(ok=True, data=data)

jsonp_callback =  request.args.get('callback', '')
if jsonp_callback:
    return Response(
            &quot;%s(%s);&quot; % (jsonp_callback, json.dumps({'ok': True, 'data':data})),
            mimetype=&quot;text/javascript&quot;
            )
return ok_jsonify(data)
</code></pre>
<h3 id="配置读取方法">配置读取方法</h3>
<pre><code>:::python
# create our little application :)
app = Flask(__name__)

# Load default config and override config from an environment variable
app.config.update(dict(
    DATABASE='/tmp/flaskr.db',
    DEBUG=True,
    SECRET_KEY='development key',
    USERNAME='admin',
    PASSWORD='default'
))
app.config.from_envvar('FLASKR_SETTINGS', silent=True)


------------------
# configuration
DATABASE = '/tmp/minitwit.db'
PER_PAGE = 30
DEBUG = True
SECRET_KEY = 'development key'

# create our little application :)
app = Flask(__name__)
app.config.from_object(__name__)
app.config.from_envvar('MINITWIT_SETTINGS', silent=True)
</code></pre>
<h3 id="几个不常用的方法">几个不常用的方法</h3>
<pre><code>:::python
from flask import abort, flash

abort
if not session.get('logged_in'):
    abort(401)

flash
flash('New entry was successfully posted')
</code></pre>
<h3 id="异步调用">异步调用</h3>
<p>想在flask的一个请求中处理异步, 除了使用消息系统, 可以用简单的线程处理</p>
<pre><code>from threading import Thread

def async(f):
    def wrapper(*args, **kwargs):
        thr = Thread(target=f, args=args, kwargs=kwargs)
        thr.start()
    return wrapper

@async
def dosomething(call_args):
    print call_args


in a request handler, call `dosomething`
</code></pre>
<h3 id="error-handler">error handler</h3>
<pre><code>@app.errorhandler(404)
def not_found_error(error):
    return render_template('404.html'), 404

@app.errorhandler(500)
def internal_error(error):
    db.session.rollback()
    return render_template('500.html'), 500
</code></pre>
<h3 id="项目配置">项目配置</h3>
<p>1.直接</p>
<pre><code>app.config['HOST']='xxx.a.com'
print app.config.get('HOST')
</code></pre>
<p>2.环境变量</p>
<pre><code>export MyAppConfig=/path/to/settings.cfg
app.config.from_envvar('MyAppConfig')
</code></pre>
<p>3.对象</p>
<pre><code> class Config(object):
     DEBUG = False
     TESTING = False
     DATABASE_URI = 'sqlite://:memory:'

 class ProductionConfig(Config):
     DATABASE_URI = 'mysql://user@localhost/foo'

 app.config.from_object(ProductionConfig)
 print app.config.get('DATABASE_URI') # mysql://user@localhost/foo
</code></pre>
<p>4.文件</p>
<pre><code># default_config.py
HOST = 'localhost'
PORT = 5000
DEBUG = True

app.config.from_pyfile('default_config.py')
</code></pre>
<h3 id="eg-一个create_app方法">EG. 一个create_app方法</h3>
<pre><code>:::python
from flask import Flask, g

def create_app(debug=settings.DEBUG):
    app = Flask(__name__,
                template_folder=settings.TEMPLATE_FOLDER,
                static_folder=settings.STATIC_FOLDER)

    app.register_blueprint(bp_test)

    app.jinja_env.globals.update(JINJA2_GLOBALS)
    app.jinja_env.filters.update(JINJA2_FILTERS)

    app.secret_key = 'PO+_)(*&amp;678OUIJKKO#%_!(((%)))'

    @app.before_request
    def before_request():
        g.xxx = ...    #do some thing

    @app.teardown_request
    def teardown_request(exception):
        g.xxx = ...    #do some thing

    return app

app = create_app(settings.DEBUG)
host=settings.SERVER_IP
port=settings.SERVER_PORT
app.run(host=host, port=port)
</code></pre>
<hr>
<p>change log:</p>
<pre tabindex="0"><code>2013-09-09 create
2014-10-25 update
</code></pre><p>wklken</p>
<p>2013-09-09</p>
]]></content>
		</item>
		
		<item>
			<title>[翻译]快速Python性能优化要点</title>
			<link>https://wklken.me/posts/2013/09/07/quick-python-performance-optimization.html</link>
			<pubDate>Sat, 07 Sep 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/09/07/quick-python-performance-optimization.html</guid>
			<description>翻译，两篇博文 Quick Python Performance Optimization一 二, 很短 第一篇 仅是解释,如何用正确并且高效的方式完成在日常Python编码中简单的事情 1.在i</description>
			<content type="html"><![CDATA[<p>翻译，两篇博文 Quick Python Performance Optimization<a href="http://infiniteloop.in/blog/quick-python-performance-optimization-part-i/">一</a>  <a href="http://infiniteloop.in/blog/quick-python-performance-optimization-part-ii/">二</a>, 很短</p>
<blockquote>
<p>第一篇</p>
</blockquote>
<p>仅是解释,如何用正确并且高效的方式完成在日常Python编码中简单的事情</p>
<p>1.在ipython交互shell中使用%timeit (per line) 和 %prun (cProfile)</p>
<p>测量你的代码，并且找到性能的瓶颈.这和&quot;过早优化是一切罪恶的根源&quot;并不矛盾.这是第一级的性能优化，而不是重量级的性能优化序列.</p>
<p>更多的测量Python代码性能，可参考 <a href="http://www.huyng.com/posts/python-performance-analysis/">http://www.huyng.com/posts/python-performance-analysis/</a></p>
<p>另一个有趣的库，line_profiler,逐行的性能测量<a href="https://bitbucket.org/robertkern/line_profiler">https://bitbucket.org/robertkern/line_profiler</a></p>
<p>2.减少函数调用次数.如果你需要处理一个列表，传递整个列表，而不是遍历列表，调用函数传递每个元素并获取返回值</p>
<p>3.使用xrange代替range</p>
<p>xrange是range的C语言实现&ndash;更高效的内存使用.</p>
<p>4.对于大数据,使用numpy, 性能优于标准数据结构</p>
<p>5.使用&quot;&quot;.join(string) 代替字符串 + 或 +=</p>
<p>6.while 1 比 while True 快</p>
<p>7.性能: 列表解析 &gt; for 循环 &gt; while 循环</p>
<p>遍历list时，列表解析性能最优，while循环最差(需要一个外部计数器)</p>
<p>8.使用 cProfile, cStringIO 和 cPickle</p>
<p>总是使用模块可用的C版本</p>
<p>9.使用局部变量</p>
<p>局部变量性能优于全局变量，内建变量及属性查找</p>
<p>10.存在序列和对待器版本- 迭代对象内存更优. 使用 itertools</p>
<p>尽可能的创建生成器和使用yield.相对于常规序列实现方式，性能更优</p>
<p><a href="https://wklken.me/posts/2013/07/18/python-translate-yield.html">http://www.diveinto.org/python3/iterators.html</a></p>
<p><a href="http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained">http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained</a>  <a href="https://wklken.me/posts/2013/07/18/python-translate-yield.html">中文翻译</a></p>
<blockquote>
<p>第二篇</p>
</blockquote>
<p>11.在适用的场景中尽可能地使用map,reduce,filter替代for循环</p>
<p>12.检查元素归属 &lsquo;a in b&rsquo;, dict或set 优于list/tuple.</p>
<p>13.处理大数据时，尽可能使用不可变数据类型，更快 - tuples &gt; list</p>
<p>14.插入list的复杂度是O(n)</p>
<p>15.如果你需要操作序列的开始和结束，使用deque</p>
<p>16.del - 使用后删除无用对象</p>
<pre><code>Python自身可以执行，通过gc模块，或者
在对象的魔术方法中写入__del__方法，或者
最简单的方式，使用后del删除
</code></pre>
<p>17.使用time.clock()</p>
<p>18.GIL(<a href="http://wiki.python.org/moin/GlobalInterpreterLock">http://wiki.python.org/moin/GlobalInterpreterLock</a>) - GIL is a demon.</p>
<p>GIL允许每个进程中运行一个python本地线程,防止CPU级别的并行.  尝试使用ctypes和原生c库来解决这个问题.  当你无法用Python进一步进行优化的时候.记住，你还有一种选择，使用原生C实现性能糟糕的函数，并且通过Python c绑定调用.  其他库，例如gevent,同样可以解决这个问题，并且某些扩展非常成功</p>
<p>TL,DR: 在写代码时，考虑一圈：数据结构，构造迭代，内建函数和必要情况下使用GIL构造C扩展</p>
]]></content>
		</item>
		
		<item>
			<title>Python-进阶-编码处理小结</title>
			<link>https://wklken.me/posts/2013/08/31/python-extra-coding-intro.html</link>
			<pubDate>Sat, 31 Aug 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/08/31/python-extra-coding-intro.html</guid>
			<description>整理下python编码相关的内容 注意: 以下讨论为Python2.x版本, Py3k的待尝试 开始 用python处理中文时，读取文件或消息，htt</description>
			<content type="html"><![CDATA[<p>整理下python编码相关的内容</p>
<p>注意: 以下讨论为Python2.x版本, Py3k的待尝试</p>
<hr>
<h2 id="开始">开始</h2>
<p>用python处理中文时，读取文件或消息，http参数等等</p>
<p>一运行，发现乱码(字符串处理，读写文件，print)</p>
<p>然后，大多数人的做法是，调用encode/decode进行调试，并没有明确思考为何出现乱码</p>
<p>所以调试时最常出现的错误</p>
<p>错误1</p>
<pre><code>:::pythontraceback
Traceback (most recent call last):
File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)
</code></pre>
<p>错误2</p>
<pre><code>:::pythontraceback
Traceback (most recent call last):
File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
File &quot;/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings/utf_8.py&quot;, line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
</code></pre>
<hr>
<h2 id="首先">首先</h2>
<p>必须有大体概念，了解下字符集，<a href="http://zh.wikipedia.org/wiki/%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81">字符编码</a></p>
<p><a href="http://zh.wikipedia.org/zh/ASCII">ASCII</a> | <a href="http://zh.wikipedia.org/zh/Unicode">Unicode</a> | <a href="http://zh.wikipedia.org/zh/UTF-8">UTF-8</a> | 等等</p>
<p><a href="http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html"> 字符编码笔记：ASCII，Unicode和UTF-8 </a></p>
<p><a href="http://www.searchtb.com/2012/04/chinese_encode.html">淘宝搜索技术博客-中文编码杂谈</a></p>
<hr>
<h2 id="str-和-unicode">str 和 unicode</h2>
<blockquote>
<p>str和unicode都是basestring的子类</p>
</blockquote>
<p>所以有判断是否是字符串的方法</p>
<pre><code>:::python
def is_str(s):
    return isinstance(s, basestring)
</code></pre>
<blockquote>
<p>str和unicode 转换</p>
</blockquote>
<p>decode <a href="http://www.tutorialspoint.com/python/string_decode.htm">文档</a></p>
<p>encode <a href="http://www.tutorialspoint.com/python/string_encode.htm">文档</a></p>
<pre><code>str  -&gt; decode('the_coding_of_str') -&gt; unicode
unicode -&gt; encode('the_coding_you_want') -&gt; str
</code></pre>
<blockquote>
<p>区别</p>
</blockquote>
<p>str是字节串，由unicode经过编码(encode)后的字节组成的</p>
<p>声明方式</p>
<pre><code>:::pythonconsole
s = '中文'
s = u'中文'.encode('utf-8')

&gt;&gt;&gt; type('中文')
&lt;type 'str'&gt;
</code></pre>
<p>求长度(返回字节数)</p>
<pre><code>:::pythonconsole
&gt;&gt;&gt; u'中文'.encode('utf-8')
'\xe4\xb8\xad\xe6\x96\x87'
&gt;&gt;&gt; len(u'中文'.encode('utf-8'))
6
</code></pre>
<p>unicode才是真正意义上的字符串，由字符组成</p>
<p>声明方式</p>
<pre><code>:::pythonconsole
s = u'中文'
s = '中文'.decode('utf-8')
s = unicode('中文', 'utf-8')

&gt;&gt;&gt; type(u'中文')
&lt;type 'unicode'&gt;
</code></pre>
<p>求长度(返回字符数),在逻辑中真正想要用的</p>
<pre><code>:::pythonconsole
&gt;&gt;&gt; u'中文'
u'\u4e2d\u6587'
&gt;&gt;&gt; len(u'中文')
2
</code></pre>
<blockquote>
<p>结论</p>
</blockquote>
<p>搞明白要处理的是str还是unicode, 使用对的处理方法(str.decode/unicode.encode)</p>
<p>下面是判断是否为unicode/str的方法</p>
<pre><code>:::pythonconsole
&gt;&gt;&gt; isinstance(u'中文', unicode)
True
&gt;&gt;&gt; isinstance('中文', unicode)
False

&gt;&gt;&gt; isinstance('中文', str)
True
&gt;&gt;&gt; isinstance(u'中文', str)
False
</code></pre>
<p>简单原则：不要对str使用encode，不要对unicode使用decode (事实上可以对str进行encode的，具体见最后，为了保证简单，不建议)</p>
<pre><code>:::pythonconsole
&gt;&gt;&gt; '中文'.encode('utf-8')
Traceback (most recent call last):
File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

&gt;&gt;&gt; u'中文'.decode('utf-8')
Traceback (most recent call last):
File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
File &quot;/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings/utf_8.py&quot;, line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
</code></pre>
<p>不同编码转换,使用unicode作为中间编码</p>
<pre><code>:::python
#s是code_A的str
s.decode('code_A').encode('code_B')
</code></pre>
<hr>
<h2 id="文件处理ide和控制台">文件处理,IDE和控制台</h2>
<p>处理流程，可以这么使用，把python看做一个水池，一个入口，一个出口</p>
<p>入口处，全部转成unicode, 池里全部使用unicode处理，出口处，再转成目标编码(当然，有例外，处理逻辑中要用到具体编码的情况)</p>
<pre><code>读文件

外部输入编码，decode转成unicode

处理(内部编码，统一unicode)

encode转成需要的目标编码

写到目标输出(文件或控制台)
</code></pre>
<p>IDE和控制台报错，原因是print时，编码和IDE自身编码不一致导致</p>
<p>输出时将编码转换成一致的就可以正常输出</p>
<pre><code>:::pythonconsole
&gt;&gt;&gt; print u'中文'.encode('gbk')
����
&gt;&gt;&gt; print u'中文'.encode('utf-8')
中文
</code></pre>
<hr>
<h2 id="建议">建议</h2>
<blockquote>
<p>规范编码</p>
</blockquote>
<p>统一编码，防止由于某个环节产生的乱码</p>
<p>环境编码，IDE/文本编辑器, 文件编码，数据库数据表编码</p>
<blockquote>
<p>保证代码源文件编码</p>
</blockquote>
<p>这个很重要</p>
<p>py文件默认编码是ASCII, 在源代码文件中，如果用到非ASCII字符，需要在文件头部进行编码声明 <a href="http://www.python.org/dev/peps/pep-0263/">文档</a></p>
<p>不声明的话，输入非ASCII会遇到的错误,必须放在文件第一行或第二行</p>
<pre><code>:::pythontraceback
File &quot;XXX.py&quot;, line 3
SyntaxError: Non-ASCII character '\xd6' in file c.py on line 3, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details
</code></pre>
<p>声明方法</p>
<pre><code># -*- coding: utf-8 -*-
或者
#coding=utf-8
</code></pre>
<p>若头部声明coding=utf-8, a = &lsquo;中文&rsquo; 其编码为utf-8</p>
<p>若头部声明coding=gb2312, a = &lsquo;中文&rsquo; 其编码为gbk</p>
<p>so, 同一项目中所有源文件头部统一一个编码,并且声明的编码要和源文件保存的编码一致(编辑器相关)</p>
<blockquote>
<p>在源代码用作处理的硬编码字符串，统一用unicode</p>
</blockquote>
<p>将其类型和源文件本身的编码隔离开, 独立无依赖方便流程中各个位置处理</p>
<pre><code>:::python
if s == u'中文':  #而不是 s == '中文'
    pass
#注意这里 s到这里时，确保转为unicode
</code></pre>
<p>以上几步搞定后，你只需要关注两个 unicode和 你设定的编码(一般使用utf-8)</p>
<blockquote>
<p>处理顺序</p>
</blockquote>
<pre><code>1. Decode early
2. Unicode everywhere
3. Encode later
</code></pre>
<h2 id="相关模块及一些方法">相关模块及一些方法</h2>
<blockquote>
<p>获得和设置系统默认编码</p>
</blockquote>
<pre><code>:::pythonconsole
&gt;&gt;&gt; import sys
&gt;&gt;&gt; sys.getdefaultencoding()
'ascii'

&gt;&gt;&gt; reload(sys)
&lt;module 'sys' (built-in)&gt;
&gt;&gt;&gt; sys.setdefaultencoding('utf-8')
&gt;&gt;&gt; sys.getdefaultencoding()
'utf-8'
</code></pre>
<blockquote>
<p>str.encode(&lsquo;other_coding&rsquo;)</p>
</blockquote>
<p>在python中，直接将某种编码的str进行encode成另一种编码str</p>
<pre><code>:::python
#str_A为utf-8
str_A.encode('gbk')

执行的操作是
str_A.decode('sys_codec').encode('gbk')
这里sys_codec即为上一步 sys.getdefaultencoding() 的编码
</code></pre>
<p>&lsquo;获得和设置系统默认编码&rsquo;和这里的str.encode是相关的，但我一般很少这么用，主要是觉得复杂不可控,还是输入明确decode，输出明确encode来得简单些(个人观点)</p>
<blockquote>
<p>chardet</p>
</blockquote>
<p>文件编码检测，<a href="https://pypi.python.org/pypi/chardet">下载</a></p>
<pre><code>:::pythonconsole
&gt;&gt;&gt; import chardet
&gt;&gt;&gt; f = open('test.txt','r')
&gt;&gt;&gt; result = chardet.detect(f.read())
&gt;&gt;&gt; result
{'confidence': 0.99, 'encoding': 'utf-8'}
</code></pre>
<blockquote>
<p>\u字符串转对应unicode字符串</p>
</blockquote>
<pre><code>:::pythonconsole
&gt;&gt;&gt; u'中'
u'\u4e2d'

&gt;&gt;&gt; s = '\u4e2d'
&gt;&gt;&gt; print s.decode('unicode_escape')
中

&gt;&gt;&gt; a = '\\u4fee\\u6539\\u8282\\u70b9\\u72b6\\u6001\\u6210\\u529f'
&gt;&gt;&gt; a.decode('unicode_escape')
u'\u4fee\u6539\u8282\u70b9\u72b6\u6001\u6210\u529f'
</code></pre>
<blockquote>
<p>python unicode文档</p>
</blockquote>
<p><a href="http://docs.python.org/2/tutorial/introduction.html#unicode-strings">入口</a></p>
<hr>
<p>好了，暂时就这么多，希望讲清楚了</p>
<p>thx</p>
<p>wklken</p>
<p>2013-08-31 于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>Python资源入口汇总</title>
			<link>https://wklken.me/posts/2013/08/27/python-sources.html</link>
			<pubDate>Tue, 27 Aug 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/08/27/python-sources.html</guid>
			<description>整理中，进度30% 官网 入口 官方文档 英文 document 2.7.6 入口| 标准库 document 3.x 入口 The Hitchhiker’s Guide to Python 入口 Python Monk 入口 中文 document 2.7 入口 非官方 google的p</description>
			<content type="html"><![CDATA[<p>整理中，进度30%</p>
<h3 id="官网">官网</h3>
<p><a href="http://www.python.org/">入口</a></p>
<h3 id="官方文档">官方文档</h3>
<blockquote>
<p>英文</p>
</blockquote>
<p>document 2.7.6  <a href="http://docs.python.org/2/index.html">入口</a>|
<a href="http://docs.python.org/2/library/">标准库</a></p>
<p>document 3.x    <a href="http://docs.python.org/3/index.html">入口</a></p>
<p>The Hitchhiker’s Guide to Python  <a href="http://docs.python-guide.org/en/latest/">入口</a></p>
<p>Python Monk <a href="https://pythonmonk.com/">入口</a></p>
<blockquote>
<p>中文</p>
</blockquote>
<p>document 2.7 <a href="http://www.pythondoc.com/pythontutorial27/index.html">入口</a></p>
<blockquote>
<p>非官方</p>
</blockquote>
<p>google的python文档 <a href="https://developers.google.com/edu/python/">Google&rsquo;s Python Class入口</a></p>
<p>pep8 <a href="http://www.python.org/dev/peps/pep-0008/">入口</a></p>
<p>zetcode <a href="http://zetcode.com/lang/python/">入口</a></p>
<p>building skills in Python <a href="http://www.itmaybeahack.com/book/python-2.6/html/index.html">入口</a></p>
<blockquote>
<p>风格</p>
</blockquote>
<p>Google Python 风格指南 <a href="http://zh-google-styleguide.readthedocs.org/en/latest/google-python-styleguide/">入口</a></p>
<blockquote>
<p>模块学习</p>
</blockquote>
<p>Python Module of the Week <a href="http://pymotw.com/2/contents.html">入口</a>|<a href="https://code.google.com/p/pymotwcn/">中文</a></p>
<blockquote>
<p>进阶必读</p>
</blockquote>
<p>英文版汇总 <a href="http://jessenoller.com/good-to-great-python-reads/">入口</a></p>
<p>中文版(准备翻译) <a href="">入口</a></p>
<h3 id="教程和书籍">教程和书籍</h3>
<p>python简明教程(A Byte of Python) <a href="http://woodpecker.org.cn/abyteofpython_cn/chinese/">入口</a> | <a href="http://lovejiani.com/python/">另一个入口</a></p>
<p>Think Python <a href="http://www.greenteapress.com/thinkpython/html/index.html">入口</a></p>
<p>Data Structures and Algorithms with Object-Oriented Design Patterns in Python <a href="http://www.brpreiss.com/books/opus7/">入口</a></p>
<p>Test-Driven Web Development with Python <a href="http://chimera.labs.oreilly.com/books/1234000000754/index.html">入口</a></p>
<p>How To Package Your Python Code <a href="http://www.scotttorborg.com/python-packaging/index.html">入口</a></p>
<h3 id="框架">框架</h3>
<p>Django <a href="https://www.djangoproject.com/">官网</a>|
<a href="https://docs.djangoproject.com/en/1.5/">文档</a>|
<a href="http://djangobook.py3k.cn/2.0/">The Django book</a>|
<a href="http://www.tangowithdjango.com/">Tango With Django</a>|
<a href="http://lincolnloop.com/django-best-practices/index.html">Django Best Practices</a>|
<a href="http://effectivedjango.com/">Effective Django</a>|
<a href="http://django-china.cn/">Django china</a>|
<a href="http://haoluobo.com/trac/wiki/Django">Django资源汇总</a>|
<a href="http://blog.jobbole.com/15555/">10个实用Django建议</a>|
<a href="http://www.mercurytide.co.uk/about/news/article/django-15-cheat-sheet/">cheat sheet</a>|
<a href="http://www.the5fire.com/django-database-access-optimization.html">Djangof访问数据库优化</a>|
<a href="https://code.djangoproject.com/wiki/UsingVimWithDjango">配置vim</a></p>
<p>Flask  <a href="https://dormousehole.readthedocs.org/en/latest/">文档</a>|<a href="http://flask.pocoo.org/docs/">英文</a>
<a href="http://www.oschina.net/translate/the-flask-mega-tutorial-part-i-hello-world">快速教材</a>|
<a href="http://pythonhosted.org/Flask-SQLAlchemy/">flask-sqlalchemy</a>|
<a href="http://pythonhosted.org/Flask-Security/">flask-security</a></p>
<p>Bottle <a href="http://bottlepy.org/docs/dev/">文档</a></p>
<p>Tornado <a href="http://www.tornadoweb.cn/documentation">文档</a>|
<a href="http://demo.pythoner.com/itt2zh/index.html">Introduction to Tornado</a></p>
<p>Celery <a href="http://docs.celeryproject.org/en/latest/index.html">文档</a>|
<a href="https://github.com/celery/celery">github</a></p>
<p>Scrapy <a href="http://scrapy.org/">入口</a>|<a href="http://doc.scrapy.org/en/latest/intro/tutorial.html">文档</a></p>
<p>pyramid <a href="http://docs.pylonsproject.org/projects/pyramid_tutorials/en/latest/index.html">入口</a></p>
<h3 id="数据库">数据库</h3>
<p>sqlalchemy <a href="http://docs.sqlalchemy.org/">入口</a></p>
<p>alembic(sqlalchemy辅助) <a href="https://alembic.readthedocs.org/en/latest/">入口</a></p>
<p>couchDB <a href="http://pythonhosted.org/CouchDB/index.html">文档</a></p>
<p>mongoDB <a href="http://docs.mongodb.org/ecosystem/drivers/python/">入口</a></p>
<h3 id="模板">模板</h3>
<p>jinja2 <a href="http://jinja.pocoo.org/docs/api/">文档</a></p>
<p>mako <a href="http://www.makotemplates.org/">文档</a></p>
<h3 id="工具及第三方包">工具及第三方包</h3>
<p>virtualenv <a href="https://virtualenv-chinese-docs.readthedocs.org/en/latest/">文档</a></p>
<p>requests <a href="http://docs.python-requests.org/en/latest/">文档</a>
<a href="http://cn.python-requests.org/en/latest/">中文</a></p>
<p>fabric [文档](<a href="http://docs.fabfile.org/en/1.7/6">http://docs.fabfile.org/en/1.7/6</a></p>
<p>beautifulsoup <a href="http://www.crummy.com/software/BeautifulSoup/">文档</a></p>
<p>gunicorn <a href="http://gunicorn.org/">入口</a>|
<a href="http://docs.gunicorn.org/en/latest/">文档</a></p>
<p>twisted <a href="http://twistedmatrix.com/trac/">入口</a>|
<a href="http://twistedmatrix.com/documents/10.0.0/core/howto/index.html">文档</a>|
<a href="http://turtlerbender007.appspot.com/twisted/index.html">入门-中文</a></p>
<p>gevent <a href="http://www.gevent.org/">入口</a></p>
<p>wtforms <a href="http://wtforms.simplecodes.com/">入口</a></p>
<p>Routes <a href="http://routes.readthedocs.org/en/latest/index.html">入口</a></p>
<p>kombu <a href="http://kombu.readthedocs.org/en/latest/introduction.html">入口</a></p>
<p>pil <a href="http://www.pythonware.com/products/pil/">入口</a></p>
<h3 id="视频">视频</h3>
<p>疯狂地Python: 快速入门精讲 <a href="http://study.163.com/course/introduction.htm?courseId=302001#/courseDetail">入口</a></p>
<h3 id="书籍">书籍</h3>
<p>一个网站 <a href="http://www.ifindbook.net/recommend/subcat/Python">入口</a></p>
<p>深入Python3 <a href="http://woodpecker.org.cn/diveintopython3/index.html">入口</a></p>
<h3 id="博客">博客</h3>
<p>Python入门及进阶笔记(我的) <a href="https://wklken.me/category/pythonru-men-ji-jin-jie-bi-ji.html">入口</a></p>
<p>Python源码剖析 <a href="http://blog.csdn.net/balabalamerobert/article/category/168910">入口</a></p>
<p>Python自然语言处理学习笔记 <a href="http://www.cnblogs.com/yuxc/category/307122.html">入口</a></p>
<p>dabeaz <a href="http://www.dabeaz.com/talks.html">入口</a></p>
<h3 id="经典博文集合">经典博文集合</h3>
<p>*github上一个文章集合 <a href="https://github.com/kirang89/pycrumbs/blob/master/pycrumbs.md">入口</a></p>
<p>Python十分钟入门 <a href="http://blog.jobbole.com/23425/">入口</a></p>
<p>为什么Python对程序员重要 <a href="http://blog.jobbole.com/13153/">入口</a></p>
<p>每个程序员都应该学习使用Python或Ruby <a href="http://blog.jobbole.com/1141/">入口</a></p>
<p>给Python初学者的一些技巧 <a href="http://blog.jobbole.com/32748/">入口</a></p>
<p>Python新手常犯错误  <a href="http://blog.jobbole.com/42706/">第一部分</a> <a href="http://blog.jobbole.com/43826/">第二部分</a></p>
<p>Python编程中需要注意的一些事 <a href="http://blog.jobbole.com/19835/">入口</a></p>
<p>Python性能鸡汤 <a href="http://www.oschina.net/question/1579_45822">入口</a></p>
<p>Python代码性能优化技巧 <a href="http://blog.jobbole.com/24197/">入口</a></p>
<p>加速你的Python代码 <a href="http://blog.jobbole.com/36701/">入口</a></p>
<p>如何成为Python高手 <a href="http://www.aqee.net/how-to-become-a-proficient-python-programmer/">入口</a></p>
<p>你真的会Python吗 <a href="http://www.dongwm.com/archives/ni-zhen-de-hui-pythonma/">入口</a></p>
<p>深刻理解Python中的metaclass <a href="http://blog.jobbole.com/21351/">入口</a></p>
<p>创建成功的Python项目 <a href="http://blog.jobbole.com/12649/">入口</a></p>
<p>Python打包入门指南 <a href="http://www.ibm.com/developerworks/opensource/library/os-pythonpackaging/index.html">入口</a></p>
<p>Python yield 使用浅析 <a href="http://blog.jobbole.com/32876/">入口</a></p>
<p>Python关键字yield详解 <a href="http://blog.jobbole.com/28506/">入口</a></p>
<p>Python中的默认参数 <a href="http://blog.jobbole.com/40088/">入口</a></p>
<p>可爱的Python Python中的函数式编程  <a href="http://blog.jobbole.com/35028/">第一部分</a>  <a href="http://blog.jobbole.com/35042/">第二部分</a> <a href="http://blog.jobbole.com/35045/">第三部分</a></p>
<h3 id="社区">社区</h3>
<p>为毛没有给力的&hellip;. (╯‵□′)╯︵┻━┻zi</p>
<h3 id="其他">其他</h3>
<p>一个python正则在线工具 <a href="http://www.pyregex.com/">入口</a></p>
<p>动态语言设计模式 <a href="http://norvig.com/design-patterns/ppframe.htm">入口</a></p>
<p>Python challenge <a href="http://www.pythonchallenge.com/">入口</a></p>
<p>Python 初学者 <a href="https://github.com/Yixiaohan/codeparkshare">PythonShare</a></p>
<p>Pythonista的vim编辑器 <a href="https://github.com/wklken/k-vim">k-vim</a></p>
<p>国内pypi镜像 <a href="http://pypi.sdutlinux.org/">pypi</a>|
<a href="http://pypi.douban.com/simple/">douban</a></p>
<pre><code>sudo easy_install -i http://pypi.douban.com/simple/ saltTesting
sudo pip install -i http://pypi.douban.com/simple/ saltTesting
</code></pre>
<p>经典代码片段 <a href="http://code.activestate.com/recipes/langs/">入口</a></p>
<p>windows下各种python第三方exe下载 <a href="http://www.lfd.uci.edu/~gohlke/pythonlibs/">入口</a></p>
<p>python 视频站 <a href="http://pyvideo.org/">pyvideo</a></p>
<p>python 源码搜索引擎 <a href="http://nullege.com/">入口</a></p>
<p>python for beginners <a href="http://www.pythonforbeginners.com/">入口</a></p>
]]></content>
		</item>
		
		<item>
			<title>Python-进阶-itertools模块小结</title>
			<link>https://wklken.me/posts/2013/08/20/python-extra-itertools.html</link>
			<pubDate>Tue, 20 Aug 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/08/20/python-extra-itertools.html</guid>
			<description>这货很强大, 必须掌握 文档 链接 pymotw 链接 基本是基于文档的翻译和补充，相当于翻译了 itertools用于高效循环的迭代函数集合 组成 总体，整体了解 无限</description>
			<content type="html"><![CDATA[<p>这货很强大, 必须掌握</p>
<p>文档 <a href="http://docs.python.org/2/library/itertools.html">链接</a></p>
<p>pymotw <a href="http://pymotw.com/2/itertools/">链接</a></p>
<p>基本是基于文档的翻译和补充，相当于翻译了</p>
<p>itertools用于高效循环的迭代函数集合</p>
<h2 id="组成">组成</h2>
<p>总体，整体了解</p>
<p>无限迭代器</p>
<pre><code>迭代器         参数         结果                                                例子
count()     start, [step]   start, start+step, start+2*step, ...                count(10) --&gt; 10 11 12 13 14 ...
cycle()     p               p0, p1, ... plast, p0, p1, ...                      cycle('ABCD') --&gt; A B C D A B C D ...
repeat()    elem [,n]       elem, elem, elem, ... endlessly or up to n times    repeat(10, 3) --&gt; 10 10 10
</code></pre>
<p>处理输入序列迭代器</p>
<pre><code>迭代器          参数            结果                                        例子
chain()     p, q, ...           p0, p1, ... plast, q0, q1, ...              chain('ABC', 'DEF') --&gt; A B C D E F
compress()  data, selectors     (d[0] if s[0]), (d[1] if s[1]), ...         compress('ABCDEF', [1,0,1,0,1,1]) --&gt; A C E F
dropwhile() pred, seq           seq[n], seq[n+1], starting when pred fails  dropwhile(lambda x: x&lt;5, [1,4,6,4,1]) --&gt; 6 4 1
groupby()   iterable[, keyfunc] sub-iterators grouped by value of keyfunc(v)
ifilter()   pred, seq           elements of seq where pred(elem) is True    ifilter(lambda x: x%2, range(10)) --&gt; 1 3 5 7 9
ifilterfalse()  pred, seq       elements of seq where pred(elem) is False   ifilterfalse(lambda x: x%2, range(10)) --&gt; 0 2 4 6 8
islice()    seq, [start,] stop [, step] elements from seq[start:stop:step]  islice('ABCDEFG', 2, None) --&gt; C D E F G
imap()      func, p, q, ...     func(p0, q0), func(p1, q1), ...             imap(pow, (2,3,10), (5,2,3)) --&gt; 32 9 1000
starmap()   func, seq           func(*seq[0]), func(*seq[1]), ...           starmap(pow, [(2,5), (3,2), (10,3)]) --&gt; 32 9 1000
tee()       it, n               it1, it2 , ... itn splits one iterator into n
takewhile() pred, seq           seq[0], seq[1], until pred fails            takewhile(lambda x: x&lt;5, [1,4,6,4,1]) --&gt; 1 4
izip()      p, q, ...           (p[0], q[0]), (p[1], q[1]), ...             izip('ABCD', 'xy') --&gt; Ax By
izip_longest()  p, q, ...       (p[0], q[0]), (p[1], q[1]), ...             izip_longest('ABCD', 'xy', fillvalue='-') --&gt; Ax By C- D-
</code></pre>
<p>组合生成器</p>
<pre><code>迭代器          参数                        结果
product()       p, q, ... [repeat=1]        cartesian product, equivalent to a nested for-loop
permutations()  p[, r]                      r-length tuples, all possible orderings, no repeated elements
combinations()  p, r                        r-length tuples, in sorted order, no repeated elements
combinations_with_replacement() p, r        r-length tuples, in sorted order, with repeated elements
product('ABCD', repeat=2)                   AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD
permutations('ABCD', 2)                     AB AC AD BA BC BD CA CB CD DA DB DC
combinations('ABCD', 2)                     AB AC AD BC BD CD
combinations_with_replacement('ABCD', 2)    AA AB AC AD BB BC BD CC CD DD
</code></pre>
<h2 id="第一部分">第一部分</h2>
<h3 id="itertoolscountstart0-step1">itertools.count(start=0, step=1)</h3>
<p>创建一个迭代器，生成从n开始的连续整数，如果忽略n，则从0开始计算（注意：此迭代器不支持长整数）</p>
<p>如果超出了sys.maxint，计数器将溢出并继续从-sys.maxint-1开始计算。</p>
<p>定义</p>
<pre><code>def count(start=0, step=1):
    # count(10) --&gt; 10 11 12 13 14 ...
    # count(2.5, 0.5) -&gt; 2.5 3.0 3.5 ...
    n = start
    while True:
        yield n
        n += step

等同于(start + step * i for i in count())
</code></pre>
<p>使用</p>
<pre><code>from itertools import *

for i in izip(count(1), ['a', 'b', 'c']):
    print i

(1, 'a')
(2, 'b')
(3, 'c')
</code></pre>
<h3 id="itertoolscycleiterable">itertools.cycle(iterable)</h3>
<p>创建一个迭代器，对iterable中的元素反复执行循环操作，内部会生成iterable中的元素的一个副本，此副本用于返回循环中的重复项。</p>
<p>定义</p>
<pre><code>def cycle(iterable):
    # cycle('ABCD') --&gt; A B C D A B C D A B C D ...
    saved = []
    for element in iterable:
        yield element
        saved.append(element)
    while saved:
        for element in saved:
            yield element
</code></pre>
<p>使用</p>
<pre><code>from itertools import *

i = 0
for item in cycle(['a', 'b', 'c']):
    i += 1
    if i == 10:
        break
    print (i, item)

(1, 'a')
(2, 'b')
(3, 'c')
(4, 'a')
(5, 'b')
(6, 'c')
(7, 'a')
(8, 'b')
(9, 'c')
</code></pre>
<h3 id="itertoolsrepeatobject-times">itertools.repeat(object[, times])</h3>
<p>创建一个迭代器，重复生成object，times（如果已提供）指定重复计数，如果未提供times，将无止尽返回该对象。</p>
<p>定义</p>
<pre><code>def repeat(object, times=None):
    # repeat(10, 3) --&gt; 10 10 10
    if times is None:
        while True:
            yield object
    else:
        for i in xrange(times):
            yield object
</code></pre>
<p>使用</p>
<pre><code>from itertools import *

for i in repeat('over-and-over', 5):
    print i

over-and-over
over-and-over
over-and-over
over-and-over
over-and-over
</code></pre>
<h2 id="第二部分">第二部分</h2>
<h3 id="itertoolschainiterables">itertools.chain(*iterables)</h3>
<p>将多个迭代器作为参数, 但只返回单个迭代器, 它产生所有参数迭代器的内容, 就好像他们是来自于一个单一的序列.</p>
<pre><code>def chain(*iterables):
    # chain('ABC', 'DEF') --&gt; A B C D E F
    for it in iterables:
        for element in it:
            yield element
</code></pre>
<p>使用</p>
<pre><code>from itertools import *

for i in chain([1, 2, 3], ['a', 'b', 'c']):
    print i
1
2
3
a
b
c


from itertools import chain, imap
def flatmap(f, items):
    return chain.from_iterable(imap(f, items))
&gt;&gt;&gt; list(flatmap(os.listdir, dirs))
&gt;&gt;&gt; ['settings.py', 'wsgi.py', 'templates', 'app.py',
     'templates', 'index.html, 'config.json']
</code></pre>
<h3 id="itertoolscompressdata-selectors">itertools.compress(data, selectors)</h3>
<p>提供一个选择列表，对原始数据进行筛选</p>
<pre><code>def compress(data, selectors):
    # compress('ABCDEF', [1,0,1,0,1,1]) --&gt; A C E F
    return (d for d, s in izip(data, selectors) if s)
</code></pre>
<h3 id="itertoolsdropwhilepredicate-iterable">itertools.dropwhile(predicate, iterable)</h3>
<p>创建一个迭代器，只要函数predicate(item)为True，就丢弃iterable中的项，如果predicate返回False，就会生成iterable中的项和所有后续项。</p>
<p>即：在条件为false之后的第一次, 返回迭代器中剩下来的项.</p>
<pre><code>def dropwhile(predicate, iterable):
    # dropwhile(lambda x: x&lt;5, [1,4,6,4,1]) --&gt; 6 4 1
    iterable = iter(iterable)
    for x in iterable:
        if not predicate(x):
            yield x
            break
    for x in iterable:
        yield x
</code></pre>
<p>使用</p>
<pre><code>from itertools import *

def should_drop(x):
    print 'Testing:', x
    return (x&lt;1)

for i in dropwhile(should_drop, [ -1, 0, 1, 2, 3, 4, 1, -2 ]):
    print 'Yielding:', i

Testing: -1
Testing: 0
Testing: 1
Yielding: 1
Yielding: 2
Yielding: 3
Yielding: 4
Yielding: 1
Yielding: -2
</code></pre>
<h3 id="itertoolsgroupbyiterable-key">itertools.groupby(iterable[, key])</h3>
<p>返回一个产生按照key进行分组后的值集合的迭代器.</p>
<p>如果iterable在多次连续迭代中生成了同一项，则会定义一个组，如果将此函数应用一个分类列表，那么分组将定义该列表中的所有唯一项，key（如果已提供）是一个函数，应用于每一项，如果此函数存在返回值，该值将用于后续项而不是该项本身进行比较，此函数返回的迭代器生成元素(key, group)，其中key是分组的键值，group是迭代器，生成组成该组的所有项。</p>
<p>即：按照keyfunc函数对序列每个元素执行后的结果分组(每个分组是一个迭代器), 返回这些分组的迭代器</p>
<p>等价于</p>
<pre><code>class groupby(object):
    # [k for k, g in groupby('AAAABBBCCDAABBB')] --&gt; A B C D A B
    # [list(g) for k, g in groupby('AAAABBBCCD')] --&gt; AAAA BBB CC D
    def __init__(self, iterable, key=None):
        if key is None:
            key = lambda x: x
        self.keyfunc = key
        self.it = iter(iterable)
        self.tgtkey = self.currkey = self.currvalue = object()
    def __iter__(self):
        return self
    def next(self):
        while self.currkey == self.tgtkey:
            self.currvalue = next(self.it)    # Exit on StopIteration
            self.currkey = self.keyfunc(self.currvalue)
        self.tgtkey = self.currkey
        return (self.currkey, self._grouper(self.tgtkey))
    def _grouper(self, tgtkey):
        while self.currkey == tgtkey:
            yield self.currvalue
            self.currvalue = next(self.it)    # Exit on StopIteration
            self.currkey = self.keyfunc(self.currvalue)
</code></pre>
<p>应用</p>
<pre><code>from itertools import groupby
qs = [{'date' : 1},{'date' : 2}]
[(name, list(group)) for name, group in itertools.groupby(qs, lambda p:p['date'])]

Out[77]: [(1, [{'date': 1}]), (2, [{'date': 2}])]


&gt;&gt;&gt; from itertools import *
&gt;&gt;&gt; a = ['aa', 'ab', 'abc', 'bcd', 'abcde']
&gt;&gt;&gt; for i, k in groupby(a, len):
...     print i, list(k)
...
2 ['aa', 'ab']
3 ['abc', 'bcd']
5 ['abcde']
</code></pre>
<p>另一个例子</p>
<pre><code>from itertools import *
from operator import itemgetter

d = dict(a=1, b=2, c=1, d=2, e=1, f=2, g=3)
di = sorted(d.iteritems(), key=itemgetter(1))
for k, g in groupby(di, key=itemgetter(1)):
    print k, map(itemgetter(0), g)


1 ['a', 'c', 'e']
2 ['b', 'd', 'f']
3 ['g']
</code></pre>
<h3 id="itertoolsifilterpredicate-iterable">itertools.ifilter(predicate, iterable)</h3>
<p>返回的是迭代器类似于针对列表的内置函数 filter() , 它只包括当测试函数返回true时的项. 它不同于 dropwhile()</p>
<p>创建一个迭代器，仅生成iterable中predicate(item)为True的项，如果predicate为None，将返回iterable中所有计算为True的项</p>
<p>对函数func执行返回真的元素的迭代器</p>
<pre><code>def ifilter(predicate, iterable):
    # ifilter(lambda x: x%2, range(10)) --&gt; 1 3 5 7 9
    if predicate is None:
        predicate = bool
    for x in iterable:
        if predicate(x):
            yield x
</code></pre>
<p>使用</p>
<pre><code>from itertools import *

def check_item(x):
    print 'Testing:', x
    return (x&lt;1)

for i in ifilter(check_item, [ -1, 0, 1, 2, 3, 4, 1, -2 ]):
    print 'Yielding:', i

Testing: -1
Yielding: -1
Testing: 0
Yielding: 0
Testing: 1
Testing: 2
Testing: 3
Testing: 4
Testing: 1
Testing: -2
Yielding: -2
</code></pre>
<h3 id="itertoolsifilterfalsepredicate-iterable">itertools.ifilterfalse(predicate, iterable)</h3>
<p>和ifilter(函数相反 ， 返回一个包含那些测试函数返回false的项的迭代器)</p>
<p>创建一个迭代器，仅生成iterable中predicate(item)为False的项，如果predicate为None，则返回iterable中所有计算为False的项
对函数func执行返回假的元素的迭代器</p>
<pre><code>def ifilterfalse(predicate, iterable):
    # ifilterfalse(lambda x: x%2, range(10)) --&gt; 0 2 4 6 8
    if predicate is None:
        predicate = bool
    for x in iterable:
        if not predicate(x):
            yield x
</code></pre>
<p>使用</p>
<pre><code>from itertools import *

def check_item(x):
    print 'Testing:', x
    return (x&lt;1)

for i in ifilterfalse(check_item, [ -1, 0, 1, 2, 3, 4, 1, -2 ]):
    print 'Yielding:', i

Testing: -1
Testing: 0
Testing: 1
Yielding: 1
Testing: 2
Yielding: 2
Testing: 3
Yielding: 3
Testing: 4
Yielding: 4
Testing: 1
Yielding: 1
Testing: -2
</code></pre>
<h3 id="itertoolsisliceiterable-stop">itertools.islice(iterable, stop)</h3>
<p>itertools.islice(iterable, start, stop[, step])</p>
<p>返回的迭代器是返回了输入迭代器根据索引来选取的项</p>
<p>创建一个迭代器，生成项的方式类似于切片返回值： iterable[start : stop : step]，将跳过前start个项，迭代在stop所指定的位置停止，step指定用于跳过项的步幅。
与切片不同，负值不会用于任何start，stop和step，
如果省略了start，迭代将从0开始，如果省略了step，步幅将采用1.</p>
<p>返回序列seq的从start开始到stop结束的步长为step的元素的迭代器</p>
<pre><code>def islice(iterable, *args):
    # islice('ABCDEFG', 2) --&gt; A B
    # islice('ABCDEFG', 2, 4) --&gt; C D
    # islice('ABCDEFG', 2, None) --&gt; C D E F G
    # islice('ABCDEFG', 0, None, 2) --&gt; A C E G
    s = slice(*args)
    it = iter(xrange(s.start or 0, s.stop or sys.maxint, s.step or 1))
    nexti = next(it)
    for i, element in enumerate(iterable):
        if i == nexti:
            yield element
            nexti = next(it)
</code></pre>
<p>使用</p>
<pre><code>from itertools import *

print 'Stop at 5:'
for i in islice(count(), 5):
    print i

print 'Start at 5, Stop at 10:'
for i in islice(count(), 5, 10):
    print i

print 'By tens to 100:'
for i in islice(count(), 0, 100, 10):
    print i

Stop at 5:
0
1
2
3
4
Start at 5, Stop at 10:
5
6
7
8
9
By tens to 100:
0
10
20
30
40
50
60
70
80
90
</code></pre>
<h3 id="itertoolsimapfunction-iterables">itertools.imap(function, *iterables)</h3>
<p>创建一个迭代器，生成项function(i1, i2, &hellip;, iN)，其中i1，i2&hellip;iN分别来自迭代器iter1，iter2 &hellip; iterN，如果function为None，则返回(i1, i2, &hellip;, iN)形式的元组，只要提供的一个迭代器不再生成值，迭代就会停止。</p>
<p>即：返回一个迭代器, 它是调用了一个其值在输入迭代器上的函数, 返回结果. 它类似于内置函数 map() , 只是前者在任意输入迭代器结束后就停止(而不是插入None值来补全所有的输入).</p>
<p>返回序列每个元素被func执行后返回值的序列的迭代器</p>
<pre><code>def imap(function, *iterables):
    # imap(pow, (2,3,10), (5,2,3)) --&gt; 32 9 1000
    iterables = map(iter, iterables)
    while True:
        args = [next(it) for it in iterables]
        if function is None:
            yield tuple(args)
        else:
            yield function(*args)
</code></pre>
<p>使用</p>
<pre><code>from itertools import *

print 'Doubles:'
for i in imap(lambda x:2*x, xrange(5)):
    print i

print 'Multiples:'
for i in imap(lambda x,y:(x, y, x*y), xrange(5), xrange(5,10)):
    print '%d * %d = %d' % i

Doubles:
0
2
4
6
8
Multiples:
0 * 5 = 0
1 * 6 = 6
2 * 7 = 14
3 * 8 = 24
4 * 9 = 36
</code></pre>
<h3 id="itertoolsstarmapfunction-iterable">itertools.starmap(function, iterable)</h3>
<p>创建一个迭代器，生成值func(*item),其中item来自iterable，只有当iterable生成的项适用于这种调用函数的方式时，此函数才有效。</p>
<p>对序列seq的每个元素作为func的参数列表执行, 返回执行结果的迭代器</p>
<pre><code>def starmap(function, iterable):
    # starmap(pow, [(2,5), (3,2), (10,3)]) --&gt; 32 9 1000
    for args in iterable:
        yield function(*args)
</code></pre>
<p>使用</p>
<pre><code>from itertools import *

values = [(0, 5), (1, 6), (2, 7), (3, 8), (4, 9)]
for i in starmap(lambda x,y:(x, y, x*y), values):
    print '%d * %d = %d' % i

0 * 5 = 0
1 * 6 = 6
2 * 7 = 14
3 * 8 = 24
4 * 9 = 36
</code></pre>
<h3 id="itertoolsteeiterable-n2">itertools.tee(iterable[, n=2])</h3>
<p>返回一些基于单个原始输入的独立迭代器(默认为2). 它和Unix上的tee工具有点语义相似, 也就是说它们都重复读取输入设备中的值并将值写入到一个命名文件和标准输出中</p>
<p>从iterable创建n个独立的迭代器，创建的迭代器以n元组的形式返回，n的默认值为2，此函数适用于任何可迭代的对象，但是，为了克隆原始迭代器，生成的项会被缓存，并在所有新创建的迭代器中使用，一定要注意，不要在调用tee()之后使用原始迭代器iterable，否则缓存机制可能无法正确工作。</p>
<p>把一个迭代器分为n个迭代器, 返回一个元组.默认是两个</p>
<pre><code>def tee(iterable, n=2):
    it = iter(iterable)
    deques = [collections.deque() for i in range(n)]
    def gen(mydeque):
        while True:
            if not mydeque:             # when the local deque is empty
                newval = next(it)       # fetch a new value and
                for d in deques:        # load it to all the deques
                    d.append(newval)
            yield mydeque.popleft()
    return tuple(gen(d) for d in deques)
</code></pre>
<p>使用</p>
<pre><code>from itertools import *

r = islice(count(), 5)
i1, i2 = tee(r)

for i in i1:
    print 'i1:', i
for i in i2:
    print 'i2:', i

i1: 0
i1: 1
i1: 2
i1: 3
i1: 4
i2: 0
i2: 1
i2: 2
i2: 3
i2: 4
</code></pre>
<h3 id="itertoolstakewhilepredicate-iterable">itertools.takewhile(predicate, iterable)</h3>
<p>和dropwhile相反</p>
<p>创建一个迭代器，生成iterable中predicate(item)为True的项，只要predicate计算为False，迭代就会立即停止。</p>
<p>即：从序列的头开始, 直到执行函数func失败.</p>
<pre><code>def takewhile(predicate, iterable):
    # takewhile(lambda x: x&lt;5, [1,4,6,4,1]) --&gt; 1 4
    for x in iterable:
        if predicate(x):
            yield x
        else:
            break
</code></pre>
<p>使用</p>
<pre><code>from itertools import *

def should_take(x):
    print 'Testing:', x
    return (x&lt;2)

for i in takewhile(should_take, [ -1, 0, 1, 2, 3, 4, 1, -2 ]):
    print 'Yielding:', i

Testing: -1
Yielding: -1
Testing: 0
Yielding: 0
Testing: 1
Yielding: 1
Testing: 2
</code></pre>
<h3 id="itertoolsizipiterables">itertools.izip(*iterables)</h3>
<p>返回一个合并了多个迭代器为一个元组的迭代器. 它类似于内置函数zip(), 只是它返回的是一个迭代器而不是一个列表</p>
<p>创建一个迭代器，生成元组(i1, i2, &hellip; iN)，其中i1，i2 &hellip; iN 分别来自迭代器iter1，iter2 &hellip; iterN，只要提供的某个迭代器不再生成值，迭代就会停止，此函数生成的值与内置的zip()函数相同。</p>
<pre><code>izip(iter1, iter2, ... iterN):
返回:(it1[0],it2 [0], it3[0], ..), (it1[1], it2[1], it3[1], ..)...

def izip(*iterables):
    # izip('ABCD', 'xy') --&gt; Ax By
    iterators = map(iter, iterables)
    while iterators:
        yield tuple(map(next, iterators))
</code></pre>
<p>使用</p>
<pre><code>from itertools import *

for i in izip([1, 2, 3], ['a', 'b', 'c']):
    print i
(1, 'a')
(2, 'b')
(3, 'c')
</code></pre>
<h3 id="itertoolsizip_longestiterables-fillvalue">itertools.izip_longest(*iterables[, fillvalue])</h3>
<p>与izip()相同，但是迭代过程会持续到所有输入迭代变量iter1,iter2等都耗尽为止，如果没有使用fillvalue关键字参数指定不同的值，则使用None来填充已经使用的迭代变量的值。</p>
<pre><code>class ZipExhausted(Exception):
    pass

def izip_longest(*args, **kwds):
    # izip_longest('ABCD', 'xy', fillvalue='-') --&gt; Ax By C- D-
    fillvalue = kwds.get('fillvalue')
    counter = [len(args) - 1]
    def sentinel():
        if not counter[0]:
            raise ZipExhausted
        counter[0] -= 1
        yield fillvalue
    fillers = repeat(fillvalue)
    iterators = [chain(it, sentinel(), fillers) for it in args]
    try:
        while iterators:
            yield tuple(map(next, iterators))
    except ZipExhausted:
        pass
</code></pre>
<h2 id="第三部分">第三部分</h2>
<h3 id="itertoolsproductiterables-repeat">itertools.product(*iterables[, repeat])</h3>
<p>笛卡尔积</p>
<p>创建一个迭代器，生成表示item1，item2等中的项目的笛卡尔积的元组，repeat是一个关键字参数，指定重复生成序列的次数。</p>
<pre><code>def product(*args, **kwds):
    # product('ABCD', 'xy') --&gt; Ax Ay Bx By Cx Cy Dx Dy
    # product(range(2), repeat=3) --&gt; 000 001 010 011 100 101 110 111
    pools = map(tuple, args) * kwds.get('repeat', 1)
    result = [[]]
    for pool in pools:
        result = [x+[y] for x in result for y in pool]
    for prod in result:
        yield tuple(prod)
</code></pre>
<p>例子</p>
<pre><code>import itertools
a = (1, 2, 3)
b = ('A', 'B', 'C')
c = itertools.product(a,b)
for elem in c:
    print elem

(1, 'A')
(1, 'B')
(1, 'C')
(2, 'A')
(2, 'B')
(2, 'C')
(3, 'A')
(3, 'B')
(3, 'C')
</code></pre>
<h3 id="itertoolspermutationsiterable-r">itertools.permutations(iterable[, r])</h3>
<p>排列</p>
<p>创建一个迭代器，返回iterable中所有长度为r的项目序列，如果省略了r，那么序列的长度与iterable中的项目数量相同：
返回p中任意取r个元素做排列的元组的迭代器</p>
<pre><code>def permutations(iterable, r=None):
    # permutations('ABCD', 2) --&gt; AB AC AD BA BC BD CA CB CD DA DB DC
    # permutations(range(3)) --&gt; 012 021 102 120 201 210
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    if r &gt; n:
        return
    indices = range(n)
    cycles = range(n, n-r, -1)
    yield tuple(pool[i] for i in indices[:r])
    while n:
        for i in reversed(range(r)):
            cycles[i] -= 1
            if cycles[i] == 0:
                indices[i:] = indices[i+1:] + indices[i:i+1]
                cycles[i] = n - i
            else:
                j = cycles[i]
                indices[i], indices[-j] = indices[-j], indices[i]
                yield tuple(pool[i] for i in indices[:r])
                break
        else:
            return
也可以用product实现

def permutations(iterable, r=None):
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    for indices in product(range(n), repeat=r):
        if len(set(indices)) == r:
            yield tuple(pool[i] for i in indices)
</code></pre>
<h3 id="itertoolscombinationsiterable-r">itertools.combinations(iterable, r)</h3>
<p>创建一个迭代器，返回iterable中所有长度为r的子序列，返回的子序列中的项按输入iterable中的顺序排序 (不带重复)</p>
<pre><code>def combinations(iterable, r):
    # combinations('ABCD', 2) --&gt; AB AC AD BC BD CD
    # combinations(range(4), 3) --&gt; 012 013 023 123
    pool = tuple(iterable)
    n = len(pool)
    if r &gt; n:
        return
    indices = range(r)
    yield tuple(pool[i] for i in indices)
    while True:
        for i in reversed(range(r)):
            if indices[i] != i + n - r:
                break
        else:
            return
        indices[i] += 1
        for j in range(i+1, r):
            indices[j] = indices[j-1] + 1
        yield tuple(pool[i] for i in indices)

#或者
def combinations(iterable, r):
    pool = tuple(iterable)
    n = len(pool)
    for indices in permutations(range(n), r):
        if sorted(indices) == list(indices):
            yield tuple(pool[i] for i in indices)
</code></pre>
<h3 id="itertoolscombinations_with_replacementiterable-r">itertools.combinations_with_replacement(iterable, r)</h3>
<p>创建一个迭代器，返回iterable中所有长度为r的子序列，返回的子序列中的项按输入iterable中的顺序排序 (带重复)</p>
<pre><code>def combinations_with_replacement(iterable, r):
    # combinations_with_replacement('ABC', 2) --&gt; AA AB AC BB BC CC
    pool = tuple(iterable)
    n = len(pool)
    if not n and r:
        return
    indices = [0] * r
    yield tuple(pool[i] for i in indices)
    while True:
        for i in reversed(range(r)):
            if indices[i] != n - 1:
                break
        else:
            return
        indices[i:] = [indices[i] + 1] * (r - i)
        yield tuple(pool[i] for i in indices)
或者
def combinations_with_replacement(iterable, r):
    pool = tuple(iterable)
    n = len(pool)
    for indices in product(range(n), repeat=r):
        if sorted(indices) == list(indices):
            yield tuple(pool[i] for i in indices)
</code></pre>
<h2 id="第四部分">第四部分</h2>
<h3 id="扩展">扩展</h3>
<p>使用现有扩展功能</p>
<pre><code>def take(n, iterable):
    &quot;Return first n items of the iterable as a list&quot;
    return list(islice(iterable, n))

def tabulate(function, start=0):
    &quot;Return function(0), function(1), ...&quot;
    return imap(function, count(start))

def consume(iterator, n):
    &quot;Advance the iterator n-steps ahead. If n is none, consume entirely.&quot;
    # Use functions that consume iterators at C speed.
    if n is None:
        # feed the entire iterator into a zero-length deque
        collections.deque(iterator, maxlen=0)
    else:
        # advance to the empty slice starting at position n
        next(islice(iterator, n, n), None)

def nth(iterable, n, default=None):
    &quot;Returns the nth item or a default value&quot;
    return next(islice(iterable, n, None), default)

def quantify(iterable, pred=bool):
    &quot;Count how many times the predicate is true&quot;
    return sum(imap(pred, iterable))

def padnone(iterable):
    &quot;&quot;&quot;Returns the sequence elements and then returns None indefinitely.

    Useful for emulating the behavior of the built-in map() function.
    &quot;&quot;&quot;
    return chain(iterable, repeat(None))

def ncycles(iterable, n):
    &quot;Returns the sequence elements n times&quot;
    return chain.from_iterable(repeat(tuple(iterable), n))

def dotproduct(vec1, vec2):
    return sum(imap(operator.mul, vec1, vec2))

def flatten(listOfLists):
    &quot;Flatten one level of nesting&quot;
    return chain.from_iterable(listOfLists)

def repeatfunc(func, times=None, *args):
    &quot;&quot;&quot;Repeat calls to func with specified arguments.

    Example:  repeatfunc(random.random)
    &quot;&quot;&quot;
    if times is None:
        return starmap(func, repeat(args))
    return starmap(func, repeat(args, times))

def pairwise(iterable):
    &quot;s -&gt; (s0,s1), (s1,s2), (s2, s3), ...&quot;
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

def grouper(iterable, n, fillvalue=None):
    &quot;Collect data into fixed-length chunks or blocks&quot;
    # grouper('ABCDEFG', 3, 'x') --&gt; ABC DEF Gxx
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)

def roundrobin(*iterables):
    &quot;roundrobin('ABC', 'D', 'EF') --&gt; A D E B F C&quot;
    # Recipe credited to George Sakkis
    pending = len(iterables)
    nexts = cycle(iter(it).next for it in iterables)
    while pending:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            pending -= 1
            nexts = cycle(islice(nexts, pending))

def powerset(iterable):
    &quot;powerset([1,2,3]) --&gt; () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)&quot;
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

def unique_everseen(iterable, key=None):
    &quot;List unique elements, preserving order. Remember all elements ever seen.&quot;
    # unique_everseen('AAAABBBCCDAABBB') --&gt; A B C D
    # unique_everseen('ABBCcAD', str.lower) --&gt; A B C D
    seen = set()
    seen_add = seen.add
    if key is None:
        for element in ifilterfalse(seen.__contains__, iterable):
            seen_add(element)
            yield element
    else:
        for element in iterable:
            k = key(element)
            if k not in seen:
                seen_add(k)
                yield element

def unique_justseen(iterable, key=None):
    &quot;List unique elements, preserving order. Remember only the element just seen.&quot;
    # unique_justseen('AAAABBBCCDAABBB') --&gt; A B C D A B
    # unique_justseen('ABBCcAD', str.lower) --&gt; A B C A D
    return imap(next, imap(itemgetter(1), groupby(iterable, key)))

def iter_except(func, exception, first=None):
    &quot;&quot;&quot; Call a function repeatedly until an exception is raised.

    Converts a call-until-exception interface to an iterator interface.
    Like __builtin__.iter(func, sentinel) but uses an exception instead
    of a sentinel to end the loop.

    Examples:
        bsddbiter = iter_except(db.next, bsddb.error, db.first)
        heapiter = iter_except(functools.partial(heappop, h), IndexError)
        dictiter = iter_except(d.popitem, KeyError)
        dequeiter = iter_except(d.popleft, IndexError)
        queueiter = iter_except(q.get_nowait, Queue.Empty)
        setiter = iter_except(s.pop, KeyError)

    &quot;&quot;&quot;
    try:
        if first is not None:
            yield first()
        while 1:
            yield func()
    except exception:
        pass

def random_product(*args, **kwds):
    &quot;Random selection from itertools.product(*args, **kwds)&quot;
    pools = map(tuple, args) * kwds.get('repeat', 1)
    return tuple(random.choice(pool) for pool in pools)

def random_permutation(iterable, r=None):
    &quot;Random selection from itertools.permutations(iterable, r)&quot;
    pool = tuple(iterable)
    r = len(pool) if r is None else r
    return tuple(random.sample(pool, r))

def random_combination(iterable, r):
    &quot;Random selection from itertools.combinations(iterable, r)&quot;
    pool = tuple(iterable)
    n = len(pool)
    indices = sorted(random.sample(xrange(n), r))
    return tuple(pool[i] for i in indices)

def random_combination_with_replacement(iterable, r):
    &quot;Random selection from itertools.combinations_with_replacement(iterable, r)&quot;
    pool = tuple(iterable)
    n = len(pool)
    indices = sorted(random.randrange(n) for i in xrange(r))
    return tuple(pool[i] for i in indices)

def tee_lookahead(t, i):
    &quot;&quot;&quot;Inspect the i-th upcomping value from a tee object
    while leaving the tee object at its current position.

    Raise an IndexError if the underlying iterator doesn't
    have enough values.

    &quot;&quot;&quot;
    for value in islice(t.__copy__(), i, None):
        return value
    raise IndexError(i)
</code></pre>
<h3 id="自定义扩展">自定义扩展</h3>
<p>将序列按大小切分,更好的性能</p>
<pre><code>from itertools import chain, islice
def chunks(iterable, size, format=iter):
    it = iter(iterable)
    while True:
        yield format(chain((it.next(),), islice(it, size - 1)))

&gt;&gt;&gt; l = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;, &quot;f&quot;, &quot;g&quot;]
&gt;&gt;&gt; for chunk in chunks(l, 3, tuple):...
        print chunk...
(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;)
(&quot;d&quot;, &quot;e&quot;, &quot;f&quot;)
(&quot;g&quot;,)
</code></pre>
<h3 id="补充">补充</h3>
<p>迭代工具，你最好的朋友</p>
<p>迭代工具模块包含了操做指定的函数用于操作迭代器。想复制一个迭代器出来？链接两个迭代器？以one liner（这里的one-liner只需一行代码能搞定的任务)用内嵌的列表组合一组值？不使用list创建Map/Zip？···，你要做的就是 import itertools，举个例子吧：</p>
<p>四匹马赛跑到达终点排名的所有可能性：</p>
<pre><code>&gt;&gt;&gt; horses = [1, 2, 3, 4]
&gt;&gt;&gt; races = itertools.permutations(horses)
&gt;&gt;&gt; print(races)
&lt;itertools.permutations object at 0xb754f1dc]]&gt;
&gt;&gt;&gt; print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
 (1, 2, 4, 3),
 (1, 3, 2, 4),
 (1, 3, 4, 2),
 (1, 4, 2, 3),
 (1, 4, 3, 2),
 (2, 1, 3, 4),
 (2, 1, 4, 3),
 (2, 3, 1, 4),
 (2, 3, 4, 1),
 (2, 4, 1, 3),
 (2, 4, 3, 1),
 (3, 1, 2, 4),
 (3, 1, 4, 2),
 (3, 2, 1, 4),
 (3, 2, 4, 1),
 (3, 4, 1, 2),
 (3, 4, 2, 1),
 (4, 1, 2, 3),
 (4, 1, 3, 2),
 (4, 2, 1, 3),
 (4, 2, 3, 1),
 (4, 3, 1, 2),
 (4, 3, 2, 1)]
</code></pre>
<p>理解迭代的内部机制：
迭代(iteration）就是对可迭代对象（iterables，实现了__iter__()方法）和迭代器（iterators，实现了__next__()方法）的一个操作过程。可迭代对象是任何可返回一个迭代器的对象，迭代器是应用在迭代对象中迭代的对象，换一种方式说的话就是：iterable对象的__iter__()方法可以返回iterator对象，iterator通过调用next()方法获取其中的每一个值(译者注)，读者可以结合Java API中的 Iterable接口和Iterator接口进行类比。</p>
<hr>
<p>wklken</p>
<p>2013-11-30</p>
]]></content>
		</item>
		
		<item>
			<title>Python-进阶-functools模块小结</title>
			<link>https://wklken.me/posts/2013/08/18/python-extra-functools.html</link>
			<pubDate>Sun, 18 Aug 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/08/18/python-extra-functools.html</guid>
			<description>文档 地址 functools.partial 作用: functools.partial 通过包装手法，允许我们 &amp;ldquo;重新定义&amp;rdquo; 函数签名 用一些默认参数包装一个可调用对象,返回结果是可调用对象，</description>
			<content type="html"><![CDATA[<p>文档 <a href="http://docs.python.org/2/library/functools.html">地址</a></p>
<h3 id="functoolspartial">functools.partial</h3>
<p>作用:</p>
<p>functools.partial 通过包装手法，允许我们 &ldquo;重新定义&rdquo; 函数签名</p>
<p>用一些默认参数包装一个可调用对象,返回结果是可调用对象，并且可以像原始对象一样对待</p>
<p>冻结部分函数位置函数或关键字参数，简化函数,更少更灵活的函数参数调用</p>
<pre><code>:::python
#args/keywords 调用partial时参数
def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(args + fargs), **newkeywords) #合并，调用原始函数，此时用了partial的参数
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc
</code></pre>
<p>声明：</p>
<pre><code>:::python
urlunquote = functools.partial(urlunquote, encoding='latin1')
</code></pre>
<p>当调用 urlunquote(*args, **kargs)</p>
<p>相当于 urlunquote(*args, **kargs, encoding=&lsquo;latin1&rsquo;)</p>
<p>E.g:</p>
<pre><code>:::python
import functools

def add(a, b):
    return a + b

add(4, 2)
6

plus3 = functools.partial(add, 3)
plus5 = functools.partial(add, 5)

plus3(4)
7
plus3(7)
10

plus5(10)
15
</code></pre>
<p>应用:</p>
<p>典型的，函数在执行时，要带上所有必要的参数进行调用。</p>
<p>然后，有时参数可以在函数被调用之前提前获知。</p>
<p>这种情况下，一个函数有一个或多个参数预先就能用上，以便函数能用更少的参数进行调用。</p>
<h3 id="functoolupdate_wrapper">functool.update_wrapper</h3>
<p>默认partial对象没有__name__和__doc__, 这种情况下，对于装饰器函数非常难以debug.使用update_wrapper(),从原始对象拷贝或加入现有partial对象</p>
<p>它可以把被封装函数的__name__、<strong>module</strong>、__doc__和 __dict__都复制到封装函数去(模块级别常量WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES)</p>
<pre><code>&gt;&gt;&gt; functools.WRAPPER_ASSIGNMENTS
('__module__', '__name__', '__doc__')
&gt;&gt;&gt; functools.WRAPPER_UPDATES
('__dict__',)
</code></pre>
<p>这个函数主要用在装饰器函数中，装饰器返回函数反射得到的是包装函数的函数定义而不是原始函数定义</p>
<pre><code>#!/usr/bin/env python
# encoding: utf-8

def wrap(func):
    def call_it(*args, **kwargs):
        &quot;&quot;&quot;wrap func: call_it&quot;&quot;&quot;
        print 'before call'
        return func(*args, **kwargs)
    return call_it

@wrap
def hello():
    &quot;&quot;&quot;say hello&quot;&quot;&quot;
    print 'hello world'

from functools import update_wrapper
def wrap2(func):
    def call_it(*args, **kwargs):
        &quot;&quot;&quot;wrap func: call_it2&quot;&quot;&quot;
        print 'before call'
        return func(*args, **kwargs)
    return update_wrapper(call_it, func)

@wrap2
def hello2():
    &quot;&quot;&quot;test hello&quot;&quot;&quot;
    print 'hello world2'

if __name__ == '__main__':
    hello()
    print hello.__name__
    print hello.__doc__

    print
    hello2()
    print hello2.__name__
    print hello2.__doc__
</code></pre>
<p>得到结果：</p>
<pre><code>before call
hello world
call_it
wrap func: call_it

before call
hello world2
hello2
test hello
</code></pre>
<h3 id="functoolwraps">functool.wraps</h3>
<p>调用函数装饰器partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)的简写</p>
<pre><code>:::python
from functools import wraps
def wrap3(func):
    @wraps(func)
    def call_it(*args, **kwargs):
        &quot;&quot;&quot;wrap func: call_it2&quot;&quot;&quot;
        print 'before call'
        return func(*args, **kwargs)
    return call_it

@wrap3
def hello3():
    &quot;&quot;&quot;test hello 3&quot;&quot;&quot;
    print 'hello world3'
</code></pre>
<p>结果</p>
<pre><code>before call
hello world3
hello3
test hello 3
</code></pre>
<h3 id="functoolsreduce">functools.reduce</h3>
<pre><code>:::python
functools.reduce(function, iterable[, initializer])
</code></pre>
<p>等同于内置函数reduce()</p>
<p>用这个的原因是使代码更兼容(python3)</p>
<h3 id="functoolscmp_to_key">functools.cmp_to_key</h3>
<pre><code>:::python
functools.cmp_to_key(func)
</code></pre>
<p>将老式鼻尖函数转换成key函数，用在接受key函数的方法中(such as sorted(), min(), max(), heapq.nlargest(), heapq.nsmallest(), itertools.groupby())</p>
<p>一个比较函数，接收两个参数，小于，返回负数，等于，返回0，大于返回整数</p>
<p>key函数，接收一个参数，返回一个表明该参数在期望序列中的位置</p>
<p>例如:</p>
<pre><code>:::python
sorted(iterable, key=cmp_to_key(locale.strcoll))  # locale-aware sort order
</code></pre>
<h3 id="functoolstotal_ordering">functools.total_ordering</h3>
<pre><code>:::python
functools.total_ordering(cls)
</code></pre>
<p>这个装饰器是在python2.7的时候加上的，它是针对某个类如果定义了__lt__、<strong>le</strong>、<strong>gt</strong>、__ge__这些方法中的至少一个，使用该装饰器，则会自动的把其他几个比较函数也实现在该类中</p>
<pre><code>:::python
@total_ordering
class Student:
    def __eq__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) ==
                (other.lastname.lower(), other.firstname.lower()))
    def __lt__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) &lt;
                (other.lastname.lower(), other.firstname.lower()))
print dir(Student)
</code></pre>
<p>得到</p>
<pre><code>['__doc__', '__eq__', '__ge__', '__gt__', '__le__', '__lt__', '__module__']
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>[翻译]130&#43;vim基本命令</title>
			<link>https://wklken.me/posts/2013/08/17/130-essential-vim-commands.html</link>
			<pubDate>Sat, 17 Aug 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/08/17/130-essential-vim-commands.html</guid>
			<description>文章 链接 从八十年代起，vi和vim在程序员中十分流行.5年前，我写了《程序员必须知道的100个vim命令》，这是重写更新版本，希望你喜欢! 基</description>
			<content type="html"><![CDATA[<p>文章  <a href="http://www.catswhocode.com/blog/130-essential-vim-commands">链接</a></p>
<p>从八十年代起，vi和vim在程序员中十分流行.5年前，我写了《程序员必须知道的100个vim命令》，这是重写更新版本，希望你喜欢!</p>
<h3 id="基础">基础</h3>
<pre><code>:e filename     在编辑器中打开一个文件
:w              保存文件
:q              退出vim
:q!             退出但不保存
:x              写文件(如果有做修改)并退出
:sav filename   保存为
.               在正常模式中重复执行上一个变更
5.              重复五次
</code></pre>
<h3 id="移动">移动</h3>
<pre><code>k or Up Arrow   上移一行
j or Down Arrow 下移一行
e               移动到单词末尾
b               移动到单词开头
0               移动到行首
G               移动到文件末尾
gg              移动到文件开头
L               移动到屏幕底
:59             移动到59行
20|             移动到第20列
%               移动到匹配的括号
[[              到函数头
[{              到块开始位置
</code></pre>
<h3 id="剪切复制和粘贴">剪切，复制和粘贴</h3>
<pre><code>y   拷贝选中部分到剪贴板
p   粘贴剪贴板中内容
dd  剪切当前行
yy  拷贝当前行
y$  拷贝到行尾
D   剪切到行尾
</code></pre>
<h3 id="搜索">搜索</h3>
<pre><code>/word           从开头到结尾搜索单词word
?word           从结尾到卡头
*               搜索光标下单词
/\cstring       搜索string或STRING, 大小写不敏感
/jo[ha]n        搜索john 或 joan
/\&lt; the         搜索以the开头的，the, theatre 或 then
/the\&gt;          搜索以the结尾的，the 或 breathe
/\&lt; the\&gt;       搜索the
/\&lt; ¦.\&gt;        搜索所有含有四个字母的
/\/             搜索fred 但不是alfred 或 frederick
/fred\|joe      搜索fred 或 joe
/\&lt;\d\d\d\d\&gt;   搜索仅有四个数字的
/^\n\{3}        搜索连续3个空行的
:bufdo /searchstr/  在所有打开buf中搜索
bufdo %s/something/somethingelse/g  在所有打开buf中搜索并替换
</code></pre>
<h3 id="替换">替换</h3>
<pre><code>:%s/old/new/g           将所有出现的old替换为new
:%s/onward/forward/gi   将所有onward替换为forward，大小写不敏感
:%s/old/new/gc          替换前确认
:2,35s/old/new/g        将第2行到第35行之间的old替换为new
:5,$s/old/new/g         将第5行到文件结尾的old替换为new
:%s/^/hello/g           在每一行开头加入hello
:%s/$/Harry/g           在每一行结尾加入Harry
:%s/ *$//g              删除每行末尾无用空格
:g/string/d             删除所有包含string的行
:v/string/d             删除所有不包含string的行
:s/Bill/Steve/          替换当前行第一个Bill为Steve
:s/Bill/Steve/g         替换当前行中所有Bill
:%s/Bill/Steve/g        替换文件中所有Bill
:%s/^M//g               删掉DOS保存文件中(^M)
:%s/\r/\r/g             Transform DOS carriage returns in returns
:%s#&lt;[^&gt;]\+&gt;##g         删除html标签但是保留文本
:%s/^\(.*\)\n\1$/\1/    删除所有连续出现过两次的行，保留一行
Ctrl+a                  递增光标下的数字
Ctrl+x                  递减光标下的数字
ggVGg?                  文本转换为 Rot13
</code></pre>
<h3 id="大小写">大小写</h3>
<pre><code>Vu                  整行小写
VU                  整行大写
g~~                 整行大小写反转
vEU                 单词转为大写
vE~                 单词大小写反转
ggguG               所有文本小写
gggUG               所有文本大写
:set ignorecase     搜索中忽略大小写
:set smartcase      搜索中忽略大小写，除非搜索词中存在大小写字母
:%s/\&lt;./\u&amp;/g       将所有单词首字母大写
:%s/\&lt;./\l&amp;/g       将所有单词首字母小写
:%s/.*/\u&amp;          将每行第一个字母大写
:%s/.*/\l&amp;          将每行第一个字母小写
</code></pre>
<h3 id="读写文件">读写文件</h3>
<pre><code>:1,10 w outfile     1到10行内容写到outfile
:1,10 w &gt;&gt; outfile  1到10行内容追加到outfile
:r infile           插入文件内容
:23r infile         插入文件23行的内容
</code></pre>
<h3 id="文件浏览器">文件浏览器</h3>
<pre><code>:e .                打开完整文件浏览器
:Sex                切分窗口，打开文件浏览器
:Sex!               同上，垂直切分
:browse e           图像化文件浏览器
:ls                 列出buffers
:cd ..              移到上一层目录
:args               列出文件
:args *.php         打开文件列表
:grep expression *.php  返回包含expression的php文件列表
gf                  打开光标下文件名对应的文件
</code></pre>
<h3 id="和unix交互">和Unix交互</h3>
<pre><code>:!pwd               执行pwd命令，返回结果
!!pwd               执行命令并插入结果到文件中
:sh                 临时返回unix
$exit               从unix中返回vim
</code></pre>
<h3 id="对齐">对齐</h3>
<pre><code>:%!fmt              所有行对齐
!}fmt               当前位置所有行对齐
5!!fmt              后五行对齐
</code></pre>
<h3 id="tabs和windows">Tabs和Windows</h3>
<pre><code>:tabnew             创建一个新的tab
gt                  展示下一个tab
:tabfirst           展示第一个tab
:tablast            展示最后一个tag
:tabm n(position)   重排tab
:tabdo %s/foo/bar/g 在所有tab中执行一个命令
:tab ball           将所有打开文件放入tab中
:new abc.txt        在新window中编辑abc.txt
</code></pre>
<h3 id="窗口分屏">窗口分屏</h3>
<pre><code>:e filename         在当前窗口中编辑文件
:split filename     切分当前窗口并打开文件(缩写 :sp filename)
ctrl-w up arrow     移到上一个文件
ctrl-w ctrl-w       移到下一个窗口
ctrl-w_             当前窗口垂直最大化
ctrl-w|             当前窗口水平最大化
ctrl-w=             所有窗口等大小
10 ctrl-w+          当前窗口增加10行
:vsplit file        竖直切分窗口
:sview file         同:split, 只读模式
:hide               关闭当前窗口
:­nly               关闭出了当前窗口之外的所有窗口
:b 2                打开2号窗口
</code></pre>
<h3 id="自动补全">自动补全</h3>
<pre><code>Ctrl+n Ctrl+p (插入模式)  补全单词
Ctrl+x Ctrl+l           补全行
:set dictionary=dict    定义dict为dictionnary
Ctrl+x Ctrl+k           用字典中内容补全
</code></pre>
<h3 id="标签">标签</h3>
<pre><code>m {a-z}                 在当前位置做标签{a-z}
' {a-z}                 移动到标签位置
''                      移动到上一个位置
</code></pre>
<h3 id="缩写">缩写</h3>
<pre><code>:ab mail mail@provider.org      定义mail 作为 mail@provider.org的缩写
</code></pre>
<h3 id="文本缩进">文本缩进</h3>
<pre><code>:set autoindent         打开自动缩进
:set smartindent        打开自动智能缩进
:set shiftwidth=4       缩进设为4个空格
ctrl-t, ctrl-d          插入模式中缩进/去缩进
&gt;&gt;                      缩进
&lt;&lt;                      去缩进
=%                      缩进括号中的代码
1GVG=                   缩进整个文件
</code></pre>
<h3 id="语法高亮">语法高亮</h3>
<pre><code>:syntax on              打开语法高亮
:syntax off             关闭语法高亮
:set syntax=perl        强制语法高亮
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Mysql基础笔记</title>
			<link>https://wklken.me/posts/2013/08/11/mysql-base.html</link>
			<pubDate>Sun, 11 Aug 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/08/11/mysql-base.html</guid>
			<description>update: 2013-08-11, 笔记录了一半不到，还没码完&amp;hellip;.&amp;gt;&amp;lt; update: 2013-11-23, DONE mysql速查 参考： Mysql必知必会,网络blog, stackoverflow 注； 基础部分s</description>
			<content type="html"><![CDATA[<p>update: 2013-08-11, 笔记录了一半不到，还没码完&hellip;.&gt;&lt;
update: 2013-11-23, DONE</p>
<p>mysql速查</p>
<p>参考： Mysql必知必会,网络blog, stackoverflow</p>
<p>注； 基础部分sql参考 《mysql必知必会》, 还不错的一本书，菜鸟入门级，需要的话可以入手</p>
<p>在浏览器中使用查找</p>
<p>寻找一个好的mysql开源gui工具</p>
<h2 id="环境配置">环境配置</h2>
<p>ubuntu安装mysql</p>
<pre><code>sudo apt-get install mysql-server mysql-client
netstat -nltp | grep mysql
配置文件 /etc/mysql/my.conf
</code></pre>
<h2 id="基本概念">基本概念</h2>
<p>数据库基础：</p>
<pre><code>InnoDB是一个可靠地事务处理引擎，不支持全文本搜索
MyISAM是一个性能极高的引擎，支持全文本搜索，不支持事务处理
</code></pre>
<p>数据库-database</p>
<pre><code>保存有组织的数据的容器（通常是一个文件或一组文件）
</code></pre>
<p>表-table</p>
<pre><code>某种特定类型数据的结构化清单
</code></pre>
<p>模式-schema</p>
<pre><code>关于数据库和表的布局及特性的信息
</code></pre>
<p>列-column</p>
<pre><code>表中的一个字段，所有表都是由一个或多个列组成的
</code></pre>
<p>数据类型-datatype</p>
<pre><code>所容许的数据的类型。每个表列都有相应的数据类型，它限制（或容许）该列中存储的数据
</code></pre>
<p>行-row</p>
<pre><code>表中的一个记录
</code></pre>
<p>主键-primary key</p>
<pre><code>一列或一组列，其值能够唯一区分表中的每个行
</code></pre>
<h2 id="mysql命令行">mysql命令行</h2>
<h3 id="进入">进入</h3>
<pre><code>输入： mysql
或者   mysql -u ken
       mysql -u ken -p -h myserver -P 9999 【给出用户名，主机名，端口】

获取帮助: mysql --help
</code></pre>
<p>命令格式和说明：</p>
<pre><code>1.命令必须；或\g结束，仅Enter不执行明林
2.help 或\h获得帮助
3.quit或exit退出
</code></pre>
<p>可以用GUI工具</p>
<pre><code>MySQL Administrator
MySQL Query Browser
</code></pre>
<h3 id="use">use</h3>
<p>创建库:</p>
<pre><code>&gt;CREATE DATABASE MYSQLDATA
</code></pre>
<p>使用某个库</p>
<pre><code>use db_name
</code></pre>
<h3 id="show">show</h3>
<p>查看所有数据库</p>
<pre><code>show databases;
</code></pre>
<p>列出库中所有表</p>
<pre><code>use db_name;
show tables;
</code></pre>
<p>列出表的所有列信息</p>
<pre><code>show columns from table_name;
or
desc table_name;
</code></pre>
<p>显示创建的sql语句</p>
<pre><code>show create database db_name;
show create table table_name;
</code></pre>
<p>其他</p>
<pre><code>show status  服务器状态信息
show grants  显示授权用户
show errors/show warnings 显示服务器错误或警告信息
</code></pre>
<h2 id="查询">查询</h2>
<p>SELECT子句顺序</p>
<pre><code>SELECT
FROM
WHERE
GROUP BY
HAVING
ORDER BY
LIMIT
</code></pre>
<h3 id="select">select</h3>
<p>检索单个列</p>
<pre><code>&gt;SELECT col FROM tb_name;
</code></pre>
<p>多个列</p>
<pre><code>&gt;SELECT col1, col2
 FROM tb_name
</code></pre>
<p>检索所有列</p>
<pre><code>&gt;SELECT *
 FROM tb_name;
#除非确认要用到所有列
</code></pre>
<p>检索去重</p>
<pre><code>&gt;SELECT DISTINCT col
 FROM tb_name
</code></pre>
<p>限制结果数</p>
<pre><code>&gt;SELECT col1
 FROM tb_name
 LIMIT 5;
返回不多于五行

&gt;SELECT col1
 FROM tb_name
 LIMIT 5, 5
 第一个为开始位置，初始为0.第二个为显示个数
等价于LIMIT 5 OFFSET 5
</code></pre>
<h3 id="order-by">order by</h3>
<p>按某个字段排序</p>
<pre><code>&gt;SELECT col1
 FROM tb_name
 ORDER BY col1
</code></pre>
<p>按多列排序</p>
<pre><code>&gt;SELECT col1, col2, col3
 FROM tb_name
 ORDER BY col1, col2
</code></pre>
<p>指定排序方向（升序降序）</p>
<pre><code>&gt;SELECT col1, col2
 FROM tb_name
 ORDER BY col1 DESC;【默认ASC】
注意：如果想在多个列上排序，必须对每个列使用DESC
注意：ORDER BY必须放在LIMIT之前
</code></pre>
<h3 id="where">where</h3>
<p>过滤</p>
<pre><code>&gt;SELECT col1, col2
 FROM tb_name
 WHERE col1 = 2.5;
</code></pre>
<p>过滤不匹配</p>
<pre><code>&gt;SELECT col1, col2
 FROM tb_name
 WHERE col1 &lt;&gt; 1000
</code></pre>
<p>范围检查</p>
<pre><code>&gt;SELECT col1, col2
 FROM tb_name
 WHERE col1 BETWEEN 5 AND 10
</code></pre>
<p>空值检查</p>
<pre><code>&gt;SELECT col1
 FROM tb_name
 WHERE col2 IS NULL
NULL, 无值，它与字段包含0，空字符串或仅仅包含空格不同
</code></pre>
<p>多条件，组合and</p>
<pre><code>&gt;SELECT col1
 FROM tb_name
 WHERE col1=100 AND col2 &lt;= 10
</code></pre>
<p>多条件, 组合or</p>
<pre><code>&gt;SELECT col1
 FROM tb_name
 WHERE col1=100 OR col2 &lt;= 10
</code></pre>
<p>优先级 and 大于 or, 先处理的and,所以应该适当使用括号</p>
<pre><code>select prod_id from products where (prod_price &lt; 2.5 or vend_id = 1000) and prod_price &gt; 1;
</code></pre>
<p>指定查询范围, in操作符</p>
<pre><code>&gt;SELECT col1
 FROM tb_name
 WHERE col1 IN (1001,1002)
</code></pre>
<p>取反，not操作符</p>
<pre><code>&gt;SELECT col1
 FROM tb_name
 WHERE col1 NOT IN (1001,1002)
</code></pre>
<p>操作符</p>
<pre><code>=
&lt;&gt;
!=
&lt;
&lt;=
&gt;
&gt;=
between A and B
</code></pre>
<h3 id="like">like</h3>
<p>通配</p>
<pre><code>&gt;SELECT col1
 FROM tb_name
 WHERE col1 LIKE ‘jet%’

%匹配0个或多个字符
</code></pre>
<p>单个字符</p>
<pre><code>&gt;SELECT col1
 FROM tb_name
 WHERE col1 LIKE ‘_ ton anvil’
</code></pre>
<h3 id="数据过滤regexp">数据过滤regexp</h3>
<p>正则搜索</p>
<pre><code>&gt;SELECT col1
FROM tb_name
WHERE col1 REGEXP ‘1000’

REGEXP ‘.000’
REGEXP对列值匹配
</code></pre>
<p>进行or匹配</p>
<pre><code>&gt;SELECT col1
 FROM tb_name
 WHERE col1 REGEXP ‘1000|2000’
</code></pre>
<p>几个之一</p>
<pre><code>select prod_id from products where prod_name regexp '[1|2]000';
</code></pre>
<p>匹配范围</p>
<pre><code>select prod_id from products where prod_name regexp '[1-5]000';
</code></pre>
<p>匹配特殊字符，\ 进行转义</p>
<pre><code>必须使用\\为前导。 \\-
&gt;SELECT col1
 FROM tb_name
 WHERE col1 REGEXP ‘\\.’
</code></pre>
<p>like和 regexp</p>
<pre><code>like整列匹配
regexp 列值内匹配
</code></pre>
<h3 id="concat">concat</h3>
<p>拼接字符</p>
<pre><code>&gt;SELECT Concat(name, ‘ ----‘, age)
 FROM tb_name
</code></pre>
<p>去除空白</p>
<pre><code>&gt;SELECT Rtrim(name)
 FROM tb_name

Ltrim() Trim()
</code></pre>
<p>使用列名</p>
<pre><code>&gt;SELECT Concat(name, ‘---‘, age) AS info
 FROM tb_name
</code></pre>
<p>算术计算</p>
<pre><code>&gt;SELECT quantity * item_price AS total_price
 FROM tb_name

支持+ - * /
</code></pre>
<h3 id="文本函数">文本函数</h3>
<p>文本处理函数</p>
<pre><code>left()  串左边字符
length() 串长度
locate() 找出串的一个子串
lower() 转为小写
ltrim() 去掉左边空格
right() 返回串右边字符
rtrim() 去掉串右边空格
soundex() 返回字符串soundex值
upper() 大写
</code></pre>
<p>eg</p>
<pre><code>&gt;SELECT Upper(name)
FROM tb_name
</code></pre>
<h3 id="日期函数">日期函数</h3>
<p>日期和时间处理函数</p>
<pre><code>adddate() 增加一个日期-天或周
addtime() 增加一个时间
curdate() 返回当前日期
curtime() 返回当前时间
date() 返回日期时间的日期部分
datediff() 计算两个日期差
date_add() 高度灵活的日期运算函数
date_format() 返回一个格式化的日期或时间串
day() 返回一个日期的天数部分
dayofweek() 对于一个日期，返回对应的星期几
hour()
minute()
month()
now() 当前日期和时间
second()
time() 当前日期时间的时间部分
year()
</code></pre>
<p>eg</p>
<pre><code>&gt;SELECT col1
 FROM tb_name
 WHERE Date(order_date) = ‘2005-09-01’
</code></pre>
<p>常用日期和时间函数</p>
<pre><code>Date()返回日期时间的日期部分
Day()返回日期的天数部分
</code></pre>
<h3 id="数值函数">数值函数</h3>
<p>数值处理函数</p>
<pre><code>abs()
cos()
exp() 指数
mod()
pi() 返回圆周率
rand() 随机数
sin()
sqrt()
tan()
</code></pre>
<h3 id="聚集函数">聚集函数</h3>
<p>avg 平均</p>
<pre><code>&gt;SELECT AVG(price) AS avg_price
 FROM tb_name
</code></pre>
<p>count 计数</p>
<pre><code>select count(*) from products; #无论Null还是非空，均纳入计数
select count(prod_id) from products; #计数有值记录，忽略NULL值
</code></pre>
<p>max 最大</p>
<pre><code>&gt;SELECT MAX(price) AS max_price
 FROM tb_name
</code></pre>
<p>min 最小</p>
<pre><code>&gt;SELECT MIN(price) AS min_price
 FROM tb_name
</code></pre>
<p>sum 求和</p>
<pre><code>&gt;SELECT SUM(quantity) AS total
 FROM tb_name
#sum函数忽略值为NULL的行
</code></pre>
<h3 id="group">group</h3>
<p>group</p>
<pre><code>&gt;SELECT id, COUNT(*) AS num_prods
 FROM tb_name
 GROUP BY id
</code></pre>
<p>注意：</p>
<pre><code>1.group by 可以包含任意数目的列
2.group by 中每个列都必须是检索列或有效的表达式（但不能使聚集函数）
3.除聚集函数外，select语句中的每个列都必须在group by子句中出现
4.如果分组列有Null值，Null将作为一个分组返回
5.group by 子句必须出现在where子句之后, order by 之前
</code></pre>
<p>过滤分组</p>
<pre><code>&gt;SELECT cust_id, COUNT(*) AS orders
 FROM orders
 GROUP BY cust_id
 HAVING COUNT(*) &gt; 2
</code></pre>
<p>where和having区别</p>
<pre><code>where在分组前过滤，having在分组后过滤
</code></pre>
<h3 id="子查询">子查询</h3>
<p>1.用于过滤</p>
<pre><code>&gt;SELECT cust_id
 FROM orders
 WHERE order_num IN (SELECT order_num
                    FROM orderitems)
</code></pre>
<p>2.作为字段</p>
<pre><code>&gt;SELECT cust_name,
       cust_state,
       (SELECT COUNT(*)
        FROM orders
        WHERE orders.cust_id = customers.cust_id) AS orders
 FROM customers
 ORDER BY cust_name
</code></pre>
<h3 id="联结表">联结表</h3>
<p>1.创建联结</p>
<pre><code>&gt;SELECT vend_name, prod_name, prod_price
 FROM vendors, products
 WHERE vendors.vend_id = products.vend_id
 ORDER BY vend_name, prod_name;
</code></pre>
<p>可进行联结多个表</p>
<p>2.内部联结</p>
<pre><code>&gt;SELECT vend_name,prod_name,prod_price
 FROM vendors INNER JOIN products ON vendors.vend_id = products.vend_id
</code></pre>
<h3 id="高级联结表">高级联结表</h3>
<p>1.自联结</p>
<pre><code>&gt;SELECT prod_id, prod_name
 FROM products
 WHERE vend_id = (SELECT vend_id FROM products
                WHERE prod_id = ‘DTNTR’)
</code></pre>
<p>等价于</p>
<pre><code>&gt;SELECT p1.prod_id, p1.prod_name
 FROM products AS p1, products AS p2
 WHERE p1.vend_id = p2.vend_id
        AND p2.prod_id = ‘DTNTR’
</code></pre>
<p>2.外部联结</p>
<pre><code>&gt;SELECT customers.cust_id, orders.order_num
 FROM customers LEFT OUTER JOIN orders
      ON customers.cust_id = orders.cust_id
</code></pre>
<h3 id="组合查询">组合查询</h3>
<p>1.UNION</p>
<pre><code>&gt;SELECT vend_id, prod_id, prod_price
 FROM products
 WHERE prod_price &lt;=5
 UNION
 SELECT vend_id, prod_id, prod_price
 FROM products
 WHERE vend_id IN (1001,1002)

UNION自动去除重复行
UNION ALL 保留
</code></pre>
<p>2.放在UNION后的排序语句</p>
<pre><code>对所有SELECT生效
</code></pre>
<h3 id="全文本搜索">全文本搜索</h3>
<p>MyISAM 支持全文本搜索</p>
<p>InnoDB不支持全文本搜索</p>
<p>1.启用</p>
<pre><code>&gt;CREATE TABLE productnotes(
 note_id int NOT NULL AUT_INCREMENT,
 note_text text NULL,
 FULLTEXT(note_text)
</code></pre>
<p>2.进行全文本搜索</p>
<pre><code>&gt;SELECT note_text
 FROM tb_name
 WHERE Match(note_text) Against(‘rabbit’)
</code></pre>
<p>3.布尔文本搜索</p>
<pre><code>&gt;SELECT note_text
 FROM productontes
 WHERE Match(note_text) Against(‘heavy’ IN BOOLEAN MODE)
</code></pre>
<h2 id="插入数据">插入数据</h2>
<p>1.基本插入</p>
<pre><code>&gt;INSERT INTO customers(cust_name,
                     cust_address)
 VALUES(‘Pep’, ‘100 main street’)
</code></pre>
<p>2.插入多行</p>
<pre><code>&gt;INSERT INTO customers(cust_name,
                     cust_address)
 VALUES(‘Pep’, ‘100 main street’),
       (‘Tim’, ‘200 main Street’);
</code></pre>
<p>3.插入检索出来的数据</p>
<pre><code>&gt;INSERT INTO customers(cust_name,
                     cust_address)
 SELECT cust_name, custaddress
 FROM custnew;
</code></pre>
<h2 id="更新">更新</h2>
<p>1.更新行</p>
<pre><code>&gt;UPDATE customers
 SET cust_email = ‘a@fudd.com’
 WHERE cust_id = 10005
</code></pre>
<p>2.即使发生错误也继续进行而不是退出</p>
<pre><code>&gt;UPDATE IGNORE customers
</code></pre>
<h2 id="删除">删除</h2>
<p>1.删除数据</p>
<pre><code>&gt;DELETE FROM customers
 WHERE cust_id = 10006
</code></pre>
<h2 id="表操作">表操作</h2>
<p>1.创建表</p>
<pre><code>&gt;CREATE TABLE customers(
   cust_id int NOT NULL AUTO_INCREMENT,
   cust_name char(50) NOT NULL,
   vend_city char(50) NULL,
   quantity int NOT NULL DEFAULT 1,
   PRIMARY KEY(cust_id)
)ENGINE=InnoDB
</code></pre>
<p>2.更新表</p>
<p>加字段</p>
<pre><code>&gt;ALTER TABLE vendors
 ADD vend_phone CHAR(20)
</code></pre>
<p>删除某个字段</p>
<pre><code>&gt;ALTER TABLE tb1 DROP COLUMN names;
</code></pre>
<p>改变列类型</p>
<pre><code>&gt;ALTER TABLE infos CHANGE list list tinyint NOT NULL DEFAULT '0'
</code></pre>
<p>加主键</p>
<pre><code>&gt;ALTER TABLE tb1 ADD primary key(id)
</code></pre>
<p>删除一个字段</p>
<pre><code>&gt;ALTER TABLE tb1 DROP field_name
</code></pre>
<p>增加自增长主键</p>
<pre><code>alter table customers change id id not null auto_increment primary key;
</code></pre>
<p>增加新字段并设置为主键</p>
<pre><code>Alter TABLE tablename ADD new_field_id int(5) default 0 not null auto_increment ADD primary key(new_field_id)

ALTER TABLE example ADD ID INT NOT NULL;
ALTER TABLE example ADD UNIQUE(url)

&gt;ALTER TABLE vendors
 DROP COLUMN vend_phone
</code></pre>
<p>alter table syntax:
<a href="http://dev.mysql.com/doc/refman/5.1/en/alter-table.html">http://dev.mysql.com/doc/refman/5.1/en/alter-table.html</a></p>
<p>3.删除表</p>
<pre><code>&gt;DROP TABLE customers2;
</code></pre>
<p>4.清空表数据</p>
<pre><code>&gt;DELETE FROM mytable;
</code></pre>
<p>5.重命名表</p>
<pre><code>&gt;RENAME TABLE customers2 TO customers;
 ALTER TABLE 'oldname' RENAME TO 'newname'
</code></pre>
<h2 id="视图操作">视图操作</h2>
<p>1.创建视图</p>
<pre><code>&gt;CREATE VIEW productcustomers AS
 SELECT cust_name, cust_contact
 FROM customers, orders, orderitems
 WHERE customers.cust_id = orders.cust_id
</code></pre>
<p>2.使用视图</p>
<pre><code>&gt;SELECT cust_name, cust_contact
 FROM productcustomers
 WHERE prod_id = ‘TNT2’
</code></pre>
<h2 id="存储过程">存储过程</h2>
<p>1.创建简单存储过程</p>
<pre><code>&gt;CREATE PROCEDURE productpricing()
 BEGIN
SELECT Avg(price) AS priceavg
FROM products;
 END;
</code></pre>
<p>调用:CALL productpricing()</p>
<p>2.删除存储过程</p>
<pre><code>&gt;DROP PROCEDURE productpricing
</code></pre>
<p>3.使用参数</p>
<pre><code>&gt;CREATE PROCEDURE ordertotal(
IN onumber INT,
OUT ototal DECIMAL(8,2)
)
BEGIN
SELECT Sum(item_price*quality)
FROM orderitems
WHERE order_num = onumber
INTO ototal;
END;
</code></pre>
<p>调用:</p>
<pre><code>&gt;CALL ordertotal(200005, @total;
&gt;SELECT @total;
</code></pre>
<p>3.检查存储过程</p>
<pre><code>&gt;SHOW CREATE PROCEDURE ordertotal;
</code></pre>
<h2 id="游标">游标</h2>
<p>1.创建游标</p>
<pre><code>&gt;CREATE PROCEDURE processorders()
 BEGIN
DECLARE ordernumbers CURSOR
FOR
SELECT order_number FROM orders;

BEGIN ordernumbers;

FETCH ordernumbers INTO o;

CLOSE ordernumbers;
 END;

DECLARE CONTINUE HANDLER FOR SQLSTATE ‘02000’ SET done=1;
REPEAT
    FETCH ordernumbers INTO o;
END;
</code></pre>
<h2 id="触发器">触发器</h2>
<p>1.创建触发器</p>
<pre><code>&gt;CREATE TRIGGER newproduct AFTER INSERT ON products
 FOR EACH ROW SELECT ‘Product added’
</code></pre>
<p>2.删除触发器</p>
<pre><code>&gt;DROP TRIGGER newproduct;
</code></pre>
<p>3.INSERT触发器</p>
<pre><code>&gt;CREATE TRIGGER neworder AFTER INSERT ON orders
 FOR EACH ROW SELECT NEW.order_num
</code></pre>
<p>4.DELETE触发器</p>
<pre><code>&gt;CREATE TRIGGER deleteorder BEFORE DELETE ON orders
 FOR EACH ROW
 BEGIN
INSERT INTO archive_orders(order_num, order_date, cust_id)
VALUES(OLD.order_num, OLD.order_date, OLD.cust_id);
 END;
</code></pre>
<p>5.UPDATE触发器</p>
<pre><code>&gt;CREATE TRIGGER updatevendor BEFORE UPDATE ON vendors
 FOR EACH ROW SET NEW.vend_state = Upper(NEW.vend_state);
</code></pre>
<h2 id="事务">事务</h2>
<p>基本概念</p>
<pre><code>ACID
A,原子性，食物是一个原子操作单元，其对数据的修改，要么全执行，要么全不执行
C.一致性，事务开始和完成的时候，数据必须都保持一致状态（所有相关数据规则和内部数据结构）
I.隔离性，保证事务不受外部并发操作影响，即事务处理中间过程状态对外不可见
D.持久性，事务完成后，对数据修改时永久性的，及时出现系统故障也能够保持
</code></pre>
<p>1.事务</p>
<pre><code>&gt;START TRANSACTION
 DELETE FROM ordertotals;
 SELECT * FROM ordertotals;

&gt;ROLLBACK
回退

&gt;COMMIT
提交
</code></pre>
<p>2.设立保留点</p>
<pre><code>&gt;SAVEPOINT delete1;

&gt;ROLLBACK TO delete1;
</code></pre>
<h2 id="导入导出">导入导出</h2>
<p>1.导入</p>
<p>用文本形式插入数据</p>
<pre><code>&gt;LOAD DATA LOCAL INFILE 'd:/mysql.txt' INTO TABLE mytable;
</code></pre>
<p>导入.sql</p>
<pre><code>&gt;use database;
&gt;source d:/mysql.sql
</code></pre>
<p>从另外一张表往这张表插入</p>
<pre><code>INSERT INTO tab1(f1,f2)
SELECT a.f1, a.f2
FROM a WHERE a.f1='a'
</code></pre>
<p>2.备份</p>
<p>导出要用到MySQL的mysqldump工具，基本用法是：</p>
<pre><code>mysqldump [OPTIONS] database [tables]
</code></pre>
<p>备份MySQL数据库的命令</p>
<pre><code>mysqldump -hhostname -uusername -ppassword databasename &gt; backupfile.sql
</code></pre>
<p>备份MySQL数据库为带删除表的格式，能够让该备份覆盖已有数据库而不需要手动删除原有数据库。</p>
<pre><code>mysqldump -–add-drop-table -uusername -ppassword databasename &gt; backupfile.sql
</code></pre>
<p>直接将MySQL数据库压缩备份</p>
<pre><code>mysqldump -hhostname -uusername -ppassword databasename | gzip &gt; backupfile.sql.gz
</code></pre>
<p>备份MySQL数据库某个(些)表</p>
<pre><code>mysqldump -hhostname -uusername -ppassword databasename specific_table1 specific_table2 &gt; backupfile.sql
</code></pre>
<p>同时备份多个MySQL数据库</p>
<pre><code>mysqldump -hhostname -uusername -ppassword –databases databasename1 databasename2 databasename3 &gt; multibackupfile.sql
</code></pre>
<p>仅仅备份数据库结构</p>
<pre><code>mysqldump –no-data –databases databasename1 databasename2 databasename3 &gt; structurebackupfile.sql
</code></pre>
<p>备份服务器上所有数据库</p>
<pre><code>mysqldump –all-databases &gt; allbackupfile.sql
</code></pre>
<p>还原</p>
<p>还原MySQL数据库的命令</p>
<pre><code>mysql -hhostname -uusername -ppassword databasename &lt; backupfile.sql
mysql -hhostname -ppassword databasename tablename &lt; backuptablefile.sql
</code></pre>
<p>还原压缩的MySQL数据库</p>
<pre><code>gunzip &lt; backupfile.sql.gz | mysql -uusername -ppassword databasename
</code></pre>
<p>将数据库转移到新服务器</p>
<pre><code>mysqldump -uusername -ppassword databasename | mysql –host=*.*.*.* -C databasename
</code></pre>
<p>将查询结果导入外部文件</p>
<pre><code>SELECT a,b,a+b 
FROM test_table
INTO OUTFILE '/tmp/result.txt'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '&quot;'
LINES TERMINATED BY '\n'

或者

mysql -u you -p -e &quot;SELECT ...&quot; &gt;  file_name
</code></pre>
<h2 id="性能研究">性能研究</h2>
<pre><code>1.什么情况下无法使用索引？
</code></pre>
<h2 id="实时监控">实时监控</h2>
<p>查看mysql数据库的当前连接数</p>
<pre><code>命令： show processlist;
或者 # mysqladmin -uroot -p密码 processlist
</code></pre>
<p>当前状态</p>
<pre><code>命令： show status;
或者 # mysqladmin -uroot -p密码 status
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>[翻译]vim入门指南</title>
			<link>https://wklken.me/posts/2013/08/04/translation-vim-introduction-and-tutorial.html</link>
			<pubDate>Sun, 04 Aug 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/08/04/translation-vim-introduction-and-tutorial.html</guid>
			<description>今天稍微整理了下vim配置 k-vim 原文链接 Vim Introduction and Tutorial 搜了下没发现中文版,顺手翻译下, 建议读原版 vim简介及入门指南 我经常试图去学习Emac编辑器/I</description>
			<content type="html"><![CDATA[<p>今天稍微整理了下vim配置 <a href="https://github.com/wklken/k-vim">k-vim</a></p>
<p>原文链接 <a href="http://blog.interlinked.org/tutorials/vim_tutorial.html">Vim Introduction and Tutorial</a></p>
<p>搜了下没发现中文版,顺手翻译下, 建议读原版</p>
<hr>
<h1 id="vim简介及入门指南">vim简介及入门指南</h1>
<p>我经常试图去学习Emac编辑器/IDE/OS. 我最后一次尝试时，我花了一些时间去适应，直到我想去配置一个自己的.emacs文件</p>
<p>在我用vi打开.emacs文件那一刻，我意识到我做了什么，vim早已经赢得了我的青睐.</p>
<p>所以，我将vim作为我的首选编辑器[注1]</p>
<p>另一个动机是，我发现我最喜欢的shell(<a href="http://www.zsh.org/">ZSH</a>)有很酷的 vi-mode 包含命令模式(使得，你可以在命令和插入模式之间切换)</p>
<p>Vim有一系列给力的特性，并且学习它们需要花费一些时间.当然，现在有很多在线指南和技巧文章，但是帮助文档同样很优秀！有概览页面，总结页面和一些注释</p>
<p>我开始使用官网的指南和帮助系统学习(输入 :help <command> 可以从命令模式获取帮助)， 我喜欢在测试文件中测试命令，并且对重要的命令写下简短的说明</p>
<p>另一个我使用Vim的原因是，使用Vim比Emacs更加健康(使用默认键映射).健康?很多命令可以简单地通过一次敲击完成 - 模式编辑器的优点，不需要使用由很多修改关键字组成的长命令串.即使你有一个正常的键盘，敲击Ctrl，Alt等键很显然不是那么正常</p>
<p>只需要记住:Vim的命令都非常简单，但是简单命令组合起来将变得十分强大</p>
<h2 id="模式">模式</h2>
<p>Vim有3种模式：</p>
<p>1.命令行模式：所有键盘输入都是命令</p>
<p>2.插入模式:大部分键盘输入作为文本插入(出了少部分特殊键)</p>
<p>3.可视模式：用于帮助选中文本，可以看做是命令行模式的子模式</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/vim-modes.png?raw=true" alt="模式转换图"></p>
<p>从插入模式或可视模式切换到命令模式,按<Esc></p>
<p>从命令模式切换到插入模式:</p>
<pre><code>* i  在当前位置之前插入
* a  在当前位置之后插入(追加)
* I  调到当前行第一个非空白字符之前插入
* A  调到当前行最后一个字符之后插入
</code></pre>
<p>从命令模式到可视模式：</p>
<pre><code>* v  切换到可视模式(字符级别)
* V  切换到可视模式(行级别)
* ctrl-v  切换到块可视模式(块级别的)
</code></pre>
<p>所有操作，比如替换，删除，拷贝或排版，在可视模式下同样适用</p>
<h2 id="移动">移动</h2>
<p>简单的移动命令</p>
<pre><code>* h 左移
* l 右移
* k 上移
* j 下移
</code></pre>
<p>很显然，这几个命令只在命令模式下可用，当然，你可以用方向键(在所有模式下适用)</p>
<p>Vim有很多移动命令，我只是学习了一部分，但是如果你需要一些特殊的移动方式，可以看看帮助，我确定你会找到有用的方法</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/vim-movement.png?raw=true" alt="移动说明图"></p>
<p>Vim distinguishes between screen-lines (those shown on the monitor) and real lines (those ended with a new-line).
Vim区分逻辑行(频幕上展示的)和物理行(实际以\n结尾的)</p>
<p>以下是一些重要的命令</p>
<pre><code>0 当前行第一列
^ 当前行第一个非空白字符
w 移到下一个单词
W 移到下一个单词，忽略标点
e 移动到单词尾部
E 移动到单词尾部，忽略标点
b 移动到单词开头
B 移动到单词开头，忽略标点
ge 移动到前一个词尾部
gE 移动到前一个词尾部，忽略标点
g_ 移动到最后一个非空白字符
$  移动到最后一列
</code></pre>
<p>如果你记住一部分，你将学会如何快速从A移动到B！另一个重要的事实是，这些命令说明了基本命令的定义方式</p>
<h2 id="编辑">编辑</h2>
<p>在Vim中，插入文本是很简单的事情，只需要敲i并且开始输入.但是vim提供了相当丰富的文本编辑命令</p>
<pre><code>d 删除当前光标位置到下一个命令哪个提供位置之间的字符(例如: d$删除当前行光标位置到最后一列的所有字符)
c 修改
x 删除光标位置字符
X 删除光标之前的字符(相当于回退)
y 拷贝
p 在当前光标之后黏贴拷贝的内容
P 在当前光标之前黏贴拷贝的内容
r 替换当前字符
s 用输入替换当前位置到下一个命令给出位置的字符
. 重复上一个命令
</code></pre>
<p>dd, cc 或yy 将操作整行，例如yy拷贝当前行</p>
<p>需要注意的是，很多命令比我在这里描述的额更为强大.例如你可以 输入&quot;ayy 拷贝当前行道一个寄存器a,可以使用 &ldquo;ap 黏贴a中的内容. Vim自动保存最后几个复制或删除的内容,要查看寄存器的内容，输入:registers, 你可以使用它们去黏贴一些老的文本</p>
<h2 id="可视块">可视块</h2>
<p>可视块使得能够在选中文本每一行某个位置插入一个字符</p>
<p>假设你选中了一块代码(Ctrl-v),你可以键入I，在代码块之前插入文本(切换到插入模式).当你离开插入模式时，输入的文本将作用于选中的每一行.使用A在代码块之后进行插入</p>
<p>另一个有用的特性是，你可以用新文本替换整个代码块.选中代码块，输入s，vim进入插入模式，然后输入内容.离开插入模式时，vim将输入的内容插入到剩余行</p>
<p>如果你想追加文本到某些行后边，使用Ctrl-v$选中需要修改的行选中需要修改的行. 这个和上一个命令不同的是，$表示&quot;行尾&rdquo;,ctrl-v选中行的行尾，忽略文本</p>
<p>使用 Ctrl-v:</p>
<pre><code>This is a testNEWLY INSERTED
This is a     NEWLY INSERTED
This is       NEWLY INSERTED
</code></pre>
<p>使用 Ctrl-v$:</p>
<pre><code>This is a testNEWLY INSERTED
This is aNEWLY INSERTED
This isNEWLY INSERTED
</code></pre>
<h2 id="文本对象">文本对象</h2>
<p>Vim命令操作文本对象(字符，单词，括号分割的字符，句子等等)</p>
<p>对我来说，最重要的一个是 在单词中: iw. 要选中当前的单词，只需要键入 viw (v可视模式，iw选中光标所在词),同样的，删除: diw</p>
<p>inner-word/block和a-word/block的区别在于，前者只选中单词的字符(不包括空白字符) 或者括号中的内容(不包括括号本身).后者包括括号本身或者单词后的空白字符</p>
<pre><code>iw 单词
aw 单词+后面空白
iW …inner WORD
aW …a WORD
is 句子
as 句子+后面空白
ip 段落
ap 段落+段落后空白
i( or i) 括号中
a( or a) 括号中+括号
i&lt; or i&gt;
a&lt; or i&gt;
i{ or i}
a{ or a}
i&quot; 引号中
a&quot; 引号中+引号
i`
a`
</code></pre>
<p>下面是命令命令的展示，[]表示选中的文本:</p>
<pre><code>iw   This is a [test] sentence.
aw   This is a [test ]sentence.
iW   This is a […test…] sentence.
aW   This is a […test… ]sentence.
is   …sentence. [This is a sentence.] This…
as   …sentence. [This is a sentence. ]This…
ip   End of previous paragraph.

     [This is a paragraph. It has two sentences.]

     The next.
ap   End of previous paragraph.

     [This is a paragraph. It has two sentences.

     ]The next.
i( or i)     1 * ([2 + 3])
a( or a)     1 * [(2 + 3)]
i&lt; or i&gt;     The &lt;[tag]&gt;
a&lt; or i&gt;     The [&lt;tag&gt;]
i{ or i}     some {[ code block ]}
a{ or a}     some [{ code block }]
i&quot;   The &quot;[best]&quot;
a&quot;   The[ “best”]
i`   The `[best]`
a`   The[ `best`]
</code></pre>
<p>可以尝试使用，熟悉并记住(对我而言，iw和i() 真正地节省了时间)</p>
<h2 id="undo和redo">Undo和Redo</h2>
<p>不要害怕使用一系列命令，你可以在命令模式下撤销，用u - 甚至用ctrl-r撤销刚才的撤销操作</p>
<p>Vim7.0 介绍了撤销分支，但我没有时间去深入了解</p>
<h2 id="外部命令">外部命令</h2>
<p>Vim很容易加入外部命令的输出,或者通过外部过滤器过滤整行或部分</p>
<p>使用外部命令 :!command    输出将会展现出来</p>
<p>通过外部命令过滤文本，可以使用  :!sort %</p>
<p>将外部命令的输出插入到当前文件  :r!command (例如 :r!which ls)</p>
<p>查看更多关于filter的信息 :h filter</p>
<h2 id="搜索和替换">搜索和替换</h2>
<p>在Vim中搜索非常简单，在命令模式下输入 / ，然后填入你要搜索的词，然后vim会搜索这个文件(当前位置向前)</p>
<p>使用? 进行向后搜索</p>
<p>在搜索结果中，使用n或N进行重复搜索(和前一个搜索同一个方向)</p>
<p>如果设置了&quot;incsearch&quot;, Vim会立刻跳到匹配文本位置，如果设置了&quot;hlsearch&quot;, 将会高亮所有匹配文本. 要去除高亮，:nohl</p>
<p>替换同样不是很难，但是你需要对正则表达式有所了解</p>
<p>使用正则替换某些文本，输入 :%s/old/new/gc 这个命令将会遍历整个文件%, 用单词&quot;new&quot;替换所有&quot;old&quot;. g代表替换行中所有匹配文本，c代表替换前询问</p>
<p>如果只需要替换选中区域中的某些文本，选中区域，输入 :s/old/new/g.</p>
<p>这个就像</p>
<pre><code>:'&lt;,'&gt;s/old/new/g
</code></pre>
<p>在标签一节之后，你将会知道 &lsquo;&lt;&lsquo;和&rsquo;&gt;&lsquo;的意思</p>
<h2 id="补全">补全</h2>
<p>在你输入时，反复输入同一个词是很正常的事情. 使用Ctrl-p，vim会反向搜索最近输入过的拥有相同开头的词, ctrl-x ctrl-l补全整个词</p>
<p>如果你不确定如何拼写某个词，并且你设置了拼写检查(:set spell),你可以使用 Ctrl-x Ctrl-k 到字典中查询已经输入的词.Vim自动补全系统在7.0后得到了很大的改善.</p>
<p>注意，自动补全命令仅在插入模式下有效，在命令模式有其他的作用</p>
<h2 id="标签">标签</h2>
<p>你可以在文档中设置标签，实现在文档中不同位置的快速跳转</p>
<p>vim自动设置的标签</p>
<pre><code>{0-9} 关闭文件的最近10个位置(0 最后一个, 1 倒数第二个)
&lt; and &gt; 标记文本左边/右边的位置
( and ) 当前句子的开始/结束
{ and } 当前段落的开始/结束
[ and ] 拷贝或修改文本的第一/最后一个字符的位置
. 最后修改点位置
' or ` 上次跳转点位置
&quot; 退出文件前的最后一个位置
^ 最后插入/修改位置
</code></pre>
<p>要设置自定义标签，使用 m{a-zA-Z} (例如 ma ), 如果要跳转到某个标签(自定义或预定义),可以使用 &rsquo; 和 `</p>
<pre><code>' 将光标移到标记行首个非空字符位置
` 移到设置标签时得准确位置
</code></pre>
<p>大写和小写标记之间有些区别</p>
<pre><code>{a-z} 文件维度
{A-Z} 会话维度，和文件关联，可以跨文件跳转
</code></pre>
<p>例如，可以使用 L 标记work-log 使用T标记time-table ,然后进行快速跳转</p>
<p>例如，你可以键入 &lsquo;&quot; 跳转到文件关闭前光标所在位置(Vim可以通过配置实现)</p>
<pre><code>列出标签列表 :marks

删除 :delmarks a b c

删除所有  :delmarks!
</code></pre>
<h2 id="标签页tab-缓冲buffer-和-窗口window">标签页(tab) ，缓冲(buffer) 和 窗口(window)</h2>
<p>Vim 7.0介绍过tabs. 我们都知道并喜欢tabs,所以这里不多提. (注意: vim中的tabs和其他程序中的tabs略有不同.不同点在于，每个tab页可以拥有自己的布局, 例如我可以在第一个tab中切分窗口，在第二个tab中不切分)</p>
<p>命令行中用vim一次性打开多个tab  vim -p *.txt</p>
<p>切换tab, 可以使用鼠标(gVim) 或者输入 gt</p>
<p>创建一个新的空tab,输入 :tabnew</p>
<p>或者在一个新tab中打开文件  :tabe xyz</p>
<p>buffer和window有些不好理解，window是打开vim时你看到的，当你打开帮助(:help bufers), 你将会得到两个window.并非真正的窗口，是vim提供的展示视图</p>
<p>你可以打开一个window,切分成水平两个 :sp 或者竖直两个 :vsp. 通过这个方法，vim将同一个buffer放在两个不同的window. 你可以打开文件, :sp filea  或者 :vsp fileb. 在窗口之间跳转，命令模式下使用 Ctrl-w {hjkl}</p>
<p>buffer大多数情况下是一个文件，但是不要求可见. 所以通常一个window中有多个buffer, 要在当前window中展现不同的buffer , 使用 :b NUMBER, 可以使用 :buffers查看对应buffer的编号，通常情况下，vim要求你在切换到另一个buffer之前，保存当前buffer, 所以切换报错时不要太过惊讶(可以 :set hidden 使未保存buffer生效，但需谨慎使用)</p>
<p>下面是我的笔记:</p>
<pre><code>:b N 切换到buffer N
:buffers 展示buffer列表, 字符含义
    % 当前window
    # 替换buffer (使用 :e# or :b#切换)
    a 活动的(加载并可见)
    h 隐藏的(加载但不可见)
    + 修改的
:bd 关闭buffer并从buffer列表移除(不关闭vim,即使最后一个buffer关闭)
:bun 关闭buffer但留存在bufferlist
:sp #N 分屏并编辑buffer N
:w 保存当前buffer
:e 重新加载当前文件
:q 退出
:new 新的空window
:on 关闭除当前活动window之外的所有window(Ctrl-W o)
Ctrl-W {h,j,k,l} 窗口之间切换
</code></pre>
<p>设置&rsquo;hidden&rsquo;后，允许修改未保存的buffer隐藏，buffer会自动保存如果未设置&rsquo;hidden&rsquo;，设置了&rsquo;autowrite&rsquo;</p>
<h2 id="宏">宏</h2>
<p>Vim允许使用点. 重复某些命令，当存在多个命令时，使用宏</p>
<p>你可以开始 录制宏， 使用 q和{0-9a-zA-Z}， 例如 qq  录制用于buffer&quot;q&quot;的宏，完成录制后，键入q退出</p>
<p>现在你可以使用 @q 在任何时候调用宏</p>
<h2 id="最后">最后</h2>
<p>我希望可以帮助你开始学习vim. 我可以做的最后一件事是，提供我的vim配置文件.  使用 :help &hellip; 去学习vim更强大的功能并在后续写一篇指南</p>
<p>可以将<a href="http://blog.interlinked.org/static/files/vimrc">vimrc</a>文件放入你的home目录(~/.vimrc),但是要先确定这个目录下还没有这个文件</p>
<p>注解1： Vim是一个编辑器，不是IDE 或者操作系统. 不要通过其试图创建一个IDE,如果你喜欢IDE,挑一个来用！当然，vim可以将很多任务自动化,例如编译，跳转到编译错误处，要实现这些，关注下vim的插件</p>
<p>Emacs is a good operating system, but it lacks a good editor.
— Old saying.</p>
]]></content>
		</item>
		
		<item>
			<title>[翻译整理]stackoverflow python 百问</title>
			<link>https://wklken.me/posts/2013/07/20/python-stackoverflow-vote-top.html</link>
			<pubDate>Sat, 20 Jul 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/07/20/python-stackoverflow-vote-top.html</guid>
			<description>更新到github了，地址：https://github.com/wklken/stackoverflow-py-top-qa 后续这里不更新</description>
			<content type="html"><![CDATA[<p>更新到github了，地址：https://github.com/wklken/stackoverflow-py-top-qa</p>
<p>后续这里不更新了哈</p>
<hr>
<p>进度40%，最近有点犯懒</p>
<p>刚刚注册，好东西</p>
<p>查看了下前面(vote前15页,挑了下,vote都是100+的样子,大概120个)的问题，<a href="http://stackoverflow.com/questions/tagged/python?page=1&amp;sort=votes&amp;pagesize=15">链接</a>, 大体梳理了下,本来想放一页搞定，奈何排版太乱，按类型分了下</p>
<p>第一页的前几个比较长，目测都有中文翻译版本，大家可以网上搜下</p>
<p>其他问题相对比较杂，有些简单，有些复杂，拉过来参考参考也不错</p>
<p>总结整理，复习印证(注意，合并了每个问题的多个答案，但是时间仓促，疏漏难免，感兴趣问题直接点链接看原文吧)</p>
<hr>
<h2 id="基本数据结构列表元组字典等">基本数据结构(列表，元组，字典等)</h2>
<h3 id="判断一个列表为空得最佳实践">判断一个列表为空得最佳实践</h3>
<p>问题 <a href="http://stackoverflow.com/questions/53513/python-what-is-the-best-way-to-check-if-a-list-is-empty">链接</a></p>
<p>答案:</p>
<pre><code>if not a:
    print &quot;List is empty&quot;
#不要用len(a)来判断
</code></pre>
<h3 id="为什么是stringjoinlist而不是listjoinstring">为什么是string.join(list)而不是list.join(string)</h3>
<p>问题 <a href="http://stackoverflow.com/questions/493819/python-join-why-is-it-string-joinlist-instead-of-list-joinstring">链接</a></p>
<pre><code>my_list = [&quot;Hello&quot;, &quot;world&quot;]
print &quot;-&quot;.join(my_list)
#为什么不是 my_list.join(&quot;-&quot;) 。。。。这个....
</code></pre>
<p>答案：</p>
<p>因为所有可迭代对象都可以被连接，但是连接者总是字符串</p>
<h3 id="如何合并两个列表">如何合并两个列表</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1720421/merge-two-lists-in-python">链接</a></p>
<pre><code>listone = [1,2,3]
listtwo = [4,5,6]
#outcome we expect: mergedlist == [1, 2, 3, 4, 5, 6]
</code></pre>
<p>1.不考虑顺序（原来问题不是很明确）</p>
<pre><code>listone + listtwo
#linstone.extend(listtwo)也行，就是会修改listone
</code></pre>
<p>2.考虑顺序做些处理</p>
<pre><code>&gt;&gt;&gt; listone = [1,2,3]
&gt;&gt;&gt; listtwo = [4,5,6]
&gt;&gt;&gt; import itertools
&gt;&gt;&gt; for item in itertools.chain(listone, listtwo):
...     print item
...
1
2
3
4
5
6
</code></pre>
<h3 id="如何扁平一个二维数组">如何扁平一个二维数组</h3>
<p>问题 <a href="http://stackoverflow.com/questions/952914/making-a-flat-list-out-of-list-of-lists-in-python">链接</a></p>
<pre><code>l = [[1,2,3],[4,5,6], [7], [8,9]]
变为[1, 2, 3, 4, 5, 6, 4, 5, 6, 7, 8, 9]
</code></pre>
<p>列表解析</p>
<pre><code>[item for sublist in l for item in sublist]
</code></pre>
<p>itertools</p>
<pre><code>&gt;&gt;&gt; import itertools
&gt;&gt;&gt; list2d = [[1,2,3],[4,5,6], [7], [8,9]]
&gt;&gt;&gt; merged = list(itertools.chain(*list2d))

# python &gt;= 2.6
&gt;&gt;&gt; import itertools
&gt;&gt;&gt; list2d = [[1,2,3],[4,5,6], [7], [8,9]]
&gt;&gt;&gt; merged = list(itertools.chain.from_iterable(list2d))
</code></pre>
<p>sum</p>
<pre><code>sum(l, [])
</code></pre>
<h3 id="如何获取一个列表的长度">如何获取一个列表的长度</h3>
<p>问题 <a href="http://stackoverflow.com/questions/518021/getting-the-length-of-an-array-in-python">链接</a></p>
<p>python中是不是只有这种方法可以获取长度？语法很奇怪</p>
<pre><code>arr.__len__()
</code></pre>
<p>应该使用这种方式</p>
<pre><code>mylist = [1,2,3,4,5]
len(mylist)
</code></pre>
<p>这样做法，不需要对每个容器都定义一个.length()方法，你可以使用len()检查所有实现了__len__()方法的对象</p>
<h3 id="python中如何复制一个列表">Python中如何复制一个列表</h3>
<p>问题 <a href="http://stackoverflow.com/questions/2612802/how-to-clone-a-list-in-python">链接</a></p>
<p>可以用切片的方法</p>
<pre><code>new_list = old_list[:]
</code></pre>
<p>可以使用list()函数</p>
<pre><code>new_list = list(old_list)
</code></pre>
<p>可以使用copy.copy(),比list()稍慢，因为它首先去查询old_list的数据类型</p>
<pre><code>import copy
new_list = copy.copy(old_list)
</code></pre>
<p>如果列表中包含对象，可以使用copy.deepcopy(), 所有方法中最慢，但有时候无法避免</p>
<pre><code>import copy
new_list = copy.deepcopy(old_list)
</code></pre>
<p>例子：</p>
<pre><code>import copy

class Foo(object):
    def __init__(self, val):
         self.val = val

    def __repr__(self):
        return str(self.val)

foo = Foo(1)

a = ['foo', foo]
b = a[:]
c = list(a)
d = copy.copy(a)
e = copy.deepcopy(a)

# edit orignal list and instance
a.append('baz')
foo.val = 5

print &quot;original: %r\n slice: %r\n list(): %r\n copy: %r\n deepcopy: %r&quot; \
       % (a, b, c, d, e)
</code></pre>
<p>结果:</p>
<pre><code>original: ['foo', 5, 'baz']
slice: ['foo', 5]
list(): ['foo', 5]
copy: ['foo', 5]
deepcopy: ['foo', 1]
</code></pre>
<p>效率简单比较</p>
<pre><code>10.59 - copy.deepcopy(old_list)
10.16 - pure python Copy() method copying classes with deepcopy
1.488 - pure python Copy() method not copying classes (only dicts/lists/tuples)
0.325 - for item in old_list: new_list.append(item)
0.217 - [i for i in old_list] (a list comprehension)
0.186 - copy.copy(old_list)
0.075 - list(old_list)
0.053 - new_list = []; new_list.extend(old_list)
0.039 - old_list[:] (list slicing)
</code></pre>
<h3 id="列表的append和extend的区别">列表的append和extend的区别</h3>
<p>问题 <a href="http://stackoverflow.com/questions/252703/python-append-vs-extend">链接</a></p>
<pre><code>&gt;&gt;&gt; x = [1, 2]
&gt;&gt;&gt; x.append(3)
&gt;&gt;&gt; x
[1, 2, 3]
&gt;&gt;&gt; x.append([4,5])
&gt;&gt;&gt; x
[1, 2, 3, [4, 5]]
&gt;&gt;&gt;
&gt;&gt;&gt; x = [1, 2, 3]
&gt;&gt;&gt; x.extend([4, 5])
&gt;&gt;&gt; x
[1, 2, 3, 4, 5]
</code></pre>
<h3 id="如何随机地从列表中抽取变量">如何随机地从列表中抽取变量</h3>
<p>问题 <a href="http://stackoverflow.com/questions/306400/how-do-i-randomly-select-an-item-from-a-list-using-python">链接</a></p>
<pre><code>foo = ['a', 'b', 'c', 'd', 'e']
from random import choice
print choice(foo)
</code></pre>
<h3 id="如何利用下标从列表中删除一个元素">如何利用下标从列表中删除一个元素</h3>
<p>问题 <a href="http://stackoverflow.com/questions/627435/how-to-remove-an-element-from-a-list-by-index-in-python">链接</a></p>
<p>1.del</p>
<pre><code>In [9]: a = range(10)
In [10]: a
Out[10]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [11]: del a[-1]
In [12]: a
Out[12]: [0, 1, 2, 3, 4, 5, 6, 7, 8]
</code></pre>
<p>2.pop</p>
<pre><code>a = ['a', 'b', 'c', 'd']
a.pop(1)
# now a is ['a', 'c', 'd']

a = ['a', 'b', 'c', 'd']
a.pop()
# now a is ['a', 'b', 'c']
</code></pre>
<h3 id="获取列表的最后一个元素">获取列表的最后一个元素</h3>
<p>问题 <a href="http://stackoverflow.com/questions/930397/how-to-get-the-last-element-of-a-list">链接</a></p>
<p>囧</p>
<pre><code>result = l[-1]
result = l.pop()
</code></pre>
<h3 id="序列的切片操作">序列的切片操作</h3>
<p>问题 <a href="http://stackoverflow.com/questions/509211/the-python-slice-notation">链接</a></p>
<p>It&rsquo;s pretty simple really:
很简单:</p>
<pre><code>a[start:end] # start 到 end-1
a[start:]    # start 到 末尾
a[:end]      # 0 到 end-1
a[:]         # 整个列表的拷贝
</code></pre>
<p>还有一个step变量，控制步长,可在上面语法中使用</p>
<pre><code>a[start🔚step] # start through not past end, by step
</code></pre>
<p>注意，左闭右开</p>
<p>其他特点，开始或结束下标可能是负数，表示从序列末尾开始计算而非从头开始计算,所以</p>
<pre><code>a[-1]    # 最后一个元素
a[-2:]   # 最后两个元素
a[:-2]   # 除了最后两个元素
</code></pre>
<p>Python对程序员很友好，如果序列中存在的元素数量少于你要的，例如，你请求 a[:-2] 但是a只有一个元素，你会得到一个空列表，而不是一个错误.有时候你或许希望返回的是一个错误，所以你必须知道这点</p>
<h3 id="如何将一个列表切分成若干个长度相同的子序列">如何将一个列表切分成若干个长度相同的子序列</h3>
<p>问题 <a href="http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python">链接</a></p>
<p>想要得到这样的效果</p>
<pre><code>l = range(1, 1000)
print chunks(l, 10) -&gt; [ [ 1..10 ], [ 11..20 ], .., [ 991..999 ] ]
</code></pre>
<p>使用yield:</p>
<pre><code>def chunks(l, n):
    &quot;&quot;&quot; Yield successive n-sized chunks from l.
    &quot;&quot;&quot;
    for i in xrange(0, len(l), n):
        yield l[i:i+n]
list(chunks(range(10, 75), 10))
</code></pre>
<p>直接处理</p>
<pre><code>def chunks(l, n):
    return [l[i:i+n] for i in range(0, len(l), n)]
</code></pre>
<h3 id="使用列表解析创建一个字典">使用列表解析创建一个字典</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1747817/python-create-a-dictionary-with-list-comprehension">链接</a></p>
<p>python 2.6</p>
<pre><code>d = dict((key, value) for (key, value) in sequence)
</code></pre>
<p>python 2.7+ or 3, 使用 <a href="http://www.python.org/dev/peps/pep-0274/">字典解析语法</a></p>
<pre><code>d = {key: value for (key, value) in sequence}
</code></pre>
<h3 id="使用in还是has_key">使用&quot;in&quot;还是&quot;has_key()&quot;</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1323410/has-key-or-in">链接</a></p>
<pre><code>d = {'a': 1, 'b': 2}
'a' in d
True
or:

d = {'a': 1, 'b': 2}
d.has_key('a')
True
</code></pre>
<p>哪种更好</p>
<p>in更pythonic, 另外 has_key()在Python3.x中已经被移除</p>
<h3 id="字典默认值">字典默认值</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1602934/check-if-a-given-key-already-exists-in-a-dictionary">链接</a></p>
<p>和问题有点偏</p>
<pre><code>#获取时,如不存在，得到默认值
d.get(key, 0)
#设置时，若key不存在，设置默认值，已存在，返回已存在value
d.setdefault(key, []).append(new_element)
#初始即默认值
from collections import defaultdict
d = defaultdict(lambda: 0)
#or d = defaultdict(int)
</code></pre>
<h3 id="如何给字典添加一个值">如何给字典添加一个值</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1024847/add-to-a-dictionary-in-python">链接</a></p>
<pre><code>#### Making a dictionary ####
data = {}
# OR #
data = dict()

#### Initially adding values ####
data = {'a':1,'b':2,'c':3}
# OR #
data = dict(a=1, b=2, c=3)

#### Inserting/Updating value ####
data['a']=1  # updates if 'a' exists, else adds 'a'
# OR #
data.update({'a':1})
# OR #
data.update(dict(a=1))

#### Merging 2 dictionaries ####
data.update(data2)  # Where data2 is also a dict.
</code></pre>
<h3 id="如何将字段转换成一个object然后使用对象-属性的方式读取">如何将字段转换成一个object，然后使用对象-属性的方式读取</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1305532/convert-python-dict-to-object">链接</a></p>
<p>有dict</p>
<pre><code>&gt;&gt;&gt; d = {'a': 1, 'b': {'c': 2}, 'd': [&quot;hi&quot;, {'foo': &quot;bar&quot;}]}}
</code></pre>
<p>想用这种方式访问</p>
<pre><code>&gt;&gt;&gt; x = dict2obj(d)
&gt;&gt;&gt; x.a
1
&gt;&gt;&gt; x.b.c
2
&gt;&gt;&gt; x.d[1].foo
bar
</code></pre>
<p>使用namedtuple</p>
<pre><code>&gt;&gt;&gt; from collections import namedtuple
&gt;&gt;&gt; MyStruct = namedtuple('MyStruct', 'a b d')
&gt;&gt;&gt; s = MyStruct(a=1, b={'c': 2}, d=['hi'])
&gt;&gt;&gt; s
MyStruct(a=1, b={'c': 2}, d=['hi'])
&gt;&gt;&gt; s.a
1
&gt;&gt;&gt; s.b
{'c': 2}
&gt;&gt;&gt; s.c
&gt;&gt;&gt; s.d
['hi']
</code></pre>
<p>使用类</p>
<pre><code>class Struct:
    def __init__(self, **entries):
        self.__dict__.update(entries)

&gt;&gt;&gt; args = {'a': 1, 'b': 2}
&gt;&gt;&gt; s = Struct(**args)
&gt;&gt;&gt; s
&lt;__main__.Struct instance at 0x01D6A738&gt;
&gt;&gt;&gt; s.a
1
&gt;&gt;&gt; s.b
2
</code></pre>
<h2 id="字符串文件">字符串，文件</h2>
<h3 id="字符如何转为小写">字符如何转为小写</h3>
<p>问题 <a href="http://stackoverflow.com/questions/6797984/how-to-convert-string-to-lowercase-in-python">链接</a></p>
<pre><code>s = &quot;Kilometer&quot;
print(s.lower())
</code></pre>
<h3 id="如何创建不存在的目录结构">如何创建不存在的目录结构</h3>
<p>问题 <a href="http://stackoverflow.com/questions/273192/python-best-way-to-create-directory-if-it-doesnt-exist-for-file-write">链接</a></p>
<pre><code>if not os.path.exists(directory):
    os.makedirs(directory)
</code></pre>
<p>需要注意的是，当目录在exists和makedirs两个函数调用之间被创建时，makedirs将抛出OSError</p>
<h3 id="如何拷贝一个文件">如何拷贝一个文件</h3>
<p>问题 <a href="http://stackoverflow.com/questions/123198/how-do-i-copy-a-file-in-python">链接</a></p>
<p><a href="http://docs.python.org/2/library/shutil.html">shutil</a>模块</p>
<pre><code>copyfile(src, dst)
</code></pre>
<p>将src文件内容拷贝到dst，目标文件夹必须可写，否则将抛出IOError异常</p>
<p>如果目标文件已存在，将被覆盖</p>
<p>另外特殊文件，想字符文件，块设备文件，无法用这个方法进行拷贝</p>
<p>src/dst是字符串</p>
<h3 id="字符串转为floatint">字符串转为float/int</h3>
<pre><code>&gt;&gt;&gt; a = &quot;545.2222&quot;
&gt;&gt;&gt; float(a)
545.2222
&gt;&gt;&gt; int(a)
Traceback (most recent call last):
File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
ValueError: invalid literal for int() with base 10: '545.2222'
&gt;&gt;&gt; int(float(a))
545
&gt;&gt;&gt; int('544')
544
</code></pre>
<p>另一种，用 <a href="http://docs.python.org/2/library/ast.html#ast.literal_eval">ast</a>模块</p>
<pre><code>&gt;&gt;&gt; import ast
&gt;&gt;&gt; ast.literal_eval(&quot;545.2222&quot;)
545.2222
&gt;&gt;&gt; ast.literal_eval(&quot;31&quot;)
31
</code></pre>
<h3 id="如何反向输出一个字符串">如何反向输出一个字符串</h3>
<p>问题 <a href="http://stackoverflow.com/questions/931092/reverse-a-string-in-python">链接</a></p>
<p>做法</p>
<pre><code>&gt;&gt;&gt; 'hello world'[::-1]
'dlrow olleh'
</code></pre>
<h3 id="如何随机生成大写字母和数字组成的字符串">如何随机生成大写字母和数字组成的字符串</h3>
<pre><code>6U1S75
4Z4UKK
U911K4
</code></pre>
<p>解决</p>
<pre><code>import string, random
''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(N))
</code></pre>
<h3 id="逐行读文件去除换行符perl-chomp-line">逐行读文件去除换行符(perl chomp line)</h3>
<p>问题 <a href="http://stackoverflow.com/questions/275018/how-can-i-remove-chomp-a-newline-in-python">链接</a>
类似问题 <a href="http://stackoverflow.com/questions/761804/trimming-a-string-in-python">链接</a></p>
<p>读一个文件，如何获取每一行内容（不包括换行符）</p>
<p>比较pythonic的做法:</p>
<pre><code>&gt;&gt;&gt; text = &quot;line 1\nline 2\r\nline 3\nline 4&quot;
&gt;&gt;&gt; text.splitlines()
['line 1', 'line 2', 'line 3', 'line 4']
</code></pre>
<p>用rstrip,(rstrip/lstrip/strip)</p>
<pre><code>#去除了空白+换行
&gt;&gt;&gt; 'test string \n'.rstrip()
'test string'
#只去换行
&gt;&gt;&gt; 'test string \n'.rstrip('\n')
'test string '
#更通用的做法，系统相关
&gt;&gt;&gt; import os, sys
&gt;&gt;&gt; sys.platform
'linux2'
&gt;&gt;&gt; &quot;foo\r\n&quot;.rstrip(os.linesep)
'foo\r'
</code></pre>
<h3 id="python中字符串的contains">python中字符串的contains</h3>
<p>问题 <a href="http://stackoverflow.com/questions/3437059/does-python-have-a-string-contains-method">链接</a></p>
<p>python中字符串判断contains</p>
<p>使用in关键字</p>
<pre><code>if not &quot;blah&quot; in somestring: continue
if &quot;blah&quot; not in somestring: continue
</code></pre>
<p>使用字符串的find/index  (注意index查找失败抛异常)</p>
<pre><code>s = &quot;This be a string&quot;
if s.find(&quot;is&quot;) == -1:
    print &quot;No 'is' here!&quot;
else:
    print &quot;Found 'is' in the string.&quot;
</code></pre>
<h3 id="如何判断一个字符串是数字">如何判断一个字符串是数字</h3>
<p>问题 <a href="http://stackoverflow.com/questions/354038/how-do-i-check-if-a-string-is-a-number-in-python">链接</a></p>
<p>使用这种方法会不会十分丑陋和低效</p>
<pre><code>def is_number(s):
    try:
        float(s)
        return True
    except ValueError:
        return False
</code></pre>
<p>使用这种方法并不丑陋和低效</p>
<p>使用isdigit(缺点，对非整数无能为力)</p>
<pre><code>a = &quot;03523&quot;
a.isdigit()
</code></pre>
<h3 id="如何填充0到数字字符串中保证统一长度">如何填充0到数字字符串中保证统一长度</h3>
<p>问题 <a href="http://stackoverflow.com/questions/339007/python-nicest-way-to-pad-zeroes-to-string">链接</a></p>
<p>对于字符串</p>
<pre><code>&gt;&gt;&gt; n = '4'
&gt;&gt;&gt; print n.zfill(3)
&gt;&gt;&gt; '004'
</code></pre>
<p>对于数字,<a href="http://docs.python.org/2/library/string.html#formatexamples">相关文档</a></p>
<pre><code>&gt;&gt;&gt; n = 4
&gt;&gt;&gt; print '%03d' % n
&gt;&gt;&gt; 004
&gt;&gt;&gt; print &quot;{0:03d}&quot;.format(4)  # python &gt;= 2.6
&gt;&gt;&gt; 004
&gt;&gt;&gt; print(&quot;{0:03d}&quot;.format(4))  # python 3
&gt;&gt;&gt; 004
</code></pre>
<h2 id="控制结构条件循环">控制结构（条件、循环）</h2>
<h3 id="如何在循环中获取下标">如何在循环中获取下标</h3>
<p>问题 <a href="http://stackoverflow.com/questions/522563/accessing-the-index-in-python-for-loops">链接</a></p>
<p>使用enumerate</p>
<pre><code>for idx, val in enumerate(ints):
    print idx, val
</code></pre>
<h3 id="如何判断一个变量的类型">如何判断一个变量的类型</h3>
<p>问题 <a href="http://stackoverflow.com/questions/402504/how-to-determine-the-variable-type-in-python">链接</a></p>
<p>使用type</p>
<pre><code>&gt;&gt;&gt; i = 123
&gt;&gt;&gt; type(i)
&lt;type 'int'&gt;
&gt;&gt;&gt; type(i) is int
True
&gt;&gt;&gt; i = 123456789L
&gt;&gt;&gt; type(i)
&lt;type 'long'&gt;
&gt;&gt;&gt; type(i) is long
True
&gt;&gt;&gt; i = 123.456
&gt;&gt;&gt; type(i)
&lt;type 'float'&gt;
&gt;&gt;&gt; type(i) is float
True
</code></pre>
<p>另外一个相同的问题  <a href="http://stackoverflow.com/questions/2225038/python-determine-the-type-of-an-object">链接</a></p>
<pre><code>&gt;&gt;&gt; type( [] ) == list
True
&gt;&gt;&gt; type( {} ) == dict
True
&gt;&gt;&gt; type( &quot;&quot; ) == str
True
&gt;&gt;&gt; type( 0 ) == int
True

&gt;&gt;&gt; class Test1 ( object ):
    pass
&gt;&gt;&gt; class Test2 ( Test1 ):
    pass
&gt;&gt;&gt; a = Test1()
&gt;&gt;&gt; b = Test2()
&gt;&gt;&gt; type( a ) == Test1
True
&gt;&gt;&gt; type( b ) == Test2
True
&gt;&gt;&gt; type( b ) == Test1
False
&gt;&gt;&gt; isinstance( b, Test1 )
True
&gt;&gt;&gt; isinstance( b, Test2 )
True
&gt;&gt;&gt; isinstance( a, Test1 )
True
&gt;&gt;&gt; isinstance( a, Test2 )
False
&gt;&gt;&gt; isinstance( [], list )
True
&gt;&gt;&gt; isinstance( {}, dict )
True
</code></pre>
<h2 id="类">类</h2>
<h3 id="如何判断一个对象是否拥有某个属性">如何判断一个对象是否拥有某个属性</h3>
<p>问题 <a href="http://stackoverflow.com/questions/610883/how-to-know-if-an-object-has-an-attribute-in-python">链接</a></p>
<pre><code>if hasattr(a, 'property'):
    a.property
</code></pre>
<p>两种风格</p>
<p>EAFP(easier to ask for forgiveness than permission)</p>
<p>LBYL(look before you leap)</p>
<p>相关内容
<a href="http://web.archive.org/web/20070929122422/http://mail.python.org/pipermail/python-list/2003-May/205182.html">EAFP vs LBYL (was Re: A little disappointed so far)</a>
<a href="http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#eafp-vs-lbyl">EAFP vs. LBYL @Code Like a Pythonista: Idiomatic Python</a></p>
<pre><code>try:
    doStuff(a.property)
except AttributeError:
    otherStuff()
or

if hasattr(a, 'property'):
    doStuff(a.property)
else:
    otherStuff()
</code></pre>
<h3 id="python中的类变量环境变量">Python中的类变量(环境变量)</h3>
<p>问题 <a href="http://stackoverflow.com/questions/68645/static-class-variables-in-python">链接</a></p>
<p>在类中定义的变量，不在方法定义中，成为类变量或静态变量</p>
<pre><code>&gt;&gt;&gt; class MyClass:
...     i = 3
...
&gt;&gt;&gt; MyClass.i
3
</code></pre>
<p>i是类级别的变量，但这里要和实例级别的变量i区分开</p>
<pre><code>&gt;&gt;&gt; m = MyClass()
&gt;&gt;&gt; m.i = 4
&gt;&gt;&gt; MyClass.i, m.i
&gt;&gt;&gt; (3, 4)
</code></pre>
<p>这和C++/java完全不同，但和C#区别不大，C#不允许类实例获取静态变量</p>
<p>具体见 <a href="http://docs.python.org/2/tutorial/classes.html#SECTION0011320000000000000000">what the Python tutorial has to say on the subject of classes and class objects</a></p>
<p>另外，静态方法</p>
<pre><code>class C:
    @staticmethod
    def f(arg1, arg2, ...): ...
</code></pre>
<h3 id="如何定义静态方法static-method">如何定义静态方法(static method)</h3>
<p>问题 <a href="http://stackoverflow.com/questions/735975/static-methods-in-python">链接</a></p>
<p>使用 <a href="http://docs.python.org/2/library/functions.html#staticmethod">staticmethod</a>装饰器</p>
<pre><code>class MyClass(object):
    @staticmethod
    def the_static_method(x):
        print x
MyClass.the_static_method(2) # outputs 2
</code></pre>
<h3 id="staticmethod和classmethod的区别">@staticmethod和@classmethod的区别</h3>
<p>问题 <a href="http://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python">链接</a></p>
<p>staticmethod，静态方法在调用时，对类及实例一无所知</p>
<p>仅仅是获取传递过来的参数，没有隐含的第一个参数，在Python里基本上用处不大，你完全可以用一个模块函数替换它</p>
<p>classmethod, 在调用时，将会获取到其所在的类，或者类实例，作为其第一个参数</p>
<p>当你想将函数作为一个类工厂时，这非常有用: 第一个参数是类，你可以实例化出对应实例对象，甚至子类对象。</p>
<p>可以观察下 dict.fromkey(),是一个类方法，当子类调用时，返回子类的实例</p>
<pre><code>&gt;&gt;&gt; class DictSubclass(dict):
...     def __repr__(self):
...         return &quot;DictSubclass&quot;
...
&gt;&gt;&gt; dict.fromkeys(&quot;abc&quot;)
{'a': None, 'c': None, 'b': None}
&gt;&gt;&gt; DictSubclass.fromkeys(&quot;abc&quot;)
DictSubclass
&gt;&gt;&gt;
</code></pre>
<h3 id="如何获取一个实例的类名">如何获取一个实例的类名</h3>
<p>问题 <a href="http://stackoverflow.com/questions/510972/getting-the-class-name-of-an-instance-in-python">链接</a></p>
<pre><code>x.__class__.__name__
</code></pre>
<h2 id="模块">模块</h2>
<h3 id="如何列出一个目录的所有文件">如何列出一个目录的所有文件</h3>
<p>问题 <a href="http://stackoverflow.com/questions/3207219/how-to-list-all-files-of-a-directory-in-python">链接</a></p>
<p>1.使用os.listdir(),得到目录下的所有文件和文件夹</p>
<pre><code>#只需要文件
from os import listdir
from os.path import isfile, join
onlyfiles = [ f for f in listdir(mypath) if isfile(join(mypath,f)) ]
</code></pre>
<p>2.os.walk()</p>
<pre><code>from os import walk

f = []
for (dirpath, dirnames, filenames) in walk(mypath):
    f.extend(filenames)
    break
</code></pre>
<p>3.glob</p>
<pre><code>import glob
print glob.glob(&quot;/home/adam/*.txt&quot;)
</code></pre>
<p>重复问题 <a href="http://stackoverflow.com/questions/120656/directory-listing-in-python">链接</a></p>
<p>import os</p>
<pre><code>for dirname, dirnames, filenames in os.walk('.'):
    # print path to all subdirectories first.
    for subdirname in dirnames:
        print os.path.join(dirname, subdirname)

    # print path to all filenames.
    for filename in filenames:
        print os.path.join(dirname, filename)
</code></pre>
<h3 id="json和simplejson的区别">json和simplejson的区别</h3>
<p>问题 <a href="http://stackoverflow.com/questions/712791/json-and-simplejson-module-differences-in-python">链接</a></p>
<p>json就是simple，加入到标准库. json在2.6加入，simplejson在2.4+,2.6+,更有优势</p>
<p>另外，simplejson更新频率更高，如果你想使用最新版本，建议用simplejson</p>
<p>好的做法是</p>
<pre><code>try: import simplejson as json
except ImportError: import json
</code></pre>
<p>另外,可以关注二者性能上的比较</p>
<h3 id="python中如何获取当前时间">python中如何获取当前时间</h3>
<p>问题 <a href="http://stackoverflow.com/questions/415511/how-to-get-current-time-in-python">链接</a></p>
<p>时间日期</p>
<pre><code>&gt;&gt;&gt; import datetime
&gt;&gt;&gt; datetime.datetime.now()
datetime(2009, 1, 6, 15, 8, 24, 78915)
</code></pre>
<p>如果仅获取时间</p>
<pre><code>&gt;&gt;&gt; datetime.datetime.time(datetime.datetime.now())
datetime.time(15, 8, 24, 78915))
#等价
&gt;&gt;&gt; datetime.datetime.now().time()
</code></pre>
<p>可以从文档中获取更多 <a href="http://docs.python.org/2/library/datetime.html">文档</a></p>
<p>如果想避免额外的datetime.</p>
<pre><code>&gt;&gt;&gt; from datetime import datetime
</code></pre>
<h2 id="其他">其他</h2>
<h3 id="如何从标准输入读取内容stdin">如何从标准输入读取内容stdin</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1450393/how-do-you-read-from-stdin-in-python">链接</a></p>
<p>使用<a href="http://docs.python.org/2/library/fileinput.html">fileinput</a></p>
<pre><code>import fileinput
for line in fileinput.input():
    pass
</code></pre>
<h3 id="foo-is-none-和-foo--none的区别">foo is None 和 foo == None的区别</h3>
<p>问题 <a href="http://stackoverflow.com/questions/26595/is-there-any-difference-between-foo-is-none-and-foo-none">链接</a></p>
<pre><code>if foo is None: pass
if foo == None: pass
</code></pre>
<p>如果比较相同的对象实例，is总是返回True
而 == 最终取决于 &ldquo;<strong>eq</strong>()&rdquo;</p>
<pre><code>&gt;&gt;&gt; class foo(object):
    def __eq__(self, other):
        return True

&gt;&gt;&gt; f = foo()
&gt;&gt;&gt; f == None
True
&gt;&gt;&gt; f is None
False

&gt;&gt;&gt; list1 = [1, 2, 3]
&gt;&gt;&gt; list2 = [1, 2, 3]]
&gt;&gt;&gt; list1==list2
True
&gt;&gt;&gt; list1 is list2
False
</code></pre>
<p>另外</p>
<pre><code>(ob1 is ob2) 等价于 (id(ob1) == id(ob2))
</code></pre>
<h3 id="__init__py是做什么用的">__init__.py是做什么用的</h3>
<p>问题 <a href="http://stackoverflow.com/questions/448271/what-is-init-py-for">链接</a></p>
<p>这是包的一部分，<a href="http://docs.python.org/2/tutorial/modules.html#packages">具体文档</a></p>
<p>__init__.py让Python把目录当成包，</p>
<p>最简单的例子，__init__.py仅是一个空文件，但它可以一样执行包初始化代码或者设置__all__变量，后续说明</p>
<h3 id="如何获取安装的python模块列表">如何获取安装的python模块列表</h3>
<p>问题 <a href="http://stackoverflow.com/questions/739993/get-a-list-of-installed-python-modules">链接</a></p>
<pre><code>&gt;&gt;&gt; help('modules')
</code></pre>
<h2 id="环境相关">环境相关</h2>
<h3 id="setuppy安装后如何卸载">setup.py安装后如何卸载</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1550226/python-setup-py-uninstall">链接</a></p>
<p>使用下面命令安装的包如何卸载</p>
<pre><code>python setup.py install
</code></pre>
<p>手工删除的话</p>
<pre><code>python setup.py install --record files.txt
cat files.txt | xargs rm -rf
</code></pre>
<h2 id="异常">异常</h2>
<h3 id="如何一行内处理多个异常">如何一行内处理多个异常</h3>
<p>问题 <a href="http://stackoverflow.com/questions/6470428/catch-multiple-exceptions-in-one-line-except-block">链接</a></p>
<p>我知道可以这么做</p>
<pre><code>try:
    # do something that may fail
except:
    # do this if ANYTHING goes wrong
</code></pre>
<p>也可以</p>
<pre><code>try:
    # do something that may fail
except IDontLikeYourFaceException:
    # put on makeup or smile
except YouAreTooShortException:
    # stand on a ladder
</code></pre>
<p>如果想在一行里处理多个异常的话</p>
<pre><code>try:
    # do something that may fail
except IDontLIkeYouException, YouAreBeingMeanException: #没生效
except Exception, e: #捕获了所有
    # say please
</code></pre>
<p>答案</p>
<pre><code># as在python2.6,python2.7中仍然可以使用
except (IDontLIkeYouException, YouAreBeingMeanException) as e:
    pass
</code></pre>
<h3 id="如何flush-python的print输出">如何flush Python的print输出</h3>
<p>问题 <a href="http://stackoverflow.com/questions/230751/how-to-flush-output-of-python-print">链接</a>
重复问题 <a href="http://stackoverflow.com/questions/107705/python-output-buffering">链接</a></p>
<p>默认print输出到sys.stdout</p>
<pre><code>import sys
sys.stdout.flush()
</code></pre>
<p>参考
<a href="http://docs.python.org/2/reference/simple_stmts.html#the-print-statement">http://docs.python.org/reference/simple_stmts.html#the-print-statement</a>
<a href="http://docs.python.org/2/library/sys.html">http://docs.python.org/library/sys.html</a>
<a href="http://docs.python.org/2/library/stdtypes.html#file-objects">http://docs.python.org/library/stdtypes.html#file-objects</a></p>
<h3 id="如何获取一个函数的函数名字符串">如何获取一个函数的函数名字符串</h3>
<p>问题 <a href="http://stackoverflow.com/questions/251464/how-to-get-the-function-name-as-string-in-python">链接</a></p>
<pre><code>my_function.__name__
&gt;&gt;&gt; import time
&gt;&gt;&gt; time.time.__name__
'time'
</code></pre>
<h3 id="应该在学习python3之前学习python2还是直接学习python3">应该在学习Python3之前学习Python2，还是直接学习Python3</h3>
<p>问题 <a href="http://stackoverflow.com/questions/170921/should-i-learn-python-2-before-3-or-start-directly-from-python-3">链接</a></p>
<p>你可以从python2开始，2和3主要的语法格式和风格相同</p>
<p>3要替代2不是短时间内能完成的，将会是一个很长的过程，所以学习Python2并没有什么坏处</p>
<p>我建议你关注下2和3的不同之处  <a href="http://stackoverflow.com/questions/170921/should-i-learn-python-2-before-3-or-start-directly-from-python-3">This slides gives you a quick introduction of the changes in Python 2 and 3</a></p>
<h3 id="python中用比较字符串is有时候会返回错误判断">python中用==比较字符串，is有时候会返回错误判断</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1504717/python-vs-is-comparing-strings-is-fails-sometimes-why">链接</a></p>
<p>is是身份测试，==是相等测试</p>
<pre><code>&gt;&gt;&gt; a = 'pub'
&gt;&gt;&gt; b = ''.join(['p', 'u', 'b'])
&gt;&gt;&gt; a == b
True
&gt;&gt;&gt; a is b
False'
</code></pre>
<p>is 等价于 id(a) == id(b)</p>
<h3 id="如何截取一个字符串获得子串">如何截取一个字符串获得子串</h3>
<p>问题 <a href="http://stackoverflow.com/questions/663171/is-there-a-way-to-substring-a-string-in-python">链接</a></p>
<pre><code>&gt;&gt;&gt; x = &quot;Hello World!&quot;
&gt;&gt;&gt; x[2:]
'llo World!'
&gt;&gt;&gt; x[:2]
'He'
&gt;&gt;&gt; x[:-2]
'Hello Worl'
&gt;&gt;&gt; x[-2:]
'd!'
&gt;&gt;&gt; x[2:-2]
'llo Worl'
</code></pre>
<p>python将这类操作称为切片，可以作用于序列类型，不仅仅是字符串</p>
<h3 id="用函数名字符串调用一个函数">用函数名字符串调用一个函数</h3>
<p>问题 <a href="http://stackoverflow.com/questions/3061/calling-a-function-from-a-string-with-the-functions-name-in-python">链接</a></p>
<p>假设模块foo有函数bar:</p>
<pre><code>import foo
methodToCall = getattr(foo, 'bar')
result = methodToCall()
</code></pre>
<p>或者一行搞定</p>
<pre><code>result = getattr(foo, 'bar')()
</code></pre>
<h3 id="如何获取文件扩展名">如何获取文件扩展名</h3>
<p>问题 <a href="http://stackoverflow.com/questions/541390/extracting-extension-from-filename-in-python">链接</a></p>
<p>使用os.path.splitext方法：</p>
<pre><code>&gt;&gt;&gt; import os
&gt;&gt;&gt; fileName, fileExtension = os.path.splitext('/path/to/somefile.ext')
&gt;&gt;&gt; fileName
'/path/to/somefile'
&gt;&gt;&gt; fileExtension
'.ext'
</code></pre>
<h3 id="如何获取list中包含某个元素所在的下标">如何获取list中包含某个元素所在的下标</h3>
<p>问题 <a href="http://stackoverflow.com/questions/176918/in-python-how-do-i-find-the-index-of-an-item-given-a-list-containing-it">链接</a></p>
<pre><code>&gt;&gt;&gt; [&quot;foo&quot;,&quot;bar&quot;,&quot;baz&quot;].index('bar')
1
</code></pre>
<p>参照 <a href="http://docs.python.org/2/tutorial/datastructures.html#more-on-lists">文档</a></p>
<h3 id="如何截掉空格包括tab">如何截掉空格（包括tab)</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1185524/how-to-trim-whitespace-including-tabs">链接</a></p>
<p>空白在字符串左右两边</p>
<pre><code>s = &quot;  \t a string example\t  &quot;
s = s.strip()
</code></pre>
<p>空白在字符串右边</p>
<pre><code>s = s.rstrip()
</code></pre>
<p>左边</p>
<pre><code>s = s.lstrip()
</code></pre>
<p>另外你可以指定要截掉的字符作为参数</p>
<pre><code>s = s.strip(' \t\n\r')
</code></pre>
<h3 id="如何将一个十六进制字符串转为整数">如何将一个十六进制字符串转为整数</h3>
<p>问题 <a href="http://stackoverflow.com/questions/209513/convert-hex-string-to-int-in-python">链接</a></p>
<pre><code>&gt;&gt;&gt; int(&quot;a&quot;, 16)
10
&gt;&gt;&gt; int(&quot;0xa&quot;,16)
10
</code></pre>
<h3 id="如何结束退出一个python脚本">如何结束退出一个python脚本</h3>
<p>问题 <a href="http://stackoverflow.com/questions/73663/terminating-a-python-script">链接</a></p>
<pre><code>import sys
sys.exit()
</code></pre>
<p>详细 <a href="http://docs.python.org/2/library/sys.html">文档</a></p>
<h3 id="如何往文件中追加文本">如何往文件中追加文本</h3>
<p>问题 <a href="http://stackoverflow.com/questions/4706499/how-do-you-append-to-file-in-python">链接</a></p>
<pre><code>with open(&quot;test.txt&quot;, &quot;a&quot;) as myfile:
    myfile.write(&quot;appended text&quot;)
</code></pre>
<p>可以使用&rsquo;a&rsquo;或&rsquo;a+b&rsquo; mode打开文件，见 <a href="http://docs.python.org/2/library/functions.html#open">文档</a></p>
<h3 id="如何使用不同分隔符切分字符串">如何使用不同分隔符切分字符串</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1059559/python-strings-split-with-multiple-separators">链接</a></p>
<p>使用re.split  <a href="http://docs.python.org/2/library/re.html#re.split">文档</a></p>
<pre><code>&gt;&gt;&gt; re.split('\W+', 'Words, words, words.')
['Words', 'words', 'words', '']
&gt;&gt;&gt; re.split('(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
&gt;&gt;&gt; re.split('\W+', 'Words, words, words.', 1)
['Words', 'words, words.'])
</code></pre>
<p>或者匹配获取正确的 re.findall</p>
<pre><code>import re
DATA = &quot;Hey, you - what are you doing here!?&quot;
print re.findall(r&quot;[\w']+&quot;, DATA)
# Prints ['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']
</code></pre>
<h3 id="如何获取一个字符的ascii码">如何获取一个字符的ASCII码</h3>
<p>问题 <a href="http://stackoverflow.com/questions/227459/ascii-value-of-a-character-in-python">链接</a></p>
<pre><code>&gt;&gt;&gt; ord('a')
97
&gt;&gt;&gt; chr(97)
'a'
&gt;&gt;&gt; chr(ord('a') + 3)
'd'
&gt;&gt;&gt;
</code></pre>
<p>另外对于unicode</p>
<pre><code>&gt;&gt;&gt; unichr(97)
u'a'
&gt;&gt;&gt; unichr(1234)
u'\u04d2'
</code></pre>
<h3 id="排序一个列表中的所有dict根据dict内值">排序一个列表中的所有dict，根据dict内值</h3>
<p>问题 <a href="http://stackoverflow.com/questions/72899/in-python-how-do-i-sort-a-list-of-dictionaries-by-values-of-the-dictionary">链接</a></p>
<p>如何排序如下列表，根据name或age</p>
<pre><code>[{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]
</code></pre>
<p>简单的做法；</p>
<pre><code>newlist = sorted(list_to_be_sorted, key=lambda k: k['name'])
</code></pre>
<p>高效的做法</p>
<pre><code>from operator import itemgetter
newlist = sorted(list_to_be_sorted, key=itemgetter('name'))
</code></pre>
<h3 id="读文件到列表中">读文件到列表中</h3>
<p>问题 <a href="http://stackoverflow.com/questions/3277503/python-read-file-line-by-line-into-array">链接</a></p>
<pre><code>f = open('filename')
lines = f.readlines()
f.close()
等价
with open(fname) as f:
    content = f.readlines()
</code></pre>
<p><a href="http://docs.python.org/2/tutorial/inputoutput.html#reading-and-writing-files">文档</a></p>
<h3 id="如何用http下载一个文件">如何用http下载一个文件</h3>
<p>问题 <a href="http://stackoverflow.com/questions/22676/how-do-i-download-a-file-over-http-using-python">链接</a></p>
<p>直接使用urllib</p>
<pre><code>import urllib
urllib.urlretrieve (&quot;http://www.example.com/songs/mp3.mp3&quot;, &quot;mp3.mp3&quot;)
</code></pre>
<p>使用urllib2,并提供一个进度条</p>
<pre><code>import urllib2

url = &quot;http://download.thinkbroadband.com/10MB.zip&quot;

file_name = url.split('/')[-1]
u = urllib2.urlopen(url)
f = open(file_name, 'wb')
meta = u.info()
file_size = int(meta.getheaders(&quot;Content-Length&quot;)[0])
print &quot;Downloading: %s Bytes: %s&quot; % (file_name, file_size)

file_size_dl = 0
block_sz = 8192
while True:
    buffer = u.read(block_sz)
    if not buffer:
        break

    file_size_dl += len(buffer)
    f.write(buffer)
    status = r&quot;%10d  [%3.2f%%]&quot; % (file_size_dl, file_size_dl * 100. / file_size)
    status = status + chr(8)*(len(status)+1)
    print status,

f.close()
</code></pre>
<p>使用第三方<a href="http://docs.python-requests.org/en/latest/index.html">requests</a>包</p>
<pre><code>&gt;&gt;&gt; import requests
&gt;&gt;&gt;
&gt;&gt;&gt; url = &quot;http://download.thinkbroadband.com/10MB.zip&quot;
&gt;&gt;&gt; r = requests.get(url)
&gt;&gt;&gt; print len(r.content)
10485760
</code></pre>
<h3 id="在virtualenv中如何使用不同的python版本">在virtualenv中如何使用不同的python版本</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1534210/use-different-python-version-with-virtualenv">链接</a></p>
<p>在创建virtualenv实例时，使用-p选项</p>
<pre><code>virtualenv -p /usr/bin/python2.6 &lt;path/to/new/virtualenv/&gt;
</code></pre>
<h3 id="python中如何将一行长代码切成多行">python中如何将一行长代码切成多行</h3>
<p>问题 <a href="http://stackoverflow.com/questions/53162/how-can-i-do-a-line-break-line-continuation-in-python">链接</a></p>
<p>例如：</p>
<pre><code>e = 'a' + 'b' + 'c' + 'd'
变成
e = 'a' + 'b' +
    'c' + 'd'
</code></pre>
<p>括号中，可以直接换行</p>
<pre><code>a = dostuff(blahblah1, blahblah2, blahblah3, blahblah4, blahblah5,
            blahblah6, blahblah7)
</code></pre>
<p>非括号你可以这么做</p>
<pre><code>a = '1' + '2' + '3' + \
    '4' + '5'
或者
a = ('1' + '2' + '3' +
    '4' + '5')
</code></pre>
<p>可以查看下代码风格： <a href="http://www.python.org/dev/peps/pep-0008/">style guide</a>
推荐是后一种，但某些个别情况下，加入括号会导致错误</p>
<h3 id="如何找到一个目录下所有txt文件">如何找到一个目录下所有.txt文件</h3>
<p>问题 <a href="http://stackoverflow.com/questions/3964681/find-all-files-in-directory-with-extension-txt-with-python">链接</a></p>
<p>使用<a href="http://docs.python.org/2/library/glob.html">glob</a></p>
<pre><code>import glob
import os
os.chdir(&quot;/mydir&quot;)
for files in glob.glob(&quot;*.txt&quot;):
    print files
</code></pre>
<p>使用os.listdir</p>
<pre><code>import os
os.chdir(&quot;/mydir&quot;)
for files in os.listdir(&quot;.&quot;):
    if files.endswith(&quot;.txt&quot;):
        print files
</code></pre>
<p>或者遍历目录</p>
<pre><code>import os
for r,d,f in os.walk(&quot;/mydir&quot;):
    for files in f:
        if files.endswith(&quot;.txt&quot;):
            print os.path.join(r,files)
</code></pre>
<h3 id="如何使用绝对路径import一个模块">如何使用绝对路径import一个模块</h3>
<p>问题 <a href="http://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path">链接</a></p>
<pre><code>import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()
</code></pre>
<h3 id="如何在遍历一个list时删除某些玄素">如何在遍历一个list时删除某些玄素</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1207406/remove-items-from-a-list-while-iterating-in-python">链接</a></p>
<p>使用列表解析</p>
<pre><code>somelist = [x for x in somelist if determine(x)]
</code></pre>
<p>上面那个操作将产生一个全新的somelist对象，而失去了对原有somelist对象的引用</p>
<pre><code>#在原有对象上进行修改
somelist[:] = [x for x in somelist if determine(x)]
</code></pre>
<p>使用itertools</p>
<pre><code>from itertools import ifilterfalse
somelist[:] = list(ifilterfalse(determine, somelist))
</code></pre>
<h3 id="如何强制使用浮点数除法">如何强制使用浮点数除法</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1267869/how-can-i-force-division-to-be-floating-point-in-python">链接</a></p>
<p>如何强制使除法结果c是浮点数</p>
<pre><code>c = a / b
</code></pre>
<p>可以使用__future__</p>
<pre><code>&gt;&gt;&gt; from __future__ import division
&gt;&gt;&gt; a = 4
&gt;&gt;&gt; b = 6
&gt;&gt;&gt; c = a / b
&gt;&gt;&gt; c
0.66666666666666663
</code></pre>
<p>或者转换,如果除数或被除数是浮点数，那么结果也是浮点数</p>
<pre><code>c = a / float(b)
</code></pre>
<h3 id="如何映射两个列表成为一个字典">如何映射两个列表成为一个字典</h3>
<p>问题 <a href="http://stackoverflow.com/questions/209840/map-two-lists-into-a-dictionary-in-python">链接</a></p>
<p>两个列表</p>
<pre><code>keys = ('name', 'age', 'food')
values = ('Monty', 42, 'spam')
</code></pre>
<p>如何得到</p>
<pre><code>dict = {'name' : 'Monty', 'age' : 42, 'food' : 'spam'}
</code></pre>
<p>使用zip</p>
<pre><code>&gt;&gt;&gt; keys = ['a', 'b', 'c']
&gt;&gt;&gt; values = [1, 2, 3]
&gt;&gt;&gt; dictionary = dict(zip(keys, values))
&gt;&gt;&gt; print dictionary
{'a': 1, 'b': 2, 'c': 3}
</code></pre>
<h3 id="找到当前目录及文件所在目录">找到当前目录及文件所在目录</h3>
<p>问题 <a href="http://stackoverflow.com/questions/5137497/find-current-directory-and-files-directory">链接</a></p>
<p>查找当前目录使用os.getcwd()</p>
<p>查找某个文件的目录，使用, <a href="http://docs.python.org/2/library/os.path.html">os.path</a></p>
<pre><code>import os.path
os.path.realpath(__file__)
</code></pre>
<h3 id="为何1-in-10--true执行结果是false">为何1 in [1,0] == True执行结果是False</h3>
<p>问题 <a href="http://stackoverflow.com/questions/9284350/why-does-1-in-1-0-true-evaluate-to-false">链接</a></p>
<p>有如下</p>
<pre><code>&gt;&gt;&gt; 1 in [1,0]             # This is expected
True
&gt;&gt;&gt; 1 in [1,0] == True     # This is strange
False
&gt;&gt;&gt; (1 in [1,0]) == True   # This is what I wanted it to be
True
&gt;&gt;&gt; 1 in ([1,0] == True)   # But it's not just a precedence issue!
                           # It did not raise an exception on the second example.

Traceback (most recent call last):
  File &quot;&lt;pyshell#4&gt;&quot;, line 1, in &lt;module&gt;
      1 in ([1,0] == True)
      TypeError: argument of type 'bool' is not iterable
</code></pre>
<p>这里python使用了比较运算符链</p>
<pre><code>1 in [1,0] == True
</code></pre>
<p>将被转为</p>
<pre><code>(1 in [1, 0]) and ([1, 0] == True)
</code></pre>
<p>很显然是false的</p>
<p>同样的</p>
<pre><code>a &lt; b &lt; c
</code></pre>
<p>会被转为</p>
<pre><code>(a &lt; b) and (b &lt; c) # b不会被解析两次
</code></pre>
<p><a href="http://docs.python.org/2/reference/expressions.html#not-in">具体文档</a></p>
<h3 id="python中的switch替代语法">Python中的switch替代语法</h3>
<p>问题 <a href="http://stackoverflow.com/questions/60208/replacements-for-switch-statement-in-python">链接</a></p>
<p>python中没有switch，有什么推荐的处理方法么</p>
<p>使用字典:</p>
<pre><code>def f(x):
    return {
        'a': 1,
        'b': 2,
    }.get(x, 9)
</code></pre>
<p>Python Cookbook中的几种方式</p>
<p><a href="http://code.activestate.com/recipes/410692/">Readable switch construction without lambdas or dictionaries</a></p>
<p><a href="http://code.activestate.com/recipes/410695/">Exception-based Switch-Case</a></p>
<p><a href="http://code.activestate.com/recipes/181064/">Using a Dictionary in place of a &lsquo;switch&rsquo; statement</a></p>
<h3 id="如何将字符串转换为datetime">如何将字符串转换为datetime</h3>
<p>问题 <a href="http://stackoverflow.com/questions/466345/converting-string-into-datetime">链接</a></p>
<p>可以查看下time模块的<a href="http://docs.python.org/2/library/time.html#time.strptime">strptime</a>方法，反向操作是<a href="http://docs.python.org/2/library/time.html#time.strftime">strftime</a></p>
<pre><code>from datetime import datetime
date_object = datetime.strptime('Jun 1 2005  1:33PM', '%b %d %Y %I:%M%p')
</code></pre>
<p><a href="http://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior">扩展文档</a></p>
<h3 id="python中有没有简单优雅的方式定义单例类">Python中有没有简单优雅的方式定义单例类</h3>
<p>问题 <a href="http://stackoverflow.com/questions/31875/is-there-a-simple-elegant-way-to-define-singletons-in-python">链接</a></p>
<p>我不认为有必要，一个拥有函数的模块（不是类）可以作为很好的单例使用，它的所有变量被绑定到这个模块，无论如何都不能被重复实例化</p>
<p>如果你确实想用一个类来实现，在python中不能创建私有类或私有构造函数,所以你不能隔离多个实例而仅仅通过自己的API来访问属性</p>
<p>我还是认为将函数放入模块，并将其作为一个单例来使用是最好的办法</p>
<h3 id="将一个字符串转为一个字典">将一个字符串转为一个字典</h3>
<p>问题 <a href="http://stackoverflow.com/questions/988228/converting-a-string-to-dictionary">链接</a></p>
<p>如何将字符串转成字典，不适用eval</p>
<pre><code>s = &quot;{'muffin' : 'lolz', 'foo' : 'kitty'}&quot;
</code></pre>
<p>从python2.6开始，你可以使用内建模块 ast.literal_eval</p>
<pre><code>&gt;&gt;&gt; import ast
&gt;&gt;&gt; ast.literal_eval(&quot;{'muffin' : 'lolz', 'foo' : 'kitty'}&quot;)
{'muffin': 'lolz', 'foo': 'kitty'}
</code></pre>
<p>这个做法比直接eval更安全
帮助文档</p>
<pre><code>&gt;&gt;&gt; help(ast.literal_eval)
Help on function literal_eval in module ast:

literal_eval(node_or_string)
    Safely evaluate an expression node or a string containing a Python
    expression.  The string or node provided may only consist of the following
    Python literal structures: strings, numbers, tuples, lists, dicts, booleans,
    and None.
</code></pre>
<p>举例</p>
<pre><code>&gt;&gt;&gt; eval(&quot;shutil.rmtree('mongo')&quot;)
Traceback (most recent call last):
File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
File &quot;&lt;string&gt;&quot;, line 1, in &lt;module&gt;
File &quot;/opt/Python-2.6.1/lib/python2.6/shutil.py&quot;, line 208, in rmtree
    onerror(os.listdir, path, sys.exc_info())
File &quot;/opt/Python-2.6.1/lib/python2.6/shutil.py&quot;, line 206, in rmtree
    names = os.listdir(path)
OSError: [Errno 2] No such file or directory: 'mongo'
&gt;&gt;&gt; ast.literal_eval(&quot;shutil.rmtree('mongo')&quot;)
Traceback (most recent call last):
File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
File &quot;/opt/Python-2.6.1/lib/python2.6/ast.py&quot;, line 68, in literal_eval
    return _convert(node_or_string)
File &quot;/opt/Python-2.6.1/lib/python2.6/ast.py&quot;, line 67, in _convert
    raise ValueError('malformed string')
ValueError: malformed string
</code></pre>
<h3 id="python如何检查一个对象是list或者tuple但是不是一个字符串">Python如何检查一个对象是list或者tuple，但是不是一个字符串</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1835018/python-check-if-an-object-is-a-list-or-tuple-but-not-string">链接</a></p>
<p>原来的做法是</p>
<pre><code>assert isinstance(lst, (list, tuple))
</code></pre>
<p>有没有更好的做法</p>
<p>我认为下面的方式是你需要的</p>
<pre><code>assert not isinstance(lst, basestring)
</code></pre>
<p>原来的方式，你可能会漏过很多像列表，但并非list/tuple的</p>
<h3 id="使用-if-x-is-not-none-还是if-not-x-is-none">使用 &lsquo;if x is not None&rsquo; 还是&rsquo;if not x is None&rsquo;</h3>
<p>问题 <a href="http://stackoverflow.com/questions/2710940/python-if-x-is-not-none-or-if-not-x-is-none">链接</a></p>
<p>我总想着使用 &lsquo;if x is not None&rsquo; 会更加简明</p>
<p>但是google的Python风格指南使用的却是 &lsquo;if x is not None&rsquo;</p>
<p>性能上没有什么区别，他们编译成相同的字节码</p>
<pre><code>Python 2.6.2 (r262:71600, Apr 15 2009, 07:20:39)
&gt;&gt;&gt; import dis
&gt;&gt;&gt; def f(x):
...    return x is not None
...
&gt;&gt;&gt; dis.dis(f)
2           0 LOAD_FAST                0 (x)
            3 LOAD_CONST               0 (None)
            6 COMPARE_OP               9 (is not)
            9 RETURN_VALUE
&gt;&gt;&gt; def g(x):
...   return not x is None
...
&gt;&gt;&gt; dis.dis(g)
2           0 LOAD_FAST                0 (x)
            3 LOAD_CONST               0 (None)
            6 COMPARE_OP               9 (is not)
            9 RETURN_VALUE
</code></pre>
<p>在风格上，我尽量避免 &rsquo;not x is y&rsquo; 这种形式，虽然编译器会认为和 &rsquo;not (x is y)&lsquo;一样，但是读代码的人或许会误解为 &lsquo;(not x) is y&rsquo;</p>
<p>如果写作 &lsquo;x is not y&rsquo; 就不会有歧义</p>
<p>最佳实践</p>
<pre><code>if x is not None:
    # Do something about x
</code></pre>
<h3 id="如何获取一个文件的创建和修改时间">如何获取一个文件的创建和修改时间</h3>
<p>问题 <a href="http://stackoverflow.com/questions/237079/how-to-get-file-creation-modification-date-times-in-python">链接</a></p>
<p>跨平台的获取文件创建及修改时间的方法</p>
<p>你有很多选择</p>
<p>使用<a href="http://docs.python.org/release/2.5.2/lib/module-os.path.html#l2h-2177">os.path.getmtime</a>或者<a href="http://docs.python.org/release/2.5.2/lib/module-os.path.html#l2h-2178">os.path.getctime</a></p>
<pre><code>import os.path, time
print &quot;last modified: %s&quot; % time.ctime(os.path.getmtime(file))
print &quot;created: %s&quot; % time.ctime(os.path.getctime(file))
</code></pre>
<p>或者<a href="http://www.python.org/doc/2.5.2/lib/module-stat.html">os.stat</a></p>
<pre><code>import os, time
(mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(file)
print &quot;last modified: %s&quot; % time.ctime(mtime)
</code></pre>
<p>注意，ctime()并非指*nix系统中文件创建时间，而是这个节点数据的最后修改时间</p>
<h3 id="如何离开virtualenv">如何离开virtualenv</h3>
<p>问题 <a href="http://stackoverflow.com/questions/990754/how-to-leave-a-python-virtualenv">链接</a></p>
<p>使用virtualenv时</p>
<pre><code>me@mymachine:~$ workon env1
(env1)me@mymachine:~$ workon env2
(env2)me@mymachine:~$ workon env1
(env1)me@mymachine:~$
</code></pre>
<p>如何退出某个环境</p>
<pre><code>$ deactivate
</code></pre>
<h3 id="如何认为地抛出一个异常">如何认为地抛出一个异常</h3>
<p>问题 <a href="http://stackoverflow.com/questions/2052390/how-do-i-manually-throw-raise-an-exception-in-python">链接</a></p>
<p>pythonic</p>
<pre><code>raise Exception(&quot;I know python!&quot;)
</code></pre>
<p>更多可参考 <a href="http://docs.python.org/2/reference/simple_stmts.html#the-raise-statement">文档</a></p>
<h3 id="在python中如何展示二进制字面值">在Python中如何展示二进制字面值</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1476/how-do-you-express-binary-literals-in-python">链接</a></p>
<p>十六进制可以</p>
<pre><code>&gt;&gt;&gt; 0x12AF
4783
&gt;&gt;&gt; 0x100
256
</code></pre>
<p>八进制可以</p>
<pre><code>&gt;&gt;&gt; 01267
695
&gt;&gt;&gt; 0100
64
</code></pre>
<p>二进制如何表示？</p>
<p>Python 2.5 及更早版本: 可以表示为 int(&lsquo;01010101111&rsquo;,2)  但没有字面量</p>
<p>Python 2.6 beta: 可以使用0b1100111 or 0B1100111 表示</p>
<p>Python 2.6 beta: 也可以使用 0o27 or 0O27 (第二字字符是字母 O)</p>
<p>Python 3.0 beta: 同2.6，但不支持027这种语法</p>
<h3 id="python中检查类型的权威方法">Python中检查类型的权威方法</h3>
<p>问题 <a href="http://stackoverflow.com/questions/152580/whats-the-canonical-way-to-check-for-type-in-python">链接</a></p>
<p>检查一个对象是否是给定类型或者对象是否继承于给定类型？</p>
<p>比如给定一个对象o,如何判断是不是一个str</p>
<p>检查是否是str</p>
<pre><code>type(o) is str
</code></pre>
<p>检查是否是str或者str的子类</p>
<pre><code>isinstance(o, str)
</code></pre>
<p>下面的方法在某些情况下有用</p>
<pre><code>issubclass(type(o), str)
type(o) in ([str] + str.__subclasses__())
</code></pre>
<p>注意，你或许想要的是</p>
<pre><code>isinstance(o, basestring)
</code></pre>
<p>因为unicode字符串可以满足判定(unicode 不是str的子类，但是str和unicode都是basestring的子类)</p>
<p>可选的，isinstance可以接收多个类型参数，只要满足其中一个即True</p>
<pre><code>isinstance(o, (str, unicode))
</code></pre>
<h3 id="如何获取python的site-packages目录位置">如何获取Python的site-packages目录位置</h3>
<p>问题 <a href="http://stackoverflow.com/questions/122327/how-do-i-find-the-location-of-my-python-site-packages-directory">链接</a></p>
<p>参考 <a href="http://docs.djangoproject.com/en/dev/topics/install/#remove-any-old-versions-of-django">How to Install Django&quot; documentation</a></p>
<p>可以在shell中执行</p>
<pre><code>python -c &quot;from distutils.sysconfig import get_python_lib; print(get_python_lib())&quot;
</code></pre>
<p>更好的可读性</p>
<pre><code>from distutils.sysconfig import get_python_lib
print(get_python_lib())
</code></pre>
<h3 id="python中和的作用">Python中**和*的作用</h3>
<p>问题  <a href="http://stackoverflow.com/questions/36901/what-does-double-star-and-star-do-for-python-parameters">链接</a></p>
<p>*args和**kwargs允许函数拥有任意数量的参数，具体可以查看 <a href="http://docs.python.org/dev/tutorial/controlflow.html#more-on-defining-functions">more on defining functions</a></p>
<p>*args将函数所有参数转为序列</p>
<pre><code>In [1]: def foo(*args):
...:     for a in args:
...:         print a
...:
...:

In [2]: foo(1)
1


In [4]: foo(1,2,3)
1
2
3
</code></pre>
<p>**kwargs 将函数所有关键字参数转为一个字典</p>
<pre><code>In [5]: def bar(**kwargs):
...:     for a in kwargs:
...:         print a, kwargs[a]
...:
...:

In [6]: bar(name=&quot;one&quot;, age=27)
age 27
name one
</code></pre>
<p>两种用法可以组合使用</p>
<pre><code>def foo(kind, *args, **kwargs):
    pass
</code></pre>
<p>*l的另一个用法是用于函数调用时的参数列表解包(unpack)</p>
<pre><code>In [9]: def foo(bar, lee):
...:     print bar, lee
...:
...:

In [10]: l = [1,2]

In [11]: foo(*l)
1 2
</code></pre>
<p>在Python3.0中，可以将*l放在等号左边用于赋值  <a href="http://www.python.org/dev/peps/pep-3132/">Extended Iterable Unpacking</a></p>
<pre><code>first, *rest = [1,2,3,4]
first, *l, last = [1,2,3,4]
</code></pre>
<h3 id="字符串格式化--vs-format">字符串格式化 % vs format</h3>
<p>问题 <a href="http://stackoverflow.com/questions/5082452/python-string-formatting-vs-format">链接</a></p>
<p>Python2.6中引入string.format()方法，语法和原先%操作符的字符串格式化差异较大</p>
<p>在什么情况下使用哪种更好?</p>
<p>以下的输出是一致的，有什么区别</p>
<pre><code>#!/usr/bin/python
sub1 = &quot;python string!&quot;
sub2 = &quot;an arg&quot;

a = &quot;i am a %s&quot;%sub1
b = &quot;i am a {0}&quot;.format(sub1)

c = &quot;with %(kwarg)s!&quot;%{'kwarg':sub2}
d = &quot;with {kwarg}!&quot;.format(kwarg=sub2)

print a
print b
print c
print d
</code></pre>
<p>.format 看起来更加强大，可以用在很多情况.</p>
<p>例如你可以在格式化时重用传入的参数,而你用%时无法做到这点</p>
<p>另一个比较讨厌的是，%只处理 一个变量或一个元组, 你或许会认为下面的语法是正确的</p>
<pre><code>&quot;hi there %s&quot; % name
</code></pre>
<p>但当name恰好是(1,2,3)时，会抛出TypeError异常.为了保证总是正确的，你必须这么写</p>
<pre><code>&quot;hi there %s&quot; % (name,)   # supply the single argument as a single-item tuple
</code></pre>
<p>这么写很丑陋， .format没有这些问题</p>
<p>什么时候不考虑使用.format</p>
<pre><code>你对.format知之甚少
使用Python2.5
</code></pre>
<h3 id="python中什么项目结构更好">Python中什么项目结构更好</h3>
<p>问题 <a href="http://stackoverflow.com/questions/193161/what-is-the-best-project-structure-for-a-python-application">链接</a></p>
<p>假设你要开发一个较大的客户端程序(非web端),如何组织项目目录和递归？</p>
<p>不要太在意这个.按你高兴的方式组织就行.Python项目很简单，所以没有那么多愚蠢的规则</p>
<pre><code>/scripts or /bin  命令行脚本
/tests 测试
/lib C-语言包
/doc 文档
/apidoc api文档
</code></pre>
<p>并且顶层目录包含README和Config</p>
<p>难以抉择的是，是否使用/src树. /src,/lib,/bin在Python中没有明显的区别，和Java/c不同</p>
<p>因为顶层/src文件夹显得没有什么实际意义，你的顶层目录可以是程序顶层架构的目录</p>
<pre><code>/foo
/bar
/baz
</code></pre>
<p>我建议将这些文件放入到&quot;模块名&quot;的目录中，这样，如果你在写一个应用叫做quux, /quux目录将包含所有这些东西</p>
<p>你可以在PYTHONPATH中加入 /path/to/quux/foo,这样你可以QUUX.foo中重用模块</p>
<p>另一个回答</p>
<pre><code>Project/
|-- bin/
|   |-- project
|
|-- project/
|   |-- test/
|   |   |-- __init__.py
|   |   |-- test_main.py
|   |
|   |-- __init__.py
|   |-- main.py
|
|-- setup.py
|-- README
</code></pre>
<h3 id="argparse可选位置参数">argparse可选位置参数</h3>
<p>问题 <a href="http://stackoverflow.com/questions/4480075/argparse-optional-positional-arguments">链接</a></p>
<p>脚本运行 usage: installer.py dir [-h] [-v]</p>
<p>dir是一个位置参数，定义如下</p>
<pre><code>parser.add_argument('dir', default=os.getcwd())
</code></pre>
<p>我想让dir变为可选，如果未设置，使用os.getcwd()</p>
<p>不幸的是，当我不指定dir时，得到错误 &ldquo;Error: Too few arguments&rdquo;</p>
<p>尝试使用 nargs=&rsquo;?&rsquo;</p>
<pre><code>parser.add_argument('dir', nargs='?', default=os.getcwd())
</code></pre>
<p>例子</p>
<pre><code>&gt;&gt;&gt; import os, argparse
&gt;&gt;&gt; parser = argparse.ArgumentParser()
&gt;&gt;&gt; parser.add_argument('-v', action='store_true')
_StoreTrueAction(option_strings=['-v'], dest='v', nargs=0, const=True, default=False, type=None, choices=None, help=None, metavar=None)
&gt;&gt;&gt; parser.add_argument('dir', nargs='?', default=os.getcwd())
_StoreAction(option_strings=[], dest='dir', nargs='?', const=None, default='/home/vinay', type=None, choices=None, help=None, metavar=None)
&gt;&gt;&gt; parser.parse_args('somedir -v'.split())
Namespace(dir='somedir', v=True)
&gt;&gt;&gt; parser.parse_args('-v'.split())
Namespace(dir='/home/vinay', v=True)
&gt;&gt;&gt; parser.parse_args(''.split())
Namespace(dir='/home/vinay', v=False)
&gt;&gt;&gt; parser.parse_args(['somedir'])
Namespace(dir='somedir', v=False)
&gt;&gt;&gt; parser.parse_args('somedir -h -v'.split())
usage: [-h] [-v] [dir]

positional arguments:
dir

optional arguments:
-h, --help  show this help message and exit
-v
</code></pre>
<h3 id="python中-__new__-和-__init__的用法">Python中 <strong>new</strong> 和 __init__的用法</h3>
<p>问题 <a href="http://stackoverflow.com/questions/674304/pythons-use-of-new-and-init">链接</a></p>
<p>我很疑惑，为何__init__总是在__new__之后调用</p>
<p>如下</p>
<pre><code>class A(object):
    _dict = dict()

    def __new__(cls):
        if 'key' in A._dict:
            print &quot;EXISTS&quot;
            return A._dict['key']
        else:
            print &quot;NEW&quot;
            return super(A, cls).__new__(cls)

    def __init__(self):
        print &quot;INIT&quot;
        A._dict['key'] = self
        print &quot;&quot;

a1 = A()
a2 = A()
a3 = A()
</code></pre>
<p>输出</p>
<pre><code>NEW
INIT

EXISTS
INIT

EXISTS
INIT
</code></pre>
<p>有木有人可以解释一下</p>
<p>来自 <a href="http://mail.python.org/pipermail/tutor/2008-April/061426.html">链接</a></p>
<p>使用__new__,当你需要控制一个实例的生成</p>
<p>使用__init__,当你需要控制一个实例的初始化</p>
<p>__new__是实例创建的第一步.最先被调用，并且负责返回类的一个新实例.</p>
<p>相反的,__init__不返回任何东西，只是负责在实例创建后进行初始化</p>
<p>通常情况下，你不必重写__new__除非你写一个子类继承不可变类型，例如str,int,unicode或tuple</p>
<p>你必须了解到，你尝试去做的用<a href="http://en.wikipedia.org/wiki/Factory_object">Factory</a>可以很好地解决，并且是最好的解决方式.使用__new__不是一个简洁的处理方式,一个<a href="http://code.activestate.com/recipes/86900/">factory例子</a></p>
<h3 id="python-self-解释">Python &lsquo;self&rsquo; 解释</h3>
<p>问题 <a href="http://stackoverflow.com/questions/2709821/python-self-explained">链接</a></p>
<p>self关键字的作用是什么？
我理解他用户在创建class时具体化实例，但我无法理解为何需要给每个方法加入self作为参数.</p>
<p>举例，在ruby中，我这么做:</p>
<pre><code>class myClass
    def myFunc(name)
        @name = name
    end
end
</code></pre>
<p>我可以很好地理解，非常简单.但是在Python中，我需要去加入self:</p>
<pre><code>class myClass:
    def myFunc(self, name):
        self.name = name
</code></pre>
<p>有谁能解释下么？</p>
<p>使用self关键字的原因是，Python没有@语法用于引用实例属性.Python决定用一种方式声明方法:实例对象自动传递给属于它的方法,但不是接收自动化：方法的第一个参数是调用这个方法的实例对象本身.这使得方法整个同函数一致,并且由你自己决定真实的名（虽然self是约定，但当你使用其他名的时候，通常人们并不乐意接受）.self对于代码不是特殊的，只是另一个对象.</p>
<p>Python本来可以做一些用来区分真实的名字和属性的区别 —— 像Ruby有的特殊语法，或者像C++/Java的命令声明,或者其他可能的的语法 —— 但是Python没有这么做.Python致力于使事情变得明确简单，让事情是其本身，虽然并不是全部地方都这么做，但是实例属性石这么做的！这就是为什么给一个实例属性赋值时需要知道是给哪个实例赋值,并且，这就是为什么需要self</p>
<p>举例</p>
<pre><code>class Vector(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def length(self):
        return math.sqrt(self.x ** 2 + self.y ** 2)
</code></pre>
<p>等价于</p>
<pre><code>def length_global(vector):
    return math.sqrt(vector.x ** 2 + vector.y ** 2)
</code></pre>
<p>另外</p>
<pre><code>v_instance.length()
转为
Vector.length(v_instance)
</code></pre>
<h3 id="为什么python的private方法并不是真正的私有方法">为什么Python的&rsquo;private&rsquo;方法并不是真正的私有方法</h3>
<p>问题 <a href="http://stackoverflow.com/questions/70528/why-are-pythons-private-methods-not-actually-private">链接</a></p>
<p>Python允许我们创建&rsquo;private&rsquo; 函数：变量以两个下划线开头，像这样： <em>__myPrivateMethod()</em>.
但是，如何解释：</p>
<pre><code>&gt;&gt;&gt; class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
...
&gt;&gt;&gt; obj = MyClass()
&gt;&gt;&gt; obj.myPublicMethod()
public method
&gt;&gt;&gt; obj.__myPrivateMethod()
Traceback (most recent call last):
File &quot;&quot;, line 1, in
AttributeError: MyClass instance has no attribute '__myPrivateMethod'
&gt;&gt;&gt; dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']
&gt;&gt;&gt; obj._MyClass__myPrivateMethod()
this is private!!
</code></pre>
<p>dir(obj) 和 obj._MyClass__myPrivateMethod()</p>
<p>回答</p>
<p>‘private&rsquo;只是用作，确保子类不会意外覆写父类的私有方法和属性.不是为了保护外部意外访问而设计的！</p>
<p>例如:</p>
<pre><code>&gt;&gt;&gt; class Foo(object):
...     def __init__(self):
...         self.__baz = 42
...     def foo(self):
...         print self.__baz
...
&gt;&gt;&gt; class Bar(Foo):
...     def __init__(self):
...         super(Bar, self).__init__()
...         self.__baz = 21
...     def bar(self):
...         print self.__baz
...
&gt;&gt;&gt; x = Bar()
&gt;&gt;&gt; x.foo()
42
&gt;&gt;&gt; x.bar()
21
&gt;&gt;&gt; print x.__dict__
{'_Bar__baz': 21, '_Foo__baz': 42}
</code></pre>
<p>当然，这对于两个同名的类没有作用</p>
<p>另外，可以查看diveintopython的解释 <a href="http://www.faqs.org/docs/diveintopython/fileinfo_private.html#d0e11521">入口</a></p>
<h3 id="python中类方法的作用是什么">Python中类方法的作用是什么</h3>
<p>问题 <a href="http://stackoverflow.com/questions/38238/what-are-class-methods-in-python-for">链接</a></p>
<p>我现在意识到，我不需要像我在使用java的static方法那样使用类方法，但是我不确定什么时候使用</p>
<p>谁能通过一个好的例子解释下Python中的类方法，至少有人能告诉我什么时候确实需要使用类方法</p>
<p>类方法用在：当你需要使用不属于任何明确实例的方法,但同时必须涉及类.有趣的是，你可以在子类中覆写，这在Java的static方法和Python的模块级别函数中是不可能做到的</p>
<p>如果你有一个MyClass, 并且一个模块级别函数操作MyClass(工厂，依赖注入桩等等), 声明一个类方法.然后这个类方法可以在子类中调用</p>
<h3 id="如何删除一个list中重复的值同时保证原有顺序">如何删除一个list中重复的值同时保证原有顺序</h3>
<p>问题 <a href="http://stackoverflow.com/questions/480214/how-do-you-remove-duplicates-from-a-list-in-python-whilst-preserving-order">链接</a></p>
<p>我是这么做的额</p>
<pre><code>def uniq(input):
output = []
for x in input:
    if x not in output:
    output.append(x)
return output
</code></pre>
<p>有什么更好的方法？</p>
<p>你可以在这里找到一些可用的方法 <a href="http://www.peterbe.com/plog/uniqifiers-benchmark">入口</a></p>
<p>最快的一个</p>
<pre><code>def f7(seq):
    seen = set()
    seen_add = seen.add
    return [ x for x in seq if x not in seen and not seen_add(x)]
</code></pre>
<p>如果你需要在同一个数据集中多次是哦那个这个方法，或许你可以使用ordered set处理 <a href="http://code.activestate.com/recipes/528878/">http://code.activestate.com/recipes/528878/</a></p>
<p>插入，删除和归属判断复杂度都是O(1)</p>
<h3 id="有什么方法可以获取系统当前用户名么">有什么方法可以获取系统当前用户名么?</h3>
<p>问题 <a href="http://stackoverflow.com/questions/842059/is-there-a-portable-way-to-get-the-current-username-in-python">链接</a></p>
<p>至少在Linux和Windows下都可用.就像 os.getuid</p>
<pre><code>&gt;&gt;&gt; os.getuid()
42
&gt;&gt;&gt; os.getusername()
'slartibartfast'
</code></pre>
<p>可以看看 <a href="http://docs.python.org/2/library/getpass.html">getpass</a> 模块</p>
<pre><code>&gt;&gt;&gt; import getpass
&gt;&gt;&gt; getpass.getuser()
'kostya'
</code></pre>
<p>可用: Unix, Windows</p>
<h3 id="python-assert最佳实践">Python assert最佳实践</h3>
<p>问题 <a href="http://stackoverflow.com/questions/944592/best-practice-for-python-assert">链接</a></p>
<p>有没有代码实例使用assert作为独立代码，而不是仅用来debug</p>
<pre><code>assert x &gt;= 0, 'x is less than zero'

类似
if x &lt; 0:
    raise Exception, 'x is less than zero'

有什么方法，可以设定一个规则就像 if x \&lt; 0 抛出错误但是不是通过try/except/finally检查的
</code></pre>
<p>搞晕了：</p>
<pre><code>原文 Also, is there any way to set a business rule like if x \&lt; 0 raise error that is always checked without the try/except/finally so, if at anytime throughout the code x is less than 0 an error is raised, like if you set assert x &lt; 0 at the start of a function, anywhere within the function where x becomes less then 0 an exception is raised?
</code></pre>
<p>回答</p>
<p>Assert仅用在，测试那些从不发生的情况！目的是让程序尽早失败</p>
<p>Exception用在，那些可以明确知道会发生的错误，并且建议总是创建自己的异常类</p>
<p>例如，你写一个函数从配置文件中读取配置放入字典，文件格式不正确抛出一个ConfigurationSyntaxError,同时你可以assert返回值非None</p>
<p>在你的例子中，如果x是通过用户接口或外部传递设置的，最好使用exception</p>
<p>如果x仅是同一个程序的内部代码，使用assert</p>
<h3 id="在非创建全局变量的地方使用全局变量">在非创建全局变量的地方使用全局变量</h3>
<p>问题 <a href="http://stackoverflow.com/questions/423379/using-global-variables-in-a-function-other-than-the-one-that-created-them">链接</a></p>
<p>如果我在一个函数中创建了全局变量，如何在另一个函数中使用？</p>
<p>回答：</p>
<p>你可以在给全局变量赋值的函数中声明 global</p>
<pre><code>globvar = 0

def set_globvar_to_one():
    global globvar    # Needed to modify global copy of globvar
    globvar = 1

def print_globvar():
    print globvar     # No need for global declaration to read value of globvar

set_globvar_to_one()
print_globvar()       # Prints 1
</code></pre>
<p>我猜想这么做的原因是，全局变量很危险，Python想要确保你真的知道你要对一个全局的变量进行操作</p>
<p>如果你想知道如何在模块间使用全局变量，查看其他回答</p>
<h3 id="如何在单一表达式中合并两个python字典">如何在单一表达式中合并两个Python字典</h3>
<p>问题 <a href="http://stackoverflow.com/questions/38987/how-can-i-merge-union-two-python-dictionaries-in-a-single-expression">链接</a></p>
<pre><code>&gt;&gt;&gt; x = {'a':1, 'b': 2}
&gt;&gt;&gt; y = {'b':10, 'c': 11}
&gt;&gt;&gt; z = x.update(y)
&gt;&gt;&gt; print z
None
&gt;&gt;&gt; x
{'a': 1, 'b': 10, 'c': 11}
</code></pre>
<p>我想要最终合并结果在z中，不是x，我要怎么做？</p>
<p>回答</p>
<p>这种情况下，可以使用</p>
<pre><code>z = dict(x.items() + y.items())
</code></pre>
<p>这个表达式将会实现你想要的，最终结果z，并且相同key的值，将会是y中key对应的值</p>
<pre><code>&gt;&gt;&gt; x = {'a':1, 'b': 2}
&gt;&gt;&gt; y = {'b':10, 'c': 11}
&gt;&gt;&gt; z = dict(x.items() + y.items())
&gt;&gt;&gt; z
{'a': 1, 'c': 11, 'b': 10}
</code></pre>
<p>如果在Python3中,会变得有些复杂</p>
<pre><code>&gt;&gt;&gt; z = dict(list(x.items()) + list(y.items()))
&gt;&gt;&gt; z
{'a': 1, 'c': 11, 'b': 10}
</code></pre>
<h3 id="如何使用-pip-更新所有包">如何使用 pip 更新所有包</h3>
<p>问题 <a href="http://stackoverflow.com/questions/2720014/upgrading-all-packages-with-pip">链接</a></p>
<p>如何使用pip更新python的所有包</p>
<p>没有内置的标志可以实现</p>
<p>但是你可以这么做</p>
<pre><code>pip freeze --local | grep -v '^\-e' | cut -d = -f 1  | xargs pip install -U
</code></pre>
<h3 id="python中声明exception的方法">Python中声明exception的方法</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1319615/proper-way-to-declare-custom-exceptions-in-modern-python">链接</a></p>
<p>在python2.6中定义异常得到警告</p>
<pre><code>&gt;&gt;&gt; class MyError(Exception):
...     def __init__(self, message):
...         self.message = message
...
&gt;&gt;&gt; MyError(&quot;foo&quot;)
_sandbox.py:3: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
</code></pre>
<p>问题很长，大意如标题</p>
<p>回答</p>
<p>或许我理解错了，但是为什么不这样做</p>
<pre><code>class MyException(Exception):
    pass
</code></pre>
<p>如果要重写什么，例如传递额外参数，可以这么做</p>
<pre><code>class ValidationError(Exception):
    def __init__(self, message, Errors):

        # Call the base class constructor with the parameters it needs
        Exception.__init__(self, message)

        # Now for your custom code...
        self.Errors = Errors
</code></pre>
<p>你可以通过第二个参数传递error 字典, 之后通过e.Errors获取</p>
<h3 id="在python中使用counter错误">在Python中使用Counter错误</h3>
<p>问题 <a href="http://stackoverflow.com/questions/13311094/counter-in-collections-module-python">链接</a></p>
<p>当使用Counter时，出现异常</p>
<pre><code>AttributeError: 'module' object has no attribute 'Counter'

from collections import Counter
ImportError: cannot import name Counter
</code></pre>
<p>原因：</p>
<p>版本问题，Counter在 python2.7中才被加入到这个模块，你可能使用了Python2.6或更老的版本</p>
<p>可以看下 <a href="http://docs.python.org/2/library/collections.html#collections.Counter">文档</a></p>
<p>如果要在 Python2.6或2.5版本使用，可以看 <a href="http://code.activestate.com/recipes/576611-counter-class/">这里</a></p>
<h3 id="如何删除python-easy_install安装的包">如何删除Python easy_install安装的包</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1231688/how-do-i-remove-packages-installed-with-pythons-easy-install">链接</a></p>
<p><a href="https://pypi.python.org/pypi/pip/">pip</a>, setuptools/easy_install的另一种选择，提供uninstall命令</p>
<p>首先，移除依赖</p>
<pre><code>$ easy_install -m [PACKAGE]
</code></pre>
<p>然后，手动删除egg文件</p>
<pre><code>$ rm -rf .../python2.X/site-packages/[PACKAGE].egg
</code></pre>
<h3 id="在python中如何解析xml">在Python中如何解析xml</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1912434/how-do-i-parse-xml-in-python">链接</a></p>
<pre><code>&lt;foo&gt;
&lt;bar&gt;
    &lt;type foobar=&quot;1&quot;/&gt;
    &lt;type foobar=&quot;2&quot;/&gt;
&lt;/bar&gt;
&lt;/foo&gt;
</code></pre>
<p>如何解析获取xml文件中内容</p>
<p>我建议使用 <a href="http://docs.python.org/2/library/xml.etree.elementtree.html">ElementTree</a> (有其他可用的实现，例如 <a href="http://lxml.de/">lxml</a>，他们只是更快, ElementTree提供更简单的编程api)</p>
<p>在使用XML建立Element实例之后，例如使用 <a href="http://docs.python.org/2/library/xml.etree.elementtree.html#xml.etree.ElementTree.XML">XML</a> 函数</p>
<pre><code>for atype in e.findall('type')
    print(atype.get('foobar'))
</code></pre>
<h3 id="如何将一个python-timestruct_time对象转换为一个datetime对象">如何将一个Python time.struct_time对象转换为一个datetime对象</h3>
<p>问题 <a href="http://stackoverflow.com/questions/1697815/how-do-you-convert-a-python-time-struct-time-object-into-a-datetime-object">链接</a></p>
<p>使用 <a href="">time.mktime()</a> 将time元组(本地时间)转成秒， 然后使用 datetime.fromtimestamp() 转成datetime对象</p>
<pre><code>from time import mktime
from datetime import datetime

dt = datetime.fromtimestamp(mktime(struct))
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>[翻译]理解python中的装饰器</title>
			<link>https://wklken.me/posts/2013/07/19/python-translate-decorator.html</link>
			<pubDate>Fri, 19 Jul 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/07/19/python-translate-decorator.html</guid>
			<description>有人翻译过了，很多转载，暂时没找到原文，各个地方的排版不一样，排版（代码格式），代码注解等都不怎么好 练练手，顺手一翻吧，权当加深理解 来源st</description>
			<content type="html"><![CDATA[<p>有人翻译过了，很多转载，暂时没找到原文，各个地方的排版不一样，排版（代码格式），代码注解等都不怎么好</p>
<p>练练手，顺手一翻吧，权当加深理解</p>
<p>来源stackoverflow上的问题  <a href="http://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python/1594484#1594484">链接</a></p>
<p>很长哦(应该是巨长&hellip;分了三次搞完)，要有耐心看完</p>
<hr>
<h2 id="python的函数是对象">python的函数是对象</h2>
<p>要理解装饰器，首先，你必须明白，在python中，函数是对象. 这很重要.</p>
<p>简单例子来理解为什么</p>
<pre><code>def shout(word=&quot;yes&quot;):
    return word.capitalize()+&quot;!&quot;

print shout()
# outputs : 'Yes!'

# 作为一个对象，你可以讲函数赋值给另一个对象
scream = shout

# 注意到这里我们并没有使用括号：我们不是调用函数，而是将函数'shout'赋给变量'scream'
# 这意味着，你可以通过'scream'调用'shout'

print scream()
# outputs : 'Yes!'

# 不仅如此，你可以删除老的名称'shout'，但是通过'scream'依旧可以访问原有函数

del shout
try:
    print shout()
except NameError, e:
    print e
    #outputs: &quot;name 'shout' is not defined&quot;

print scream()
# outputs: 'Yes!'
</code></pre>
<p>好了，记住这点，我们将会很快用到它.</p>
<p>Python函数另一个有趣的特性是，函数可以被定义在另一个函数里面</p>
<pre><code>def talk():
    # 你可以定义一个函数
    def whisper(word=&quot;yes&quot;):
        return word.lower()+&quot;...&quot;

    # ... 并且立刻调用
    print whisper()

# 每次当你调用&quot;talk&quot;, 都会定义&quot;whisper&quot;
# 并且在&quot;talk&quot;中被调用
talk()
# outputs:
# &quot;yes...&quot;

#但是在&quot;talk&quot;外部，函数&quot;whisper&quot;不存在！
try:
    print whisper()
except NameError, e:
    print e
    #outputs : &quot;name 'whisper' is not defined&quot;*
</code></pre>
<h2 id="函数引用">函数引用</h2>
<p>好了，到这里了，接下来是有意思的部分，我们刚才看到 函数是对象，然后:</p>
<p>1.函数可以赋值给一个变量</p>
<p>2.函数可以定义在另一个函数内部</p>
<p>即，这也意味着一个函数可以返回另一个函数:-）,让我们来看另一段代码</p>
<pre><code>def getTalk(type=&quot;shout&quot;):

    # 定义函数
    def shout(word=&quot;yes&quot;):
        return word.capitalize()+&quot;!&quot;

    def whisper(word=&quot;yes&quot;) :
        return word.lower()+&quot;...&quot;;

    # 返回函数
    if type == &quot;shout&quot;:
        # 没有使用&quot;()&quot;, 并不是要调用函数，而是要返回函数对象
        return shout
    else:
        return whisper

# 如何使用？

# 将函数返回值赋值给一个变量
talk = getTalk()

# 我们可以打印下这个函数对象
print talk
#outputs : &lt;function shout at 0xb7ea817c&gt;

# 这个对象是函数的返回值
print talk()
#outputs : Yes!

# 不仅如此，你还可以直接使用之
print getTalk(&quot;whisper&quot;)()
#outputs : yes...
</code></pre>
<p>但是稍等，如果你可以返回一个函数，那么你也可以将函数作为参数传递</p>
<pre><code>def doSomethingBefore(func):
    print &quot;I do something before then I call the function you gave me&quot;
    print func()

doSomethingBefore(scream)
#outputs:
#I do something before then I call the function you gave me
#Yes!
</code></pre>
<p>好了，现在你已经了解要理解装饰器的每件事.</p>
<p>装饰器就是封装器，可以让你在被装饰函数之前或之后执行代码，而不必修改函数本身</p>
<h2 id="手工装饰器">手工装饰器</h2>
<p>如何书写一个装饰器</p>
<pre><code># 装饰器是一个以另一个函数为参数的函数
def my_shiny_new_decorator(a_function_to_decorate):

    # 在这里，装饰器定义一个函数： 包装器.
    # 这个函数将原始函数进行包装，以达到在原始函数之前、之后执行代码的目的
    def the_wrapper_around_the_original_function():

        # 将你要在原始函数之前执行的代码放到这里
        print &quot;Before the function runs&quot;

        # 调用原始函数(需要带括号)
        a_function_to_decorate()

        # 将你要在原始函数之后执行的代码放到这里
        print &quot;After the function runs&quot;

    # 代码到这里，函数‘a_function_to_decorate’还没有被执行
    # 我们将返回刚才创建的这个包装函数
    # 这个函数包含原始函数及要执行的附加代码，并且可以被使用
    return the_wrapper_around_the_original_function

# 创建一个函数
def a_stand_alone_function():
    print &quot;I am a stand alone function, don't you dare modify me&quot;

a_stand_alone_function()
#outputs: I am a stand alone function, don't you dare modify me

# 好了，在这里你可以装饰这个函数，扩展其行为
# 将函数传递给装饰器，装饰器将动态地将其包装在任何你想执行的代码中，然后返回一个新的函数
a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)

# 调用新函数，可以看到装饰器的效果
a_stand_alone_function_decorated()
#outputs:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs
</code></pre>
<p>到这里，或许你想每次调用a_stand_alone_function都使用a_stand_alone_function_decorated替代之
很简单，只需要将a_stand_alone_function用my_shiny_new_decorator装饰返回</p>
<pre><code>a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function()
#outputs:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs

# 这就是装饰器做的事情!
</code></pre>
<h2 id="装饰器阐述">装饰器阐述</h2>
<p>前面的例子，使用装饰器语法</p>
<pre><code>@my_shiny_new_decorator
def another_stand_alone_function():
    print &quot;Leave me alone&quot;

another_stand_alone_function()
#outputs:
#Before the function runs
#Leave me alone
#After the function runs
</code></pre>
<p>是的，就是这么简单. @decorator是下面代码的简写</p>
<pre><code>nother_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)
</code></pre>
<p>装饰器只是 <a href="http://en.wikipedia.org/wiki/Decorator_pattern">装饰器模式</a>的python实现</p>
<p>python代码中还存在其他几个经典的设计模式，以方便开发，例如迭代器iterators</p>
<p>当然，你可以累加装饰器</p>
<pre><code>def bread(func):
    def wrapper():
        print &quot;&lt;/''''''\&gt;&quot;
        func()
        print &quot;&lt;\______/&gt;&quot;
    return wrapper

def ingredients(func):
    def wrapper():
        print &quot;#tomatoes#&quot;
        func()
        print &quot;~salad~&quot;
    return wrapper

def sandwich(food=&quot;--ham--&quot;):
    print food

sandwich()
#outputs: --ham--

#累加两个装饰器
sandwich = bread(ingredients(sandwich))
sandwich()
#outputs:
#&lt;/''''''\&gt;
# #tomatoes#
# --ham--
# ~salad~
#&lt;\______/&gt;
</code></pre>
<p>使用python装饰器语法</p>
<pre><code>@bread
@ingredients
def sandwich(food=&quot;--ham--&quot;):
    print food

sandwich()
#outputs:
#&lt;/''''''\&gt;
# #tomatoes#
# --ham--
# ~salad~
#&lt;\______/&gt;
</code></pre>
<p>装饰器位置的顺序很重要</p>
<pre><code>@ingredients
@bread
def strange_sandwich(food=&quot;--ham--&quot;):
    print food

    strange_sandwich()
#outputs:
##tomatoes#
#&lt;/''''\&gt;
# --ham--
#&lt;\______/&gt;
# ~salad~'
</code></pre>
<h2 id="最后回答问题">最后回答问题</h2>
<pre><code># bold装饰器
def makebold(fn):
    def wrapper():
        # 在前后加入标签
        return &quot;&lt;b&gt;&quot; + fn() + &quot;&lt;/b&gt;&quot;
    return wrapper

# italic装饰器
def makeitalic(fn):
    def wrapper():
        # 加入标签
        return &quot;&lt;i&gt;&quot; + fn() + &quot;&lt;/i&gt;&quot;
    return wrapper

@makebold
@makeitalic
def say():
    return &quot;hello&quot;

print say()
#outputs: &lt;b&gt;&lt;i&gt;hello&lt;/i&gt;&lt;/b&gt;

# 等价的代码
def say():
    return &quot;hello&quot;
say = makebold(makeitalic(say))

print say()
#outputs: &lt;b&gt;&lt;i&gt;hello&lt;/i&gt;&lt;/b&gt;
</code></pre>
<p>好了，到这里你可以高兴地离开了，或者来看下一些装饰器高级的用法</p>
<h3 id="向装饰器函数传递参数">向装饰器函数传递参数</h3>
<pre><code># 这不是黑魔法，你只需要让包装传递参数:

def a_decorator_passing_arguments(function_to_decorate):
    def a_wrapper_accepting_arguments(arg1, arg2):
            print &quot;I got args! Look:&quot;, arg1, arg2
            function_to_decorate(arg1, arg2)
    return a_wrapper_accepting_arguments

# 当你调用装饰器返回的函数，实际上是调用包装函数，所以给包装函数传递参数即可将参数传给装饰器函数

@a_decorator_passing_arguments
def print_full_name(first_name, last_name):
    print &quot;My name is&quot;, first_name, last_name

print_full_name(&quot;Peter&quot;, &quot;Venkman&quot;)
# outputs:
#I got args! Look: Peter Venkman
#My name is Peter Venkman
</code></pre>
<h3 id="装饰方法">装饰方法</h3>
<p>Python中对象的方法和函数是一样的，除了对象的方法首个参数是指向当前对象的引用(self)。这意味着你可以用同样的方法构建一个装饰器，只是必须考虑self</p>
<pre><code>def method_friendly_decorator(method_to_decorate):
    def wrapper(self, lie):
        lie = lie - 3 # very friendly, decrease age even more :-)
        return method_to_decorate(self, lie)
    return wrapper

class Lucy(object):

    def __init__(self):
        self.age = 32

    @method_friendly_decorator
    def sayYourAge(self, lie):
        print &quot;I am %s, what did you think?&quot; % (self.age + lie)

l = Lucy()
l.sayYourAge(-3)
#outputs: I am 26, what did you think?
</code></pre>
<p>当然，你可以构造一个更加通用的装饰器，可以作用在任何函数或对象方法上，而不必关系其参数
使用</p>
<pre><code>*args, **kwargs
</code></pre>
<p>如下代码</p>
<pre><code>def a_decorator_passing_arbitrary_arguments(function_to_decorate):
    # 包装函数可以接受任何参数
    def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):
        print &quot;Do I have args?:&quot;
        print args
        print kwargs
        # 然后你可以解开参数， *args，**kwargs
        # 如果你对此不是很熟悉，可以参考 http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
        function_to_decorate(*args, **kwargs)
    return a_wrapper_accepting_arbitrary_arguments

@a_decorator_passing_arbitrary_arguments
def function_with_no_argument():
    print &quot;Python is cool, no argument here.&quot;

function_with_no_argument()
#outputs
#Do I have args?:
#()
#{}
#Python is cool, no argument here.

@a_decorator_passing_arbitrary_arguments
def function_with_arguments(a, b, c):
    print a, b, c

function_with_arguments(1,2,3)
#outputs
#Do I have args?:
#(1, 2, 3)
#{}
#1 2 3

@a_decorator_passing_arbitrary_arguments
def function_with_named_arguments(a, b, c, platypus=&quot;Why not ?&quot;):
    print &quot;Do %s, %s and %s like platypus? %s&quot; %\
    (a, b, c, platypus)

function_with_named_arguments(&quot;Bill&quot;, &quot;Linus&quot;, &quot;Steve&quot;, platypus=&quot;Indeed!&quot;)
#outputs
#Do I have args ? :
#('Bill', 'Linus', 'Steve')
#{'platypus': 'Indeed!'}
#Do Bill, Linus and Steve like platypus? Indeed!

class Mary(object):
    def __init__(self):
        self.age = 31

    @a_decorator_passing_arbitrary_arguments
    def sayYourAge(self, lie=-3): # You can now add a default value
        print &quot;I am %s, what did you think ?&quot; % (self.age + lie)

m = Mary()
m.sayYourAge()
#outputs
# Do I have args?:
#(&lt;__main__.Mary object at 0xb7d303ac&gt;,)
#{}
#I am 28, what did you think?
</code></pre>
<h3 id="向装饰器传递参数">向装饰器传递参数</h3>
<p>好了，现在你或许会想是否可以向装饰器本身传递参数</p>
<p>装饰器必须使用函数作为参数，所以这看起来会有些复杂，你不能直接传递参数给装饰器本身</p>
<p>在开始处理这个问题前，看一点提醒</p>
<pre><code># 装饰器是普通的方法
def my_decorator(func):
    print &quot;I am a ordinary function&quot;
    def wrapper():
        print &quot;I am function returned by the decorator&quot;
        func()
    return wrapper

# 所以，你可以不通过@调用它

def lazy_function():
    print &quot;zzzzzzzz&quot;

decorated_function = my_decorator(lazy_function)
#outputs: I am a ordinary function

# It outputs &quot;I am a ordinary function&quot;, because that's just what you do:

# 调用一个函数，没有什么特别
@my_decorator
def lazy_function():
    print &quot;zzzzzzzz&quot;

#outputs: I am a ordinary function
</code></pre>
<p>上面两个形式本质上是相同的， &ldquo;my_decorator&rdquo; 被调用.所以当你使用&quot;@my_decorator&quot;,告诉python一个函数被变量&quot;my_decorator&quot;标记
这十分重要,因为你提供的标签直接指向装饰器&hellip;或者不是，继续</p>
<pre><code># 声明一个用于创建装饰器的函数
def decorator_maker():

    print &quot;I make decorators! I am executed only once: &quot;+\
          &quot;when you make me create a decorator.&quot;

    def my_decorator(func):
        print &quot;I am a decorator! I am executed only when you decorate a function.&quot;

        def wrapped():
            print (&quot;I am the wrapper around the decorated function. &quot;
                  &quot;I am called when you call the decorated function. &quot;
                  &quot;As the wrapper, I return the RESULT of the decorated function.&quot;)
            return func()

        print &quot;As the decorator, I return the wrapped function.&quot;
        return wrapped

    print &quot;As a decorator maker, I return a decorator&quot;
    return my_decorator

# Let's create a decorator. It's just a new function after all.
# 创建一个装饰器，本质上只是一个函数
new_decorator = decorator_maker()
#outputs:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator

# 使用装饰器装饰函数

def decorated_function():
    print &quot;I am the decorated function.&quot;

decorated_function = new_decorator(decorated_function)
#outputs:
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function

# 调用被装饰函数
decorated_function()
#outputs:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.
</code></pre>
<p>我们跳过中间变量，做同样的事情</p>
<pre><code>def decorated_function():
    print &quot;I am the decorated function.&quot;
decorated_function = decorator_maker()(decorated_function)
#outputs:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function.

# 最后:
decorated_function()    
#outputs:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.
</code></pre>
<p>使用装饰器语法，更简短</p>
<pre><code>@decorator_maker()
def decorated_function():
    print &quot;I am the decorated function.&quot;
#outputs:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function.

#最终: 
decorated_function()    
#outputs:
#I am the wrapper around the decorated function. I am called when you call the decorated function.
#As the wrapper, I return the RESULT of the decorated function.
#I am the decorated function.
</code></pre>
<p>到这里，我们使用@调用一个函数</p>
<p>回到问题，向装饰器本身传递参数，如果我们可以通过函数去创建装饰器，那么我们可以传递参数给这个函数，对么？</p>
<pre><code>def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):

    print &quot;I make decorators! And I accept arguments:&quot;, decorator_arg1, decorator_arg2

    def my_decorator(func):
        # 这里能传递参数的能力，是闭包的特性
        # 更多闭包的内容，参考 http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python
        print &quot;I am the decorator. Somehow you passed me arguments:&quot;, decorator_arg1, decorator_arg2

        # 不要搞混了装饰器参数和函数参数
        def wrapped(function_arg1, function_arg2) :
            print (&quot;I am the wrapper around the decorated function.\n&quot;
                  &quot;I can access all the variables\n&quot;
                  &quot;\t- from the decorator: {0} {1}\n&quot;
                  &quot;\t- from the function call: {2} {3}\n&quot;
                  &quot;Then I can pass them to the decorated function&quot;
                  .format(decorator_arg1, decorator_arg2,
                          function_arg1, function_arg2))
            return func(function_arg1, function_arg2)

        return wrapped

    return my_decorator

@decorator_maker_with_arguments(&quot;Leonard&quot;, &quot;Sheldon&quot;)
def decorated_function_with_arguments(function_arg1, function_arg2):
    print (&quot;I am the decorated function and only knows about my arguments: {0}&quot;
           &quot; {1}&quot;.format(function_arg1, function_arg2))

decorated_function_with_arguments(&quot;Rajesh&quot;, &quot;Howard&quot;)
#outputs:
#I make decorators! And I accept arguments: Leonard Sheldon
#I am the decorator. Somehow you passed me arguments: Leonard Sheldon
#I am the wrapper around the decorated function. 
#I can access all the variables 
#   - from the decorator: Leonard Sheldon 
#   - from the function call: Rajesh Howard 
#Then I can pass them to the decorated function
#I am the decorated function and only knows about my arguments: Rajesh Howard
</code></pre>
<p>好了，that&rsquo;s it.参数可以设置为变量</p>
<pre><code>c1 = &quot;Penny&quot;
c2 = &quot;Leslie&quot;

@decorator_maker_with_arguments(&quot;Leonard&quot;, c1)
def decorated_function_with_arguments(function_arg1, function_arg2):
    print (&quot;I am the decorated function and only knows about my arguments:&quot;
           &quot; {0} {1}&quot;.format(function_arg1, function_arg2))

decorated_function_with_arguments(c2, &quot;Howard&quot;)
#outputs:
#I make decorators! And I accept arguments: Leonard Penny
#I am the decorator. Somehow you passed me arguments: Leonard Penny
#I am the wrapper around the decorated function. 
#I can access all the variables 
#   - from the decorator: Leonard Penny 
#   - from the function call: Leslie Howard 
#Then I can pass them to the decorated function
#I am the decorated function and only knows about my arguments: Leslie Howard
</code></pre>
<p>你可以看到，你可以使用像其它函数一样使用这个方法向装饰器传递参数.如果你愿意你甚至可以使用 *arg **kwargs.</p>
<p>但是记住，装饰器仅在Python代码导入时被调用一次,之后你不能动态地改变参数.当你使用&quot;import x&quot;,函数已经被装饰，所以你不能改变什么</p>
<h3 id="练习一个装饰装饰器的装饰器">练习：一个装饰装饰器的装饰器</h3>
<p>作为奖励，我将展示创建可以处理任何参数的装饰器代码片段. 毕竟，为了接收参数，必须使用另一个函数来创建装饰器</p>
<p>让我们来给装饰器写一个装饰器:</p>
<pre><code># 装饰 装饰器 的装饰器 (好绕.....)
def decorator_with_args(decorator_to_enhance):
    &quot;&quot;&quot; 
    这个函数将作为装饰器使用
    它必须装饰另一个函数
    它将允许任何接收任意数量参数的装饰器
    方便你每次查询如何实现
    &quot;&quot;&quot;

    # 同样的技巧传递参数
    def decorator_maker(*args, **kwargs):

        # 创建一个只接收函数的装饰器
        # 但是这里保存了从创建者传递过来的的参数
        def decorator_wrapper(func):

            # 我们返回原始装饰器的结果
            # 这是一个普通的函数，返回值是另一个函数
            # 陷阱：装饰器必须有这个特殊的签名，否则不会生效
            return decorator_to_enhance(func, *args, **kwargs)

        return decorator_wrapper

    return decorator_maker
</code></pre>
<p>使用：</p>
<pre><code># 你创建这个函数是作为一个装饰器，但是给它附加了一个装饰器
# 别忘了，函数签名是： &quot;decorator(func, *args, **kwargs)&quot;
@decorator_with_args 
def decorated_decorator(func, *args, **kwargs): 
    def wrapper(function_arg1, function_arg2):
        print &quot;Decorated with&quot;, args, kwargs
        return func(function_arg1, function_arg2)
    return wrapper

# 然后，使用这个装饰器(your brand new decorated decorator)

@decorated_decorator(42, 404, 1024)
def decorated_function(function_arg1, function_arg2):
    print &quot;Hello&quot;, function_arg1, function_arg2

decorated_function(&quot;Universe and&quot;, &quot;everything&quot;)
#outputs:
#Decorated with (42, 404, 1024) {}
#Hello Universe and everything

# Whoooot!
</code></pre>
<p>我知道，到现在你一定会有这种感觉，就像你听一个人说“在理解递归之前，你必须首先了解递归”，但是现在，掌握这儿你有没有觉得很棒？</p>
<h2 id="装饰器使用最佳实践">装饰器使用最佳实践</h2>
<ul>
<li>这是Python2.4的新特性，所以确保你的代码在2.4及之上的版本运行</li>
<li>装饰器降低了函数调用的性能，记住这点</li>
<li>You can not un-decorate a function. There are hacks to create decorators that can be removed but nobody uses them. So once a function is decorated, it&rsquo;s done. For all the code.</li>
<li>装饰器包装函数，所以很难debug</li>
</ul>
<p>Python2.5解决了最后一个问题，它提供functools模块，包含functools.wraps.这个函数会将被装饰函数的名称，模块，文档字符串拷贝给封装函数,有趣的是，functools.wraps是一个装饰器:-)</p>
<pre><code># 调试，打印函数的名字
def foo():
    print &quot;foo&quot;

print foo.__name__
#outputs: foo

# 但当你使用装饰器，这一切变得混乱
def bar(func):
    def wrapper():
        print &quot;bar&quot;
        return func()
    return wrapper

@bar
def foo():
    print &quot;foo&quot;

print foo.__name__
#outputs: wrapper

# &quot;functools&quot; 可以改变这点
import functools

def bar(func):
    # 我们所说的 &quot;wrapper&quot;, 封装 &quot;func&quot;
    @functools.wraps(func)
    def wrapper():
        print &quot;bar&quot;
        return func()
    return wrapper

@bar
def foo():
    print &quot;foo&quot;

# 得到的是原始的名称, 而不是封装器的名称
print foo.__name__
#outputs: foo
</code></pre>
<h3 id="装饰器为何那么有用">装饰器为何那么有用</h3>
<p>现在的问题是，我们用装饰器来坐什么？看起来很酷很强大，但是如果有实践的例子会更好.好了，有1000种可能。经典的用法是，在函数的外部，扩展一个函数的行为（你不需要改变这个函数），或者，为了调试的目的（我们不修改的原因是这是临时的），你可以使用装饰器扩展一些函数,而不用在这些函数中书写相同的函数实现一样的功能</p>
<p>DRY原则，例子：</p>
<pre><code>def benchmark(func):
    &quot;&quot;&quot;
    装饰器打印一个函数的执行时间
    &quot;&quot;&quot;
    import time
    def wrapper(*args, **kwargs):
        t = time.clock()
        res = func(*args, **kwargs)
        print func.__name__, time.clock()-t
        return res
    return wrapper

def logging(func):
    &quot;&quot;&quot;
    装饰器记录函数日志
    &quot;&quot;&quot;
    def wrapper(*args, **kwargs):
        res = func(*args, **kwargs)
        print func.__name__, args, kwargs
        return res
    return wrapper

def counter(func):
    &quot;&quot;&quot;
    记录并打印一个函数的执行次数
    &quot;&quot;&quot;
    def wrapper(*args, **kwargs):
        wrapper.count = wrapper.count + 1
        res = func(*args, **kwargs)
        print &quot;{0} has been used: {1}x&quot;.format(func.__name__, wrapper.count)
        return res
    wrapper.count = 0
    return wrapper

@counter
@benchmark
@logging
def reverse_string(string):
    return str(reversed(string))

print reverse_string(&quot;Able was I ere I saw Elba&quot;)
print reverse_string(&quot;A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!&quot;)

#outputs:
#reverse_string ('Able was I ere I saw Elba',) {}
#wrapper 0.0
#wrapper has been used: 1x
#ablE was I ere I saw elbA
#reverse_string ('A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!',) {}
#wrapper 0.0
#wrapper has been used: 2x
#!amanaP :lanac a ,noep a ,stah eros ,raj a ,hsac ,oloR a ,tur a ,mapS ,snip ,eperc a ,)lemac a ro( niaga gab ananab a ,gat a ,nat a ,gab ananab a ,gag a ,inoracam ,elacrep ,epins ,spam ,arutaroloc a ,shajar ,soreh ,atsap ,eonac a ,nalp a ,nam A
</code></pre>
<p>装饰器意味着，你可以用正确的方法实现几乎所有的事情，而不必重写他们</p>
<pre><code>@counter
@benchmark
@logging
def get_random_futurama_quote():
    import httplib
    conn = httplib.HTTPConnection(&quot;slashdot.org:80&quot;)
    conn.request(&quot;HEAD&quot;, &quot;/index.html&quot;)
    for key, value in conn.getresponse().getheaders():
        if key.startswith(&quot;x-b&quot;) or key.startswith(&quot;x-f&quot;):
            return value
    return &quot;No, I'm ... doesn't!&quot;

print get_random_futurama_quote()
print get_random_futurama_quote()

#outputs:
#get_random_futurama_quote () {}
#wrapper 0.02
#wrapper has been used: 1x
#The laws of science be a harsh mistress.
#get_random_futurama_quote () {}
#wrapper 0.01
#wrapper has been used: 2x
#Curse you, merciful Poseidon!
</code></pre>
<p>Python本身提供了一些装饰器：property,staticmethod,等等，</p>
<p>Django使用装饰器去管理缓存和权限. Twisted to fake inlining asynchronous functions calls.用途广泛</p>
<p>EDIT: 鉴于这个回答的完美，人们希望我去回答metaclass,我这样做了</p>
]]></content>
		</item>
		
		<item>
			<title>[翻译]Python中yield的解释</title>
			<link>https://wklken.me/posts/2013/07/18/python-translate-yield.html</link>
			<pubDate>Thu, 18 Jul 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/07/18/python-translate-yield.html</guid>
			<description>翻译 来源于stackoverflow问答，原文链接 Here SN上面看到的，顺手翻译下，第一次翻译，好多地方翻的不是很好 :) 问题: Python中yie</description>
			<content type="html"><![CDATA[<p>翻译</p>
<p>来源于stackoverflow问答，原文链接 <a href="http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained">Here</a></p>
<p>SN上面看到的，顺手翻译下，第一次翻译，好多地方翻的不是很好 :)</p>
<hr>
<p>问题:</p>
<pre><code>Python中yield关键字的作用是什么？它做了什么？
</code></pre>
<p>例如，我想理解以下代码</p>
<pre><code>def node._get_child_candidates(self, distance, min_dist, max_dist):
    if self._leftchild and distance - max_dist &lt; self._median:
        yield self._leftchild
    if self._rightchild and distance + max_dist &gt;= self._median:
        yield self._rightchild
</code></pre>
<p>下面是调用者</p>
<pre><code>result, candidates = list(), [self]
while candidates:
    node = candidates.pop()
    distance = node._get_dist(obj)
    if distance &lt;= max_dist and distance &gt;= min_dist:
        result.extend(node._values)
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result
</code></pre>
<p>在_get_child_candidates这个函数被调用时发生了什么？返回了一个列表？还是只返回了一个元素？然后又再次被调用？什么时候调用结束？</p>
<p>这段代码的来源 Jochen Schulz (jrschulz), who made a great Python library for metric spaces. 完整源码链接: <a href="http://well-adjusted.de/~jrschulz/mspace/">here</a></p>
<hr>
<p>要了解yield的作用，你必须先明白什么是生成器，在此之前，你需要了解什么是可迭代对象（可迭代序列）</p>
<h2 id="迭代">迭代</h2>
<p>你可以创建一个列表，然后逐一遍历，这就是迭代</p>
<pre><code>&gt;&gt;&gt; mylist = [1, 2, 3]
&gt;&gt;&gt; for i in mylist:
...    print(i)
1
2
3
</code></pre>
<p>mylist是可迭代的对象，当你使用列表解析时，你创建一个列表,即一个可迭代对象</p>
<pre><code>&gt;&gt;&gt; mylist = [x*x for x in range(3)]
&gt;&gt;&gt; for i in mylist:
...    print(i)
0
1
4
</code></pre>
<p>任何你可用 &ldquo;for&hellip; in&hellip;&rdquo; 处理的都是可迭代对象：列表，字符串，文件&hellip;.
这些迭代对象非常便捷，因为你可以尽可能多地获取你想要的东西</p>
<p>但，当你有大量数据并把所有值放到内存时，这种处理方式可能不总是你想要的
(but you store all the values in memory and it&rsquo;s not always what you want when you have a lot of values.)</p>
<h2 id="生成器">生成器</h2>
<p>生成器是迭代器，但你只能遍历它一次(iterate over them once)
因为生成器并没有将所有值放入内存中，而是实时地生成这些值</p>
<pre><code>&gt;&gt;&gt; mygenerator = (x*x for x in range(3))
&gt;&gt;&gt; for i in mygenerator:
...    print(i)
0
1
4
</code></pre>
<p>这和使用列表解析地唯一区别在于使用()替代了原来的[]</p>
<p>注意，你不能执行for i in mygenerator第二次，因为每个生成器只能被使用一次: 计算0，并不保留结果和状态，接着计算1，然后计算4，逐一生成</p>
<h2 id="yield">yield</h2>
<p>yield是一个关键词，类似return, 不同之处在于，yield返回的是一个生成器</p>
<pre><code>&gt;&gt;&gt; def createGenerator():
...    mylist = range(3)
...    for i in mylist:
...        yield i*i
...
&gt;&gt;&gt; mygenerator = createGenerator() # create a generator
&gt;&gt;&gt; print(mygenerator) # mygenerator is an object!
&lt;generator object createGenerator at 0xb7555c34&gt;
&gt;&gt;&gt; for i in mygenerator:
...     print(i)
0
1
4
</code></pre>
<p>这个例子并没有什么实际作用,仅说明当你知道你的函数将产生大量仅被读取一次的数据时,使用生成器将是十分有效的做法</p>
<p>要掌握yield,你必须明白 - 当你调用这个函数，函数中你书写的代码并没有执行。这个函数仅仅返回一个生成器对象</p>
<p>这有些狡猾 :-)</p>
<p>然后，在每次for循环使用生成器时,都会执行你的代码</p>
<p>然后，是比较困难的部分：</p>
<p>第一次函数将会从头运行，直到遇到yield，然后将返回循环的首个值. 然后，每次调用，都会执行函数中的循环一次，返回下一个值，直到没有值可以返回</p>
<p>当循环结束，或者不满足&quot;if/else&quot;条件，导致函数运行但不命中yield关键字，此时生成器被认为是空的</p>
<h3 id="问题代码的解释">问题代码的解释</h3>
<p>生成器:</p>
<pre><code># 这你你创建了node的能返回生成器的函数
def node._get_child_candidates(self, distance, min_dist, max_dist):

# 这里的代码你每次使用生成器对象都会调用

# 如果node节点存在左子节点,且距离没问题，返回该节点
if self._leftchild and distance - max_dist &lt; self._median:
                yield self._leftchild

# 同理，返回右子节点
if self._rightchild and distance + max_dist &gt;= self._median:
                yield self._rightchild

# 如果函数运行到这里，生成器空，该节点不存在左右节点
</code></pre>
<p>调用者:</p>
<pre><code># 创建一个空列表，一个包含当前候选对象引用的列表
result, candidates = list(), [self]

# 当前候选非空，循环(开始时仅有一个元素)
while candidates:

    # 从候选列表取出最后一个元素作为当前节点
    node = candidates.pop()

    # 获取obj和当前节点距离
    distance = node._get_dist(obj)

    # 如果距离满足条件，将节点值加入结果列表
    if distance &lt;= max_dist and distance &gt;= min_dist:
        result.extend(node._values)

    # 获取节点的子节点，加入到候选列表，回到循环开始, 这里使用了生成器
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
    # 注意这里extend会反复调用获取到所有生成器返回值

return result
</code></pre>
<p>这段代码包含几个灵活的部分:</p>
<p>1.这个循环遍读取历候选列表,但过程中，候选列表不断扩展:-)</p>
<p>这是一种遍历嵌套数据的简明方法，虽然有些危险，你或许会陷入死循环中</p>
<p>在这个例子中， candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) 读取了生成器产生的所有值, 同时while循环产生新的生成器对象加入到列表，因为每个对象作用在不同节点上，所以每个生成器都将生成不同的值</p>
<p>2.列表方法extend() 接收一个生成器，生成器的所有值被添加到列表中</p>
<p>通常，我们传一个列表作为参数:</p>
<pre><code>&gt;&gt;&gt; a = [1, 2]
&gt;&gt;&gt; b = [3, 4]
&gt;&gt;&gt; a.extend(b)
&gt;&gt;&gt; print(a)
[1, 2, 3, 4]
</code></pre>
<p>但是，在代码中，这个函数接受到一个生成器</p>
<p>这样的做法好处是:</p>
<p>1.你不需要重复读这些值</p>
<p>2.你可能有海量的子节点，但是不希望将所有节点放入内存</p>
<p>并且，可以这么传递生成器作为参数的原因是，Python不关心参数是一个方法还是一个列表</p>
<p>Python接收可迭代对象，对于字符串，列表，元组还有生成器，都适用!</p>
<p>这就是所谓的“鸭子类型”(duck typing), 这也是Python如此酷的原因之一, 但这是另一个问题了，对于这个问题&hellip;&hellip;</p>
<p>你可以在这里完成阅读，或者读一点点生成器的进阶用法:</p>
<p>####控制一个生成器的消耗</p>
<pre><code>&gt;&gt;&gt; class Bank(): # let's create a bank, building ATMs
...    crisis = False
...    def create_atm(self):
...        while not self.crisis:
...            yield &quot;$100&quot;
&gt;&gt;&gt; hsbc = Bank() # when everything's ok the ATM gives you as much as you want
&gt;&gt;&gt; corner_street_atm = hsbc.create_atm()
&gt;&gt;&gt; print(corner_street_atm.next())
$100
&gt;&gt;&gt; print(corner_street_atm.next())
$100
&gt;&gt;&gt; print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
&gt;&gt;&gt; hsbc.crisis = True # crisis is coming, no more money!
&gt;&gt;&gt; print(corner_street_atm.next())
&lt;type 'exceptions.StopIteration'&gt;
&gt;&gt;&gt; wall_street_atm = hsbc.create_atm() # it's even true for new ATMs
&gt;&gt;&gt; print(wall_street_atm.next())
&lt;type 'exceptions.StopIteration'&gt;
&gt;&gt;&gt; hsbc.crisis = False # trouble is, even post-crisis the ATM remains empty
&gt;&gt;&gt; print(corner_street_atm.next())
&lt;type 'exceptions.StopIteration'&gt;
&gt;&gt;&gt; brand_new_atm = hsbc.create_atm() # build a new one to get back in business
&gt;&gt;&gt; for cash in brand_new_atm:
...    print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...
</code></pre>
<p>这在很多场景都非常有用，例如控制资源的获取</p>
<h2 id="itertools">Itertools</h2>
<p>一个很好的工具</p>
<p>itertools模块包含很多处理可迭代对象的具体方法. 例如</p>
<p>复制一个生成器？连接两个生成器？一行将嵌套列表中值分组？不使用另一个列表进行Map/Zip?
(Ever wish to duplicate a generator? Chain two generators? Group values in a nested list with a one liner? Map / Zip without creating another list?)</p>
<p>只需要使用itertools模块</p>
<p>一个例子，4匹马赛跑的可能抵达顺序</p>
<pre><code>&gt;&gt;&gt; horses = [1, 2, 3, 4]
&gt;&gt;&gt; races = itertools.permutations(horses)
&gt;&gt;&gt; print(races)
&lt;itertools.permutations object at 0xb754f1dc&gt;
&gt;&gt;&gt; print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
(1, 2, 4, 3),
(1, 3, 2, 4),
(1, 3, 4, 2),
(1, 4, 2, 3),
(1, 4, 3, 2),
(2, 1, 3, 4),
(2, 1, 4, 3),
(2, 3, 1, 4),
(2, 3, 4, 1),
(2, 4, 1, 3),
(2, 4, 3, 1),
(3, 1, 2, 4),
(3, 1, 4, 2),
(3, 2, 1, 4),
(3, 2, 4, 1),
(3, 4, 1, 2),
(3, 4, 2, 1),
(4, 1, 2, 3),
(4, 1, 3, 2),
(4, 2, 1, 3),
(4, 2, 3, 1),
(4, 3, 1, 2),
(4, 3, 2, 1)]
</code></pre>
<h2 id="了解迭代器的内部机制">了解迭代器的内部机制</h2>
<p>迭代过程包含可迭代对象(实现__iter__()方法) 和迭代器(实现__next__()方法)</p>
<p>你可以获取一个迭代器的任何对象都是可迭代对象，迭代器可以让你迭代遍历一个可迭代对象(Iterators are objects that let you iterate on iterables.) [好拗口:]</p>
<p>更多关于这个问题的 <a href="http://effbot.org/zone/python-for-statement.htm">how does the for loop work</a></p>
<p>如果你喜欢这个回答，你也许会喜欢我关于 <a href="http://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python#1594484">decorators</a> 和 <a href="http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python/6581949#6581949">metaclasses</a> 的解释</p>
]]></content>
		</item>
		
		<item>
			<title>Linux Shell脚本攻略笔记[速查]</title>
			<link>https://wklken.me/posts/2013/07/04/note-of-linux-shell-scripting-cookbook.html</link>
			<pubDate>Thu, 04 Jul 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/07/04/note-of-linux-shell-scripting-cookbook.html</guid>
			<description>Linux Shell脚本攻略的笔记，markdown编写，可以速查(ctrl+f) 2013-09-08 从历史网摘中补充 2014-02-16 增加&amp;laquo;shell脚本指南&amp;raqu</description>
			<content type="html"><![CDATA[<p>Linux Shell脚本攻略的笔记，markdown编写，可以速查(ctrl+f)</p>
<p>2013-09-08 从历史网摘中补充
2014-02-16 增加&laquo;shell脚本指南&raquo;笔记补充</p>
<hr>
<h3 id="资源">资源</h3>
<p><a href="http://coolshell.cn/articles/9104.html">sed简明教程</a></p>
<p><a href="http://coolshell.cn/articles/9070.html">awk简明教程</a></p>
<hr>
<h3 id="shell-script">shell script</h3>
<pre><code>#!/bin/bash
# do something
</code></pre>
<h3 id="run-shell-script">run shell script</h3>
<pre><code>sh script.sh

or

chmod a+x script.sh
./script.sh
# 会读取首行的解释器, 执行
</code></pre>
<p>cmd</p>
<pre><code>cmd1; cmd2

or

cmd1
cmd2
</code></pre>
<h3 id="echo">echo</h3>
<p>echo  的功能正如其名，就是基于标准输出打印一段文本</p>
<pre><code>echo &quot;welcome to bash&quot;
echo welcome to bash
</code></pre>
<p>使用不带引号的echo时，无法显示分号</p>
<p>使用单引号echo时，bash不会对单引号中变量求值  &lsquo;$var&rsquo;</p>
<p>echo 中转义换行符</p>
<p>默认情况，echo将换行标志追加到文本尾部，可以忽略结尾换行符</p>
<pre><code>echo -n 'test\n'
</code></pre>
<p>对字符串进行转义</p>
<pre><code>echo -e '1\t2\t3'
</code></pre>
<p>打印彩色输出</p>
<pre><code>文字颜色码
    重置0
    黑色30
    红色31
    绿色32
    黄色33
    蓝色34
    洋红35
    青色36
    白色37

echo -e &quot;\e[1;31m This is red test \e[0m&quot;

背景颜色码
    重置0
    黑色40
    红色41
    绿色42
    黄色43
    蓝色44
    洋红45
    青色46
    白色47

echo -e &quot;\e[1;42m Green Background \e[0m&quot;
</code></pre>
<h3 id="printf">printf</h3>
<p>可以格式化字符串, 使用参数同c中printf一样</p>
<pre><code>printf &quot;hello world&quot;
</code></pre>
<p>默认不会加换行符, 需要手动添加</p>
<pre><code>printf &quot;%-5s %-10s %-4.2f\n&quot; 3 Jeff 77.564

3    Jeff      77.56
</code></pre>
<h3 id="环境变量和变量">环境变量和变量</h3>
<p>bash中，每个变量的值都是字符串，无论你给变量赋值时是否使用引号，值都会以字符串的形式存储</p>
<p>环境变量</p>
<p>查看所有与此终端进程相关的环境变量</p>
<pre><code>env
</code></pre>
<p>查看某个进程的环境变量</p>
<pre><code>cat /proc/$PID/environ
</code></pre>
<p>变量赋值</p>
<pre><code>var=value
var='the value'
var=&quot;the $PARAM&quot;

echo $var
echo ${var}

var = value非变量赋值是相等操作
</code></pre>
<p>环境变量</p>
<pre><code>未在当前进程中定义，而是从父进程中继承而来的变量
export 设置环境变量,之后,从当前shell 执行的任何程序都会继承这个变量

export PYTHONPATH=$PYTHONPATH:/home/ken/workspace
</code></pre>
<p>常用的环境变量</p>
<pre><code>PATH 查找可执行文件路径, 通常定义在/etc/environment or /ect/profile or ~/.bashrc
修改:        export PATH=$PATH:/new/path/
HOME
PWD
USER
UID
SHELL
</code></pre>
<p>获取字符串长度</p>
<pre><code>length=${#var}
</code></pre>
<p>识别当前shell版本</p>
<pre><code>echo $SHELL
    /bin/bash
echo $0
    bash
</code></pre>
<p>检查是否为超级用户 or 普通用户</p>
<pre><code>root的UID=0

if [ $UID -ne 0 ]
then
    echo &quot;not root user&quot;
else
    echo &quot;root&quot;
fi
</code></pre>
<p>修改bash的提示字符</p>
<pre><code>设置PS1变量
\u用户名
\h主机名
\w当前工作目录
</code></pre>
<h3 id="pgrep">pgrep</h3>
<p>获取某个进程名对应进程id</p>
<pre><code>pgrep gedit
</code></pre>
<h3 id="shell数学运算">shell数学运算</h3>
<p>整数运算</p>
<p>let</p>
<pre><code>no1=4
no2=5
let result=no1+no2

let no1++
let no2--
let no1+=7
let no2-=7
</code></pre>
<p>expr(少用)</p>
<pre><code>result=`expr 3 + 4`
result=$(expr $no1 + 5)
</code></pre>
<p>其他方法</p>
<pre><code>result=$[ no1 + no2 ]
result=$[ $no + 5 ]

result=$(( no1 + 5 ))
</code></pre>
<p>浮点数</p>
<pre><code>echo &quot;4 * 0.56&quot; | bc
设定精度
echo &quot;scale=2;3/8&quot; | bc
进制转换
echo &quot;obase=2;100&quot; | bc
平方
echo &quot;10^10&quot; | bc
平方根
echo &quot;sqrt(100)&quot; | bc
</code></pre>
<h3 id="命令状态">命令状态</h3>
<p>当命令成功完成, 返回0</p>
<p>发生错误并退回, 返回非0</p>
<p>可以从$?中获取  cmd; echo $?</p>
<h3 id="文件描述符和重定向">文件描述符和重定向</h3>
<p>文件描述符: 与文件输入/输出相关联的整数, 用来跟踪已打开的文件</p>
<pre><code>0 stdin  标砖输入
1 stdout 标准输出
2 stderr 标准错误
</code></pre>
<p>重定向到文件</p>
<pre><code>清空文件写入新内容
echo &quot;test&quot; &gt; temp.txt
追加
echo &quot;test&quot; &gt;&gt; temp.txt

&gt;等价于1&gt;
&gt;&gt;等价于 1&gt;&gt;
</code></pre>
<p>输出分离或合并</p>
<pre><code>分离
cmd 2&gt;stderr.txt  1&gt;stdout.txt

合并
cmd &gt; output.txt 2&gt;&amp;1
or
cmd &amp;&gt; output.txt
</code></pre>
<p>扔到垃圾桶</p>
<pre><code>/dev/null 特殊设备文件, 接收到的任何数据都会被丢弃(位桶/黑洞)

只有标准错误
cmd 2 &gt; /dev/null

标准输出和标准错误
cmd &gt;/dev/null 2&gt;&amp;1
</code></pre>
<p>同时输出到终端和文件</p>
<pre><code>cmd | tee file1

tee默认覆盖，可以-a选项追加
cmd | tee -a file1
</code></pre>
<p>将stdin作为命令参数</p>
<pre><code>cmd1 | cmd2 | cmd3 -
</code></pre>
<p>将文件重定向到命令</p>
<pre><code>cmd &lt; file
</code></pre>
<p>自定义文件描述符</p>
<pre><code>使用文件描述符3打开并读取文件
exec 3&lt;input.txt
cat &lt;&amp;3

使用文件描述符4进行写入
exec 4&gt;output.txt
echo newline &gt;&amp;4
</code></pre>
<h3 id="cat">cat</h3>
<p>cat, concatenate(拼接)</p>
<p>“cat”代表了连结（Concatenation），连接两个或者更多文本文件或者以标准输出形式打印文件的内容</p>
<p>一般格式</p>
<pre><code>cat file1 file2 file3

从管道中读取
OUTPUT_FROM_SOME_CMDS | cat

echo &quot;test&quot; | cat - file1
</code></pre>
<p>压缩空白行, 多个连续空行变成单个</p>
<pre><code>cat -s  file
</code></pre>
<p>配合tr移除空白行</p>
<pre><code>cat file | tr -s '\n' #连续多个\n -&gt; \n
</code></pre>
<p>加行号</p>
<pre><code>cat -n file
</code></pre>
<p>显示制表符等</p>
<pre><code>cat -T file

cat f &gt; t
注意：“&gt;&gt;”和“&gt;”调用了追加符号。它们用来追加到文件里，而不是显示在标准输出上。
“&gt;”符号会删除已存在的文件，然后创建一个新的文件。
所以因为安全的原因，建议使用“&gt;&gt;”，它会写入到文件中，而不是覆盖或者删除。
</code></pre>
<p>输入多行文字(CTRL + d 退出)</p>
<pre><code>cat &gt; test.txt
</code></pre>
<h3 id="数组和关联数组">数组和关联数组</h3>
<p>普通数组，整数作为数组索引, 借助索引将多个独立的数据存储为一个集合(list)</p>
<p>关联数组，可以使用字符串作为索引(map)</p>
<p>数组</p>
<p>定义</p>
<pre><code>array_var=(1 2 3 4 5)

or
array_var[0]=&quot;test1&quot;
array_var[3]=&quot;test3&quot;
</code></pre>
<p>读取</p>
<pre><code>echo ${array_var[0]}
</code></pre>
<p>以清单形式打印</p>
<pre><code>echo ${array_var[*]}
echo ${array_var[@]}
</code></pre>
<p>长度</p>
<pre><code>echo ${#array_var[*]}
</code></pre>
<p>获取索引列表</p>
<pre><code>echo ${!array_var[*]}
</code></pre>
<p>关联数组</p>
<pre><code>declare -A ass_array

内嵌索引-值
ass_array=([index1]=value1 [index2]=value2)

独立
ass_array[index3]=value3

echo ${ass_array[index1]}
</code></pre>
<h3 id="alias">alias</h3>
<p>alias是一个系统自建的shell命令，允许你为名字比较长的或者经常使用的命令指定别名。</p>
<pre><code>alias new_command='command seq'
unalias new_command

使用原生命令
\new_command
</code></pre>
<h3 id="date">date</h3>
<p>“date”命令使用标准的输出打印当前的日期和时间，也可以深入设置</p>
<p>读取日期</p>
<pre><code>date
</code></pre>
<p>时间戳</p>
<pre><code>date +%s
</code></pre>
<p>日期转换为时间戳</p>
<pre><code>date --date &quot;Thu Nov 18 08:07:21 IST 2010&quot; +%s
</code></pre>
<p>日期格式化</p>
<pre><code>星期  %a  Sat
      %A  Saturday
月    %b  Nov
      %B  November
日    %d  31
固定日期格式mm/dd/yy     %D
年    %y  10
      %Y  2010
小时  %I/%H    08
分钟  %M  33
秒    %S  10
纳秒  %N  696308515
Unix纪元时  %s
</code></pre>
<p>格式化</p>
<pre><code>date &quot;+%Y %B %d&quot;

date +%Y-%m-%d
输出： 2011-07-28
date +&quot;%Y-%m-%d %H:%M:%S&quot;
</code></pre>
<p>设置日期和时间</p>
<pre><code>date -s &quot;格式化日期字符串&quot;

date -s &quot;21 June 2009 11:01:22&quot;
</code></pre>
<p>延时</p>
<pre><code>sleep number_of_seconds
</code></pre>
<p>两天后及两天前</p>
<pre><code>date -d '2 days' +%Y%m%d
date -d '2 days ago' +%Y%m%d
</code></pre>
<p>某一天的几天前</p>
<pre><code>TODAY=`date +%Y%m%d`
DAY_1_AGO=`date -d &quot;$TODAY 1 days ago&quot; +%Y%m%d`
</code></pre>
<p>时间戳日期转换</p>
<pre><code>date -d @1193144433
date -d @1193144433 &quot;+%Y-%m-%d %T&quot;

反向:
date -d &quot;2007-10-23 15:00:23&quot; &quot;+%s&quot;
</code></pre>
<p>赋值给变量</p>
<pre><code>DATE=$(date +%Y%m%d)
DATE=`date +%Y%m%d`
</code></pre>
<h3 id="调试脚本">调试脚本</h3>
<p>打印出所执行的每一行命令</p>
<pre><code>bash -x script.sh
sh -x script.sh
</code></pre>
<p>在脚本中设置开关</p>
<pre><code>set -x 在执行时显示参数和命令
set +x 关闭调试
set -v 当命令进行读取时显示输入
set +v 禁止打印输入
</code></pre>
<p>直接修改脚本</p>
<pre><code>#!/bin/bash -xv
</code></pre>
<h3 id="函数和参数">函数和参数</h3>
<p>定义函数</p>
<pre><code>function fname()
{
    statements;
}
or
fname()
{
    statements;
}
</code></pre>
<p>调用</p>
<pre><code>fname;
传参
fname arg1 arg2;
</code></pre>
<p>接收参数</p>
<pre><code>$1第一个参数
$2第二个参数
$n第n个参数

&quot;$@&quot;被扩展成 &quot;$1&quot; &quot;$2&quot; &quot;$3&quot;
&quot;$*&quot;扩展成&quot;$1c$2c$3&quot;, 其中c是IFS第一个字符

&quot;$@&quot;使用最多, $*将所有的参数当做单个字符串
</code></pre>
<p>bash支持递归</p>
<p>导出函数,可以作用到子进程中</p>
<pre><code>export -f fname
</code></pre>
<p>函数及命令返回值</p>
<pre><code>cmd;
echo $?

退出状态，成功退出，状态为0，否则，非0

cmd
if [ $? -eq 0 ]
then
    echo &quot;success&quot;
else
    echo &quot;fail&quot;
fi
</code></pre>
<h3 id="管道">管道</h3>
<p>前一个命令的输出作为后一个命令的输入</p>
<pre><code>$cmd1 | cmd2 | cmd3
</code></pre>
<h3 id="读取命令输出">读取命令输出</h3>
<pre><code>子shell  subshell
cmd_output=$(COMMANDS)
or
反引用
cmd_output=`COMMANDS`
</code></pre>
<p>子shell本身是独立进程, 不会对当前shell有任何影响</p>
<pre><code>pwd;
(cd /bin; ls)
pwd #同上一个pwd
</code></pre>
<p>保留空格和换行符</p>
<pre><code>out=$(cat text.txt)
echo $out  #丢失所有换行符

out=&quot;$(cat text.txt)&quot;
echo $out  #保留

cat a
1
2
3
echo $(cat a)
1 2 3
echo &quot;$(cat a)&quot;
1
2
3
</code></pre>
<h3 id="read">read</h3>
<p>read, 用于从键盘或标准输入中读取文本</p>
<p>读取n个字符存入变量</p>
<pre><code>read -n number_of_chars variable_name
</code></pre>
<p>不回显的方式读取密码</p>
<pre><code>read -s var
</code></pre>
<p>显示提示信息</p>
<pre><code>read -p &quot;Enter input:&quot; var
</code></pre>
<p>限时输入</p>
<pre><code>read -t  timeout var
</code></pre>
<p>设置界定符</p>
<pre><code>read -d delim_char var
read -d &quot;:&quot; var
hello:
</code></pre>
<h3 id="字段分隔符和迭代器">字段分隔符和迭代器</h3>
<p>内部字段分隔符，Internal Field Separator, IFS</p>
<p>IFS默认为空白字符（换行符，制表符，空格）</p>
<pre><code>data=&quot;name,sex,rollno&quot;
oldIFS=$IFS
IFS=,
for item in $data
do
        echo $item
done

IFS=$oldIFS
</code></pre>
<h3 id="循环">循环</h3>
<p>for循环</p>
<pre><code>echo {1..50}

for i in {a..z}; do actions; done;

or

for((i=0;i&lt;10;i++))
{
    commands;
}
</code></pre>
<p>while循环</p>
<pre><code>while condition
do
    commands;
done
</code></pre>
<p>until循环</p>
<pre><code>until condition
do
    commands;
done
</code></pre>
<h3 id="比较和测试">比较和测试</h3>
<p>if条件</p>
<pre><code>if condition;
then
    commands;
elif condition;
then
    commands;
else
    commands;
fi
</code></pre>
<p>逻辑运算符进行简化, 短路运算更简洁</p>
<pre><code>[ condition ] &amp;&amp; action;
[ condition ] || action;
</code></pre>
<p>算术比较</p>
<pre><code>-gt 大于
-lt 小于
-ge 大于等于
-le 小于等于
-ne 不等于
-eq 等于

注意[]和操作数之间的空格
[ $var -eq 0 ]

and
[ $var -ne 0 -a $var2 -ge 2 ]
or
[ $var -ne 0 -o $var2 -ge 2 ]
</code></pre>
<p>文件测试</p>
<pre><code>[ -f $file_var ] 正常文件路径或文件名
[ -x $var ] 可执行
-d 目录
-e 存在
-c 字符设备文件
-b 块设备文件
-w 可写
-r 可读
-L 符号链接
</code></pre>
<p>字符串比较</p>
<pre><code>[[ $str1 = $str2 ]]
[[ $str1 == $str2 ]]

[[ $str1 != $str2 ]] 不等

[[ $str1 &gt; $str2 ]]
[[ $str1 &lt; $str2 ]]

[[ -z $str1 ]]  空
[[ -n $str1 ]]  非空

if [[ -n $str1 ]] &amp;&amp; [[ -z $str2 ]]
then
    commands;
fi
</code></pre>
<h3 id="find">find</h3>
<p>搜索指定目录下的文件，从开始于父目录，然后搜索子目录</p>
<p>基本</p>
<pre><code>find base_path

# 打印文件和目录列表
find . -print  #默认\n分割文件名
</code></pre>
<p>文件名</p>
<pre><code>find path -name &quot;*.txt&quot; -print
          -iname  忽略大小写

多个条件 or
find . \( -name &quot;*.txt&quot; -o -name &quot;*.py&quot; \)
</code></pre>
<p>文件路径</p>
<pre><code>通配符
find /home/users -path &quot;*slynux*&quot; -print

正则
find . -regex &quot;.*\(\.py\|\.sh\)$&quot;
       -iregex 忽略大小写
</code></pre>
<p>否定参数</p>
<pre><code>find . ! -name &quot;*.txt&quot; -print
</code></pre>
<p>根据文件类型</p>
<pre><code>find . -type d -print
f 普通文件
l 符号链接
d 目录
c 字符设备
b 块设备
s 套接字
p Fifo
</code></pre>
<p>设定目录深度</p>
<pre><code>find . -maxdepth 1 -type f -print
find . -mindepth 2 -type f -print
</code></pre>
<p>根据文件时间搜索</p>
<pre><code>计量单位 天
-atime 最近一次访问时间
-mtime 最后一次被修改时间
-ctime 文件元数据，最近一次修改时间

find . -type f -atime -7 -print #最近七天内被访问的
find . -type f -atime 7 -print  #恰好在七天前
                      +7 -print #超过七天

计量单位 分钟
-amin 访问时间
-mmin 修改时间
-cmin 变化时间

find . -type f -amin +7 -print #访问时间超过7分钟的

find . -type f -newer file.txt -print  #用于比较时间戳的参考文件，比参考文件更新的文件
</code></pre>
<p>基于文件大小的搜索</p>
<pre><code>find . -type f -size +2k
+ 大于  -小于   无符号，恰好等于

b 块
c 字节
w 字（2字节）
k 千字节
M 兆字节
G 吉字节
</code></pre>
<p>删除匹配的文件</p>
<pre><code>find . -type f -name &quot;*.swp&quot; -delete
#注意：-delete位置一定是最后
</code></pre>
<p>文件权限及所有权</p>
<pre><code>find . -type f -perm 644 -print

find . -type f -user slynux -print
</code></pre>
<p>执行命令或动作(最强大的命令)</p>
<pre><code>find . -type f -user root -exec chown slynux {} \;
find . -type f -exec cp {} OLD \;
find . -iname &quot;abc.txt&quot; -exec md5sum {} \;

{} 江北替换成对应文件名
exec无法结合多个命令，可以将多个命令放入脚本，调用之
</code></pre>
<p>跳过指定目录</p>
<pre><code>find . \( -name &quot;.git&quot; -prune \) -name '*.txt'
</code></pre>
<h3 id="xargs">xargs</h3>
<p>将标准输入数据转化成命令行参数</p>
<p>将stdin接收到的数据重新格式化，再将其作为参数传给其他命令</p>
<p>多行输入转化成单行输出</p>
<pre><code>cat  example.txt | xargs  #空格替换掉\n
</code></pre>
<p>切成多行，每行n个参数</p>
<pre><code>cat examplet.txt | xargs -n 3
</code></pre>
<p>可以指定分隔符</p>
<pre><code>echo &quot;aaaXbbbXccc&quot; | xargs -d 'X'
</code></pre>
<p>将参数传递给脚本(类似循环)</p>
<pre><code>cat args.txt | xargs -n 1 ./cecho.sh

./cecho.sh -p arg1 1
需要变更
cat args.txt | xargs -I {} ./cecho.sh -p {} 1
</code></pre>
<p>find与xargs组合</p>
<pre><code>find . -type f -name &quot;*.txt&quot; -print | xargs rm -rf
</code></pre>
<p>其他</p>
<pre><code>cat file | ( while read arg; do cat $arg; done )
cat file | xargs -I {} cat {}
</code></pre>
<h3 id="tr">tr</h3>
<p>tr可以对来自标准输入的字符进行替换，删除以及压缩(translate, 可以将一组字符变成另一组字符)</p>
<p>tr只能通过stdin，无法通过其他命令行进行接收参数</p>
<p>格式</p>
<pre><code>tr [options] source-char-set replace-char-set
</code></pre>
<p>选项</p>
<pre><code>-c 取source-char-set补集，通常与-d/-s配合
-d 删除字source-char-set中的所列的字符
-s 浓缩重复字符，连续多个变成一个
</code></pre>
<p>字符替换</p>
<pre><code>cat /proc/12501/environ | tr '\0' '\n'
</code></pre>
<p>大小写替换</p>
<pre><code>echo  &quot;HELLO&quot; | tr 'A-Z' 'a-z'
cat text | tr '\t' ' '
</code></pre>
<p>删除字符</p>
<pre><code>echo &quot;hello 123 world 456&quot;| tr -d '0-9'
hello  world
</code></pre>
<p>字符集补集</p>
<pre><code>echo &quot;hello 1 char 2&quot; | tr -d -c '0-9'  #删除非0-9
12
</code></pre>
<p>压缩字符</p>
<p>连续的重复字符</p>
<pre><code>echo &quot;GNU is    not UNix&quot; | tr -s ' '
</code></pre>
<p>字符类</p>
<pre><code>alnum 字母和数字
alpha 字母
cntrl 控制字符
digit 数字
graph 图形字符
lower 小写字母
print 可打印字符
punct 标点符号
space 空白字符
upper 大写字母
xdigit 十六进制字符

tr '[:lower:]' '[:upper:]'
</code></pre>
<h3 id="md5sum">md5sum</h3>
<p>32个字符的十六进制串</p>
<pre><code>md5sum filename
md5sum filename1 filename2
</code></pre>
<h3 id="sha1sum">sha1sum</h3>
<p>40个字符十六进制串</p>
<pre><code>sha1sum file
</code></pre>
<h3 id="对目录进行校验">对目录进行校验</h3>
<p>需安装md5deep软件包</p>
<pre><code>md5deep/sha1deep
md5deep -rl dirname
         r递归，l相对路径
</code></pre>
<h3 id="sort">sort</h3>
<p>语法</p>
<pre><code>sort [options] [file(s)]

-c 检查是否已排序
-u 丢弃所有具有相同键值的记录

-b 忽略开头空白
-d 字典序
-g 一般数值，以浮点数类型比较字段，仅支持gnu
-i 忽略无法打印的字符

-k 定义排序键值字段
-n 以整数类型比较字段
-r 倒转
-o 输出到指定文件
</code></pre>
<p>排序</p>
<pre><code>sort file1 &gt; file1.sorted
sort -o file1.sored file1
</code></pre>
<p>按数字, 要明确</p>
<pre><code>sort -n file1
</code></pre>
<p>逆序</p>
<pre><code>sort -r file
</code></pre>
<p>测试一个文件是否已经被排过序</p>
<pre><code>sort -C file
if [ $? -eq 0 ]; then
    echo ssss
fi
</code></pre>
<p>合并两个排过序的文件，并不需要对合并后的文件进行再排序</p>
<pre><code>sort -m sorted1 sorted2
</code></pre>
<p>根据键或者列排序(按照哪一个列)</p>
<pre><code>sort -k 1 data
</code></pre>
<p>限定特定范围内一组字符</p>
<pre><code>key=char4-char8
sort -k 2,3 data

sort -k2.4,5.6 file
第二个字段的第四个字符开始比较，直到第五个字段的第六个字符
</code></pre>
<p>忽略前导空白及字典序排序</p>
<pre><code>sort -bd unsorted.txt
</code></pre>
<p>去重</p>
<pre><code>sort a.txt | uniq
sort -u a.txt
</code></pre>
<h3 id="uniq">uniq</h3>
<p>用法</p>
<pre><code>uniq file
</code></pre>
<p>只显示未重复的记录</p>
<pre><code>uniq -u file
</code></pre>
<p>找出重复的行</p>
<pre><code>uniq -d file
-s 可指定跳过前N个字符
-w 指定用于比较的最大字符数
</code></pre>
<p>统计各行出现的次数</p>
<pre><code>uniq -c file
</code></pre>
<p>p57</p>
<h3 id="tempfile">tempfile</h3>
<p>只有在基于Debian的发布版才有(Ubuntu/Debian)</p>
<pre><code>temp_file=$(tempfile)
等同
temp_file=&quot;/tmp/file-$RANDOM&quot;

#$$为进程id
temp_file=&quot;/tmp/var.$$&quot;
</code></pre>
<h3 id="split">split</h3>
<p>按大小分割文件, 单位k(KB), M, G, c(byte), w(word)</p>
<pre><code>split -b 10k data.file
</code></pre>
<p>-d数字后缀，-a后缀长度</p>
<pre><code>split -b 10k data.file -d -a 4
</code></pre>
<p>分割后指定文件名前缀</p>
<pre><code>split -b 10k data.file file_prefix

设置后缀格式
split -b 10k data.file -d -a 4 file_prefix
</code></pre>
<p>根据行数分割</p>
<pre><code>spilt -l 10 data
</code></pre>
<p>其扩展是csplit，可根据文件特性切分，关注</p>
<h3 id="bash变量匹配切分">bash变量匹配切分</h3>
<p>sample.jpg</p>
<pre><code>file_jpg=&quot;sample.jpg&quot;

从右向左匹配
${file_jpg%.*}
#sample

从左向右匹配
${file_jpg#.*}
#jpg

% # 属于非贪婪
%% ## 属于贪婪
</code></pre>
<p>贪婪非贪婪</p>
<pre><code>var=hack.fun.book.txt
${var%.*} #hack.fun.book
${var%%.*} #hack

${var#.*} #fun.book.txt
${var##.*} #txt
</code></pre>
<h3 id="expect">expect</h3>
<p>实现自动化</p>
<pre><code>spawn ./ineractive.sh
expect &quot;Enter the number&quot;
send &quot;1\n&quot;
expect &quot;Enter name:&quot;
send &quot;hello\n&quot;
expect eof

spawn指定需要自动化的命令
expect提供需要等待的消息
send发送消息
expect eof指明命令交互结束
</code></pre>
<h3 id="dd">dd</h3>
<p>生成任意大小的文件</p>
<pre><code># 创建一个1M大小的文件junk.data
bs=2M count=2 则文件大小4M

dd if=/dev/zero of=junk.data bs=1M count=1
   输入文件     输出文件     块大小   复制块数

块大小单位
字节(1B) c
字(2B)   w
块(512B)   b
千字节(1024B) k
兆字节(1024KB) M
吉字节(1024MB) G
</code></pre>
<h3 id="comm">comm</h3>
<p>两个文件之间比较，输出三列</p>
<pre><code>onleA \t onlyB \t bothAB

comm A B -1 -2 #删除第一第二列
-3 删除第三列

可以得到A^B  A-B B-A
</code></pre>
<h3 id="mkdir">mkdir</h3>
<p>“mkdir”(Make directory)命令在命名路径下创建新的目录。然而如果目录已经存在了，那么它就会返回一个错误信息”不能创建文件夹，文件夹已经存在了”(“cannot create folder, folder already exists”)</p>
<pre><code>mkdir dirpath

mkdir -p dirpath1/dirpath2

#一次多个目录
mkdir -p /home/user/{test,test1,test2}
</code></pre>
<p>注意：目录只能在用户拥有写权限的目录下才能创建</p>
<h3 id="ls">ls</h3>
<p>ls命令是列出目录内容(List Directory Contents)的意思。运行它就是列出文件夹里的内容，可能是文件也可能是文件夹</p>
<p>ls文件的内容关系</p>
<pre><code>- 普通文件
d 目录
c 字符设备
b 块设备
l 符号链接
s 套接字
p 管道

文件权限序列
rwx
rwS  setuid(S)，特殊权限, 出现在x的位置, 允许用户以其拥有者的权限来执行文件, 即使这个可执行文件是由其他用户运行的

目录
r,允许读取目录中文件和子目录列表
w,允许在目录中创建或删除文件或目录
x,指明是否可以访问目录中的文件和子目录
rwt/rwT 粘滞位，只有创建该目录的用户才能删除目录中的文件，即使用户组和其他用户也有写权限，典型例子/tmp, 写保护
</code></pre>
<p>查看目录</p>
<pre><code>ls -d */
ls -F | grep &quot;/$&quot;
ls -l | grep &quot;^d&quot;
find . -type d -maxdepth 1 -print
</code></pre>
<p>其他</p>
<pre><code>ls -l    命令已详情模式(long listing fashion)列出文件夹的内容
ls -a    命令会列出文件夹里的所有内容，包括以”.”开头的隐藏文件
</code></pre>
<h3 id="chmod">chmod</h3>
<p>设置文件权限</p>
<p>“chmod”命令就是改变文件的模式位。chmod会根据要求的模式来改变每个所给的文件，文件夹，脚本等等的文件模式（权限）。</p>
<p>设置权限</p>
<pre><code>user group others all
u    g     o      a

chmod u=rwx g=rw o=r filename

chmod u+x filename
chomd a+x filename #所有

chmod a-x filename

chmod 764 filename

#设置粘滞位
chmod a+t dirname

#递归改变

chmod 777 . -R
</code></pre>
<p>注意：对于系统管理员和用户来说，这个命令是最有用的命令之一了。在多用户环境或者服务器上，对于某个用户，如果设置了文件不可访问，那么这个命令就可以解决，如果设置了错误的权限，那么也就提供了为授权的访问。</p>
<h3 id="chown">chown</h3>
<p>每个文件都属于一个用户组和一个用户“chown”命令用来改变文件的所有权，所以仅仅用来管理和提供文件的用户和用户组授权。</p>
<p>改变所有权</p>
<pre><code>chown user.group filename
</code></pre>
<p>递归</p>
<pre><code>chown -R user.group .
</code></pre>
<p>每次都以其他用户身份执行(允许其他用户以文件所有者的身份来执行)</p>
<pre><code>chomod +s executable_file

chown root.root executable_file
chmod +s executable_file
./executable_file
</code></pre>
<h3 id="chattr">chattr</h3>
<p>创建不可修改文件</p>
<pre><code>chattr +i file
</code></pre>
<p>一旦被设置为不可修改, 任何用户包括超级用户都不能删除该文件, 除非其不可修改的属性被移除</p>
<pre><code>chattr -i file
</code></pre>
<h3 id="touch">touch</h3>
<p>“touch”命令代表了将文件的访问和修改时间更新为当前时间。</p>
<p>touch命令只会在文件不存在的时候才会创建它(空白文件)。如果文件已经存在了，它会更新时间戳，但是并不会改变文件的内容。</p>
<p>空白文件</p>
<pre><code>touch filename

for name {1..100}.txt
do
    touch $name
done
</code></pre>
<p>修改文件访问时间</p>
<pre><code>touch -a &quot;Fri Jun 25 20:50:14 IST 1999&quot; filename
touch -m #修改文件内容的修改时间
</code></pre>
<p>修改文件或目录的时间戳(YYMMDDhhmm)</p>
<pre><code>touch -t 0712250000 file
</code></pre>
<p>注意：touch 可以用来在用户拥有写权限的目录下创建不存在的文件。</p>
<h3 id="ln">ln</h3>
<p>建立软连接</p>
<pre><code>ln -s target symbolic_link_name
</code></pre>
<p>如果目的路径已经存在，而没有指定 -f 标志，ln 命令不会创建新的链接，而是向标准错误写一条诊断消息并继续链接剩下的 SourceFiles。</p>
<p>-f 促使 ln 命令替换掉任何已经存在的目的路径</p>
<h3 id="readlink">readlink</h3>
<p>读取链接对应真是路径</p>
<pre><code>readlink web

 readlink ~/.vim
 /Users/ken/github/k-vim
</code></pre>
<h3 id="file">file</h3>
<p>通过查看文件内容来找出特定类型的文件</p>
<p>打印文件类型信息</p>
<pre><code>file filename
</code></pre>
<p>打印不包含文件名在内</p>
<pre><code>file -b filename
</code></pre>
<p>e.g.</p>
<pre><code>file /etc/passwd
/etc/passwd: ASCII English text

file -b /etc/passwd
ASCII English text
</code></pre>
<h3 id="读文件">读文件</h3>
<pre><code>while read line;
do
    something
done &lt; filename
</code></pre>
<h3 id="diff">diff</h3>
<p>生成文件差异</p>
<p>非一体化</p>
<pre><code>diff version1.txt version2.txt
</code></pre>
<p>一体化, 可读性更好</p>
<pre><code>diff -u version.txt
</code></pre>
<p>使用patch将命令应用于任意一个文件</p>
<pre><code>diff -u version1.txt version2.txt &gt; version.patch
patch -p1 version1.txt &lt; version.patch
</code></pre>
<p>递归作用于目录</p>
<pre><code>diff -Naur directory1 directory2

-N 所有缺失的文件作为空文件
-a 所有文件视为文本文件
-u 一体化输出
-r 递归遍历
</code></pre>
<h3 id="head">head</h3>
<p>前10行打印</p>
<pre><code>head file
</code></pre>
<p>前n行</p>
<pre><code>head -n 4 file
</code></pre>
<p>扣除最后N行之外的所有行</p>
<pre><code>head -n -5 file
</code></pre>
<h3 id="tail">tail</h3>
<p>最后10行</p>
<pre><code>tail file
</code></pre>
<p>打印最后五行</p>
<pre><code>tail -n 5 file
tail -5 file
</code></pre>
<p>扣除前n行</p>
<pre><code>tail -n +(N+1)
</code></pre>
<p>实时动态打印</p>
<pre><code>tail -f growing_file
</code></pre>
<p>当某个给定进程结束后,  tail随之终结</p>
<pre><code>tail -f file --PID $PID
</code></pre>
<h3 id="pushdpopd">pushd/popd</h3>
<p>将当前路径压入栈</p>
<pre><code>pushd
</code></pre>
<p>压入某个路径</p>
<pre><code>pushd /home/ken
</code></pre>
<p>查看当前路径列表</p>
<pre><code>dirs
</code></pre>
<p>切换到某一个</p>
<pre><code>#dirs从左到右编号 0 -
pushd +3
</code></pre>
<p>移除最近压入栈的路径并切换到下一个目录</p>
<pre><code>popd
</code></pre>
<h3 id="cd">cd</h3>
<p>经常使用的“cd”命令代表了改变目录。它在终端中改变工作目录来执行，复制，移动，读，写等等操作</p>
<p>切换到上一目录</p>
<pre><code>cd -
</code></pre>
<p>会到HOME目录</p>
<pre><code>cd
cd ~
</code></pre>
<p>会到上一级目录</p>
<pre><code>cd ..
</code></pre>
<h3 id="wc">wc</h3>
<p>Word Count</p>
<p>统计行数</p>
<pre><code>wc -l file
</code></pre>
<p>统计单词数</p>
<pre><code>wc -w file
</code></pre>
<p>统计字符数</p>
<pre><code>wc -c file
</code></pre>
<p>统计所有</p>
<pre><code>wc file
</code></pre>
<p>统计最长行的长度</p>
<pre><code>wc file -L
</code></pre>
<h3 id="tree">tree</h3>
<p>以图形化的树状结构打印文件和目录的结构，需要自行安装</p>
<pre><code>tree ~/unixfile
</code></pre>
<p>重点标记出匹配某种样式的文件</p>
<pre><code>tree PATH -P &quot;*.sh&quot;
</code></pre>
<p>只标记符合样式之外的文件</p>
<pre><code>tree path -I PATTERN
</code></pre>
<p>同时打印文件和目录大小</p>
<pre><code>tree -h
</code></pre>
<h3 id="grep">grep</h3>
<p>文本搜索工具, 支持正则表达式和通配符</p>
<p>‘grep‘命令搜索指定文件中包含给定字符串或者单词的行</p>
<p>基本用法</p>
<pre><code>grep &quot;match_pattern&quot; file1 file2
</code></pre>
<p>使用颜色重点标记</p>
<pre><code>grep word filename --color=auto
</code></pre>
<p>扩展型使用正则</p>
<pre><code>grep -E &quot;[a-z]+&quot;
egrep &quot;[a-z]+&quot;
</code></pre>
<p>只输出匹配到的文本部分</p>
<pre><code>grep -o word filename
</code></pre>
<p>除匹配行外的所有行</p>
<pre><code>grep -v word filename
</code></pre>
<p>统计匹配行数</p>
<pre><code>grep -c 'text' filename
</code></pre>
<p>打印出包含匹配字符串的行数</p>
<pre><code>grep linux -n filename
</code></pre>
<p>打印样式匹配所位于的字符或字节的偏移</p>
<pre><code>echo &quot;gnu is not unix&quot; | grep -b -o &quot;not&quot;
</code></pre>
<p>搜索多个文件，找出匹配文本位于哪个文件中</p>
<pre><code>grep -l linux file1 file2
取反
grep -L
</code></pre>
<p>递归搜索目录</p>
<pre><code>grep -R &quot;text&quot; dir
</code></pre>
<p>忽略大小写</p>
<pre><code>grep -i &quot;hello&quot; filename
</code></pre>
<p>匹配多个样式</p>
<pre><code>grep -e &quot;pattern1&quot; -e &quot;pattern2&quot; file
</code></pre>
<p>运行匹配脚本</p>
<pre><code>grep -f pattern_file source_file

pattern_file:
hello
cool
</code></pre>
<p>在搜索中包含、排除文件</p>
<pre><code>grep --include *.{c,cpp} word file
</code></pre>
<p>排除</p>
<pre><code>grep --exclude &quot;Readme&quot; filename
--exclude-dir
</code></pre>
<p>静默输出，用于判断(不会产生任何输出)</p>
<pre><code>grep -q word file
if [ $? -eq 0 ]
</code></pre>
<p>打印匹配行之前，之后的行</p>
<pre><code>grep -A 3 之后3行
grep -B 3 之前
grep -C 3 前后
</code></pre>
<p>使用行缓冲</p>
<pre><code>在使用tail -f命令时是可以及时看到文件的变化的，但是如果再加上一个grep命令，可能看到的就不那么及时了，
因为grep命令在buffer写不满时就不输出，可以通过选项  --line-buffered 来搞定，如：

tail -f file.txt | grep something  --line-buffered
</code></pre>
<h3 id="cut">cut</h3>
<p>语法</p>
<pre><code>cut -c list [ file ... ]
cut -f list [ -d delim ] [ file ...]

-c list 以字符为主，作剪切操作
-f list 以字段为主，作剪切操作
</code></pre>
<p>提取字段或列</p>
<pre><code>#第一列
cut -f1 filenam

#第二三列
cut -f2,3 filename
</code></pre>
<p>提取补集</p>
<pre><code>cut -f1 --complement filename
</code></pre>
<p>指定字段分隔符</p>
<pre><code>cut -d &quot;;&quot; -f2 filename
cut -d : -f 1,5 /etc/passwd
</code></pre>
<p>指定字符</p>
<pre><code>-b 字节
-c 字符
-f 字段

cut -c1-5 filename
N-
N-M
-M

ls -l | cut -c 1-10
</code></pre>
<p>指定输出分隔符</p>
<pre><code>cut -c1-3,6-9 --output-delimiter &quot;,&quot;
</code></pre>
<h3 id="join">join</h3>
<p>语法</p>
<pre><code>join [options] file1 file2

选项
-1 field1
-2 field2
-o file.field
-t separator
</code></pre>
<p>例子</p>
<pre><code>join file1 file2
</code></pre>
<h3 id="sed">sed</h3>
<p>sed(Stream editor)流编辑器, 可以配合正则使用, 进行替换等</p>
<p>sed替换语法</p>
<pre><code>sed 's/pattern/replace_string/' file
</code></pre>
<p>将结果直接运用于源文件</p>
<pre><code>-i 用于, 直接修改源文件

替换第一个
sed -i 's/pattern/replace_string/' file

替换第二个
sed -i 's/pattern/replace_string/2' file

替换所有
sed -i 's/pattern/replace_string/g' file

从第N处开始替换
sed -i 's/pattern/replcae_string/2g' file
</code></pre>
<p>移除空白行</p>
<pre><code>sed '/^$/d' file
</code></pre>
<p>已匹配字符串标记</p>
<pre><code>引用匹配到的
sed 's/\w\+/[&amp;]/g' filename
</code></pre>
<p>组合多个表达式</p>
<pre><code>sed 'exp1' | sed 'exp2'
等价
sed 'exp1;exp2'
</code></pre>
<p>使用引用</p>
<pre><code>sed &quot;s/$text/HELLO/&quot;
</code></pre>
<p>子串匹配标记(后向引用，最多9个)</p>
<pre><code>sed 's/\([a-z]\+\)' \([A-Z\]\+\)/\2 \1/' filename
</code></pre>
<p>保存到文件</p>
<pre><code>sed 's/pattern/replacement/' -i outfile
</code></pre>
<p>使用其他分隔符</p>
<pre><code>sed 's#/home/#/tmp/#'
</code></pre>
<h3 id="awk">awk</h3>
<p>基本结构</p>
<pre><code>awk -F '-' 'BEGIN{statements} {statements} END{statements}' file
表达式中单引号可以换成双引号
BEGIN -&gt; 每一行，执行statements, 执行END
</code></pre>
<p>打印某一列</p>
<pre><code>awk -F '-' '{print $0}' file #全部
awk -F '-' '{print $2}' file #第二列
</code></pre>
<p>print拼接字符</p>
<pre><code>awk '{var=&quot;v1&quot;; var1=&quot;v2&quot;; print var1&quot;-&quot;var2;}'
</code></pre>
<p>特殊变量</p>
<pre><code>NR nuber of records, 记录数
NF number of fields, 字段数
$0 当前行文本
$1 第一字段
$2 第二字段
$NF 最后一个字段

FILENAME 当前输入文件的名称
FNR 当前输入文件记录数
FS 字段分隔字符
OFS 输出字段分隔符，默认&quot; &quot;
ORS 输出记录分隔符，默认&quot;\n&quot;
</code></pre>
<p>统计行数</p>
<pre><code>awk 'END{print NF}'
</code></pre>
<p>将外部变量值传递给awk</p>
<pre><code>awk -v VARIABLE=$VAR '{ print VARIABLE }'
awk '{print v1, v2}' v1=$var1 v2=$var2
</code></pre>
<p>读取行</p>
<pre><code>seq 5 | awk '{ getline var; print var}'
</code></pre>
<p>进行行过滤</p>
<pre><code>awk 'NR&lt;5' #行号小于5
awk 'NR==1,NR==4' #行号在1到5之间
awk '/linux/' #包含样式linux
awk '!/linux/' #不包含
awk '$1 ~/jones/' #第一个字段包含jones

tail file
awk 'NR &lt;= 10' file
</code></pre>
<p>设定分隔符</p>
<pre><code>awk -F: '{ print $NF }' file
</code></pre>
<p>设定输出分隔符</p>
<pre><code>awk -F: -v &quot;OFS=-&quot; '{print $1,$2}' /etc/passwd
</code></pre>
<p>打印空行</p>
<pre><code>awk 'NF&gt;0 {print $0}'
or
awk 'NF&gt;0' #未指定action默认打印
</code></pre>
<p>print和printf</p>
<pre><code>awk -F: '{print &quot;User&quot;, $1, &quot;is really&quot;, $5}' /etc/passwd
awk -F: '{printf &quot;User %s is really %s\n&quot;, $1, $5}' /etc/passwd
</code></pre>
<p>awk中使用循环</p>
<pre><code>for(i=0;i&lt;10;i++) { print $i; }

for(i in array) { print array[i] }
</code></pre>
<p>内建函数</p>
<pre><code>length(str)
index(str,search_str)
split(str,array,delimiter) 用界定符生成一个字符串列表
substr(string, start, end) #子串
sub(regex, replacement_str, str) #正则替换首个匹配位置
gsub(regex, replacement_str, string) #最后一个匹配位置
match(string, regex) #检查是否能够匹配字符串
tolower(string) #转小写
toupper(string) #转大写
</code></pre>
<p>写成脚本文件</p>
<pre><code>BEGIN {}
pattern1 {action1}
pattern2 {action2}
END {}
</code></pre>
<h3 id="文件迭代">文件迭代</h3>
<p>读文件行</p>
<pre><code>while read line;
do
    echo $line;
done &lt; file.txt
</code></pre>
<p>迭代每个单词</p>
<pre><code>for word in $line;
do
    echo $word;
done
</code></pre>
<p>迭代每一个字符</p>
<pre><code>for((i=0;i&lt;${#word};i++))
do
    echo ${word:i:1} ;
done
</code></pre>
<h3 id="paste">paste</h3>
<p>按列合并文件</p>
<pre><code>paste file1 file2 file3
</code></pre>
<p>指定分隔符</p>
<pre><code>paste file1 file2 -d ','
</code></pre>
<h3 id="tac">tac</h3>
<p>逆序打印</p>
<pre><code>tac file1 file2
</code></pre>
<h3 id="rev">rev</h3>
<p>接收一个文件或stdin作为输入, 逆序打印每一行内容</p>
<pre><code>echo &quot;abc&quot; | rev
</code></pre>
<h3 id="wget">wget</h3>
<p>Wget是用于非交互式（例如后台）下载文件的免费工具.支持HTTP, HTTPS, FTP协议和 HTTP 代理(选项多, 用法灵活)</p>
<p>一个用于文件下载的命令行工具</p>
<pre><code>wget URL1 URL2
</code></pre>
<p>指定保存文件名</p>
<pre><code>wget URL -O local.txt
</code></pre>
<p>指定日志，默认达到stdout</p>
<pre><code>wget URL -O local.txt -o log.txt
</code></pre>
<p>指定重复尝试次数</p>
<pre><code>wget -t 5 URL
</code></pre>
<p>下载限速</p>
<pre><code>wget --limit-rate 20k url
</code></pre>
<p>指定限额</p>
<pre><code>wget -Q 100m url
</code></pre>
<p>断点续传</p>
<pre><code>wget -c URL

$ wget -c -t 100 -T 120 http://www.linux.com/xxxx.data

当文件特别大或者网络特别慢的时候，往往一个文件还没有下载完，连接就已经被切断，此时就需要断点续传。
wget的断点续传是自动的。
-c 选项的作用为断点续传。
-t 参数表示重试次数(例如重试100次，-t 100，如果设成-t 0，表示无穷次重试，直到连接成功)
-T 参数表示超时等待时间，例如-T 120，表示等待120秒连接不上就算超时
</code></pre>
<p>复制或镜像整个网站</p>
<pre><code>wget --mirror exampledomain.com
wget -r -N -l DEPTH URL
     递归，允许对文件使用时间戳，层级
$ wget -r -np -nd http://www.linux.com/packs/

-np 的作用是不遍历父目录
-nd 表示不在本机重新创建目录结构
</code></pre>
<p>访问需要认证的HTTP/FTP</p>
<pre><code>wget --user username --password pass URL
</code></pre>
<p>post请求</p>
<pre><code>wget url -post-data &quot;name=value&quot; -O output.html
</code></pre>
<p>批量下载</p>
<pre><code>wget -i downloads.txt #将文件地址写入一个文件
</code></pre>
<p>用wget命令执行ftp下载</p>
<pre><code>wget -m ftp://username:password@hostname
</code></pre>
<h3 id="curl">curl</h3>
<p>基本用法</p>
<pre><code>curl url &gt; index.html
</code></pre>
<p>不显示进度信息</p>
<pre><code>curl URL --slient
</code></pre>
<p>将内容写入文件，而非标准输出</p>
<pre><code>curl URL --slient -O
</code></pre>
<p>写入指定文件</p>
<pre><code>curl URL --slient -o filename
</code></pre>
<p>显示进度条</p>
<pre><code>curl url -o index.html --progress
</code></pre>
<p>断点续传</p>
<pre><code>curl -C - URL
</code></pre>
<p>设置参照页字符串</p>
<pre><code>curl --referer Referer_URL target_URL
跳转到target_URL,其头部referer为Referer_URL
</code></pre>
<p>设置cookie</p>
<pre><code>curl url --cookie &quot;user=slynux;pass=hack&quot;
另存为一个文件

curl URL --cookie-jar cookie_file
</code></pre>
<p>设置用户代理</p>
<pre><code>curl URL --user-agent &quot;Mozilla/5.0&quot;
头部信息
curl -H &quot;Host: www.slynux.org&quot; -H &quot;Accept-language: en&quot; url
</code></pre>
<p>限定下载带宽</p>
<pre><code>curl url --limit-rate 20k
</code></pre>
<p>指定最大下载量(可下载的最大文件大小)</p>
<pre><code>curl url --max-filesize bytes
超出限制的话，返回非0
</code></pre>
<p>进行认证</p>
<pre><code>curl -u user:pass url
</code></pre>
<p>只打印头部信息,不下载远程文件</p>
<pre><code>curl -I url
curl -head url
</code></pre>
<p>发送post请求</p>
<pre><code>curl URL -d &quot;va1=1&amp;va2=2&quot;
         --data
</code></pre>
<h3 id="lynx">lynx</h3>
<p>将网页以ascii字符形式下载</p>
<pre><code>lynx -dump URL &gt; webpage_as_text.txt
</code></pre>
<p>打印出网站的文本板块而非html</p>
<pre><code>lynx -dump url
</code></pre>
<p>生成信息文件</p>
<pre><code>lynx -traversal url
</code></pre>
<h3 id="tar">tar</h3>
<p>“tar”命令是磁带归档(Tape Archive)，对创建一些文件的的归档和它们的解压很有用。</p>
<p>将多个文件和文件夹保存成单个文件, 同时还能保留所有的文件属性</p>
<p>对文件进行归档</p>
<pre><code>-c create file,创建文件
-f specify filename,指定文件名

tar -cf output.tar file1 file2 file3
tar -cf output.tar *.txt

tar -cvf output.tar *.txt
</code></pre>
<p>向归档中追加文件</p>
<pre><code>tar -rvf original.tar new_file
-r,追加
</code></pre>
<p>查看过程中更多信息</p>
<pre><code>tar -tvvf archive.tar
-v/-vv, verbose
</code></pre>
<p>提取文件或文件夹</p>
<pre><code>-x, exact
tar -xf archive.tar

-C,指定文件
tar -xf archive.tar -C /path/to/extraction_directory


tar -xvf archive.tar
</code></pre>
<p>提取指定文件</p>
<pre><code>tar -xvf file.tar file1 file4
</code></pre>
<p>拼接两个归档文件</p>
<pre><code>tar -Af file1.tar file2.tar
#file2合并到file1中
</code></pre>
<p>只有在文件内容修改时间更新(newer),才进行添加</p>
<pre><code>tar -uvvf archive.tar filea
</code></pre>
<p>比较归档文件与文件系统中的内容</p>
<pre><code>tar -df archive.tar filename1 filename2
</code></pre>
<p>从归档文件中删除文件</p>
<pre><code>tar -f archive.tar --delete file1 file2
</code></pre>
<p>提取到某个目录</p>
<pre><code> tar zxvf package.tar.gz -C new_dir
</code></pre>
<p>压缩归档文件</p>
<pre><code>gzip/gunzip -&gt; .gz
f.tar.gz   -z
tar -czvf 
tar -xzvf 

bzip/bunzip -&gt; .bz2
f.tar.bz2  -j

f.tar.lzma --lzma
f.tar.lzo
</code></pre>
<p>从归档中排除部分文件</p>
<pre><code>tar -cf arch.tar * --exclude &quot;*.txt&quot;
cat list
   filea
   fileb
tar -cf arch.tar * -X list
</code></pre>
<p>排除版本控制文件</p>
<pre><code>tar --exclude-vcs -czvvf source.tar.gz files
</code></pre>
<p>打印总字节数</p>
<pre><code>tar -cf arc.tar * --exclude &quot;*.txt&quot; --totals
</code></pre>
<h3 id="cpio">cpio</h3>
<p>使用频率不高</p>
<p>归档，保留文件属性（权限、所有权等）</p>
<pre><code>echo file1 file2 | cpio -ov &gt; archive.cpio
-o 指定输出
-v 打印归档文件列表
</code></pre>
<p>列出cpio中的文件内容</p>
<pre><code>cpio -it &lt; archive.cpio
-i指定输入
-t列出归档文件中的内容
</code></pre>
<h3 id="gzip">gzip</h3>
<p>压缩，会删除源文件</p>
<pre><code>gzip filename
#got filename.gz
</code></pre>
<p>解压</p>
<pre><code>gunzip filename.gz
</code></pre>
<p>列出文件属性信息</p>
<pre><code>gzip -l text.gz
</code></pre>
<p>stdin读入文件并写出到stdout</p>
<pre><code>cat file | gzip -c &gt; file.gz
</code></pre>
<p>压缩归档文件</p>
<pre><code>tar -czvvf archive.tar.gz [files]
or
tar -cvvf archive.tar.gz [files]
gzip archive.tar
</code></pre>
<p>指定压缩率</p>
<pre><code>1-9,1最低，但速度最快
gzip -9 test.img
</code></pre>
<h3 id="zcat">zcat</h3>
<p>无需解压缩，直接从.gz中提取内容</p>
<pre><code>zcat test.gz
</code></pre>
<h3 id="bzip">bzip</h3>
<p>更大的压缩率</p>
<pre><code>bzip2 filename
</code></pre>
<p>解压缩</p>
<pre><code>bunzip2 filename.bz2
</code></pre>
<p>stdin到stdout</p>
<pre><code>cat file &gt; bzip2 -c &gt; file.tar.bz2
</code></pre>
<p>压缩归档</p>
<pre><code>tar -cjvvf archive.tar.bz2 [files]
or
tar -cvvf archive.tar [files]
bzip2 archive.tar
</code></pre>
<p>保留输入文件</p>
<pre><code>bunzip2 test.bz2 -k
</code></pre>
<p>压缩率</p>
<pre><code>bzip2 -9 test.img
</code></pre>
<h3 id="lzma">lzma</h3>
<p>比gzip/bzip2更好的压缩率</p>
<p>压缩</p>
<pre><code>lzma filename
</code></pre>
<p>解压</p>
<pre><code>unlzma filename.lzma
</code></pre>
<p>stdin到stdout</p>
<pre><code>cat file | lzma -c &gt; file.lzma
</code></pre>
<p>创建归档</p>
<pre><code>tar -cavvf archive.tar.lzma [files]
    -xavf
</code></pre>
<p>保留输入文件</p>
<pre><code>lzma test.bz2 -k
</code></pre>
<p>压缩率</p>
<pre><code>lzma -9 test.img
</code></pre>
<h3 id="zip">zip</h3>
<p>压缩</p>
<pre><code>zip archive_name.zip [source files/dirs]
</code></pre>
<p>对目录和文件进行递归操作</p>
<pre><code>zip -r archive.zip folder1 file2
</code></pre>
<h3 id="base64">base64</h3>
<p>编码</p>
<pre><code>base64 filename &gt; outfile
cat file | base64 &gt; outfile
</code></pre>
<p>解码</p>
<pre><code>base64 -d file &gt; outfile
</code></pre>
<h3 id="md5sum-1">md5sum</h3>
<p>“md5sum”就是计算和检验MD5信息签名。
md5 checksum(通常叫做哈希)使用匹配或者验证文件的文件的完整性，因为文件可能因为传输错误，磁盘错误或者无恶意的干扰等原因而发生改变。</p>
<p>单向散列</p>
<pre><code>md5sum file
sha1sum file
</code></pre>
<h3 id="rsync">rsync</h3>
<p>可以对位于不同位置的文件和目录进行备份, 借助差异计算和压缩技术实现最小化数据传输量</p>
<p>要确保远端安装了 openssh</p>
<p>从一个目录复制到另一个目录</p>
<pre><code>rsync -av source_path dest_path
-a 进行归档  -v打印细节
路径可以使本地，也可以是远端路径

e.g.
rsync -av /home/test /home/backups/ #复制到backups目录下
rsync -av /home/test /home/backups  #创建backups目录, 复制
</code></pre>
<p>备份到远程服务器</p>
<pre><code>rsync -av source_path user@host:PATH
可以反向
</code></pre>
<p>改善传输速度</p>
<pre><code>rsync -avz source destination
</code></pre>
<p>排除文件</p>
<pre><code>rsync -avz source dest --exclude &quot;*.txt&quot;
                       --exclude-from FILEPATH
FILEPATH:
*.bak
</code></pre>
<p>更新备份时，删除不存在的文件</p>
<pre><code>rsync -avz source dest --delete
</code></pre>
<h3 id="git">git</h3>
<p>初始化目录</p>
<pre><code>git init
</code></pre>
<p>配置用户信息</p>
<pre><code>git config --global user.name &quot;wklken&quot;
git config --global user.email &quot;wklken@yeah.net&quot;
</code></pre>
<p>加到远端</p>
<pre><code>git remote add origin user@remotehost:/home/backup/backup.git
git push origin master
</code></pre>
<p>添加</p>
<pre><code>git add *
</code></pre>
<p>删除</p>
<pre><code>git rm *.py
</code></pre>
<p>标记一个检查点</p>
<pre><code>git commit -m &quot;Commit message&quot;
</code></pre>
<p>查看日志</p>
<pre><code>git log
</code></pre>
<p>回滚到某个版本</p>
<pre><code>git checkout hashid [ filename ]
</code></pre>
<p>克隆</p>
<pre><code>git clone url
</code></pre>
<h3 id="dd-1">dd</h3>
<p>Dtat Definiton, 要注意参数顺序, 错误的参数会损毁所有数据</p>
<p>可以用来转换和复制文件，大多数时间是用来复制iso文件(或任何其它文件)到一个usb设备(或任何其它地方)中去，所以可以用来制作USB启动器</p>
<p>语法说明</p>
<pre><code>dd if=SOURCE of=TARGET bs=BLOCK_SIZE count=COUNT
if/of  输入/输出文件或设备路径
bs块大小
count 限制复制到目标的字节数

dd if=/dev/zero of=/dev/sda1

#制作iso 从cdrom设备读取所有数据, 创建iso文件
dd if=/dev/cdrom of=cdrom.iso
</code></pre>
<p>备份恢复</p>
<pre><code>dd if=/dev/sda1 of=x.img

dd if=x.img of=/dev/sda1
</code></pre>
<h3 id="mount">mount</h3>
<p>mount 是一个很重要的命令，用来挂载不能自动挂载的文件系统。你需要root权限挂载设备。
在插入你的文件系统后，</p>
<pre><code>mount --bind /source /destination

首先运行”lsblk”命令，识别出你的设备，然后把分配的设备名记下来。
root@tecmint:~# lsblk
创建一个任何名字的目录，但是最好和引用相关。

root@tecmint:~# su
Password:
root@tecmint:~# cd /dev
root@tecmint:~# mkdir usb
现在将“sdb1”文件系统挂载到“usb”目录.
root@tecmint:~# mount /dev/sdb1 /dev/usb
</code></pre>
<p>挂载镜像</p>
<pre><code>mount -o loop file.img /mnt/mount_point
</code></pre>
<p>##网络相关</p>
<h3 id="ifconfig">ifconfig</h3>
<p>显示网络接口、子网掩码等详细信息</p>
<pre><code>ifconfig
/sbin/ifconfig
</code></pre>
<p>打印某个特定网络接口</p>
<pre><code>ifconfig iface_name

e.g.
ifconfig en1

HWaddr     MAC地址
inet addr  ip地址
Bcast      广播地址
Mask       子网掩码
</code></pre>
<p>设置网络接口ip</p>
<pre><code>ifconfig wlan0 192.168.0.80
</code></pre>
<p>dns</p>
<pre><code>cat /etc/resolv.conf

host google.com #Dns查找

nslookup google.com #更详细信息
</code></pre>
<p>修改dns/host</p>
<pre><code>echo nameserver IP_ADDRESS &gt;&gt; /etc/resolv.conf

echo ip domain &gt;&gt; /etc/hosts
</code></pre>
<p>ping</p>
<pre><code>ping www.baidu.com
</code></pre>
<p>路由信息</p>
<pre><code>显示路由表
route

以数字形式显示地址
route -n
</code></pre>
<p>设置默认网关</p>
<pre><code>route add default gw 192.168.0.1 wlan0
</code></pre>
<p>trace_route, 显示分组途径的所有网关的地址</p>
<pre><code>traceroute google.com
</code></pre>
<h3 id="ping">ping</h3>
<p>基本</p>
<pre><code>ping ADDRESS  #主机名，域名或ip
</code></pre>
<p>PING命令可以得到RTT(Round Trip Time), 分组从源到目的主机的往返时间, 单位ms</p>
<p>限制发送分组数</p>
<pre><code>ping ADDRESS -c COUNT

ping 
</code></pre>
<h3 id="fping">fping</h3>
<p>同时ping一组ip, 而且响应非常快</p>
<pre><code>fping -a ip1 ip2 -g
fping -a 192.160.1/24 -g
fping -a &lt; ip.list

-a, 所有活动主机的ip
-g, 从IP/mask生成的ip地址范围
</code></pre>
<p>进行dns查询</p>
<pre><code>fping -a -d 2 &gt; /dev/null  &lt; ip.list
</code></pre>
<h3 id="lftp">lftp</h3>
<p>基本用法</p>
<pre><code>lftp username@ftphost
cd dir
lcd改变本地主机目录
mkdir 创建目录
get/put 下载上传
quit退出
</code></pre>
<h3 id="scp">scp</h3>
<p>scp是 secure copy的缩写, scp是linux系统下基于ssh登陆进行安全的远程文件拷贝命令。</p>
<p>linux的scp命令可以在linux服务器之间复制文件和目录.</p>
<p>拷贝文件</p>
<pre><code>scp filename user@remotehost:/home/pat
ip或主机名均可

scp SOURCE DESTINATION
</code></pre>
<p>递归复制</p>
<pre><code>scp -r dir1 user@remotehost:/home/backup
</code></pre>
<p>提高拷贝速度</p>
<pre><code>scp  -c arcfour -r -P20755 dir/ 192.168.2.*:/**/**/data/
-c arcfour 这个算法没有加校验不保证完整性，注意慎用，内网1000M带宽，默认算法速度只能达到30M/s，用arcfour这个算法速度可以达到50-80M/s
</code></pre>
<h3 id="ssh">SSH</h3>
<p>连接远程</p>
<pre><code>ssh username@remote_host

ssh -p port username@remote_host
</code></pre>
<p>执行命令</p>
<pre><code>ssh username@remote_host 'cmd1; cmd2' &gt; stdout.txt 2&gt;errors.txt
</code></pre>
<p>压缩功能</p>
<pre><code>ssh -C user@hostname 'cmds'
</code></pre>
<p>打通ssh</p>
<pre><code>1.创建SSH密钥
  ssh-keygen -t rsa
  公钥, ~/.ssh/id_rsa.pub
2.登陆远端服务器, 将公钥写入 ~/.ssh/authorized_keys
</code></pre>
<h3 id="lsof">lsof</h3>
<p>列出系统中开放端口及运行在端口上的服务</p>
<pre><code>lsof -i
</code></pre>
<p>配合grep, 获取需要的信息</p>
<h3 id="netstat">netstat</h3>
<p>查看开放端口和服务</p>
<pre><code>netstat -tnp
</code></pre>
<p>##磁盘和系统</p>
<h3 id="du">du</h3>
<p>du = disk usage</p>
<p>估计文件的空间占用。 逐层统计文件（例如以递归方式）并输出摘要。</p>
<p>查看占用磁盘空间</p>
<pre><code>du FILENAME1 FILENAME2
</code></pre>
<p>查看目录</p>
<pre><code>du -a dir
</code></pre>
<p>以KB,MB或块为单位展示</p>
<pre><code>du -h FILENAME1
</code></pre>
<p>显示总计情况</p>
<pre><code>du -c FILENAME1
</code></pre>
<p>只显示合计</p>
<pre><code>du -s FILENAME1
</code></pre>
<p>以特定单位打印</p>
<pre><code>du -b/-k/-m/-B FILES
</code></pre>
<p>排除部分文件</p>
<pre><code>du --exclude &quot;*.txt&quot; DIR
   --exclude-from EXCLUDE.txt DIR
</code></pre>
<p>指定最深层级</p>
<pre><code>du --max-depth 2 DIR
</code></pre>
<p>指定目录最大的10个文件</p>
<pre><code>du -ak S_DIR | sort -nrk 1 | head
</code></pre>
<h3 id="df">df</h3>
<p>df = disk free</p>
<p>报告系统的磁盘使用情况。在跟踪磁盘使用情况方面对于普通用户和系统管理员都很有用。 ‘df‘ 通过检查目录大小工作，但这一数值仅当文件关闭时才得到更新。</p>
<p>查看磁盘可用空间</p>
<pre><code>df
df -h
</code></pre>
<h3 id="time">time</h3>
<p>计算命令执行时间</p>
<pre><code>time COMMAND

real 挂钟时间, 从开始执行到结束的时间
user 进程花费在用户模式中的cpu时间, 真正用于执行进程所花得时间
sys  进程花费在内核模式中的cpu时间
</code></pre>
<p>写入文件</p>
<pre><code>time -o output.txt COMMAND
time -a output.txt COMMAND #追加
</code></pre>
<p>格式化输出</p>
<pre><code>time -f &quot;Time: %U&quot;  -a -o timing.log uname
real %e
user %U
sys %S
</code></pre>
<h3 id="who">who</h3>
<p>获取当前用户登陆信息</p>
<pre><code>who / w
</code></pre>
<p>当前登陆主机的用户列表</p>
<pre><code>users
</code></pre>
<h3 id="uptime">uptime</h3>
<p>查看系统已经通电运行多长时间了</p>
<pre><code>uptime
#也可以看到负载
</code></pre>
<h3 id="last">last</h3>
<p>显示上次用户登录信息- 前一次启动会话信息</p>
<pre><code>last
</code></pre>
<p>获取单个用户</p>
<pre><code>last USER
</code></pre>
<h3 id="watch">watch</h3>
<p>在终端中以固定间隔监视命令输出</p>
<pre><code>#default 2s
watch ls

# 5s
watch -n 5 ls
</code></pre>
<p>颜色标示</p>
<pre><code>watch -d 'COMMAND'
</code></pre>
<h2 id="进程和线程">进程和线程</h2>
<h3 id="ps">ps</h3>
<p>ps命令给出正在运行的某个进程的状态，每个进程有特定的id成为PID。</p>
<p>ps命令主要查看系统中进程的状态</p>
<pre><code>USER              PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
USER表示启动进程用户
PID表示进程标志号

%CPU表示运行该进程占用CPU的时间与该进程总的运行时间的比例
%MEM表示该进程占用内存和总内存的比例。

VSZ表示占用的虚拟内存大小，以KB为单位。
RSS为进程占用的物理内存值，以KB为单位。

TTY表示该进程建立时所对应的终端，&quot;?&quot;表示该进程不占用终端。
STAT表示进程的运行状态，包括以下几种代码：
    D，不可中断的睡眠；
    R，就绪（在可运行队列中）；
    S，睡眠；
    T，被跟踪或停止；
    Z，终止（僵死）的进程，Z不存在，但暂时无法消除；
    W，没有足够的内存分页可分配；&lt;高优先序的进程；
    N，低优先序的进程；
    L，有内存分页分配并锁在内存体内（实时系统或I/O）。

START为进程开始时间。
TIME为执行的时间。
COMMAND是对应的命令名。
</code></pre>
<p>查看进程信息</p>
<pre><code>#当前终端
ps

PID TTY TIME CMD
PID  进程ID
TTY  终端
TIME 进程启动后过去的时间
CMD  进程对应的命令
</code></pre>
<p>显示更多信息</p>
<pre><code>#当前终端
ps -f
</code></pre>
<p>查看所有进程</p>
<pre><code>ps aux
ps -ef
</code></pre>
<p>查看某个用户的所有进程</p>
<pre><code>ps U ken
</code></pre>
<p>命令格式</p>
<pre><code>ps [OTHER OPTIONS] -o par1,par2,par3
ps -eo comm,pcpu | head
pmem 内存使用率，comm可执行文件名,user启动进程的用户,etime启动后度过的时间
</code></pre>
<p>设置升序降序</p>
<pre><code>ps -eo comm,pcpu --sort -pcpu | head
+升序，-降序
</code></pre>
<p>找出给定命令名对应进程ID</p>
<pre><code>ps -C COMMAND_NAME
ps -C bash -o pid=
</code></pre>
<p>进程线程相关</p>
<pre><code>ps -eLf --sort -nlwp | head
</code></pre>
<p>查看子进程树</p>
<pre><code>ps axwef
</code></pre>
<p>注意：当你要知道有哪些进程在运行或者需要知道想杀死的进程PID时ps命令很管用。你可以把它与‘grep‘合用来查询指定的输出结果，例如：</p>
<pre><code># ps -A | grep -i ssh
</code></pre>
<h3 id="pgrep-1">pgrep</h3>
<p>pgrep只需要命令名的一部分, ps需要准确的全名</p>
<p>基本用法</p>
<pre><code>pgrep bash
</code></pre>
<p>指定进程的用户</p>
<pre><code>pgrep -u root,slynux COMMAND
</code></pre>
<p>返回匹配进程数</p>
<pre><code>pgrep -c COMANND
</code></pre>
<h3 id="top">top</h3>
<p>查看占用cpu最多的进程列表</p>
<pre><code>top
</code></pre>
<h3 id="kill">kill</h3>
<p>kill是用来杀死已经无关紧要或者没有响应的进程,杀死一个进程需要知道进程的PID</p>
<p>列出可用信号</p>
<pre><code>kill -l
</code></pre>
<p>终止一个进程</p>
<pre><code>kill PROCESS_ID_LIST
</code></pre>
<p>强杀进程</p>
<pre><code>kill -9 PROCESS_ID
</code></pre>
<p>杀死一组命令</p>
<pre><code>killall process_name
killall -9 process_name

指定用户
killall -u USERNAME process_name
</code></pre>
<h3 id="pkill">pkill</h3>
<p>杀，接受进程名</p>
<pre><code>pkill process_name
pkill -s SIGNAL process_name
</code></pre>
<h3 id="which">which</h3>
<p>查找PATH下某个命令位置</p>
<pre><code>which ls
</code></pre>
<h3 id="whereis">whereis</h3>
<p>whereis的作用是用来定位命令的二进制文件\资源\或者帮助页.举例来说,获得ls和kill命令的二进制文件/资源以及帮助页:</p>
<pre><code>whereis ls
whereis kill
</code></pre>
<p>类似which，多了命令手册位置，源代码位置</p>
<p>注意:当需要知道二进制文件保存位置时有用.</p>
<h3 id="file-1">file</h3>
<p>确定文件类型</p>
<h3 id="whatis">whatis</h3>
<p>对命令的简短描述</p>
<h3 id="hostname">hostname</h3>
<p>当前主机名</p>
<h3 id="uname">uname</h3>
<pre><code>主机名
uname -n

#内核版本，硬件架构等
uname -a

#内核发行版本
uname -r

主机类型(32位/64位)
uname -m

cpu相关信息
cat /proc/cpuinfo

内存信息
cat /proc/meminfo
</code></pre>
<p>例子</p>
<pre><code>#uname -a
Linux tecmint 3.8.0-19-generic #30-Ubuntu SMP Wed May 1 16:36:13 UTC 2013 i686 i686 i686 GNU/Linux

1. “Linux“: 机器的内核名
2. “tecmint“: 机器的分支名
3. “3.8.0-19-generic“: 内核发布版本
4. “#30-Ubuntu SMP“: 内核版本
5. “i686“: 处理器架构
6. “GNU/Linux“: 操作系统名
</code></pre>
<h3 id="crontab">crontab</h3>
<p>格式</p>
<pre><code>* * * * * cmd
分钟(0-59)，小时(0-23)，天(1-31)，月份(1-12)，工作日(0-6)

A,B  A and B
*/C  every C
</code></pre>
<p>查看</p>
<pre><code>crontab -l
crontab -l -u slynux
</code></pre>
<p>编辑</p>
<pre><code>crontab -e
</code></pre>
<p>移除</p>
<pre><code>crontab -r
crontab -u slynux -r
</code></pre>
<p>可以在crontab 中加入环境变量</p>
<h3 id="getopts">getopts</h3>
<p>命令行参数处理</p>
<pre><code>while getopts :f:vql opt
do
    case $opt in
    f)  file=$OPTARG
        ;;
    v)  verbose=true
        ;;
    ....
</code></pre>
<h3 id="history">history</h3>
<p>“history”命令就是历史记录。它显示了在终端中所执行过的所有命令的历史</p>
<pre><code>history
</code></pre>
<p>注意：按住“CTRL + R”就可以搜索已经执行过的命令，它可以你写命令时自动补全</p>
<h3 id="sudo">sudo</h3>
<p>“sudo”(super user do)命令允许授权用户执行超级用户或者其它用户的命令。通过在sudoers列表的安全策略来指定。</p>
<p>注意：sudo 允许用户借用超级用户的权限，然而”su”命令实际上是允许用户以超级用户登录。所以sudo比su更安全。
并不建议使用sudo或者su来处理日常用途，因为它可能导致严重的错误如果你意外的做错了事，这就是为什么在linux社区流行一句话：</p>
<p>“To err is human, but to really foul up everything, you need root password.”
“人非圣贤孰能无过，但是拥有root密码就真的万劫不复了。”</p>
<h3 id="cal">cal</h3>
<p>“cal”（Calender），它用来显示当前月份或者未来或者过去任何年份中的月份</p>
<pre><code>cal
cal 02 1835
</code></pre>
<h3 id="cp">cp</h3>
<p>“copy”就是复制。它会从一个地方复制一个文件到另外一个地方</p>
<pre><code>cp file1 file2
cp -r dir1 dir2
</code></pre>
<p>快速备份一个文件：</p>
<pre><code>cp some_file_name{,.bkp}
</code></pre>
<p>注意： cp，在shell脚本中是最常用的一个命令，而且它可以使用通配符（在前面一块中有所描述），来定制所需的文件的复制。</p>
<h3 id="mv">mv</h3>
<p>“mv”命令将一个地方的文件移动到另外一个地方去。</p>
<p>“mv”命令将一个地方的文件移动到另外一个地方去。</p>
<h3 id="pwd">pwd</h3>
<p>“pwd”（print working directory），在终端中显示当前工作目录的全路径。</p>
<p>注意： 这个命令并不会在脚本中经常使用，但是对于新手，当从连接到nux很久后在终端中迷失了路径，这绝对是救命稻草。</p>
<h3 id="free">free</h3>
<pre><code>free -m
             total       used       free     shared    buffers     cached
Mem:          7982       6811       1171          0        350       5114
-/+ buffers/cache:       1346       6636
Swap:        16935         11      16924
</code></pre>
<p>显示剩余内存</p>
<pre><code>free -m | grep cache | awk '/[0-9]/{ print $4&quot; MB&quot; }'
</code></pre>
<p>在这里例子中,应用程序只使用了1346MB内存,还有6636MB空闲内存可以使用.</p>
<p>一些简单的计算方法：</p>
<p>物理已用内存 = 实际已用内存 - 缓冲 - 缓存 = 6811M - 350M - 5114M</p>
<p>物理空闲内存 = 总物理内存 - 实际已用内存 + 缓冲 + 缓存</p>
<p>应用程序可用空闲内存 = 总物理内存 - 实际已用内存</p>
<p>应用程序已用内存 = 实际已用内存 - 缓冲 - 缓存</p>
<p>原始解释：转至互联网：
Linux的基本原则是没有资源应该被浪费.因此核心会使用尽可能多的RAM,来缓存来自本地和远程的文件系统的信息.系统做读写操作的时候,会将与当前运行的进程相关的数据尽量存储在RAM里.系统报告的缓存是缓冲和页缓存两者之和.缓存并不是在进程结束的时候被回收(你可能很快会启动另外一个进程,需要同样的数据),而是随需回收–比如,当你启动一个需要大量内存的进程时,Linux核心会从内存中回收缓存,将得到的内存分配给新的进程.</p>
<p>有些区域,比如匿名内存映射(mmps)和共享内存区域,它们被报告为缓存,但不是被核心直接释放.一般的缓存不映射到进程的地址空间,仅仅是简单的核心映射,而这些特别的缓存映射到所有挂接到它们上面的进程.</p>
<h3 id="eval">eval</h3>
<pre><code>eval &quot;ls -l&quot;
</code></pre>
<h3 id="basename">basename</h3>
<p>获取路径中文件部分</p>
<pre><code>basename resolv.conf #resolv.conf
basename /etc/resolv.conf # resolv.conf
</code></pre>
<h3 id="cmp">cmp</h3>
<p>比较两个任意类型的文件并将结果输出至标准输出。如果两个文件相同， ‘cmp‘默认返回0；如果不同，将显示不同的字节数和第一处不同的位置。</p>
<pre><code>cmp file1 file2
diff file1 file2
</code></pre>
<h3 id="rm">rm</h3>
<p>‘rm’ 标准移除命令。 rm 可以用来删除文件和目录</p>
<pre><code>rm file1
rm -r dir1  #递归删除空目录
</code></pre>
<p>强删</p>
<pre><code>rm -rf fileordir
</code></pre>
<p>警告: ”rm -rf” 命令是一个破坏性的命令,假如你不小心删除一个错误的目录。
一旦你使用’rm -rf’ 删除一个目录,在目录中所有的文件包括目录本身会被永久的删除,所以使用这个命令要非常小心。</p>
<h3 id="service">service</h3>
<p>‘service‘命令控制服务的启动、停止和重启，它让你能够不重启整个系统就可以让配置生效以开启、停止或者重启某个服务。</p>
<p>注意：要想使用service命令，进程的脚本必须放在‘/etc/init.d‘，并且路径必须在指定的位置。
如果要运行“service apache2 start”实际上实在执行“service /etc/init.d/apache2 start”.</p>
<h3 id="man">man</h3>
<p>‘man‘是系统帮助页。Man提供命令所有选项及用法的在线文档。几乎所有的命令都有它们的帮助页</p>
<pre><code>man thecommand
</code></pre>
<p>注意：系统帮助页是为了命令的使用和学习而设计的。</p>
<h3 id="passwd">passwd</h3>
<p>这是一个很重要的命令，在终端中用来改变自己密码很有用。显然的，因为安全的原因，你需要知道当前的密码。</p>
<h3 id="gcc">gcc</h3>
<p>gcc 是Linux环境下C语言的内建编译器。下面是一个简单的C程序，在桌面上保存为Hello.c （记住必须要有‘.c‘扩展名</p>
<pre><code>gcc Hello.c
./a.out
gcc -o Hello Hello.c
./Hello
</code></pre>
<p>注意: 编译C程序时，输出会自动保存到一个名为“a.out”的新文件，因此每次编译C程序 “a.out”都会被修改。
因此编译期间最好定义输出文件名.，这样就不会有覆盖输出文件的风险了。</p>
<h3 id="g">g++</h3>
<p>g++是C++的内建编译器</p>
<pre><code>g++ Add.cpp
./a.out
g++ -o Add Add.cpp
./Add
</code></pre>
<h3 id="java">java</h3>
<p>Java 是世界上使用最广泛的编程语言之一. 它也被认为是高效, 安全和可靠的编程语言. 现在大多数基于网络的服务都使用Java实现.</p>
<pre><code>javac tecmint.java
java tecmint
</code></pre>
<p>注意: 几乎所有的Linux发行版都带有gcc编译器, 大多数发行版都内建了g++ 和 java 编译器, 有些也可能没有. 你可以用apt 或 yum 安装需要的包.</p>
<h3 id="关于devnull">关于/dev/null</h3>
<p>特别有用的特殊文件，位桶，传送到此文件的数据都会被系统丢弃。</p>
<h3 id="语言及乱码">语言及乱码</h3>
<p>查看变量值</p>
<pre><code>echo $LANG   未设置任何LC_XXX时使用的默认值
echo $LC_ALL 覆盖所有LC_XXX变量，总控开关
</code></pre>
<p>好的做法是，避免为任何LC_XXX变量赋值，使用LC_ALL和LANG来控制</p>
<p>避免乱码：从编辑器到语言，再到系统，统一编码为UTF-8</p>
<h3 id="shell的版本">shell的版本</h3>
<pre><code>bash --version
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>工作两周年小结</title>
			<link>https://wklken.me/posts/2013/07/04/summary-07-worktwoyear.html</link>
			<pubDate>Thu, 04 Jul 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/07/04/summary-07-worktwoyear.html</guid>
			<description>两年了，又到了总结的时候，都成习惯了 从哪说起呢，有点散，有点乱，还有错别字，囧 2011年的7月4号，是毕业后工作的第一天 2012年的7月4号</description>
			<content type="html"><![CDATA[<p>两年了，又到了总结的时候，都成习惯了</p>
<p>从哪说起呢，有点散，有点乱，还有错别字，囧</p>
<hr>
<p>2011年的7月4号，是毕业后工作的第一天</p>
<p>2012年的7月4号，满一年，记得那天太阳很大，坐在靠窗的位置，外面的天空很蓝，偶尔战斗机飞过，划出一道白，现在偶尔发呆，望着高楼外墙反射的阳光，想着身后是那片橙，那些人</p>
<p>感谢那些可爱的人:)</p>
<p>那时候的日子，简单快乐，周一到周五，工作，加加班，晚上回去看看书什么的，周六，有一段时间常来公司，写一些东西，学一些东西</p>
<p>周日骑车出去溜溜，山里转一圈，然后绕西湖一圈，在湖边静静坐着，回公司蹭蹭水，上上网</p>
<p>周末的公司，很安静，让人很是怀念，如今，已经没有了那种环境</p>
<p>那时候的节奏，像在冬天里晒太阳，很幸福，家的感觉</p>
<p>现在的节奏，像大夏天太阳底下登山，天热山陡，还好风景不错，副本没完，得接着打</p>
<hr>
<p>人，总是在成长，在变化，在思考</p>
<p>刚入职那会，想着，好好干活，三年后再说，那时候，没想过变化，没想过最终会离职，换了个城市，换了份工作</p>
<p>美好的日子，总是过得很快，七月，八月，九月，然后到了十月</p>
<p>十月注定不平静</p>
<p>参了个会，投了几个，面了几个，拿到offer,交接，离职，离开</p>
<p>时间，终究没有跨过2到达3</p>
<p>那段时间，一直在思考一个问题，自己想做的，擅长做的到底是什么</p>
<p>最终，还是没有想明白，只是摸到点头绪，想到了，便要做决定</p>
<p>以前认为帮主的follow your heart是很虚的东西，现在想来，有点道理</p>
<p>花了很长时间，想明白一件事情，也算值得</p>
<p>I just need to move on.</p>
<p>很多时候在想，或许不变也不错，熟悉的环境，熟悉的人，熟悉的事</p>
<p>但是，随着时间的推移，对待问题，事物的看法，总是在变化的，环境虽然不错，但是可能和自身的节奏已经不匹配，或许，需要换个环境了</p>
<hr>
<p>切换，从测试开发，转成了开发，用着喜欢的python，到现在，八个月了</p>
<p>这八个月，很是漫长，又很是短暂</p>
<p>身边的人，认识，熟悉，也有离开的</p>
<p>相识用了很长，离开却只要短短几天</p>
<p>离职，几天后，飞到另一个城市，入职</p>
<p>刚开始一周，住在同学那，每天挤公交，哎，记得，装了三天系统，坑爹的联想机器</p>
<p>后来，花了一天，找了房子，开始了每天地铁的生活</p>
<p>熟悉环境，重构东西，写新的东西，上线，开始了这里的工作</p>
<p>过年，回家，你妹的没年假，颇为无奈</p>
<p>五月份，请了两天假，去海南溜了半圈，想清楚了一些问题</p>
<p>六月底，公司outing，回厦门逛了两天，当做回家了</p>
<p>好像流水账，实际上就是，这么，过了八个月</p>
<hr>
<p>这一年变化很多</p>
<p>开始每天十点上班七点下班的节奏，早睡，然后七点多起来，看书看到八点半，起床然后晃悠悠去上班，地铁三站，十一分钟，加走路，十八分钟</p>
<p>疯狂买书，书桌上一堆，书架上两层，都是战利品，这几个月啃了一部分，目测这是一年的量，读书是最好的投资，实践中也印证，坚信不疑</p>
<p>足够强大的执行力和足够高的效率，空出了好多时间，看书，玩，发呆，做一些想做的事情</p>
<p>开始，逐渐不上csdn了(感觉俩字：浮躁，勿喷，不喜欢负能量，另一个原因是，开始用markdown了)，转小道消息和hacknews（有效信息），qq越用越少，也逐渐习惯上线隐身…依然每天刷微博，慢慢在翻墙</p>
<p>再也没没事去公司了</p>
<p>小黑换mac了，继续倒腾，的确好用</p>
<p>倒腾了个<a href="https://github.com/wklken/k-vim">vim</a>，写了几个东西，开始迁移<a href="https://wklken.github.io/">博客</a></p>
<p>写了几天代码，把摩托换iphone了，前两天换的，用着还成，铁杆moto粉变成伪果粉的节奏，想再搞个pad</p>
<p>也渐渐熟悉了，自己搞定从设计到上线运维这一整套，修复bug，分分钟上线的节奏…..当然，形成一些好的习惯，开发或者去挖掘了一些好用的东西，提高效率</p>
<p>相对一年前，算上跳槽的话，工资小提了两次，不算多，每个月依旧很穷的感觉，争取过两月再来一次</p>
<p>越来越懒了，在这车况太复杂，没有再出去骑过车，只是偶尔徒步到海边逛逛</p>
<p>看是啃一些原来认为很难很高深的东西，虽然过程有些艰难，但是目测能吃掉，没有什么东西，是轻而易举能得到的</p>
<p>还有7月第一天，由于所在位置，或者其他原因，反正我不是很明白，很突然的，开始要带人了，五个人的小组，角色变化有点快，顿感压力山大，以前惬意的日子或许不再，需要一段时间好好过渡，发现写代码的时间被压缩了，经常打断处理些其他事情，意味着加会班搞定一些事情，需要再次提升下效率…..</p>
<hr>
<p>很多东西没有变，或许也改变不了，譬如性格，譬如习惯，譬如外婆红烧肉，很怀念西湖，还有茶馆</p>
<p>依然会对一些事情有莫名的坚持，这或许是一个码农的固执</p>
<p>偏执地认为需要去做一些事情</p>
<hr>
<p>两年了，感谢这一路遇到的人，都很nice</p>
<p>性格内向的我，经常会带来一些困扰吧</p>
<p>感谢你们，对我的包容，教会了我很多，留下了很多美好的回忆</p>
<hr>
<p>其实有时想想，坚持做一件事情，没有错，但是需要偶尔抬头，看看天空，思考下</p>
<p>做选择，没有对错，没有后悔的选项，很多时候，只是单选题</p>
<p>现在想来，一个适合自己的节奏</p>
<p>无非，一个良性循环</p>
<p>思考成长，能力效率提升，时间越多，看书生活发呆，思考成长，循环</p>
<p>无论工作，生活，都需要进入这个状态</p>
<hr>
<p>总感觉自己还很弱，也的确很弱，要变得强力，还需要努力，还需要时间</p>
<p>需要磨砺自己做事的方法，风格，对待问题的观点，以及同外界交流沟通的能力</p>
<p>保持简单，保持高效，看很多书，做很多事，把事情做得漂亮，依然是这个目标</p>
<p>什么时候，才能战斗力爆表呢</p>
<p>继续思考，继续行走</p>
<p>吴昆亮</p>
<p>2013-07-04 00：08</p>
<p>于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>我的vim配置及说明【k-vim】</title>
			<link>https://wklken.me/posts/2013/06/11/linux-my-vim.html</link>
			<pubDate>Tue, 11 Jun 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/06/11/linux-my-vim.html</guid>
			<description>ps: k-vim github地址 : https://github.com/wklken/k-vim vim插件分类及快捷键 给人一条Vim 命令，他能折腾一晚上；告诉他怎么自定义Vim 命令，他能捣腾一辈子 生命不息,折腾不</description>
			<content type="html"><![CDATA[<p>ps: k-vim github地址 : <a href="https://github.com/wklken/k-vim">https://github.com/wklken/k-vim</a></p>
<h3 id="vim插件分类及快捷键">vim插件分类及快捷键</h3>
<blockquote>
<p>给人一条Vim 命令，他能折腾一晚上；告诉他怎么自定义Vim 命令，他能捣腾一辈子</p>
<p>生命不息,折腾不止 (╯‵□′)╯︵┻━┻)</p>
<p>编辑器之神 = 生产力(效率为王) + 性感(界面快捷键) + 装x神器</p>
</blockquote>
<h3 id="vim基本用法">vim基本用法</h3>
<p>初学者: <a href="http://blog.csdn.net/wklken/article/details/7533272">vim训练稿</a>
几年前的三月份,第一次正儿八经开始使用vim,后来整理了一份,对着敲几遍,训练稿</p>
<p>推荐: 耗子叔的 <a href="http://coolshell.cn/articles/5426.html">简明vim练级攻略</a></p>
<p>或者,玩游戏 <a href="http://vim-adventures.com/">vim大冒险</a></p>
<h3 id="使用说明">使用说明</h3>
<ol>
<li>
<p>能熟练使用原生vim,最好先熟悉了再来使用插件扩展</p>
</li>
<li>
<p>以下插件,仅介绍用途优点等,可以在github中搜索查看详细用途和配置</p>
<p>当前vim使用配置,在vimrc中查看</p>
<p>快捷键为插件默认/或者当前配置vimrc定义的,如果需要修改,查看vimrc中对插件配置进行修改 [sd]标记的为自定义 [d]标记的为默认快捷键</p>
</li>
<li>
<p>由于平时会使用python和golang,所以语言方面的配置偏向于这两个</p>
<p>其它的可以参照网上配置(通用的插件可以配置,其他具体语言插件可以自己配置加入)</p>
</li>
<li>
<p>fork一份</p>
<p>根据自己使用的语言，自身习惯进行修改</p>
<p>有些插件用不到，可以注释删除，有些插件没有，可以自行添加（vundle很强大只要github上有都能配置），有些插件快捷键等可以自己去进一步了解</p>
<p>得到一份符合自己习惯的vim配置，后续能在任何地方进行一键配置</p>
<pre><code>   二八定律,关注可以最大提升自身生产力的那20%插件,具体要亲自实践
   有什么问题,先看插件文档说明-&gt;代码选项-&gt;github上的issues-&gt;google it
   你遇到的问题,一定别人也遇到了,大部分可解决,少部分无解….
</code></pre>
<p>欢迎推荐好用更酷的插件配置:)</p>
<p>我的配置也会不定期更新，thx</p>
</li>
</ol>
<p>PS: 这个vim配置是我的<a href="https://github.com/wklken/linux_config">linux_config</a>下一部分，如果需要，可以参考，主要是用于一键配置环境</p>
<hr>
<h3 id="配置步骤">配置步骤</h3>
<ol>
<li>
<p>clone到本地,配置到linux个人目录（如果是从linux_config过来的，不需要clone）</p>
<pre><code> git clone https://github.com/wklken/k-vim.git
</code></pre>
</li>
<li>
<p>安装依赖包</p>
<pre><code> sudo apt-get install ctags
 #brew install ctags     (mac用户)

 #使用python需要
 sudo pip install pyflakes
 sudo pip install pylint
 sudo pip install pep8
</code></pre>
</li>
<li>
<p>安装插件</p>
<pre><code> cd k-vim/

 sh -x install.sh

 #会进入安装插件的列表，目前30+个插件，一一安装是从github clone的，完全取决于网速

 #安装完插件后，会自动编译YCM，注意，可能编译失败（缺少某些依赖包,暂不支持mac osx 10.9）
 失败的话手动编译吧，看第4步 编译自动补全YouCompleteMe （这步耗时也有点长，但绝对值得）

 install.sh
 本质上做的事情
 1.将vimrc/vim文件夹软链接到$HOME，编程系统vim配置
 2.git clone安装vundle（clone到bundle目录下）
 3.通过vundle安装其他所有插件（相当于进入vimrc, 命令行执行:BundleInstall）,从github全部搞到本地
 4.编译需要手动编译的插件，eg.YCM
</code></pre>
</li>
<li>
<p>可能遇到的问题:</p>
<ul>
<li>编译自动补全YouCompleteMe</li>
</ul>
<p><a href="https://github.com/Valloric/YouCompleteMe">文档</a></p>
<p>这个插件需要Vim 7.3.584,所以,如果vim版本太低,需要<a href="https://github.com/Valloric/YouCompleteMe/wiki/Building-Vim-from-source">编译安装</a></p>
<ul>
<li>相对行号</li>
</ul>
<p>vimrc中配置,如果不习惯,可以去掉,<a href="http://jeffkreeftmeijer.com/2012/relative-line-numbers-in-vim-for-super-fast-movement/">相关参考</a></p>
<ul>
<li>配置主题</li>
</ul>
<p>到vimrc中修改colortheme,可以使用molokai(用惯sublimetext2的童鞋很熟悉)</p>
<p>默认配置的是<a href="https://github.com/altercation/vim-colors-solarized">solarized dark主题</a></p>
<p>想要修改终端配色为solarized可以参考 <a href="https://github.com/sigurdga/gnome-terminal-colors-solarized">这里</a></p>
</li>
</ol>
<hr>
<h3 id="其他">其他</h3>
<ol start="5">
<li>
<p>安装/卸载/更新插件：</p>
<p>可能发现打开vim很慢，可能是插件有点多了，这个配置插件全开</p>
<p>去掉某些自己用不到的插件: 编辑vimrc，注释掉插件对应Bundle行即可(加一个双引号),保存退出即可</p>
<pre><code> &quot;Bundle 'fholgado/minibufexpl.vim'
</code></pre>
<p>如果想从物理上清除（删除插件文件），注释保存后再次进入vim</p>
<p>命令行模式，执行:</p>
<pre><code> :BundleClean
</code></pre>
<p>如果要安装新插件，在vimrc中加入bundle，然后执行</p>
<pre><code> :BundleInstall
</code></pre>
<p>更新插件</p>
<pre><code> :BundleUpdate
</code></pre>
</li>
<li>
<p>给mac用户</p>
<p>可以使用mac vim</p>
<p>首先，安装最新mac vim ,可以正常打开</p>
<p>然后(需要sudo)</p>
<pre><code> mv /usr/bin/vim /usr/bin/vim.bk
 ln -s /usr/local/bin/mvim /usr/bin/vim
</code></pre>
<p>最后，在.bashrc/.bash_profile中加入</p>
<pre><code> alias vi='mvim -v'
 alias vim='mvim -v'
</code></pre>
<p>配置完成</p>
</li>
<li>
<p>冲突和问题排查</p>
<p>插件很多，并且其默认快捷键或者配置可能发生冲突</p>
<p>当加入新插件发现有冲突或者展现有问题</p>
<p>排除法进行排查：注掉所有插件或配置，然后二分法逐一恢复，可以定位到出现问题的插件或配置</p>
</li>
</ol>
<hr>
<h3 id="截图">截图</h3>
<p>solarized主题</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/solarized.png?raw=true" alt="solarized"></p>
<p>molokai主题</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/molokai.png?raw=true" alt="molokai"></p>
<hr>
<h3 id="自定义快捷键说明">自定义快捷键说明</h3>
<pre><code>F1  关掉，防止跳出帮助
F2  set nu/nonu
F3  set list/nolist
F4  set wrap/nowrap
F5  set paste/nopaste
F6  syntax on/off
空格 /开启查找
Y   =y$   复制到行尾
w!!  以sudo的权限保存
kj   &lt;Esc&gt;，不用到角落去按esc了
t    新起一行，下面，不进入插入模式
T    新起一行，上面
,sa   全选(select all)
hjkl  上下左右，强迫使用，要解开的自己改
ctrl + jkhl 进行上下左右窗口跳转,不需要ctrl+w+jkhl

,tn  new tab
,tc  tab close
,to  tab only
,tm  tab move
,te  new tab edit
ctrl+n  相对行号绝对行号变换，默认用相对行号
5j/5k  在相对行号模式下，往上移动5行 往下移动5行

,y 展示历史剪贴板
,yc 清空
yy/dd -&gt; p -&gt; ctrl+p可以替换非最近一次剪贴内容

,p 开启文件搜索 ctrlp
,/ 去除匹配高亮
</code></pre>
<hr>
<h3 id="插件及其快捷键说明">插件及其快捷键说明</h3>
<p>图片有点多，展示有点慢，截得不是很专业，耐心看完:)</p>
<blockquote>
<p>插件管理</p>
</blockquote>
<ol>
<li>
<p>####<a href="https://github.com/gmarik/vundle">gmarik/vundle</a></p>
<p>必装,用于管理所有插件</p>
<p>命令行模式下管理命令:</p>
<pre><code> :BundleInstall     install
 :BundleInstall!    update
 :BundleClean       remove plugin not in list
</code></pre>
</li>
</ol>
<blockquote>
<p>导航及搜索</p>
</blockquote>
<ol>
<li>
<p>####<a href="https://github.com/scrooloose/nerdtree">scrooloose/nerdtree</a></p>
<p>必装,开启目录树导航</p>
<pre><code> [sd]
     ,n  打开 关闭树形目录结构

     在nerdtree窗口常用操作：(小写当前，大写root)
     x.......Close the current nodes parent收起当前目录树
     R.......Recursively refresh the current root刷新根目录树
     r.......Recursively refresh the current directory刷新当前目录
     P.......Jump to the root node
     p.......Jump to current nodes parent
     K.......Jump up inside directories at the current tree depth  到同目录第一个节点
     J.......Jump down inside directories at the current tree depth 最后一个节点
     o.......Open files, directories and bookmarks
     i.......Open selected file in a split window上下分屏
     s.......Open selected file in a new vsplit左右分屏
</code></pre>
<p>演示</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/thenerdtree.gif?raw=true" alt="thenerdtree"></p>
</li>
<li>
<p>####<a href="https://github.com/fholgado/minibufexpl.vim">fholgado/minibufexpl.vim</a></p>
<p>必装，buffer管理, 可以查找其他同类插件</p>
<pre><code> [sd]
     &lt;Tab&gt;  切换buffer
     左右方向键  切换buffer
     ,bn   切到后一个
     ,bp   切到前一个
     ,bd   关闭当前buffer
</code></pre>
</li>
<li>
<p>####<a href="https://github.com/majutsushi/tagbar">majutsushi/tagbar</a></p>
<p>必装,标签导航,纬度和taglist不同</p>
<pre><code> [sd] &lt;F9&gt; 打开
</code></pre>
<p>演示</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/tagbar.gif?raw=true" alt="tagbar"></p>
</li>
<li>
<p>####<a href="https://github.com/vim-scripts/taglist.vim">vim-scripts/taglist.vim</a></p>
<p>必装</p>
<pre><code> [sd] &lt;F8&gt;打开
</code></pre>
<p>演示:</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/taglist.png?raw=true" alt="taglist"></p>
</li>
<li>
<p>####<a href="https://github.com/hdima/python-syntax">kien/ctrlp.vim</a></p>
<p>文件搜索,ack/Command-T需要依赖于外部包,不喜欢有太多依赖的,除非十分强大, 具体 <a href="http://kien.github.io/ctrlp.vim/">文档</a></p>
<pre><code> [sd] ,p  打开ctrlp搜索
 [sd] ,f  相当于mru功能，show recently opened files

 ctrl + j/k 进行上下移动
</code></pre>
<p>演示</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/ctrlp.gif?raw=true" alt="ctrip"></p>
</li>
</ol>
<blockquote>
<p>显示增强</p>
</blockquote>
<pre><code>被动技能,无快捷键
</code></pre>
<ol>
<li>
<p>####<a href="https://github.com/Lokaltog/vim-powerline">Lokaltog/vim-powerline</a></p>
<p>必装，状态栏美观</p>
<p>演示</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/powerline.png?raw=true" alt="powerline"></p>
</li>
<li>
<p>####<a href="https://github.com/kien/rainbow_parentheses.vim">kien/rainbow_parentheses.vim</a></p>
<p>必装,括号高亮</p>
<p>演示</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/rainbow_parentheses.png?raw=true" alt="rainbow"></p>
</li>
<li>
<p>####<a href="https://github.com/Yggdroot/indentLine">Yggdroot/indentLine</a></p>
<p>选装,装不装看个人喜好了,缩进标识</p>
<p>另一个类似的,整块背景色的的,<a href="https://github.com/nathanaelkane/vim-indent-guides">nathanaelkane/vim-indent-guides</a>,自选吧, 看来看去还是st2的好看,唉</p>
<p>调整颜色和solarized一致,不至于太显眼影响注意力,可以根据自己主题设置颜色(<a href="http://vim.wikia.com/wiki/Xterm256_color_names_for_console_Vim?file=Xterm-color-table.png">颜色</a>)</p>
<p>演示:</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/indentline.png?raw=true" alt="indentline"></p>
</li>
<li>
<p>####<a href="https://github.com/bronson/vim-trailing-whitespace">bronson/vim-trailing-whitespace</a></p>
<p>将代码行最后无效的空格标红</p>
</li>
<li>
<p>####<a href="https://github.com/altercation/vim-colors-solarized">altercation/vim-colors-solarized</a></p>
<p>经典主题,目前我使用的,看起来舒服</p>
</li>
<li>
<p>####<a href="https://github.com/tomasr/molokai">tomasr/molokai</a></p>
<p>用sublime text2的同学应该很熟悉, 另一个主题,可选,偶尔换换味道</p>
</li>
</ol>
<blockquote>
<p>快速移动</p>
</blockquote>
<pre><code>主动技能,需要快捷键
</code></pre>
<ol>
<li>
<p>####<a href="https://github.com/Lokaltog/vim-easymotion">Lokaltog/vim-easymotion</a></p>
<p>必装，效率提升杀手锏，跳转到光标后任意位置</p>
<p>配置(我的leader键配置 let g:mapleader = &lsquo;,&rsquo;)</p>
<pre><code> ,, + w  跳转
 ,, + fe  查找'e',快速跳转定位到某个字符位置
</code></pre>
<p>演示</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/easymotion.gif?raw=true" alt="easy_motion"></p>
</li>
<li>
<p>####<a href="https://github.com/vim-scripts/matchit.zip">vim-scripts/matchit.zip</a></p>
<p>选装</p>
<p>% 匹配成对的标签，跳转</p>
</li>
</ol>
<blockquote>
<p>自动补全及快速编辑</p>
</blockquote>
<pre><code>主动技能,需要快捷键,高效编辑无上利器
</code></pre>
<ol>
<li>
<p>####<a href="https://github.com/Valloric/YouCompleteMe">Valloric/YouCompleteMe</a></p>
<p>必装，强烈推荐</p>
<p>YCM是我目前用到的最好的自动补全插件,我只能说，用这个写代码太舒畅了</p>
<p>需要编译这个插件(见github文档)</p>
<p>这个需要自己去看官方的配置方式,演示在官方github有</p>
<p>需要Vim 7.3.584 以上版本(<a href="https://github.com/Valloric/YouCompleteMe/wiki/Building-Vim-from-source">如何编译vim</a>)</p>
<p>这个插件包含了以下四个插件功能,所以不需要装下面四个</p>
<pre><code> clang_complete
 AutoComplPop
 Supertab
 neocomplcache
 jedi(对python的补全)
</code></pre>
<p>快捷键:</p>
<pre><code> ,gd  跳到声明位置, 仅 filetypes: c, cpp, objc, objcpp, python 有效
</code></pre>
</li>
<li>
<p>####<a href="https://github.com/SirVer/ultisnips">SirVer/ultisnips</a></p>
<p>必装，效率杀手锏，快速插入自定义的代码片段</p>
<p>自动补全加这个,高效必备, 针对各种语言已经带了一份配置了，可以到安装目录下查看具体，我有针对性补全一份，在snippets目录下，可自行修改</p>
<p>演示</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/utilsnips.gif?raw=true" alt="ultisnips"></p>
</li>
<li>
<p>####<a href="https://github.com/scrooloose/nerdcommenter">scrooloose/nerdcommenter</a></p>
<p>必装，另一个大大提升效率的地方，快速批量加减注释</p>
<pre><code> [d] shift+v+方向键选中(默认当前行)   -&gt;  ,cc  加上注释  -&gt; ,cu 解开注释
</code></pre>
<p>演示</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/nerdcomment.gif?raw=true" alt="nerdcommenter"></p>
</li>
<li>
<p>####<a href="https://github.com/tpope/vim-surround">tpope/vim-surround</a></p>
<p>必装，很给力的功能，快速给词加环绕符号,例如引号</p>
<p><a href="https://github.com/tpope/vim-repeat">tpope/vim-repeat</a></p>
<p>repeat进行增强,&rsquo;.&lsquo;可以重复命令</p>
<pre><code> [d]
 cs&quot;' [inside]
 &quot;Hello world!&quot; -&gt; 'Hello world!'
 ds&quot;
 &quot;Hello world!&quot; -&gt; Hello world!
 ysiw&quot;
 Hello -&gt; &quot;Hello&quot;
</code></pre>
<p>演示</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/surround.gif?raw=true" alt="surround"></p>
</li>
<li>
<p>####<a href="https://github.com/Raimondi/delimitMate">Raimondi/delimitMate</a></p>
<p>必装，输入引号,括号时,自动补全</p>
<p>对python的docstring 三引号做了处理(只处理&quot;&quot;&quot;, &lsquo;&lsquo;&lsquo;暂时没配，可以自己加)</p>
<p>演示</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/delimate.gif?raw=true" alt="delimitmate"></p>
</li>
<li>
<p>####<a href="https://github.com/godlygeek/tabular">godlygeek/tabular</a></p>
<p>选装，代码格式化用的，code alignment</p>
<pre><code> [sd]
 ,a=  按等号切分格式化
 ,a:  按逗号切分格式化
</code></pre>
</li>
<li>
<p>####<a href="https://github.com/terryma/vim-expand-region">terryma/vim-expand-region</a></p>
<p>选装，visual mode selection
视图模式下可伸缩选中部分，用于快速选中某些块</p>
<pre><code> [sd]
 = 增加选中范围(+/=按键)
 - 减少选中范围(_/-按键)
</code></pre>
<p>演示（直接取链到其github图)</p>
<p><img src="https://raw.github.com/terryma/vim-expand-region/master/expand-region.gif" alt="expand-region"></p>
</li>
<li>
<p>####<a href="https://github.com/terryma/vim-multiple-cursors">vim-multiple-cursors</a></p>
<p>选装，多光标批量操作</p>
<pre><code> [sd]
 ctrl + m 开始选择
 ctrl + p 向上取消
 ctrl + x 跳过
 esc   退出
</code></pre>
<p>演示(官方演示图)</p>
<p><img src="https://raw.github.com/terryma/vim-multiple-cursors/master/assets/example1.gif" alt="multiple-cursors"></p>
</li>
</ol>
<blockquote>
<p>语法检查</p>
</blockquote>
<ol>
<li>
<p>####<a href="https://github.com/scrooloose/syntastic">scrooloose/syntastic</a></p>
<p>建议安装，静态语法及风格检查,支持多种语言</p>
<p>修改了下标记一列的背景色,原有的背景色在solarized下太难看了…..</p>
<p>演示</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/syntastic.png?raw=true" alt="syntastic"></p>
</li>
<li>
<p>####<a href="https://github.com/kevinw/pyflakes-vim">kevinw/pyflakes-vim</a></p>
<p>虽然这个的作者推荐使用syntastic,但是这个插件对于pythoner还是很需要的</p>
<p>因为有一个特牛的功能,fly check,即,编码时在buffer状态就能动态查错标记,弥补syntastic只能保存和打开时检查语法错误的不足</p>
<p>演示
<img src="https://github.com/wklken/gallery/blob/master/vim/pyflakes.png?raw=true" alt="pyflakes"></p>
</li>
</ol>
<blockquote>
<p>具体语言</p>
</blockquote>
<pre><code>主要是python  其它语言以及前端的,用得少没有研究使用过
python   golang   markdown
需要其它语言支持的,可以到github上捞,上面很多流行的vim配置,eg. spf13-vim
以下均为选装，根据自己需要
</code></pre>
<ol>
<li>
<p>####<a href="https://github.com/hdima/python-syntax">python-syntax</a></p>
<p>使用Python建议安装，python语法高亮,就是python.vim,在github,有维护和更新</p>
</li>
<li>
<p>####<a href="https://github.com/jnwhiteh/vim-golang">jnwhiteh/vim-golang</a></p>
<p>使用golang建议安装， golang语法高亮</p>
<p>golang刚入门使用,项目中还没正式开始,目前很多golang的手册有配置vim的介绍,后续有需求再弄</p>
</li>
<li>
<p>####<a href="https://github.com/plasticboy/vim-markdown">plasticboy/vim-markdown</a></p>
<p>markdown语法,编辑md文件</p>
</li>
<li>
<p>####<a href="https://github.com/pangloss/vim-javascript">pangloss/vim-javascript</a></p>
<p>偶尔会看看js,频率不高</p>
</li>
<li>
<p>####<a href="https://github.com/nono/jquery.vim">nono/jquery.vim</a></p>
<p>jquery高亮</p>
</li>
<li>
<p>####<a href="https://github.com/thiderman/nginx-vim-syntax">thiderman/nginx-vim-syntax</a></p>
<p>nginx配置文件语法高亮,常常配置服务器很有用</p>
</li>
<li>
<p>####<a href="https://github.com/Glench/Vim-Jinja2-Syntax">Glench/Vim-Jinja2-Syntax</a></p>
<p>jinja2 语法高亮</p>
</li>
</ol>
<blockquote>
<p>其它扩展增强</p>
</blockquote>
<pre><code>根据自身需求自取配置,不需要的话自己注解
</code></pre>
<ol>
<li>
<p>####<a href="https://github.com/vim-scripts/TaskList.vim">vim-scripts/TaskList.vim</a></p>
<p>查看并快速跳转到代码中的TODO列表</p>
<p>重构代码时一般通读,标记修改位置,非常实用</p>
<pre><code> [sd]
 ,td 打开todo列表
</code></pre>
<p>演示</p>
<p><img src="https://github.com/wklken/gallery/blob/master/vim/tasklist.gif?raw=true" alt="tasklist"></p>
</li>
<li>
<p>####<a href="https://github.com/tpope/vim-fugitive">tpope/vim-fugitive</a></p>
<p>git插件</p>
<p>不是很习惯,所以用的次数太少,目前和现有配置快捷键有冲突,尚未解决</p>
</li>
<li>
<p>####<a href="https://github.com/sjl/gundo.vim">sjl/gundo.vim</a></p>
<p>编辑文件时光机</p>
<pre><code> [sd] ,h  查看文件编辑历史
</code></pre>
</li>
</ol>
<blockquote>
<p>待考察的</p>
</blockquote>
<ol>
<li>
<p>####sjl/vitality.vim</p>
</li>
<li>
<p>####vim-scripts/Conque-Shell</p>
</li>
<li>
<p>####vim-scripts/YankRing.vim</p>
</li>
<li>
<p>####vim-scripts/auto.git</p>
</li>
</ol>
<hr>
<p>The End!</p>
<p>wklken (凌岳/pythoner/vim党预备党员)</p>
<p>Email: <a href="mailto:wklken@yeah.net">wklken@yeah.net</a></p>
<p>Github: <a href="https://github.com/wklken">https://github.com/wklken</a></p>
<p>Blog: <a href="https://wklken.me">https://wklken.me</a></p>
<p>2013-06-11 于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>shell下小抄速查工具cmdcheatsheet[python实现]</title>
			<link>https://wklken.me/posts/2013/05/17/shell-cheat-sheet.html</link>
			<pubDate>Fri, 17 May 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/05/17/shell-cheat-sheet.html</guid>
			<description>github地址 打开 需求 最近老是要翻配置文档/wiki/history/笔记/google等等，为的仅仅是拿个服务器地址，或者一个密码，异或</description>
			<content type="html"><![CDATA[<h3 id="github地址">github地址</h3>
<p><a href="https://github.com/wklken/cmdcheatsheet">打开</a></p>
<h3 id="需求">需求</h3>
<p>最近老是要翻配置文档/wiki/history/笔记/google等等，为的仅仅是拿个服务器地址，或者一个密码，异或一条复杂点常用但懒得打的命令，次数多了自己也烦了，低效耗时，伤不起</p>
<h3 id="优化">优化</h3>
<p>把常用的东西，记不住的东西，写到文本配置里，在shell下通过一个命令+关键字，秒杀获取想要的内容</p>
<h3 id="效果">效果</h3>
<p><img src="https://github.com/wklken/gallery/blob/master/tools/cmdcheatsheet.png?raw=true" alt="use_img"></p>
<h3 id="实现方法">实现方法</h3>
<p>1.配置文件格式：yaml</p>
<p>配置关键字，多行内容，读取，用python非常方便</p>
<p>2.建索引文件cheatsheet.py</p>
<p>很简单的处理</p>
<p>3.一键安装文件install.sh</p>
<p>安装</p>
<h3 id="后续改进">后续改进</h3>
<p>1.模糊匹配</p>
<p>2.读索引而非每次建索引</p>
<p>3.更友好的提示方式</p>
]]></content>
		</item>
		
		<item>
			<title>Python fabric实现远程操作和部署</title>
			<link>https://wklken.me/posts/2013/03/25/python-tool-fabric.html</link>
			<pubDate>Mon, 25 Mar 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/03/25/python-tool-fabric.html</guid>
			<description>fabric title是开发，但是同时要干开发测试还有运维的活 (o(╯□╰)o) 近期接手越来越多的东西，发布和运维的工作相当机械，加上频率还蛮高，导致</description>
			<content type="html"><![CDATA[<h2 id="fabric">fabric</h2>
<p>title是开发，但是同时要干开发测试还有运维的活 (o(╯□╰)o)</p>
<p>近期接手越来越多的东西，发布和运维的工作相当机械，加上频率还蛮高，导致时间浪费还是优点多。</p>
<p>修复bug什么的，测试，提交版本库(2分钟)，ssh到测试环境pull部署（2分钟），rsync到线上机器A,B,C,D,E（1分钟），分别ssh到ABCDE五台机器，逐一重启(8-10分钟) =  13-15分钟</p>
<p>其中郁闷的是，每次操作都是相同的，命令一样，要命的是在多个机器上，很难在本机一个脚本搞定，主要时间都浪费在ssh，敲命令上了，写成脚本，完全可以一键执行，花两分钟看下执行结果</p>
<p>直到，发现了fabric这货</p>
<p>官方文档 <a href="http://fabric.readthedocs.org/">入口</a></p>
<h3 id="作用">作用</h3>
<p>很强大的工具</p>
<p>可以将自动化部署或者多机操作的命令固化到一个脚本里</p>
<p>和某些运维工具很像，用它主要是因为，python…..</p>
<p>简单好用易上手</p>
<p>当然，shell各种命令组合起来也可以，上古神器和现代兵器的区别</p>
<h3 id="环境配置">环境配置</h3>
<p>在本机和目标机器安装对应包（注意，都要有）</p>
<pre><code>sudo easy_install fabric
</code></pre>
<p>目前是1.8版本</p>
<p>安装完后，可以查看是否安装成功</p>
<pre><code>[ken@~$] which fab
/usr/local/bin/fab
</code></pre>
<p>装完之后，可以浏览下<a href="http://docs.fabfile.org/en/1.8/">官方文档</a></p>
<p>然后，可以动手了</p>
<h3 id="hello-world">hello world</h3>
<p>先进行本机简单操作，有一个初步认识，例子来源与官网</p>
<p>新建一个py脚本: fabfile.py</p>
<pre><code>:::python
def hello():
    print(&quot;Hello world!&quot;)
</code></pre>
<p>命令行执行：</p>
<pre><code>[ken@~/tmp/fab$] fab hello
Hello world!

Done.
</code></pre>
<p>注意，这里可以不用fabfile作为文件名，但是在执行时需指定文件</p>
<pre><code>[ken@~/tmp/fab$] mv fabfile.py test.py
fabfile.py -&gt; test.py
[ken@~/tmp/fab$] fab hello

Fatal error: Couldn't find any fabfiles!

Remember that -f can be used to specify fabfile path, and use -h for help.

Aborting.
[ken@~/tmp/fab$] fab -f test.py hello
Hello world!

Done.
</code></pre>
<p>带参数：</p>
<p>修改fabfile.py脚本：</p>
<pre><code>:::python
def hello(name, value):
    print(&quot;%s = %s!&quot; % (name, value))
</code></pre>
<p>执行</p>
<pre><code>[ken@~/tmp/fab$] fab hello:name=age,value=20
age = 20!

Done.
[ken@~/tmp/fab$] fab hello:age,20
age = 20!

Done.
</code></pre>
<h3 id="执行本机操作">执行本机操作</h3>
<p>简单的本地操作:</p>
<pre><code>:::python
from fabric.api import local, lcd

def lsfab():
    with lcd('~/tmp/fab'):
        local('ls')
</code></pre>
<p>结果：</p>
<pre><code>[ken@~/tmp/fab$] pwd;ls
/Users/ken/tmp/fab
fabfile.py   fabfile.pyc  test.py      test.pyc
[ken@~/tmp/fab$] fab -f test.py lsfab
[localhost] local: cd ~/tmp/fab
[localhost] local: ls
fabfile.py  fabfile.pyc test.py     test.pyc

Done.
</code></pre>
<p>实战开始：</p>
<p>假设，你每天要提交一份配置文件settings.py到版本库（这里没有考虑冲突的情况）</p>
<p>如果是手工操作：</p>
<pre><code>cd /home/project/test/conf/
git add settings.py
git commit -m 'daily update settings.py'
git pull origin
git push origin
</code></pre>
<p>也就是说，这几个命令你每天都要手动敲一次，所谓daily job，就是每天都要重复的，机械化的工作，让我们看看用fabric怎么实现一键搞定：(其实用shell脚本可以直接搞定，但是fab的优势不是在这里，这里主要位后面本地+远端操作做准备，毕竟两个地方的操作写一种脚本便于维护)</p>
<pre><code>:::python
from fabric.api import local, lcd

def setting_ci():
    with lcd('/home/project/test/conf/'):
        local(&quot;git add settings.py&quot;)
        #后面你懂的，懒得敲了…..
</code></pre>
<h3 id="混搭整合远端操作">混搭整合远端操作</h3>
<p>这时候，假设，你要到机器A的/home/ken/project对应项目目录把配置文件更新下来</p>
<pre><code>#!/usr/bin/env python
# encoding: utf-8

from fabric.api import local,cd,run, env

env.hosts=['user@ip:port',] #ssh要用到的参数
env.password = 'pwd'


def setting_ci():
    local('echo &quot;add and commit settings in local&quot;')
    #刚才的操作换到这里，你懂的

def update_setting_remote():
    print &quot;remote update&quot;
    with cd('~/temp'):   #cd用于进入某个目录
        run('ls -l | wc -l')  #远程操作用run

def update():
    setting_ci()
    update_setting_remote()
</code></pre>
<p>然后，执行之：</p>
<pre><code>[ken@~/tmp/fab$] fab -f deploy.py update
[user@ip:port] Executing task 'update'
[localhost] local: echo &quot;add and commit settings in local&quot;
add and commit settings in local
remote update
[user@ip:port] run: ls -l | wc -l
[user@ip:port] out: 12
[user@ip:port] out:


Done.
</code></pre>
<p>注意，如果不声明env.password，执行到对应机器时会跳出要求输入密码的交互</p>
<h3 id="多服务器混搭">多服务器混搭</h3>
<p>操作多个服务器，需要配置多个host</p>
<pre><code>#!/usr/bin/env python
# encoding: utf-8

from fabric.api import *

#操作一致的服务器可以放在一组，同一组的执行同一套操作
env.roledefs = {
            'testserver': ['user1@host1:port1',],
            'realserver': ['user2@host2:port2', ]
            }

#env.password = '这里不要用这种配置了，不可能要求密码都一致的，明文编写也不合适。打通所有ssh就行了'

@roles('testserver')
def task1():
    run('ls -l | wc -l')

@roles('realserver')
def task2():
    run('ls ~/temp/ | wc -l')

def dotask():
    execute(task1)
    execute(task2)
</code></pre>
<p>结果：</p>
<pre><code>[ken@~/tmp/fab$] fab -f mult.py dotask
[user1@host1:port1] Executing task 'task1'
[user1@host1:port1] run: ls -l | wc -l
[user1@host1:port1] out: 9
[user1@host1:port1] out:

[user2@host2:port2] Executing task 'task2'
[user2@host2:port2] run: ls ~/temp/ | wc -l
[user2@host2:port2] out: 11
[user2@host2:port2] out:


Done.
</code></pre>
<h3 id="扩展">扩展</h3>
<p>1.颜色</p>
<p>可以打印颜色，在查看操作结果信息的时候更为醒目和方便</p>
<pre><code>:::python
from fabric.colors import *

def show():
    print green('success')
    print red('fail')
    print yellow('yellow')
#fab -f color.py show
</code></pre>
<p>2.错误和异常</p>
<p>关于<a href="http://docs.fabfile.org/en/1.6/usage/execution.html#failures">错误处理</a></p>
<p>默认，一组命令，上一个命令执行失败后，不会接着往下执行</p>
<p>失败后也可以进行不一样的处理， <a href="http://docs.fabfile.org/en/1.6/tutorial.html#failure-handling">文档</a></p>
<p>目前没用到，后续用到再看了</p>
<p>3.密码管理</p>
<p>看<a href="http://docs.fabfile.org/en/1.6/usage/execution.html#password-management">文档</a></p>
<p>更好的密码管理方式，哥比较土，没打通，主要是服务器列表变化频繁，我的处理方式是：</p>
<p>3.1 host,user,port,password配置列表，所有的都写在一个文件</p>
<p>或者直接搞到脚本里，当然这个更&hellip;&hellip;..</p>
<pre><code>:::python
env.hosts = [
        'host1',
        'host2'
]
# 注意: 要使env.passwords生效, host格式必须是  user@ip:port 端口号一定要显式写出来,即使是使用的默认22端口
env.passwords = {
    'host1': &quot;pwdofhost1&quot;,
    'host2': &quot;pwdofhost2&quot;,
}

或者
env.roledefs = {
'testserver': ['host1:22', 'host2:22'],
'realserver': ['host3:22', ]
}
# 注意: 要使env.passwords生效, host格式必须是  user@ip:port 端口号一定要显式写出来,即使是使用的默认22端口
env.passwords = {
    'host1:22': &quot;pwdofhost1&quot;,
    'host2:22': &quot;pwdofhost2&quot;,
    'host3:22': &quot;pwdofhost3&quot;,
}
</code></pre>
<p>3.2 根据key解析成map嵌套，放到deploy中</p>
<p>另外命令其实也可以固化成一个cmds列表的…..</p>
<p>粗略就用到这些，后续有更多需求的时候再去捞文档了，话说文档里好东西真多，就是太多了，看了晕。。。</p>
<p>TODO:</p>
<pre><code>装饰器作用？
@task
@parallel

命令行常用： fab --help
fab -l             -- 显示可用的task（命令）
fab -H             -- 指定host，支持多host逗号分开
fab -R             -- 指定role，支持多个
fab -P             -- 并发数，默认是串行
fab -w             -- warn_only，默认是碰到异常直接abort退出
fab -f             -- 指定入口文件，fab默认入口文件是：fabfile/fabfile.py

状态确认及错误处理

更复杂的操作
</code></pre>
<hr>
<p>update log</p>
<pre><code>2014-10-26 fix error of local/lcd
</code></pre>
<hr>
<p>The end!</p>
<p>To be continue….</p>
<p>wklken</p>
<p>Email: <a href="mailto:wklken@yeah.net">wklken@yeah.net</a></p>
<p>Blog:  <a href="http://www.wklken.com">http://www.wklken.com</a></p>
<p>2013-03-25</p>
<p>转载请注明出处，谢谢</p>
]]></content>
		</item>
		
		<item>
			<title>Python-基础-内置函数小结</title>
			<link>https://wklken.me/posts/2013/03/16/python-base-builtins.html</link>
			<pubDate>Sat, 16 Mar 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/03/16/python-base-builtins.html</guid>
			<description>##内置函数 ###常用函数 ####1.数学相关 abs(x) abs()返回一个数字的绝对值。如果给出复数，返回值就是该复数的模。 &amp;gt;&amp;gt;&amp;gt;print abs(-100) 100 &amp;gt;&amp;gt;&amp;gt;print abs(1+2j) 2.2360679775 divmod(x,y) divmo</description>
			<content type="html"><![CDATA[<p>##内置函数</p>
<p>###常用函数</p>
<p>####1.数学相关</p>
<ul>
<li>abs(x)</li>
</ul>
<p>abs()返回一个数字的绝对值。如果给出复数，返回值就是该复数的模。</p>
<pre><code>&gt;&gt;&gt;print abs(-100)
100
&gt;&gt;&gt;print abs(1+2j)
2.2360679775
</code></pre>
<ul>
<li>divmod(x,y)</li>
</ul>
<p>divmod(x,y)函数完成除法运算，返回商和余数。</p>
<pre><code>&gt;&gt;&gt; divmod(10,3)
(3, 1)
&gt;&gt;&gt; divmod(9,3) (3, 0)
</code></pre>
<ul>
<li>pow(x,y[,z])</li>
</ul>
<p>pow()函数返回以x为底，y为指数的幂。如果给出z值，该函数就计算x的y次幂值被z取模的值。</p>
<pre><code>&gt;&gt;&gt; print pow(2,4)
16
&gt;&gt;&gt; print pow(2,4,2)
0
&gt;&gt;&gt; print pow(2.4,3)
13.824
</code></pre>
<ul>
<li>round(x[,n])</li>
</ul>
<p>round()函数返回浮点数x的四舍五入值，如给出n值，则代表舍入到小数点后的位数。</p>
<pre><code>&gt;&gt;&gt; round(3.333)
3.0
&gt;&gt;&gt; round(3)
3.0
&gt;&gt;&gt; round(5.9)
6.0
</code></pre>
<ul>
<li>min(x[,y,z&hellip;])</li>
</ul>
<p>min()函数返回给定参数的最小值，参数可以为序列。</p>
<pre><code>&gt;&gt;&gt; min(1,2,3,4)
1
&gt;&gt;&gt; min((1,2,3),(2,3,4))
(1, 2, 3)
</code></pre>
<ul>
<li>max(x[,y,z&hellip;])</li>
</ul>
<p>max()函数返回给定参数的最大值，参数可以为序列。</p>
<pre><code>&gt;&gt;&gt; max(1,2,3,4)
4
&gt;&gt;&gt; max((1,2,3),(2,3,4))
(2, 3, 4)
</code></pre>
<p>####2.序列相关</p>
<ul>
<li>len(object) -&gt; integer</li>
</ul>
<p>len()函数返回字符串和序列的长度。</p>
<pre><code>&gt;&gt;&gt; len(&quot;aa&quot;)
2
&gt;&gt;&gt; len([1,2])
2
</code></pre>
<ul>
<li>range([lower,]stop[,step])</li>
</ul>
<p>range()函数可按参数生成连续的有序整数列表。</p>
<pre><code>&gt;&gt;&gt; range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
&gt;&gt;&gt; range(1,10)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
&gt;&gt;&gt; range(1,10,2)
[1, 3, 5, 7, 9]
</code></pre>
<ul>
<li>xrange([lower,]stop[,step])</li>
</ul>
<p>xrange()函数与range()类似，但xrnage()并不创建列表，而是返回一个xrange对象，它的行为</p>
<p>与列表相似，但是只在需要时才计算列表值，当列表很大时，这个特性能为我们节省内存。</p>
<pre><code>&gt;&gt;&gt; a=xrange(10)
&gt;&gt;&gt; print a[0]
0
&gt;&gt;&gt; print a[1]
1
&gt;&gt;&gt; print a[2]
2
</code></pre>
<p>####3.对象及类型</p>
<ul>
<li>callable(object)</li>
</ul>
<p>callable()函数用于测试对象是否可调用，如果可以则返回1(真)；否则返回0(假)。可调用对象包括函数、方法、代码对象、类和已经定义了 调用 方法的类实例。</p>
<pre><code>&gt;&gt;&gt; a=&quot;123&quot;
&gt;&gt;&gt; print callable(a)
False
&gt;&gt;&gt; print callable(chr)
True
</code></pre>
<ul>
<li>cmp(x,y)</li>
</ul>
<p>cmp()函数比较x和y两个对象，并根据比较结果返回一个整数，如果x&lt;y，则返回-1；如果
x&gt;y，则返回1,如果x==y则返回0。</p>
<pre><code>&gt;&gt;&gt;a=1
&gt;&gt;&gt;b=2
&gt;&gt;&gt;c=2
&gt;&gt;&gt; print cmp(a,b)
-1
&gt;&gt;&gt; print cmp(b,a)
1
&gt;&gt;&gt; print cmp(b,c)
0
</code></pre>
<ul>
<li>isinstance(object,class-or-type-or-tuple) -&gt; bool</li>
</ul>
<p>测试对象类型</p>
<pre><code>&gt;&gt;&gt; a='isinstance test'
&gt;&gt;&gt; b=1234
&gt;&gt;&gt; isinstance(a,str)
True
&gt;&gt;&gt;isinstance(a,int)
False
&gt;&gt;&gt; isinstance(b,str)
False
&gt;&gt;&gt; isinstance(b,int) True
</code></pre>
<ul>
<li>type(obj)</li>
</ul>
<p>type()函数可返回对象的数据类型。</p>
<pre><code>&gt;&gt;&gt; type(a)
&lt;type 'list'&gt;
&gt;&gt;&gt; type(copy)
&lt;type 'module'&gt;
&gt;&gt;&gt; type(1)
&lt;type 'int'&gt;
</code></pre>
<p>###内置类型转换函数
####1.字符及字符串</p>
<ul>
<li>chr(i)</li>
</ul>
<p>chr()函数返回ASCII码对应的字符串。</p>
<pre><code>&gt;&gt;&gt; print chr(65)
A
&gt;&gt;&gt; print chr(66)
B
&gt;&gt;&gt; print chr(65)+chr(66)
AB
</code></pre>
<ul>
<li>ord(x)</li>
</ul>
<p>ord()函数返回一个字符串参数的ASCII码或Unicode值。</p>
<pre><code>&gt;&gt;&gt; ord(&quot;a&quot;)
97
&gt;&gt;&gt; ord(u&quot;a&quot;)
97
</code></pre>
<ul>
<li>str(obj)</li>
</ul>
<p>str()函数把对象转换成可打印字符串。</p>
<pre><code>&gt;&gt;&gt; str(&quot;4&quot;)
'4'
&gt;&gt;&gt; str(4)
'4'
&gt;&gt;&gt; str(3+2j)
'(3+2j)'
</code></pre>
<p>####2.进制转换</p>
<ul>
<li>int(x[,base])</li>
</ul>
<p>int()函数把数字和字符串转换成一个整数，base为可选的基数。</p>
<pre><code>&gt;&gt;&gt; int(3.3)
3
&gt;&gt;&gt; int(3L)
3
&gt;&gt;&gt; int(&quot;13&quot;)
13
&gt;&gt;&gt; int(&quot;14&quot;,15)
19
</code></pre>
<ul>
<li>long(x[,base])</li>
</ul>
<p>long()函数把数字和字符串转换成长整数，base为可选的基数。</p>
<pre><code>&gt;&gt;&gt; long(&quot;123&quot;)
123L
&gt;&gt;&gt; long(11)
11L
</code></pre>
<ul>
<li>float(x)</li>
</ul>
<p>float()函数把一个数字或字符串转换成浮点数。</p>
<pre><code>&gt;&gt;&gt; float(&quot;12&quot;)
12.0
&gt;&gt;&gt; float(12L)
12.0
&gt;&gt;&gt; float(12.2)
12.199999999999999
</code></pre>
<ul>
<li>hex(x)</li>
</ul>
<p>hex()函数可把整数转换成十六进制数。</p>
<pre><code>&gt;&gt;&gt; hex(16)
'0x10'
&gt;&gt;&gt; hex(123)
'0x7b'
</code></pre>
<ul>
<li>oct(x)</li>
</ul>
<p>oct()函数可把给出的整数转换成八进制数。</p>
<pre><code>&gt;&gt;&gt; oct(8)
'010'
&gt;&gt;&gt; oct(123)
'0173'
</code></pre>
<ul>
<li>complex(real[,imaginary])</li>
</ul>
<p>complex()函数可把字符串或数字转换为复数。</p>
<pre><code>&gt;&gt;&gt; complex(&quot;2+1j&quot;)
(2+1j)
&gt;&gt;&gt; complex(&quot;2&quot;)
(2+0j)
&gt;&gt;&gt; complex(2,1)
(2+1j)
&gt;&gt;&gt; complex(2L,1)
(2+1j)
</code></pre>
<p>####3.数据结构</p>
<ul>
<li>tuple(x)</li>
</ul>
<p>tuple()函数把序列对象转换成tuple。</p>
<pre><code>&gt;&gt;&gt; tuple(&quot;hello world&quot;)
('h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd')
&gt;&gt;&gt; tuple([1,2,3,4])
(1, 2, 3, 4)
</code></pre>
<ul>
<li>list(x)</li>
</ul>
<p>list()函数可将序列对象转换成列表。如：</p>
<pre><code>&gt;&gt;&gt; list(&quot;hello world&quot;)
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
&gt;&gt;&gt; list((1,2,3,4))
[1, 2, 3, 4]
</code></pre>
<p>###序列处理函数</p>
<p>常用函数中的len()、max()和min()同样可用于序列。</p>
<ul>
<li>filter(function,list)</li>
</ul>
<p>调用filter()时，它会把一个函数应用于序列中的每个项，并返回该函数返回真值时的所有项，从而过滤掉返回假值的所有项。</p>
<pre><code>&gt;&gt;&gt; def nobad(s):
    ... return s.find(&quot;bad&quot;) == -1
    ...
&gt;&gt;&gt; s = [&quot;bad&quot;,&quot;good&quot;,&quot;bade&quot;,&quot;we&quot;]
&gt;&gt;&gt; filter(nobad,s)
['good', 'we']
</code></pre>
<ul>
<li>map(function,list[,list])</li>
</ul>
<p>map()函数把一个函数应用于序列中所有项，并返回一个列表。</p>
<pre><code>&gt;&gt;&gt; import string
&gt;&gt;&gt; s=[&quot;python&quot;,&quot;zope&quot;,&quot;linux&quot;]
&gt;&gt;&gt; map(string.capitalize,s)
['Python', 'Zope', 'Linux']
</code></pre>
<p>map()还可同时应用于多个列表。如：</p>
<pre><code>&gt;&gt;&gt; import operator
&gt;&gt;&gt; s=[1,2,3]; t=[3,2,1]
&gt;&gt;&gt; map(operator.mul,s,t) # s[i]*t[j]
[3, 4, 3]
</code></pre>
<p>如果传递一个None值，而不是一个函数，则map()会把每个序列中的相应元素合并起来，并返回
该元组。如：</p>
<pre><code>&gt;&gt;&gt; a=[1,2];b=[3,4];c=[5,6]
&gt;&gt;&gt; map(None,a,b,c)
[(1, 3, 5), (2, 4, 6)]
</code></pre>
<ul>
<li>reduce(function,seq[,init])</li>
</ul>
<p>reduce()函数获得序列中前两个项，并把它传递给提供的函数，获得结果后再取序列中的下一项，连同结果再传递给函数，以此类推，直到处理完所有项为止。</p>
<pre><code>&gt;&gt;&gt; import operator
&gt;&gt;&gt; reduce(operator.mul,[2,3,4,5]) # ((2*3)*4)*5
120
&gt;&gt;&gt; reduce(operator.mul,[2,3,4,5],1) # (((1*2)*3)*4)*5
120
&gt;&gt;&gt; reduce(operator.mul,[2,3,4,5],2) # (((2*2)*3)*4)*5
240
</code></pre>
<hr>
<p>The end!</p>
<p>To be continue</p>
<p>wklken</p>
<p>Email: <a href="mailto:wklken@yeah.net">wklken@yeah.net</a></p>
<p>Blog:  <a href="http://blog.csdn.net/wklken">http://blog.csdn.net/wklken</a></p>
<p>2013-03-16</p>
<p>转载请注明出处，谢谢</p>
]]></content>
		</item>
		
		<item>
			<title>Python-基础-函数小结</title>
			<link>https://wklken.me/posts/2013/03/16/python-base-function.html</link>
			<pubDate>Sat, 16 Mar 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/03/16/python-base-function.html</guid>
			<description>###函数 ####简介及定义 #####简介 函数是可重用的程序段 它们允许你给一块语句一个名称，然后你可以在你的程序的任何地方使用这个名称任意多</description>
			<content type="html"><![CDATA[<p>###函数</p>
<p>####简介及定义</p>
<p>#####简介
函数是可重用的程序段</p>
<p>它们允许你给一块语句一个名称，然后你可以在你的程序的任何地方使用这个名称任意多次地运行这个语句块。</p>
<p>函数作用：最大程度重用和最小化代码冗余，流程分解</p>
<p>#####定义</p>
<p>使用def可以声明一个函数，完整的函数由函数名，参数以及函数实现语句组成。</p>
<p>一般形式:</p>
<pre><code>def &lt;函数名&gt; (参数列表):
   &lt;函数语句&gt;
   return &lt;返回值&gt;
</code></pre>
<p>其中参数和返回值非必须</p>
<p>注意，没有返回值的return语句等价于return None</p>
<p>示例</p>
<pre><code>:::python
def hello():
    print 'hello world'

hello()

l = [1,2,3,]
def sum(List)
    result = 0
    for i in List:
        result = result+i
    return result
sum(l)
</code></pre>
<p>注意：</p>
<ul>
<li>
<p>def是可执行代码</p>
<p>def可以出现在任一语句可以出现的地方，甚至是嵌套在其他语句中</p>
<pre><code>:::python
if test:
   def func():
     …..
else:
   def func():
     ….
</code></pre>
</li>
<li>
<p>def创建一个函数对象并将其赋值给一个变量</p>
<p>函数可以赋值给一个变量，类似别名,othername = func，在需要时再调用</p>
</li>
<li>
<p>return将结果对象发送给调用者</p>
</li>
<li>
<p>函数是通过赋值传递的（对象引用）</p>
</li>
<li>
<p>参数，返回值及变量并不不要类型声明</p>
</li>
</ul>
<p>####参数</p>
<p>#####简介</p>
<p>#####参数类型
主要明确几个概念</p>
<ul>
<li>形参实参</li>
</ul>
<p>函数中参数的名称位形参</p>
<p>调用函数提供给函数的调用值位实参</p>
<pre><code>:::python
def sum(a, b):  #a, b形参
    return a + b

sum(1, 5)  # 1, 5实参
</code></pre>
<ul>
<li>位置参数</li>
</ul>
<p>默认情况下，函数参数位位置参数,即通过位置指定实参</p>
<p>格局传递位置确认值</p>
<pre><code>:::python
def sum(a, b):
    return a + b
sum(1, 2)   # a=1, b=2
sum(2, 1)   # a=2, b=1
</code></pre>
<ul>
<li>默认参数</li>
</ul>
<p>对于一些函数，你可能希望它的一些参数是 可选 的，如果用户不想要为这些参数提供值的话，这些参数就使用默认值</p>
<pre><code>:::python
def sum(a, b=1): #b为默认参数
    return a + b
sum(1,2) #3
sum(1)   #2
</code></pre>
<p>注意，默认参数定义必须在位置参数之后。</p>
<pre><code>:::python
def sum(a, b=1) #有效
def sum(b=1, a) #无效
</code></pre>
<p>特别注意，默认参数值只计算一次，当值为可变对象时需特别注意：</p>
<pre><code>&gt;&gt;&gt; def sum(a, b=[]):
...     b.append(a)
...     return b
...
&gt;&gt;&gt; sum(1)
[1]
&gt;&gt;&gt; sum(2)
[1, 2]

#若是不想缺省值在连续调用中被保留
def sum(a, b=None):
    if not b:
        b = []
    b.append(a)
    return b
</code></pre>
<ul>
<li>关键参数</li>
</ul>
<p>使用名字（关键字）而不是位置来给函数指定实参，keyword = value形式</p>
<p>可以通过命名为参数赋值</p>
<pre><code>:::python
def sum(a=0, b=0):
    return a + b
sum()  # 0 + 0
sum(a=1) # 1 + 0
sum(b=1) # 0 + 1
sum(a=1, b=2) # 1 + 2
sum(b=1, a=2) # 2 + 1
</code></pre>
<ul>
<li>可变长参数</li>
</ul>
<p>具有任意个参数，而不必将所有参数定义</p>
<p>参数开头以*</p>
<p>*seq  序列位置参数,接收一个元组，包含了所有没有出现在形式参数列表中的参数值</p>
<pre><code>&gt;&gt;&gt; def test(*seq):
...     print seq
...
&gt;&gt;&gt; test(1,2,3,4)
(1, 2, 3, 4)
&gt;&gt;&gt; test(*[1,2,3])
(1, 2, 3)
</code></pre>
<p>**dic  关键参数,接收一个字段，包含了所有未出现在形式参数列表中的关键字参数</p>
<pre><code>&gt;&gt;&gt; def test2(**dic):
...     print dic
...
&gt;&gt;&gt; test2(a=1,b=2)
{'a': 1, 'b': 2}
&gt;&gt;&gt; test2(**{'a':1, 'b':2})
{'a': 1, 'b': 2}
</code></pre>
<p>#####参数匹配
参数匹配方式：</p>
<p>定义:</p>
<p>在函数定义中，参数顺序：</p>
<p>任何一版参数name,默认参数name=value,之后*name，之后任何name,最后**name</p>
<pre><code>def func(name) 位置或变量名匹配
def func(name=value) 参数存在默认值，若是没有传入，使用默认值
def func(*name) 匹配并收集在元组中所有包含位置的参数
def func(**name) 匹配并收集在字典中所有包含位置的参数
def func(*args,name)参数必须在调用中按照关键字传递
</code></pre>
<p>调用:</p>
<p>在函数调用中，参数出现顺序：</p>
<p>位置参数value,关键字参数name=value，*sequence形式组合，最后是**dict</p>
<pre><code>func(value) 常规，位置匹配
func(name = value) 关键字匹配，通过变量名
func(*sequence) 以name传递所有对象，并作为独立的基于位置的参数
func(**dict) 以name传递所有关键字/值，并作为独立的关键字参数
</code></pre>
<p>可以混合定义和传递多种类型参数，参数匹配顺序：</p>
<p>位置分配分关键字-&gt;匹配变量名-&gt;而外非关键字分配到*name-&gt;额外关键字参数分配到**name中-&gt;默认值分配给头部未得到匹配的参数</p>
<pre><code>#!/usr/bin/env python
# encoding: utf-8


def test(a, b, c=1, d=2, *tp, **dp):
    print 'a', a
    print 'b', b
    print 'c', c
    print 'd', d
    print 'tp', tp
    print 'dp', dp
    print &quot;==========&quot;

test(1, 2)

test(1,2,d=3)

test(1,2,3,4,5,e=6,f=7)
</code></pre>
<p>结果:</p>
<pre><code>a 1
b 2
c 1
d 2
tp ()
dp {}
==========
a 1
b 2
c 1
d 3
tp ()
dp {}
==========
a 1
b 2
c 3
d 4
tp (5,)
dp {'e': 6, 'f': 7}
==========
</code></pre>
<p>#####参数传递</p>
<p>不可变参数“通过值”进行传递，整数，字符串通过对象引用，而非拷贝一份，指向同一内存，效果很像创建拷贝</p>
<pre><code>&gt;&gt;&gt; a = 1
&gt;&gt;&gt; def p(param):
...     param = 2
...
&gt;&gt;&gt; a
1
&gt;&gt;&gt; p(a)
&gt;&gt;&gt; a
1
</code></pre>
<p>可变对象通过“指针”进行传递的,改变函数的可变对象参数的值也许会对调用者造成影响</p>
<pre><code>&gt;&gt;&gt; b
[1, 2, 3]
&gt;&gt;&gt;
&gt;&gt;&gt; def d(param):
...     param.append(9)
...
&gt;&gt;&gt; b
[1, 2, 3]
&gt;&gt;&gt; d(b)
&gt;&gt;&gt; b
[1, 2, 3, 9]
</code></pre>
<p>特别注意：</p>
<p>实参为可变对象，传递传引用，形参改变对象引用不会对实参造成影响</p>
<pre><code>&gt;&gt;&gt; b = [1, 2, 3]
&gt;&gt;&gt; def c(param):  #执行到这里，param赋值，param和b指向同一内存位置
...     param = [4, 5, 6]  #param再次被赋值，指向另一位置，不会对原先造成影响
...
&gt;&gt;&gt; b
[1, 2, 3]
&gt;&gt;&gt; c(b)
</code></pre>
<p>####lambda
特殊的声明函数方式，用于创建新的函数对象，并且在运行时返回它们【返回函数对象】</p>
<p>lambda是一个表达式，而不是一个语句</p>
<p>lambda主体是一个单个的表达式，而不是一个代码块</p>
<p>用于定义小型的函数，在函数中仅包含单一的参数表达式，而不能包含其他语句，但是可以调用其他函数</p>
<p>语法：</p>
<pre><code>#lambda 参数列表：表达式
lambda arg1,arg2,…argN: expression using args
</code></pre>
<p>为什么使用：函数速写，GUI</p>
<p>例子：</p>
<pre><code>&gt;&gt;&gt; result = lambda x:x**2
&gt;&gt;&gt; result(3)
9
</code></pre>
<p>####函数设计概念
指导原则：</p>
<ul>
<li>耦合性：对于输入使用参数并且对于输出使用return 语句,让函数独立于它外部的东西</li>
<li>耦合性：只有在真正必要的情况下使用全局变量使用参数和返回值</li>
<li>耦合性：不要改变可变类型参数，除非调用者希望如此
特殊，不友好，不可预料的改变</li>
<li>聚合性：每个函数都应该有一个单一的，统一的目标</li>
<li>大小：每个函数都应该相对较小，一个函数只做一件事，保持简单，保持简短</li>
<li>耦合：避免直接改变在另一个模块文件中的变量</li>
</ul>
<p>####内置函数
常用且有点多，独立一章说明</p>
<hr>
<p>The end!</p>
<p>To be continue</p>
<p>wklken</p>
<p>Email: <a href="mailto:wklken@yeah.net">wklken@yeah.net</a></p>
<p>Blog:  <a href="http://blog.csdn.net/wklken">http://blog.csdn.net/wklken</a></p>
<p>2013-03-16</p>
<p>转载请注明出处，谢谢</p>
]]></content>
		</item>
		
		<item>
			<title>Python-基础-文件处理小结</title>
			<link>https://wklken.me/posts/2013/03/16/python-base-file.html</link>
			<pubDate>Sat, 16 Mar 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/03/16/python-base-file.html</guid>
			<description>##文件处理 python常用，主要是用于文件操作，配合字符串操作，数值计算等，完成数据处理。 ###文件模式 打开一个文件，返回一个文件对象。可</description>
			<content type="html"><![CDATA[<p>##文件处理</p>
<p>python常用，主要是用于文件操作，配合字符串操作，数值计算等，完成数据处理。</p>
<p>###文件模式</p>
<p>打开一个文件，返回一个文件对象。可以用open()或者file()，建议使用前者</p>
<pre><code>file_object = open(file_name, access_mode = ‘r’, buffering = -1)
file_name：打开的文件名,若非当前路径，需指出具体路径
mode:可选参数，文件打开模式
bufsize:可选参数，是否使用缓存
</code></pre>
<p>####mode</p>
<pre><code>模式	    描述
r	    以读方式打开文件，可读取文件信息.文件必须已存在
w	    以写方式打开文件，可向文件写入信息。存在则清空，不存在创建
a	    以追加方式打开文件，文件指针自动移到文件尾。追加
r+	    以读写方式打开文件，可对文件进行读和写操作。
w+	    消除文件内容，然后以读写方式打开文件。
a+	    以读写方式打开文件，并把文件指针移到文件尾。
b	    以二进制模式打开文件，而不是以文本模式。该模式只对Windows或Dos有效，类Unix的文件是用二进制模式进行操作的

U      通用换行符支持，任何系统下的文件, 不管换行符是什么, 使用U模式打开时, 换行符都会被替换为NEWLINE(\n)
</code></pre>
<p>+ 代表同时作为输入和输出文件，可以对相同文件进行读写</p>
<p>b代表二进制数据处理</p>
<p>和 r/w/a组合.
r+ 使用读写方式打开， rb二进制读</p>
<p>注意，在mode中使用b，b不能作为第一个字符出现</p>
<p>####bufsize
bufsize取值	描述</p>
<pre><code>0	禁用缓冲
1	行缓冲，只缓冲一行
\&gt;1	指定缓冲区的大小，定制
&lt;1	系统默认的缓冲区大小,m默认
</code></pre>
<p>###文件对象属性
常用几个:</p>
<p>file.name 文件名</p>
<p>file.encoding文件使用编码,None 时使用系统默认编码</p>
<p>file.mode Access文件打开时使用的额访问模式</p>
<p>file.closed表文件已关闭，否则False</p>
<p>file.newlines未读取到分隔符时为None，包含行结束符的列表</p>
<p>file.softspace为0表示在输出一数据后，加上一空格，1表示不加，内部使用</p>
<pre><code>:::python
f = open(&quot;a.py&quot;, &quot;r&quot;)
print f.name
print f.mode
print f.encoding
print f.closed
</code></pre>
<p>###文件操作
获取帮助</p>
<pre><code>:::python
dir(f)
help(f.seek)
</code></pre>
<p>操作列表</p>
<pre><code>#读
file.read(size=-1) 	     从文件读取size个字节，未给定或为负，读取所有
file.readline(size=-1) 	读取并返回一行，或返回最大size个字符,包括\n
file.readlines(sizeint=0) 	读取所有行并返回列表，若给定sizeint&gt;0，返回总和大约为sizeint字节的行, 实际读取值可能比sizhint较大, 因为需要填充缓冲区

#写
file.write(str) 	向文件中写入字符串(文本或二进制)
file.writelines(seq) 	写入多行，向文件中写入一个字符串列表，注意，要自己加入每行的换行符

#其他
file.seek(off,whence=0) 	从文件中给移动指针，从whence(0起始，1当前，2末尾)偏移off个字节，正结束方向移动，负往开始方向移动
file.tell()	返回当前文件中的位置。获得文件指针位置
file.truncate(size=file.tell())	截取文件到最大size个字节，默认为当前文件位置
file.close()    关闭打开的文件,垃圾回收机制也会在文件对象的引用计数降至0的时候自动关闭文件
file.fileno()    返回文件描述符(file descriptor FD 整型)是一个整数, 可以用在如os模块的read方法等一些底层操作上.
file.flush()    刷新文件内部缓冲,直接把内部缓冲区的数据立刻写入文件, 而不是被动的等待输出缓冲区写入.
file.isatty()	判断file是否是类tty设备
file.next()	    返回文件下一行
</code></pre>
<p>###最佳实践</p>
<p>1.养成手动close</p>
<pre><code>:::python
f = open('a.py')
……
f.close()
</code></pre>
<p>2.读取大文件</p>
<p>方法一:一次性读入,去左右空白+换行符，文件太大不建议这么做</p>
<pre><code>:::python
f = open('bigdata')
lines = [ line.strip() for line in f.readlines()]
…..
f.close()
</code></pre>
<p>方法二:迭代</p>
<pre><code>:::python
f = open('bigdata')
for line in f:
    line = line.strip()
…..
f.close()
</code></pre>
<p>3.上下文管理器
用with，等价与上面方法二，注意不用显式close</p>
<pre><code>&gt;&gt;&gt; with open('a.py') as f:
...     for line in f:
...         line = line.strip()
</code></pre>
<p>4.去除换行符/跳过空行</p>
<pre><code>:::python
line = line.rstrip()

if not line.strip():
    # if line is empty
    continue            # skip it
</code></pre>
<p>###StringIO
StringIO“虚拟文件”不是永久的</p>
<p>如果不保存它（如将它写入一个真正的文件，或者使用 shelve 模块或数据库），则程序结束时，它将消失</p>
<pre><code>&gt;&gt;&gt; import cStringIO
&gt;&gt;&gt; fs = cStringIO.StringIO()
&gt;&gt;&gt; fs.write('hello world')
&gt;&gt;&gt; fs.getvalue()
'hello world'
</code></pre>
<p>###pickle
pickle任意python对象和字符串之间的序列化
类似java序列化存储到文件的过程</p>
<pre><code>:::python
# encoding: utf-8

import pickle

d = {'a':1,'b':2}

f = open('datafile.pkl','wb')
pickle.dump(d,f)
f.close()

f=open('datafile.pkl','rb')
e=pickle.load(f)
print e
</code></pre>
<p>###struct</p>
<p>能够构造并解析打包的二进制数据</p>
<pre><code>:::python
#!/usr/bin/env python
# encoding: utf-8

import struct
f = open('data.bin','wb')
data = struct.pack('hhl', 1, 2, 3)
f.write(data)
f.close()

f = open('data.bin','rb')
data = f.read()
values = struct.unpack('hhl',data)
print values
</code></pre>
<p>###其他相关模块</p>
<pre><code>base64  二进制字符串和文本字符串之间的编码/解码操作
binascii  二进制和ascii编码的二进制字符串间的编码/解码操作
bz2  访问BZ2格式的压缩文件
csv  访问csv文件(逗号分割文件)
filecmp   用于比较目录和文件
fileinput  提供多个文本文件的行迭代器
getopt/optparse  提供了命令行参数的解析/处理
glob/fnmatch  提供Unix样式的通配符匹配功能
gzip/zlib   读写GNU zip(gzip)文件(压缩需要zlib模块)
shutil  提供高级文件访问能力
c/StringIO   对字符串对象提供类文件接口
tarfile  读写TAR归档文件, 支持压缩文件
tempfile   创建一个临时文件(名)
uu  格式的编码和解码
zipfile  用于读取ZIP归档文件的工具
</code></pre>
<p>The end!</p>
<p>To be continue</p>
<p>wklken</p>
<p>Email: <a href="mailto:wklken@yeah.net">wklken@yeah.net</a></p>
<p>Blog:  <a href="http://blog.csdn.net/wklken">http://blog.csdn.net/wklken</a></p>
<p>2013-03-16</p>
<p>转载请注明出处，谢谢</p>
]]></content>
		</item>
		
		<item>
			<title>Python-基础-简介/入门</title>
			<link>https://wklken.me/posts/2013/03/16/python-base-introduction.html</link>
			<pubDate>Sat, 16 Mar 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/03/16/python-base-introduction.html</guid>
			<description>##Python简介及入门 ###python 为什么是python 选择自己喜欢的语言，这往往不容易，更多的是根据需求 话说，之前是java，大学用了三年+实习半</description>
			<content type="html"><![CDATA[<p>##Python简介及入门</p>
<p>###python
为什么是python</p>
<p>选择自己喜欢的语言，这往往不容易，更多的是根据需求</p>
<p>话说，之前是java，大学用了三年+实习半年，后来入职做测试开发后，碰到了python</p>
<p>到最后，转python开发了</p>
<p>写起来快，缩进，不用打花括号，省手指，读起来舒服…..</p>
<p>喜欢，貌似不需要什么太牛的理由，用着顺手舒服就行</p>
<p>什么语言之争，编辑器之战啥的，能忽略就忽略吧，能无视就无视吧，工具，够用，用这舒服就ok了，浪费口水精力争来争去图个啥呢</p>
<p>Life is short, I use python!</p>
<p>###简介
python介绍: 到官网自个看</p>
<p>有兴趣可以看看: 解释性语言+动态类型语言+强类型语言</p>
<p>优点缺点: 自己google</p>
<p>国际惯例</p>
<p>交互模式:(主要拿来试验，可以试试 <a href="http://ipython.org/">ipython</a> 支持tab自动补全)</p>
<pre><code>$python
&gt;&gt;&gt; print 'hello world'
</code></pre>
<p>脚本</p>
<pre><code>#!/usr/bin/env python
print 'hello world'
</code></pre>
<p>###环境及其他
基本安装: 自己google[安装和配置一搜一大把，基本技能，不解释,貌似很久之前我也写了一篇<a href="http://blog.csdn.net/wklken/article/details/6311292">http://blog.csdn.net/wklken/article/details/6311292</a></p>
<p>环境：</p>
<pre><code>建议python2.7 + easy_install + pip + virtualenv + ipython
</code></pre>
<p>开发工具:</p>
<pre><code>vim/sublimetext2/eclipse+pydev/pycharm
建议初学idle或者pydev吧，用着顺手就行，
</code></pre>
<p>关于编码风格：</p>
<p>谷歌的：<a href="http://google-styleguide.googlecode.com/svn/trunk/pyguide.html">http://google-styleguide.googlecode.com/svn/trunk/pyguide.html</a></p>
<p>中文: <a href="http://www.bsdmap.com/articles/zh-google-python-style-guide/">http://www.bsdmap.com/articles/zh-google-python-style-guide/</a></p>
<hr>
<p>##入门</p>
<p>不扯，开始</p>
<p>###缩进</p>
<p>Python 函数没有明显的 begin 和 end，没有标明函数的开始和结束的花括号。唯一的分隔符是一个冒号 (:)，接着代码本身是缩进的。</p>
<p>例子：</p>
<pre><code>:::python
#函数
def func(value):
    print value  #缩进

    if value == 1:
        value += 1
    elif value == 2:
        pass
    else:
        value += 10
</code></pre>
<p>###标识符
变量是标识符的例子。 标识符 是用来标识 某样东西 的名字。在命名标识符的时候，你要遵循这些规则：</p>
<p>1.python中的标识符是区分大小写的。</p>
<p>2.标示符以字母或下划线开头，可包括字母，下划线和数字,大小写敏感</p>
<p>3.以下划线开头的标识符是有特殊意义的。</p>
<ul>
<li>以单下划线开头（_foo）的代表不能直接访问的类属性，需通过类提供的接口进行访问，不能用“from xxx import *”而导入；</li>
<li>以双下划线开头的（__foo）代表类的私有成员；</li>
<li>以双下划线开头和结尾的（<strong>foo</strong>）代表python里特殊方法专用的标识，如__init__（）代表类的构造函数。</li>
</ul>
<p>4.标识符不能是保留字</p>
<pre><code>and            elif        global	or    yield
assert         else        if	    pass
break          except      import   print
class          exec        in	    raise
continue       finally	   is       return
def            for         lambda   try
del            from        not      while
</code></pre>
<p>###变量</p>
<p>赋值语句</p>
<ul>
<li>赋值语句建立对象引用值</li>
<li>变量名在首次赋值时会被建立</li>
<li>变量名在引用前必须先赋值,不能引用未声明赋值的变量</li>
</ul>
<p>赋值方式</p>
<ul>
<li>简单赋值</li>
</ul>
<p>Variable(变量)=Value(值)</p>
<pre><code>s = ‘spam’
</code></pre>
<ul>
<li>多变量赋值</li>
</ul>
<p>python中原始的元组和列表赋值语句形成，最后已被通用化，以接受右侧可以是是任何类型的序列，只要长度相等即可。注意，长度一定相等</p>
<p>Variable1,variable2,&hellip;=Value1,Value2,&hellip;</p>
<pre><code>s,h = ‘a’,’b’  元组赋值，位置性  【常用】
[s,h] =[‘a’,’b’] 列表赋值，位置性
a,b,c,d = ‘spam’ 序列赋值，通用性
a,*b = ‘spam’ 拓展序列解包（python3）
</code></pre>
<ul>
<li>多目标赋值</li>
</ul>
<p>a=b=variable</p>
<pre><code>:::python
s = h = ‘spam’ 多目标赋值
</code></pre>
<p>注意：多个变量内存中指向同一对象，对于可变类型需要，修改一个会对其他造成影响</p>
<ul>
<li>自变赋值</li>
</ul>
<p>如+=，-=，*=等。</p>
<p>在自变赋值中，python仅计算一次，而普通写法需计算两次；</p>
<p>自变赋值会修改原始对象，而不是创建一个新对象。</p>
<pre><code>:::python
s +=42 增强赋值
x += y
</code></pre>
<p>优点:</p>
<pre><code>程序员输入更少

左侧只需计算一次，优化技术自动原处修改，更快
l +=[] 原处修改
l = l+[] 复制，生成新的对象
</code></pre>
<p>###运算符
一个表达式可以分解为运算符和操作数</p>
<p>运算符 的功能是完成某件事，它们由如+这样的符号或者其他特定的关键字表示</p>
<p>运算符需要数据来进行运算，这样的数据被称为 操作数</p>
<p>运算符优先顺序列表(从最高到最低)</p>
<pre><code>运算符	描述
'expr'	字符串转换
{key:expr,...}	字典
[expr1,expr2...]	列表
(expr1,expr2,...)	元组
function(expr,...)	函数调用
x[index:index]	切片
x[index]	下标索引取值
x.attribute	属性引用
~x	按位取反
+x，-x	正，负
x**y	幂
x*y，x/y，x%y	乘，除，取模
x+y，x-y	加，减
x&lt;&lt;y，x&gt;&gt;y	移位
x&amp;y	按位与
x^y	按位异或
x|y	按位或
x&lt;y，x&lt;=y，x==y，x!=y，x&gt;=y，x&gt;y	比较
x is y，x is not y	等同测试
x in y，x not in y	成员判断
not x	逻辑否
x and y	逻辑与
x or y	逻辑或
lambda arg,...:expr	Lambda匿名函数
</code></pre>
<p>结合规律</p>
<p>运算符通常由左向右结合，即具有相同优先级的运算符按照从左向右的顺序计算</p>
<p>计算顺序</p>
<p>默认地，运算符优先级表决定了哪个运算符在别的运算符之前计算。然而，如果你想要改变它们的计算顺序，你得使用圆括号。好的做法：默认对复杂的运算加括号，而不是依赖于默认结合和计算顺序</p>
<p>###真值
####真值测试</p>
<ul>
<li>任何非零数字或非空对象都为真</li>
<li>数字零，空对象以及特殊对象None都为假</li>
<li>比较和相等测试都会递归地运用到数据结构中</li>
<li>比较和相等测试会返回True或False</li>
</ul>
<p>真值表</p>
<pre><code>对象/常量	值
&quot;&quot;	假
&quot;string&quot;	真
0	假
2&gt;=1	真
-2&lt;=-1	真
()空元组	假
[]空列表	假
{}空字典	假
None	假
</code></pre>
<p>####布尔表达式
三种布尔表达式运算符</p>
<pre><code>:::python
x and y
x or y
not x
</code></pre>
<p>####比较</p>
<ul>
<li>数字通过相对大小进行比较</li>
<li>字符串时按照字典顺序的，一个字符一个字符比较</li>
<li>列表和元组从左到右对每部分的内容进行比较</li>
<li>字典通过排序后的键值列表进行比较</li>
<li>数字混合类型比较在python3是错误的，但是python2.6支持，固定但任意的排序规则</li>
</ul>
<p>####布尔数</p>
<ul>
<li>有两个永远不改变的值True，False</li>
<li>布尔是整型的子类，但不能被再继承</li>
<li>没有__nonzero__()方法的对象的默认值是True</li>
<li>对于值为0的任何数字或空集，值False</li>
<li>在数学运算中，Bollean值的True和False分别对应于1和0</li>
</ul>
<p>###基本控制流</p>
<p>####if
基本的条件测试语句，用来判断可能遇到的不同情况，并针对不同的情况进行操作</p>
<p>基本形式</p>
<pre><code>if &lt;条件&gt;:
   &lt;语句&gt;
elif &lt;条件&gt;:
   &lt;语句&gt;
else:
   &lt;语句&gt;
</code></pre>
<p>注意</p>
<p>python根据缩进判断, elif和else部分是可选的</p>
<p>例子:</p>
<pre><code>:::python
a = 1
b = 2
c = 3;d=4 #两个放一句用分号隔开，不过建议分行

if a &lt; b and c &lt; d:
    print(&quot;branch a&quot;)
elif a == b:
    print(&quot;branch b&quot;)
else:
    print(&quot;branch c&quot;)
</code></pre>
<p>switch</p>
<p>python 本身没有 switch 语句，若需要，用if/elif/else实现完成同样的工作,某些情况可以考虑用字典</p>
<p>也可以用dict的形式</p>
<p>if/else三元运算符</p>
<pre><code>A = ((X and Y) or Z)
A = Y  if X else Z
例： a = ‘t’ if x else ‘a’
</code></pre>
<p>####for</p>
<ul>
<li>基本语法</li>
</ul>
<p>循环控制语句，可以用于循环遍历某一序列</p>
<p>else块可选，在循环终止的时候执行，若是break终止循环，else不执行</p>
<p>格式：</p>
<pre><code>for &lt;对象变量&gt; in &lt;对象集合&gt;:
    if&lt;条件&gt;:
       break
    if&lt;条件&gt;:
       continue
    &lt;其他语句&gt;
else:
    &lt;其他语句&gt;
</code></pre>
<p>注意：</p>
<pre><code>1.对象集合可以是列表，字典以及元组等
2.for..in循环对于任何序列都适用
3.for遍历一个字典时，遍历的是字典的键
</code></pre>
<ul>
<li>rang &amp; xrange</li>
</ul>
<p>可以通过range()函数产生一个整数列表，完成计数循环</p>
<pre><code>range([start,] stop[, step])

start可选参数，起始数
stop终止数，若为x，产生从0-(x-1)的整数列表
step可选参数，步长,未写默认为1
</code></pre>
<p>range(1,5)   包含序列为 [1,2,3,4]</p>
<p>xrange和range区别</p>
<p>(python3.x的可无视)</p>
<p>在Range的方法中，它会生成一个list的对象，但是在XRange中，它生成的却是一个xrange的对象，当返回的东西不是很大的时候，或者在一个循环里，基本上都是从头查到底的情况下，这两个方法的效率差不多。但是，当返回的东西很大，或者循环中常常会被Break出来的话，还是建议使用XRange，这样既省空间，又会提高效率。</p>
<pre><code>&gt;&gt;&gt; print range(1, 5)
[1, 2, 3, 4]
&gt;&gt;&gt; print xrange(1, 5)
xrange(1, 5)
</code></pre>
<p>在上面语句中，range返回了一个普通List，而xrange返回了一个特定的xrange类型的对象。
由于 xrange 方法也创建整数 list（其使用相同参数），所以它与 range 方法非常相似。但是，xrange 方法仅在需要时才在 list 中创建整数。当需要迭代大量整数时，xrange 方法更适用，因为它不会创建极大的 list，那样会消耗大量计算机内存。</p>
<p>####while
与if语句类似，含一个条件测试语句，循环，允许重复执行一个语句块。</p>
<p>可选else语句块，同for的else块。</p>
<p>格式：</p>
<pre><code>while &lt;条件&gt;:
   if &lt;条件&gt;:
      break
   if &lt;条件&gt;:
      continue
   &lt;其他语句&gt;
else:
   &lt;语句&gt;
</code></pre>
<p>说明:</p>
<ul>
<li>while循环条件变为False的时候，else块才被执行</li>
<li>若是使用break结束循环，while可选的else块不执行</li>
<li>python没有do while或do until循环语句</li>
</ul>
<p>####break &amp; continue &amp; pass</p>
<p>break，终止循环语句，停止循环，若是for/while循环中终止，其else不执行</p>
<p>continue,结束当前，进入下一轮循环 - 跳到最近所在循环的开头处（来到循环首行）</p>
<p>pass 什么事也不做，只是空占位语句,它用于那些语法上必须要有什么语句，但程序什么也不做的场合</p>
<p>循环else块 ：只有循环正常离开时才会执行，即</p>
<p>如果你从for或while循环中break终止 ，任何对应的循环else块将不执行。
记住，break语句也可以在for循环中使用</p>
<p>###其他
####编写循环的技巧：
在迭代过程中修改迭代序列不安全（只有在使用链表这样的可变序列时才会有这样的情况）。如果你想要修改你迭代的序列（例如，复制选择项），你可以迭代它的复本。使用切割标识就可以很方便的做到这一点</p>
<pre><code>&gt;&gt;&gt; for x in a[:]: # make a slice copy of the entire list
...    if len(x) &gt; 6: a.insert(0, x)
</code></pre>
<p>在字典中循环时，关键字和对应的值可以使用 iteritems() 方法同时解读出来</p>
<pre><code>&gt;&gt;&gt; knights = {'gallahad': 'the pure', 'robin': 'the brave'}
&gt;&gt;&gt; for k, v in knights.iteritems():
...     print k, v
...
gallahad the pure
robin the brave
</code></pre>
<p>在序列中循环时，索引位置和对应值可以使用 enumerate() 函数同时得到。</p>
<pre><code>&gt;&gt;&gt; for i, v in enumerate(['tic', 'tac', 'toe']):
...     print i, v
</code></pre>
<hr>
<p>The end!</p>
<p>To be continue</p>
<p>wklken</p>
<p>Email: <a href="mailto:wklken@yeah.net">wklken@yeah.net</a></p>
<p>Blog:  <a href="http://blog.csdn.net/wklken">http://blog.csdn.net/wklken</a></p>
<p>2013-03-16</p>
<p>转载请注明出处，谢谢</p>
]]></content>
		</item>
		
		<item>
			<title>Python-基础-字符串小结</title>
			<link>https://wklken.me/posts/2013/03/10/python-base-string.html</link>
			<pubDate>Sun, 10 Mar 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/03/10/python-base-string.html</guid>
			<description>##字符串 ###简介 字符串序列 用于表示和存储文本，python中字符串是不可变的，一旦声明，不能改变 通常由单引号(&amp;rsquo; )，双引号(</description>
			<content type="html"><![CDATA[<p>##字符串</p>
<p>###简介
字符串序列
用于表示和存储文本，python中字符串是不可变的，一旦声明，不能改变</p>
<p>通常由单引号(&rsquo; )，双引号(&quot; )，三引号(&rsquo;&rsquo;&rsquo;   &ldquo;&rdquo;&quot;)包围</p>
<p>其中三引号可以由多行组成，编写多行文本的快捷语法，常用语文档字符串，在文件的特定地点，被当做注释。便捷的多行注释</p>
<p>Python实际三类字符串：</p>
<pre><code>1.通常意义字符串(str)
2.原始字符串，以大写R 或 小写r开始，r''，不对特殊字符进行转义
3.Unicode字符串，u'' basestring子类
</code></pre>
<p>在 Python 中，字符串是“不可改变的序列”</p>
<pre><code>1.不可变

2.满足序列基本操作，按位置存取，切片及索引
</code></pre>
<p>####字符串</p>
<p>1.获得帮助:</p>
<pre><code>&gt;&gt;&gt; help(str)
&gt;&gt;&gt; dir(str)
&gt;&gt;&gt; help(str.replace)
</code></pre>
<p>2.不可变性</p>
<p>在创建之后就不能就地改变（同java），不能通过对其某一位置进行赋值而改变字符
划分为不可变序列，这些字符串所包含的字符存在从左到右的顺序，不可在原处修改。
python中字符串相当于一个不可变序列的列表，一旦声明，每个字符位置固定</p>
<p>意味着若想改变，必须新建一个！</p>
<pre><code>&gt;&gt;&gt;s=’spam’
&gt;&gt;&gt;s[0]=’k’   #TypeError
#修改字符串类似java,重新赋值
s = ‘k’ + s[1:]
</code></pre>
<p>####原始字符串
原始字符串常量，r”abcd”，(r/R)即去掉了反斜线转义机制。关闭转义机制，即\不再表示转义</p>
<p>用处：</p>
<p>1.正则表达式</p>
<p>用于处理正则表达式，减少反斜杠</p>
<pre><code>:::python
p4search = re.compile(r'\s*')
</code></pre>
<p>2.系统路径</p>
<p>可以方便地表示系统路径</p>
<pre><code>:::python
path = r'e:\book'
</code></pre>
<p>####unicode字符串</p>
<p>Unicode是书写国际文本的标准方法。</p>
<p>Python允许你处理Unicode文本——你只需要在字符串前加上前缀u或U。例如，u&quot;This is a Unicode string.&quot;</p>
<p>BP: 在你处理文本文件的时候使用Unicode字符串，特别是当你知道这个文件含有用非英语的语言写的文本。</p>
<p>###常用操作
1.基本操作</p>
<pre><code>+   :string1+string2 	#联接字符串,将后一个串链接到前一个串的后面
    Python不允许在+表达式中出现其他类型，需要手工转【这点不同于java】‘abc’+str(9)
*   :string*n			#创建一个新字符串重复n次原来的串
[]  :string[n]			#从字符串中获取对应位置的一个字符
[:] :string[n:m]		#截取字符串,如果为:m从头到m如果为n:从n到尾
in  :char in string	#判断一个字符是否在串中,如果在返回为真(True)
not in :char not in string #判断一个字符是否不在串中,如果在返回为真(True)
r/R : r/Rstring		#禁止转义字符的实际意义,整个字符为原始意义
len() : 长度len(s)
</code></pre>
<p>2.类型转换</p>
<ul>
<li>字符串和数字相互转换</li>
</ul>
<p>字符串到数字
int/float/long</p>
<p>数字到字符串
str</p>
<pre><code>&gt;&gt;&gt; int(42)
42
&gt;&gt;&gt; int('42')
42
&gt;&gt;&gt; str(42)
'42'
&gt;&gt;&gt; float('42.0')
42.0
&gt;&gt;&gt; str(42.0)
'42.0'
</code></pre>
<p>或者使用string模块的函数</p>
<p>s：进行转换的字符串, base:可选，目标进制</p>
<pre><code>:::python
import string
string.atoi(s[,base])  	#base默认为10，如果为0,那么s就可以是012或0x23这种形式的字符串，如果是16那么s就只能是0x23或0X12这种形式的字符串 

string.atol(s[,base]) 		#转成long 

string.atof(s[,base]) 		#转成float
</code></pre>
<ul>
<li>字符串和列表的转换</li>
</ul>
<p>字符串转列表：</p>
<pre><code>:::python
s=’spam’
l = list(s)

l2 = &quot;hello world&quot;.spilt()
</code></pre>
<p>列表转字符串</p>
<pre><code>:::python
k = ‘’.join(l)
</code></pre>
<p>注意，不能join列表中的非字符串
 </p>
<p>3.修改字符串</p>
<pre><code>:::python
s =  s + ’a’
s = s[3:] + ‘b’
s = s.replace(‘pl’,’pa’)

a = '' #赋值空

del a  #整个变量删除
</code></pre>
<p>4.索引和分片</p>
<p>索引s[i]</p>
<pre><code>s[0]首个

s[-1] = s[len(s)-1] 倒数第一个
</code></pre>
<p>分片s[i:j]</p>
<pre><code>不含上边界，s[1:3] 取[1-2]
s[1:]取1到结束   s[:3] 取开始到2
s[:-1]开始到倒数第二个
s[:]开始到结尾，相当于一个复制
s[1:10:2]  取1-9，步长=2
s[a🅱️-2] 步长为负数，两个边界意义反转了，表示从b+1到a,步长-2
s=’abcdefg’  
s[5:1:-1] 得到 fedc
s[1:3] == s[slice(1,3)]  内置函数
</code></pre>
<p>###字符串格式化</p>
<p>这里只介绍基本字符串格式化，扩展在后续篇幅介绍
%c 单个字符
%d 十进制整数
%o 八进制整数
%s 字符串
%x 十六进制整数，其中字母小写
%X 十六进制整数，其中字母大写</p>
<pre><code>&gt;&gt;&gt; str = &quot;so %s a day!&quot;
&gt;&gt;&gt; str % 'beautiful'
'so beautiful a day!'

&gt;&gt;&gt; '{0} is {1}'.format('a','b')
'a is b'

&gt;&gt;&gt; template = &quot;{0}, {1} and {2}&quot;
&gt;&gt;&gt; template.format('a', 'b', 'c')
'a, b and c'
</code></pre>
<p>  
###内建函数列表
【字符串方法是python文本处理头号工具】
####string.capitalize()
字符串第一个字符大写
####string.center(width,[,fill])
原字符居中，空格填充至width长度
####string.count(str,beg=0,end=len(string))
获得字符串中某一个子串的数目,计算出现次数，可指定范围
####string.decode(encoding=’UTF-8’,errors=’strict’)
解码字符串，出错默认报ValueError,除非errors是ignore或replace
####string.encode(encoding=’UTF-8’,errors=’strict’)
####string.endswith(suffix,beg=0,end=len(string))
是否以**结尾
####string.expandtabs(tabsize=8)
把字符串中tab转为空格，默认8个
####string.find(str,beg=0,end=len(stirng))
检测是否包含str，存在返回开始索引，否则返回-1
####string.index(str,begin=0,end=len(string))
同find，不存在报异常,ValueError
####string.isalnum()
至少一个字符，且所有字符均为字母或数字，True. 检测字符串是否只包含0-9A-Za-z
####string.isalpha()
至少一个字符，所有字符都是字母，True. 检测字符串是否只包含字母
####string.isdecimal()
只包含十进制数，True
####stirng.isdigit()
只包含数字，True. 检测字符串是否仅包含数字
####string.islower()
至少一个区分大小写字符且所有字符小写，True. 检测字符串是否均为小写字母
####string.isnumeric()
只含数字字符,True
####string.isspace()
只包含空格，True. 检测字符串是否均为空白字符
####string.istitle()
标题化字符，True. 检测字符串中的单词是否为首字母大写
####string.isupper()
至少一个区分大小写字符且所有字符大写，True. 检测字符串是否均为大写字母
####string.join(seq)
以string作为分隔符，seq中所有元素合并为新的字符串. 将原字符串插入参数字符串中的每两个字符之间
####string.ljust(width)
返回一个原字符串左对齐，空格补充至长度width
####string.lower()
转小写. 将字符串全部转为小写
####string.lstrip()
截掉左侧的空格
####string.partition(str)
= find+split，从str出现第一个位置，截断为pre_str,str,after_str元组，不含str则pre_str=str
string.replace(str1,str2,num=string.count(str1))
替换，指定不超过num次，可作为模板实现
####string.rfind(str,beg=0,end=len(string))
同find，右边开始
####string.rindex(str,beg=0,end=len(string))
同index,右边开始
####string.rjust(width)
右对齐，空格补齐
####string.rpartition(str)
同partition，右边开始
####string.rstrip([chars])
清理右侧空白，包括换行符，返回处理后字符串
####string.split(str=””, maxsplit =string.count(str))
以str切片，可指定分割次数, 分割字符串，返回列表，默认分隔符空格
####string.splitlines(num=string.count(‘\n’))
s.splitlines([keepends])
按行分隔，可指定分割次数
####string.startswith(obj,beg=0,end=len(string))
以str开头，True. 检测字符串是否以某一子串开头
####string.strip([obj])
在string上执行lstrip和rstrip
####string.swapcase
反转string中大小写. 字符串中小写转大写，大写转小写
####string.title()
标题花，单词首字母大写，其余小写
####string.translate(str,del=””)
s.translate(table)
根据str给出表转换string字符，要过滤的字符放在del参数中
####string.upper()
转大写. 将字符串全部转为大写
####string.zfill(width)
返回长度width的字符串，原字符串右对齐，前面填充0
####len(string)
获取字符串的长度</p>
<p>###最佳实践
1.循环中用到长度</p>
<pre><code>:::python
while i &lt; len(stri):
#修改
size = len(stri)
while i &lt; size
</code></pre>
<p>2.字符串追加</p>
<pre><code>:::python
l = ['a', 'b']
result = ''
for i in l:
    result += i
#修改
result = ''.join(l)
</code></pre>
<p>###其他
1.转义符</p>
<p>几个常用：</p>
<pre><code>\n换行，\\反斜杠
\t制表  \'单引号
\r回车 \&quot;双引号
</code></pre>
<p>###后续需扩展</p>
<pre><code>字符串编码详解
字符串格式化
正则表达式
字符串涉及常用模块(序列化/文本包装等)
</code></pre>
<p>The end!</p>
<p>To be continue</p>
<p>wklken</p>
<p>Email: <a href="mailto:wklken@yeah.net">wklken@yeah.net</a></p>
<p>Blog:  <a href="http://blog.csdn.net/wklken">http://blog.csdn.net/wklken</a></p>
<p>2013-03-10</p>
<p>转载请注明出处，谢谢</p>
]]></content>
		</item>
		
		<item>
			<title>Python-基础-数字处理相关模块</title>
			<link>https://wklken.me/posts/2013/03/10/python-base-extra-math.html</link>
			<pubDate>Sun, 10 Mar 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/03/10/python-base-extra-math.html</guid>
			<description>##数字处理相关模块 ###math 1.简介 &amp;gt;&amp;gt;&amp;gt; import math &amp;gt;&amp;gt;&amp;gt;dir(math) #这句可查看所有函数名列表 &amp;gt;&amp;gt;&amp;gt;help(math) #查看具体定义及函数0原型 2.常用函数 ceil(x) 取顶 floor(x) 取底 fabs(x) 取绝对值 factorial (x) 阶乘 hypot(x,y) sqrt(x*x+y*y) pow(x,y)</description>
			<content type="html"><![CDATA[<p>##数字处理相关模块</p>
<p>###math
1.简介</p>
<pre><code>&gt;&gt;&gt; import math
&gt;&gt;&gt;dir(math)          #这句可查看所有函数名列表
&gt;&gt;&gt;help(math)         #查看具体定义及函数0原型
</code></pre>
<p>2.常用函数</p>
<pre><code>ceil(x) 取顶
floor(x) 取底
fabs(x) 取绝对值
factorial (x) 阶乘
hypot(x,y)  sqrt(x*x+y*y)
pow(x,y) x的y次方
sqrt(x) 开平方
log(x)
log10(x)
trunc(x)  截断取整数部分
isnan (x)  判断是否NaN(not a number)
degree (x) 弧度转角度
radians(x) 角度转弧度
</code></pre>
<p>另外该模块定义了两个常量:</p>
<pre><code>e = 2.718281828459045
pi = 3.141592653589793
</code></pre>
<p>###random
1.简介</p>
<p>random是用于生成随机数，我们可以利用它随机生成数字或者选择字符串</p>
<pre><code>:::python
import random
</code></pre>
<p>2.常用函数</p>
<p>####random.random()</p>
<p>用于生成一个随机浮点数：range[0.0,1.0)</p>
<pre><code>&gt;&gt;&gt; import random
&gt;&gt;&gt; random.random()
0.999410896951364
</code></pre>
<p>####random.uniform(a,b)
用于生成一个指定范围内的随机浮点数，a,b为上下限</p>
<p>只要a!=b,就会生成介于两者之间的一个浮点数，若a=b，则生成的浮点数就是a</p>
<pre><code>&gt;&gt;&gt; random.uniform(10,20)
13.224754825064881
&gt;&gt;&gt; random.uniform(20,10)
14.104410713376437
&gt;&gt;&gt; random.uniform(10,10)
10.0
</code></pre>
<p>####random.randint(a,b)
用于生成一个指定范围内的整数，a为下限，b为上限，生成的随机整数a&lt;=n&lt;=b;</p>
<p>若a=b，则n=a；若a&gt;b，报错</p>
<pre><code>&gt;&gt;&gt; random.uniform(10,10)
10.0
&gt;&gt;&gt; random.randint(10,20)
15
&gt;&gt;&gt; random.randint(10,10)
10
&gt;&gt;&gt; random.randint(20,10)
Traceback (most recent call last):
……
ValueError: empty range for randrange() (20,11, -9)
</code></pre>
<p>####random.randrange([start], stop, [,step])
从指定范围内，按指定基数递增的集合中获取一个随机数，基数缺省值为1</p>
<pre><code>&gt;&gt;&gt; random.randrange(10,100,5)
95
&gt;&gt;&gt; random.randrange(10,100,5)
45
</code></pre>
<p>####random.choice(sequence)
从序列中获取一个随机元素，参数sequence表示一个有序类型，并不是一种特定类型，泛指list，tuple，字符串等</p>
<pre><code>&gt;&gt;&gt; random.choice([1,2,3,4])
1
&gt;&gt;&gt; random.choice([1,2,3,4])
3
&gt;&gt;&gt; random.choice('hello')
'e'
</code></pre>
<p>####random.shuffle(x[, random])
用于将一个列表中的元素打乱</p>
<pre><code>&gt;&gt;&gt; a = [1,2,3,4,5]
&gt;&gt;&gt; random.shuffle(a)
&gt;&gt;&gt; a
[4, 5, 2, 1, 3]
&gt;&gt;&gt; random.shuffle(a)
&gt;&gt;&gt; a
[3, 2, 5, 1, 4]
</code></pre>
<p>####random.sample(sequence, k)
从指定序列中随机获取k个元素作为一个片段返回，sample函数不会修改原有序列</p>
<pre><code>&gt;&gt;&gt; a = [1,2,3,4,5]
&gt;&gt;&gt; random.sample(a,3)
[1, 4, 5]
&gt;&gt;&gt; random.sample(a,3)
[1, 2, 5]
&gt;&gt;&gt; a
[1, 2, 3, 4, 5]
</code></pre>
<p>###decimal
1.简介</p>
<p>默认，浮点数学缺乏精确性</p>
<p>decimal 模块提供了一个 Decimal 数据类型用于浮点数计算。相比内置的二进制浮点数实现 float
这个类型有助于</p>
<pre><code>金融应用和其它需要精确十进制表达的场合，
控制精度，
控制舍入以适应法律或者规定要求，
确保十进制数位精度，或者用户希望计算结果与手算相符的场合。
</code></pre>
<p>Decimal 重现了手工的数学运算，这就确保了二进制浮点数无法精确保有的数据精度。 高精度使 Decimal 可以执行二进制浮点数无法进行的模运算和等值测试。</p>
<p>2.使用</p>
<pre><code>&gt;&gt;&gt; from decimal import Decimal
&gt;&gt;&gt; Decimal('0.1') / Decimal('0.3')
Decimal('0.3333333333333333333333333333')

&gt;&gt;&gt; from decimal import getcontext
&gt;&gt;&gt; getcontext().prec = 4 #设置全局精度
&gt;&gt;&gt; Decimal('0.1') / Decimal('0.3')
Decimal('0.3333')
</code></pre>
<p>###fractions
分数类型</p>
<p>构造</p>
<pre><code>&gt;&gt;&gt; from fractions import Fraction
&gt;&gt;&gt; Fraction(16, -10)  #分子分母
Fraction(-8, 5)
&gt;&gt;&gt; Fraction(123)   #分子
Fraction(123, 1)

&gt;&gt;&gt; Fraction('3/7')   #字符串分数
Fraction(3, 7)

&gt;&gt;&gt; Fraction('-.125')  #字符串浮点数
Fraction(-1, 8)

&gt;&gt;&gt; Fraction(2.25)  #浮点数
Fraction(9, 4)

&gt;&gt;&gt; from decimal import Decimal
&gt;&gt;&gt; Fraction(Decimal('1.1')) #Decimal
Fraction(11, 10)
</code></pre>
<p>计算</p>
<pre><code>&gt;&gt;&gt; from fractions import Fraction
&gt;&gt;&gt; a = Fraction(1,2)
&gt;&gt;&gt; a
Fraction(1, 2)
&gt;&gt;&gt; b = Fraction('1/3')
&gt;&gt;&gt; b
Fraction(1, 3)
&gt;&gt;&gt; a + b
Fraction(5, 6)
&gt;&gt;&gt; a - b
Fraction(1, 6)
</code></pre>
<p>The end!</p>
<p>To be continue</p>
<p>wklken</p>
<p>Email: <a href="mailto:wklken@yeah.net">wklken@yeah.net</a></p>
<p>Blog:  <a href="http://blog.csdn.net/wklken">http://blog.csdn.net/wklken</a></p>
<p>2013-03-10</p>
<p>转载请注明出处，谢谢</p>
]]></content>
		</item>
		
		<item>
			<title>Python-基础-集合小结</title>
			<link>https://wklken.me/posts/2013/03/10/python-base-set.html</link>
			<pubDate>Sun, 10 Mar 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/03/10/python-base-set.html</guid>
			<description>##集合 ###简介 python的set和其他语言类似, 是一个无序不重复元素集, 基本功能包括关系测试和消除重复元素. sets 支持 x in set, len(set), 和 for x in se</description>
			<content type="html"><![CDATA[<p>##集合</p>
<p>###简介
python的set和其他语言类似, 是一个无序不重复元素集, 基本功能包括关系测试和消除重复元素.</p>
<p>sets 支持 x in set, len(set), 和 for x in set。</p>
<p>集合对象还支持union(联合), intersection(交), difference(差)和sysmmetric difference(对称差集)等数学运算.</p>
<p>作为一个无序的集合，sets 不记录元素位置或者插入点。因此，sets 不支持 indexing, slicing, 或其它类序列（sequence-like）的操作。</p>
<p>set为可变集合</p>
<p>frozenset为固定集合</p>
<p>可变集合特有的方法: add, remove, discard, pop, clear, 这些接受对象的方法, 参数必须是可哈希的</p>
<p>###声明</p>
<p>用集合的工厂方法 set()和 frozenset():</p>
<p>set</p>
<pre><code>&gt;&gt;&gt; s = set('cheeseshop')
&gt;&gt;&gt; s
set(['c', 'e', 'h', 'o', 'p', 's'])
</code></pre>
<p>frozenset</p>
<pre><code>&gt;&gt;&gt; b = frozenset([1,2,3,2])
&gt;&gt;&gt; b
frozenset([1, 2, 3])
&gt;&gt;&gt; b.add(4)
Traceback (most recent call last):
  File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
AttributeError: 'frozenset' object has no attribute 'add'
</code></pre>
<p>Set 和 ImmutableSet</p>
<p>字符串-&gt;字符集</p>
<pre><code>&gt;&gt;&gt; set('hello')
set(['h', 'e', 'l', 'o'])
</code></pre>
<p>列表/元组-&gt;集合</p>
<pre><code>&gt;&gt;&gt; set([1,2,3,2,1])
set([1, 2, 3])
&gt;&gt;&gt; set((1,2,3,2,1))
set([1, 2, 3])
</code></pre>
<p>甚至是 字典-&gt;集合</p>
<pre><code>&gt;&gt;&gt; a = {'name':'tom','age':22,'score':22}
&gt;&gt;&gt; set(a)
set(['age', 'score', 'name'])
</code></pre>
<p>###常用操作</p>
<p>####成员关系</p>
<pre><code>&gt;&gt;&gt; h = set('hello')
&gt;&gt;&gt; h
set(['h', 'e', 'l', 'o'])
&gt;&gt;&gt; 'l' in h
True
&gt;&gt;&gt; 'l' not in h
False
</code></pre>
<p>####新增删除
新增单个元素
s.add(x)</p>
<p>向 set “s”中增加元素 x</p>
<pre><code>&gt;&gt;&gt; a = set([1,2,3,4,2])
&gt;&gt;&gt; a
set([1, 2, 3, 4])
&gt;&gt;&gt; a.add(2)
&gt;&gt;&gt; a
set([1, 2, 3, 4])
&gt;&gt;&gt; a.add(5)
&gt;&gt;&gt; a
set([1, 2, 3, 4, 5])
</code></pre>
<p>新增多个元素</p>
<p>s.update(t)</p>
<p>s |= t</p>
<pre><code>&gt;&gt;&gt; a = set([1,2,3])
&gt;&gt;&gt; b = set([2,3,4])
&gt;&gt;&gt; a.update(b)
&gt;&gt;&gt; a
set([1, 2, 3, 4])
&gt;&gt;&gt; b
set([2, 3, 4])
</code></pre>
<p>删除</p>
<p>s.remove(x)</p>
<p>从 set “s”中删除元素 x, 如果不存在则引发 KeyError</p>
<pre><code>&gt;&gt;&gt; a
set([1, 2, 3, 4, 5])
&gt;&gt;&gt; a.remove(4)
&gt;&gt;&gt; a
set([1, 2, 3, 5])
&gt;&gt;&gt; a.remove(4)
Traceback (most recent call last):
  File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
KeyError: 4
</code></pre>
<p>s.discard(x)</p>
<p>如果在 set “s”中存在元素 x, 则删除</p>
<pre><code>&gt;&gt;&gt; a
set([1, 2, 3, 5])
&gt;&gt;&gt; a.discard(3)
&gt;&gt;&gt; a
set([1, 2, 5])
&gt;&gt;&gt; a.discard(3)
&gt;&gt;&gt; a
set([1, 2, 5])
</code></pre>
<p>s.pop()</p>
<p>删除并且返回 set “s”中的一个不确定的元素, 如果为空则引发 KeyError</p>
<pre><code>&gt;&gt;&gt; a
set([1, 5])
&gt;&gt;&gt; a.pop()
1
&gt;&gt;&gt; a.pop()
5
&gt;&gt;&gt; a.pop()
Traceback (most recent call last):
  File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
KeyError: 'pop from an empty set'
</code></pre>
<p>s.clear()</p>
<p>删除 set “s”中的所有元素</p>
<pre><code>&gt;&gt;&gt; a
set([1, 2, 3, 4])
&gt;&gt;&gt; a.clear()
&gt;&gt;&gt; a
set([])

&gt;&gt;&gt; b = set([1,2,3])
&gt;&gt;&gt; del b
</code></pre>
<p>####集合间操作
注意，集合操作可以通过函数进行，也存在等价的运算符</p>
<p>1.交集</p>
<p>s.union(t)  等价  s | t</p>
<p>返回一个新的 set 包含 s 和 t 中的每一个元素</p>
<p>2.并集</p>
<p>s.intersection(t) 等价 s &amp; t</p>
<p>返回一个新的 set 包含 s 和 t 中的公共元素</p>
<p>3.差集</p>
<p>s.difference(t)  等价  s - t</p>
<p>返回一个新的 set 包含 s 中有但是 t 中没有的元素</p>
<p>4.差分集</p>
<p>s.symmetric_difference(t) 等价 s ^ t</p>
<p>返回一个新的 set 包含 s 和 t 中不重复的元素</p>
<pre><code>&gt;&gt;&gt; a = set([1,2,3])
&gt;&gt;&gt; b = set([2,3,4])
&gt;&gt;&gt; a.symmetric_difference(b)
set([1, 4])
</code></pre>
<p>5.关系判断</p>
<p>s.issubset(t) 等价 s &lt;= t</p>
<p>测试是否 s 中的每一个元素都在 t 中</p>
<p>s.issuperset(t) 等价 s &gt;= t</p>
<p>测试是否 t 中的每一个元素都在 s 中</p>
<p>6.浅拷贝</p>
<pre><code>&gt;&gt;&gt; a
set([1, 2, 3])
&gt;&gt;&gt; b = a.copy()
&gt;&gt;&gt; b
set([1, 2, 3])
</code></pre>
<p>###其他
1.用的较少的函数</p>
<p>s.intersection_update(t)  等价 s &amp;= t</p>
<p>返回只保留含有 set “t”中元素的 set “s”</p>
<p>s.difference_update(t)  等价 s -= t</p>
<p>返回删除了 set “t”中含有的元素后的 set “s”</p>
<p>s.symmetric_difference_update(t) 等价  s ^= t</p>
<p>返回含有 set “t”或者 set “s”中有而不是两者都有的元素的 set “s”</p>
<h3 id="补充">补充</h3>
<p>集合推导(2013-08-13)</p>
<pre><code>&gt;&gt;&gt; { x for x in range(10) }
set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
</code></pre>
<hr>
<p>The end!</p>
<p>To be continue</p>
<p>wklken</p>
<p>Email: <a href="mailto:wklken@yeah.net">wklken@yeah.net</a></p>
<p>Blog:  <a href="http://blog.csdn.net/wklken">http://blog.csdn.net/wklken</a></p>
<p>2013-03-10</p>
<p>转载请注明出处，谢谢</p>
]]></content>
		</item>
		
		<item>
			<title>Python-基础-元组小结</title>
			<link>https://wklken.me/posts/2013/03/09/python-base-tuple.html</link>
			<pubDate>Sat, 09 Mar 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/03/09/python-base-tuple.html</guid>
			<description>##元组 ###简介 tuple 1.元组是以圆括号“()”包围的数据集合，不同成员以“,”分隔。通过下标进行访问 2.不可变序列，可以看做不可变的列表，与</description>
			<content type="html"><![CDATA[<p>##元组</p>
<p>###简介</p>
<p>tuple</p>
<p>1.元组是以圆括号“()”包围的数据集合，不同成员以“,”分隔。通过下标进行访问</p>
<p>2.不可变序列，可以看做不可变的列表，与列表不同：元组中数据一旦确立就不能改变（所以没有类似列表的增删改操作，只有基本序列操作）</p>
<p>3.支持任意类型，任意嵌套以及常见的序列操作</p>
<p>4.元组通常用在使语句或用户定义的函数能够安全地采用一组值的时候，即被使用的元组的值不会改变</p>
<p>###声明及使用</p>
<pre><code>:::python
t = ()  #空元组
t =(1,)  #单个元素元组，注意逗号必须
t =(1,2,3)


1 in t #判断
2 not in t

#其他同序列基本操作：分片，索引
print t[0]
print t[-1]
print t[:2]

#不会对原来元组造成影响
print t+(4,5)  #返回新元组(1,2,3,4,5)
print t * 2    #(1,2,3,1,2,3)
t.index(1)
t.count(1)

#列表元组转换
l = [1,2,3]
lt = tuple(l)
tl = list(lt)
lt_sorted = sorted(l)  #对元组进行排序，返回是列表

#字符串转元组(得到字符元组序列)
print tuple('hello)   #('h','e','l','l','o')
</code></pre>
<p>tuple没有append/extend/remove/pop等增删改操作
tuple没有find</p>
<p>查看帮助</p>
<pre><code>:::python
help(tuple)
</code></pre>
<p>###用途</p>
<p>1.赋值</p>
<pre><code>:::python
t = 1,2,3   #等价 t = (1, 2, 3)
x, y, z = t   #序列拆封，要求左侧变量数目和右侧序列长度相等
</code></pre>
<p>2.函数多个返回值</p>
<pre><code>:::python
def test():
    return (1,2)

x, y = test()
</code></pre>
<p>3.传参[强制不改变原始序列]</p>
<pre><code>:::python
def print_list(l):
    t = tuple(l)   #或者t = l[:]
    dosomething()
</code></pre>
<p>4.字符串格式化</p>
<pre><code>:::python
print '%s is %s years old' % ('tom', 20)
</code></pre>
<p>5.作为字典的key</p>
<p>###优点
1.性能</p>
<p>tuple比列表操作速度快</p>
<p>若需要定义一个常量集，或者是只读序列，唯一的操作是不断遍历之，使用tuple代替list</p>
<pre><code>&gt;&gt;&gt; a = tuple(range(1000))
&gt;&gt;&gt; b = range(1000)
&gt;&gt;&gt; def test_t():
...     for i in a:
...             pass
...
&gt;&gt;&gt; def test_l():
...     for i in b:
...             pass
...
&gt;&gt;&gt; from timeit import Timer
&gt;&gt;&gt; at = Timer(&quot;test_t()&quot;, &quot;from __main__ import test_t&quot;)
&gt;&gt;&gt; bt = Timer(&quot;test_l()&quot;, &quot;from __main__ import test_l&quot;)
</code></pre>
<p>简单测试</p>
<pre><code>&gt;&gt;&gt; at.repeat(3, 100000)
[1.526214838027954, 1.5191287994384766, 1.5181210041046143]

&gt;&gt;&gt; bt.repeat(3, 100000)
[1.5545141696929932, 1.557785987854004, 1.5511009693145752]
</code></pre>
<p>2.不可变性</p>
<p>对不需要的数据进行“写保护”，使代码更加安全</p>
<p>不可变性，若在程序中以列表形式传递对象集合，可能在任何地方被改变，使用元组，则不能</p>
<p>不可变性只适用于元组本身顶层而非其内容，例如元组内部的列表可以修改</p>
<pre><code>:::python
l = [1,2,3]
t = (1,2,l)
l.append(4)
</code></pre>
<p>不可变性提供了某种完整性，规范化，确保不会被修改，保持某种固定关系</p>
<p>修改的方法</p>
<pre><code>tuple -&gt; list -&gt; tuple
</code></pre>
<h3 id="补充">补充</h3>
<blockquote>
<p>元组定义易错点</p>
</blockquote>
<p>2013-08-31</p>
<p>一个和多个的区别是定义1个后面必须有&quot;,&ldquo;否则就是 类型 &ldquo;str&rdquo;</p>
<pre><code>&gt;&gt;&gt; t = (&quot;a&quot;)
&gt;&gt;&gt; type (t)
&lt;type 'str'&gt;
&gt;&gt;&gt; t = (&quot;a&quot;,)
&gt;&gt;&gt; type (t)
&lt;type 'tuple'&gt;
&gt;&gt;&gt; t = (&quot;a&quot;,&quot;b&quot;)
&gt;&gt;&gt; type (t)
&lt;type 'tuple'&gt;
&gt;&gt;&gt; t = &quot;a&quot;,
&gt;&gt;&gt; type(t)
&lt;type 'tuple'&gt;
</code></pre>
<p>The end!</p>
<p>To be continue</p>
<p>wklken</p>
<p>Email: <a href="mailto:wklken@yeah.net">wklken@yeah.net</a></p>
<p>Blog:  <a href="http://blog.csdn.net/wklken">http://blog.csdn.net/wklken</a></p>
<p>2013-03-09</p>
<p>转载请注明出处，谢谢</p>
]]></content>
		</item>
		
		<item>
			<title>Python-基础-字典小结</title>
			<link>https://wklken.me/posts/2013/03/09/python-base-dict.html</link>
			<pubDate>Sat, 09 Mar 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/03/09/python-base-dict.html</guid>
			<description>##字典 ###定义 dictionary 1.键值对的集合(map) 2.字典是以大括号“{}”包围的数据集合 3.字典是无序的，在字典中通过键来访问成员。 可变的，可</description>
			<content type="html"><![CDATA[<p>##字典</p>
<p>###定义
dictionary</p>
<p>1.键值对的集合(map)</p>
<p>2.字典是以大括号“{}”包围的数据集合</p>
<p>3.字典是无序的，在字典中通过键来访问成员。
可变的，可嵌套，可以原处修改扩展等，不产生新的字典</p>
<p>4.字典的键，可以是字符串(大小写敏感)，数字常量或元组（不可变类型），同一个字典的键可以混用类型。字典的键必须是可哈希的</p>
<p>元组作为键的条件是，元组内的值都是不可变类型</p>
<pre><code>  a = (1,2)  #可以作为键
  b = (1,2,[3,4])  #不可以
</code></pre>
<p>5.字典的值可以是任意类型，可以嵌套，可以自由修改</p>
<p>###声明</p>
<p>创建字典的几种方式:</p>
<p>1.基本</p>
<pre><code>:::python
d = {} #空字典
d = {'name':'tom', 'age':22}
#等价
d = {}
d['name'] = 'tom'
d['age'] = 22
</code></pre>
<p>2.dict</p>
<pre><code>:::python
d = dict() #空
d = dict(name='tom', age=22)

d = dict([('name','tom'), ('age',22)])
#等价
keys = ['name','age']
values = ['tom', 22]
d = dict(zip(keys,values))
</code></pre>
<p>3.fromkeys</p>
<p>不指定default_value的话，默认None</p>
<pre><code>&gt;&gt;&gt; dict.fromkeys(['name','age'],'default_value')
{'age': 'default_value', 'name': 'default_value'}
</code></pre>
<p>###基本操作
0.获取帮助</p>
<pre><code>:::python
help(dict)
</code></pre>
<p>1.判定键是否存在于字典中</p>
<pre><code>:::python
if k in d:   #k not in
    dosomething()
</code></pre>
<p>2.读取</p>
<pre><code>:::python
d = {'a':1, 'b':2}

print d['a']  #得到1，但是若键不存在，将引发异常KeyError。慎用，建议不使用

print d.get('c', 3) #得到3,get方法，若是键不存在，返回第二个参数default_value.若是没有设default_value返回None
</code></pre>
<p>处理missing-key错误三种方式，根据具体需要</p>
<pre><code>:::python
if k in d:
    print d[k]

try:
    print d[k]
except KeyError:
    dosomething()

print d.get(k, default)
#等价 d[k] if k in d else default
</code></pre>
<p>3.遍历</p>
<p>方式1：</p>
<pre><code>:::python
for key in d:
    print key, d[key]
#等价 for key in d.keys()
</code></pre>
<p>方式2：</p>
<pre><code>:::python
for key,value in d.items():
    print key, value
</code></pre>
<p>4.修改
方式1：某个键值对</p>
<pre><code>:::python
d['key'] = 'newvalue'
</code></pre>
<p>方式2：批量添加或更新</p>
<pre><code>:::python
#另一个字典
d.update({'key':'newvalue'})  #这里支持一整组值

#元组列表
d.update( [ ('a',1), ('b',2) ] ) #每个元组两个元素，(key,value)

#**key
d.update(c=3, e=4)
</code></pre>
<p>5.删除</p>
<pre><code>:::python
del d['key']
value = d.pop('key') #删除并返回值
d.clear() #清空
</code></pre>
<p>6.其他：</p>
<pre><code>len(d)   #长度
d.keys()  #key列表
d.values()  #value列表
d.items()   #(key, value) 列表
c = d.copy()   #浅拷贝
#返回迭代器，节省内存
d.iterkeys()
d.itervalues()
d.iteritems()
d.setdefault('name', 'ken') #若原来没有，设置，否则原值不变
</code></pre>
<p>###其他</p>
<p>1.字典排序
按照key排序</p>
<pre><code>:::python
keys = d.keys()
keys.sort()
for key in keys:
    print d.get(key)
</code></pre>
<p>按照value进行排序</p>
<pre><code>:::python
sorted(d.items(), lambda x,y: cmp(x[1],y[1]))
</code></pre>
<p>另外：</p>
<pre><code>:::python
#假设d为字典
sorted(d)  #返回同 sorted(d.keys())，返回的是key排序
</code></pre>
<p>补充 2013-08-31</p>
<p>最快的字典排序方式(性能考虑,来自互联网)</p>
<pre><code>&gt;&gt;&gt; d = {'a':2, 'b':23, 'c':5, 'd':17, 'e':1}
&gt;&gt;&gt; from operator import itemgetter
&gt;&gt;&gt; sorted(d.iteritems(), key=itemgetter(1), reverse=True)
[('b', 23), ('d', 17), ('c', 5), ('a', 2), ('e', 1)]
</code></pre>
<p>2.自定义对象作为key</p>
<p>必须:</p>
<pre><code>:::python
def __hash__(self):
    pass
def __eq__(self, other):
    pass
</code></pre>
<p>3.字典拷贝
浅拷贝：</p>
<pre><code>:::python
c = d.copy() #
</code></pre>
<p>深拷贝必须用copy模块</p>
<pre><code>:::python
form copy import deepcopy
c = deepcopy(d)
</code></pre>
<p>4.一种使用场景
假设有一个很大的列表l,假设10w条记录</p>
<p>有一个小列表b，要判断b中元素是否在l中</p>
<p>如果:</p>
<pre><code>:::python
for i in b:
    if i in l:
        dosomething()
</code></pre>
<p>你会发现非常非常慢&hellip;因为第二个in语句，会遍历10w条….</p>
<p>改进:</p>
<pre><code>:::python
d = dict.fromkeys(l)
for i in b:
    if i in d:
        dosomething()
#空间换时间，O(n) -&gt; O(1)
</code></pre>
<p>5.利用dict进行函数switch</p>
<p>函数调用的switch(2013-09-01)</p>
<pre><code>:::python
def a():
    print &quot;a&quot;
def b():
    print &quot;b&quot;
def default():
    print &quot;default&quot;
apply({1:a, 2:b}.get(x, default))
</code></pre>
<hr>
<p>The end!</p>
<p>To be continue</p>
<p>wklken</p>
<p>Email: <a href="mailto:wklken@yeah.net">wklken@yeah.net</a></p>
<p>Blog:  <a href="http://blog.csdn.net/wklken">http://blog.csdn.net/wklken</a></p>
<p>2013-03-09</p>
<p>转载请注明出处，谢谢</p>
]]></content>
		</item>
		
		<item>
			<title>Python-基础-数字小结</title>
			<link>https://wklken.me/posts/2013/03/09/python-base-math.html</link>
			<pubDate>Sat, 09 Mar 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/03/09/python-base-math.html</guid>
			<description>##数字 python中的数字类型完整工具： 整数&amp;amp;浮点数，复数，固定精度十进制数，有理分数，集合，布尔类型，无穷的整数精度，各种数字内</description>
			<content type="html"><![CDATA[<p>##数字
python中的数字类型完整工具：
整数&amp;浮点数，复数，固定精度十进制数，有理分数，集合，布尔类型，无穷的整数精度，各种数字内置函数和模块</p>
<p>###数类型
Python中有四种类型的数</p>
<pre><code>整数：一般意义上的数，包含十进制(无前缀),八进制(0开头)，十六进制(0x开头),二进制(0b开头)
eg.  2

长整数：无限大小的数,表示范围和内存有关
eg.   2012121200

浮点数：小数或用e/E(10的幂)表示的幂,默认，浮点数学缺乏精确性,可以使用decimal
eg.   3.23      50.2E2

复数：复数的虚部以字母J 或 j结尾
eg.    2+3i
</code></pre>
<p>1.分数</p>
<pre><code>:::python
from fractions import Fraction
x = Fraction(1,3)
y = Fraction(4,6)
</code></pre>
<p>浮点数和分数转换：</p>
<pre><code>(2.5).as_integer_ratio()  #返回元组(5,2)
或者Fraction.from_float(1.75)
</code></pre>
<p>2.复数</p>
<pre><code>a = 实数 + 虚数j
a.real  #实部
a.imag  #虚部
a.conjugate()  #共轭复数
</code></pre>
<p>虚数不能单独存在, 总是和一个值为0.0的实数部分一起构成一个复数</p>
<p>实数部分和虚数部分都是浮点数</p>
<p>###运算符</p>
<pre><code>+加法
-减法
*乘法
**幂次(也可以使用内建的pow()函数来进行幂运算) 
/除法(2.x中是整除,3.x是浮点除)
//取整，商的整数部分强制整除(浮点数也会做整除运算)
%取余(可以使用内建的divmod()函数获得结果和余数的元组)
&amp;位与
|位或
^位异或
~位翻转 x -&gt; -(x+1)
&lt;&lt;左移
&gt;&gt;右移
值不相等 a != b
</code></pre>
<p>运算符优先级，同级从左到右</p>
<p>BP:良好的做法是随时根据优先级需要加入括号，而不是依赖默认优先级</p>
<p>###内置函数
最常用round</p>
<p>1.abs(number)</p>
<p>返回数字的绝对值,</p>
<p>如果是复数, 返回math.sqrt(num.real2 + num.imag2)</p>
<p>2.coerce(number1, number2)</p>
<p>按照类型转换规则,将num1和num2转为同一类型，以元组形式返回</p>
<p>3.divmod(number1, number2)</p>
<p>返回一个包含商和余数的元组</p>
<p>整数返回地板除和取余操作结果</p>
<p>浮点数返回的商部分是math.floor(number1/number2)</p>
<p>复数的商部分是ath.floor((number1/number2).real)</p>
<p>4.pow(num1,num2,mod=1)</p>
<p>同**操作符</p>
<p>第三个为可选参数，即一个余数操作，若存在这个参数，先指数，后与第三个参数取余,性能比pow(x,y)%z更高</p>
<p>5.round(number[, base])</p>
<p>对浮点数进行四舍五入运算,base参数是小数位参数，决定了精确到小数点后几位</p>
<p>6.其他</p>
<p>其他内建及工厂函数在后面类型转换和进制转换中说明</p>
<p>###类型转换</p>
<p>转换工厂函数
主要在类型转换和进制转换中用到</p>
<pre><code>int(str, base=10) 转换为整型数,base为相应的进制;base的范围是2~32,base缺省十进制

long(obj,base=10),  转长整型


float(obj),转浮点数


complex(str),complex(real, imag=0.0), 转复数

bool(obj) 返回obj对象的布尔值,即obj.__nonzero__()方法返回值,没有__nonzero__()方法的对象默认值是True

string()，转字符串

chr(num) ASCII数字值转为字符

ord(chr) 字符转对应ASCII/Unicode值

unichr(num) 接收Unicode码值
</code></pre>
<p>###进制转换
八进制:0o1</p>
<p>十六进制:0x10</p>
<p>二进制0b1</p>
<p>1.十进制到其他</p>
<pre><code>:::python
oct(64)  #-&gt;8
hex(64)  #-&gt;16
bin(64)  #-&gt;2
</code></pre>
<p>接收一个整型对象（任意进制），返回对应值的字符串</p>
<p>2.其他到十进制</p>
<pre><code>:::python
int(‘64’)
int(‘100’,8)
int(‘40’,16)
int(‘1000000’,2)
#或者
eval(‘64’)
eval(‘0o100’)
eval(‘0x40’)
eval(‘0b1000’)
</code></pre>
<p>###其他
1.小整数缓存</p>
<pre><code>&gt;&gt;&gt; a = -5
&gt;&gt;&gt; b = -5
&gt;&gt;&gt; a  is b
True
&gt;&gt;&gt; a = -6
&gt;&gt;&gt; b = -6
&gt;&gt;&gt; a is b
False
&gt;&gt;&gt; a = -6
&gt;&gt;&gt; b = -6
&gt;&gt;&gt; a is b
False
&gt;&gt;&gt; a = 256
&gt;&gt;&gt; b = 256
&gt;&gt;&gt; a is b
True
&gt;&gt;&gt; a = 257
&gt;&gt;&gt; b = 257
&gt;&gt;&gt; a  is b
False
</code></pre>
<p>看python源代码可知，下限-5，上限256被设定为缓存范围。</p>
<p>2.除法
关于传统除法/真正除法/地板除
传统除法</p>
<pre><code>&gt;&gt;&gt; 1/2
0
&gt;&gt;&gt; 1.0/2.0
0.5
</code></pre>
<p>真正除法</p>
<pre><code>&gt;&gt;&gt; from __future__ import division
&gt;&gt;&gt; 1/2
0.5
&gt;&gt;&gt; 1.0/2.0
0.5
</code></pre>
<p>地板除</p>
<pre><code>&gt;&gt;&gt; 1//2
0
&gt;&gt;&gt; 1.0//2.0
0.0
&gt;&gt;&gt; -1//2
-1
</code></pre>
<p>###常用模块</p>
<pre><code>math
random
decimal
operator
</code></pre>
<p>下一篇数学常用模块中说明</p>
<p>The end!</p>
<p>To be continue</p>
<p>wklken</p>
<p>Email: <a href="mailto:wklken@yeah.net">wklken@yeah.net</a></p>
<p>Blog:  <a href="http://blog.csdn.net/wklken">http://blog.csdn.net/wklken</a></p>
<p>2013-03-09</p>
<p>转载请注明出处，谢谢</p>
]]></content>
		</item>
		
		<item>
			<title>写在2012结尾，2013的开始</title>
			<link>https://wklken.me/posts/2013/01/02/summary-06-2012end2013begin.html</link>
			<pubDate>Wed, 02 Jan 2013 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2013/01/02/summary-06-2012end2013begin.html</guid>
			<description>本来已经关电脑了，发现似乎落了些什么，想着昨晚似乎没有像以往写下总结，记录一些东西。 看了下去年的总结: 地址,那时刚工作不久，感觉写的还是很不</description>
			<content type="html"><![CDATA[<p>本来已经关电脑了，发现似乎落了些什么，想着昨晚似乎没有像以往写下总结，记录一些东西。</p>
<p>看了下去年的总结: <a href="https://wklken.me/posts/2011/12/31/summary-02-2011end-2012begin.html">地址</a>,那时刚工作不久，感觉写的还是很不错的。每每回顾，总感觉过去的自己做得比现在好。难道老了么。。。。。</p>
<p>既然去年标题，今年也俗一下，写在2012的结尾，2013的开始。虽然是补的。
思维有些跳跃，很少吃油条，纯粹是跳跃 &gt; - &lt;</p>
<hr>
<p>这一年做了什么呢？</p>
<p>十月以前，没日没夜没心没肺开开心心过了玩玩整整的十个月，工作生活学习，充实，开心。</p>
<p>工作上逐渐熟悉大部分东西了，做事效率什么的也上去了，所以过的也还算轻松。</p>
<p>工具是提高生产力的方式之一。期间继续捣鼓一些工具脚本，有几个还算满意，有几个折戟沉沙颇为可惜。搞完了vim第一版本配置，搞完第一版alias。搞完了sublimetext2首个配置，开始玩github，dropbox+金山快盘+有道笔记。</p>
<p>也瞎折腾了一些东西，注册SAE，搞了个开发者身份，迁移东西，搞了自己的域名和博客，瞎折腾。</p>
<p>然后就是，感觉毕业快一年了，好多东西生疏了，买了几本数据结构和算法的书，同时开始看开放课，博客上也陆续扯了点东西，可惜看着看着直感叹大学白读了。唉。过了那么久，其实剩下的才是真正学到的，忘了的，就忘了吧，最大收获就是，复习后，对很多东西有了重新的认识，会从其他角度来看待问题吧。</p>
<p>年后回来买了个两轮的车，从此周末的1／2不宅了，每周有了新项目，晨起冲山，每每累趴了，饿过了都暗暗发誓lz以后再也不来了，结果下周末又是屁颠屁颠往山里钻。其实每周出去逛逛挺好，目前深有感触，感觉现在深度宅了，很怀念那段山里的日子。每次从山里出来，无论在哪个口，都习惯性跑西湖边逛一圈，然后回公司蹭个水，上上网。现在回想起来，其实杭州大部分景点都去过了，不过，都是路过&hellip;..</p>
<p>然后，是七月，工作满一周年，一年香是什么感觉呢？不好说，不过很难忘。对自个一年理了理，工作一年也算收获颇多，逐渐适应了那环境和氛围，也认同和接受那里的一切，然后那时候想，接着干，三年醇。哪想，生活总是存在变数的。</p>
<p>那时候貌似开始要写日报，每天干干活，总要对自己今天做了什么进行思考，然后，想着想着，每天有收获，但是似乎不多，每每对自己说，熟悉了，要有更多的收获，往往比较困难，但是似乎这很难说服自己，有些迷茫吧，不过迷茫归迷茫，生活还得继续，做好当下认为正确的事情。继续埋头捣鼓代码。</p>
<p>工作也还算顺利，和很多人合作都很愉快，所以效率还好，压力一般，状态不错，很怀念。</p>
<hr>
<p>然后，到了十月。</p>
<p>十月，或者注定不会平静。</p>
<p>有个大会召开了，然后哥激动地去膜拜了一把大神，虽然是远程视频，回头看看，虽然自己喜欢一样东西，业余搞一搞，用得也还算顺风顺水，但是和专业的一比，微不足道，有种从自我感觉良好直接被打落凡尘的挫败感。</p>
<p>事实是，这回打击大了&hellip;&hellip;&hellip;.唉，你妹的纯属自虐。</p>
<p>前前后后想了几天，发现，目前情况继续下去，永远菜鸟水准，当然，这时候相对比较狭隘吧，只追求技术方面的。</p>
<p>然后，花了两个钟头，在博客上写了类似自荐的简历，然后扔了出去。</p>
<p>然后，六天时间，面了三家，都拿到offer。</p>
<p>然后，是最煎熬的时候，选择，是走，是留，去哪，何去何从。</p>
<p>我只想说，这五天，是我最煎熬的时刻。</p>
<p>抉择，总是艰难的吧，这个，真不好选。</p>
<p>然后，是做决定。</p>
<p>从开始，到结束，十一天整。</p>
<p>那句很土的话，我猜得到开始，却猜不到结局。</p>
<p>决定，总是要做的。唉。</p>
<hr>
<p>十一月，是我最后一个月，把离别的愁絮扔到脑后，最后的时光。</p>
<p>善始善终，不留遗憾，做事原则吧。</p>
<p>当最后踏出公司，想想要离开自己这熟悉的一切，这里的人，这里的事，心中还是很不舍的。</p>
<p>这一年，要感谢的人很多，主管，同事，对应的开发&hellip;&hellip;&hellip;</p>
<p>感谢，这里所有的人，所有的一切。</p>
<p>Thank you , for everything.</p>
<p>PS：写了一个很长的邮件，回头看了下，有点糗，但是那时心境，也就释然，发了篇博文还被秒杀了，真心不爽，所以决定写到自己的地盘去&hellip;&hellip;&hellip;</p>
<p>第二天，飞机南飘。</p>
<p>第四天，入职。</p>
<p>十二月，入职，干活，第一个月还算顺利，该搞定的都搞定了，开始适应吧，每天多出来好几个钟头，却又开始迷茫了，主要原因是不知道怎么花。尼玛没时间迷茫，有时间还迷茫，这叫个什么事呀。</p>
<p>这几天继续思考吧，好好规划规划时间的花法。</p>
<p>为何十一月十二月写的那么少，这两个月发生了太多的事情，很多记忆，太过深刻。每每，还没从原来的状态中恢复。</p>
<p>好吧，我是鸵鸟，在回避一些感觉。</p>
<p>南下，带了一本相册，几本书，一箱衣服。</p>
<hr>
<p>2012</p>
<p>2012，工作满一年了，收获很多。</p>
<p>2012，做很多事，见很多人，读很多书，第二个不及格，两外两个勉强80.</p>
<p>2012，折腾了很多东西，时间有浪费了，也有不浪费的，大多东西没瞎折腾。</p>
<p>2012，想要做一些事情，跳槽了，告别了一些人，离开了一个地方，到了另一个地方，去做一些事情。</p>
<p>2012，12月21日，活下来了，说好的末日呢？</p>
<p>2012，刷微博，看网易成了每天日常，也同步了这一年发生的几乎所有大小事，有丑陋，也有美好</p>
<p>2012，尾巴，从win彻底迁linux，发现没啥不便，彻底无弹窗广告了&hellip;..</p>
<p>2012，很多事情依旧没有想清楚</p>
<p>2012，依旧单身，唉</p>
<hr>
<p>最后</p>
<p>命运感觉是很奇妙的东西，是你决定又不是你决定，就像当年高考差了一分，然后，哥孤身杀到了山东。就像那次去笔试，已然迟到了，但是还是去试了，结果到了杭州，然后是十月，这青黄不接竟然让我面上了，然后来了深圳。</p>
<p>是你决定，却又不是你决定的东西</p>
<p>其实，很多时候我在想，自己要什么？而大多数时候，这个问题是无解的，很难明确出一些事情。</p>
<p>INTJ，注定的独行者，性格如此，别无他法，只能继续。</p>
<p>做决定，跟着自己内心的直觉走吧，以前感觉乔帮主的follow your heart有点虚，但是实际经历，发现这个很重要，还是那句话，既然选择了远方，便只顾风雨兼程。</p>
<p>2013，好吧，这是我经历第一个四个数不一样的年。</p>
<p>90后都出来混职场了，真心觉得老了。</p>
<p>还没想好，目前挂着Python后台开发的头衔，当然，是初级的，继续搞Python，也要去搞Go，还要去深入linux，总之，这块，很忙。打算自己搞个blog，迁走，更自由吧。另外开始读源码，写些东西，参与开源的项目。然后，要去做题刷题，继续啃书。然后，争取今年不过双十一,目测有点难度的&hellip;..</p>
<p>然后，祝大伙新年快乐吧，今天2号，不算太迟:)</p>
<p>The end!</p>
<p>wklken</p>
<p>2013-01-02 00:45 于深圳</p>
]]></content>
		</item>
		
		<item>
			<title>Python-基础-列表及列表解析小结</title>
			<link>https://wklken.me/posts/2012/12/30/python-base-list.html</link>
			<pubDate>Sun, 30 Dec 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/12/30/python-base-list.html</guid>
			<description>##列表及列表解析 发现要到2013了，这两个月，离职入职，忙七忙八的，博文少了好多，笔记到是一大堆。 最近开始整理书签，微博收藏以及笔记梳理 采</description>
			<content type="html"><![CDATA[<p>##列表及列表解析</p>
<p>发现要到2013了，这两个月，离职入职，忙七忙八的，博文少了好多，笔记到是一大堆。
最近开始整理书签，微博收藏以及笔记梳理
采用删的方式，就是在evernote中新建一片，然后捞对应的笔记，一点点总结梳理，删除原有的笔记，发现这样效率高些（原来是在原有笔记之上直接修改总结，发现有用的信息其实并不多，效率有点低）</p>
<p>相对而言比较全和有用，会持续优化。</p>
<p>2013，发完这个系列的博客之后，将会整个博客迁移到自己的博客（刚开始写&hellip;），后续将同步吧，不过csdn可能会慢一些，自己blog会更自由些。后续将会是python&amp;框架,linux后台开发，Go等等其他。</p>
<p>不扯了，看正题
以下是对列表笔记梳理的第一个版本，仅供参考</p>
<p>资料来源于书籍，网络，个人练习等等</p>
<hr>
<pre><code>#author:wklken
#version: 1.0
#date: 2012-12-30
#history:
  2012-12-30  created
</code></pre>
<p>###定义：</p>
<p>列表是Python中使用最频繁的数据类型【可以说没有之一】</p>
<p>关键词：有序，可变</p>
<pre><code>&gt;一组有序项目的集合
&gt;可变的数据类型【可进行增删改查】
&gt;列表中可以包含任何数据类型，也可包含另一个列表【可任意组合嵌套】
&gt;列表是以方括号“ []”包围的数据集合，不同成员以“ ,”分隔
&gt;列表可通过序号访问其中成员
</code></pre>
<p>查看帮助 : help(list)</p>
<p>###常见的列表操作</p>
<p>####声明&amp;创建</p>
<pre><code>:::python
l = []    #空列表
l = [1, 2, 3, 4]
l = [1, 'a', [2,3] ]
l = list('hello')     #得到 ['h', 'e', 'l', 'l', 'o']
l = list(range(4))   #[0, 1, 2, 3]
l = '1,2,3,4,5'.split(',')  #['1', '2', '3', '4', '5']
</code></pre>
<p>内建函数list(a_sequence) 可以将一个序列转为列表</p>
<p>通过下标访问</p>
<pre><code>&gt;&gt;&gt;l = [1, 2, 3, 4]
&gt;&gt;&gt;l[0]  #1
</code></pre>
<p>####增加元素</p>
<p>A.新加入一个元素append</p>
<p>append方法添加。它在原列表末尾添加一个 item， item类型可以是任意的</p>
<pre><code>:::python
l = [1, 2, 3]
l.append('hello')   #得到 [1, 2, 3, 'hello']
l.append(['hello'])   #得到 [1, 2, 3, 'hello', ['hello']]
</code></pre>
<p>B.插入一个元素insert</p>
<pre><code>:::python
l1 = [1, 2, 3]
l1.insert(1,9)    #[1, 9, 2, 3]
</code></pre>
<p>C.两个列表相加
两种方式
第一种</p>
<pre><code>:::python
l1 = [1, 2, 3]
l3 = l1 + [4, 5, 6]  #这种方式,l1不变，二者返回新的列表，当列表很长时，会消耗大量内存
</code></pre>
<p>第二种(必须接收一个参数，且是另一个列表)</p>
<pre><code>:::python
l1.extend([4, 5, 6])  #直接扩增l1
</code></pre>
<p>等价的做法</p>
<pre><code>:::python
l1 += [4,5,6]
</code></pre>
<p>梳理：</p>
<p>s.append(x)</p>
<p>same as s[len(s):len(s)] = [x] 在列表尾部追加单个对象x。使用多个参数会引起异常。</p>
<p>s.extend(x)</p>
<p>same as s[len(s):len(s)] = x将列表L中的表项添加到列表中。返回None。</p>
<p>s.insert(i, x)</p>
<p>same as s[i:i] = [x] 在索引为i的元素前插入对象x。如list.insert(0,x)在第一项前插入对象。返回None。</p>
<p>####删除元素</p>
<p>A.按item的索引或切片删除</p>
<pre><code>:::python
l1 = [1, 2, 3, 4, 5, 6]
del l1[0]   #得到[2, 3, 4, 5, 6]
del l1[0:2]  #得到[4, 5, 6]
</code></pre>
<p>B.按item的值进行删除</p>
<pre><code>:::python
l1 = [1,2,3,1,2]
l1.remove(1)  #得到[2,3,1,2]
</code></pre>
<p>若是remove对应值查无，将跑ValueError</p>
<p>C.删除某个位置并返回该位置值</p>
<p>pop若是不传位置参数，默认删除列表最后一个元素</p>
<pre><code>:::python
l1 = [1, 2, 3, 4, 5]
a = l1.pop(1)   #a=2
b = l1.pop()   #a=5
</code></pre>
<p>梳理:</p>
<p>s.pop([i])</p>
<p>same as x = s[i]; del s[i]; return x删除列表中索引为x的表项，并返回该表项的值。若未指定索引，pop返回列表最后一项。</p>
<p>s.remove(x)</p>
<p>same as del s[s.index(x)] 删除列表中匹配对象x的第一个元素。匹配元素时产生异常。返回None。</p>
<p>del s[i:j]</p>
<p>same as s[i:j] = []</p>
<p>####修改元素</p>
<p>对指定索引进行赋值操作</p>
<p>A.某个元素</p>
<pre><code>:::python
l1 = [1, 2, 3, 4]
l1[0] = 0   #[0,2,3,4]
</code></pre>
<p>B.某一段元素</p>
<pre><code>:::python
l1= [1,2,3,4]
l1[0:2] = [7,8,9]  #[7,8,9,3,4]

l1[:] = []   #清空了
</code></pre>
<p>梳理：</p>
<p>s[i] = x</p>
<p>item i of s is replaced by x</p>
<p>s[i:j] = t</p>
<p>slice of s from i to j is replaced by the contents of the iterable t</p>
<p>5.切片和索引</p>
<p>A.索引l[i]</p>
<pre><code>:::python
l1 = [1,2,3,4,5]
l1[0]  #1
l1[-1]  #5，负数的索引从尾部开始计数,最后一个元素为-1
</code></pre>
<p>B.切片l[i:j:k]
i,j,k可选，冒号必须的 i不指定默认0，j不指定默认序列尾，k不指定默认1</p>
<pre><code>:::python
l1 =  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
l1[0:2]   #[1, 2],   取区间[i,j) ，左闭右开
l1[:2]     #同上，可省略第一位
l1[2:]     #[3, 4, 5, 6, 7, 8, 9, 10, 11]
l1[2:-1]   #[3, 4, 5, 6, 7, 8, 9, 10]
l1[:]       #同l1，相当于复制一份

l1[::2]   #步长2，[1, 3, 5, 7, 9, 11]
l1[0:7:2]  #[1, 3, 5, 7]
l1[7:0:-2]  #[8, 6, 4, 2]   注意步长为负、理解起来相当于从7到1，倒序步长2
</code></pre>
<p>####排序
A.原地排list.sort()</p>
<pre><code>:::python
l1 = [5,3,2,1,4,6]
l1.sort()   #得到[1,2,3,4,5,6]   默认升序
</code></pre>
<p>sort可接受参数</p>
<pre><code>cmp，比较函数，接受两个参数,小于时返回负，大于返回正，相等返回0
key，指定排序键
reverse，指定是否反序
</code></pre>
<p>列表的比较操作 , 隐式调用cmp 方法 , 比较规则是逐个扫描元素 , 进行比较, 如果可以比较 , 比较, 如果相等扫描下一个元素 , 如果不相等返回结果 , 如果两个元素类型不可以比较 , 就比较两个对象的 id()值 .. 如果一直相等 ,直到一个列表扫描结束 , 那么返回较长的列表较大</p>
<pre><code>&gt;&gt;&gt; l1 = [(1,99),(3,97),(2,98),(4,96)]
&gt;&gt;&gt; l1.sort(key=lambda x: x[1])
&gt;&gt;&gt; l1
[(4, 96), (3, 97), (2, 98), (1, 99)]
&gt;&gt;&gt; l1.sort(key=lambda x: x[1], reverse=True)
&gt;&gt;&gt; l1
[(1, 99), (2, 98), (3, 97), (4, 96)]
</code></pre>
<p>B.sorted函数</p>
<p>sorted(l1)   #返回l1的有序序列，l1不变</p>
<pre><code>:::python
sorted(l,key=str.lower,reverse=True)
</code></pre>
<p>C.反序</p>
<pre><code>:::python
l1.reverse()  #l1反序
</code></pre>
<p>同样</p>
<pre><code>:::python
reversed(l1)   #返回一个iterator
</code></pre>
<p>l[::-1]可以达到一样的效果，但是这个是返回一个新的列表</p>
<p>梳理：</p>
<p>sort  sorted 区别</p>
<pre><code>sort:     在原 list 上排序，不返回排序后的 list
sorted: 不改变原 list ，返回排序后的 list
</code></pre>
<p>s.reverse()</p>
<p>reverses the items of s in place颠倒列表元素的顺序。</p>
<p>s.sort([cmp[, key[, reverse]]])</p>
<p>sort the items of s in place对列表排序，返回none。bisect模块可用于排序列表项的添加和删除。</p>
<p>####查找和统计</p>
<p>A.包含判断in ,not in</p>
<pre><code>:::python
l1  = [1, 2, 3, 4]
1 in l1  #True
1 not in l1 #False
</code></pre>
<p>B.查询位置索引index</p>
<pre><code>l1 = [1, 2, 3, 4]
l1.index(1)    #0
&gt;&gt;&gt; l1.index(5)    #特别注意，当值不存在于列表，用index将抛ValueError

Traceback (most recent call last):
  File &quot;&lt;pyshell#44&gt;&quot;, line 1, in &lt;module&gt;
    l1.index(5)
ValueError: 5 is not in list
</code></pre>
<p>C.统计一个元素的出现次数</p>
<pre><code>:::python
l1 = [1, 2, 3, 4, 1]
l1.count(1)    #2
</code></pre>
<p>梳理：</p>
<p>s.count(x)</p>
<p>return number of i&rsquo;s for which s[i] == x返回对象x在列表中出现的次数。</p>
<p>s.index(x[, i[, j]])</p>
<p>return smallest k such that s[k] == x and i &lt;= k &lt; j返回列表中匹配对象x的第一个列表项的索引。无匹配元素时产生异常。</p>
<p>####遍历列表</p>
<p>A.直接</p>
<pre><code>:::python
l1 = [1, 2, 3, 4, 5]
for i in l1:
     print i
</code></pre>
<p>B.需要索引位置</p>
<pre><code>:::python
l1 = [1, 2, 3, 4, 5]
for index,value in enumerate(l1):
     print index,value
</code></pre>
<p>9.其他操作</p>
<pre><code>len(l)   #列表长度
l*3 重复
l1 = [1, 2]
l1*3   #[1,2,1,2,1,2]
</code></pre>
<p>清空列表</p>
<pre><code>:::python
l1 = []
l1[:] = []
del l1[:]
</code></pre>
<p>复制列表</p>
<pre><code>:::python
l2 = l1[:]
</code></pre>
<p>注意：在操作list时，如果是涉及原地修改的操作，例如append,insert等，返回值是None
要防止出现这种语法   l1 = l1.append(&lsquo;a&rsquo;)  ，如果这样，你将得到None&hellip;&hellip;.</p>
<p>###列表解析
####定义和说明</p>
<pre><code>&gt;Python 的强大特性之一是其对 list 的解析，它提供一种紧凑的方法，可以通过对 list 中的每个元素应用一个函数，从而将一个 list 映射为另一个 list。
&gt;列表解析，又叫列表推导式( list comprehension)
&gt;列表解析比 for 更精简，运行更快，特别是对于较大的数据集合
&gt;列表解析可以替代绝大多数需要用到 map和 filter的场合
</code></pre>
<p>列表推导式提供了一个创建链表的简单途径，无需使用 map() ， filter() 以及 lambda 。以定义方式得到列表通常要比使用构造函数创建这些列表更清晰。每一个列表推导式包括在一个 for 语句之后的表达式，零或多个 for 或 if 语句。返回值是由 for 或 if 子句之后的表达式得到的元素组成的列表。如果想要得到一个元组，必须要加上括号。</p>
<p>####基本列表解析</p>
<p>基本</p>
<pre><code>&gt;&gt;&gt; [x for x in range(5)]   # [0, 1, 2, 3, 4]
l1 = [1,2,3,4]
[ x*2 for x in l1]   #[2,4,6,8]
</code></pre>
<p>多个值的</p>
<pre><code>:::python
[ '%s = %s' for (k, v) in a_map.items()]
</code></pre>
<p>两次循环</p>
<pre><code>&gt;&gt;&gt; l1 = [1,2,3,4]
&gt;&gt;&gt; l2 = [1,2,3,4]
&gt;&gt;&gt; [x+y for x in l1 for y in l2]
[2, 3, 4, 5, 3, 4, 5, 6, 4, 5, 6, 7, 5, 6, 7, 8]
</code></pre>
<p>可以调用函数</p>
<pre><code>:::python
[ func(x) for x in l1]  #等价于map
</code></pre>
<p>注意，列表解析不会改变原有列表的值，会创建新的list</p>
<p>####条件列表解析</p>
<pre><code>:::python
[ x for x in range(100) if x%2 ==0 ]
</code></pre>
<p>####嵌套列表解析</p>
<pre><code>:::python
mat = [ [1, 2, 3],[4, 5, 6], [7, 8, 9]]
</code></pre>
<p>交换行列</p>
<pre><code>:::python
[ [row[i] for row in mat] for i in (0,1,2)] #[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
</code></pre>
<p>###其他：</p>
<p>1.根据索引取元素时，需要进行边界检查  IndexError
切片取，不需要，超过边界不会异常</p>
<p>2.在迭代中修改列表
注意，不安全，不建议这么干
但是可以
for i in l1[:]:
l1.insert()&hellip;&hellip;</p>
<p>3.多个list合成一个
就是</p>
<pre><code>['a','b',.....],['a','b'.....]['a','b'.....]
</code></pre>
<p>变为</p>
<pre><code>['a','b',.....,'a','b'.....'a','b'.....]
</code></pre>
<p>处理</p>
<pre><code>&gt;&gt;&gt; sum ([[ 'a', 'b' ],['a' , 'b'],[ 'a' ,'b' ]], [])
['a' , 'b' , 'a', 'b' , 'a' , 'b']
&gt;&gt;&gt; list (itertools .chain([ 'a' ,'b' ],[ 'a', 'b' ],['a' , 'b']))
['a' , 'b' , 'a', 'b' , 'a' , 'b']
</code></pre>
<p>4.关于堆栈和队列</p>
<p>通过上面的操作，可以发现，很轻易可以拿列表当做堆栈或者队列使用</p>
<p>当然，他们有自己的模块，可以查相关库</p>
<p>5.序列相关模块</p>
<p>array 一种受限制可变序列类型，要求所有元素必须是相同类型</p>
<p>copy 提供浅拷贝和深拷贝的能力</p>
<p>operator 包含函数调用形式的序列操作符，如 operator.concat(m,n) 相当于m+n</p>
<p>re 正则表达式</p>
<p>types 包含Python 支持的所有类型</p>
<p>collections 高性能容器数据类型</p>
<h3 id="补充">补充</h3>
<p>2013-09-01 二维数组初始化陷阱</p>
<p>对一维数组，可以这么做</p>
<pre><code>lst =[0]*3
</code></pre>
<p>但是对二维数组，不成立</p>
<pre><code>&gt;&gt;&gt; lst_2d =[[0]*3]*3
&gt;&gt;&gt; lst_2d
[[0,0,0],[0,0,0],[0,0,0]]
&gt;&gt;&gt; lst_2d[0][0]=5
&gt;&gt;&gt; lst_2d
[[5,0,0],[5,0,0],[5,0,0]]
</code></pre>
<p>二维数组，可以这么做</p>
<pre><code>&gt;&gt;&gt; lst_2d = [[0] * 3 for i in xrange(3)]
&gt;&gt;&gt; lst_2d
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
&gt;&gt;&gt; lst_2d[0][0] = 5
&gt;&gt;&gt; lst_2d
[[5, 0, 0], [0, 0, 0], [0, 0, 0]]
</code></pre>
<hr>
<p>The end!</p>
<p>wklken</p>
<p>Gighub: <a href="https://github.com/wklken">https://github.com/wklken</a></p>
<p>Blog: <a href="https://wklken.sinaapp.com/">https://wklken.sinaapp.com/</a></p>
<p>2012-12-30</p>
<p>转载请注明出处，谢谢!</p>
]]></content>
		</item>
		
		<item>
			<title>关于测试开发及其他——写在离职之前</title>
			<link>https://wklken.me/posts/2012/11/28/summary-05-quitfirstjob.html</link>
			<pubDate>Wed, 28 Nov 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/11/28/summary-05-quitfirstjob.html</guid>
			<description>前面 在入职一周年的时候，写过一些东西，有过一些思考。 而在一个月前，花了六天，投了下简历，花了五天，思考，抉择。 虽然要投身入另一个地方，不再搞</description>
			<content type="html"><![CDATA[<p>前面</p>
<p>在入职一周年的时候，写过一些东西，有过一些思考。</p>
<p>而在一个月前，花了六天，投了下简历，花了五天，思考，抉择。</p>
<p>虽然要投身入另一个地方，不再搞测试开发，但是，工作十六个月迄今，还是有一些感受的。</p>
<p>所以，想针对这个，说说一些个人看法，仅供参考。</p>
<p>（SDET，测试开发，但是貌似实际工作和字面理解还是有些差距的，工作时，角色更像QA+Tester，实际工作中，被称为QA&hellip;&hellip;其实我还是喜欢SDET多些）</p>
<hr>
<p>作为测试这一年多（准确来说16个月）：</p>
<p>有为了赶进度，和开发一起加班到凌晨两点的时候</p>
<p>有深夜上线，一大早又起来冒烟的时候(冬天很痛苦)</p>
<p>有要开发帮忙搭环境，拉数据，查问题的时候</p>
<p>有经历各种问题，挖出一个潜伏很深的bug的时候</p>
<p>有误提bug导致开发查老半天的时候（面壁面壁）</p>
<p>有搭环境搭到抓狂的时候</p>
<p>有为了提高效率（不加班），狂写工具的时候</p>
<p>也会偶尔测漏，郁闷的时候</p>
<p>当然，也会吐吐槽，某个项目或某些流程很让人蛋疼</p>
<p>一年多，经历了很多事情，接触了很多开发，接触了很多测试，无论是开发和测试，都很nice，合作也很愉快，一年多下来，自认做到了“靠谱”二字，侥幸，一年多经手测试没出过测漏故障。</p>
<hr>
<p>关于开发：</p>
<p>开发，我划分了三类</p>
<p>一种，完美型，这类开发，对自己要求十分严格，思维严谨，逻辑清晰，严格遵守流程规范，严格自测（或许还懂得测试理论的）。对于这种开发，作为测试对其是十分佩服的，他们的提测近乎完美，文档清晰，从头到尾测试起来效率非常高，提测近于形式。当然，这类开发还是十分少的，大部分是经验丰富而且严格要求自己的开发。</p>
<p>另一种，次完美型，这类开发，刚刚工作或工作有一段时间，离完美型还有一定距离，（经验问题而非能力问题），在向完美型努力，代码会有bug,规范有不遵守，文档有缺失，等等，但是非常配合测试，测试提的bug等及时修复，文档规范等及时改正，犯过一次的错误后续不会再出现，逐步地完善。合作起来还是非常愉快地，良性循环，逐步提高的过程，随着合作深入，效率不断提高。接触的大部分开发属于此类。</p>
<p>还一种，新手型，这类开发，大多是新人，对环境流程等等还不熟悉，处于初生牛犊不怕虎的。又分为两种，一种认为不需要测试，另一种认为走走测试流程也还好。无论哪种，第一次提测比较头痛，如果对应开发部门没有新人规范或者没师兄指导的话，提测是属于头痛型甚至灾难型的，一次提测bug频出，频繁触犯规范，狂打包&hellip;&hellip;.一般头痛一次即可，每一个开发都是需要培养的，逐渐就会遵守规范了。</p>
<p>很幸运，碰到的开发都很nice，非常配合。</p>
<hr>
<p>关于测试：</p>
<p>测试有依赖型和独立型，依赖型过度地依赖开发(只用不学，用过即忘，对开发各种依赖-环境搭建，调试定位等等)，独立型相对独立(接触-请教-学习-实践，自我驱动，独立能搞定大部分东西)。后者我认为才是合格的测试人员，</p>
<p>这年头，测试不再是所谓的点点鼠标能搞定的</p>
<p>我们要：</p>
<ol>
<li>对业务熟悉，项目的业务，涉及逻辑，上下游等等</li>
<li>对代码熟悉，参与code review，读懂各类代码，java/c++/perl/python/shell&hellip;.同时要懂debug，往往要定位到错误原因</li>
<li>要会写代码，测试需要各种工具，自动化等等，都需要实现</li>
<li>对流程规范熟悉，从项目初始介入，到最终上线完成，要制定测试计划，协助和跟进各个环节风险和进度（测试环节在最后，前面环节的失误会影响测试进程及项目主体进度，而且这时候PM往往会压缩测试时间），各种沟通</li>
<li>会搭环境，sap.apach,nginx,hadoop&hellip;..要面对各种千奇百怪的错误异常。</li>
<li>要会测试理论，进行各种测试（功能性能稳定性黑盒白盒&hellip;&hellip;.），造各种case,模拟各种场景</li>
<li>要会写文档，会提bug，描述清楚问题&hellip;.总之，文笔要好，不要求会吟一首好诗，但要会以最简洁的文字描述清楚问题</li>
<li>要会分配时间，多线程运作&hellip;&hellip;往往开发测试比开始很高的，有时候同时要处理好多事情&hellip;..</li>
</ol>
<p>&hellip;&hellip;&hellip;&hellip;&hellip;..</p>
<p>当然，这些都是需要时间的。</p>
<p>以及</p>
<p>测试，需要有足够的细心和耐心，只有忍无可忍的时候，没有无需再忍的时候。(当然，事不过三，过三就是你自个问题了)</p>
<p>测试，要求什么都要达到一定程度（什么都要会，会到足够用，但也经常被人吐槽什么都会点，什么都不精），测试其实对广度要求来得高些，但其实很多前辈身怀绝技，卧虎藏龙之地。</p>
<p>测试，有时候想想挺郁闷的，因为我们的目标是没有测试。通过各种方式提高质量，终极目标没有测试一样质量有保证。即，我们的目的是消灭自己。（这个想象就&hellip;&hellip;.）</p>
<p>测试，还有个很矛盾的问题，一个牛逼的项目，不出问题，好像没你测试什么事，出了问题，却又觉得辜负了这份信任，哎。（这个有些苦逼了）</p>
<p>测试，还有个问题，就是要全程参与关注流程，必须像一个老妈子一样，事无巨细，关注很多事情&hellip;&hellip;</p>
<p>测试，有些被动的角色，却要时时掌握主动权，督促开发</p>
<p>测试，既要和开发保持良好关系，又要坚持自身的原则（不能放水）</p>
<p>记得在哪听过，代码问题，和测试有毛关系。其实作为测试，存在测试遗漏，无论是否是自身问题，测试都会数省吾身，反省，思考，改进，提高自身及改进流程，防止再次发生。(我不杀伯仁，伯仁却因我而死&hellip;&hellip;.)</p>
<p>测试，其实有时候是相当累的，项目多的时候，满负荷运转，数量上去了，还得质量保证。但将一个个项目送上线，还是很有成就感的。</p>
<p>测试，更多的像是幕后全能型选手，操心一切，保证一切ok，一切ok，我们便ok。(你好，我好&hellip;&hellip;&hellip;&hellip;.囧)</p>
<p>其实测试涉及的内容还很多，我只是作为最初级的测试人员，就所做所见写了下.</p>
<hr>
<p>关于测试开发关系：</p>
<p>说说测试和开发的关系。</p>
<p>记得上次会上，提到测试和开发的关系，有恋爱关系，长辈晚辈关系，敌人关系等等，不同人有不同看法，不同条件也会得到不同结论。</p>
<p>开发和测试其实是需要磨合的，老开发新测试，开发需要多配合指导一些。新开发老测试，测试需要更花心思督促规范。新开发新测试，都需要多问前辈多思考。老开发老测试，一句你懂的足矣。</p>
<p>网上看到很多开发吐槽测试或者测试吐槽开发的，其实，这只是其中某一方做得不够好，导致磨合进入恶性循环而非良性循环。</p>
<p>要保证良性循环，开发在不断提高自身代码质量，测试在不断完善和优化流程规范，提高自身测试相关的素质，保证项目质量，二者配合之下，一个良性循环，开发和测试效率都会大大提高。</p>
<p>这需要做的，首先还是开发需要积极的态度配合测试，而测试，需要了解开发的习惯，了解对应的项目，读代码，熟悉流程，会环境搭建，熟悉逻辑，测试逻辑，很多都需要在开发的帮助下完成。但这并不是依赖关系，开发教你搭了一次环境，你就要学会搭环境，下次不再麻烦。</p>
<p>恶性循环往往始于轻视和不配合，开发认为测试不重要，测试吐槽开发不配合，但是测试流程还是要走的，往往结果是，双方效率直线下降，消耗了大量时间和精力。搞完之后，对对方的印象都再次恶化。</p>
<p>看到有吐槽测试不重要，不需要QA的。</p>
<p>当然，如果团队都是完美型的选手，再加上某些方法，当然可以。</p>
<p>但是现实很骨感的，大部分情况是次完美型选手居多，这类情况，QA还是有存在必要的。</p>
<p>合格的开发+合格的测试 + 良性循环 =  最佳组合</p>
<p>无论是开发或测试，都会走的更远。</p>
<hr>
<p>关于规范：</p>
<p>很多开发可能吐槽很多规范(代码，打包，流程等等)的形式化or不重要or浪费时间。</p>
<p>但是，其实这些规范一开始也都是没有的，都是前人一次次踩坑踩出来的。</p>
<p>存在即合理，该遵守的规范还是不能忽视。但对于流程规范，也没有完全死板硬性要求一定要做什么，根据项目特点，确认某些规范是否适用，是否有改进之处。这并不矛盾，很多规范，有个适用性，很多规范，也是在一步步改进和优化，目的只有一个，提高质量。</p>
<p>开发和测试或许大目标不一样，但是我们目的只有一个，保证最终产品正常运行。</p>
<hr>
<p>关于靠谱：</p>
<p>要做到“靠谱”二字，感觉还是有些难度的</p>
<p>有几类：相当不靠谱，不靠谱，一般靠谱，靠谱</p>
<p>大多数处于一般靠谱-&gt;靠谱之间，当然，有极少数属于相当不靠谱/不靠谱/靠谱。</p>
<p>其实，要达到靠谱，需要一个过程，这个过程有快有慢</p>
<p>大部分都是随经验和能力增长变得靠谱，当然，也有极少数顿悟型的，直接进入靠谱行列</p>
<p>测试和开发，同样要经历一个过程，逐步“靠谱”</p>
<p>是否，我说修复了，一定就是修复了，我说有bug，一定是有问题，我说，我回头check下，一定就check了。</p>
<p>最给力的，我说，没问题，一定就没问题。</p>
<p>可以自问下，我靠不靠谱？</p>
<p>开发最怕改需求(特别是刚写完代码就改)，其实测试最怕提测后改需求（特别是测完的时候）&hellip;&hellip;哎，来来回回尽折腾。</p>
<p>所以，一群靠谱的人，可以释放超乎想象的能量。</p>
<p>靠谱的最高境界，或许是把复杂的事情做简单，把简单的事情做好。</p>
<hr>
<p>关于选择：</p>
<p>你为什么选择做测试？</p>
<p>记得面试的时候被问过这个问题，（那时候是，你为什么选择测试开发？），好吧，当时刚刚毕业的我，实习半年多(Java开发)，那时对测试开发印象模糊，工作半年后才发现，测试比例多一些，和SDET其实还是有些差距的。</p>
<p>曾经有段迷茫的时候，也被主管问及到底想要做什么，那时候的回答是“做当下认为正确的事情”，很多事情，没想明白之前，做自己认为正确的事情。</p>
<p>其实，每个阶段有每个阶段的想法，受到那时经验/阅历/环境的限制，我们做出的决定或许并不是最好的。</p>
<p>但是，选择没有正不正确，只有后不后悔。</p>
<p>我当时的选择，我并不后悔，工作后这一年多，很充实，很快乐，这就足够了。</p>
<p>So,当面临选择时，问下自己，自己是否喜欢？SWOT，分析下</p>
<p>做出决定，便不后悔。</p>
<p>既然选择了远方，便只顾风雨兼程。</p>
<hr>
<p>SDET，测试开发，我的第一份工作。三天后，我只能说，我曾经是。</p>
<p>哎，伤感</p>
<p>最后，在这里，感谢这一年多，一起奋战的同事，谢谢！！！</p>
<p>：）</p>
<p>wklken</p>
<p>2012-11-28 于杭州</p>
]]></content>
		</item>
		
		<item>
			<title>Python-进阶-魔术方法小结(方法运算符重载)</title>
			<link>https://wklken.me/posts/2012/10/29/python-base-magic.html</link>
			<pubDate>Mon, 29 Oct 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/10/29/python-base-magic.html</guid>
			<description>##魔术方法 对定制及重载做了下梳理，还没消化完，后续补全 ###基本定制 说明 C.__init__(self[, arg1, ...]) 构造器（带一些可选的参数） C.__new__(self[, arg1, ...]) 构造器（带一些可选的参数）；</description>
			<content type="html"><![CDATA[<p>##魔术方法</p>
<p>对定制及重载做了下梳理，还没消化完，后续补全</p>
<p>###基本定制</p>
<p>说明</p>
<pre><code>C.__init__(self[, arg1, ...]) 构造器（带一些可选的参数）
C.__new__(self[, arg1, ...]) 构造器（带一些可选的参数）；通常用在设置不变数据类型的子类
C.__del__(self) 解构器
C.__str__(self) 可打印的字符输出；内建str()及print 语句
C.__repr__(self) 运行时的字符串输出；内建repr()  ‘‘  和 操作符
C.__unicode__(self)b Unicode 字符串输出；内建unicode()
C.__call__(self, *args) 表示可调用的实例
C.__nonzero__(self) 为object 定义False 值；内建bool() （从2.2 版开始）
C.__len__(self) “ ” 长度（可用于类）；内建len()
</code></pre>
<p>示例</p>
<pre><code>:::python
class A():
    def __init__(self):
        print &quot;call __init__&quot;
        self.a = 1

    def __new__(self):
        print &quot;call __new__&quot;

    def __del__(self):
        print &quot;call __del__&quot;

    def __str__(self):
        print &quot;call __str__&quot;
        return &quot;class A str&quot;

    def __repr__(self):
        print &quot;call __repr__&quot;
        return &quot;class A repr&quot;

    def __unicode__(self):
        print &quot;call __unicode__&quot;
        return &quot;class A unicode&quot;

    def __nozero__(self):
        print &quot;call __nozero__&quot;
        return 1

    def __len__(self):
        print &quot;call __len__&quot;
        return 1
    #定以后callable(instance) True
    def __call__(self, *args):
        print &quot;call __call__&quot;
a = A()
print a
repr(a)
unicode(a)
print bool(a)
print len(a)
print callable(a)
</code></pre>
<p>A.new和init区别</p>
<pre><code>:::python
class A(object):
    def __init__(self):
        print &quot;call __init__&quot;
        self.value = 1

    def __new__(cls):
        print &quot;call __new__&quot;
        return super(A, cls).__new__(cls)

a = A()
print a.value
</code></pre>
<p>_<em>new</em>_：创建对象时调用，返回当前对象的一个实例,相当于java里面的构造器
一般是用于继承内置类的，返回值是一个对象</p>
<p>使用：需要控制一个新实例的创建,一般情况下不会使用，除非需要子类化不可变类型例如str/int/unicode/tuple</p>
<p>_<em>init</em>_：创建完对象后调用，对当前对象的实例的一些初始化，无返回值</p>
<p>使用：需要控制一个实例的初始化</p>
<p>可以这样理解，默认是创建（<strong>new</strong>），然后调用__init__
(new的时候，self还不存在, init的时候self已经存在了)</p>
<p>###对象值比较</p>
<p>说明</p>
<pre><code>C.__cmp__(self, obj) 对象比较；内建cmp()
C.__lt__(self, obj) and 小于/小于或等于；对应&lt;及&lt;=操作符
C.__le__(self,obj)
C.__gt__(self, obj) and 大于/大于或等于；对应&gt;及&gt;=操作符
C.__ge__(self,obj)
C.__eq__(self, obj) and 等于/不等于；对应==,!=及&lt;&gt;操作符
C.__ne__(self,obj)
</code></pre>
<p>示例</p>
<pre><code>:::python
class A():
    def __init__(self, value):
        self.value = value

    def __cmp__(self, obj):
        print &quot;call __cmp__&quot;
        return self.value - obj.value

    def __lt__(self, obj):
        print &quot;call __lt__&quot;
        return self.value &lt; obj.value

    def __gt__(self, obj):
        print &quot;call __gt__&quot;
        return self.value &gt; obj.value

    def __eq__(self, obj):
        print &quot;call __eq__&quot;
        return self.value == obj.value

a1 = A(1)
a2 = A(2)
print cmp(a1,a2)
print a1 &lt; a2
print a1 &gt; a2
print a1 == a2
</code></pre>
<p>###属性操作
说明：</p>
<pre><code>C.__getattr__(self, attr) 获取属性；内建getattr()；仅当属性没有找到时调用
C.__setattr__(self, attr, val) 设置属性
C.__delattr__(self, attr) 删除属性
C.__getattribute__(self, attr) 获取属性；内建getattr()；总是被调用
C.__get__(self, attr) （描述符）获取属性
C.__set__(self, attr, val)  （描述符）设置属性
C.__delete__(self, attr)  （描述符）删除属性
</code></pre>
<p>示例</p>
<pre><code>:::python
class A():
    def __init__(self):
        self.value = 1
    def __getattr__(self, name):
        print &quot;call __getattr__&quot;
        try:
            return self.__dict__[name]
        except:
            return &quot;not found&quot;

    def __setattr__(self, name, value):
        print &quot;call __setattr__&quot;
        self.__dict__[name] = value

    def __delattr__(self, name):
        print &quot;call __delattr__&quot;
        del self.__dict__[name]

    def __getattribute__(self, name):
        print &quot;call __getattribute__&quot;
        return self.__dict__[name]

    def __get__(self, name):
        pass
    def __set__(self, name, value):
        pass
    def __del__(self):
        pass

a = A()
print getattr(a, &quot;value&quot;)
print getattr(a, &quot;name&quot;)
del a.value
</code></pre>
<p>A.get/getattr/getattribute区别</p>
<p>object.<strong>getattr</strong>(self, name)</p>
<p>当一般位置找不到attribute的时候，会调用getattr，返回一个值或AttributeError异常。</p>
<p>object.<strong>getattribute</strong>(self, name)</p>
<p>无条件被调用，通过实例访问属性。如果class中定义了__getattr__()，则__getattr__()不会被调用（除非显示调用或引发AttributeError异常）</p>
<p>object.<strong>get</strong>(self, instance, owner)</p>
<p>如果class定义了它，则这个class就可以称为descriptor。owner是所有者的类，instance是访问descriptor的实例，如果不是通过实例访问，而是通过类访问的话，instance则为None。（descriptor的实例自己访问自己是不会触发__get__，而会触发__call__，只有descriptor作为其它类的属性才有意义。）</p>
<p>###数值及二进制</p>
<p>二元</p>
<pre><code>C.__*add__(self, obj) 加；+操作符
C.__*sub__(self, obj) 减；-操作符
C.__*mul__(self, obj) 乘；*操作符
C.__*div__(self, obj) 除；/操作符
C.__*truediv__(self, obj)  True 除；/操作符
C.__*floordiv__(self, obj)  Floor 除；//操作符
C.__*mod__(self, obj) 取模/取余；%操作符
C.__*divmod__(self, obj) 除和取模；内建divmod()
C.__*pow__(self, obj[, mod]) 乘幂；内建pow();**操作符
C.__*lshift__(self, obj) 左移位；&lt;&lt;操作符
</code></pre>
<p>二进制</p>
<pre><code>C.__*rshift__(self, obj) 右移；&gt;&gt;操作符
C.__*and__(self, obj) 按位与；&amp;操作符
C.__*or__(self, obj) 按位或；|操作符
C.__*xor__(self, obj) 按位与或；^操作符
</code></pre>
<p>一元</p>
<pre><code>C.__neg__(self) 一元负
C.__pos__(self) 一元正
C.__abs__(self) 绝对值；内建abs()
C.__invert__(self) 按位求反；~操作符
</code></pre>
<p>数值转换</p>
<pre><code>C.__complex__(self, com) 转为complex(复数);内建complex()
C.__int__(self) 转为int;内建int()
C.__long__(self) 转为long；内建long()
C.__float__(self) 转为float；内建float()
</code></pre>
<p>其他</p>
<pre><code>C.__oct__(self) 八进制表示；内建oct()
C.__hex__(self) 十六进制表示；内建hex()
C.__coerce__(self, num) 压缩成同样的数值类型；内建coerce()
C.__index__(self)g 在有必要时,压缩可选的数值类型为整型（比如：用于切片索引等等
</code></pre>
<p>###序列
说明</p>
<pre><code>C.__len__(self) 序列中项的数目
C.__getitem__(self, ind) 得到单个序列元素
C.__setitem__(self, ind,val) 设置单个序列元素
C.__delitem__(self, ind) 删除单个序列元素

C.__getslice__(self, ind1,ind2) 得到序列片断
C.__setslice__(self, i1, i2,val) 设置序列片断
C.__delslice__(self, ind1,ind2) 删除序列片断
C.__contains__(self, val) f 测试序列成员；内建in 关键字
C.__*add__(self,obj) 串连；+操作符
C.__*mul__(self,obj) 重复；*操作符
C.__iter__(self)  创建迭代类；内建iter()
</code></pre>
<p>示例</p>
<pre><code>:::python
class A():
    def __init__(self):
        print &quot;call __init__&quot;
        self.value = [1,2,3,4,5,6]

    def __len__(self):
        print &quot;call __len__&quot;
        return len(self.value)

    def __getitem__(self, index):
        print &quot;call __getitem__&quot;
        return self.value[index]

    def __setitem__(self, index, value):
        print &quot;call __setitem__&quot;
        self.value[index] = value

    def __delitem__(self, index):
        print &quot;call __delitem__&quot;
        del self.value[index]

a = A()
print len(a)
print a[2]
a[2] = 99
del a[2]
</code></pre>
<p>###映射</p>
<pre><code>C.__len__(self) mapping 中的项的数目
C.__hash__(self) 散列(hash)函数值
C.__getitem__(self,key) 得到给定键(key)的值
C.__setitem__(self,key,val) 设置给定键(key)的值
C.__delitem__(self,key) 删除给定键(key)的值
C.__missing__(self,key) 给定键如果不存在字典中，则提供一个默认值
</code></pre>
<hr>
<p>The end!</p>
<p>wklken</p>
<p>Gighub: <a href="https://github.com/wklken">https://github.com/wklken</a></p>
<p>Blog: <a href="https://wklken.sinaapp.com/">https://wklken.sinaapp.com/</a></p>
<p>2012-10-29</p>
<p>转载请注明出处，谢谢!</p>
]]></content>
		</item>
		
		<item>
			<title>Python-进阶-装饰器小结</title>
			<link>https://wklken.me/posts/2012/10/27/python-base-decorator.html</link>
			<pubDate>Sat, 27 Oct 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/10/27/python-base-decorator.html</guid>
			<description>想找个地方快乐的coding，貌似不是一件容易的事情。 一时冲动，不过后路已断，做自己想做的事情，总要付出一些代价的，坚持吧，只能。 吐槽下，本</description>
			<content type="html"><![CDATA[<p>想找个地方快乐的coding，貌似不是一件容易的事情。</p>
<p>一时冲动，不过后路已断，做自己想做的事情，总要付出一些代价的，坚持吧，只能。</p>
<p>吐槽下，本周各种事情，搞得如越级打怪般艰难。周六，去三小时，回来三小时，大败而归，但是还是学到不少东西。</p>
<p>差距还是有的，虽然自信可以在最短时间补上，但是，需要成本。</p>
<p>总之，貌似时机不对，哎
以上废话，进入正题</p>
<hr>
<p>上周六碰到了，一周忙碌，今天稍微理下，待补全，资料主要来源于书籍，网络&amp;self.coding()。有任何问题，请指正哈</p>
<hr>
<p>##资源</p>
<p><a href="http://coolshell.cn/articles/11265.html">Python修饰器的函数式编程</a></p>
<hr>
<p>##基本概念
具体概念自己google</p>
<p>装饰器是一个很著名的设计模式，经常被用于有切面需求的场景，较为经典的有插入日志、性能测试、事务处理, Web权限校验, Cache等。</p>
<p>很有名的例子，就是咖啡，加糖的咖啡，加牛奶的咖啡。
本质上，还是咖啡，只是在原有的东西上，做了“装饰”，使之附加一些功能或特性。</p>
<p>例如记录日志，需要对某些函数进行记录</p>
<p>笨的办法，每个函数加入代码，如果代码变了，就悲催了</p>
<p>装饰器的办法，定义一个专门日志记录的装饰器，对需要的函数进行装饰，搞定</p>
<p>##优点</p>
<p>抽离出大量函数中与函数功能本身无关的雷同代码并继续重用</p>
<p>即，可以将函数“修饰”为完全不同的行为，可以有效的将业务逻辑正交分解，如用于将权限和身份验证从业务中独立出来</p>
<p>概括的讲，装饰器的作用就是为已经存在的对象添加额外的功能</p>
<p>##Python中的装饰器</p>
<p>在Python中，装饰器实现是十分方便的</p>
<p>原因是：函数可以被扔来扔去。</p>
<p>函数作为一个对象：</p>
<pre><code>A.可以被赋值给其他变量，可以作为返回值

B.可以被定义在另外一个函数内
</code></pre>
<p>def:</p>
<p>装饰器是一个函数,一个用来包装函数的函数，装饰器在函数申明完成的时候被调用，调用之后返回一个修改之后的函数对象，将其重新赋值原来的标识符，并永久丧失对原始函数对象的访问(申明的函数被换成一个被装饰器装饰过后的函数)</p>
<p>当我们对某个方法应用了装饰方法后， 其实就改变了被装饰函数名称所引用的函数代码块入口点，使其重新指向了由装饰方法所返回的函数入口点。</p>
<p>由此我们可以用decorator改变某个原有函数的功能，添加各种操作，或者完全改变原有实现</p>
<p>##分类：</p>
<p>装饰器分为无参数decorator，有参数decorator</p>
<pre><code>* 无参数decorator

生成一个新的装饰器函数

* 有参decorator

有参装饰，装饰函数先处理参数，再生成一个新的装饰器函数，然后对函数进行装饰
</code></pre>
<p>装饰器有参/无参，函数有参/无参，组合共4种</p>
<p>##具体定义：</p>
<p>decorator方法</p>
<p>A.把要装饰的方法作为输入参数，</p>
<p>B.在函数体内可以进行任意的操作(可以想象其中蕴含的威力强大，会有很多应用场景)，</p>
<p>C.只要确保最后返回一个可执行的函数即可（可以是原来的输入参数函数， 或者是一个新函数）</p>
<p>###无参数装饰器 – 包装无参数函数
不需要针对参数进行处理和优化</p>
<pre><code>:::python
def decorator(func):
    print &quot;hello&quot;
    return func

@decorator
def foo():
    pass

foo()
</code></pre>
<p>foo()
等价于:</p>
<pre><code>:::python
foo = decorator(foo)
foo()
</code></pre>
<p>###无参数装饰器 – 包装带参数函数</p>
<pre><code>:::python
def decorator_func_args(func):
    def handle_args(*args, **kwargs): #处理传入函数的参数
        print &quot;begin&quot;
        func(*args, **kwargs)   #函数调用
        print &quot;end&quot;
    return handle_args


@decorator_func_args
def foo2(a, b=2):
    print a, b

foo2(1)
</code></pre>
<p>foo2(1)
等价于</p>
<pre><code>:::python
foo2 = decorator_func_args(foo2)
foo2(1)
</code></pre>
<p>###带参数装饰器 – 包装无参数函数</p>
<pre><code>:::python
def decorator_with_params(arg_of_decorator):#这里是装饰器的参数
    print arg_of_decorator
    #最终被返回的函数
    def newDecorator(func):
        print func
        return func
    return newDecorator


@decorator_with_params(&quot;deco_args&quot;)
def foo3():
    pass
foo3()
</code></pre>
<p>与前面的不同在于：比上一层多了一层封装，先传递参数，再传递函数名</p>
<p>第一个函数decomaker是装饰函数，它的参数是用来加强“加强装饰”的。由于此函数并非被装饰的函数对象，所以在内部必须至少创建一个接受被装饰函数的函数，然后返回这个对象（实际上此时foo3= decorator_with_params(arg_of_decorator)(foo3)）</p>
<p>###带参数装饰器– 包装带参数函数</p>
<pre><code>:::python
def decorator_whith_params_and_func_args(arg_of_decorator):
    def handle_func(func):
        def handle_args(*args, **kwargs):
            print &quot;begin&quot;
            func(*args, **kwargs)
            print &quot;end&quot;
            print arg_of_decorator, func, args,kwargs
        return handle_args
    return handle_func


@decorator_whith_params_and_func_args(&quot;123&quot;)
def foo4(a, b=2):
    print &quot;Content&quot;

foo4(1, b=3)
</code></pre>
<p>###内置装饰器</p>
<p>内置的装饰器有三个：staticmethod,classmethod, property</p>
<pre><code>:::python
class A():
    @staticmethod
    def test_static():
        print &quot;static&quot;
    def test_normal(self):
        print &quot;normal&quot;
    @classmethod
    def test_class(cls):
        print &quot;class&quot;, cls

a = A()
A.test_static()
a.test_static()
a.test_normal()
a.test_class()
</code></pre>
<p>结果：</p>
<pre><code>static
static
normal
class __main__.A
</code></pre>
<p>A.test_static</p>
<p>staticmethod 类中定义的实例方法变成静态方法</p>
<p>基本上和一个全局函数差不多(不需要传入self，只有一般的参数)，只不过可以通过类或类的实例对象来调用，不会隐式地传入任何参数。</p>
<p>类似于静态语言中的静态方法</p>
<p>B.test_normal</p>
<p>普通对象方法：
普通对象方法至少需要一个self参数，代表类对象实例</p>
<p>C.test_class</p>
<p>类中定义的实例方法变成类方法</p>
<p>classmethod需要传入类对象，可以通过实例和类对象进行调用。</p>
<p>是和一个class相关的方法，可以通过类或类实例调用，并将该class对象（不是class的实例对象）隐式地当作第一个参数传入。</p>
<p>就这种方法可能会 比较奇怪一点，不过只要你搞清楚了python里class也是个真实地存在于内存中的对象，而不是静态语言中只存在于编译期间的类型，就好办了。正常的方法就是和一个类的实例对象相关的方法，通过类实例对象进行调用，并将该实例对象隐式地作为第一个参数传入，这个也和其它语言比较像。</p>
<p>D.区别</p>
<p>staticmethod，classmethod相当于全局方法，一般用在抽象类或父类中。一般与具体的类无关。</p>
<p>类方法需要额外的类变量cls，当有子类继承时，调用类方法传入的类变量cls是子类，而不是父类。</p>
<p>类方法和静态方法都可以通过类对象和类的实例对象访问</p>
<p>定义方式，传入的参数，调用方式都不相同。</p>
<p>E.property</p>
<p>对类属性的操作，类似于java中定义getter/setter</p>
<pre><code>:::python
class B():
    def __init__(self):
        self.__prop = 1
    @property
    def prop(self):
        print &quot;call get&quot;
        return self.__prop
    @prop.setter
    def prop(self, value):
        print &quot;call set&quot;
        self.__prop = value
    @prop.deleter
    def prop(self):
        print &quot;call del&quot;
        del self.__prop
</code></pre>
<p>###其他</p>
<p>A.装饰器的顺序很重要，需要注意</p>
<pre><code>:::python
@A
@B
@C
def f ():
</code></pre>
<p>等价于</p>
<pre><code>:::python
f = A(B(C(f)))
</code></pre>
<p>B.decorator的作用对象可以是模块级的方法或者类方法</p>
<p>C.functools模块提供了两个装饰器。
这个模块是Python 2.5后新增的。</p>
<p>functools.wraps(func)
total_ordering(cls)
这个具体自己去看吧，后续用到了再补充</p>
<p>###一个简单例子</p>
<p>通过一个变量，控制调用函数时是否统计时间</p>
<pre><code>#!/usr/bin/env python
# -*- coding:utf-8 -*-
#@author: wklken@yeah.net
#@version: a test of decorator
#@date: 20121027
#@desc: just a test


import logging

from time import time

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
is_debug = True

def count_time(is_debug):
    def  handle_func(func):
        def  handle_args(*args, **kwargs):
            if is_debug:
                begin = time()
                func(*args, **kwargs)
                logging.debug( &quot;[&quot; + func.__name__ + &quot;] -&gt; &quot; + str(time() - begin) )
            else:
                func(*args, **kwargs)
        return handle_args
    return handle_func

def pr():
    for i in range(1,1000000):
        i = i * 2
    print &quot;hello world&quot;

def test():
    pr()

@count_time(is_debug)
def test2():
    pr()

@count_time(False)
def test3():
    pr()

if __name__ == &quot;__main__&quot;:
    test()
    test2()
    test3()
</code></pre>
<p>结果：</p>
<pre><code>hello world
hello world
DEBUG:root:[test2] -&gt; 0.0748538970947
hello world
</code></pre>
<p>The end!</p>
<p>wklken</p>
<p>Gighub: <a href="https://github.com/wklken">https://github.com/wklken</a></p>
<p>Blog: <a href="https://wklken.sinaapp.com/">https://wklken.sinaapp.com/</a></p>
<p>2012-10-27</p>
<p>转载请注明出处，谢谢!</p>
]]></content>
		</item>
		
		<item>
			<title>如何进行shell脚本正确性测试</title>
			<link>https://wklken.me/posts/2012/09/15/how-to-test-shell.html</link>
			<pubDate>Sat, 15 Sep 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/09/15/how-to-test-shell.html</guid>
			<description>在实际工作中，需要对shell脚本进行正确性测试。 如何用最快最有效的方式进行测试？ 很多开发的习惯是，二话不说，写完/拿到，就跑一把，看看输入</description>
			<content type="html"><![CDATA[<p>在实际工作中，需要对shell脚本进行正确性测试。</p>
<p>如何用最快最有效的方式进行测试？</p>
<p>很多开发的习惯是，二话不说，写完/拿到，就跑一把，看看输入，输出，想要的操作是否完成，也就过了。</p>
<p>其实这是十分不严谨的，若是未经过QA，风险还是相当大的。</p>
<p>以下即shell脚本测试流程，仅供参考</p>
<p>1.代码走读：</p>
<p>写完，或者拿到一个shell脚本，不必急于运行，虽然实践是检验整理的唯一标准，但是，在读代码这个过程中，可以规避很多低级的bug.</p>
<p>读什么？</p>
<pre><code>A.代码逻辑，这个脚本用来做什么，主要分为多少步，分别做了什么事情？
  用于检查是否有遗漏逻辑，或有悖于需求。
B.具体语法，变量，判断语句
  语法方面的东西，变量是否定义，判断语句逻辑是否正确，是否考虑各种异常，错误是否退出，返回正确状态值等。
</code></pre>
<p>2.语法检测：</p>
<p>shell的语法还是相当让人无语的，很多很容易疏忽遗漏的地方</p>
<p>命令格式：</p>
<pre><code>sh -n ***.sh
</code></pre>
<p>若是没有异常输出，证明脚本没有明显的语法问题。</p>
<p><img src="https://github.com/wklken/gallery/blob/master/shell_test/shell_test_1.jpg?raw=true" alt="运行结果"></p>
<p>3.运行跟踪：</p>
<p>实践是检验整理的唯一标准，跑一把。</p>
<p>不过，可不是直接运行然后去看最终结果，这样会遗漏掉很多中间过程。</p>
<p>命令格式:</p>
<pre><code>sh -vx ***.sh
</code></pre>
<p>得到效果如下:</p>
<p><img src="https://github.com/wklken/gallery/blob/master/shell_test/shell_test_2.jpg?raw=true" alt="运行结果"></p>
<p>我们可以看到</p>
<p>每行代码原始命令（无+的）:[这是-v的效果]</p>
<p>代码执行时的情况（带+），包括运算结果，逻辑判断结果，变量赋值等等[-x的效果]</p>
<p>而我们所要关注的就是这些信息，主要是变量值和逻辑判断结果。</p>
<p>4.覆盖分支：</p>
<p>直接跑，只能覆盖到主体流程，对于其他控制流分支的代码是无法覆盖到的。</p>
<p>对于关键性的，重点的逻辑，我们需要制造条件，使运行脚本可以进入对应分支</p>
<p>5.其他：</p>
<pre><code>A.关于bashdb:
  可以尝试下，但是感觉投入产出比不高
B.关于单元测试：
  实际工作中，由于项目压力比较大，单元测试的成本还是相当高的，所以目前为止没有。
</code></pre>
<p>6.有没有更好的方式?</p>
<p>好吧，单步跟踪，脚本短的还好，日志信息不会太多，要是多了，存在调用其他脚本等等&hellip;..</p>
<p>日志量达到几千行，这是很轻易的事情。</p>
<p>跟踪过的童鞋有同感，展现不够友好，惨白惨白一片，一千行下来，看的眼花。</p>
<p>很容易遗漏（LZ被坑了好多回，你看，或不看&hellip;&hellip;错误信息明明就在那里，就是视而不见）</p>
<p>So.进行了一层优化，对日志进行处理，使用正则，标注我关心的信息</p>
<p>效果图对比：</p>
<p><img src="https://github.com/wklken/gallery/blob/master/shell_test/shell_test_3.jpg?raw=true" alt="原始图"></p>
<p><img src="https://github.com/wklken/gallery/blob/master/shell_test/shell_test_4.jpg?raw=true" alt="扩展图"></p>
<p>脚本是用python实现的，位置:https://github.com/wklken/pytools/tree/master/shell</p>
<p>思想是：执行，抓到所有日志，用正则进行匹配，打上颜色，然后输出</p>
<p>好了，就这些</p>
<p>工具的实现是为了提高效率，节约时间。</p>
<p>The end!</p>
<p>wklken</p>
<p>Gighub: <a href="https://github.com/wklken">https://github.com/wklken</a></p>
<p>Blog: <a href="https://wklken.me">https://wklken.me</a></p>
<p>2012-09-15</p>
<p>转载请注明出处，谢谢！</p>
]]></content>
		</item>
		
		<item>
			<title>Python通用邮件发送[smtplib]</title>
			<link>https://wklken.me/posts/2012/09/02/python-email-smtplib.html</link>
			<pubDate>Sun, 02 Sep 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/09/02/python-email-smtplib.html</guid>
			<description>使用到的模块 smtplib, email 代码托管位置 github-pytools 需求 1.发送邮件 2.不需要登录任何邮箱等等 3.支持多接收人 4.支持附件 5.支持命令行+方法调用 基于版本 2.4 使用2</description>
			<content type="html"><![CDATA[<p>使用到的模块 <a href="http://docs.python.org/2/library/smtplib.html">smtplib</a>,  <a href="http://docs.python.org/2/library/email">email</a></p>
<p>代码托管位置 <a href="https://github.com/wklken/pytools">github-pytools</a></p>
<h3 id="需求">需求</h3>
<p>1.发送邮件</p>
<p>2.不需要登录任何邮箱等等</p>
<p>3.支持多接收人</p>
<p>4.支持附件</p>
<p>5.支持命令行+方法调用</p>
<h3 id="基于版本">基于版本</h3>
<p>2.4
使用2.7和3.x的童鞋，可能需要修改下import信息</p>
<h3 id="源代码">源代码</h3>
<p>使用官网一份代码进行重新修改，扩增功能</p>
<p>代码托管地址 <a href="https://github.com/wklken/pytools">github-pytools</a></p>
<pre><code>#!/usr/bin/env python
#@author : wklken@yeah.ent
#@version : 0.1
#@desc: for mail sending.

import smtplib
import getopt
import sys
import os

from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase

from email.MIMEText import MIMEText
import email.Encoders as encoders

def send_mail(mail_from, mail_to, subject, msg_txt, files=[]):
    # Create message container - the correct MIME type is multipart/alternative.
    msg = MIMEMultipart('alternative')
    msg['Subject'] = subject
    msg['From'] = mail_from
    msg['To'] = mail_to

    # Create the body of the message (a plain-text and an HTML version).
    #text = msg
    html = msg_txt

    # Record the MIME types of both parts - text/plain and text/html.
    #part1 = MIMEText(text, 'plain')
    part2 = MIMEText(html, 'html')

    # Attach parts into message container.
    # According to RFC 2046, the last part of a multipart message, in this case
    # the HTML message, is best and preferred.
    #msg.attach(part1)
    msg.attach(part2)

    #attachment
    for f in files:
        #octet-stream:binary data
        part = MIMEBase('application', 'octet-stream')
        part.set_payload(open(f, 'rb').read())
        encoders.encode_base64(part)
        part.add_header('Content-Disposition', 'attachment; filename=&quot;%s&quot;' % os.path.basename(f))
        msg.attach(part)

    # Send the message via local SMTP server.
    s = smtplib.SMTP('localhost')
    # sendmail function takes 3 arguments: sender's address, recipient's address
    # and message to send - here it is sent as one string.

    mailto_list = mail_to.strip().split(&quot;,&quot;)
    if len(mailto_list) &gt; 1:
        for mailtoi in mailto_list:
            s.sendmail(mail_from, mailtoi.strip(), msg.as_string())
    else:
        s.sendmail(mail_from, mail_to, msg.as_string())

    s.quit()
    return True

def main():
    files = []
    try:
        opts, args = getopt.getopt(sys.argv[1:], &quot;f:t:sⓜ️a:&quot;)
        for op, value in opts:
            if op == &quot;-f&quot;:
                mail_from = value
            elif op == &quot;-t&quot;:
                mail_to = value
            elif op == &quot;-s&quot;:
                subject = value
            elif op == &quot;-m&quot;:
                msg_txt = value
            elif op == &quot;-a&quot;:
                files = value.split(&quot;,&quot;)
    except getopt.GetoptError:
        print(sys.argv[0] + &quot; : params are not defined well!&quot;)

    print mail_from, mail_to, subject, msg_txt
    if files:
        send_mail(mail_from, mail_to, subject, msg_txt, files)
    else:
        send_mail(mail_from, mail_to, subject, msg_txt)

if __name__ == &quot;__main__&quot;:
    main()
</code></pre>
<h3 id="使用">使用</h3>
<p>CMD:</p>
<pre><code>./sendEmail.py -f &quot;fromSomeOne@XXX.com&quot; \
            -t &quot;toA@XXX.com,toB@XXX.com&quot; \
            -s &quot;the subject of mail&quot; \
            -m &quot;the mail Message.Main Content&quot; \
            -a &quot;attachment1_path,attachment2_path&quot;
</code></pre>
<p>IMPORT:</p>
<pre><code>:::python
import sendEmail
sendEmail.send_mail(mail_from, mail_to, subject, msg_txt, files)
</code></pre>
<p>The end!</p>
<p>wklken</p>
<p>Blog:  <a href="https://wklken.me">https://wklken.me</a></p>
<p>Email: <a href="mailto:wklken@yeah.net">wklken@yeah.net</a></p>
<p>2012-09-02</p>
]]></content>
		</item>
		
		<item>
			<title>入职一周年小结</title>
			<link>https://wklken.me/posts/2012/07/04/summary-04-workoneyear.html</link>
			<pubDate>Wed, 04 Jul 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/07/04/summary-04-workoneyear.html</guid>
			<description>@author: wklken @date: 2012-07-04 @version: 0.1 @desc: 入职一年，算作小结 2012-07-04 皓月当空，很久没有停下来好好望望月。 从去年毕业入职到现在，正好一周年。 一年中，不变的很多，改变的也很多。 记</description>
			<content type="html"><![CDATA[<p>@author: wklken
@date: 2012-07-04
@version: 0.1
@desc: 入职一年，算作小结</p>
<p>2012-07-04</p>
<p>皓月当空，很久没有停下来好好望望月。</p>
<p>从去年毕业入职到现在，正好一周年。</p>
<p>一年中，不变的很多，改变的也很多。</p>
<p>记得去年的7月4日，背着包挤公交去入职，认识新同事，打了两个礼拜酱油（熟悉各种&hellip;），然后开工。</p>
<p>上班，培训，上课，写作业，加班，outing,新人秀&hellip;&hellip;.</p>
<p>回想一年，过得还算充实。</p>
<p>时间流逝，生活也是一个不断发展的过程，是过程而非结果，这中间，得到什么，失去什么，迷茫，信念，思考，欢乐，失落，看过，走过，遗忘的，铭记的，遇到的事情，见到的人，做过的事，成功的，失败的&hellip;&hellip;.</p>
<p>人生，就像老天爷起了一个进程，问题是丫永远不知道接下去会干什么，碰到什么，溢出，阻塞，挂起，还是直接被干掉，虽然大部分时间运行稳定，但是很不幸：1，资源是抢占的，2，程序没有经过测试，有bug，而且还不少</p>
<p>工作后</p>
<p>工作后，最纠结的是，你不能想在学校那样，随心所欲。我一直认为，学校是人生中停留最最幸福和干净的地方（对比后才知道），但不幸的是，当局者迷，围城，年轻的我们总是想跳出这围城，但，很多东西，都是失去后才觉得美好，所以，珍惜当下吧。不能凭好恶，不能凭心情去做事，很多事情，虽不像不愿，却也需要去面对，去处理，而且要处理得让自己满意。</p>
<p>另外一个很明显的变化是，上班，你再也没有整整一大段时间去做一件自己想做的事情，上班时间，下班时间，作为IT工程师的我们，还有加班时间（囧），大学或许可以一个礼拜攻完一本书，但是工作后，你会发现，可能一个月才能勉勉强强啃完，时间碎片化，而且每天工作很晚，回来还要做点自己喜欢的事情，生活会相当充实。嘿。看很多书，做很多事。这是我刚入职后不久贴到墙上的，后者不清楚是否足够，幸好前者感觉还是达到。工作后最明显的变化还有一点就是这个，你可以买书，哈哈，不用像大学里，从生活费里省出来。当当，京东各种促销，三百减一百，算下来今年买书估计也有一千，当然，光买不看是件极其败家的事情，算到今天，四十余本，一半杂书，一半技术砖头，都啃得差不多，所获颇多，今年也算没有虚度，算是给自个先投资了。</p>
<p>工作后，还有一个，就是没有各种假了，没有暑假寒假，一年只有法定假日加上那么可怜的几天年假，上学是偶尔可以翘翘课，上班时却没法翘班，记得刚来时，7点10分起，公交，八点多准时到公司，三个月，风雨无阻，一年后最大的变化是，八点起，接近九点到公司，老油条了&hellip;&hellip;.所谓不打卡，上下班自由，但是事情多的时候，只能悲剧地加班了。刚来那半年，加班比较多，因为各种不懂，不熟悉，但那段时间或许是指数级地上涨，最近加班不多，大部分在做自己想做的事情，却也发现，没压力，动力似乎少了不少，哎。</p>
<p>很多时候</p>
<p>很多时候，都在思考，自己想要的是什么，活在这个世上，所追求的是什么？难道简简单单遵循一个模式，这不是我想要的，这个问题思考很久，一直没有想清楚，继续思考中。很多时候，感觉现在得到的似乎并不是自己想要的东西，就像，原本想要一个西瓜，生活return一个苹果回来，想着好歹是个苹果，就接受了。外人看起来很美好，起码你有了个苹果，但苹果是酸是甜，有木有虫子，好不好吃，这是个问题。</p>
<p>很多时候，困扰我们的，不是我们没有，而是我们拥有的不是我们想要的。</p>
<p>很多时候，生活不易，冷暖自知。</p>
<p>一直在想，是不是应该放下所有，去找自己想要的东西。但自己想要的东西，似乎又没想好。这么一个蛋疼的问题，哎。</p>
<p>每天</p>
<p>每天依旧跟上学似地，三点一线，完成工作，看书，睡觉，周末敲敲代码，逛逛西湖（去过几次不记得了&hellip;）</p>
<p>每天刷刷微博看看网易新闻（每天的报道都让我感觉这世界快完了&hellip;&hellip;..）</p>
<p>每天逛逛论坛，更新下blog，更新下自己的代码</p>
<p>这一年</p>
<p>这一年，工作中同事给予了很多帮助，衷心感谢。</p>
<p>这一年，开始了思考，学会了如何去解决各式各样的问题。</p>
<p>这一年，见过靠谱的，不靠谱的，自认自己做到“靠谱”二字。</p>
<p>这一年，一起入职的同事走了几个。</p>
<p>最近</p>
<p>最近，一直在攻书，感觉有进步</p>
<p>最近，迷上了骑行，每周五十公里（周末，平常上班6<em>2</em>5=60，上班有点远）</p>
<p>最近，最幸福的时候，便是周末，泡杯清茶，放段音乐，看书or敲代码or思考，安安静静地。远离喧嚣的外界，思考。</p>
<p>列了20个自己想去的地方，列了20个自己会实现的目标。</p>
<p>未来是个未知数，在思考，在前进，一年后会是怎么样我不清楚，但我相信可以达到目标，一步一个脚印。</p>
<p>几点，权作小结：</p>
<ol>
<li>要主动。做任何事情，多要主动，不要被动地放任事情发展，否则最后悲剧的往往是你；</li>
<li>站在别人的角度思考和沟通；</li>
<li>事不过三，发生一次，可以，两次，忍了，三次，决不允许；</li>
<li>学会拒绝，大包大揽老好人是不行的，严格遵循原则，否则，悲剧的肯定是你（加班&amp;背锅，对谁都不好）</li>
<li>学习&amp;思考，做靠谱的人，你对所做事情的学习和思考，决定了你是不是变得越来越靠谱，也决定了你是工作一年有三年经验，还是工作三年有一年经验；（干IT的毕业一年经验一年半，加班加的）</li>
<li>没有大事，没有小事，认认真真做事，淡定严谨</li>
<li>读书&amp;尝试新事物&amp;使用工具</li>
<li>锻炼身体</li>
</ol>
<p>The End!</p>
]]></content>
		</item>
		
		<item>
			<title>数据结构&amp;算法实践—【排序|插入排序】插入排序</title>
			<link>https://wklken.me/posts/2012/06/02/python-ds-10-sort-insert.html</link>
			<pubDate>Sat, 02 Jun 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/06/02/python-ds-10-sort-insert.html</guid>
			<description>排序&amp;raquo;选择排序&amp;raquo;选择排序 List: 0.概念+示例分析 1.插入排序实现 start 基本概念： 维基百科http://zh.wikipedi</description>
			<content type="html"><![CDATA[<blockquote>
<p>排序&raquo;选择排序&raquo;选择排序</p>
</blockquote>
<p>List:</p>
<pre><code>0.概念+示例分析
1.插入排序实现
</code></pre>
<ol start="0">
<li>start</li>
</ol>
<p>基本概念：</p>
<p>维基百科http://zh.wikipedia.org/wiki/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F</p>
<p>插入排序，简单来说就是每次拿一个新的数，将其插入到有序序列中.</p>
<p>示例：</p>
<pre><code>[8, 4, 3, 1, 6, 9, 2, 7]
</code></pre>
<p>index- 1  #从第二个数开始</p>
<pre><code>move 1 , change-&gt; [4, 8, 3, 1, 6, 9, 2, 7]  #移动一次，插入
</code></pre>
<p>index- 2</p>
<pre><code>move 2 , change-&gt; [3, 4, 8, 1, 6, 9, 2, 7] #移动两次，插入
</code></pre>
<p>index- 3</p>
<pre><code>move 3 , change-&gt; [1, 3, 4, 8, 6, 9, 2, 7]
</code></pre>
<p>index- 4</p>
<pre><code>move 1 , change-&gt; [1, 3, 4,6, 8, 9, 2, 7]
</code></pre>
<p>index- 5</p>
<pre><code>move 0 ,  nochange -&gt; [1, 3, 4, 6, 8, 9, 2, 7]
</code></pre>
<p>index- 6</p>
<pre><code>move 5 , change-&gt; [1,2, 3, 4, 6, 8, 9, 7]
</code></pre>
<p>index- 7</p>
<pre><code>move 2 , change-&gt; [1, 2, 3, 4, 6,7, 8, 9]

[1, 2, 3, 4, 6, 7, 8, 9]
</code></pre>
<ol>
<li>start</li>
</ol>
<p>插入排序python实现</p>
<pre><code>#!/usr/bin/python
# -*- coding:utf-8 -*-
#插入排序
#@author: wklken@yeah.net

def insert_sort(l):
    print l
    for i in range(1,len(l)): #从第二个元素开始
        value = l[i]
        while i &gt;= 1 and l[i-1] &gt; value:
            l[i] = l[i-1]
            i -= 1
        l[i] = value
    return l
l = [8, 4, 3, 1, 6, 9, 2, 7]
print insert_sort(l)
</code></pre>
<p>改进及优化：</p>
<p>1.加入监控，已排序完成直接退出</p>
<p>2.使用二分插入排序，即，处理某个节点往前插入的时候，使用二分查找插入</p>
]]></content>
		</item>
		
		<item>
			<title>数据结构&amp;算法实践—【排序|选择排序】堆排序</title>
			<link>https://wklken.me/posts/2012/06/02/python-ds-09-sort-heap.html</link>
			<pubDate>Sat, 02 Jun 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/06/02/python-ds-09-sort-heap.html</guid>
			<description>排序&amp;raquo;选择排序&amp;raquo;堆排序 List: 0.概念+伪代码+示例分析 1.堆排序实现 2.Question start 基本概念： 维基百科http://zh.wikip</description>
			<content type="html"><![CDATA[<blockquote>
<p>排序&raquo;选择排序&raquo;堆排序</p>
</blockquote>
<p>List:</p>
<pre><code>0.概念+伪代码+示例分析
1.堆排序实现
2.Question
</code></pre>
<ol start="0">
<li>start</li>
</ol>
<p>基本概念：</p>
<p>维基百科http://zh.wikipedia.org/zh-cn/%E5%A0%86%E7%A9%8D%E6%8E%92%E5%BA%8F</p>
<pre><code>function heapSort(A : list[1..n]) {
    max_heap = make_max_heap(A)  #构建一个最大堆
    i = 1
    while(max_heap.size() &gt; 0){   #当堆中还存在值
        A[n-i] = max_heap.pop_max()   #取出最大一个
        i++
    }
}
</code></pre>
<p>堆为一棵完全二叉树，每个节点值都&gt;=子节点值</p>
<p>堆排序根据这个特性，首先将所有元素建立堆，然后一个个取出，即有序的</p>
<p>堆中每个节点的位置：</p>
<pre><code>父节点i的左子节点在位置 (2*i);
父节点i的右子节点在位置 (2*i+1);
子节点i的父节点在位置 floor(i/2);
</code></pre>
<p>最大堆主要操作逻辑：</p>
<p>插入：将新元素加入完全二叉树最后一个节点，依次往上，调整直到满足父节点值都&gt;=子节点值</p>
<p>删除：移除根节点，将最后一个节点拿到根节点，依次往下，调整</p>
<p>原始:</p>
<p><img src="https://github.com/wklken/gallery/blob/master/pyds/heap-1.jpg?raw=true" alt="heap1"></p>
<p>插入操作：12，先假定放在最后一个位置，然后从这个节点开始往上，同父节点比较，依次调整</p>
<p><img src="https://github.com/wklken/gallery/blob/master/pyds/heap-2.jpg?raw=true" alt="heap2"></p>
<p>删除：取走11，将最后一个元素8移到根节点，从上往下，重新调整</p>
<p><img src="https://github.com/wklken/gallery/blob/master/pyds/heap-3.jpg?raw=true" alt="heap3"></p>
<ol>
<li>start</li>
</ol>
<p>根据公式，我们可以使用数组模拟实现完全二叉树(不使用首个位置)</p>
<p>首先，我们先实现堆:</p>
<pre><code>#!/usr/bin/python
# -*- coding:utf-8 -*-
#堆排序
#@author: wklken@yeah.net

#先实现一个最大堆
class MaxHeap:
    def __init__(self):
        self.heap = [0]  #第一个元素用不到，只是为了将下标转为1开始，方便计算节点的位置
    def isEmpty(self):
        return len(self.heap) == 1
    def size(self):
        return len(self.heap) - 1
    #插入节点
    def insert(self, value):
        i = len(self.heap)
        self.heap.append(value)
        while i != 1 and value &gt; self.heap[i/2]:  #如果插入节点大于其父节点，需要交换二者,反复，直到值小于父节点
            self.heap[i], self.heap[i/2] = self.heap[i/2], self.heap[i]  #父节点下移
            i = i/2
        self.heap[i] = value  #把 value插入对应位置
    #删除最大节点——最大的是根节点
    def deleteMax(self):
        if self.isEmpty(): #没有元素了
            return None
        x = self.heap[1]  #最大

        last = self.heap.pop()
        if self.size() == 0: #每次取最后一个，若是只剩两个的情况，pop
            return x
        #每次，移除根节点，将树的最后一个节点挪到根节点，然后从上到下，调整位置，保证树是一个最大堆
        i = 1
        ci = 2
        current_size = self.size()
        while  ci &lt;= current_size:
            if ci &lt; current_size and self.heap[ci] &lt; self.heap[ci+1]:
                ci += 1

            if last &gt;= self.heap[ci]:
                break

            self.heap[i] = self.heap[ci]
            i = ci
            ci *= 2
        self.heap[i] = last
        return x

    def initFromList(self, l):
        self.heap.extend(l)
        size = self.size()
        #从最后一棵子树开始，调整每一棵子树
        for i in range(size/2,0,-1):
              t_root = self.heap[i]

              c = 2*i
              while c &lt;= size:
                  if c &lt; size and self.heap[c] &lt; self.heap[c+1]:
                      c += 1
                  if t_root &gt;= self.heap[c]:
                      break
                  self.heap[c/2] = self.heap[c]
                  c *= 2
                  self.heap[c/2] = t_root
</code></pre>
<p>然后，实现排序过程：</p>
<pre><code>:::python
def heap_sort(l):
    m = MaxHeap()
    m.initFromList(l)
    result = []
    for i in range(len(l)):
        result.append(m.deleteMax())
        print result
    return result
</code></pre>
<ol start="2">
<li>start</li>
</ol>
<p>A.概念，过程描述？</p>
<p>B. 时间复杂度？空间复杂度？是否是稳定排序？</p>
<p>C.适用场景，何种情况下表现最优</p>
]]></content>
		</item>
		
		<item>
			<title>数据结构&amp;算法实践—【排序|交换排序】Bogo排序</title>
			<link>https://wklken.me/posts/2012/05/27/python-ds-07-sort-bogo.html</link>
			<pubDate>Sun, 27 May 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/05/27/python-ds-07-sort-bogo.html</guid>
			<description>维基百科上排序算法表里的算法基本都实现完了，陆续发出来 有几个排序只有英文维基说明，有几个有中文，但是压根对不上，例如臭皮匠排序&amp;hellip</description>
			<content type="html"><![CDATA[<p>维基百科上排序算法表里的算法基本都实现完了，陆续发出来</p>
<p>有几个排序只有英文维基说明，有几个有中文，但是压根对不上，例如臭皮匠排序&hellip;..晕死，查找中&hellip;.</p>
<p>另外快排不敢轻易发出，等完全所有实现版本都搜罗分析完了再来.</p>
<hr>
<blockquote>
<p>排序&raquo;交换排序&raquo;地精排序</p>
</blockquote>
<p>List:</p>
<pre><code>0.概念(木有啥伪代码)
2.bogo排序实现
</code></pre>
<ol start="0">
<li>start</li>
</ol>
<p>这是一个比较蛋碎的排序算法&hellip;..囧</p>
<p>原理：将一堆卡片撒到地上，查看是否已排序好，若没有，捡起来再撒一次，直到有序情况出现.</p>
<p>维基百科:http://zh.wikipedia.org/wiki/Bogo%E6%8E%92%E5%BA%8F</p>
<p>有兴趣看看无限猴子定理:http://zh.wikipedia.org/wiki/%E7%84%A1%E9%99%90%E7%8C%B4%E5%AD%90%E5%AE%9A%E7%90%86</p>
<p>1.实现:</p>
<pre><code>#!/usr/bin/python
# -*- coding:utf-8 -*-
#bogo排序
#@author: wklken@yeah.net

import random
def is_order(l): #判断序列是否有序
    for i in range(len(l)-1):
        if l[i] &gt; l[i+1]:
          return False
    return True

def bogo_sort(l):
    while not is_order(l):
        random.shuffle(l) #随机重排
        print l
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>数据结构&amp;算法实践—【排序|交换排序】地精排序及改进</title>
			<link>https://wklken.me/posts/2012/05/27/python-ds-06-sort-gnome.html</link>
			<pubDate>Sun, 27 May 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/05/27/python-ds-06-sort-gnome.html</guid>
			<description>排序&amp;raquo;交换排序&amp;raquo;地精排序 List: 0.概念+伪代码+示例分析 1.地精排序实现 2.改进 3.Question start 基本概念: 维基百科:http://e</description>
			<content type="html"><![CDATA[<blockquote>
<p>排序&raquo;交换排序&raquo;地精排序</p>
</blockquote>
<p>List:</p>
<pre><code>0.概念+伪代码+示例分析
1.地精排序实现
2.改进
3.Question
</code></pre>
<ol start="0">
<li>start</li>
</ol>
<p>基本概念:</p>
<p>维基百科:http://en.wikipedia.org/wiki/Gnome_sort(目前只有英文版的)</p>
<p>地精排序又称侏儒排序，类似于插入排序，但是将一个数放入其正确位置的交换同冒泡排序（一系列交换）</p>
<p>简单，只有一层循环，</p>
<p>时间复杂度O(n^2)，最优复杂度O(n),平均时间复杂度O(n^2)</p>
<p>其实思想很简单,往前冒泡，一旦发生数据交换，就往回冒泡，直到把被交换数字放入正确位置，之后，继续前进</p>
<p>伪代码(来自于维基百科)</p>
<pre><code>procedure gnomeSort(a[])
    pos := 1
    while pos &lt; length(a)
        if (a[pos] &gt;= a[pos-1])
            pos := pos + 1
        else
            swap a[pos] and a[pos-1]
            if (pos &gt; 1)
                pos := pos - 1
            else
                pos := pos + 1
            end if
        end if
    end while
end procedure
</code></pre>
<p>例子：</p>
<pre><code>[5, 3, 2, 4]               #输入数组

i=0, i=i+1=1    #初始，i=0 ，直接i+=1

cmp l[0]= 5  l[1]= 3
change -&gt; [3, 5, 2, 4]
swap, i=i-1=0   #发生交换，i=i-1

i=0, i=i+1=1   #i=0,i+=1

cmp l[0]= 3  l[1]= 5
no swap, i=i+1=1   #无交换，i+=1

cmp l[1]= 5  l[2]= 2
change -&gt; [3, 2, 5, 4]  #交换
swap, i=i-1=1    #i=i-1，反向冒泡开始

cmp l[0]= 3  l[1]= 2
change -&gt; [2, 3, 5, 4]
swap, i=i-1=0  # 交换

i=0, i=i+1=1
cmp l[0]= 2  l[1]= 3
no swap, i=i+1=1 #无交换，i+=1

cmp l[1]= 3  l[2]= 5
no swap, i=i+1=2 #无交换，i+=1

cmp l[2]= 5  l[3]= 4
change -&gt; [2, 3, 4, 5]
swap, i=i-1=2  #交换,i-=1

cmp l[1]= 3  l[2]= 4
no swap, i=i+1=2

cmp l[2]= 4  l[3]= 5
no swap, i=i+1=3 #结束排序
</code></pre>
<p>1 start</p>
<pre><code>#!/usr/bin/python
# -*- coding:utf-8 -*-
# 地精排序
#@author: wklken@yeah.net

def gnome_sort(l):
    i = 0
    while i &lt; len(l):
        if i == 0 or l[i - 1] &lt; l[i]: #i=0或者正序不需交换，i+1
            i += 1
        else:  #否则，交换两数，i回退
            l[i - 1], l[i] = l[i], l[i - 1]
            i -= 1
</code></pre>
<ol start="2">
<li>start</li>
</ol>
<p>观察上面例子，是不是发现有些别扭&hellip;&hellip;.</p>
<pre><code>[3, 5, 2, 4]  #比较 5,2
[3, 2, 5, 4]  #交换
[3, 2,5, 4]  #比较 3,2
[2, 3, 5, 4]  #交换
[2, 3, 5, 4]    #比较2,3
[2, 3, 5, 4]    #比较3,5
</code></pre>
<p>没错，若是到了b存在交换，反向冒泡，直至把被交换数冒泡放到其有序位置a,然后再从a-&gt;b进行比较冒泡</p>
<p>其实，a-&gt;b这一段序列已经是有序的，不需要浪费比较次数在这上面</p>
<p>所以我们进行jump</p>
<p>即，记录b的位置，当发现反序冒泡没有交换时（冒泡结束），jump到b位置，继续正序冒泡</p>
<p>代码:</p>
<pre><code>:::python
def gnome_sort2(l):
    i = 0
    current_index = 0  #保存反向冒泡前位置
    back_noswap = True  #标识反向冒泡是否完成
    while i &lt; len(l):
        if i == 0 or l[i - 1] &lt; l[i]: #i=0或者正序不需交换，i+1
            i += 1
            back_noswap = True
        else:  #否则，交换两数，i回退
            l[i - 1], l[i] = l[i], l[i - 1]
            current_index = i + 1  #开始反序，记录位置,跳转回来后比较就是 i i+1两个数的比较，之前数已有序
            back_noswap = False
            i -= 1
            print &quot;change -&gt;&quot;, l
        if current_index and back_noswap:  #满足 当前是反序冒泡，且未发数据交换，代表已结束，可以跳回
            i = current_index
            current_index = 0
            print &quot;jump&quot;,i    
</code></pre>
<p>实际过程：</p>
<pre><code>[5, 3, 2, 4]
</code></pre>
<p>cmp  5 3</p>
<pre><code>change -&gt; [3, 5, 2, 4]
jump 2   #这里jump的位置是i+1
</code></pre>
<p>cmp  5 2</p>
<pre><code>change -&gt; [3, 2, 5, 4]
</code></pre>
<p>cmp  3 2</p>
<pre><code>change -&gt; [2, 3, 5, 4]
</code></pre>
<p>jump 2
cmp  3 5
cmp  5 4</p>
<pre><code>change -&gt; [2, 3, 4, 5]
</code></pre>
<p>cmp  3 4
jump 4</p>
<p>相同例子的序列，改进前比较次数12，改进后只需要9次</p>
<ol start="3">
<li>start</li>
</ol>
<p>A.地精排序概念，过程描述？</p>
<p>B.时间复杂度？空间复杂度？是否是稳定排序？</p>
<p>C.适用场景，何种情况下表现最优</p>
]]></content>
		</item>
		
		<item>
			<title>数据结构&amp;算法实践—【排序|选择排序】选择排序</title>
			<link>https://wklken.me/posts/2012/05/27/python-ds-07-sort-select.html</link>
			<pubDate>Sun, 27 May 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/05/27/python-ds-07-sort-select.html</guid>
			<description>排序&amp;raquo;选择排序&amp;raquo;选择排序 List: 0.概念+伪代码+示例分析 1.选择排序实现 2.Question start 基本概念： 维基百科http://zh.wik</description>
			<content type="html"><![CDATA[<blockquote>
<p>排序&raquo;选择排序&raquo;选择排序</p>
</blockquote>
<p>List:</p>
<pre><code>0.概念+伪代码+示例分析
1.选择排序实现
2.Question
</code></pre>
<ol start="0">
<li>start</li>
</ol>
<p>基本概念：</p>
<p>维基百科http://zh.wikipedia.org/wiki/%E9%81%B8%E6%93%87%E6%8E%92%E5%BA%8F</p>
<p>伪代码:</p>
<pre><code>function selectSort(A : list[1..n]) {
    index = n
    while (index &gt; 1): #共有n-1次选择
    {
        max_index = index
        for i from index  downto 1 {  #每次从剩余序列选出最大的
        if(A[i] &gt; A[max_index)
        {
            max_index = i
            }
        }
        swap(A[index], A[max_index ])  #将最大的换到后面
        index = index -1
    }
}
</code></pre>
<p>示例：</p>
<p>[49, 38, 65, 97, 76, 13, 27]</p>
<pre><code>Current index 6 value= 27 Max index: 3 value= 97
exchange -&gt; [49, 38, 65, 27, 76, 13, 97]
Current index 5 value= 13 Max index: 4 value= 76
exchange -&gt; [49, 38, 65, 27, 13, 76, 97]
Current index 4 value= 13 Max index: 2 value= 65
exchange -&gt; [49, 38, 13, 27, 65, 76, 97]
Current index 3 value= 27 Max index: 0 value= 49
exchange -&gt; [27, 38, 13, 49, 65, 76, 97]
Current index 2 value= 13 Max index: 1 value= 38
exchange -&gt; [27, 13, 38, 49, 65, 76, 97]
Current index 1 value= 13 Max index: 0 value= 27
exchange -&gt; [13, 27, 38, 49, 65, 76, 97]
Done
</code></pre>
<ol>
<li>start</li>
</ol>
<p>实现代码</p>
<pre><code>:::python
def select_sort(l):
    index = len(l) -1
    while index:
        max_index = index
        for i in range(index):
            if l[i] &gt; l[max_index]:
                max_index = i
        if l[max_index] &gt; l[index]:
            l[index],l[max_index] = l[max_index], l[index]
        index -= 1
</code></pre>
<ol start="2">
<li>start</li>
</ol>
<p>A.概念，过程描述？</p>
<p>B.交换次数，比较次数，赋值次数?</p>
<p>C. 时间复杂度？空间复杂度？是否是稳定排序？</p>
<p>D.适用场景，何种情况下表现最优</p>
]]></content>
		</item>
		
		<item>
			<title>Python修改xml任意内容[xml.etree.ElementTree]</title>
			<link>https://wklken.me/posts/2012/05/25/python-xml-etree.html</link>
			<pubDate>Fri, 25 May 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/05/25/python-xml-etree.html</guid>
			<description>使用库 xml.etree.ElementTree 代码托管位置 github-pytools 需求 在实际应用中，需要对xml配置文件进行实时修改， 1.增加、删除 某些节点 2.增加，删除，修改某个节点下的某些属性 3.</description>
			<content type="html"><![CDATA[<p>使用库 <a href="http://docs.python.org/library/xml.etree.elementtree.html">xml.etree.ElementTree</a></p>
<p>代码托管位置 <a href="https://github.com/wklken/pytools">github-pytools</a></p>
<h3 id="需求">需求</h3>
<p>在实际应用中，需要对xml配置文件进行实时修改，</p>
<p>1.增加、删除 某些节点</p>
<p>2.增加，删除，修改某个节点下的某些属性</p>
<p>3.增加，删除，修改某些节点的文本</p>
<h3 id="使用xml文档">使用xml文档</h3>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;framework&gt;
    &lt;processers&gt;
        &lt;processer name=&quot;AProcesser&quot; file=&quot;lib64/A.so&quot;
            path=&quot;/tmp&quot;&gt;
        &lt;/processer&gt;
        &lt;processer name=&quot;BProcesser&quot; file=&quot;lib64/B.so&quot; value=&quot;fordelete&quot;&gt;
        &lt;/processer&gt;
        &lt;processer name=&quot;BProcesser&quot; file=&quot;lib64/B.so2222222&quot;/&gt;

        &lt;services&gt;
            &lt;service name=&quot;search&quot; prefix=&quot;/bin/search?&quot;
                output_formatter=&quot;OutPutFormatter:service_inc&quot;&gt;

                &lt;chain sequency=&quot;chain1&quot;/&gt;
                &lt;chain sequency=&quot;chain2&quot;&gt;&lt;/chain&gt;
            &lt;/service&gt;
            &lt;service name=&quot;update&quot; prefix=&quot;/bin/update?&quot;&gt;
                &lt;chain sequency=&quot;chain3&quot; value=&quot;fordelete&quot;/&gt;
            &lt;/service&gt;
        &lt;/services&gt;
    &lt;/processers&gt;
&lt;/framework&gt;
</code></pre>
<h3 id="实现思想">实现思想</h3>
<p>使用ElementTree，先将文件读入，解析成树，之后，根据路径，可以定位到树的每个节点，再对节点进行修改，最后直接将其输出</p>
<h3 id="实现代码">实现代码</h3>
<pre><code>#!/usr/bin/python
# -*- coding=utf-8 -*-
# author : wklken@yeah.net
# date: 2012-05-25
# version: 0.1

from xml.etree.ElementTree import ElementTree,Element

def read_xml(in_path):
    '''读取并解析xml文件
       in_path: xml路径
       return: ElementTree'''
    tree = ElementTree()
    tree.parse(in_path)
    return tree

def write_xml(tree, out_path):
    '''将xml文件写出
       tree: xml树
       out_path: 写出路径'''
    tree.write(out_path, encoding=&quot;utf-8&quot;,xml_declaration=True)

def if_match(node, kv_map):
    '''判断某个节点是否包含所有传入参数属性
       node: 节点
       kv_map: 属性及属性值组成的map'''
    for key in kv_map:
        if node.get(key) != kv_map.get(key):
            return False
    return True

#---------------search -----
def find_nodes(tree, path):
    '''查找某个路径匹配的所有节点
       tree: xml树
       path: 节点路径'''
    return tree.findall(path)

def get_node_by_keyvalue(nodelist, kv_map):
    '''根据属性及属性值定位符合的节点，返回节点
       nodelist: 节点列表
       kv_map: 匹配属性及属性值map'''
    result_nodes = []
    for node in nodelist:
        if if_match(node, kv_map):
            result_nodes.append(node)
    return result_nodes

#---------------change -----
def change_node_properties(nodelist, kv_map, is_delete=False):
    '''修改/增加 /删除 节点的属性及属性值
       nodelist: 节点列表
       kv_map:属性及属性值map'''
    for node in nodelist:
        for key in kv_map:
            if is_delete:
                if key in node.attrib:
                    del node.attrib[key]
            else:
                node.set(key, kv_map.get(key))

def change_node_text(nodelist, text, is_add=False, is_delete=False):
    '''改变/增加/删除一个节点的文本
       nodelist:节点列表
       text : 更新后的文本'''
    for node in nodelist:
        if is_add:
            node.text += text
        elif is_delete:
            node.text = &quot;&quot;
        else:
            node.text = text

def create_node(tag, property_map, content):
    '''新造一个节点
       tag:节点标签
       property_map:属性及属性值map
       content: 节点闭合标签里的文本内容
       return 新节点'''
    element = Element(tag, property_map)
    element.text = content
    return element

def add_child_node(nodelist, element):
    '''给一个节点添加子节点
       nodelist: 节点列表
       element: 子节点'''
    for node in nodelist:
        node.append(element)

def del_node_by_tagkeyvalue(nodelist, tag, kv_map):
    '''同过属性及属性值定位一个节点，并删除之
       nodelist: 父节点列表
       tag:子节点标签
       kv_map: 属性及属性值列表'''
    for parent_node in nodelist:
        children = parent_node.getchildren()
        for child in children:
            if child.tag == tag and if_match(child, kv_map):
                parent_node.remove(child)

if __name__ == &quot;__main__&quot;:
    #1. 读取xml文件
    tree = read_xml(&quot;./test.xml&quot;)

    #2. 属性修改
      #A. 找到父节点
    nodes = find_nodes(tree, &quot;processers/processer&quot;)
      #B. 通过属性准确定位子节点
    result_nodes = get_node_by_keyvalue(nodes, {&quot;name&quot;:&quot;BProcesser&quot;})
      #C. 修改节点属性
    change_node_properties(result_nodes, {&quot;age&quot;: &quot;1&quot;})
      #D. 删除节点属性
    change_node_properties(result_nodes, {&quot;value&quot;:&quot;&quot;}, True)

    #3. 节点修改
      #A.新建节点
    a = create_node(&quot;person&quot;, {&quot;age&quot;:&quot;15&quot;,&quot;money&quot;:&quot;200000&quot;}, &quot;this is the firest content&quot;)
      #B.插入到父节点之下
    add_child_node(result_nodes, a)

    #4. 删除节点
       #定位父节点
    del_parent_nodes = find_nodes(tree, &quot;processers/services/service&quot;)
       #准确定位子节点并删除之
    target_del_node = del_node_by_tagkeyvalue(del_parent_nodes, &quot;chain&quot;, {&quot;sequency&quot; : &quot;chain1&quot;})

    #5. 修改节点文本
       #定位节点
    text_nodes = get_node_by_keyvalue(find_nodes(tree, &quot;processers/services/service/chain&quot;), {&quot;sequency&quot;:&quot;chain3&quot;})
    change_node_text(text_nodes, &quot;new text&quot;)

    #6. 输出到结果文件
    write_xml(tree, &quot;./out.xml&quot;)
</code></pre>
<h3 id="修改后的结果">修改后的结果</h3>
<pre><code>&lt;?xml version='1.0' encoding='utf-8'?&gt;
&lt;framework&gt;
    &lt;processers&gt;
        &lt;processer file=&quot;lib64/A.so&quot; name=&quot;AProcesser&quot; path=&quot;/tmp&quot;&gt;
        &lt;/processer&gt;
        &lt;processer age=&quot;1&quot; file=&quot;lib64/B.so&quot; name=&quot;BProcesser&quot;&gt;
            &lt;person age=&quot;15&quot; money=&quot;200000&quot;&gt;this is the firest content&lt;/person&gt;
        &lt;/processer&gt;
        &lt;processer age=&quot;1&quot; file=&quot;lib64/B.so2222222&quot; name=&quot;BProcesser&quot;&gt;
            &lt;person age=&quot;15&quot; money=&quot;200000&quot;&gt;this is the firest content&lt;/person&gt;
        &lt;/processer&gt;

        &lt;services&gt;
            &lt;service name=&quot;search&quot; output_formatter=&quot;OutPutFormatter:service_inc&quot;
                prefix=&quot;/bin/search?&quot;&gt;

                &lt;chain sequency=&quot;chain2&quot; /&gt;
            &lt;/service&gt;
            &lt;service name=&quot;update&quot; prefix=&quot;/bin/update?&quot;&gt;
                &lt;chain sequency=&quot;chain3&quot; value=&quot;fordelete&quot;&gt;new text&lt;/chain&gt;
            &lt;/service&gt;
        &lt;/services&gt;
    &lt;/processers&gt;
&lt;/framework&gt;
</code></pre>
<p>The end!</p>
<p>wklken</p>
<p>2012-05-25</p>
]]></content>
		</item>
		
		<item>
			<title>数据结构&amp;算法实践—【排序|交换排序】奇偶排序</title>
			<link>https://wklken.me/posts/2012/05/17/python-ds-04-sort-oddeven.md.html</link>
			<pubDate>Thu, 17 May 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/05/17/python-ds-04-sort-oddeven.md.html</guid>
			<description>排序&amp;raquo;交换排序&amp;raquo;奇偶排序 List: 0.概念+伪代码+示例分析 1.奇偶排序实现 2.Question 3.后续扩展 start 基本概念: 维基百科http://</description>
			<content type="html"><![CDATA[<blockquote>
<p>排序&raquo;交换排序&raquo;奇偶排序</p>
</blockquote>
<p>List:</p>
<pre><code>0.概念+伪代码+示例分析
1.奇偶排序实现
2.Question
3.后续扩展
</code></pre>
<ol start="0">
<li>start</li>
</ol>
<p>基本概念:</p>
<p>维基百科http://zh.wikipedia.org/wiki/%E5%A5%87%E5%81%B6%E6%8E%92%E5%BA%8F</p>
<p>伪代码:</p>
<pre><code>function odd_even(A: list[1..n]){
    whie has_swap:
        for i from 0 to n-1 &amp;&amp; i%2==0 &amp;&amp; i+1&lt;=n-1{
            if(A[i] &gt; A[i+1])
                swap(A[i], A[i+1])
        }
        for j from 1 to n-1 &amp;&amp; j%2==1 &amp;&amp; j+1&lt;=n-1{
            if(A[j] &gt; A[j+1])
                swap(A[j], A[j+1])
        }
}
</code></pre>
<p>奇偶排序</p>
<p>类似于冒泡排序，冒泡排序并行化的版本()</p>
<p>简单但效率不高</p>
<p>每一轮存在两次排序：奇数排序（下标奇数与其邻居比较&amp;交换），偶数排序（下标偶数与其邻居比较交换）</p>
<p>直到不存在数据交换</p>
<p>示例：</p>
<pre><code>[50, 10, 30, 20, 40, 60]
</code></pre>
<p>第一轮 偶数排序</p>
<pre><code>cmp 50 10
change [10, 50, 30, 20, 40, 60]
cmp 30 20
change [10, 50, 20, 30, 40, 60]
cmp 40 60
odd range [10, 50, 20, 30, 40, 60]
</code></pre>
<p>第一轮 奇数排序</p>
<pre><code>cmp 50 20
change [10, 20, 50, 30, 40, 60]
cmp 30 40
even range [10, 20, 50, 30, 40, 60]
</code></pre>
<p>第二轮 偶数排序</p>
<pre><code>cmp 10 20
cmp 50 30
change [10, 20, 30, 50, 40, 60]
cmp 40 60
odd range [10, 20, 30, 50, 40, 60]
</code></pre>
<p>第二轮 奇数排序</p>
<pre><code>cmp 20 30
cmp 50 40
change [10, 20, 30, 40, 50, 60]
even range [10, 20, 30, 40, 50, 60]
</code></pre>
<p>第三轮 不存在数据交换</p>
<pre><code>cmp 10 20
cmp 30 40
cmp 50 60
odd range [10, 20, 30, 40, 50, 60]
cmp 20 30
cmp 40 50
even range [10, 20, 30, 40, 50, 60] #到这里，无数据交换，结束
[10, 20, 30, 40, 50, 60]
</code></pre>
<ol>
<li>
<p>start</p>
<p>:::python
def oddeven_sort(l):
odd_range = range(0,len(l)-1,2)
even_range = range(1,len(l)-1,2)
sign = 1
while sign:
sign = 0
for i in odd_range:
if l[i] &gt; l[i+1]:
l[i], l[i+1] = l[i+1],l[i]
sign = 1
for j in even_range:
if l[j] &gt; l[j+1]:
l[j], l[j+1] = l[j+1], l[j]
sign = 1
print l</p>
</li>
<li>
<p>start</p>
</li>
</ol>
<p>A.奇偶排序概念，过程描述？</p>
<p>B. 时间复杂度？空间复杂度？是否是稳定排序？</p>
<ol start="3">
<li>start</li>
</ol>
<p>后续扩展——Batcher奇偶归并排序（后面实现）</p>
]]></content>
		</item>
		
		<item>
			<title>数据结构&amp;算法实践—【排序|交换排序】梳子排序</title>
			<link>https://wklken.me/posts/2012/05/17/python-ds-05-sort-comb.html</link>
			<pubDate>Thu, 17 May 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/05/17/python-ds-05-sort-comb.html</guid>
			<description>排序&amp;raquo;交换排序&amp;raquo;梳子排序 List: 0.概念+伪代码+示例分析 1.梳子排序实现 2.Question start 基本概念: 维基百科http://zh.wik</description>
			<content type="html"><![CDATA[<blockquote>
<p>排序&raquo;交换排序&raquo;梳子排序</p>
</blockquote>
<p>List:</p>
<pre><code>0.概念+伪代码+示例分析
1.梳子排序实现
2.Question
</code></pre>
<ol start="0">
<li>start</li>
</ol>
<p>基本概念:</p>
<p>维基百科http://zh.wikipedia.org/wiki/%E6%A2%B3%E6%8E%92%E5%BA%8F</p>
<p>伪代码</p>
<pre><code>function comb_sort(A: list[1..n]){
    gap = A.size
    rate = 1.3
    while gap &lt;= 1 &amp;&amp; swap = 0{
        //更新间距
        gap := (int)gap/rate
        swap := 0
        i := 0
        //梳一次
        while i + gap &lt; A.size{
            if A[i] &gt; A[i+gap]
            A(array[i] , A[i+gap])
            swap := 1 
            i := i + 1
        }
    }
}
</code></pre>
<p>梳子排序:</p>
<p>间隔gap   递减率rate(大于1的数)</p>
<p>比较 i 和 i+gap 位置的数字，若反序，交换，然后i+=1，直到比较i+gap超过最大索引</p>
<p>然后gap /= rate，再重复上面操作</p>
<p>直到gap=1 ，执行最后一遍梳理后结束</p>
<p>可以想象成 先拿一把大梳子（只有三个齿两个缝的）从第一个梳到最后一个，把两个缝隙里面反序的数交换</p>
<p>再换把小点的梳子，重复.</p>
<p>最终，中间那个齿消失（梳理相邻两个数），完成最后一遍梳理</p>
<p>例子：(关注gap和cmp的下标)</p>
<pre><code>[8, 4, 3, 7, 6, 5, 2, 1]
</code></pre>
<p>gap:  6  [初始设定gap=size/1.3]</p>
<pre><code>cmp l[0]=8,l[6]=2
change [2, 4, 3, 7, 6, 5,8, 1]
cmp l[1]=4,l[7]=1
change [2, 1, 3, 7, 6, 5, 8,4]
one time: [2, 1, 3, 7, 6, 5, 8, 4]
</code></pre>
<p>gap:  4</p>
<pre><code>cmp l[0]=2,l[4]=6
cmp l[1]=1,l[5]=5
cmp l[2]=3,l[6]=8
cmp l[3]=7,l[7]=4
change [2, 1, 3, 4, 6, 5, 8,7]
one time: [2, 1, 3, 4, 6, 5, 8, 7]
</code></pre>
<p>gap:  3</p>
<pre><code>cmp l[0]=2,l[3]=4
cmp l[1]=1,l[4]=6
cmp l[2]=3,l[5]=5
cmp l[3]=4,l[6]=8
cmp l[4]=6,l[7]=7
one time: [2, 1, 3, 4, 6, 5, 8, 7]
</code></pre>
<p>gap:  2</p>
<pre><code>cmp l[0]=2,l[2]=3
cmp l[1]=1,l[3]=4
cmp l[2]=3,l[4]=6
cmp l[3]=4,l[5]=5
cmp l[4]=6,l[6]=8
cmp l[5]=5,l[7]=7
one time: [2, 1, 3, 4, 6, 5, 8, 7]
</code></pre>
<p>gap:  1</p>
<pre><code>cmp l[0]=2,l[1]=1
change [1,2, 3, 4, 6, 5, 8, 7]
cmp l[1]=2,l[2]=3
cmp l[2]=3,l[3]=4
cmp l[3]=4,l[4]=6
cmp l[4]=6,l[5]=5
change [1, 2, 3, 4, 5,6, 8, 7]
cmp l[5]=6,l[6]=8
cmp l[6]=8,l[7]=7
change [1, 2, 3, 4, 5, 6, 7,8]
one time: [1, 2, 3, 4, 5, 6, 7, 8]
</code></pre>
<p>观察上面例子，梳排序可以有效地将乌龟（尾部的小数值和头部的大数值）调整到有序后位置的附近</p>
<ol>
<li>
<p>start</p>
<p>:::python
def comb_sort(l):
dis = int(len(l)/1.3)
while dis:
for i in range(len(l)-dis):
if l[i] &gt; l[i+dis]:
l[i], l[i+dis] = l[i+dis], l[i]
dis = int(dis/1.3)</p>
</li>
</ol>
<p>2 start</p>
<p>A.奇偶排序概念，过程描述？</p>
<p>B. 时间复杂度？空间复杂度？是否是稳定排序？</p>
<p>C.适用场景</p>
]]></content>
		</item>
		
		<item>
			<title>数据结构&amp;算法实践—【排序|交换排序】冒泡排序及改进</title>
			<link>https://wklken.me/posts/2012/05/16/python-ds-02-sort-bubble.html</link>
			<pubDate>Wed, 16 May 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/05/16/python-ds-02-sort-bubble.html</guid>
			<description>排序&amp;raquo;交换排序&amp;raquo;冒泡排序 List: 0.概念+伪代码+示例分析 1.基本冒泡排序 2.冒泡排序改进1 3.冒泡排序改进2——局部冒泡</description>
			<content type="html"><![CDATA[<blockquote>
<p>排序&raquo;交换排序&raquo;冒泡排序</p>
</blockquote>
<p>List:</p>
<pre><code>0.概念+伪代码+示例分析
1.基本冒泡排序
2.冒泡排序改进1
3.冒泡排序改进2——局部冒泡排序
4.Question
</code></pre>
<ol start="0">
<li>start</li>
</ol>
<p>基本概念：</p>
<p>维基百科http://zh.wikipedia.org/wiki/%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F</p>
<p>伪代码：(来自百科)</p>
<pre><code>function bubblesort (A : list[1..n]) {
    var int i, j;
    for i from n downto 1 {
        for j from 0 to i-1 { 
            if (A[j] &gt; A[j+1])
                swap(A[j], A[j+1])
        }
    }
}
</code></pre>
<p>简要排序过程的示例：(基本冒泡排序)</p>
<p>初始数组</p>
<pre><code>[50, 10, 30, 20, 40, 60]
</code></pre>
<p>第一轮：</p>
<pre><code>cmp 50 10 -&gt; change [10, 50, 30, 20, 40, 60]
cmp 50 30 -&gt; change [10, 30, 50, 20, 40, 60]
cmp 50 20 -&gt; change [10, 30, 20, 50, 40, 60]
cmp 50 40 -&gt; change [10, 30, 20, 40, 50, 60]
cmp 50 60 -&gt; nochange
</code></pre>
<p>第二轮:</p>
<pre><code>[10, 30, 20, 40, 50, 60]
cmp 10 30 -&gt; nochange
cmp 30 20 -&gt; change [10, 20, 30, 40, 50, 60]
cmp 30 40 -&gt; nochange
cmp 40 50 -&gt; nochange
</code></pre>
<p>第三轮</p>
<pre><code>[10, 20, 30, 40, 50, 60]
cmp 10 20 -&gt; nochange
cmp 20 30 -&gt; nochange
cmp 30 40 -&gt; nochange
</code></pre>
<p>第四轮：</p>
<pre><code>[10, 20, 30, 40, 50, 60]
cmp 10 20 -&gt; nochange
cmp 20 30 -&gt; nochange
</code></pre>
<p>第五轮：</p>
<pre><code>[10, 20, 30, 40, 50, 60]
cmp 10 20 -&gt; nochange
[10, 20, 30, 40, 50, 60]
</code></pre>
<p>cmp count 15</p>
<p>即共进行n-1=5轮冒泡，比较次数为 (n-1) + (n-2) + &hellip;&hellip;+1 =n*(n-1)/2=15</p>
<ol>
<li>start</li>
</ol>
<p>基本冒泡排序python实现:</p>
<pre><code>:::python
#冒泡排序，base
def bubble(l):
    print l
    for i in range(len(l)-1,0,-1):
        #每一轮冒泡，第 i个 元素会是最大的(i&lt;=size-1)
        for j in range(i):
        #从0到i-1,比较 current 和next,若current &gt; next，对换
        if l[j] &gt; l[j+1]:
            l[j], l[j+1] = l[j+1], l[j]
        print l
</code></pre>
<ol start="2">
<li>start</li>
</ol>
<p>问题：在基本冒泡排序的示例中，第三轮结束时，其实已经排序完成了，但是还是一直会持续后面几轮的排序，这就带来了无谓的浪费.</p>
<p>改进：加入标志，判断，若是上一轮不存在数据交换，代表上一轮已经是排序的了，退出</p>
<p>比较次数:12</p>
<pre><code>:::python
#改进1:  当某一轮跑完，不存在数据交换时，代表已排序完成，此时退出
def bubble_improve(l):
    flag = 1 #初始标志，1
    for i in range(len(l)-1,0,-1):
        #若是上一轮存在数据交换，继续执行排序
        if flag:
            flag = 0 #每一轮初始，交换标志为0
            for j in range(i):
                if l[j] &gt; l[j+1]:
                    l[j], l[j+1] = l[j+1], l[j]
                    flag = 1 #存在交换，标志置为1
            print l
        #否则，代表目前序列已经排序完毕了
        else:
            break
</code></pre>
<ol start="3">
<li>start</li>
</ol>
<p>局部冒泡排序：(资料不多，不知道自己理解对不对)</p>
<p>序列[ a b c d ] 冒泡到了b,此时a小于b，比较b c，若是 b 大于 c，交换b c 得到 [ a c b d ]</p>
<p>通常冒泡排序一直往前，继续比较b和d</p>
<p>其实，在完成一次数据交换时(b&lt;-&gt;c)，可以反向增加一次比较，(a 和 c) ，若是a&gt;c,再次交换得到[ c a b d] ——反向做一次冒泡</p>
<p>（百度百科有几行&hellip;.凑合看）</p>
<p>定义：可以在一趟全局扫描中，对每一反序数据对进行局部冒泡排序处理，称之为局部冒泡排序
局部冒泡排序与冒泡排序算法具有相同的时间复杂度，并且在正序和逆序的情况下，所需的关键字的比较次数和移动次数完全相同。
由于局部冒泡排序和冒泡排序的数据移动次数总是相同的，而局部冒泡排序所需关键字的比较次数常少于冒泡排序，这意味着局部冒泡排序很可能在平均比较次数上对冒泡排序有所改进
当比较次数较少的优点不足以抵消其程序复杂度所带来的额外开销，而当数据量较大时，局部冒泡排序的时间性能则明显优于冒泡排序
(查看百度百科，有张对比图)</p>
<p>简而言之，正向冒泡时，若存在数据交换，反向再进行一次冒泡比较。减少了比较次数</p>
<p>why?</p>
<p>假设在第二轮冒泡  到了50 &lt;-&gt; 30</p>
<pre><code>[10, 20, 40, 50, 30, 60]
带标志位冒泡：cmp 50 30 -&gt;change  [10, 20, 40, 30, 50, 60]     count+=1
第三轮 count+= 4      (10,20) (20,40) (40,30) (40,50)
第四轮count +3   (10,20) (20,30) (30,40)     (无数据交换了，退出)
</code></pre>
<p>共   8次</p>
<pre><code>局部冒泡: cmp 50 30 -&gt;change  [10, 20, 40, 30, 50, 60]    count+=1
cmp 40 30 -&gt; change  [10, 20, 30, 40,  50, 60]  count+=1
第三轮 count+=4(无数据交换了，退出)
</code></pre>
<p>共 6次</p>
<pre><code>:::python
#改进2: 局部冒泡排序
def bubble_improve2(l):
    print l
    flag = 1
    scope = len(l)-1
    for i in range(scope,0,-1):
        if flag: 
            flag = 0
            for j in range(i):
                inner_flag = 0 #本次是否存在数据交换标志，每次置空，不复用flag的原因是如果第一次就交换了，会造成不必要的局部冒泡
                if l[j] &gt; l[j+1]:
                    l[j], l[j+1] = l[j+1], l[j]
                    flag = 1
                    inner_flag = 1
                #从前往后的冒泡，j与j+1发生数据交换了,反向冒泡 j-1 j
                #若是本轮存在数据交换，局部排序处理 j-1  j j+1，保证是从小到大的 
                if inner_flag:
                    if j - 1 &gt; 0:
                        if l[j-1] &gt; l[j]:
                            l[j-1], l[j] = l[j],l[j-1]
            print l
        else:
        break
</code></pre>
<p>局部冒泡排序一个示例过程：</p>
<pre><code>[50, 10, 30, 20, 40, 60]
cmp 50 10 -&gt; change [10, 50, 30, 20, 40, 60]
cmp 50 30 -&gt; change [10, 30, 50, 20, 40, 60]
cmp 50 20 -&gt; change [10, 30, 20, 50, 40, 60]
inner cmp 30 20
inner change [10, 20, 30, 50, 40, 60]
cmp 50 40 -&gt; change [10, 20, 30, 40, 50, 60]
inner cmp 30 40
cmp 50 60 -&gt; nochange
[10, 20, 30, 40, 50, 60]
cmp 10 20 -&gt; nochange
cmp 20 30 -&gt; nochange
cmp 30 40 -&gt; nochange
cmp 40 50 -&gt; nochange
[10, 20, 30, 40, 50, 60]
</code></pre>
<ol start="4">
<li>start</li>
</ol>
<p>仅是贴出来，权当复习，木有答案，后续补充</p>
<p>A.冒泡排序概念，过程描述？</p>
<p>B.最差，平均，最优 时间复杂度？</p>
<p>C.空间复杂度？</p>
<p>D.是否是稳定排序？</p>
<p>E.如何改进？</p>
<p>F.局部冒泡排序原理？</p>
<p>G.适用场景，什么情况下最优，什么情况下最差？</p>
<p>&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&ndash;  END &mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-</p>
<p>P.S.</p>
<p>这是第一篇，有什么不对请指正哈，欢迎补充任何问题和答案</p>
<p>白天上班加班(SDET)，夜深敲代码(python,java&hellip;&hellip;.)，会坚持写完的</p>
]]></content>
		</item>
		
		<item>
			<title>数据结构&amp;算法实践—【排序|交换排序】鸡尾酒排序</title>
			<link>https://wklken.me/posts/2012/05/16/python-ds-03-sort-cocktail.html</link>
			<pubDate>Wed, 16 May 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/05/16/python-ds-03-sort-cocktail.html</guid>
			<description>排序&amp;raquo;交换排序&amp;raquo;鸡尾酒排序 List: 0.概念+伪代码+示例分析 1.鸡尾酒排序实现 2.Question start 基本概念: 维基百科http://zh.w</description>
			<content type="html"><![CDATA[<blockquote>
<p>排序&raquo;交换排序&raquo;鸡尾酒排序</p>
</blockquote>
<p>List:</p>
<pre><code>0.概念+伪代码+示例分析
1.鸡尾酒排序实现
2.Question
</code></pre>
<ol start="0">
<li>start</li>
</ol>
<p>基本概念:</p>
<p>维基百科http://zh.wikipedia.org/wiki/%E9%B8%A1%E5%B0%BE%E9%85%92%E6%8E%92%E5%BA%8F</p>
<p>伪代码：</p>
<pre><code>function cocktail_sort(A: list[1..n]){
    for i from 0 to n/2{
        for f from i to (n-i-2){
            if(A[a] &gt; A[a+1])
                swap(A[a],A[a+1])
        }
        for b from  (n-i-2) to (i+1){
            if(A[b] &lt; A[b-1])
                swap(A[b],A[b-1]
        }
    }
}
</code></pre>
<p>鸡尾酒排序是冒泡排序的变种——双向冒泡排序</p>
<p>从伪代码可以看到，每一轮循环，从前到后一次正向冒泡，之后从后往前再进行一次逆向冒泡(每一轮存在两个数被排序)</p>
<p>可以看到的表现是两边先排序好，逐渐向中间有序</p>
<p>示例：</p>
<pre><code>-&gt;[50, 10, 30, 20, 60, 40, 1]
-&gt; [10, 30, 20, 50, 40, 1, 60]  第一轮正向
-&gt; [1, 10, 30, 20, 50, 40, 60]  第一轮逆向
-&gt; [1, 10, 20, 30, 40, 50, 60]  第二轮正向
-&gt; [1, 10, 20, 30, 40, 50, 60]  第二轮逆向，无交换，结束
</code></pre>
<p>详细比较过程:</p>
<pre><code>[50, 10, 30, 20, 60, 40, 1]
</code></pre>
<p>第一轮     正向</p>
<pre><code>l-&gt;r  cmp 50 10
change [10, 50, 30, 20, 60, 40, 1]
l-&gt;r  cmp 50 30
change [10, 30, 50, 20, 60, 40, 1]
l-&gt;r  cmp 50 20
change [10, 30, 20, 50, 60, 40, 1]
l-&gt;r  cmp 50 60
l-&gt;r  cmp 60 40
change [10, 30, 20, 50, 40, 60, 1]
l-&gt;r  cmp 60 1
change [10, 30, 20, 50, 40, 1, 60]
</code></pre>
<p>第一轮    逆向</p>
<pre><code>r-&gt;l  cmp 1 40
change [10, 30, 20, 50, 1, 40, 60]
r-&gt;l  cmp 1 50
change [10, 30, 20, 1, 50, 40, 60]
r-&gt;l  cmp 1 20
change [10, 30, 1, 20, 50, 40, 60]
r-&gt;l  cmp 1 30
change [10, 1, 30, 20, 50, 40, 60]
r-&gt;l  cmp 1 10
change [1, 10, 30, 20, 50, 40, 60]
</code></pre>
<p>[1, 10, 30, 20, 50, 40, 60]</p>
<p>第二轮 正向</p>
<pre><code>l-&gt;r  cmp 10 30
l-&gt;r  cmp 30 20
change [1, 10, 20, 30, 50, 40, 60]
l-&gt;r  cmp 30 50
l-&gt;r  cmp 50 40
change [1, 10, 20, 30, 40, 50, 60]
</code></pre>
<p>第二轮 逆向</p>
<pre><code>r-&gt;l  cmp 40 30
r-&gt;l  cmp 30 20
r-&gt;l  cmp 20 10
[1, 10, 20, 30, 40, 50, 60] (上一轮逆向无交换，结束排序)
[1, 10, 20, 30, 40, 50, 60]
</code></pre>
<p>本数组共比较18次，而使用带标志冒泡排序需要21次</p>
<ol>
<li>start</li>
</ol>
<p>实现代码</p>
<pre><code>:::python
def cocktail_sort(l):
    size = len(l)
    sign = 1  #用于判断上轮排序是否存在数据交换
    for i in range(size / 2):
        if sign:
            sign = 0
            #正向，冒泡   从   i 到    对称的位置-1
            for j in range(i, size - 1 - i):
                if l[j] &gt; l[j + 1]:
                    l[j], l[j + 1] = l[j + 1], l[j]
            #逆向，冒泡  从正向排完最大数的前一个开始，到  i
            for k in range(size - 2 - i, i, -1):
                if l[k] &lt; l[k - 1]:
                    l[k], l[k - 1] = l[k - 1], l[k]
                    sign = 1  #若是逆向存在交换，代表还没排序完成，否则，排序完成
        else:
            break
    print l
</code></pre>
<p>改换成while</p>
<pre><code>:::python
def cocktail_sort2(l):
    size = len(l)
    sign = 1  #用于判断上轮排序是否存在数据交换
    i = 0
    while sign:
    sign = 0
    for j in range(i, size - 1 - i):
        if l[j] &gt; l[j + 1]:
            l[j], l[j + 1] = l[j + 1], l[j]
    for k in range(size - 2 - i, i, -1):
        if l[k] &lt; l[k - 1]:
            l[k], l[k - 1] = l[k - 1], l[k]
            sign = 1  #若是逆向存在交换，代表还没排序完成，否则，排序完成
    i += 1
</code></pre>
<p>也可以维护一个bottom和top，每次bottom+1,top-1</p>
<ol start="2">
<li>start</li>
</ol>
<p>A.鸡尾酒排序概念，过程描述？</p>
<p>B.最差，平均，最优 时间复杂度？
最差=平均=O(n^2)  最优=O(n)</p>
<p>C.空间复杂度？</p>
<p>D.是否是稳定排序？</p>
<p>E.存在什么方法可以更优化</p>
<p>F.适用场景，什么情况下最优，什么情况下最差？</p>
<p>&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&ndash;  END &mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-</p>
<p>p.s.维基百科的动态排序图很赞&amp;坑爹好几种排序无中文描述，只能啃英文版的百科了，到时候一块贴了</p>
]]></content>
		</item>
		
		<item>
			<title>数据结构&amp;算法实践-Python——序章</title>
			<link>https://wklken.me/posts/2012/05/10/python-ds-01-start.html</link>
			<pubDate>Thu, 10 May 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/05/10/python-ds-01-start.html</guid>
			<description>很久很久之前的blog，没写完的系列，当时想写完来着，有点用，先转过来 数据结构&amp;amp;算法实践——Python &amp;mdash;&amp;mdash;</description>
			<content type="html"><![CDATA[<p>很久很久之前的blog，没写完的系列，当时想写完来着，有点用，先转过来</p>
<p>数据结构&amp;算法实践——Python </p>
<p>&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&ndash;目录 START&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;</p>
<p>第一部分列表(目录主要来自于维基百科)</p>
<p>模块一：经典排序实现</p>
<p>交换排序法</p>
<p>      冒泡排序 |鸡尾酒排序 |奇偶排序 |梳排序 |地精排序(gnome_sort) |Bogo排序|快速排序</p>
<p>选择排序法</p>
<p>      选择排序 | 堆排序</p>
<p>插入排序法</p>
<p>      插入排序 | 希尔排序 | 二叉查找树排序 | Library sort | Patience sorting</p>
<p>归并排序法</p>
<p>      归并排序 | Strand sort</p>
<p>非比较排序法</p>
<p>      基数排序 | 桶排序 | 计数排序 | 鸽巢排序 | Burstsort | Bead sort</p>
<p>其他</p>
<p>      拓扑排序 | 排序网络 | Bitonic sorter | Batcher odd-even mergesort | Pancake sorting</p>
<p>低效排序法</p>
<p>      Bogosort | Stooge sort</p>
<p>模块二：经典查找</p>
<p>模块三：数据结构(后续补充完整，树和图是大头，包含很多分类和经典算法)</p>
<p>线性表   队列   栈   堆   树  图</p>
<p> &mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&ndash;目录 END&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;</p>
<p>写在前面</p>
<p>毕业迄今也接近一年了，发现很多学校的东西似乎生疏了.</p>
<p>最近重新拿起数据结构，算法导论，离散数学，决定用代码敲些东西，权当复习</p>
<p>大部分的地方我只会给出例子和具体的代码实现，顺带给出一些百科的链接，概念和理论性的东西网上都有，不赘述了
  
之所以选择用python来写，主要是python的可读性非常好，即使不写注释，也能很轻松读懂.</p>
<p>我把这个过程大概切成三个部分：</p>
<p>1.经典数据结构和算法的实现</p>
<p>实现基本的经典算法，包括经典排序，经典查找，索引等，基本实现及改进</p>
<p>实现基本的数据结构，包括线性表，队列，栈，堆，树，图等，包含扩展</p>
<p>使用实现类似Java的数据结构，至始至终都认为java的api最为优美，使用Python实现之，包括Map,List,Set等，提供相同的API，同时希望会循序渐进，先用简单直观的方法实现，给出优化，涉及的知识主要是python面向对象，继承，重写内置方法，封装，（要对Python和java数据结构实现的底层源码有了解，需要看源代码）</p>
<p>2.笔试题面试题数据结构和算法实现</p>
<p>笔试&amp;面试题的python处理</p>
<p>使用Python搞定笔试题&amp;面试题中出现的算法和数据结构题目</p>
<p>包含大规模数据处理的详细例子</p>
<p>3.challenge</p>
<p>挑战一些大个的东西，深入实现一些较为复杂的算法</p>
<p>不罗嗦，先列下目录，已经写完一部分了，逐步发出来，更新目录(挪到前头去了)
     
先列这些，逐渐补充.</p>
<p>每天上完班回来，啃这堆砖头，然后敲出来，累却充实.</p>
<p>敲代码，调试代码其实是一件十分快乐的事情</p>
<p>My daytime job is SDET,平时敲自己喜欢的代码的时间并不会太多，业余时间有限</p>
<p>但做事贵善始善终，会坚持搞完的哈!
    
The End!</p>
<p><a href="mailto:wklken@yeah.net">wklken@yeah.net</a></p>
<p>2012-05-10</p>
]]></content>
		</item>
		
		<item>
			<title>Python解析xml大文件[sax]</title>
			<link>https://wklken.me/posts/2012/04/07/python-xml-sax.html</link>
			<pubDate>Sat, 07 Apr 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/04/07/python-xml-sax.html</guid>
			<description>使用到的包：xml.sax 文档 代码托管位置 github-pytools 需求 读取xml数据文件，文件较大，需要实时处理插入到数据库 xml文档 &amp;lt;PERSONS&amp;gt; &amp;lt;person&amp;gt; &amp;lt;id&amp;gt;100000&amp;lt;/id&amp;gt; &amp;lt;sex&amp;gt;</description>
			<content type="html"><![CDATA[<p>使用到的包：xml.sax <a href="">文档</a></p>
<p>代码托管位置 <a href="https://github.com/wklken/pytools">github-pytools</a></p>
<h3 id="需求">需求</h3>
<p>读取xml数据文件，文件较大，需要实时处理插入到数据库</p>
<h3 id="xml文档">xml文档</h3>
<pre><code>&lt;PERSONS&gt;
&lt;person&gt;
    &lt;id&gt;100000&lt;/id&gt;
    &lt;sex&gt;男&lt;/sex&gt;
    &lt;address&gt;北京，海淀区&lt;/address&gt;
    &lt;fansNum&gt;437&lt;/fansNum&gt;
    &lt;summary&gt;1989&lt;/summary&gt;
    &lt;wbNum&gt;333&lt;/wbNum&gt;
    &lt;gzNum&gt;242&lt;/gzNum&gt;
    &lt;blog&gt;null&lt;/blog&gt;
    &lt;edu&gt;大学&lt;/edu&gt;
    &lt;work&gt;&lt;/work&gt;
    &lt;renZh&gt;1&lt;/renZh&gt;
    &lt;brithday&gt;2月14日&lt;/brithday&gt;
&lt;/person&gt;
&lt;/PERSONS&gt;
</code></pre>
<h3 id="处理">处理</h3>
<p>sax处理时并不会像dom一样可以以类似节点的维度进行读取,它只有 开始标签  内容  结束标签 之分</p>
<p>处理思想是：通过一个handler，对开始标签，内容，结束标签各有一个处理函数</p>
<h3 id="代码及注解">代码及注解</h3>
<p>#person 处理类</p>
<pre><code>:::python
from xml.sax import handler,parseString
class PersonHandler(handler.ContentHandler):
  def __init__(self, db_ops):
    #db op obj
    self.db_ops = db_ops
    #存储一个person的map
    self.person = {}
    #当前的tag
    self.current_tag = &quot;&quot;
    #是否是tag之间的内容 ，目的拿到tag间内容，不受空白的干扰
    self.in_quote = 0
  #开始，清空map
  def startElement(self, name, attr):
    #以person，清空map
    if name == &quot;person&quot;:
      self.person = {}
    #记录 状态
    self.current_tag = name
    self.in_quote = 1
  #结束，插入数据库
  def endElement(self, name):
    #以person结尾  代表读取一个person的信息结束
    if name == &quot;person&quot;:
      #do something
      in_fields = tuple([ ('&quot;' + self.person.get(i,&quot;&quot;) + '&quot;')  for i in fields ])
      print in_sql % in_fields
      db_ops.insert( in_sql%(in_fields))
    #处理
    self.in_quote = 0
  def characters(self, content):
    #若是在tag之间的内容，更新到map中
    if self.in_quote:
      self.person.update({self.current_tag: content})
</code></pre>
<h3 id="加上入库的完整代码">加上入库的完整代码</h3>
<pre><code>#!/usr/bin/python
# -*- coding:utf-8 -*-
#parse_person.py
#version : 0.1
#author : wukunliang@163.com
#desc : parse person.xml and out sql


import sys,os
import MySQLdb

reload(sys)
sys.setdefaultencoding('utf-8')

in_sql = &quot;insert into person(id,sex,address,fansNum,summary,wbNum,gzNum,blog,edu,work,renZh,brithday) values(%s, %s, %s, %s, %s, %s,
          %s, %s, %s, %s, %s, %s)&quot;

fields = (&quot;id&quot;,&quot;sex&quot;,&quot;address&quot;,&quot;fansNum&quot;,&quot;summary&quot;,&quot;wbNum&quot;,&quot;gzNum&quot;,&quot;blog&quot;,&quot;edu&quot;,&quot;work&quot;,&quot;renZh&quot;,&quot;brithday&quot;)

#数据库方法
class Db_Connect:
    def __init__(self, db_host, user, pwd, db_name, charset=&quot;utf8&quot;,  use_unicode = True):
        print &quot;init begin&quot;
        print db_host, user, pwd, db_name, charset , use_unicode
        self.conn = MySQLdb.Connection(db_host, user, pwd, db_name, charset=charset , use_unicode=use_unicode)
        print &quot;init end&quot;

    def insert(self, sql):
        try:
            n = self.conn.cursor().execute(sql)
            return n
        except MySQLdb.Warning, e:
            print &quot;Error: execute sql '&quot;,sql,&quot;' failed&quot;

    def close(self):
        self.conn.close()

#person 处理类
from xml.sax import handler,parseString
class PersonHandler(handler.ContentHandler):
    def __init__(self, db_ops):
        #db op obj
        self.db_ops = db_ops
        #存储一个person的map
        self.person = {}
        #当前的tag
        self.current_tag = &quot;&quot;
        #是否是tag之间的内容
        self.in_quote = 0
    #开始，清空map
    def startElement(self, name, attr):
        #以person，清空map
        if name == &quot;person&quot;:
          self.person = {}
        #记录 状态
        self.current_tag = name
        self.in_quote = 1
    #结束，插入数据库
    def endElement(self, name):
        #以person结尾  代表读取一个person的信息结束
        if name == &quot;person&quot;:
            #do something
            in_fields = tuple([ ('&quot;' + self.person.get(i,&quot;&quot;) + '&quot;')  for i in fields ])
            print in_sql % in_fields
            db_ops.insert( in_sql%(in_fields))
        #处理
        self.in_quote = 0
    def characters(self, content):
        #若是在tag之间的内容，更新到map中
        if self.in_quote:
            self.person.update({self.current_tag: content})

if __name__ == &quot;__main__&quot;:
    f = open(&quot;./person.xml&quot;)
    #如果源文件gbk  转码      若是utf-8，去掉decode.encode
    db_ops = Db_Connect(&quot;127.0.0.1&quot;, &quot;root&quot;, &quot;root&quot;, &quot;test&quot;)
    parseString(f.read().decode(&quot;gbk&quot;).encode(&quot;utf-8&quot;), PersonHandler(db_ops))
    f.close()
    db_ops.close()
</code></pre>
<p>平时拿python来分析数据，工具脚本还有hadoop streamming，但是用的面和深度实在欠缺
只能说道行还浅，需要多多实践</p>
<p>The end!</p>
<p>2012-04-07</p>
]]></content>
		</item>
		
		<item>
			<title>Python读取修改ini配置文件[ConfigParser]</title>
			<link>https://wklken.me/posts/2012/02/19/python-ini-configparser.html</link>
			<pubDate>Sun, 19 Feb 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/02/19/python-ini-configparser.html</guid>
			<description>使用到的包：ConfigParser 文档 代码托管位置 github-pytools 需求 写个项目，用到数据库，多个地方使用，不能硬编码。很类似java的propertie</description>
			<content type="html"><![CDATA[<p>使用到的包：ConfigParser <a href="http://docs.python.org/2/library/configparser.html">文档</a></p>
<p>代码托管位置 <a href="https://github.com/wklken/pytools">github-pytools</a></p>
<h3 id="需求">需求</h3>
<p>写个项目，用到数据库，多个地方使用，不能硬编码。很类似java的properties文件</p>
<p>Python支持ini文件的读取</p>
<h3 id="ini文件">ini文件</h3>
<p>db_config.ini</p>
<pre><code>[baseconf]
host=127.0.0.1
port=3306
user=root
password=root
db_name=evaluting_sys
[concurrent]
processor=20
</code></pre>
<h3 id="示例代码">示例代码</h3>
<pre><code>#!/usr/bin/python
# -*- coding:utf-8 -*-
#author: lingyue.wkl
#desc: use to db ops
#---------------------
#2012-02-18 created
#---------------------

import sys,os
import ConfigParser
def test(config_file_path):
    cf = ConfigParser.ConfigParser()
    cf.read(config_file_path)

    s = cf.sections()
    print 'section:', s

    o = cf.options(&quot;baseconf&quot;)
    print 'options:', o

    v = cf.items(&quot;baseconf&quot;)
    print 'db:', v

    db_host = cf.get(&quot;baseconf&quot;, &quot;host&quot;)
    db_port = cf.getint(&quot;baseconf&quot;, &quot;port&quot;)
    db_user = cf.get(&quot;baseconf&quot;, &quot;user&quot;)
    db_pwd = cf.get(&quot;baseconf&quot;, &quot;password&quot;)

    print db_host, db_port, db_user, db_pwd

    cf.set(&quot;baseconf&quot;, &quot;db_pass&quot;, &quot;123456&quot;)
    cf.write(open(&quot;config_file_path&quot;, &quot;w&quot;))
if __name__ == &quot;__main__&quot;:
    test(&quot;../conf/db_config.ini&quot;)
</code></pre>
<h3 id="通用模块init_oppy">通用模块init_op.py</h3>
<p>支持命令行+import两种</p>
<pre><code>#!/usr/bin/python
# -*- coding:utf-8 -*-
#author: lingyue.wkl
#desc: use to read ini
#---------------------
#2012-02-18 created
#2012-09-02 changed for class support
#---------------------
import sys,os,time
import ConfigParser

class Config:
    def __init__(self, path):
        self.path = path
        self.cf = ConfigParser.ConfigParser()
        self.cf.read(self.path)
    def get(self, field, key):
        result = &quot;&quot;
        try:
            result = self.cf.get(field, key)
        except:
            result = &quot;&quot;
        return result
    def set(self, filed, key, value):
        try:
            self.cf.set(field, key, value)
            cf.write(open(self.path,'w'))
        except:
            return False
        return True

def read_config(config_file_path, field, key): 
    cf = ConfigParser.ConfigParser()
    try:
        cf.read(config_file_path)
        result = cf.get(field, key)
    except:
        sys.exit(1)
    return result

def write_config(config_file_path, field, key, value):
    cf = ConfigParser.ConfigParser()
    try:
        cf.read(config_file_path)
        cf.set(field, key, value)
        cf.write(open(config_file_path,'w'))
    except:
        sys.exit(1)
    return True

if __name__ == &quot;__main__&quot;:
   if len(sys.argv) &lt; 4:
      sys.exit(1)

   config_file_path = sys.argv[1] 
   field = sys.argv[2]
   key = sys.argv[3]
   if len(sys.argv) == 4:
      print read_config(config_file_path, field, key)
   else:
      value = sys.argv[4]
      write_config(config_file_path, field, key, value)
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Python解析xml[xml.dom]</title>
			<link>https://wklken.me/posts/2012/02/18/python-xml-dom.html</link>
			<pubDate>Sat, 18 Feb 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/02/18/python-xml-dom.html</guid>
			<description>用到的包：xml.dom.minidom 文档 代码托管位置 github-pytools 需求 有一个表，里面数据量比较大，每天一更新，其字段可以通过xml配置文件进行配置，</description>
			<content type="html"><![CDATA[<p>用到的包：xml.dom.minidom <a href="http://docs.python.org/2/library/xml.dom.minidom.html">文档</a></p>
<p>代码托管位置 <a href="https://github.com/wklken/pytools">github-pytools</a></p>
<h3 id="需求">需求</h3>
<p>有一个表，里面数据量比较大，每天一更新，其字段可以通过xml配置文件进行配置，即，可能每次建表的字段不一样。</p>
<p>上游跑时会根据配置从源文件中提取，到入库这一步需要根据配置进行建表。</p>
<h3 id="解决">解决</h3>
<p>写了一个简单的xml，配置需要字段及类型</p>
<p>上游读取到对应的数据</p>
<p>入库这一步，先把原表删除，根据配置建新表</p>
<h3 id="xml文件">XML文件</h3>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!-- 表名 ,数据库名   可灵活配置插入哪个库哪个表 --&gt;
&lt;table name=&quot;top_query&quot; db_name=&quot;evaluting_sys&quot;&gt;
&lt;!-- 非业务主键，自增长,可配名，其他 INTEGER UNSIGNED AUTO_INCREMENT  --&gt;
&lt;primary_key&gt;
&lt;name&gt;id&lt;/name&gt;
&lt;/primary_key&gt;
&lt;!-- 字段开始 --&gt;
&lt;field&gt;
&lt;name&gt;query&lt;/name&gt;
&lt;type&gt;varchar(200)&lt;/type&gt;
&lt;is_index&gt;false&lt;/is_index&gt;
&lt;description&gt;query&lt;/description&gt;
&lt;/field&gt;
&lt;field&gt;
&lt;name&gt;pv&lt;/name&gt;
&lt;type&gt;integer&lt;/type&gt;
&lt;is_index&gt;false&lt;/is_index&gt;
&lt;description&gt;pv&lt;/description&gt;
&lt;/field&gt;
&lt;field&gt;
&lt;name&gt;avg_money&lt;/name&gt;
&lt;type&gt;integer&lt;/type&gt;
&lt;is_index&gt;false&lt;/is_index&gt;
&lt;description&gt;&lt;/description&gt;
&lt;/field&gt;
&lt;!-- 字段配置结束 --&gt;
&lt;/table&gt;
</code></pre>
<h3 id="处理脚本">处理脚本</h3>
<pre><code>#!/usr/bin/python
# -*- coding:utf-8 -*-
#author: wklken
#desc: use to read db xml config.
#-----------------------
#2012-02-18 created
#----------------------

import sys,os
from xml.dom import minidom, Node

def read_dbconfig_xml(xml_file_path):
    content = {}

    root = minidom.parse(xml_file_path)
    table = root.getElementsByTagName(&quot;table&quot;)[0]

    #read dbname and table name.
    table_name = table.getAttribute(&quot;name&quot;)
    db_name = table.getAttribute(&quot;db_name&quot;)

    if len(table_name) &gt; 0 and len(db_name) &gt; 0:
        db_sql = &quot;create database if not exists `&quot; + db_name +&quot;`; use &quot; + db_name + &quot;;&quot;
        table_drop_sql = &quot;drop &quot; + table_name + &quot; if exists &quot; + table_name + &quot;;&quot;
        content.update({&quot;db_sql&quot; : db_sql})
        content.update({&quot;table_sql&quot; : table_drop_sql })
    else:
        print &quot;Error:attribute is not define well!  db_name=&quot; + db_name + &quot; ;table_name=&quot; + table_name
        sys.exit(1)
    #print table_name, db_name

    table_create_sql = &quot;create table &quot; + table_name +&quot;(&quot;

    #read primary cell
    primary_key = table.getElementsByTagName(&quot;primary_key&quot;)[0]
    primary_key_name = primary_key.getElementsByTagName(&quot;name&quot;)[0].childNodes[0].nodeValue

    table_create_sql += primary_key_name + &quot; INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,&quot;

    #print primary_key.toxml()
    #read ordernary field
    fields = table.getElementsByTagName(&quot;field&quot;)
    f_index = 0
    for field in fields:
        f_index += 1
        name = field.getElementsByTagName(&quot;name&quot;)[0].childNodes[0].nodeValue
        type = field.getElementsByTagName(&quot;type&quot;)[0].childNodes[0].nodeValue
        table_create_sql += name + &quot; &quot; + type
        if f_index != len(fields):
        table_create_sql += &quot;,&quot;
        is_index = field.getElementsByTagName(&quot;is_index&quot;)[0].childNodes[0].nodeValue

    table_create_sql += &quot;);&quot;
    content.update({&quot;table_create_sql&quot; : table_create_sql})
    #character set latin1 collate latin1_danish_ci;
    print content


if __name__ == &quot;__main__&quot;:
read_dbconfig_xml(sys.argv[1])
</code></pre>
<h3 id="涉及方法">涉及方法</h3>
<p>root = minidom.parse(xml_file_path)  获取dom对象</p>
<p>root.getElementsByTagName(&ldquo;table&rdquo;)  根据tag获取节点列表</p>
<p>table.getAttribute(&ldquo;name&rdquo;)  获取属性</p>
<p>primary_key.getElementsByTagName(&ldquo;name&rdquo;)[0].childNodes[0].nodeValue  获取子节点的值（<name>id</name>  得到id）</p>
<p>2012-02-18</p>
]]></content>
		</item>
		
		<item>
			<title>半年工作成长小结</title>
			<link>https://wklken.me/posts/2012/02/05/summary-03-workhalfyear.html</link>
			<pubDate>Sun, 05 Feb 2012 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2012/02/05/summary-03-workhalfyear.html</guid>
			<description>自去年毕业工作至今，满打满算，七个月 这七个月思考不断接触新的东西，学习很多，思考很多，却也依旧很是迷茫 很多问题还没想好，所以，继续思考 收获还</description>
			<content type="html"><![CDATA[<p>自去年毕业工作至今，满打满算，七个月</p>
<p>这七个月思考不断接触新的东西，学习很多，思考很多，却也依旧很是迷茫</p>
<p>很多问题还没想好，所以，继续思考</p>
<p>收获还是很多的，主要是一些前辈的建议</p>
<ol>
<li>技术与业务的关系？</li>
</ol>
<p>A. 业务是目的，技术是手段。要在业务的设计开发中沉淀出通用的技术产品，要不断了解出现的技术，是否能为我所用，形成敏锐的判断力</p>
<p>B. 业务分析能力更多的是经验、思考和坚持</p>
<p>C. 技术研究与创新要与团队的整体目标紧密相连，才能得到更好地认同和发展</p>
<p>刚毕业，对于技术，还是相当有热情的，理所当然，所谓的业务在心里的占比，就不是那么大了。去啃shell，啃python，回过头来发现似乎不太对劲。</p>
<p>对于这几点，迄今还不是很能理解。起码对技术热情依然盖过了业务。</p>
<p>很多问题，需要去深入学习和思考，或许只能慢慢来</p>
<ol start="2">
<li>要形成自己的知识体系</li>
</ol>
<p>术业有专攻，东西那么多，人的精力总是有限的，需要对自己整体的技术体系和业务体系有一个规划，形成体系。</p>
<p>盲目通吃，多而不精，浅尝则止，是不行的</p>
<p>单点 到 网络 再到体系</p>
<p>规划下目标，实现之</p>
<ol start="3">
<li>成长的过程就是进步</li>
</ol>
<p>A.形成自己的思想，自己的知识体系</p>
<p>B.形成自己的做事方法和风格</p>
<p>C.建立自己对事情的评判标准</p>
<p>这是一个迭代的过程，需要自己不断去摸索和改进</p>
<p>目前自己方法和风格刚刚雏形，所谓一步一个脚印，还是那句话，思考</p>
<ol start="4">
<li>
<p>多思考和分享，在不断分享中接受挑战，完善自己的思路</p>
</li>
<li>
<p>更加开放的心态，乐于接受新的事物，乐于接受不同的意见</p>
</li>
<li>
<p>忙不是成长的绊脚石，而是助推器，想方设法（技术方案，工具，解决方法），解决那些耗费时间的，机械性的工作，变得不忙，便是成长。</p>
</li>
</ol>
<p>我按照这个原则这么做的，结果发现，效率上去了，活也变多了，总体工作时间几乎没被降下来</p>
<p>加班，依然如故。所谓能力越大责任越大，同理，效率越高，活也越多</p>
<p>不过需要继续改进，为了不加班，继续努力改进</p>
<ol start="7">
<li>成长是长跑，持久的坚持是最基本的前提</li>
</ol>
<p>坚持，总之，需要形成好的习惯</p>
<ol start="8">
<li>
<p>多看多问，求甚解</p>
</li>
<li>
<p>从小事做起，主动承担，学习推动事情的发展，并解决之</p>
</li>
<li>
<p>停止抱怨，主动改变现状，很多事情，都不是问题</p>
</li>
</ol>
<p>开始抱怨的时候，就是问题出现的时候，想办法解决之</p>
<ol start="11">
<li>学会规划，也要学会量化</li>
</ol>
<p>记录于此</p>
<p>刚来，资源紧张，连续加了三个月班，然而，资源总是紧张滴，所以，要从自己，从流程上，不断审视，思考，优化和改进，提高自己的效率</p>
<p>虽然现在还是得加班，但是，毕竟机械性重复性劳动全部秒杀掉了，不用那么苦逼了</p>
<p>效率提高，意味着有更多时间学习想学的东西，然后再次提高效率，良性循环</p>
<p>以上很多事这半年来各位前辈给的建议，也供大家参考吧</p>
<p>虽然很多还需要继续思考，例如依旧偏执地将技术放在首位，例如为啥效率提高那么多还需要加班，</p>
<p>例如为啥那么苦逼，例如怎样才能不苦逼</p>
<p>继续思考，但是不要停下来，即使还没想清楚，即使依旧迷茫</p>
<p>做当前正确的事情</p>
<p>写年度规划去鸟，提前祝大伙元宵快乐</p>
<p>2012-02-05</p>
]]></content>
		</item>
		
		<item>
			<title>写在2011的结尾，2012的开始</title>
			<link>https://wklken.me/posts/2011/12/31/summary-02-2011end-2012begin.html</link>
			<pubDate>Sat, 31 Dec 2011 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2011/12/31/summary-02-2011end-2012begin.html</guid>
			<description>写在2011的结尾，2012的开始 2011-12-31 22:50 今天是2011的最后一天。 本来已经绝了写点东西的念头，但是想想，还是写点东西吧，算是对过去一年的一个总</description>
			<content type="html"><![CDATA[<p>写在2011的结尾，2012的开始</p>
<p>2011-12-31 22:50</p>
<p>今天是2011的最后一天。</p>
<p>本来已经绝了写点东西的念头，但是想想，还是写点东西吧，算是对过去一年的一个总结吧。</p>
<p>有点长，估计得写到明年才能写完</p>
<p>从哪开始呢</p>
<p>还是从头来吧</p>
<p>2010年最后一天，也就是去年的今天，我正在北京，什么区来着，额，丰台区，四环外的一个小区，程庄路程庄北里八号楼，一个半地下室，和同来实习的一群哥们在一起。</p>
<p>那时候实习，是来学校招的，大三时候也没想太多，面上了便来了。七八个人住半地下室，刚来第一个月是培训，什么都不管，早上五点半起，倒两个小时车，到北三环学院路一个大厦里上课，一天的课，那时起，对北京4毛的公交印象深刻，对一堵堵成列车的公交迄今难忘。</p>
<p>上课那段只是复习而已，个人而言，还没自己看十天书敲十天代码来得强，主要是快餐式地灌输J2EE快速开发的一些内容，当然，也学到了不少东西，起码在学校接触不到。</p>
<p>那时候，没想那么多，实习而已嘛，没想到我们几个实习生却承担了很大部分的工作，当做正式员工使，每天很忙，上班，吃饭，加班。印象里有几回凌晨两三点回去，满大街清冷，只有黑的不时靠上来问是否要打车。</p>
<p>上班，加班，轮回，但每周固定回去超市，我主要是买一周的补给。每周去一次超市，这个习惯，那时候养成。迄今保留。逛超市是件令人愉快的事情，虽然俺是效率派，去两三次之后彻底摸清货架，之后每次都是直达目的。</p>
<p>突然很怀念那段日子，不用想太多，除了干活，睡觉，偶尔出去聚一聚，奢侈一把。一群人。</p>
<p>天下没有不散的宴席，先是培训结束后，分到了不同项目组，然后，又是陆续离开了公司。</p>
<p>快到年前的时候，只剩下我一个人呆在宿舍，很是凄凉。</p>
<p>看过电影的快放没，一大群人拖着行李到了这个地方，一起生活，一起去上班，一起下班，做饭，吃饭，热热闹闹，然后，然后，一个个离开，到最后剩下你一个人。</p>
<p>实习的公司不大不小，最后我的感想：人很好，体制不行。的确，里面每个人都很好，很友善，那是我们刚踏入社会，虽然是半只脚，从他们身上，我学到了很多东西，我进的第一个项目组已经进入了维护期，所以也没什么事情，领了一台电脑，也没什么事干，前后经理就扔了两个任务给我。那一个月，却是感觉敲代码那么久第一次有了提升的感觉，一个月，new了一个java project，从无到有，搭建出了那个项目现有使用的基本框架，对还是菜鸟的我，开始感觉有点进步了，后台java-&gt;hibernate-&gt;mysql,前台完全是freemarker模板。也摸到点感觉，仿着写了一个。然后的然后，没然后了，我被一个电话借走，一个全国邮政的项目，慧哥是我们几个实习生的TL，那段时间还是很累的额，一周六天班，周一三五晚上必须加班，因为模板化了，每个人负责部分都是从前台到后台一路打到底，期间维护了所有的公有代码，以及对现有框架进行重构和升级。那七个月，过得也算充实，期间主要接触oracle，还有需求方来的各种苦逼的更改和要求，也开始以另一种角度看自己的代码。</p>
<p>之后，因为各种原因，实习生相继离开了。最后组里就剩三个人，记得有一次整层楼都我一个人，那种感觉。那段时间，一个人当几个使，要cover所有人的bug，要写程序搞定的上线初始化数据，那时候还不会脚本，对linux木有概念，用的是java+第三方开源jar包，现在那个系统用的数据批量导入还是java+poi用annotation搞定的通用数据处理。当晚上回到宿舍只有自己一个人，一天可能除了接几个电话处理问题，找不到任何人说话。这种状态，直到快过年前两天才结束。因为，我也选择了离开。</p>
<p>做出这个决定其实挺难的，当时经理找我谈过几次，问过我想做什么，其实，那时候出学校实习，七个月，没有仔细想过要做什么。真让我想想，要做什么？那时候才发现，自己也不知道。这个问题我思考了接近三个月，自己想做什么，究竟喜欢做的是什么？</p>
<p>结果，没想通。</p>
<p>呵呵，很多问题，那时候是搞不懂的，所谓历史局限性。</p>
<p>很多问题，现在也还不懂。</p>
<p>思考了很久，做出了决定。</p>
<p>没想出来的问题，接着想，但是，必须做出决定，做当下正确的事情，起码自己认为正确的事情。这是我的原则。</p>
<p>做了决定，却没有立刻走，做一件事情，善始善终，这也是我的原则。</p>
<p>一直cover掉所有东西，过年了，才向经理道别。</p>
<p>南下的飞机，离开的那时，我对自己说，京城，俺还会回来的。</p>
<p>然后，最后终是没有回去。</p>
<p>过年，一家人团聚，这年有大事，老姐嫁人了，呵呵。</p>
<p>日子一天天过去，那段时间，压力其实挺大的，因为校招在年前已经结束了，很多同学已然定下了，考研，考公务员，工作，等等。而自己，还未定。</p>
<p>所以，年后匆匆，再次北上，这次目的很明确，找工作。</p>
<p>到了北京，雪还未化去，而我，开始了为期一个月最精彩的日子，酸甜苦辣，尽在其中，压力山大，那段时间，经历很多，也学到很多。</p>
<p>面了很多公司，基本都拿到offer，却迟迟没有决定，因为一个问题，还是那个问题，你到底想做什么？</p>
<p>前半月，基本晚上投简历，白天出去笔试和面试，有的效率比较高，一路到底给结果，慢的也就隔几天，因为校招已经结束，这时候还是相当难找的，只能参加各个社招。有一次面一非常想去的公司，终面挂了，有一次去了才知道，要找有三年经验的，结果人事MM拉我简历过去了，败得一塌糊涂。</p>
<p>面试的好处是，搞定笔试后可以直接接触到面试官，再次感谢所有面过我的面试官，从中学到了很多东西，对某些事情的看法，对技术的观点，解决问题的思路，以及我自身的短板和不足。面试其实是一件很有意思的事情，你不知道对方是谁，会问什么问题，关注什么，同样，他也不知道你是何许人也，一个小时，两个小时谈下来，其实，可以看到自己很多看不到的东西。</p>
<p>下半月，前半月的顺利就此截止，拿到很多offer, 有的还好，没有要求立刻答复，有几个给了一个礼拜，有一个给了两天。期限在这时候到来，意味着我必须做出决定。选择什么？</p>
<p>很多时候，抉择是困难的，特别是难以抉择的时候……囧，有点绕</p>
<p>这时候，已然开学很久，某天收到华为短信，才发现跑我们学校招去了，问了下同学，基本都定下了，就剩我了……..这时候，方知何为压力。</p>
<p>对付压力的办法，睡觉！</p>
<p>对我来所蛮有效的方法，对付搞不定的事情，好好睡一觉，第二天满状态再去搞。</p>
<p>至于那个问题，我想做的是什么？起码不想做重复枯燥无意义的因为体制导致的无法优化的重复性劳动【这基本就是我实习的状态】</p>
<p>评估以后，去掉了一半，然后，想做什么？有活力，比较自由，可以通过自己代码看到效果的，不会因为体制原因导致重复劳动的，好吧，没有了。</p>
<p>思考了两天，当然，期限的最后一天。打电话，一个个回复了。把所有的都拒掉了。然后，重写简历，重新开始。</p>
<p>做自己认为正确的事情。</p>
<p>笔试，面试，奔走于北京各个角落，地铁，公交，记忆里那常常坐的390.额，面的倒数第二家公司，就是我现在在的，笔试只有三道题，迄今做题最少的一次，半小时，当时大致看了下休息区，椅子很舒服。面试的时候，一点半进去，四点半出来，三面，简历，问题，项目，写写画画，映像很深的是第一面时画了整整两页，第三面坐在二十五楼临窗，看下去很壮观。后来又参加了几个公司在学校里的校招，见识过管培生与技术同时笔试时五百比二十的场面，话说那次笔试题起码三个小时的量，给了一个半小时，涉及各种，答到手酸。</p>
<p>然后，事情还算顺利，顺利拿到offer，很多通知面试的也就没去了。没有选择去实习，因为经历了这几个月，分外珍惜学校的日子。</p>
<p>最后，拖着行李箱，关上了那扇门，南下回校。</p>
<p>既然选择了远方，便只顾风雨兼程。这是我初中英语老师送的一句话。</p>
<p>回学校的日子，仿佛却又是另一个世界，没有任何压力。那时候已然知道离别的日子将要到来，一天天数着日子。</p>
<p>泡图书馆，上自习，还有，就是敲代码。那时候宿舍哥们都去打篮球，打累了差不多到饭点了打个电话呼我下去，一起校外去吃饭。那时候会去跑步，跑了两次，发现坚持不了，也发现身体是该锻炼地。</p>
<p>最后，毕业季，散伙饭，然后，各奔东西。</p>
<p>送走一个个同学，然后，再被同学送走。</p>
<p>美好的时光总是短暂，怀念一起的日子，虽然平淡，但是，那种日子再也没有了。</p>
<p>然后，就是来到杭州，开始新的篇章。</p>
<p>话说六个月了，这篇章书写的，并不是十分满意吧。</p>
<p>本身，话比较少，额，周围人应该都能感受。性格使然，用别人对我的评价就是，性格偏冷。</p>
<p>性格偏内向，所以或许会给大家带来一些困扰，十分抱歉。</p>
<p>迄今六个月，linux从一点概念都木有，到现在稍微有点概念，摸到门槛，python，也是从无到有，好歹想做什么就写什么。对测试，也逐渐了解和深入，技术活，而且还挺难。</p>
<p>六个月，形成了一套自己的工具体系，例如dropbox，有道笔记,chrome及各种插件，桌面工具，开发工具等等，有了一整套，效率，提高很多。</p>
<p>六个月，写了很多脚本，根据事不过三的原则，对每一个项目测试进行一轮轮优化，力求达到最快速高效准确地搞定任务。唯一苦逼的是平时木有什么时间写脚本，周末偶尔写写，还得自己测，貌似组里就我一个用python,也就意味着写完就自个用。</p>
<p>六个月，看了很多文章，很多书，对很多东西有了新的认识，继续，视野需要拓展。</p>
<p>六个月，唯一遗憾就是放掉了java,用了三年多，记得时隔几个月再一次打开eclipse时，激动了一把，虽然现在拿来写python…….发现学完java,python基本不难接受。</p>
<p>六个月，还是没有很好地形成自己的做事风格，当然，学会承诺并兑现自己的承诺，承诺做过什么，一定尽全力做好。好，好的，额，ok，没问题，我会去….。这是我的承诺，我会做好。算是风格部分，需要更多的东西。</p>
<p>六个月，还是没有学会更好的沟通，这个需要加强的，清楚的表达意思。</p>
<p>六个月其实经历了很多事情，很多事，都需要去学习，经历过一次，便掌握之，不需要第二遍。第二遍就是轻车熟路了，这或许是这六个月做的还算不错的地方。</p>
<p>绝对不在同一个地方摔倒两次。</p>
<p>至于明年，额，淘宝上搞了张船票，O(∩_∩)O~。</p>
<p>额，对了，是今年了，规划，还在思考，需要继续深入学习很多东西，想写个工具库，自己用也好，把流程最优化，提高效率才是王道。想好了自己规划，一步步执行。</p>
<p>2011，难忘的一年，实习了，毕业了，工作了，写过很多代码，看过很多书，见过很多人，去过很多地方，做过很多事，想过很多问题，很多问题依旧没有想通。</p>
<p>还算充实。</p>
<p>我给自己大学四年的评价，有遗憾，无后悔。</p>
<p>我给自己2011年评价，无遗憾，无后悔，继续。</p>
<p>2012，继续努力</p>
<p>Be good，do right!</p>
<p>感谢所有人！</p>
<p>The end of summary</p>
<p>just the beginning of 2012.Happy new year.</p>
<p>2012-1-1 1:31 于杭州</p>
]]></content>
		</item>
		
		<item>
			<title>Python实现ftp常用操作[ftplib]</title>
			<link>https://wklken.me/posts/2011/12/10/python-ftp-ftplib.html</link>
			<pubDate>Sat, 10 Dec 2011 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2011/12/10/python-ftp-ftplib.html</guid>
			<description>使用到的模块 ftplib 代码托管位置 github-pytools 需求 快速进行ftp上传 ，下载，查询文件 原来直接在shell下操作：需要【连接，输用户名，输密码，单文件操作，存在</description>
			<content type="html"><![CDATA[<p>使用到的模块  <a href="http://docs.python.org/2/library/ftplib.html">ftplib</a></p>
<p>代码托管位置 <a href="https://github.com/wklken/pytools">github-pytools</a></p>
<h3 id="需求">需求</h3>
<p>快速进行ftp上传 ，下载，查询文件</p>
<p>原来直接在shell下操作：需要【连接，输用户名，输密码，单文件操作，存在超时限制】</p>
<p>太过于繁琐，容易操作失败</p>
<h3 id="改进">改进</h3>
<p>一句命令，搞定多文件上传，下载，查询，列表等操作</p>
<p>后期可以加入更强大的功能</p>
<h3 id="源代码">源代码</h3>
<pre><code>#!/usr/bin/python
# -*- coding:utf-8 -*-
#ftp.py
#    wklken@yeah.net
#this script is used to do some operations more convenient via ftp
  #1.[p]upload many files in the same time,show md5s
  #2.[g]download many files in the same time,show md5s
  #3.[l]list all the files on ftp site
  #4.[f]search a file on ftp site,return True or Flase
  #5.[h]show help info

#add upload and download operations  20111210 version0.1
#add md5sum after ops 20120308 version0.2

import sys,os,ftplib,socket

CONST_HOST = &quot;ip&quot;
CONST_USERNAME = &quot;username&quot;
CONST_PWD = &quot;pwd&quot;
CONST_BUFFER_SIZE = 8192

COLOR_NONE = &quot;\033[m&quot;
COLOR_GREEN = &quot;\033[01;32m&quot;
COLOR_RED = &quot;\033[01;31m&quot;
COLOR_YELLOW = &quot;\033[01;33m&quot;

def connect():
    try:
        ftp = ftplib.FTP(CONST_HOST)
        ftp.login(CONST_USERNAME,CONST_PWD)
        return ftp
    except socket.error,socket.gaierror:
        print(&quot;FTP is unavailable,please check the host,username and password!&quot;)
        sys.exit(0)

def disconnect(ftp):
    ftp.quit()

def upload(ftp, filepath):
    f = open(filepath, &quot;rb&quot;)
    file_name = os.path.split(filepath)[-1]
    try:
        ftp.storbinary('STOR %s'%file_name, f, CONST_BUFFER_SIZE)
    except ftplib.error_perm:
        return False
    return True

def download(ftp, filename):
    f = open(filename,&quot;wb&quot;).write
    try:
        ftp.retrbinary(&quot;RETR %s&quot;%filename, f, CONST_BUFFER_SIZE)
    except ftplib.error_perm:
        return False
    return True

def list(ftp):
    ftp.dir()

def find(ftp,filename):
    ftp_f_list = ftp.nlst()
    if filename in ftp_f_list:
        return True
    else:
        return False

def help():
    print(&quot;help info:&quot;)
    print(&quot;[./ftp.py l]\t show the file list of the ftp site &quot;)
    print(&quot;[./ftp.py f filenamA filenameB]\t check if the file is in the ftp site&quot;)
    print(&quot;[./ftp.py p filenameA filenameB]\t upload file into ftp site&quot;)
    print(&quot;[./ftp.py g filenameA filenameB]\t get file from ftp site&quot;)
    print(&quot;[./ftp.py h]\t show help info&quot;)
    print(&quot;other params are invalid&quot;)

def main():
    args = sys.argv[1:]
    if len(args) == 0:
        print(&quot;Params needed!&quot;)
        sys.exit(0)

    ftp = connect()

    success_list = []
    failed_list = []

    if args[0] == &quot;p&quot;:
        f_list = args[1:]

        for up_file in f_list:
        if not os.path.exists(up_file):
            print((&quot;UPLOAD: %s &quot;+COLOR_RED+&quot;FAILED&quot;+COLOR_NONE+&quot;  :file not exist&quot;)%up_file)
            continue
        elif not os.path.isfile(up_file):
            print((&quot;UPLOAD: %s &quot;+COLOR_RED+&quot;FAILED&quot;+COLOR_NONE+&quot;  :%s is not a file&quot;)%(up_file,up_file))
            continue

        if upload(ftp, up_file):
            success_list.append(up_file)
        else:
            failed_list.append(up_file)
        if len(success_list) &gt; 0 :
        print((COLOR_GREEN + &quot;UPLOAD SUCCESS: %s&quot; + COLOR_NONE)%(&quot; &quot;.join(success_list)))
        print(&quot;md5sum:&quot;)
        for f in success_list:
            print( os.popen(&quot;md5sum &quot; + f).read()[:-1])
        if len(failed_list) &gt; 0:
        print((COLOR_RED + &quot;UPLOAD FAILED: %s&quot; + COLOR_NONE)%(&quot; &quot;.join(failed_list)))

    elif args[0] == &quot;g&quot;:
        f_list = args[1:]

        for down_file in f_list:
        if not find(ftp,down_file):
            print((&quot;DOWNLOAD: %s &quot;+COLOR_RED+&quot;FAILED&quot;+COLOR_NONE+&quot;  :%s is not in the ftp site&quot;)%(down_file,down_file))
            continue

        if download(ftp, down_file):
            success_list.append(down_file)
        else:
            failed_list.append(down_file)
        if len(success_list) &gt; 0 :
        print((COLOR_GREEN + &quot;DOWNLOAD SUCCESS: %s&quot; + COLOR_NONE)%(&quot; &quot;.join(success_list)))
        print(&quot;md5sum:&quot;)
        for f in success_list:
            print( os.popen(&quot;md5sum &quot; + f).read()[:-1])
        if len(failed_list) &gt; 0:
        print((COLOR_RED + &quot;DOWNLOAD FAILED: %s&quot; + COLOR_NONE)%(&quot; &quot;.join(failed_list)))

    elif args[0] == &quot;l&quot;:
        list(ftp)
    elif args[0] == &quot;f&quot;:
        f_list = args[1:]
        for f_file in f_list:
        if find(ftp,f_file):
            print((&quot;SEARCH: %s &quot;+COLOR_GREEN+&quot;EXIST&quot;+COLOR_NONE)%f_file)
        else:
            print((&quot;SEARCH: %s &quot;+COLOR_RED+&quot;NOT EXIST&quot;+COLOR_NONE)%f_file)
            if len(f_file) &gt; 3:
            print(&quot;Similar File List:&quot;)
            s = ftp.nlst()
            print  &quot;, &quot;.join([k for k in s if f_file in k])

    elif args[0] == &quot;h&quot;:
        help()
    else:
        print(&quot;args are invalid!&quot;)
        help()

    disconnect(ftp)

if __name__ == &quot;__main__&quot;:
  main()
</code></pre>
<h3 id="常用函数">常用函数</h3>
<p>用手册查看，以下只是简略，因为没用用到，[待整理]：</p>
<pre><code>login(user='',passwd='', acct='')     登录到FTP 服务器，所有的参数都是可选的
pwd()                       当前工作目录
cwd(path)                   把当前工作目录设置为path
dir([path[,...[,cb]])       显示path 目录里的内容，可选的参数cb 是一个回调函数，会被传给retrlines()方法
nlst([path[,...])           与dir()类似，但返回一个文件名的列表，而不是显示这些文件名
retrlines(cmd [, cb])       给定FTP 命令（如“RETR filename”），用于下载文本文件。可选的回调函数cb 用于处理文件的每一行
retrbinary(cmd, cb[,bs=8192[, ra]])     与retrlines()类似，只是这个指令处理二进制文件。回调函数cb 用于处理每一块（块大小默认为8K）下载的数据。
storlines(cmd, f)   给定FTP 命令（如“STOR filename”），以上传文本文件。要给定一个文件对象f
storbinary(cmd, f[,bs=8192])    与storlines()类似，只是这个指令处理二进制文件。要给定一个文件对象f，上传块大小bs 默认为8Kbs=8192])
rename(old, new)    把远程文件old 改名为new
delete(path)     删除位于path 的远程文件
mkd(directory)  创建远程目录
</code></pre>
]]></content>
		</item>
		
		<item>
			<title>Python通用数据格式转换工具</title>
			<link>https://wklken.me/posts/2011/12/10/python-dataformat.html</link>
			<pubDate>Sat, 10 Dec 2011 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2011/12/10/python-dataformat.html</guid>
			<description>已独立成项目在github上面 dataformat 涉及模块 os, getopt, sys 需求 在进行hadoop测试时，需要造大量数据，例如某个表存在56列，但实际程序逻辑只适用到某几</description>
			<content type="html"><![CDATA[<hr>
<p>已独立成项目在github上面 <a href="https://github.com/wklken/dataformat">dataformat</a></p>
<hr>
<p>涉及模块 os, getopt, sys</p>
<h3 id="需求">需求</h3>
<p>在进行hadoop测试时，需要造大量数据，例如某个表存在56列，但实际程序逻辑只适用到某几列，我们造的数据 也只需要某几列</p>
<p>构造几列数据，转化为对应数据表格式</p>
<h3 id="源代码">源代码</h3>
<pre><code>#!/usr/bin/env python
# -*- coding: utf-8 -*-
#dataformat.py
#   wklken@yeah.net
#this script change data from your source to the dest data format
#2011-08-05 created version0.1
#2011-10-29 add row-row mapping ,default row value .rebuild all functions. version0.2 
#next:add data auto generate by re expression
#2011-12-17 add new functions, add timestamp creator.  version0.3
#2012-03-08 rebuild functions. version0.4
#2012-06-22 add function to support multi output separators
#2012-07-11 fix bug  line 44,add if
#2012-09-03 rebuild functions,add help msg! version0.5
#2012-11-08 last version edited by lingyue.wkl
#           this py: https://github.com/wklken/pytools/blob/master/data_process/dataformat.py

import os
import sys
import getopt
import time
import re

#read file and get each line without \n
def read_file(path):
    f = open(path, &quot;r&quot;)
    lines = f.readlines()
    f.close()
    return [line[:-1] for line in lines ]

#处理一行，转为目标格式，返回目标行
def one_line_proc(parts, total, ft_map, outsp, empty_fill, fill_with_sno):
    outline = []
    #step1.获取每一列的值
    for i in range(1, total + 1):
        if i in ft_map:
            fill_index = ft_map[i]
            #加入使用默认值列  若是以d开头，后面是默认，否则取文件对应列 done
            if fill_index.startswith(&quot;d&quot;):
                #列默认值暂不开启时间戳处理
                outline.append(fill_index[1:])
            else:
                outline.append(handler_specal_part(parts[int(fill_index) - 1]))
        else:
            #-s 选项生效，填充列号
            if fill_with_sno:
                outline.append(str(i))
            #否则，填充默认填充值
            else:
                outline.append(empty_fill)

    #step2.组装加入输出分隔符，支持多分隔符
    default_outsp = outsp.get(0,&quot;\t&quot;)
    result = []
    outsize = len(outline)
    for i in range(outsize):
        result.append(outline[i])
        if i &lt; outsize - 1:
            result.append(outsp.get(i + 1, default_outsp))
    #step3.拼成一行返回
    return ''.join(result)

#处理入口，读文件，循环处理每一行，写出
#输入数据分隔符默认\t,输出数据默认分隔符\t
def process(inpath, total, to, outpath, insp, outsp, empty_fill, fill_with_sno, error_line_out):
    ft_map = {}
    #有效输入字段数（去除默认值后的）
    in_count = 0
    used_row = []
    #step1-3相当于数据预处理，解析传入选项

    #step1 处理映射列 不能和第二步合并
    for to_row in to:
        if r&quot;\:&quot; not in to_row and len(to_row.split(&quot;:&quot;)) == 2:
            used_row.append(int(to_row.split(&quot;:&quot;)[1]))
        if r&quot;\=&quot; not in str(to_row) and len(str(to_row).split(&quot;=&quot;)) == 2:
            pass
        else:
            in_count += 1

    #step2 处理默认值列
    for to_row in to:
        #处理默认值列
        if r&quot;\=&quot; not in str(to_row) and len(str(to_row).split(&quot;=&quot;)) == 2:
            ft_map.update({int(to_row.split(&quot;=&quot;)[0]): &quot;d&quot;+to_row.split(&quot;=&quot;)[1]})
            continue
        #处理列列映射
        elif r&quot;\:&quot; not in to_row and len(to_row.split(&quot;:&quot;)) == 2:
            ft_map.update({int(to_row.split(&quot;:&quot;)[0]): to_row.split(&quot;:&quot;)[1]})
            continue
        #其他普通列
        else:
            to_index = 0
            for i in range(1, total + 1):
                if i not in used_row:
                    to_index = i
                    break
            ft_map.update({int(to_row): str(to_index)})
            used_row.append(to_index)

    #setp3 处理输出分隔符   outsp  0=\t,1=    0代表默认的，其他前面带列号的代表指定的
    if len(outsp) &gt; 1 and len(outsp.split(&quot;,&quot;)) &gt; 1:
        outsps = re.findall(r&quot;\d=.+?&quot;, outsp)
        outsp = {}
        for outsp_kv in  outsps:
            k,v = outsp_kv.split(&quot;=&quot;)
            outsp.update({int(k): v})
    else:
        outsp = {0: outsp}

    #step4 开始处理每一行
    lines = read_file(inpath)
    f = open(outpath, &quot;w&quot;)
    result = []
    for line in lines:
        #多个输入分隔符情况，使用正则切分成列
        if len(insp.split(&quot;|&quot;)) &gt; 0:
            parts = re.split(insp, line)
        #否则使用正常字符串切分成列
        else:
            parts = line.split(insp)

        #正常的，切分后字段数大于等于配置的选项个数
        if len(parts) &gt;= in_count:
            outline = one_line_proc(parts, total, ft_map, outsp, empty_fill, fill_with_sno)
            result.append(outline + &quot;\n&quot;)
        #不正常的，列数少于配置
        else:
            #若配置了-e 输出，否则列数不符的记录过滤
            if error_line_out:
                result.append(line + &quot;\n&quot;)

    #step5 输出结果
    f.writelines(result)
    f.close()

#特殊的处理入口，处理维度为每一行,目前只有时间处理
def handler_specal_part(part_str):
    #timestamp 时间处理
    #时间列，默认必须 TS数字=时间
    if part_str.startswith(&quot;TS&quot;) and &quot;=&quot; in part_str:
        ts_format = {8: &quot;%Y%m%d&quot;,
                     10: &quot;%Y-%m-%d&quot;,
                     14: &quot;%Y%m%d%H%M%S&quot;,
                     19: &quot;%Y-%m-%d %H:%M:%S&quot;}
        to_l = 0
        #step1 确认输出的格式 TS8 TS10 TS14 TS19
        if part_str[2] != &quot;=&quot;:
            to_l = int(part_str[2:part_str.index(&quot;=&quot;)])

        part_str = part_str.split(&quot;=&quot;)[1].strip()
        interval = 0
        #step2 存在时间+-的情况 确认加减区间
        if &quot;+&quot; in part_str:
            inputdate = part_str.split(&quot;+&quot;)[0].strip()
            interval = int(part_str.split(&quot;+&quot;)[1].strip())
        elif &quot;-&quot; in part_str:
            parts = part_str.split(&quot;-&quot;)
            if len(parts) == 2: #20101020 - XX
                inputdate = parts[0].strip()
                interval = -int(parts[1].strip())
            elif len(parts) == 3: #2010-10-20
                inputdate = part_str
            elif len(parts) == 4: #2010-10-20 - XX
                inputdate = &quot;-&quot;.join(parts[:-1])
                interval = -int(parts[-1])
            else:
                inputdate = part_str
        else:
            inputdate = part_str.strip()
        #step3 将原始时间转为目标时间
        part_str = get_timestamp(inputdate, ts_format, interval)

        #step4 如果定义了输出格式，转换成目标格式，返回
        if to_l &gt; 0:
            part_str = time.strftime(ts_format.get(to_l), time.localtime(int(part_str)))
    return part_str

#将时间由秒转化为目标格式
def get_timestamp(inputdate, ts_format, interval=0):
    if &quot;now()&quot; in inputdate:
        inputdate = time.strftime(&quot;%Y%m%d%H%M%S&quot;) 
    inputdate = inputdate.strip()
    try:
        size = len(inputdate)
        if size in ts_format:
            ts = time.strptime(inputdate, ts_format.get(size))
        else:
            print &quot;the input date and time expression error,only allow 'YYYYmmdd[HHMMSS]' or 'YYYY-MM-DD HH:MM:SS'  &quot;
            sys.exit(0)
    except:
        print &quot;the input date and time expression error,only allow 'YYYYmmdd[HHMMSS]' or 'YYYY-MM-DD HH:MM:SS'  &quot;
        sys.exit(0)
    return str(int(time.mktime(ts)) + interval)

#打印帮助信息
def help_msg():
    print(&quot;功能：原数据文件转为目标数据格式&quot;)
    print(&quot;选项:&quot;)
    print(&quot;\t -i inputfilepath  [必输，input, 原文件路径]&quot;)
    print(&quot;\t -t n              [必输，total, n为数字，目标数据总的域个数]&quot;)
    print(&quot;\t -a '1,3,4'        [必输，array, 域编号字符串，逗号分隔。指定域用原数据字段填充，未指定用'0'填充]&quot;)
    print(&quot;\t                          -a '3,5=abc,6:2'  第5列默认值abc填充,第6列使用输入的第1列填充，第3列使用输入第1列填充&quot;)
    print(&quot;\t -o outputfilepath [可选，output, 默认为 inputfilepath.dist ]&quot;)
    print(&quot;\t -F 'FS'           [可选，field Sep，原文件域分隔符，默认为\\t,支持多分隔符，eg.'\t||\|' ]&quot;)
    print(&quot;\t -P 'OFS'          [可选，out FS，输出文件的域分隔符，默认为\\t,可指定多个，多个需指定序号=分隔符,逗号分隔,默认分隔符序号0 ]&quot;)
    print(&quot;\t -f 'fill_str'     [可选，fill，未选列的填充值，默认为空 ]&quot;)
    print(&quot;\t -s                [可选，serial number,当配置时，-f无效，使用列号填充未指派的列]&quot;)
    print(&quot;\t -e                [可选，error, 源文件列切分不一致行/空行/注释等，会被直接输出，正确行按原逻辑处理]&quot;)
    sys.exit(0)

#判断某个参数必须被定义
def must_be_defined(param, map, error_info):
    if param not in map:
       print error_info
       sys.exit(1)

#程序入口，读入参数，执行
def main():
    #init default value
    insp = &quot;\t&quot;
    outsp = &quot;\t&quot;
    empty_fill = ''
    fill_with_sno = False
    error_line_out = False
    #handle options
    try:
        opts,args = getopt.getopt(sys.argv[1:],&quot;F:P:t🅰️i⭕f:hse&quot;)

        for op,value in opts:
          if op in (&quot;-h&quot;, &quot;-H&quot;, &quot;--help&quot;):
            help_msg()
          if op == &quot;-i&quot;:
            inpath = value
          elif op == &quot;-o&quot;:
            outpath = value
          elif op == &quot;-t&quot;:
            total = int(value)
          elif op == &quot;-a&quot;:
            to = value.split(&quot;,&quot;)
          elif op == &quot;-F&quot;:
            insp = value.decode(&quot;string_escape&quot;)
          elif op == &quot;-P&quot;:
            outsp = value.decode(&quot;string_escape&quot;)
          elif op == &quot;-f&quot;:
            empty_fill = value
          elif op == &quot;-s&quot;:
            fill_with_sno = True
          elif op == &quot;-e&quot;:
            error_line_out = True
        if len(opts) &lt; 3:
          print(sys.argv[0]+&quot; : the amount of params must great equal than 3&quot;)
          print(&quot;Command : ./dataformat.py -h&quot;)
          sys.exit(1)

    except getopt.GetoptError:
        print(sys.argv[0]+&quot; : params are not defined well!&quot;)
        print(&quot;Command : ./dataformat.py -h&quot;)
        sys.exit(1)

    params_map = dir()

    must_be_defined('inpath', params_map, sys.argv[0]+&quot; : -i param is needed,input file path must define!&quot;)
    must_be_defined('total', params_map, sys.argv[0]+&quot; : -t param is needed,the fields of result file must define!&quot;)
    must_be_defined('to', params_map, sys.argv[0]+&quot; : -a param is needed,must assign the field to put !&quot;)

    if not os.path.exists(inpath):
        print(sys.argv[0]+&quot; file : %s is not exists&quot;%inpath)
        sys.exit(1)

    if 'outpath' not in dir():
        outpath = inpath+&quot;.dist&quot;

    process(inpath, total, to, outpath, insp, outsp, empty_fill, fill_with_sno, error_line_out)

if __name__ ==&quot;__main__&quot;:
    main()
</code></pre>
<h3 id="使用说明">使用说明</h3>
<p>功能：可指定输入分隔，输出分隔，无配置字段填充，某列默认值,可按顺序填充，也可乱序映射填充</p>
<p>输入：输入文件路径</p>
<p>选项：</p>
<pre><code>-i “path”
必设
输入文件路径

-t n
必设
目标数据表总列数

-a “r1,r2”
必设
将要填充的列号列表，可配置默认值，可配置映射

-o “path”
可选
输出文件路径，默认为 输入文件路径.dist

-F “IFS”
可选
输入文件中字段域分隔符，默认\t

-P ”OFS”
可选
输出文件中字段域分隔符，默认\t

-f “”
可选
指定未配置列的填充内容，默认为空

-h
单独
查看帮助信息
</code></pre>
<p>列填充的配置示例：</p>
<p>普通用法【最常用】</p>
<p>命令：</p>
<pre><code>./dataformat.py –i in_file –t 65 -a “22,39,63” –F “^I” –P “^A” –f “0”
</code></pre>
<p>说明：</p>
<pre><code>in_file中字段是以\t分隔的[可不配-F,使用默认]。
将in_file的第1,2,3列分别填充到in_file.dist[use default]的第22,39,63列
in_file.dist共65列，以^A分隔，未配置列以0填充
-a中顺序与源文件列序有关，若-a “39,22,63” 则是将第1列填充到第39列，第二列填充到22列，第3列填充到63列
</code></pre>
<p>列默认值用法:【需要对某些列填充相同的值，但不想在源文件中维护】</p>
<p>命令:</p>
<pre><code>./dataformat.py -i in_file –t 30 –a “3=tag_1,9,7,12=0.0” –o out_file
</code></pre>
<p>说明:</p>
<pre><code>in_file以\t分隔，输出out_file以\t分隔
将in_file的第1列,第2列填充到out_file的第9列，第7列
out_file共30列，第3列均用字符串”tag_1”填充，第12列用0.0填充，其他未配置列为空
注意：默认值 的取值，若是使用到等号和冒号，需转义，加 \= \:
</code></pre>
<p>列列乱序映射：</p>
<p>命令:</p>
<pre><code>./dataformat.py –i in_file –t 56 –a “3:2,9,5:3,1=abc,11”
</code></pre>
<p>说明:</p>
<pre><code>分隔，输入，输出，同上…..
冒号前面为输出文件列号，后面为输入文件列号
目标文件第3列用输入文件第2列填充，目标文件第5列用输入文件第3列填充
目标文件第一列均填充“abc”
目标文件第9列用输入文件第1列填充，第11列用输入文件第4列填充【未配置映射，使用从头开始还没有被用过的列】
脚本会对简单的字段数量等映射逻辑进行检测，复杂最好全配上，使用默认太抽象
</code></pre>
<p>代码托管位置  <a href="https://github.com/wklken/pytools/blob/master/data_process/dataformat.py">链接</a></p>
]]></content>
		</item>
		
		<item>
			<title>虽有遗憾，不曾后悔——写在离校之前</title>
			<link>https://wklken.me/posts/2011/06/18/summary-01-leaveschool.html</link>
			<pubDate>Sat, 18 Jun 2011 08:00:00 +0800</pubDate>
			
			<guid>https://wklken.me/posts/2011/06/18/summary-01-leaveschool.html</guid>
			<description>即将离开学校了，突然想写点什么，既然是CSDN，就写写这四年是咋过的吧。 高考一分之差，错过了自己报的本省大学，机缘巧合，补录到了离家两千多公</description>
			<content type="html"><![CDATA[<p>即将离开学校了，突然想写点什么，既然是CSDN，就写写这四年是咋过的吧。</p>
<p>高考一分之差，错过了自己报的本省大学，机缘巧合，补录到了离家两千多公里外的这个大学。</p>
<p>大一，只身北上，两千五百多公里，二十七个钟头火车，来到了学校。</p>
<p>话说那时候挺猛，一个人杀到学校注册，结果到的时候发现就自己是一个人来的，囧。</p>
<p>注册，军训，然后开始了正儿八经的大学生活。</p>
<p>大一，中规中矩，大一教的编程语言是Java，其实一直到现在对这个一直抱有怨念，坑爹的应该教C才对。虽然简单易学，但是毕竟属于比较非底层的语言。但毕竟有自己的优点，也就是那时候，开始使用Java。</p>
<p>记得那时候的实验课，是感觉最具有挑战性的，题目都是实验老师临时出的，一晚上一个，做出来交上就完事，呵呵，记得写print第一个画图形的程序花了两个多钟头，还记得另一个写日历程序，那时候压根还不知道Java自带的日历，坑爹的拿纸整整演算了两大页，用算法愣是搞出来了。呵呵，回头看，似乎没啥，但是人总是存在一个成长的过程，经历了。</p>
<p>晕晕乎乎，把Java学完了，那时候做习题，以及考试，都是用手写代码，总算是培养了个好习惯，手写最高境界是一口气写完，木有涂改，然后肯定地说：这段代码木有问题！呵呵，后来和同学合作写东西，压根没有测试环境，只能说说实现，然后各自去写，凭“臆想”去规划书写代码，然后最后整合起来再进行测试。</p>
<p>大一的高等数学，以及后来的概率统计，数值计算，离散数学，线性代数等等数学课程，刚开始可能觉得没啥，写个程序不会用到多少，但是由于大一自己不知道学了有没有用，索性就好好学，所以基础还是不错的，其实，数学学好，无形中思维上，以及算法，代码等等都有很强的提高。所以，或许暂时用不上，但是好好学，多学点东西总是没错的。</p>
<p>大一下学期课程设计，写的是聊天工具，话说上学期上完基本还没什么概念，有点赶鸭子上架的味道，但是有压力才是有动力，你会发现自己潜力还是不错的。O(∩_∩)O~。下学期基本都在这敲敲打打中度过，学会了String的所有操作，大概熟悉了集合框架，多线程，图形界面，还有GUI的设计。话说最重要的是学到了怎么去查找信息，应用到问题的解决中。</p>
<p>大一下学期最后到大二上学期期间，感觉应该可以做更多的事情了，然后开始学JSP。刚开始一个月，完全不知道这玩意儿到底是个神马东西，两本书，快一个月，一堆大大的笔记，全是代码。后来悟了，开始敲代码，发现Eclipse不好用，就用上了MyEclipse，呵呵，大三又换回了Eclipse。敲代码实现，记得第一个比较完善的程序是一个论坛，页面超级简陋的论坛，实现主体的所有功能，刚开始全是JSP页面，就是&lt;%%&gt;加一堆代码的那种，后来看到JavaBean，重构了一次，发现代码量骤减，后来看了EL，再次重构，又少了一大堆，再看到JSP标签部分，再次重构，&lt;%%&gt;已经很少了，后来JSTL，再次重构，彻底清理了&lt;%%&gt;。感觉学习是一个循序渐进的过程，而写代码，实在不断重构中实现的进步。话说那时候经常性死磕，中文乱码死磕了一个礼拜才搞出来，但是虽说苦了点，后来这个问题就不是问题了，还是值得的。</p>
<p>大一下，五一的时候，同学拿了个彩铃的网站过来，说是从老师那拿到的项目，那时候一般般，本想拒了，但是同学接了却找不到人做，只能自己试试了，记得那时候刚看了点MVC，就用JSP+JavaBean+Servlet实现了，这期间大概一个多月不断写不断测和修改，又用了一个多月维护。主要用到socket编程，js，多线程，回头看看，代码十分简单，呵呵。貌似这两天查了下，网站还在线上，点了下，貌似还有几个BUG，囧，那时候拿到了四百块钱，这是第一次用代码换钱，请舍友到饭店改善了下伙食，买了两本书，基本没了。</p>
<p>大二，开始学数据结构，用的是C++，大一学Java，让我怨念的就是这个。每一章都有实验课，这时候只能由模仿开始，学习C++，学习数据结构，相当于学了两样东西。我一直认为数据结构，算法和语言三者是最最基础的东西，所以相当上心，这时候实验课就没啥挑战了，基本能前几个交了，秒杀。大二下的数据结构课程设计，写的是计算器和Huffman文本压缩，计算器没有用传统的栈解决，用了递归，解析字符串，直接出结果。Huffman用C++，花了挺长时间的，貌似还被命中抽到上台将PPT，想起自己上台讲解的样子，挺搞。</p>
<p>大二暑假，和同学合作参加了软件设计大赛，写一个局域网资源搜索引擎。负责XML处理和搜索部分。整整两个月，相当忙碌，也挺充实，早上自然醒，也不会太晚，醒了就开始写代码，然后午饭，下午继续写，傍晚夹本书，穿着大裤头，买个冷饮，晃晃悠悠晃到没有几个人的自习室【主要是有空调，宿舍电扇不给力呀】，边想着问题，看看书，写写解决方案，感觉差不多了再晃悠回宿舍，和同写的那个哥们讨论下，然后继续敲代码，敲到想睡为止，基本都是凌晨，貌似有一回快天亮才睡。想想那时候，太有激情了。充实，自由自在，没有烦恼。一直很佩服合作的那个哥们，逻辑性比我强了不是一点半点。那时候不知道有SVN这玩意儿，写代码接口啥的都是讨论下，他写他的，我写我的，然后最后再测，经常是“臆想”着就敲完了。那时候印象最深的是一个问题，搞不定，就一直想，查东西，吃饭时也在考虑，睡觉时还在考虑，然后第二天早上醒过来突然就想通了，这种感觉真奇妙。那段时间感觉自己编码能力提高很大吧，量变引起质变，写代码写多了，自然而然感觉“境界”提升了，思考的方式，看问题的角度，解决问题的方法都有很大的提高。暑假快结束，也是大赛快结束，写完代码才发现木有文档，坑爹的，花了三天草草弄了文档。问题就出在这，大赛只拿了个三等奖，后来补齐文档，重新整理，把项目拿到学院科技创新评比，也拿了三等，汗倒，最后拿了四百，项目组五个人，其实就我两干活。</p>
<p>后来，队长进了银行，另一个去了北邮读研，和我合作的哥们去了中科院，我，工作，快了，过几天南下杭州。逝去的青春，牢记于心。</p>
<p>大二大三是所有写程序最多的，多个课程设计同时上，忙呀。期间写了个学生选课系统【软院的绝对传统】，写了个基于Struts2的班级网站，然后是SSH的网上商城，写了个大的教务监控管理系统，一个lucene搜索，中间选修数据仓库和图形学，写了个算法模拟器，一个opengl迷宫。循序渐进，虽然有提高，但是感觉只是会用，而不会写。知其然而不知其所以然。不过，总是有历史局限性的嘛，那时候，还没有意识到那么多，只是不断地去学怎么用，而不会去思考为什么。</p>
<p>大三下学期，面临着考研，考公务员还有找工作的抉择，家里的意思是让我自己选择，最终还是决定工作，从农村出来，一路走到这，想想，该承担一些责任了。有些东西，还不能享受，有些责任，已要承担。</p>
<p>那时候有公司来招聘实习生，在不知情的情况下，和同学去笔试面试，呵呵，貌似当时来得挺多，开始了人生第一次笔试面试。忙碌了一段时间，定了下去向，决定到北京闯一闯。那段时间近一个多月，一直在学c,因为听说金融系统用的比较多，后来才发现没用上，但是毕竟学了。</p>
<p>大三选修了现代软件开发和面向对象，还有几门偏向实用性质的选修课程。这时候开始接触“设计”，分层，设计模式，接口等等。</p>
<p>大三已结束，立马赶到北京实习了，兴冲冲到了才发现，住的是六人一屋子的半地下室，想想，该锻炼下，也就忍了。刚开始没分项目组，一堆人9个全住一块，去拉网，去买厨具等等，都是学生，都很穷，但是日子还过得去。第一个月培训，木有工资，只管午饭。一个月培训主要是Java和J2ee的东西，天天上课，一天近七小时，那段时间，感觉只是对Java整体的一个查漏补缺，进步是有，但是不是很多。培训老师干了近十年，经验应该算是相当丰富，讲的内容大部分是快速应用，基本木有涉及原理，所以差不多是快餐，也算还行。印象深刻的是刚去那会，五点半起床，倒车，从北四环外到南三环，三趟公交车，直接挤死。八点半左右勉强到了，吃个早饭，然后上一整天课，后来才发现可以只倒两趟的，只是多走一站地，那时候上了车开始迷迷糊糊睡觉。</p>
<p>培训完之后的一个月，算是比较轻松的吧，刚进项目组，也没什么事情，发现就我一个分去做Java了，其他人去做C，我一个人呆在五楼的一个项目组，项目后期运维，也没我什么事，另外一些人在一楼，天天做C的一些练习题。我可能性格使然，算是比较悲剧的，不怎么好过。因为刚开始不熟，自己话又比较少，而项目运维本身就没什么事让我干，老员工电话来了处理下问题，基本不会和我有什么交集，天天坐着，经理一天可能都不会跟我说句话【性格内向的人伤不起呀】，没我什么事，虽然交代下来的事情基本都能按时搞定，但是感觉到孤独呀。事实上经理人很不错，呵呵，那时候主要还是自己的问题。既然没事干，便开始看代码，从头开始，new了一个工程，仿照着开始搭项目框架，看文档，跟踪代码，画结构图，写写文档等等，快一个月，勉强搭出一个半成品。然后，就被另一个项目组借走了。</p>
<p>第一个月实习公司，九百大洋。</p>
<p>新的项目组是一个新的项目，写了一个多月文档，然后开始编码和测试，加了近四个月班，天天，一二四加班，三五六不加，但是正常上班，那时候只有周日，但基本用来睡觉了。有时候晚的时候加班到凌晨一两点。因为实习生工资是按工时算的，所以加班最多一个月拿到了一千六百五，哎。其实实习生干的活绝对和正式员工差不多，一个小组，两个正式员工，带着六个实习生。那时候加班直接加郁闷了，主要是累，回去就睡觉，还有就是太没营养，天天重复性劳动。我还算好，因为有那个月看代码，负责组件扩展，重构，公共代码部分，当然还有自己负责模块，工作量是两倍吧，但是主要是交替着来不会觉得枯燥，其他人基本重复着写东西，相当枯燥。</p>
<p>最后在决定去留的时候还是选择离开，同去的实习生都走了，我一直坚持加班加到大年二十八，第二个省上完线才撤的，那时候整个五楼就我一个人，偶尔处理下bug，也没什么事，住的地方就我一个人了，感觉，挺凄凉。那段时间想的最多的是自己到底想要的是什么，是什么样的工作？最终还是没有想通，但是绝对不是目前的工作。所以决定离开。</p>
<p>经理和组长对我还是十分不错的，走的时候谈话了，一下子加了一千五工资，让我留下好好干，后来还是决定离开，前阵听说经理和组长都升了一级。</p>
<p>大四下，正月十几，我就又杀到北京了，目标很明确，找个工作，找个自己想要的工作。</p>
<p>这期间整整一个月，到最后自己接受offer，一个月的时间，这辈子，永远忘不了。苦过，累过，烦躁过，痛苦过。在偌大的北京，跑来跑去，笔试面试，这一个月的经历，感觉比过去一整年的经历还多，最后逼到绝路，拒了所有offer，然后重头来过，重新开始找，逼着自己，要么找到自己想要的，要么滚蛋。呵呵，最后三面一直通过，找到了目前的工作，算是比较想做的一个工作。记得某天傍晚，看到宣讲通知，没吃晚饭便杀过去了，两个多小时车程，笔完，回来，一个人在寒风中等车，可谓饥寒交迫。还有一次，早上出发两钟头去笔试，笔完说过两天通知面试，坑爹的刚回来还么吃饭呢让我回去面试，草草吃了饭又杀回去面试，那天，花了八个钟头在车上。那段时间，瘦了二十来斤吧。</p>
<p>到北京半年多，却没有去过一个地方游玩，基本都在加班，睡觉的循环中，找工作这一个月，压力太大，也没有出去，天天忙碌，早出晚归，基本见不到什么人，以至于刚面完那天下午，坐地铁半路下了车，到天安门广场沾沾人气，记得安检查我包的时候，一个笔，一瓶水，一个笔记本，一本书，那天等了两个多小时看完降旗，才慢慢悠悠晃回去。</p>
<p>后来的后来，找到了工作，回了学校，却一直没有去想，没有去提这段时间。可能是回避吧，想喘口气，往昔不堪回首，这段时间，铭记了，但是却不轻易去想。所以回到学校，倍加珍惜学校的生活，因为，这样的日子不多了。慢悠悠晃去吃饭，偶尔上个自习，图书馆，散散步，跟同学胡吹海聊，这样的日子，好好珍惜，外面社会压力太大，这样的日子，不多了。</p>
<p>现在是凌晨2:21，距离离校还有九天，想想还是写些东西，记录下吧。虽然自认为记忆力还行，几年内发生的事情基本都全部清楚地记录，但是，十几年后呢，几十年后呢。呵呵，想得远了。</p>
<p>这四年，基本算是充实，也算是丰富吧，木有参加社团什么的，木有谈恋爱，这只能算是遗憾吧。大一大二天天上自习，大二大三疯狂代码，大四找工作，大学，结束了啊。</p>
<p>虽有遗憾，不曾后悔。</p>
<p>再看看未来吧，宿舍六人，一国家公务员，两省公务员，一保研，还有我和另一个哥们找工作，一个深圳一个杭州，从此天南地北，各奔东西了。</p>
<p>未来，不敢说太远，今后五到十年，好好努力吧，好好奋斗</p>
]]></content>
		</item>
		
	</channel>
</rss>
