import thisで親父の小言を表示する

この記事は、[ビットスター Advent Calendar 2021](https://qiita.com/advent-calendar/2021/bitstar-inc)の3日目です。

# はじめに

みんな大好きThe Zen of Python。pythonでimport thisすると出てくるあれです。

```bash
$ python3
Python 3.8.10 (default, Jun  2 2021, 10:49:15)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
>>>
```

ふと、これをこっそり親父の小言に変えても誰も気が付かないんじゃないかと思ったので、ちょっとやってみます。

# そもそも、thisモジュールはどこにあるのか

まずは本家本元のthisがどこにあるのか確認したく、pythonがインストールされているディレクトリやソースコードを'Beautiful is better'でgrepしてみたのですがヒットしません。
しばらく探してようやく見つけたのがこれです。

```python:/usr/lib/python3.8/this.py
s = """Gur Mra bs Clguba, ol Gvz Crgref

Ornhgvshy vf orggre guna htyl.
Rkcyvpvg vf orggre guna vzcyvpvg.
Fvzcyr vf orggre guna pbzcyrk.
Pbzcyrk vf orggre guna pbzcyvpngrq.
Syng vf orggre guna arfgrq.
Fcnefr vf orggre guna qrafr.
Ernqnovyvgl pbhagf.
Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.
Nygubhtu cenpgvpnyvgl orngf chevgl.
Reebef fubhyq arire cnff fvyragyl.
Hayrff rkcyvpvgyl fvyraprq.
Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.
Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.
Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.
Abj vf orggre guna arire.
Nygubhtu arire vf bsgra orggre guna *evtug* abj.
Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.
Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.
Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"""

d = {}
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)

print("".join([d.get(c, c) for c in s]))
```

シンプルにfindでthisを探せばよかった...
Zen of PythonはROT13で暗号化されていたのでgrepではヒットしなかったという落ちでした。

# 親父の小言化したthis.pyを作る

本家のthisモジュールっぽく、親父の小言化したthis.pyを作ります。

## Step1 親父の小言を英語化する

[deepl](https://www.deepl.com/translator)の力をかりて親父の小言を英語化します。

```text:親父の小言
火は粗末にするな
朝、機嫌を良くしろ
朝早く起きろ
神仏を祈れ
～ 略 ～
人の気を揉む時、力を付けてやれ
悪しき事も「よし、よし」と祝い直せ
```

```text:dadsscoldings.txt
Don't mess with fire.
Be happy in the morning.
Get up early.
Pray to God and Buddah.
...
When you are worried about others, help them.
Celebrate the good with the bad.
```

## Step2 親父の小言をROT13で暗号化する

親父の小言をROT13で暗号化します。pythonのcodecsがROT13に対応しているのでこれを利用します。
以下のような簡易スクリプトでthis.pyの元を作ります。

```python:to_rot13arr.py
import codecs
import sys


print("SCOLDINGS = (")

for line in open(sys.argv[1]):
    encoded = codecs.encode(line.strip(), "rot13")
    print(f'    "{encoded}",')

print(")")
```

先ほど英語化したテキストファイルを引数にto_rot13arr.pyを実行します。

```bash
$ python to_rot13arr.py dadsscoldings.txt > this.py
$ cat this.py
SCOLDINGS = (
    "Qba'g zrff jvgu sver.",
    "Or unccl va gur zbeavat.",
    "Trg hc rneyl.",
～ 略 ～
    "Jura lbh ner jbeevrq nobhg bguref, uryc gurz.",
    "Pryroengr gur tbbq jvgu gur onq.",
)
```

すでに違和感ありありですが、本家のthisにはない隠れ機能を一つ追加したくて小言をtuple化しています。

## Step3 import時に小言を表示する部分を実装する。

importしたときに小言を表示する部分をtupleの下に実装します。なるべく本家に似せて実装します。

```python:this.py
SCOLDINGS = (
    "Qba'g zrff jvgu sver.",
    "Or unccl va gur zbeavat.",
    "Trg hc rneyl.",
～ 略 ～
    "Jura lbh ner jbeevrq nobhg bguref, uryc gurz.",
    "Pryroengr gur tbbq jvgu gur onq.",
)

s = """Qnq'f fpbyqvatf, ol n qbabe jub yvirq va Xnaqn

""" + "\n".join(SCOLDINGS)

d = {}
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)

print("".join([d.get(c, c) for c in s]))
```

## Step4 隠れ機能を追加する

今日の小言を表示する隠れ機能を追加します。

```python
import datetime
import hashlib


def todays_scolding():
    datestr = datetime.datetime.now().strftime("%Y%m%d").encode("utf8")
    index = int(hashlib.md5(datestr).hexdigest(), 16) % len(SCOLDINGS)
    print("".join([d.get(c, c) for c in SCOLDINGS[index]]))
```

日ごとに疑似ランダム的に小言が出るよう、日付をハッシュ化したものを数値化して利用しています。
(もっといい方法知ってる方いれば教えてください)

以上で完成となります。

# 実行してみる

本家のthis.pyをこのthis.pyで置き換えるのはあまりに乱暴なので、カレントディレクトリにthis.pyを置いて実行してみます。

```bash
$ python3
>>> import this
Dad's scoldings, by a donor who lived in Kanda

Don't mess with fire.
Be happy in the morning.
Get up early.
～ 全81の小言。長すぎるので略 ～
When you are worried about others, help them.
Celebrate the good with the bad.
>>> 
```

長すぎて違和感ありありですね。
せっかくなので隠れ機能も使ってみます。

```bash
>>> import this
～ 長いので略 ～
>>> this.todays_scolding()
Don't take a boat when the wind blows.
>>>
```

どうでもいいことですが隠れ機能を使うときはas dadでimportした方がしっくりきます。

```bash
>>> import this as dad
>>> dad.todays_scolding()
Don't take a boat when the wind blows.
>>>
```

# 学んだこと

- thisはbuilt-inモジュールではない(カレントディレクトリにthis.pyを置くだけでとりあえず差し替わる)
- The Zen of PythonはROT13で暗号化されている
- The Zen of Pythonを親父の小言に差し替えると違和感がある

# おまけ

今回作成したthis.pyを載せておきます

```python
import datetime
import hashlib


SCOLDINGS = (
    "Qba'g zrff jvgu sver.",
    "Or unccl va gur zbeavat.",
    "Trg hc rneyl.",
    "Cenl gb Tbq naq Ohqqnu.",
    "Gnxr pner bs lbhefrys",
    "Xrrc n qvfgnapr sebz vzchevgl.",
    "Cenl sbe lbhe fhpprff.",
    "Qba'g fnl onq yhpx.",
    "Yvir va crnpr ng ubzr.",
    "Qba'g or natel.",
    "Qba'g rzoneenff nalbar.",
    "Qba'g znxr bguref cnl.",
    "Yrg crbcyr znxr sha bs lbh.",
    "Qba'g or wrnybhf bs bguref.",
    "Yrg gur fzneg barf or fzneg.",
    "Erfcrpg ryqreyl crbcr.",
    "Erghea gur snibe fbzrubj",
    "Xrrc lbhe thneq hc.",
    "Yvfgra gb unys bs jung lbhe jvsr fnlf",
    "Qba'g yvfgra gb jung gur xvqf unir gb fnl",
    "Nf sbe gur snzvyl ohfvarff, jbex uneq",
    "Lbh unir gb or cngvrag.",
    "Qba'g uvg gur xvq'f urnq.",
    "Cvapu lbhe guvtuf",
    "Znxr n ybg bs zbarl naq hfr vg.",
    "Qba'g obeebj naq fcraq.",
    "Yraq gurz gb bguref.",
    "Qba'g ohl n jbzna.",
    "Trg zneevrq.",
    "Fcner ab rssbeg sbe gur fvpx.",
    "Tvir vg gb gubfr jub unir qvssvphygl.",
    "Qba'g jnfgr lbhe zbarl.",
    "Ab xvyyvat.",
    "Qba'g rng oveqf naq navznyf.",
    "Qb naahny zrzbevny freivprf.",
    "Rirelguvat vf zbqrfg ba Cneragf' Qnl.",
    "Qba'g or hatengrshy.",
    "Qrprvir gur puvyq naq hfr vg jryy.",
    "Qba'g yrg gur jvsr sbby lbh.",
    "Qba'g cynl tnzoyvat.",
    "Qba'g fgneg n svtug.",
    "Qba'g qevax gbb zhpu.",
    "Qba'g rng gbb zhpu",
    "Qba'g or n thnenagbe",
    "Qba'g or n anaal.",
    "Pyrna gur tngr.",
    "Tb gb gur fuevar jvguva svefg guerr qnlf bs gur Arj Lrne.",
    "Ba gur ynfg qnl bs gur lrne, pyrna gur ubhfr.",
    "Qba'g yrg cbiregl obgure lbh.",
    "Or cercnerq sbe sver.",
    "Jura n sver oernxf bhg, uryc chg bhg gur sver naq cebgrpg gur ubhfr.",
    "Qba'g tb sne vagb n jvaqfgbez.",
    "Qb abg or terrql va pnfr bs sver.",
    "Qba'g jrg gur gvaqreobk.",
    "Qba'g eha bhg bs jngre.",
    "Arire eha bhg bs fnyg.",
    "Xrrc lbhe qbbef ybpxrq.",
    "Qb abg jnyx yngr ng avtug.",
    "Trg bhg bs gur pbyq.",
    "Orng gur urng.",
    "Qba'g tb ba n fyrrcbire.",
    "Qba'g pyvzo gbb uvtu.",
    "Qba'g fyrrc ba lbhe onpx jura gurer'f guhaqre.",
    "Qba'g gnxr n ongu jura lbh srry pbyq.",
    "Guvax bs vawhevrf naq zvfuncf nf chavfuzrag.",
    "Cvpx guvatf hc, qba'g jrne gurz.",
    "Chg vg nyy njnl sbe gur jvagre.",
    "Jura lbh'er lbhat, fgnl njnxr naq rnea zbarl.",
    "Gnxr vg rnfl jura lbh ner byq.",
    "Ivfvg gur grzcyr sebz gvzr gb gvzr.",
    "Gnxr pner bs gubfr jub unir ab bar gb gnxr pner bs gurz.",
    "Qba'g unttyr n guva ohfvarff.",
    "Qba'g gnxr n obng jura gur jvaq oybjf.",
    "Yvir jvguva zrnaf.",
    "Gnxr pner bs gur certanag jbzna.",
    "Zber pner sbe cbfgcneghz jbzra.",
    "Gnxr lbhe cvff va gur cvff-ubyr.",
    "Fgbc juvavat.",
    "Qba'g haqrerfgvzngr gur qvfrnfr.",
    "Jura lbh ner jbeevrq nobhg bguref, uryc gurz.",
    "Pryroengr gur tbbq jvgu gur onq.",
)

s = """Qnq'f fpbyqvatf, ol n qbabe jub yvirq va Xnaqn

""" + "\n".join(SCOLDINGS)

d = {}
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)

print("".join([d.get(c, c) for c in s]))


def todays_scolding():
    datestr = datetime.datetime.now().strftime("%Y%m%d").encode("utf8")
    index = int(hashlib.md5(datestr).hexdigest(), 16) % len(SCOLDINGS)
    print("".join([d.get(c, c) for c in SCOLDINGS[index]]))
```