<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://kioipp.com</id>
    <title>自留地</title>
    <updated>2021-03-29T11:42:45.635Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://kioipp.com"/>
    <link rel="self" href="https://kioipp.com/atom.xml"/>
    <subtitle>梦里不知身是客，一晌贪欢。</subtitle>
    <logo>https://kioipp.com/images/avatar.png</logo>
    <icon>https://kioipp.com/favicon.ico</icon>
    <rights>All rights reserved 2021, 自留地</rights>
    <entry>
        <title type="html"><![CDATA[浅谈 Ansible Playbook 任务执行顺序组织]]></title>
        <id>https://kioipp.com/post/du-dong-ansible-playbook-ren-wu-zhi-xing-shun-xu/</id>
        <link href="https://kioipp.com/post/du-dong-ansible-playbook-ren-wu-zhi-xing-shun-xu/">
        </link>
        <updated>2021-03-29T06:32:57.000Z</updated>
        <content type="html"><![CDATA[<p>根据 strategy 和 serial 及 forks 数测试 Playbook 实际执行顺序。</p>
<h1 id="基本概念">基本概念</h1>
<p>官方文档参考：</p>
<blockquote>
<p>https://docs.ansible.com/ansible/latest/user_guide/playbooks_strategies.html</p>
</blockquote>
<h2 id="strategy">strategy</h2>
<p>单个 play 级的调度策略，定义执行实例是否有步骤锁，即单个 task 是否等待其他实例执行完成。具体有两种方式：</p>
<ul>
<li>linear</li>
</ul>
<blockquote>
<p>Task execution is in lockstep per host batch as defined by serial (default all). Up to the fork limit of hosts will execute each task at the same time and then the next series of hosts until the batch is done, before going on to the next task.</p>
</blockquote>
<p>默认值。在一个实例分组(serial)内，开始执行下一个任务需要等待<code>所有</code>实例上一个任务执行完成。</p>
<ul>
<li>free</li>
</ul>
<blockquote>
<p>Task execution is as fast as possible per batch as defined by serial (default all). Ansible will not wait for other hosts to finish the current task before queuing more tasks for other hosts. All hosts are still attempted for the current task, but it prevents blocking new tasks for hosts that have already finished.<br>
With the free strategy, unlike the default linear strategy, a host that is slow or stuck on a specific task won’t hold up the rest of the hosts and tasks.</p>
</blockquote>
<p>非默认值。在一个实例分组(serial)内，开始执行下一个任务需要两个条件：</p>
<ul>
<li><code>本实例</code>上一个任务执行完成，</li>
<li>下一个任务的并发数(forks)有冗余。</li>
</ul>
<h2 id="forks">forks</h2>
<blockquote>
<p>Maximum number of simultaneous connections Ansible made on each Task.</p>
</blockquote>
<p>并发数。在单个分组和单个任务中，同时执行任务的最大实例数。</p>
<h2 id="serial">serial</h2>
<p>默认值为 <code>all</code> 。定义单轮执行实例的数量。当一轮实例<code>全部</code>执行完成后，下一轮的实例才会执行。</p>
<h1 id="举例说明">举例说明</h1>
<p>定义5个实例数和2个任务，每个任务耗时5s:</p>
<ul>
<li>Node: C1\C2\C3\C4\C5</li>
<li>Task:T1(5s), T2(5s)</li>
</ul>
<p>先看看不同 <code>strategy</code> 场景：</p>
<p><em><strong>场景1</strong>：strategy=linear, forks=5, (serial=all)</em></p>
<p>T1: C1-C2-C3-C4-C5 （5s）<br>
T2: C1-C2-C3-C4-C5 （5s）</p>
<p>总耗时 <code>10s</code></p>
<p><em><strong>场景2</strong>: strategy=free, forks=5, (serial=all)</em></p>
<p>C1: T1-&gt;T2, 同时 C2: T1-&gt; T2，同时 C3: T1-&gt;T2，同时 C4: T1-&gt;T2，同时 C5：T1-&gt;T2</p>
<p>总耗时 <code>10s</code></p>
<p>在这个基础上修改 <code>forks</code> 数量，再看看不同 <code>strategy</code> 场景</p>
<p><em><strong>场景3</strong>：strategy=linear, forks=3, (serial=all)</em></p>
<p>C1: T1-&gt;T2, 同时 C2: T1-&gt; T2，同时 C3: T1-&gt;T2<br>
C4: T1-&gt;T2，同时 C5：T1-&gt;T2</p>
<p>总耗时 <code>20s</code></p>
<p><em><strong>场景4</strong>：strategy=free, forks=3, (serial=all)</em></p>
<p>T1: C1-C2-C3 （5s）<br>
T1: C4-C5 （5s）<br>
T2: C1-C2-C3 （5s）<br>
T2: C4-C5 （5s）</p>
<p>和场景3一样：<code>20s</code><br>
场景4和场景3表现一致：虽然单个实例之间互相无关联，但 C1-C2-C3 执行完 T1 前，T1最大 forks 为3，C4和C5需要等当前 T1 forks 有冗余时才能继续执行。</p>
<p>在 forks 为5时，修改 serial 的值：</p>
<p><em><strong>场景5</strong>：strategy=linear, forks=5, serial=3</em></p>
<p>T1: C1-C2-C3 （5s）<br>
T2: C1-C2-C3 （5s）<br>
T1: C4-C5 （5s）<br>
T2: C4-C5 （5s）</p>
<p>总耗时 <code>20s</code></p>
<p><em><strong>场景6</strong>：strategy=free, forks=5, serial=3</em></p>
<p>T1: C1-C2-C3 （5s）<br>
T2: C1-C2-C3 （5s）<br>
T1: C4-C5 （5s）<br>
T2: C4-C5 （5s）</p>
<p>总耗时 <code>20s</code></p>
<p>当 forks=3，serial=3 时：</p>
<p><em><strong>场景7</strong>：strategy=linear, forks=3, serial=3</em></p>
<p>和场景5一致。</p>
<p><em><strong>场景8</strong>：strategy=free, forks=3, serial=3</em></p>
<p>和场景6一致。</p>
<p>当 forks 小于 serial 时：</p>
<p><em><strong>场景9</strong>：strategy=linear, forks=2, serial=3</em></p>
<p>T1: C1-C2 (5s)<br>
T1: C3 （5s）<br>
T2: C1-C2 （5s）<br>
T2: C3 （5s）<br>
T1: C4-C5 (5s)<br>
T2: C4-C5 (5s)</p>
<p>总耗时 <code>30s</code></p>
<p><em><strong>场景10</strong>：strategy=free, forks=2, serial=3</em></p>
<p>T1: C1-C2 (5s)<br>
T2: C1-C2  (5s)<br>
T1: C3 （5s）<br>
T2: C3 （5s）<br>
T1: C4-C5 (5s)<br>
T2: C4-C5 (5s)</p>
<p>总耗时 <code>30s</code></p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[2020.12.31]]></title>
        <id>https://kioipp.com/post/20201231/</id>
        <link href="https://kioipp.com/post/20201231/">
        </link>
        <updated>2020-12-31T06:48:50.000Z</updated>
        <summary type="html"><![CDATA[<p>又是一年结束的反省。</p>
]]></summary>
        <content type="html"><![CDATA[<p>又是一年结束的反省。</p>
<!-- more -->
<h2 id="工作">工作</h2>
<p>结束了一段 3 年的岗位，如愿以偿地到了某 E 厂。<br>
尽管岗位有点不太理想，但也算是到了新环境，看到了很多新鲜的事情（But fresh not means good)。<br>
回顾上一份工作的收获，应该是让我找到了自己的定位，或者说要努力的方向。毕业后糊里糊涂地过了 5 年，然后又经历了 2 年才看到自己尚且可做且还有些喜欢的一条路，可能浪费的时间有点多。但之前的经验是，每一份不起眼或者当时觉得无用的经历其实都是某种成长，在之后某个时刻一定会成为你脚下的基石。<br>
迷茫的时候请认真做好现在每一件能做的事情，并且踏实地做到极致。<br>
在这个基础上再去反思自己是否有更好的方向。</p>
<p>Time is short 。</p>
<p>且先扎实一年看看。</p>
<h2 id="电影">电影</h2>
<p>因为疫情的原因今年去电影院看电影的次数屈指可数，基本是在手机、电视或者电脑上完成观影。<br>
节奏也还 OK，在慢慢清理一部分之前某个人给我列的清单，大概看完也这样了。<br>
是不是每次看完电影后写个双向链接笔记，做一些自我回顾和写作，前期可以一个月至少一次？<br>
我觉得可以有。<br>
慢慢在补一些之前没来得及或者因为懒癌发作而错过的日常番。</p>
<h2 id="游戏">游戏</h2>
<p>似乎今年玩游戏的整体时间少了很多，但有时候还是会放纵自己，比如今年国庆期间的 5 天假期，一个人宅家里昏天黑地地一直撸无主之地3，等日子过去了才想打脸：干点啥不好？<br>
明年期待的是怪物猎人新作，其他时间尽量在手机上打发，这样还可以陪陪老婆（笑）。<br>
总的来讲还是要继续减少游戏时间……</p>
<h2 id="健康">健康</h2>
<p>体重悄无声息地往上走，但我最不能忍的是肚子竟然变大了……为此焦虑了好一段时间，并买了升降电脑桌。<br>
健身环和有氧拳击玩了一段时间后没有再继续，是时候继续捡起来，不能再放任自己的腹部继续膨胀……<br>
比如先从每周玩个三次，再增加到四次，或者早上起来锻炼？<br>
为了管理体态请对自己狠一点……</p>
<h2 id="关系">关系</h2>
<p>要更关爱家人，每周主动给爸妈打个电话。<br>
明年买好房子，然后把关系定下来？</p>
<blockquote>
<p>逝者不可追。</p>
</blockquote>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[python3: dataclass 个人 tips 合集]]></title>
        <id>https://kioipp.com/post/python3-dataclass-ge-ren-tips-he-ji/</id>
        <link href="https://kioipp.com/post/python3-dataclass-ge-ren-tips-he-ji/">
        </link>
        <updated>2020-12-17T07:16:02.000Z</updated>
        <content type="html"><![CDATA[<p><code>dataclass</code> 是 python3 很好用的一个官方库，可以有效地节省各类数据体在 <code>__init__()</code> 中重复类似 self.name = name 的书写。</p>
<p>总（抄）结（袭）了一些好用的自定义方法，让 <code>dataclass</code> 用起来更舒服。</p>
<h2 id="返回所有非-none-元素作为字典">返回所有非 None 元素作为字典</h2>
<pre><code class="language-python">from dataclasses import dataclass, asdict, fields


def factory_filter(data: list) -&gt; dict:
    return dict(duple_item for duple_item in data if duple_item[1] is not None)


@dataclass
class BaseDataClass:
    &quot;&quot;&quot;基础数据类&quot;&quot;&quot;

    def get_data(self) -&gt; dict:
        &quot;&quot;&quot;返回所有非 None 元素的字典&quot;&quot;&quot;
        return asdict(self, dict_factory=factory_filter)
</code></pre>
<p>测试：</p>
<pre><code class="language-python">@dataclass
class Person(BaseDataClass):
    name: str
    age: str
    sex: str = None


peter = Person(name=&quot;peter&quot;, age=17)
peter.get_data()
</code></pre>
<p>输出:</p>
<pre><code class="language-json">{'name': 'peter', 'age': 17}
</code></pre>
<hr>
<h2 id="工厂方法自动忽略未定义的传入变量">工厂方法：自动忽略未定义的传入变量</h2>
<p>如果用传入未定义的变量，那么会自动出错，比如：</p>
<pre><code class="language-python">peter = Person(name=&quot;peter&quot;, age=17, job='student')
---------------------------------------------------------------------------
TypeError  Traceback (most recent call last)
&lt;ipython-input-4-948a3ccfe901&gt; in &lt;module&gt;
      6 
      7 
----&gt; 8 peter = Person(name=&quot;peter&quot;, age=17, job='student')

TypeError: __init__() got an unexpected keyword argument 'job'
</code></pre>
<p>还是上面的 <code>BaseDataClass</code>， 添加类方法：</p>
<pre><code class="language-python">    @classmethod
    def create_from_dict(cls, **dict_) -&gt; dataclass:
        &quot;&quot;&quot;工厂方法，自动忽略非 dataclass 参数&quot;&quot;&quot;
        class_fields = {f.name for f in fields(cls)}
        return cls(**{k: v for k, v in dict_.items() if k in class_fields})
</code></pre>
<p>再试一次：</p>
<pre><code class="language-python">peter = Person(name=&quot;peter&quot;, age=17)
print(Person.create_from_dict(name=&quot;peter&quot;, age=17, job='student').get_data())
</code></pre>
<p>输出:</p>
<pre><code class="language-json">{'name': 'peter', 'age': 17}
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[写给自己的 Golang Cheatsheet]]></title>
        <id>https://kioipp.com/post/xie-gei-zi-ji-de-golang-cheatsheet/</id>
        <link href="https://kioipp.com/post/xie-gei-zi-ji-de-golang-cheatsheet/">
        </link>
        <updated>2020-11-18T07:02:58.000Z</updated>
        <content type="html"><![CDATA[<pre><code class="language-go">:=
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[namedtuple 与 dataclass]]></title>
        <id>https://kioipp.com/post/namedtuple-yu-dataclass/</id>
        <link href="https://kioipp.com/post/namedtuple-yu-dataclass/">
        </link>
        <updated>2020-11-13T06:29:51.000Z</updated>
        <summary type="html"><![CDATA[<p>常用的数据定义类。</p>
]]></summary>
        <content type="html"><![CDATA[<p>常用的数据定义类。</p>
<!-- more -->
<h1 id="对比">对比</h1>
<table>
<thead>
<tr>
<th>class</th>
<th>require</th>
<th>write_able</th>
</tr>
</thead>
<tbody>
<tr>
<td>namedtuple</td>
<td>&gt;= 2.7</td>
<td>不可改写或者改写性较差，不能自定义方法</td>
</tr>
<tr>
<td>dataclass</td>
<td>&gt;= 3.7</td>
<td>属性可写可提供默认值（也可以 fronzen 后只读），可自定方法，可继续子类</td>
</tr>
</tbody>
</table>
<hr>
<h1 id="namedtuple">namedtuple</h1>
<p>用起来像是对 tuple 类添加了类似 index 或者 iterm name 的访问方式。</p>
<pre><code class="language-python"># 定义一个namedtuple类型User，并包含name，sex和age属性。
User = namedtuple('User', ['name', 'sex', 'age'])

# 创建一个User对象
user = User(name='kongxx', sex='male', age=21)

# 也可以通过一个list来创建一个User对象，这里注意需要使用&quot;_make&quot;方法
user = User._make(['kongxx', 'male', 21])

print(user)
# User(name='user1', sex='male', age=21)

# 获取用户的属性
print(user.name)
print(user.sex)
print(user.age)

# 修改对象属性，注意要使用&quot;_replace&quot;方法，看起来很不优雅
user = user._replace(age=22)
print(user)
# User(name='user1', sex='male', age=21)

# 将User对象转换成字典，注意要使用&quot;_asdict&quot;
print(user._asdict())
# OrderedDict([('name', 'kongxx'), ('sex', 'male'), ('age', 22)])
</code></pre>
<hr>
<h1 id="dataclass">dataclass</h1>
<p>官方文档链接： https://docs.python.org/3/library/dataclasses.html</p>
<p>简单用法如下，用 py3 function annotion 的语法来创建属性</p>
<pre><code class="language-python">from dataclasses import dataclass

@dataclass
class InventoryItem:
    &quot;&quot;&quot;Class for keeping track of an item in inventory.&quot;&quot;&quot;
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -&gt; float:
        return self.unit_price * self.quantity_on_hand
</code></pre>
<p>有一个扑克牌的例子写得很好： https://juejin.im/post/6844903853700153357</p>
<!-- more -->
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Python3: 快速来一发装饰器]]></title>
        <id>https://kioipp.com/post/python3-kuai-su-lai-yi-fa-zhuang-shi-qi/</id>
        <link href="https://kioipp.com/post/python3-kuai-su-lai-yi-fa-zhuang-shi-qi/">
        </link>
        <updated>2020-11-09T12:17:46.000Z</updated>
        <summary type="html"><![CDATA[<p>一直非常喜欢这个东西，简洁、明快～</p>
]]></summary>
        <content type="html"><![CDATA[<p>一直非常喜欢这个东西，简洁、明快～</p>
<!-- more -->
<pre><code class="language-python">from decorator import decorator
import time


@decorator
def time_counter(func, ignore: bool = False, *args, **kwargs):
    start = time.time()
    if ignore:
        result = False
    else:
        result = func(*args, **kwargs)
    end = time.time()
    delta = end - start
    print(f&quot;The function runs {delta} seconds.&quot;)
    return result


@time_counter
def do_sleep(secondes):
    time.sleep(secondes)
    return True


@time_counter(ignore=True)
def do_nothing(secondes):
    time.sleep(secondes)
    return True


if do_sleep(4.5):
    print(&quot;Take a sleep :)&quot;)
if not do_nothing(4.5):
    print(&quot;Doing nothing :(&quot;)
</code></pre>
<hr>
<pre><code class="language-bash">$ python3 de_func.py
The function runs 4.505078077316284 seconds.
Take a sleep :)
The function runs 0.0 seconds.
Doing nothing :(
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Docker 多阶段构建]]></title>
        <id>https://kioipp.com/post/docker-duo-jie-duan-gou-jian/</id>
        <link href="https://kioipp.com/post/docker-duo-jie-duan-gou-jian/">
        </link>
        <updated>2020-10-29T02:47:25.000Z</updated>
        <summary type="html"><![CDATA[<blockquote>
<p>偷懒是人类进步的阶梯</p>
</blockquote>
]]></summary>
        <content type="html"><![CDATA[<blockquote>
<p>偷懒是人类进步的阶梯</p>
</blockquote>
<!-- more -->
<p>今天看到一个概念，不是新的，但之前却很奇怪地完全没有接触过。</p>
<p><a href="https://docs.docker.com/develop/develop-images/multistage-build/">Docker multi-stage builds</a></p>
<p>简单来说，就是 Dockerfile 支持多次 build，然后把上一次 build 产生的文件直接 COPY 到下一次的 build 过程中。</p>
<p>相当于第二次的 build 里只执行了一次 COPY，省去中间很多下载、安装、编译的过程。</p>
<h2 id="优点">优点</h2>
<ul>
<li>把 build + run 过程分离，让 run 只负责 run；</li>
<li>大幅度减少 run 镜像的层数，从而压缩 run 环境的分发和更新时间；</li>
</ul>
<h2 id="缺点">缺点</h2>
<ul>
<li>对于动态语言来说（比如 SRE 工程师最爱的 Python），依赖环境较难处理，需要用额外的打包工具；</li>
<li>打包后镜像只有 run 环境，出问题的时候不方便进行 debug；</li>
</ul>
<p>从上面的优缺点可以看到，对静态语言，尤其是最后可以打包成二进制的文件来说，多阶段构建的好处是显而易见的，对动态语言来说，就需要各种尝试，确认打包后程序的稳定性。</p>
<h2 id="例子">例子</h2>
<p>这里摘抄两个在网上看到的 python 的例子：</p>
<h3 id="使用-wheel">使用 wheel</h3>
<pre><code>FROM python:2.7-alpine as base
RUN mkdir /svc
COPY . /svc
WORKDIR /svc
RUN apk add --update \
    postgresql-dev \
    gcc \
    musl-dev \
    linux-headers
RUN pip install wheel &amp;&amp; pip wheel . --wheel-dir=/svc/wheels

FROM python:2.7-alpine
COPY --from=base /svc /svc
WORKDIR /svc
RUN pip install --no-index --find-links=/svc/wheels -r requirements.txt
</code></pre>
<h3 id="使用-pyinstaller">使用 pyinstaller</h3>
<pre><code>FROM python:3.6 as build-env
WORKDIR /app/
ADD . /app/
ADD https://github.com/pyinstaller/pyinstaller/archive/develop.zip /app/pyinstaller-develop.zip
RUN pip install -r requirements.txt &amp;&amp; \
    pip install pyinstaller-develop.zip
RUN pyinstaller --hidden-import=tornado --onefile -d -y run.py
 
FROM frolvlad/alpine-glibc
WORKDIR /app/
COPY --from=build-env /app/dist/run .
ENTRYPOINT [&quot;./run&quot;]
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[macOS 上 Python 虚拟环境的管理: pyenv]]></title>
        <id>https://kioipp.com/post/macos-shang-python-xu-ni-huan-jing-de-guan-li-pyenv/</id>
        <link href="https://kioipp.com/post/macos-shang-python-xu-ni-huan-jing-de-guan-li-pyenv/">
        </link>
        <updated>2020-10-27T12:03:17.000Z</updated>
        <summary type="html"><![CDATA[<blockquote>
<p>&quot;Shit moutain.&quot;</p>
</blockquote>
]]></summary>
        <content type="html"><![CDATA[<blockquote>
<p>&quot;Shit moutain.&quot;</p>
</blockquote>
<!-- more -->
<h2 id="项目地址">项目地址</h2>
<blockquote>
<p>https://github.com/pyenv/pyenv</p>
</blockquote>
<h2 id="安装">安装</h2>
<pre><code>brew install pyenv
</code></pre>
<h2 id="命令">命令</h2>
<p>列出当前已安装的 python 版本</p>
<pre><code>pyenv version
</code></pre>
<p>列出可安装版本</p>
<pre><code>pyenv install --list
</code></pre>
<p>列出当前可用的虚拟环境</p>
<pre><code>pyenv  virtualenvs
</code></pre>
<p>创建虚拟环境</p>
<pre><code>pyenv virtualenv &lt;Python 版本号&gt; &lt;虚拟环境名称&gt;
</code></pre>
<p>激活虚拟环境</p>
<pre><code>pyenv activate &lt;虚拟环境名称&gt;
</code></pre>
<p>退出虚拟环境</p>
<pre><code>pyenv deactivate
</code></pre>
<p>删除虚拟环境</p>
<pre><code>pyenv uninstall &lt;虚拟环境名称&gt;
</code></pre>
<h2 id="vscode">Vscode</h2>
<p>把工作区配置文件里的 <code>&quot;python.pythonPath&quot;</code> 换成 <code>pyenv which python</code> 打印的路径。</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Python实例方法、类方法和静态方法举例理解]]></title>
        <id>https://kioipp.com/post/python-shi-li-fang-fa-lei-fang-fa-he-jing-tai-fang-fa-ju-li-li-jie/</id>
        <link href="https://kioipp.com/post/python-shi-li-fang-fa-lei-fang-fa-he-jing-tai-fang-fa-ju-li-li-jie/">
        </link>
        <updated>2020-10-20T11:42:30.000Z</updated>
        <summary type="html"><![CDATA[<p>一次性理解实例方法、静态方法和类方法。</p>
]]></summary>
        <content type="html"><![CDATA[<p>一次性理解实例方法、静态方法和类方法。</p>
<!-- more -->
<p>今天的对象：</p>
<ul>
<li>@classmethod 类方法；</li>
<li>@staticmethod 静态方法；</li>
<li>普通实例方法；</li>
</ul>
<h3 id="1-基本类">1. 基本类</h3>
<p>先写从普通的一个类写起：</p>
<pre><code class="language-python">
class Fruit(object):

    def __init__(self, name):
        self.name = name

apple = Fruit('apple')
</code></pre>
<p>这个时候，我们称<code>Fruit</code>为<code>class（类）</code>，<code>apple</code>为<code>instance（实例）</code>。</p>
<hr>
<h3 id="2-实例方法">2. 实例方法</h3>
<p>接下来再添加一个值<code>price</code>和方法<code>show_price</code>：</p>
<pre><code class="language-python">
class Fruit(object):

    def __init__(self, name, price):
        self.name = name
        self.price = price

    def show_price(self):
        print(f'The price is {self.price}')

apple = Fruit('apple', 7.0)
apple.show_price()
</code></pre>
<p>这个时候，<code>apple.show_price</code>就是<code>apple</code>这个实例的<code>instance method（实例方法）</code>。<br>
直接用 Fruit.show_price() 调用会出错，从两个概念上说：</p>
<ul>
<li>instance method是<code>实例方法</code>，只能由<code>实例</code>调用，不能由<code>类</code>调用。<code>show_price(self)</code>的第一个参数是<code>实例</code>。</li>
<li><code>水果</code>没有价格的概念，<code>苹果</code>才有。</li>
</ul>
<hr>
<h3 id="3-类方法">3. 类方法</h3>
<p>继续添加一个方法<code>eat()</code>：</p>
<pre><code class="language-python">
class Fruit(object):

    def __init__(self, name, price):
        self.name = name
        self.price = price

    def show_price(self):
        print(f'The price is {self.price}')

    @classmethod
    def eat(cls):
        print(&quot;Enjoy.&quot;)

apple = Fruit('apple', 7.0)
apple.show_price()
Fruit.eat()
apple.eat()
</code></pre>
<p>同样，我们从两个纬度理解：</p>
<ul>
<li><code>eat(cls)</code>的参数是 <code>cls</code>，即类是它的参数，因此可以从类直接调用。实例也是类，因此实例也可以调用。类方法主要用于一些不需要实例化类就能调用的方法。</li>
<li>绝大部分水果具有可以食用的属性，因此说<code>吃水果</code>和<code>吃苹果</code>在逻辑上都成立。</li>
</ul>
<hr>
<h3 id="4-静态方法">4. 静态方法</h3>
<p>这次我们添加一个静态方法 <code>will_go_bad()</code> 和一个外部的静态变量 <code>BAD_WEATHER</code></p>
<pre><code class="language-python">
BAD_WEATHER = True

class Fruit(object):

    def __init__(self, name, price):
        self.name = name
        self.price = price

    def show_price(self):
        print(f'The price is {self.price}')

    @classmethod
    def eat(cls):
        print(&quot;Enjoy.&quot;)

    @staticmethod
    def will_go_bad():
        if BAD_WEATHER:
            return True

apple = Fruit('apple', 7.0)
apple.will_go_bad()
Fruit.will_go_bad()
</code></pre>
<ul>
<li>从空间上理解，静态方法 <code>will_go_bad()</code> 只是定义在类这个空间（类命名空间）中，没有任何特殊的<code>cls</code> 或者<code>self</code> 的绑定，它也<code>不能访问类里任何的对象或者方法</code>。和全局函数不同的是，它只能通过访问类这个空间来调用。静态方法用于处理类外部依赖的交互。</li>
<li>从逻辑上理解，坏天气会影响水果的好坏；但坏天气和水果之间是不能互相访问的，只能通过某种逻辑进行关联。</li>
</ul>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[manage.py: 简明好用的 CLI builder]]></title>
        <id>https://kioipp.com/post/managepy-jian-ming-hao-yong-de-cli-builder/</id>
        <link href="https://kioipp.com/post/managepy-jian-ming-hao-yong-de-cli-builder/">
        </link>
        <updated>2020-10-19T03:16:05.000Z</updated>
        <summary type="html"><![CDATA[<p>&quot;CLI is everything.&quot;</p>
]]></summary>
        <content type="html"><![CDATA[<p>&quot;CLI is everything.&quot;</p>
<!-- more -->
<h2 id="pip-官方项目地址">PIP 官方项目地址</h2>
<blockquote>
<p>https://pypi.org/project/manage.py/  （含官方用例）</p>
</blockquote>
<h2 id="安装">安装</h2>
<pre><code class="language-bash">pip install mange.py
</code></pre>
<h2 id="使用">使用</h2>
<h3 id="1创建命令">1.创建命令</h3>
<pre><code class="language-python">from manager import Manager

manager_echo = Manager()


@manager_echo.command
def echo_with_no_args():
    &quot;&quot;&quot;Command description, will show on command line.
    &quot;&quot;&quot;
    print(&quot;Hello&quot;)


if __name__ == '__main__':
    manager_echo.main()
</code></pre>
<pre><code class="language-bash">$ python3 cli_sample.py
usage: cli_sample.py &lt;command&gt; [&lt;args&gt;]

positional arguments:
  command     the command to run

optional arguments:
  -h, --help  show this help message and exit

available commands:
  echo_with_no_args        Command description, will show on command line.
</code></pre>
<pre><code class="language-bash">$ python3 cli_sample.py echo_with_no_args
Hello
</code></pre>
<hr>
<h3 id="2-多重-namepspace-管理">2. 多重 namepspace 管理</h3>
<pre><code class="language-python">from manager import Manager
import time

manager_echo = Manager()
manager_ops = Manager()
manager_main = Manager()

manager_main.merge(manager_echo, namespace='echo')
manager_main.merge(manager_ops, namespace='ops')


@manager_echo.command
def echo_with_no_args():
    &quot;&quot;&quot;Command description, will show on command line.
    &quot;&quot;&quot;
    print(&quot;Hello&quot;)


@manager_ops.command
def ops_with_no_args():
    &quot;&quot;&quot;Do some operation without args.
    &quot;&quot;&quot;
    print(time.time() + 1.0)


manager_main.merge(manager_echo, namespace='echo')
manager_main.merge(manager_ops, namespace='ops')

if __name__ == '__main__':
    manager_main.main()
</code></pre>
<pre><code class="language-bash">python3 cli_sample.py
usage: cli_sample.py [&lt;namespace&gt;.]&lt;command&gt; [&lt;args&gt;]

positional arguments:
  command     the command to run

optional arguments:
  -h, --help  show this help message and exit

available commands:

  [echo]
    echo_with_no_args      Command description, will show on command line.


  [ops]
    ops_with_no_args       Do some operation without args.
</code></pre>
<pre><code class="language-bash">python3 cli_sample.py ops.ops_with_no_args
1603078473.9244218
</code></pre>
<hr>
<h3 id="3不带提示-按照顺序输入的参数">3.不带提示、按照顺序输入的参数</h3>
<pre><code class="language-python">@manager_echo.command
def echo_with_args(name, pid):
    &quot;&quot;&quot;Echo given &lt;params&gt;
    &quot;&quot;&quot;
    print(f'Name is {name}, PID is {pid}')
</code></pre>
<pre><code class="language-bash">$ python3 cli_sample.py echo.echo_with_args  test 77
Name is test, PID is 77
</code></pre>
<hr>
<h3 id="4带提示的顺序输入的参数">4.带提示的顺序输入的参数</h3>
<pre><code class="language-python">@manager_echo.arg('name', help='User\'s name')
@manager_echo.arg('pid', help='App\'s PID')
@manager_echo.command
def echo_with_args(name, pid):
    &quot;&quot;&quot;Echo given &lt;params&gt;
    &quot;&quot;&quot;
    print(f'Name is {name}, PID is {pid}')
</code></pre>
<pre><code class="language-bash">$ python3 cli_sample.py echo.echo_with_args -h
usage: cli_sample.py echo.echo_with_args [-h] name pid

Echo given &lt;params&gt;

positional arguments:
  name        User's name
  pid         App's PID

optional arguments:
  -h, --help  show this help message and exit
</code></pre>
<hr>
<h3 id="5-必要参数和非必要参数的提示输入">5. 必要参数和非必要参数的提示输入</h3>
<pre><code class="language-python">@manager_echo.arg('name', help='User\'s name')
@manager_echo.arg('pid', help='App\'s PID')
@manager_echo.command
def echo_with_args(name, pid=''):
    &quot;&quot;&quot;Echo given &lt;params&gt;
    &quot;&quot;&quot;
    print(f'Name is {name}, PID is {pid}')
</code></pre>
<pre><code class="language-bash">$ python3 cli_sample.py echo.echo_with_args -h
usage: cli_sample.py echo.echo_with_args [-h] [--name NAME] [--pid PID]

Echo given &lt;params&gt;

optional arguments:
  -h, --help   show this help message and exit
  --name NAME  User's name
  --pid PID    App's PID
</code></pre>
<pre><code class="language-bash">$ temp python3 cli_sample.py echo.echo_with_args 123
Name is 123, PID is
$ temp python3 cli_sample.py echo.echo_with_args 123 --pid 777
Name is 123, PID is 777
</code></pre>
<hr>
<p>或者作为布尔值使用：</p>
<pre><code class="language-python">@manager_echo.arg('name', help='User\'s name')
@manager_echo.arg('pid', help='App\'s PID')
@manager_echo.command
def echo_with_args(name, pid=False):
    &quot;&quot;&quot;Echo given &lt;params&gt;
    &quot;&quot;&quot;
    if pid:
        print(f'Name is {name}, PID exists')
    else:
        print(f'Name is {name}, PID not exists')
</code></pre>
<hr>
<pre><code class="language-bash">$ python3 cli_sample.py echo.echo_with_args batman
Name is batman, PID not exists
$ python3 cli_sample.py echo.echo_with_args batman --pid
Name is batman, PID exists
</code></pre>
<hr>
<h3 id="6参数-shortcuts">6.参数 shortcuts</h3>
<pre><code class="language-python">@manager_echo.arg('name', shortcut='n', help='User\'s name')
@manager_echo.arg('pid', shortcut='p', help='App\'s PID')
@manager_echo.command
def echo_with_args(name, pid=False):
    &quot;&quot;&quot;Echo given &lt;params&gt;
    &quot;&quot;&quot;
    if pid:
        print(f'Name is {name}, PID exists')
    else:
        print(f'Name is {name}, PID not exists')
</code></pre>
<hr>
<pre><code class="language-bash">$ python3 cli_sample.py echo.echo_with_args batman --p
Name is batman, PID exists
</code></pre>
]]></content>
    </entry>
</feed>